STM8L-Контроллер прерываний + Таймеры общего назначения

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

Потихоньку переходим к системе прерываний и таймерам.

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

Возможности:

  • управление прерываниями от периферийных модулей
  • управление внешними прерывания от линий ввода-вывода (индивидуальный вектор на порт и индивидуальный флаг на линию ввода-вывода)
  • управление программным прерыванием (команда TRAP)
  • до четырех программных уровней для маскируемых прерываний
  • два не маскируемых прерывания (сброс и программное прерывание)
  • до тридцати маскируемых прерываний с «жестким» приоритетом
  • и много ещё чего :) см. документацию

Далее в моем понимание логика работы.

Всё базируется на таблице векторов прерываний. Таблица располагается в начале флеш-памяти (адрес 0x00800000), один вектор занимает 4 байта. Самый первый вектор располагающийся по адресу 0x8000 отведен под «начальную» точку входа, он обрабатывается автоматически при сбросе микроконтроллера. Приоритет у него самый высокий. Второе место отдано под программное прерывание (ассемблерная команда TRAP), оно так же как сброс не маскируется. Далее располагаются 30 аппаратных векторов прерываний.

Для наглядности вся таблица прерываний:

struct interrupt_vector const _vectab[] = {
	{0x82, (interrupt_handler_t)_stext}, /* reset */
	{0x82, NonHandledInterrupt}, /* trap  */
	{0x82, NonHandledInterrupt}, /* irq0  */
	{0x82, NonHandledInterrupt}, /* irq1  */
	{0x82, NonHandledInterrupt}, /* irq2  */
	{0x82, NonHandledInterrupt}, /* irq3  */
	{0x82, NonHandledInterrupt}, /* irq4  */
	{0x82, NonHandledInterrupt}, /* irq5  */
	{0x82, NonHandledInterrupt}, /* irq6  */
	{0x82, NonHandledInterrupt}, /* irq7  */
	{0x82, NonHandledInterrupt}, /* irq8  */
	{0x82, NonHandledInterrupt}, /* irq9  */
	{0x82, NonHandledInterrupt}, /* irq10 */
	{0x82, NonHandledInterrupt}, /* irq11 */
	{0x82, NonHandledInterrupt}, /* irq12 */
	{0x82, NonHandledInterrupt}, /* irq13 */
	{0x82, NonHandledInterrupt}, /* irq14 */
	{0x82, NonHandledInterrupt}, /* irq15 */
	{0x82, NonHandledInterrupt}, /* irq16 */
	{0x82, NonHandledInterrupt}, /* irq17 */
	{0x82, NonHandledInterrupt}, /* irq18 */
	{0x82, NonHandledInterrupt}, /* irq19 */
	{0x82, NonHandledInterrupt}, /* irq20 */
	{0x82, NonHandledInterrupt}, /* irq21 */
	{0x82, NonHandledInterrupt}, /* irq22 */
	{0x82, NonHandledInterrupt}, /* irq23 */
	{0x82, NonHandledInterrupt}, /* irq24 */
	{0x82, handler_tim4}, /* irq25 */
	{0x82, NonHandledInterrupt}, /* irq26 */
	{0x82, NonHandledInterrupt}, /* irq27 */
	{0x82, NonHandledInterrupt}, /* irq28 */
	{0x82, NonHandledInterrupt}, /* irq29 */
};

Примечание. Пока прописан только один вектор (TIM4), на место не используемых я прописал заглушку.

Приоритет прерываний убывает с ростом номера вектора, т. е. максимальный приоритет у вектора по адресу 0x8000 и минимальный у вектора по адресу 0x807С. Кроме этого предусмотрена возможность задания программного приоритета, с числом уровней до 4. Для этого под каждый из 30 векторов выделено два бита, которые позволяют установить приоритет в соответствии с таблицей:

image

Примечание. Обратите внимание что установить приоритет (1;0) записью двух битов (1;0) нельзя, необходимо обнулить оба бита и только потом установить (1;0).

В совокупности наличие программных и аппаратных приоритетов позволяет очень гибко настраивать под свои нужды обработку прерываний. Более наглядно понять процесс можно анализируя ниже приведенный рисунки:

image

image

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

Для примера рассмотрим обработчик прерывания от таймера 4.

Прописываем обработчик в таблицу прерываний:

{0x82, handler_tim4}, /* irq25 */

Программный приоритет:

ITC->ISPR6 &= ~(BIT(6) | BIT(7));
ITC->ISPR6 |= BIT(6) | BIT(7);

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

enableInterrupts();

Ну и не забыть разрешить генерацию событий в настройках таймера 4 (см. ниже).

Таймеры

В состав микроконтроллера входит пять таймеров таймеров: один таймер с расширенными настройками TIM1, три общего назначения TIM2, TIM3, TIM5 и один базовый таймер TIM4. Что бы понять «крутизну» первого таймера достаточно сравнить количество страниц которое потребовалось для описания его возможностей (87 стр) с количеством страниц для таймера 4 (9 стр). :)

Сводная таблица:

image

Как обычно начнем с самого простого, т. е. с таймера TIM4.

Посмотрев на его структурную схему понимаем что он действительно прост ;)

image

Но тем не менее данный восьми битный счетчик позволяет:

  • работать в качестве ведомого (ведущим может быть любой из оставшихся)
  • имеет встроенный предварительный делитель, с коэффициентом деления от 1 до 32768 (к сожалению коэффициент задается степенью двойки, т. е. от 0 до 15)
  • имеет регистр предварительной загрузки
  • генерировать события по переполнению (или перезагрузке)
  • генерировать события для контроллера ПДП (DMA)

В качестве примера рассмотрим настройку таймера для генерации событий каждые 4 мс (частота 250 Гц).

Вначале разрешим тактирование модуля:

CLK->PCKENR1 |= CLK_PCKENR1_TIM4;

Далее установим предварительный делитель на максимально возможное значение, нехитрый расчет показывает, что при тактовой частоте 2 МГц (не забываем, что мы по прежнему работает на этой частоте) кратными коэффициенты для частоты 250 Гц получаются такими:

  • предварительный 2000000/32 = 6250 Гц
  • значение для регистра предварительной загрузки 6250/25 = 250 Гц

Прописываем:

TIM4->PSCR = 5; // 2^5
TIM4->ARR = 25 - 1;

Разрешаем генерацию событий:

TIM4->IER = TIM_IER_UIE;

Разрешаем предварительную загрузку и работу самого таймера:

TIM4->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;

В обработчике необходимо сбросить бит в регистре статуса:

@far @interrupt void handler_tim4(void)
{
	TIM4->SR1 &= (uint8_t)(~TIM_SR1_UIF);

	PIN_TOGGLE(LED_GREEN);
}

Для проверки использую зеленый светодиод, просто мигаю им (замечу что 250 Гц для глаза не заметно, поэтому можно использовать осциллограф или просто снизить частоту генерации прерываний или завести программный делитель…)

Частоту в 250 Гц я выбрал только для более точной фильтрации помехи 50 Гц от помехи простым цифровым фильтром - скользящее среднее, при использовании АЦП.

Ну вот на этом пока все, хочу спать :)

Пишите комментарии или на форуме.

P/S Пока не нашел способа выводить список статей в удобном формате, поэтому если вы что-то не увидели попробуйте воспользоваться поиском (в меню справа;))



comments powered by Disqus