Ведение
Попав в отпуске в город на Неве и посетив множество красивых мест, я все таки, вечерами за чашкой пива, разбирался с UARTом. Тем более, что я купил не плохие наушники Fisher FA011, к которым пришлось прикупить USB SOUND BLASTER X-FI HD и хотел послушать музыку.
Предыдущие статьи вначале переехали на Geektime потом я обратно их перегнал, даже и не знаю, куда теперь их деть :)
Но так на всякий случай они тут:
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 2 и
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 3 (LCD и Экраны)
UART
После детального изучения микроконтроллера, мне казалось, что все просто. Настройка и тестовая посылка байта в порт прошла без задоринки, все работало как часы, и тут я решил использовать прерывания. Нужно было сделать так, чтобы обработчик прерывания был статическим методом класса. И IAR в руководстве на компилятор, так и писал:
Special function types can be used for static member functions. For example, in the
following example, the function handler is declared as an interrupt function:class Device { static __irq void handler(); };
Но вот незадача, для Cortex M такой способ не подходит и
On ARM Cortex-M, an interrupt service routine enters and returns in the same way as a
normal function, which means no special keywords are required. Thus, the keywords
__irq, __fiq, and __nested are not available when you compile for ARM Cortex-M.
These exception function names are defined in cstartup_M.c and cstartup_M.s.
They are referred to by the library exception vector code:
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
…
The vector table is implemented as an array. It should always have the name
__vector_table,
Или по простому, ваш обработчик прерывания должен иметь такое же имя, какое он имеет в таблице векторов определенной в startup файле. Это делается с помощью специального ключевого слова — слабой ссылки __weak (в ассемблере PUBWEAK), которая означает, что данное определение будет использоваться до тех пора, пока не найдется хотя бы одно совпадающее по написанию без ключевого слова __week. Ну т.е., если вы определите функцию с точно таким же именем без этой директивы, то компилятро будет использовать это определение, а если не определите, то которое помечено __weak.
Понятное дело, что я не могу в файл startup_stm32l1xx_md.s или startup_stm32l1xx_md.с вставить С++ имя статического метода типа cUart::USART2_IRQHandler(), ассемблер его просто не поймет.
А просто «USART2_IRQHandler» не совпадает с определением «cUart::USART2_IRQHandler()».
Можно использовать extern «C» { void USART2_IRQHandler(void) {...}}, но это означает, что я тут буду делать встаки из Си, что мне совсем не надо, и вообще доступа из такой функции к атрибутам моего класса, например буферу — не будет, и надо будет городить кучу некрасивого кода :).
Поэтому, я решил пойти другим путем и создать файл startup_stm32l1xx_md.cpp. Поиск в интернете обнаружил, что точно такая же проблема была у некоторых людей
В общем идея заключается в следующем: Определяем startup_stm32l1xx_md.cpp в классы со статическими методами (которые и будут являться обработчиками прерываний), создаем таблицу __vector_table, где на каждом из векторов прерываний стоит указатель на на эти статические методы. Дальше делаем __weak определение каждого метода
И теперь когда в коде компилятор видет реализацию void cUart1::handler(), он не задумываясь берет её. Конечно же при этом ваши классы и методы должны называться точь в точь так, как они определены в startup_stm32l1xx_md.cpp.
Нужно еще не забыть про функции FreeRtos: vPortSVCHandler, xPortPendSVHandler, xPortSysTickHandler и поставить их на нужное прерывание и вуаля — все работает:
#pragma language = extended
#pragma segment = "CSTACK"
extern "C" void __iar_program_start( void );
extern "C" void vPortSVCHandler(void);
extern "C" void xPortPendSVHandler(void);
extern "C" void xPortSysTickHandler(void);
class cNMI
{
public:
static void handler(void);
};
class cHardFault
{
public:
static void handler(void);
};
class cMemManage
{
public:
static void handler(void);
};
class cBusFault
{
public:
static void handler(void);
};
class cUsageFault
{
public:
static void handler(void);
};
class cDebugMon
{
public:
static void handler(void);
};
class cWindowWatchdog
{
public:
static void handler(void);
};
class cPvd
{
public:
static void handler(void);
};
class cTamperTimeStamp
{
public:
static void handler(void);
};
class cRtcWakeup
{
public:
static void handler(void);
};
class cFlash
{
public:
static void handler(void);
};
class cRcc
{
public:
static void handler(void);
};
class cExti
{
public:
static void line0Handler(void);
static void line1Handler(void);
static void line2Handler(void);
static void line3Handler(void);
static void line4Handler(void);
static void line9Handler(void);
static void line15_10Handler(void);
};
class cDma
{
public:
static void channellHandler(void);
static void channel2Handler(void);
static void channel3Handler(void);
static void channel4Handler(void);
static void channel5Handler(void);
static void channel6Handler(void);
static void channel7Handler(void);
};
class cAdc
{
public:
static void handler(void);
};
class cDac
{
public:
static void handler(void);
};
class cUsb
{
public:
static void highPriorityHandler(void);
static void lowPriorityHandler(void);
static void fsWakeupHandler(void);
};
class cComp
{
public:
static void handler(void);
};
class cLcdDriver
{
public:
static void handler(void);
};
class cTim9
{
public:
static void handler(void);
};
class cTim2
{
public:
static void handler(void);
};
class cTim3
{
public:
static void handler(void);
};
class cTim4
{
public:
static void handler(void);
};
class cTim10
{
public:
static void handler(void);
};
class cTim6
{
public:
static void handler(void);
};
class cTim7
{
public:
static void handler(void);
};
class cTim11
{
public:
static void handler(void);
};
class cI2C1
{
public:
static void eventHandler(void);
static void errorHandler(void);
};
class cI2C2
{
public:
static void eventHandler(void);
static void errorHandler(void);
};
class cSpi1
{
public:
static void handler(void);
};
class cSpi2
{
public:
static void handler(void);
};
class cUart1
{
public:
static void handler(void);
};
class cUart2
{
public:
static void handler(void);
};
class cUart3
{
public:
static void handler(void);
};
class cRtcAlarm
{
public:
static void handler(void);
};
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
// The vector table is normally located at address 0.
// When debugging in RAM, it can be located in RAM, aligned to at least 2^6.
// If you need to define interrupt service routines,
// make a copy of this file and include it in your project.
// The name "__vector_table" has special meaning for C-SPY:
// it is where the SP start value is found, and the NVIC vector
// table register (VTOR) is initialized to this address if != 0.
#pragma location = ".intvec"
extern "C" const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) },
__iar_program_start,
cNMI::handler,
cHardFault::handler,
cMemManage::handler,
cBusFault::handler,
cUsageFault::handler,
0,
0,
0,
0,
vPortSVCHandler, //функции freeRTOS не трогать!
cDebugMon::handler,
0,
xPortPendSVHandler, //функции freeRTOS не трогать!
xPortSysTickHandler, //функции freeRTOS не трогать!
//External Interrupts
cWindowWatchdog::handler, //Window Watchdog
cPvd::handler, //PVD through EXTI Line detect
cTamperTimeStamp::handler, //Tamper and Time Stamp
cRtcWakeup::handler, //RTC Wakeup
cFlash::handler, //FLASH
cRcc::handler, //RCC
cExti::line0Handler, //EXTI Line 0
cExti::line1Handler, //EXTI Line 1
cExti::line2Handler, //EXTI Line 2
cExti::line3Handler, //EXTI Line 3
cExti::line4Handler, //EXTI Line 4
cDma::channellHandler, //DMA1 Channel 1
cDma::channel2Handler, //DMA1 Channel 2
cDma::channel3Handler, //DMA1 Channel 3
cDma::channel4Handler, //DMA1 Channel 4
cDma::channel5Handler, //DMA1 Channel 5
cDma::channel6Handler, //DMA1 Channel 6
cDma::channel7Handler, //DMA1 Channel 7
cAdc::handler, //ADC1
cUsb::highPriorityHandler, //USB High Priority
cUsb::lowPriorityHandler, //USB Low Priority
cDac::handler, //DAC
cComp::handler, //COMP through EXTI Line
cExti::line9Handler, //EXTI Line 9..5
cLcdDriver::handler, //LCD
cTim9::handler, //TIM9
cTim10::handler, //TIM10
cTim11::handler, //TIM11
cTim2::handler, //TIM2
cTim3::handler, //TIM3
cTim4::handler, //TIM4
cI2C1::eventHandler, //I2C1 Event
cI2C1::errorHandler, //I2C1 Error
cI2C2::eventHandler, //I2C2 Event
cI2C2::errorHandler, //I2C2 Error
cSpi1::handler, //SPI1
cSpi2::handler, //SPI2
cUart1::handler, //USART1
cUart2::handler, //USART2
cUart3::handler, //USART3
cExti::line15_10Handler, //EXTI Line 15..10
cRtcAlarm::handler, //RTC Alarm through EXTI Line
cUsb::fsWakeupHandler, //USB FS Wakeup from suspend
cTim6::handler, //TIM6
cTim7::handler //TIM7
};
__weak void cNMI::handler() { while (1) {} }
__weak void cHardFault::handler() { while (1) {} }
__weak void cMemManage::handler() { while (1) {} }
__weak void cBusFault::handler() { while (1) {} }
__weak void cUsageFault::handler() { while (1) {} }
__weak void cDebugMon::handler() { while (1) {} }
__weak void cWindowWatchdog::handler() { while (1) {} }
__weak void cPvd::handler() { while (1) {} }
__weak void cTamperTimeStamp::handler() { while (1) {} }
__weak void cRtcWakeup::handler() { while (1) {} }
__weak void cFlash::handler() { while (1) {} }
__weak void cRcc::handler() { while (1) {} }
__weak void cExti::line0Handler() { while (1) {} }
__weak void cExti::line1Handler() { while (1) {} }
__weak void cExti::line2Handler() { while (1) {} }
__weak void cExti::line3Handler() { while (1) {} }
__weak void cExti::line4Handler() { while (1) {} }
__weak void cExti::line9Handler() { while (1) {} }
__weak void cExti::line15_10Handler() { while (1) {} }
__weak void cDma::channellHandler() { while (1) {} }
__weak void cDma::channel2Handler() { while (1) {} }
__weak void cDma::channel3Handler() { while (1) {} }
__weak void cDma::channel4Handler() { while (1) {} }
__weak void cDma::channel5Handler() { while (1) {} }
__weak void cDma::channel6Handler() { while (1) {} }
__weak void cDma::channel7Handler() { while (1) {} }
__weak void cAdc::handler() { while (1) {} }
__weak void cUsb::fsWakeupHandler() { while (1) {} }
__weak void cUsb::highPriorityHandler() { while (1) {} }
__weak void cUsb::lowPriorityHandler() { while (1) {} }
__weak void cDac::handler() { while (1) {} }
__weak void cComp::handler() { while (1) {} }
__weak void cLcdDriver::handler() { while (1) {} }
__weak void cTim2::handler() { while (1) {} }
__weak void cTim3::handler() { while (1) {} }
__weak void cTim4::handler() { while (1) {} }
__weak void cTim6::handler() { while (1) {} }
__weak void cTim7::handler() { while (1) {} }
__weak void cTim9::handler() { while (1) {} }
__weak void cTim10::handler() { while (1) {} }
__weak void cTim11::handler() { while (1) {} }
__weak void cI2C1::errorHandler() { while (1) {} }
__weak void cI2C1::eventHandler() { while (1) {} }
__weak void cI2C2::errorHandler() { while (1) {} }
__weak void cI2C2::eventHandler() { while (1) {} }
__weak void cSpi1::handler() { while (1) {} }
__weak void cSpi2::handler() { while (1) {} }
__weak void cUart1::handler() { while (1) {} }
__weak void cUart2::handler() { while (1) {} }
__weak void cUart3::handler() { while (1) {} }
__weak void cRtcAlarm::handler() { while (1) {} }
extern "C" void __cmain( void );
extern "C" __weak void __iar_init_core( void );
extern "C" __weak void __iar_init_vfp( void );
#pragma required=__vector_table
void __iar_program_start( void )
{
__iar_init_core();
__iar_init_vfp();
__cmain();
}