STM32 Часть 8–Таймеры общего назначения (+прерывания)

15 Января 2011 К комментариям

Хотел заняться АЦП, но похоже что перед этим нужно все таки попробовать в работе любой таймер, проще всего показалось использовать таймер общего назначения (General-purpose).

Основная задача которую будет выполнять счетчик это генерация прерываний через установленный промежуток времени.

Зима

Таймеры общего назначения

В состав микроконтроллера STM32F100RBT6B входят шесть таймеров общего назначения TIM2, TIM3, TIM4, TIM15, TIM16, TIM17 (и один таймер с расширенными настройками TIM1), честно говоря и таймеры общего назначения имеют немало настроек и возможностей.

Таймеры выделены в три группы по набору возможностей:

  • TIM2, TIM3, TIM4
  • TIM15
  • TIM16, TIM17

Все таймеры имеют разрядность 16-бит, однако есть возможность сконфигурировать последовательное соединение двух таймеров (один в режиме “ведущий” другой в режиме “ведомый”).

Приведу для наглядности таблицу из документации просмотреть на офф сайте.

image

На первый взгляд самые простые за номерами 16 и 17, думаю с них и начнем.

Источником тактовых сигналов для таймера могут быть как внешние сигналы, так и сигналы тактового генератора МК:

image

Примечание: похоже на схеме ошибка, так как данный таймер может вести только инкрементный счет, а на схеме указана возможность и декремента.

при этом стоит обратить внимание что тактирование идет не напрямую от генератора, а через цепочку делителей коэффициент деления которых вы выбираете сами:

image

На данном этапе воспользуемся внутренним источником, для этого необходимо разрешить в модуле RCC прохождение тактового сигнала до соответствующего таймера, буду рассматривать таймер 17:

RCC->APB2ENR |= RCC_APB2ENR_TIM17EN;

далее необходимо установить предварительный делитель на необходимый коэффициент деления.

Я хочу использовать таймер для отсчета временных интервалов в 1 мс, а так как я по прежнему использую внутренний RC генератор с частотой 8 МГц, установим предварительный делитель равным 1000, таким образом значения счетчика будут инкрементироваться с частотой 8000 Гц:

TIM17->PSC = 1000 - 1;

Примечание: коэффициент деления равен единице при нулевом значении предварительного делителя, таким образом для получения коэффициента деления N, необходимо установить значения предварительного делителя в N-1.

а максимальное значение до которого будет считать таймер установим равным восьми:

TIM17->ARR = 8 - 1;

После того как таймер достигнет своего максимального значения необходимо что бы он уведомил об этом контроллер прерываний, для этого разрешим генерацию событий:

TIM17->DIER |= TIM_DIER_UIE;

Осталось только разрешить работу таймера и сброс по достижении максимального значения:

TIM17->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE;

Хочется надеяться, что и дальше будет всё так просто ;)

Контроллер прерываний

Описание контроллера прерываний вынесено в отдельный документ, так как он является стандартным блоком ядра Cortex-M3:

STM32F100xx Cortex-M3 programming manual

И не понятно по чему определения вынесены из заголовочного файла stm32f10x.h, пришлось брать заголовочный файл core_cm3.h от CMSIS.

Описание на русском можно почитать на gaw.ru

Я же не буду углубляться в его структуру и логику работы.

Для активации того или иного прерывания необходимо прописать соответствующий вектор в таблицу прерываний, установить приоритет и разрешить его работу.

Все прерывания от таймера 17 сгруппированы в одно событие, при возникновении которого контроллер прерываний сохраняет текущий контекст и передает управление обработчику прерываний.

У данного микроконтроллера к событиям таймера 17, добавлены еще два: от таймера 1 (TIM1 Trigger) и от Commutation (пока не смотрел что это за событие) и привязаны к 26 вектору.

Внесем изменения в нашу таблицу прерываний, т.е. пропишем адрес обработчика:

handler_tim1_trigger_and_communication_and_tim17, // 26

Так как контроллер является стандартным для всех микроконтроллеров с данным ядром, я решил использовать функции для работы с ним прописанные в заголовочном файле core_cm3.h.

Устанавливаем приоритет и разрешаем прерывания:

NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 15);
NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn);

Вроде ни чего не забыл, можно приступать к проверке.

Для проверки пропишем в обработчик небольшую логику:

void handler_tim1_trigger_and_communication_and_tim17(void)
{
	if (TIM17->SR & TIM_SR_UIF)
	{
		TIM17->SR &= ~TIM_SR_UIF;

		if (test_count == 0)
		{
			PIN_ON(LED_GREEN);
		}
		else if (test_count == 1000)
		{
			PIN_OFF(LED_GREEN);
		}

		if (++test_count >= 2000)
			test_count = 0;
	}
}

При входе в прерывания необходимо проверить источник и сбросить флаг (по крайней мере для таймера 17 указанно что данный флаг сбрасывается программно). Далее для теста я завел глобальную переменную test_count при каждом вхождении инкрементирую её и проверяю на ряд значений (0, 1000, 2000), при нулевом значении включаю зеленый светодиод, при значении 1000 выключаю и при достижении 2000 обнуляю. Задумка - просто мигать зеленым светодиодом с частотой 0,5 Гц.


Всю необходимую информацию вы можете найти на офф стайте:

STM32F100RB - STM32 Value Line 32-bit Microcontrollers

Проект с примером:

2011-01-02_tim17_interrupt



comments powered by Disqus