Вектора прерываний в C++ стиле

Хочу представить на суд широкой общественности (да и самому не потерять) мою реализацию таблицы векторов прерываний для arm микроконтроллеров STM32. Данная реализация генерируется из asm-файлов SPL, написана на С++ и облегчает работу поддержкой автодополнений и корректной работой с таблицей векторов в памяти.


Уже полгода пишу код для МК только на С++11.

Дело и в шаблонах, и в constexpr функциях, и в ссылках, и в лямбда-выражениях, которые настолько удобны для асинхронного кода, насколько это вообще может быть.

И, конечно, startup*.asm файлы ну никак не вписываются в стройную концепцию данного подхода.

Я сделал простенькую програмку для того, чтобы переводить asm файлы в удобные пары cpp/h.
Всё, что связано с прерываниями я убрал в namespace ISR.
В результате мы получаем структуру ISR::Vectos, которая пишется в секцию .isr_vector при линковке и содержит ссылки на все вектора прерываний.
Вектора объявлены как void ISR::USART1 (void) и имеют слабый(weak) алиас на void ISR::DefaultHandler(), на который будет скомпилирована ссылка в векторе, если прерывание не будет объявлено в проекте явно.
void ISR::USART1_IRQ()
{
    if(USART1->SR & USART_SR_TC)
    {
        USART1->CR1 &= ~USART_CR1_TCIE; // Transmission complete interrupt disable
        RS485::WriteOff(); // turn off RS485 transmitter
        USART::sendBusyFlag = false; // tell the system that transmition is end<s></s>
    }
}

Также данный подход позволяет очень удобно оперировать с таблицей векторов прерываний в оперативке.
При помощи препроцессора может быть сгенерирована короткая таблица векторов прерываний, содержащая только ссылку на ISR::Reset(), где уже в оперативку будет перенесена истинная таблица прерываний, которую можно будет легко редактировать прямо по ходу программ.
Для этого надо раскомментировать
//#define USE_MEMORY_ISR

и после этого можно писать выражения типа

void timHandler1()
{
    //code of handler1;
}
void timHandler2()
{
    //code of handler2;
}


//...
ISR::TIM7_IRQ = handler1; // Теперь при прерывании будет вызываться handler1
//...
ISR::TIM7_IRQ = handler2; // Теперь при прерывании будет вызываться handler2


и то же самое на лямда-выражениях

ISR::TIM7_IRQ = []{//code of handler1;}; // Теперь при прерывании будет вызываться handler1
//...
ISR::TIM7_IRQ = []{//code of handler2;}; // Теперь при прерывании будет вызываться handler2


Коротко о том, как файлы устроенны:
ISRstm32f2xx.h
/*********************************************************
 * ISRstm32f2xx.h
 * Automatic generated header file for MCU stm32f2xx
 **/
#ifndef INTERRUPTS_H
#define INTERRUPTS_H
#include <stdint.h>
extern uint32_t _estack; // Переменная, которую подставляет линковщик. Определяется в ld скрипте
//#define USE_MEMORY_ISR // Раскоментируя это, мы получаем таблицу векторов прерываний в оперативке

extern "C"
{
    namespace ISR
    {
        void DefaultHandler(); // Объявление обработчика по-умолчанию для прерываний
        struct ShortVectors // Объявляем короткую структуру таблицы векторов прерываний для инициализации в случае расположения основной таблици в памяти
        {
            uint32_t estack;
            void (*Reset )(void);
        };

        struct Vectors //Структура, которая содержит ссылки на прерывания - сама таблица векторов.
        {
            uint32_t estack;
            void (*Reset)               (void); // Прерывание Reset
            void (*NMI)                 (void); // немаскируемое прерывание
        ...............                         // Дальше идут ещё куча прерываний, которые можно глянуть в исходниках
            void (*__unused0[4])        (void); // Иногда в таблице прерываний есть пропуски. Тут они выглядят так
        ...............                         // Ещё куча прерываний, которые можно глянуть в исходниках
        };
        void Reset                (void); // Объявляю прототип для функции
        ...............                         // Ещё куча прототипов
    }
}
#endif // INTERRUPTS_H

ISRstm32f2xx.cpp
/*********************************************************
 * ISRstm32f2xx.cpp
 * Automatic generated source file for MCU stm32f2xx
 *
 *
 **/
#include "ISRstm32f2xx.h"
__attribute__((naked, noreturn)) void ISR::DefaultHandler() //Реализация вектора по-умолчанию. naked и noreturn нужны для минимализации кода. По хорошему сюда вообще программа никогда попадать не должна, и этот код - плата за удобство.
{
   for(;;);
}


#ifdef USE_MEMORY_ISR
__attribute__((section(".isr_vector"))) const ISR::ShortVectors interruptsVectorTable =
#else
__attribute__((section(".isr_vector"))) const ISR::Vectors interruptsVectorTable =
#endif
{
#ifdef USE_MEMORY_ISR
   _estack,
   ISR::Reset
};

__attribute__((section(".isr_vector2"))) ISR::Vectors MeminterruptsVectorTable =
{
#endif
   _estack,
   ISR::Reset, //заполняю списком инициализации структуру основной таблицы прерываний
 ...... // И ещё туча прерываний
   {0, 0, 0, 0}, // Про пропуски не забываем и тут
};
#pragma weak Reset                = DefaultHandler // создание слабого алиаса нункции Reset на DefaultHandler. Алиас будет перезаписан перывм же явным объявлением
 ...... // Ещё куча алиасов, например
#pragma weak UART4_IRQ            = DefaultHandler //прерывание для USART4
 ...... // и так до конца кода
 


Сами cpp/h файлы можно глянуть также на гитхабе: тут

Код преобразователя (C++, Qt) там же, но код уродлив, писался для обучения человека плохому.
  • +6
  • 11 ноября 2014, 17:25
  • dekar

Комментарии (16)

RSS свернуть / развернуть
Это пять! =)
0
Вектора прерываний с C++ стиле
С какому стилем?
0
Fixed.
0
Лучше «… в стиле C++»
+1
По просьбе перенёс в STM
0
И читается хорошо.

А почему #pragma weak? Атрибута weak нет?
0
__attribute__((weak)) не нужен, если есть #pragma.
А через аттрибут алиас не задать.
0
У Вас же символ повляется в заголовке — прототип вектора. Может для понятности вынести прагмы туда?
Хотя я бы лично воспользовался __attribute__ ((weak, alias («foo»))); Насколько я помню разработчики GCC рекомендуют не пользоваться прагмами. Да и смотреться будет органично, ведь для всего остального у Вас используются аттрибуты, а не прагмы.
Но дело вкуса наверное, не больше :)
0
__attribute__ ((weak, alias («foo»))); — это да. Это пять.
Так и сделаю.
0
А тогда оно для IAR работать не будет.
0
Каким компилятором можно пользоваться?
0
В текущем виде работает и для IAR, согласно слыке
0
А вообще писалось для GCC
0
Пользуетесь ли try/catch, виртуальными функциями? Реализовывали ли аллокатор памяти?

Используется линковщик gcc или g++?
0
Скажу даже больше: не пользуюсь ключевым словом class.
Для МК в этом (в отличие от хорошего препроцессора с контролем типов и компил-тайм выражениями) мало смысла.
Линкуюсь gcc.
+1
всё ясно
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.