-
Notifications
You must be signed in to change notification settings - Fork 0
Interrupt Management
Interrupt management has been changed since stack version v0.3.0, see !7 for more details.
[[TOC]]
Previously, every application had to correctly set its interrupt callbacks in application code, making interrupt handling more error prone. For example, when a shield was moved from one connector to another (and changed in Makefile accordingly), this had to also be manually changed in application interrupt callback functions.
Furthermore, most of these interrupt callbacks were the same in each application where a certain shield is used. This makes the shield driver a more logical place to handle this interrupt.
A downside of handling interrupts in the shield driver is that only one interrupt handler can be active for a certain interrupt. For UART for example, this would cause trouble even when only two shields using UART are being used.
The choice is made to move this interrupt handler to the platform code, with function pointers that can be set from both the application and the shield driver.
This is best showcase with a pseudocode example, in this case for UART. This function is set in the uart.c file:
UART_Callback(uart)
{
switch(uart)
case P1_UART:
doShieldCallback();
doApplicationCallback();
....
}For UART, see the uart.c file under core/platform/octa/src for the exact implementation.
At the top of the file, some function pointers are set, and initialized to 0.
static void (*P1_Shield_UART_RX_Callback)(void) = 0;
static void (*P1_Application_UART_RX_Callback)(void) = 0;
static void (*P2_Shield_UART_RX_Callback)(void) = 0;
static void (*P2_Application_UART_RX_Callback)(void) = 0;
static void (*P3_Shield_UART_RX_Callback)(void) = 0;
static void (*P3_Application_UART_RX_Callback)(void) = 0;
static void (*BLE_UART_RX_Callback)(void) = 0;
static void (*USB_UART_RX_Callback)(void) = 0;
static void (*FTDI_UART_RX_Callback)(void) = 0;For the connector's RX callbacks, these function pointers have to be set using the following functions, for the application and shield driver respectively:
void UART_SetApplicationCallback(void (*appcallback), uint8_t connector)
{
switch(connector)
{
case 1:
P1_Application_UART_RX_Callback = appcallback;
break;
case 2:
P2_Application_UART_RX_Callback = appcallback;
break;
case 3:
P3_Application_UART_RX_Callback = appcallback;
break;
default:
break;
}
}
void UART_SetShieldCallback(void (*shieldcallback), uint8_t connector)
{
switch(connector)
{
case 1:
P1_Shield_UART_RX_Callback = shieldcallback;
break;
case 2:
P2_Shield_UART_RX_Callback = shieldcallback;
break;
case 3:
P3_Shield_UART_RX_Callback = shieldcallback;
break;
default:
break;
}
}A connector is passed to this function, based on the connector set in the Makefile.
There are also functions to set the rx callback for BLE, USB and FTDI UART. These follow the same principle.
With the function pointers set, we can look at the actual interrupt callback:
// UART RX CALLBACK
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &P1_UART)
{
if(P1_Shield_UART_RX_Callback)
P1_Shield_UART_RX_Callback();
if(P1_Application_UART_RX_Callback)
P1_Application_UART_RX_Callback();
}
else if (huart == &P2_UART)
{
if(P2_Shield_UART_RX_Callback)
P2_Shield_UART_RX_Callback();
if(P2_Application_UART_RX_Callback)
P2_Application_UART_RX_Callback();
}
else if (huart == &P3_UART)
{
if(P3_Shield_UART_RX_Callback)
P3_Shield_UART_RX_Callback();
if(P3_Application_UART_RX_Callback)
P3_Application_UART_RX_Callback();
}
else if (huart == &BLE_UART)
{
if(BLE_UART_RX_Callback)
BLE_UART_RX_Callback();
}
else if (huart == &USB_UART)
{
if(USB_UART_RX_Callback)
USB_UART_RX_Callback();
}
else if (huart == &FTDI_UART)
{
if(FTDI_UART_RX_Callback)
FTDI_UART_RX_Callback();
}
else
{
printERR("UART Handle unknown\r\n");
Error_Handler();
}
}As previously mentioned, this function is called for any UART RX interrupt. We check the *huart parameter, to see from which UART the interrupt is being triggered.
Furthermore, a check is done to see if the callback function pointer is set (if not zero) before calling the function behind the function pointer.
For GPIO, see the gpio.c file under core/platform/octa/src for the exact implementation.
At the top of the file, some function pointers are set, and initialized to 0.
static void (*OCTA_BTN1_Pressed_Callback)(void) = 0;
static void (*OCTA_BTN2_Pressed_Callback)(void) = 0;The callback should be set using a similar function as used in UART, being:
void GPIO_SetApplicationCallback(void (*callback), uint16_t pinNumber)
{
switch(pinNumber)
{
case OCTA_BTN1_Pin:
OCTA_BTN1_Pressed_Callback = callback;
break;
case OCTA_BTN2_Pin:
OCTA_BTN2_Pressed_Callback = callback;
break;
default:
break;
}
}For now, only the two OCTA BTNs are implemented.
These callbacks are then called when an actual interrupt is triggered:
void HAL_GPIO_EXTI_Callback(uint16_t gpioPinNumber)
{
// OCTA B1
if (gpioPinNumber==OCTA_BTN1_Pin)
{
printDBG("OCTA BTN1 PRESSED\r\n");
if(OCTA_BTN1_Pressed_Callback)
OCTA_BTN1_Pressed_Callback();
}
// OCTA B2
if (gpioPinNumber==OCTA_BTN2_Pin)
{
printDBG("OCTA BTN2 PRESSED\r\n");
#ifdef DEBUG
//RESUME rtos print handle, which will print the stats once
if(osKernelRunning())
osThreadResume(rtosprintHandle);
else
printDBG("Kernel not running\r\n");
#endif
if(OCTA_BTN2_Pressed_Callback)
OCTA_BTN2_Pressed_Callback();
}
}For RTC, see the rtc.c file under core/platform/octa/src for the exact implementation.
At the top of the file, some function pointers are set, and initialized to 0.
static void (*RTC_Wakeup_ApplicationCallback)(void) = 0;The callback is set using this function:
void RTC_SetApplicationWakeupCallback(void (*callback))
{
RTC_Wakeup_ApplicationCallback = callback;
}Which is then called in the actual interrupt callback as follows:
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hrtc);
if(RTC_Wakeup_ApplicationCallback)
RTC_Wakeup_ApplicationCallback();
}