В предыдущем занятии мы создали обработчик прерывания 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().
В программном коде файла есть еще и такая функция:
Это реакция на все прерывания, которые могут возникнуть, но не содержат обработчика. По сути, неожиданные прерывания нежелательны, и когда они возникают, то это говорит о том, что программист где-то что-то неправильно настроил или забыл создать/подключить обработчик прерывания. Должна последовать какая-то реакция вроде выдачи ошибки каким либо способом. Но по-умолчанию указана лишь процедура возврата из прерывания для создания breakpoint в этом месте для отладки - очень полезная вещь.
Теперь, когда у вас есть базовое понимание механизма срабатывания функций обработки прерываний, можно приступить к разбору кода из практического занятия.
Выставлен бит 5 - EOCIE - разрешить прерывание по завершению преобразования ADC, и выбран канал 2 (CH[3:0] = 0b0010)
Как известно из основ работы с АЦП микроконтроллеров STM8, для включения модуля выставляем бит ADON, для первого или повторного запуска преобразования, так же записываем 1 в бит ADON.
Ассемблерная вставка глобального включения (разрешения) прерываний. RIM - разрешает прерывания, SIM - запрещает прерывания.
Это, непосредственно, описание самого обработчика (или функции обработчика, как угодно)
Если прерывание возникло с флагом окончания EOC (7-ой бит регистра ADC_CSR) преобразования, то.. Это нужно для того, чтобы отсеять это событие (EOC) от других, которые могут возникнуть в векторе. В данном практическом занятии никаких других прерываний в этом векторе быть не может, однако это иллюстрирует способ сортировки источников прерывания. Вот, например, если разрешить прерывание от AWD, то это событие тоже будет вызывать вектор IRQ22 с выставлением флага AWD.
Заключительным этапом обработки прерываний является программный сброс флага 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 | - | - | - |
В этом практическом занятии и в этой пояснительной статье вы получили минимум знаний, которых вполне достаточно, чтобы начать работать с прерываниями на базовом уровне. Более продвинутый уровень будет дан через какое-то время, когда в нем возникнет необходимость и когда закрепятся самые азы.