Я пытаюсь настроить и использовать периферийное устройство TIM2
в одноимпульсном режиме (OPM) на плате STM32F303 Discovery
.
Проблема, с которой я сталкиваюсь, заключается в том, что таймер мгновенно завершается после его включения.
На данный момент я не использую interrupt
, а просто опрашиваю бит TIM2_SR
(статусный регистр) UIF
, чтобы определить, завершился ли таймер.
Это происходит только при первом включении таймера, если я использую таймер снова, он работает правильно (не завершается мгновенно).
Я пытался сбросить регистр TIM2_CNT
перед включением таймера, но результат тот же.
use cortex_m_rt::entry;
use stm32f3xx_hal::pac;
#[entry]
fn main( ) -> ! {
let p = pac::Peripherals::take( ).unwrap( );
p.RCC.apb1enr.modify( | _, w | w.tim2en( ).set_bit( ) );
p.TIM2.cr1.write( | w | w
.urs( ).set_bit( )
.opm( ).set_bit( )
.cen( ).clear_bit( ) );
// I've tried resetting the CNT register at this point
// in the application but the result was the same.
// Set the prescaler based on an 8MHz clock.
p.TIM2.psc.write( | w | w.psc( ).bits( 7999 ) );
// Here I initialize an LED (GPIOE). I've removed this code to
// keep the example as clean as possible.
let delay = | duration | {
p.TIM2.arr.write( | w | w.arr( ).bits( duration ) );
// I've also tried resetting the CNT register here
// but the result was the same.
p.TIM2.cr1.modify( | _, w | w.cen( ).set_bit( ) );
while p.TIM2.sr.read( ).uif ( ).bit_is_clear( ) { }
p.TIM2.sr.write( | w | w.uif ( ).clear_bit( ) );
};
// Enable LED.
// This call instantly returns.
delay( 3999 );
// Disable LED.
loop { }
}
В приведенном выше примере светодиод мигает практически без задержки.
Если вместо этого я использую бесконечный цикл, таймер работает, как и предполагалось, после первоначального вызова delay
.
loop {
// Enable LED.
// The first call in the first loop iteration
// returns instantly.
delay( 3999 );
// Disable LED.
// This call, and every call here after correctly
// returns after 4 seconds.
delay( 3999 );
}
Я просмотрел регистры во время работы приложения, и все, кажется, настроено правильно.
TIM2_CNT
читается 0x0000_0000
перед включением таймера.UIF
в регистре TIM2_SR
не установлен до включения таймераTIM2_PSC
считывает правильную предварительную шкалу 7999
TIM2_ARR
содержит правильное значение автоперезагрузки 3999
OPM
в регистре TIM_CR1
установлен правильноПосле прочтения аналогичной проблемы на другом форуме в этом ответе было предложено включить бит URS
в регистре TIM2_CR1
, что приводит к тому, что запрос прерывания обновления / DMA выдается только при переполнении / недополнении счетчика. Это, конечно, не сработало.
У меня такое ощущение, что где-то есть bit
, который мне нужно сбросить / установить, чтобы таймер работал должным образом при первом включении.
Итак, перечитав раздел о таймерах в руководстве, я обнаружил следующее:
Новое отношение предделителя учитывается при следующем событии обновления.
Итак, как я могу сгенерировать событие обновления с помощью программного обеспечения, чтобы значение предварительного делителя обновлялось до запуска таймера. Вот когда я нашел это (курсив мой):
В режиме обратного счета счетчик считает от 0 до значения автоперезагрузки (содержимое TIMx_ARR), затем перезапускается с 0 и генерирует событие переполнения счетчика. Событие Update может генерироваться при каждом переполнении счетчика или установкой бита UG в регистре TIMx_EGR (с помощью программного обеспечения или с помощью контроллера ведомого режима).
Поэтому после установки значения prescaler
я затем устанавливаю бит UG
в регистре TIM2_EGR
.
// Set the prescaler based on an 8MHz clock.
p.TIM2.psc.write( | w | w.psc( ).bits( 7999 ) );
// Generate an update event so that the new prescaler
// value gets loaded.
p.TIM2.egr.write( | w | w.ug( ).update( ) );
После принудительного обновления таймер каждый раз (в том числе и в первый раз) начинал работать корректно.
Итак, подводя итог моей проблеме RT*M
...