Цього разу -- зовсім коротка нотатка. Таймери володіють цікавою особливістю -- якщо встановити біт OPM в CR1, таймер автоматично зупинятиметься після події оновлення.
Важливе зауваження -- цей біт, OPM -- One-Pulse Mode, використовується для реалізації однойменного режиму One-pulse mode, який призначений керувати формою імпульсу -- генерувати його у відповідь на подію, із заданою затримкою після події та заданою довжиною. Про цей режим говоритимемо окремо. Цей пост -- своєрідна прелюдія до відповідної розмови -- ми просто навчимо таймер зупинятися. Така можливість теж корисна -- наприклад, для нашого далекоміра немає потреби робити якісь затримки, генерувати хитрі хвилі, абощо, але хотілося б, щоб таймер генерував один імпульс заданої довжини, і хотілося б, щоб він робив це повністю автоматично -- не використовуючи переривання та ручне вмикання-вимикання логічної одинички на піні.
Важливе зауваження -- цей біт, OPM -- One-Pulse Mode, використовується для реалізації однойменного режиму One-pulse mode, який призначений керувати формою імпульсу -- генерувати його у відповідь на подію, із заданою затримкою після події та заданою довжиною. Про цей режим говоритимемо окремо. Цей пост -- своєрідна прелюдія до відповідної розмови -- ми просто навчимо таймер зупинятися. Така можливість теж корисна -- наприклад, для нашого далекоміра немає потреби робити якісь затримки, генерувати хитрі хвилі, абощо, але хотілося б, щоб таймер генерував один імпульс заданої довжини, і хотілося б, щоб він робив це повністю автоматично -- не використовуючи переривання та ручне вмикання-вимикання логічної одинички на піні.
TIM3->CR1 &= ~TIM_CR1_DIR; // Відлік -- вверх. (Default) /*==>*/ TIM3->CR1 |= TIM_CR1_OPM; // One pulse mode TIM3->CR1 |= TIM_CR1_CEN;
Тепер, згенерувавши один період ШІМ (див. попередній пост за подробицями), таймер зупинятиметься. Фактично, ми отримуємо один імпульс заданої довжини. Якщо після імпульсу треба ще щось зробити, наприклад -- дочекатися результату далекоміра, можна зробити цикл, який перевірятиме стан біту CEN. Ну, до далекоміра ми ще повернемося, а поки -- приклад, мигаємо світлодіодом:
Дозволяючи таймер знову -- встановлюючи біт CEN, ми запускаємо генерацію чергового циклу ШІМ, який змусить світлодіод засвітитися, після чого таймер його погасить і знову зупиниться.
Важливе зауваження: таймер, звичайно, зупинятиметься, дорахувавши до значення в ARR, однак, перед тим переведе керовані канали у новий стан:
Про це слід пам'ятати! Трішки більше про наслідки цього буде мова, коли працюватимемо із далекоміром з допомогою таймерів.
Скачати проект із кодом для цього та двох попередніх постів ("Таймери STM32 -- зовнішнє тактування/CMSIS", "Таймери STM32 -- внутрішні тригери/CMSIS"), можна тут.
Код, який стосується лише цього посту, можна знайти у файлі main_OPM.c , в директорії app/ проекту (не включений у проект CoIDE -- щоб не конфліктувати із int main(), котрий містить все зразу).
while(1) { GPIOC->ODR^=(GPIO_Pin_9); // Зміною стану LED відмічаємо цикл delay_some_ms(5000000); TIM3->CR1 |= TIM_CR1_CEN; }
Дозволяючи таймер знову -- встановлюючи біт CEN, ми запускаємо генерацію чергового циклу ШІМ, який змусить світлодіод засвітитися, після чого таймер його погасить і знову зупиниться.
Важливе зауваження: таймер, звичайно, зупинятиметься, дорахувавши до значення в ARR, однак, перед тим переведе керовані канали у новий стан:
Поведінка каналу ШІМ за автоматичної зупинки в режимі PWM1 (на прикладі TIM1) |
Поведінка каналу ШІМ за автоматичної зупинки в режимі PWM2 (на прикладі TIM1) |
main.c
#include <stm32f10x.h> volatile uint32_t curTicks; #ifdef __cplusplus extern "C"{ #endif void SysTick_Handler(void) { curTicks++; /* Зроблено ще один тік */ } #ifdef __cplusplus } #endif #define MICROSECONDS_GRANULARITY 10 // Потрібна частота спрацювань таймера SysTick в герцах #define FREQ ((1000000)/(MICROSECONDS_GRANULARITY)) #define TICKS ((SystemCoreClock)/(FREQ)) /*! Затримка в мікросекундах, грануляція -- microseconds_granularity мікросекунд, * одну не витягує сам контролер. 24МГц -- один такт це 42нс=0.042мкс */ inline static void delay_some_us(uint32_t mks) { uint32_t ticks=mks/MICROSECONDS_GRANULARITY; int stop_ticks=ticks+curTicks; while (curTicks < stop_ticks) {}; } int main(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // Дозволяємо тактування порту C із // світлодіодами плати STM32VLDiscovery // PC8 -- blue, PC9 -- green // TIM3 -- використовується в пості "Таймери STM32 -- Автоматична зупинка/CMSIS" RCC->APB1ENR |= RCC_APB1Periph_TIM3; // Дозволяємо тактування TIM3 // Порти світлодіодів - теж на вивід. Звертаємося до старших пінів, тому CRH -- High GPIOC->CRH &= ~GPIO_CRH_CNF8; GPIOC->CRH |= GPIO_CRH_MODE8; GPIOC->CRH &= ~GPIO_CRH_CNF9; GPIOC->CRH |= GPIO_CRH_MODE9; //------------------------------------------------------------------------- TIM3->PSC = 24000-1; // Подільник частоти TIM3->ARR = 5000; // Оновлюємо кожних XXX тіків TIM3->CCR2 = 3000; TIM3->CCER |= (TIM_CCER_CC2E); // Capture/Compare 2 output enable -- PA7 TIM3->CCER &= ~TIM_CCER_CC2P; // Полярність -- active high (Default) //TIM3->CCMR1|=(TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2); // PWM1 -- OCxM=0b110 //TIM3->CCMR1&=~TIM_CCMR1_OC2M_0; TIM3->CCMR1|=(TIM_CCMR1_OC2M_0| TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2); // PWM2 -- OCxM=0b111 TIM3->CCMR1&= ~TIM_CCMR1_CC2S; // Зануляємо -- на вивід. (Default) TIM3->CCMR1|= TIM_CCMR1_OC2PE; // Preload для CCR2, вимагається Datasheet для PWM TIM3->CR1 |= TIM_CR1_ARPE; // Preload для ARR, вимагається Datasheet для PWM TIM3->CR1 &= ~TIM_CR1_DIR; // Відлік -- вверх. (Default) TIM3->CR1 |= TIM_CR1_OPM; // One pulse mode TIM3->CR1 |= TIM_CR1_CEN; // Ініціалізуємо таймер SysTick if (SysTick_Config (TICKS)) { // Помилка -- таймер не ініціалізувався GPIOC->BSRR = GPIO_BSRR_BS8; // Синім позначатимемо помилку while(1); // Зависаємо } while(1) { GPIOC->ODR^=(GPIO_Pin_9); // Зміною стану LED відмічаємо цикл delay_some_us(5000000/3); TIM3->CR1 |= TIM_CR1_CEN; // Запускаємо знову -- завдяки OPM він зупиниться } }
Завершальне слово
Скачати проект із кодом для цього та двох попередніх постів ("Таймери STM32 -- зовнішнє тактування/CMSIS", "Таймери STM32 -- внутрішні тригери/CMSIS"), можна тут.
Код, який стосується лише цього посту, можна знайти у файлі main_OPM.c , в директорії app/ проекту (не включений у проект CoIDE -- щоб не конфліктувати із int main(), котрий містить все зразу).
Немає коментарів:
Дописати коментар