В предыдущем занятии мы создали обработчик прерывания 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 |
- |
- |
- |
В этом практическом занятии и в этой пояснительной статье вы получили минимум знаний, которых вполне достаточно, чтобы начать работать с прерываниями на базовом уровне. Более продвинутый уровень будет дан через какое-то время, когда в нем возникнет необходимость и когда закрепятся самые азы.