Работаем с простыми таймерами 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);
}

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

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

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий