пʼятниця, 11 березня 2016 р.

Таймери STM32 -- внутрішні тригери/CMSIS

Взято тут.
Як вже не раз згадувалося, таймери можуть постачати тактування один одному. Наприклад, один таймер може служити подільником для іншого -- переповнення головного (master) таймера приводить до одного відліку підлеглого (slave). Це дозволяє зробити із двох 16-бітних таймерів один 32-бітний. Хоча, комбінацій master-slave є багато більше -- як підлеглий таймер може реагувати на сигнал, описувалося тут (мова про біти SMS), а за яких умов головний (master) сигнал посилає -- описано нижче.

Подивимося, як користуватися цією можливістю на дещо надуманому прикладі -- мигатимемо світлодіодом у перериванні від TIM1, який буде підлеглим для TIM4.

На відміну від зовнішнього тактування,  розглянутого раніше, тут використано тактування від внутрішніх тригерів -- ITRx.

Як і раніше, почати слід із ініціалізації тактування портів та таймерів:

 RCC->APB2ENR  |= RCC_APB2ENR_IOPCEN; // Дозволяємо тактування порту C

 RCC->APB1ENR  |= RCC_APB1Periph_TIM4; // Дозволяємо тактування TIM4
 RCC->APB2ENR  |= RCC_APB2Periph_TIM1; // Дозволяємо тактування TIM1

 // PC8 -- blue LED
 GPIOC->CRH &= ~GPIO_CRH_CNF8;
 GPIOC->CRH  |= GPIO_CRH_MODE8;

Процедура обробки переривання -- така ж, як в попередньому пості:

void TIM1_UP_TIM16_IRQHandler(void)
{
 if( TIM1->SR & TIM_SR_UIF ) // Перевіряємо джерело
 {
  TIM1->SR &= ~TIM_SR_UIF; //Очищаємо прапорець переривання
  GPIOC->ODR^=(GPIO_Pin_8); //Змінюємо стан світлодіода
 }
}

Потім налаштовуємо TIM4:

 TIM4->PSC = 0; // Подільник -- 1 (0+1)
 TIM4->ARR = 24000 ;     // На частоті 24МГц, тік -- раз на 1 мс

 // MMS: Master mode selection -->
 // 010: Update - The update event is selected as a trigger output (TRGO).
 TIM4->CR2 &= ~( TIM_CR2_MMS_0 | TIM_CR2_MMS_2 );
 TIM4->CR2 |=  ( TIM_CR2_MMS_1 );

Новою частиною є налаштування бітів MMS регістра CR2 -- вказується, що за переповнення слід генерувати сигнал тригера для підлеглого таймера -- TRGO.

Таймер буде генерувати тактовий сигнал для підлеглого таймера раз на мілісекунду -- подільник 1 (нагадаємо, він отримується додаванням 1 до вмісту PSC), оновлення кожних 24000 тіки, при частоті 24МГц.
Взагалі, варіанти MMS для таймерів STM32F100 і таймерів загального використання STM32F303, наступні:
  • 000 -- Reset, біт UG з TIMx_EGR, встановлення котрого примусово створює подію оновлення, служить джерелом TRGO.
  • 001 -- Enable, запуск таймера служить сигналом. Використовується для синхронного запуску кількох таймерів.
  • 010 -- Update, використаний нами -- оновлення таймера генерує TRGO (не плутати із Reset -- примусовим оновленням!)
  • 011 -- Compare Pulse, генерується, коли чергова подія оновлення готується встановити відповідний прапорець переривань, CC1IF (навіть якщо він вже був встановленим).
  • 100/101/110/111 -- Compare, сигнал на OC1/2/3/4REF відповідно, служить командою подати TRGO.
Для просунутих таймерів STM32F303, які мають два TRGO, згадані вище біти MMS такі ж, але присутнє ще 4-бітове поле MMS2 -- в TRGO2 є всі режими TRGO1 (мають той же код, що і 3-бітовий вище, із нульовим старшим бітом) та ряд додаткових режимів:
  • 1000/1001 -- Compare для додаткових каналів, OC5/6REF 
  • 1010..1111 -- Compare Pulse, всілякі хитрі комбінації захоплення для 4-6 каналів, див. документацію (типу обох фронтів сигналу 4-го каналу, або зростання 4-го і спад 6-го, і т.д.).
Для TIM1 -- підлеглого (slave), конфігурація трохи складніша, але починається звично:

 TIM1->PSC = 0;  // Подільник -- 1 (0+1)
 TIM1->ARR = 1000;     // Переривання -- раз на 1с
 TIM1->DIER |= TIM_DIER_UIE; // Дозволяємо переривання

Параметри підібрано так, щоб переривання виникало раз на секунду.

Наступним кроком слід вказати, звідки отримувати тактові імпульси:

 // TS[2:0]: Trigger selection, 011: Internal Trigger 3 (ITR3) -- TIM4
 TIM1->SMCR &= ~( TIM_SMCR_TS_2 );
 TIM1->SMCR |=  ( TIM_SMCR_TS_1 | TIM_SMCR_TS_0 );

Щоб зрозуміти код вище, нам знадобиться таблички із документації, із назвою "TIMx internal trigger connection". Для TIM1, єдиного просунутого таймера STM32F100RBT6B, вона зовсім коротенька:

Тобто, TIM4 може бути під'єднаним до ITR3 TIM1.

Нарешті, встановлюємо вже обговорені в попередньому пості біти SMS:

  // 111: External Clock Mode 1 -
 // Rising edges of the selected trigger (TRGI) clock the counter.
 TIM1->SMCR |= TIM_SMCR_SMS;

Все, можна запускати таймери та насолоджуватися миганням світлодіода:

  TIM1->CR1 |= TIM_CR1_CEN;   // Дозволити slave-таймеру працювати
 NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); // Дозволити відповідне переривання

 TIM4->CR1 |= TIM_CR1_CEN;   // Дозволити master-таймеру працювати


main.c


#include <stm32f10x.h>

#ifdef __cplusplus
extern "C"{
#endif

void TIM1_UP_TIM16_IRQHandler(void)
{
 if( TIM1->SR & TIM_SR_UIF ) // Перевіряємо джерело
 {
  TIM1->SR &= ~TIM_SR_UIF; //Очищаємо прапорець переривання
  GPIOC->ODR^=(GPIO_Pin_8); //Змінюємо стан світлодіода
 }
}

#ifdef __cplusplus
}
#endif

int main(void)
{
 RCC->APB2ENR  |= RCC_APB2ENR_IOPCEN; // Дозволяємо тактування порту C із
       // світлодіодами плати STM32VLDiscovery
       // PC8 -- blue, PC9 -- green

 // TIM1 та TIM4 використовуються в пості "Таймери STM32 -- внутрішні тригери/CMSIS"
 RCC->APB1ENR  |= RCC_APB1Periph_TIM4; // Дозволяємо тактування TIM4
 RCC->APB2ENR  |= RCC_APB2Periph_TIM1; // Дозволяємо тактування TIM1


 GPIOA->CRL &= ~GPIO_CRL_CNF7_0; // 10 -- AF PP (Alternative function -- push-pull)
 GPIOA->CRL |=  GPIO_CRL_CNF7_1;
 GPIOA->CRL  |= GPIO_CRL_MODE7; //Встановити обидва біти MODE для піна 1 -- швидкість 50MHz

 // PC8
 GPIOC->CRH &= ~GPIO_CRH_CNF8;
 GPIOC->CRH  |= GPIO_CRH_MODE8;


 //-------------------------------------------------------------------------
 // TIM1 смикатиметься TIM4.
 TIM4->PSC = 0; // Подільник -- 1 (0+1)
 TIM4->ARR = 24000 ;     // На частоті 24МГц, тік -- раз на 1 мс

 // MMS: Master mode selection -->
 // 010: Update - The update event is selected as a trigger output (TRGO).
 TIM4->CR2 &= ~( TIM_CR2_MMS_0 | TIM_CR2_MMS_2 );
 TIM4->CR2 |=  ( TIM_CR2_MMS_1 );

 TIM1->PSC = 0;  // Подільник -- 1 (0+1)
 TIM1->ARR = 1000;     // Переривання -- раз на 1с
 TIM1->DIER |= TIM_DIER_UIE; // Дозволяємо переривання

 // TS[2:0]: Trigger selection, 011: Internal Trigger 3 (ITR3) -- TIM4
 TIM1->SMCR &= ~( TIM_SMCR_TS_2 );
 TIM1->SMCR |=  ( TIM_SMCR_TS_1 | TIM_SMCR_TS_0 );

 // 111: External Clock Mode 1 -
 // Rising edges of the selected trigger (TRGI) clock the counter.
 TIM1->SMCR |= TIM_SMCR_SMS;

 TIM1->CR1 |= TIM_CR1_CEN;   // Дозволити slave-таймеру працювати
 NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); // Дозволити відповідне переривання

 TIM4->CR1 |= TIM_CR1_CEN;   // Дозволити master-таймеру працювати

 //-------------------------------------------------------------------------

    while(1)
    {
    }
}




Завершальне слово


Скачати проект із кодом для цього, попереднього ("Таймери STM32 -- зовнішнє тактування/CMSIS") та наступного ("Таймери STM32 -- Автоматична зупинка/CMSIS") постів можна тут.

Код, який стосується лише цього посту, можна знайти у файлі main_internal_trig.c,  в директорії app/ проекту (не включений у проект CoIDE -- щоб не конфліктувати із int main(), котрий містить все зразу).

Немає коментарів:

Дописати коментар