Работаем с простыми таймерами STM32 F4 discovery

В любом современном контроллере есть таймеры. В этой статье речь пойдёт о простых (базовых) таймерах stm32f4 discovery.
Это обычные таймеры. Они 16 битные с автоматической перезагрузкой. Кроме того имеется 16 битный программируемый делитель частоты. Есть возможность генерирования прерывания по переполнению счётчика и/или запросу DMA.

Приступим. Как и раньше я пользуюсь Eclipse + st-util в ubuntu linux

Первым делом подключаем заголовки:

#include <stm32f4xx.h>
#include <stm32f4xx_rcc.h>
#include <stm32f4xx_gpio.h>
#include <stm32f4xx_tim.h>
#include <misc.h>
Ничего нового в этом нет. Если не ясно откуда они берутся либо читайте предыдущие статьи, либо открывайте файл и читайте.

Определим две константы. Одну для обозначения диодов, другую массив из техже диодов:

const uint16_t LEDS = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;  // все диоды
const uint16_t LED[] = {GPIO_Pin_12, GPIO_Pin_13, GPIO_Pin_14, GPIO_Pin_15};  // массив с диодами

Скорее всего уже знакомая вам функция-инициализации периферии (то есть диодов) :

void init_leds(){
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);  // разрешаем тактирование
    GPIO_InitTypeDef gpio;   // структура
    GPIO_StructInit(&gpio);  // заполняем стандартными значениями
    gpio.GPIO_OType = GPIO_OType_PP;   // подтяжка резисторами
    gpio.GPIO_Mode = GPIO_Mode_OUT;  // работаем как выход
    gpio.GPIO_Pin = LEDS;   // все пины диодов
    GPIO_Init(GPIOD, &gpio);

Функция инициализатор таймера:

void init_timer(){
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // включаем тактирование таймера
   /* Другие параметры структуры TIM_TimeBaseInitTypeDef
   * не имеют смысла для базовых таймеров.
   */

    TIM_TimeBaseInitTypeDef base_timer;
    TIM_TimeBaseStructInit(&base_timer);
  /* Делитель учитывается как TIM_Prescaler + 1, поэтому отнимаем 1 */
    base_timer.TIM_Prescaler = 24000 - 1; // делитель 24000
    base_timer.TIM_Period = 1000; //период 1000 импульсов
    TIM_TimeBaseInit(TIM6, &base_timer);

  /* Разрешаем прерывание по обновлению (в данном случае -
   * по переполнению) счётчика таймера TIM6.
   */

    TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM6, ENABLE);  // Включаем таймер

  /* Разрешаем обработку прерывания по переполнению счётчика
   * таймера TIM6. это же прерывание
   * отвечает и за опустошение ЦАП.
   */

    NVIC_EnableIRQ(TIM6_DAC_IRQn);
}

Я прокомментировал код, так-что думаю всё ясно.
Ключевыми параметрами тут являются делитель (TIM_Prescaler) и период (TIM_Period) таймера. Это параметры, которые собственно и настраивают работу таймера.

К примеру, если у вас на STM32F4 DISCOVERY тактовая частота установлена в 48МГц, то на таймерах общего назначения частота 24МГц. Если установить делитель (TIM_Prescaler) в 24000 (частота счёта = 24МГц/24000 = 1КГц), а период (TIM_Period) в 1000, то таймер будет отсчитывать интервал в 1с.

Обратите внимание, что всё зависит от тактовой частоты. Её вы должны выяснить точно.

Так же отмечу, что на высоких частотах переключение светодиода по прерыванию существенно искажает значение частоты. При значении в 1МГц на выходе я получал примерно 250КГц, т.е. разница не приемлима. Такой результат видимо получается из-за затрат времени на выполнение прерывания.

Глобальная переменная - флаг горящего диода:

u16 flag = 0;

Обработчик прерывания, которое генерирует таймер. Т.к. этоже прерывание генерируется и при работе ЦАП, сначала проверяем, что сработало оно именно от таймера:

void TIM6_DAC_IRQHandler(){

  /* Так как этот обработчик вызывается и для ЦАП, нужно проверять,
   * произошло ли прерывание по переполнению счётчика таймера TIM6.
   */
    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) {
      flag++;
      if (flag>3) flag = 0;
    /* Очищаем бит обрабатываемого прерывания */
      TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
      GPIO_Write(GPIOD, LED[flag]); // зажигаем слудующий диод
    }
}

Функция main:

int main(){
      init_leds();
      init_timer();
  do {
  } while(1);
}

Цикл оставляем пустым. Счётчик выполняет свою работу асинхронно, а прерывание на то и прерывание, чтобы не зависеть от выполняемой в данный момент операции.

Скачать проект.

Просмотров:   32130

Комментарии

чт, 11/22/2012 - 14:11

По поводу полей в структуре:

    base_timer.TIM_Prescaler = 24000000 - 1; // делитель 24000000
    base_timer.TIM_Period = 1000;

Первая - делитель частоты. Задаёт частоту счётных импульсов.

Вторая - Указывает сколько счётных импульсов нужно отсчитать до события TIM_FLAG_Update

В примере частота будет 84МГц/24МГц = 3,5Гц

А период обновления счётчика 1/3,5 * 1000 = 285 мс

чт, 11/22/2012 - 14:11

каким образом делитель 24000000, если разешено использовать целочисленный 16битный регистр,

вт, 03/19/2013 - 21:57
Михаил

Присоединяюсь к вопросу [serega222], объясните пожалуйста?

чт, 03/21/2013 - 11:20

после переезда сайта на другой движок пропал комент с объяснением.

Да действительно, это ошибка. Записать можно максимум 0xFFFF.

Исправляю.

ср, 02/26/2014 - 13:59

Здравствуйте! Пробую этот код на stm32f4discovery под IAR 6.3. Чудесно компилируется и прошивается, но при выполнении отладчиком строки 26: NVIC_EnableIRQ(TIM6_DAC_IRQn) контроллер уходит в некий бесконечный цикл навсегда (постоянно мигает зелено-красным диодом). Подскажите пожалуйста в чём может быть причина такого неприятного зависания? Гугление вывело на необходимость подключения startup_stm32f4xx.s. Однако и это не спасает (может подключал как-то криво). Перепробовал массу аналогичных кодов на различные таймеры. Эффект тот же. При попытке включить одновременно таймер и прерывания повисает. Подскажите решенице пожалуйста!

чт, 02/27/2014 - 07:00

Лечение найдено! Нужно переключить проект с режима С++ на режим С и подключить startup_stm32f4xx.s и всё заработает!

пн, 06/30/2014 - 11:45
Егор

А не проще поменять делитель на реальные "24 000", и при делении 24 млн на 24 тысячи вы получите реальные 1000 тиков в секунду, что есть 1мс... просто и прозрачно.

Добавить комментарий

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.
CAPTCHA
Введи эти символы. Ато роботы одолели!