>
Число комментариев: 1
Просмотров: 79

Уроки программирования STM8. Урок 8. STVD + CXSTM8. Основы работы с прерываниями. Теория

В предыдущем занятии мы создали обработчик прерывания IRQ22, для этого понадобилось создать взаимосвязь между таблицей векторов прерываний и кодом основной программы. Таким связующим звеном послужил файл main.h, в котором был определен прототип функции ADC_int_handler - которая и является обработчиком прерывания. Алгоритм работы функции был помещен в main.c, а ссылку на функцию я вписал в таблицу векторов из файла stm8_interrupt_vector.c.

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

Таблица векторов прерываний находится в памяти программ (flash) в диапазоне адресов 0x008000 - 0x00807F. Каждый вектор занимает 4 байта. Всего векторов 32 и под них отведено 128 байт.

Вектор прерывания представляет из себя блок из 4 байт, где первый байт - ассемблерная инструкция INT (машинный код 0x82), а остальные 3 байта - абсолютный 24-битный адрес функции обработчика прерываний.

При вызове прерывания происходит переход по вектору прерывания (при этом программный счетчик PC не меняется), в стек загружаются регистры ядра (счетчик команд, регистры X и Y, аккумулятор, регистр состояния), затем в счетчик команд загружается 24-битный адрес из вектора прерывания. Выход из обработчика прерывания выполняется ассемблерной инструкцией IRET. Если в ассемблерном коде с векторами прерываний все понятно и просто, то в языке Си реализация векторов прерываний выглядит немного ужасающей и непонятной. Но компилятору передаются специальные директивы, указывающие на файл таблицы векторов и все это на основании соглашения помещается как нужно и куда нужно.
Модификаторы типа функции @far @interrupt для void позволяют: @far: получить именно 24-битное значение адреса функции в адресном пространстве программного кода. @interrupt: выполнять выход из функции инструкцией IRET.
Как вы уже догадались, в описании вектора (в структуре таблицы векторов) первый байт 0x82 - есть ни что иное как машинный код инструкции вызова прерывания INT.

_vectab[] - это уже соглашение. Данные именно этого массива компилятор запишет по адресу 0x8000.

_stext - и это соглашение. Это встроенная функция обработки прерывания Reset, "вшитая" в память чипа еще на заводе. Она инициализирует регистры до состояния Default и переносит счетчик команд на адрес 0x8080 : точка входа main().

В программном коде файла есть еще и такая функция:

@far @interrupt void NonHandledInterrupt (void)
{
/* in order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction
*/
return;
}

Это реакция на все прерывания, которые могут возникнуть, но не содержат обработчика. По сути, неожиданные прерывания нежелательны, и когда они возникают, то это говорит о том, что программист где-то что-то неправильно настроил или забыл создать/подключить обработчик прерывания. Должна последовать какая-то реакция вроде выдачи ошибки каким либо способом. Но по-умолчанию указана лишь процедура возврата из прерывания для создания breakpoint в этом месте для отладки - очень полезная вещь.

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

ADC_CSR = 0b00100010;

Выставлен бит 5 - EOCIE - разрешить прерывание по завершению преобразования ADC, и выбран канал 2 (CH[3:0] = 0b0010)

ADC_CR1 |= 0x01; // adc pwr on
ADC_CR1 |= 0x01; // adc start

Как известно из основ работы с АЦП микроконтроллеров STM8, для включения модуля выставляем бит ADON, для первого или повторного запуска преобразования, так же записываем 1 в бит ADON.

_asm("rim"); // Enable Interrupts Global

Ассемблерная вставка глобального включения (разрешения) прерываний. RIM - разрешает прерывания, SIM - запрещает прерывания.

@far @interrupt void ADC_int_handler (void)

Это, непосредственно, описание самого обработчика (или функции обработчика, как угодно)

if (ADC_CSR & (1<<7)) { // if flag is set

Если прерывание возникло с флагом окончания EOC (7-ой бит регистра ADC_CSR) преобразования, то.. Это нужно для того, чтобы отсеять это событие (EOC) от других, которые могут возникнуть в векторе. В данном практическом занятии никаких других прерываний в этом векторе быть не может, однако это иллюстрирует способ сортировки источников прерывания. Вот, например, если разрешить прерывание от AWD, то это событие тоже будет вызывать вектор IRQ22 с выставлением флага AWD.

ADC_CSR &= ~(1<<7); // reset EOC flag

Заключительным этапом обработки прерываний является программный сброс флага EOC.

Таблица всех векторов прерываний контроллеров STM8 Value line и Access line.

Адрес Номер Имя Пояснение
0x8000 - RESET Сброс
0x8004 - TRAP Программное прерывание
0x8008 IRQ_0 TLI Внешнее прерывание высшего уровня
0x800C IRQ_1 AWU Пробуждение из режима HALT
0x8010 IRQ_2 CLK Прерывание модуля тактирования
0x8014 IRQ_3 EXTI0 Внешнее прерывание порта A
0x8018 IRQ_4 EXTI1 Внешнее прерывание порта B
0x801C IRQ_5 EXTI2 Внешнее прерывание порта C
0x8020 IRQ_6 EXTI3 Внешнее прерывание порта D
0x8024 IRQ_7 EXTI4 Внешнее прерывание порта E
0x8028 IRQ_8 - -
0x802C IRQ_9 - -
0x8030 IRQ_10 SPI Окончание передачи SPI
0x8034 IRQ_11 TIM1 Переполнение TIM1
0x8038 IRQ_12 TIM1CC CAPCOM TIM1
0x803C IRQ_13 TIM2 Переполнение TIM2
0x8040 IRQ_14 TIM2CC CAPCOM TIM2
0x8044 IRQ_15 TIM3 Переполнение TIM3
0x8048 IRQ_16 TIM3CC CAPCOM TIM3
0x804C IRQ_17 - -
0x8050 IRQ_18 - -
0x8054 IRQ_19 I2C События интерфейса I2C
0x8058 IRQ_20 UART2TX Завершена передача по UART
0x805C IRQ_21 UART2RX Завершен прием по UART
0x8060 IRQ_22 ADC1 Возник флаг AWD или EOC
0x8064 IRQ_23 TIM4 Переполнение TIM4
0x8068 IRQ_24 FLASH Окончена запись flash EOP/WR_PG_DIS
0x806C - - -
... ... ... ...
0x807C - - -

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