I want to notify my task to run from an ISR. I red the RTOS docs but I could not do it. I would really appreciate if you tell me what I am supposed to do and give an example if it is possible. I used cmsis-V2.
Inside the ISR which I am sure the ISR works correctly I wrote:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM15) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
if (htim == &htim16)
{
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(ADXL_HandlerHandle , &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/* USER CODE END Callback 1 */
}
I also used systick timer for FREE RTOS and timer 15 as the system timer . is it possible that the problem is related to this part ? I dout because task_notify_give function only add up and is not a blocking mechanism like semaphore.
and inside the thask, inside the for loop the first lines are:
ulNotifiedValue = ulTaskNotifyTake( pdFALSE, portMAX_DELAY);
if( ulNotifiedValue > 0 ){
//my codes ....
}
before for loop I defined:
uint32_t ulNotifiedValue;
but the task is not executed. even once.
I use Nucleo H755ZIQ.
before the definition of global variable, tasks are defined like this:
/* Definitions for ADXL_Handler */
osThreadId_t ADXL_HandlerHandle;
const osThreadAttr_t ADXL_Handler_attributes = {
.name = "ADXL_Handler",
.priority = (osPriority_t) osPriorityNormal,
.stack_size = 1024 * 4
};
then inside the main function initializing the schduler is as follows :
osKernelInitialize();
ADXL_HandlerHandle = osThreadNew(ADXL_Handler_TaskFun, NULL, &ADXL_Handler_attributes);
osKernelStart();
Then the timers will be started:
HAL_TIM_Base_Start_IT(&htim16);
In CMSIS there is no such a thing like task notification, I took a short look. The functions I used inside the ISR routine are from FreeRTOS. will not there a contradiction? should I use only Free RTOS task create function instead of CMSIS functions?
Thanks in advance.
Related
I've read multiple times that it is usually good practice to minimize the amount of time to spend in a timer interrupt, and the advice of only raising a flag came up several times.
I am using a timer to run a bit of code (conversion of sensor data into usable data). It is important in my application to read and manipulate this data at fairly high-speed (8KHz).
Here's how I am approaching the problem:
I am using an STM32 H743
I am using RTOS with two threads, with slightly different priority levels
I am using 2 timers (TIM2 and TIM3) in my case
TIM2 is set to trigger a callback at 1KHz, and is started in my main thread (slightly higher priority than the secondary thread)
TIM3 is set to trigger a callback at 8KHz, and is started in the secondary thread
the HAL_TIM_PeriodElapsedCallback is used for both Timers and is looking like this:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM6) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
else if (htim->Instance == TIM2) {
TIM3_flag = 1;
}
else if (htim->Instance == TIM3) {
TIM3_flag = 1;
}
/* USER CODE END Callback 1 */
}
And then each of the 2 threads have a simple test on the flag, here's what it looks like for the secondary thread:
void StartSecondaryThread(void *argument)
{
/* USER CODE BEGIN StartSecondaryThread */
HAL_TIM_Base_Start_IT(&htim3);
/* Infinite loop */
for(;;)
{
if (TIM3_flag == 1) {
runCALC();
//MORE USER CODE HERE
TIM3_flag = 0;
}
}
/* USER CODE END StartSecondaryThread */
}
Per the autogenerated code from CubeMX, both the mainThread and secondaryThread infinite for(;;) loops had a osDelay(1).
am I supposed keep these days? outside of the if statement for the raised flag?
I have some concerns that if I don't it will crash the MCU because outside nothing to do when the flag isn't raised. And I am concerns that keeping the osDelay(1) will be "too long" (1ms vs 125 us). Is there a way to apply a shorter delay that would not slow down my 8KHz polling?
Of course the runCAL() stuff will take significantly less time than the 125 us period.
It would make sense for me to remove the delay all together but I have a feeling that it will crash severely.
What should I do?
cheers
Flags not are net very good way of the thread synchronisation when you use RTOS.
In this case use semaphores, mutexes or direct task notifications.
slightly higher priority than the secondary thread
It does not make any difference in code you shown The different priorities RTOS tasks are not preempted by the scheduler and context switch happens only when you pass the control yourself. The only task which will actually run all the time is the last started one, as your task does not pass the control to the RTOS and the ISR also does not. Your code is not actually the correct RTOS code.
You can have it in one task.
void StartSecondaryThread(void *argument)
{
/* USER CODE BEGIN StartSecondaryThread */
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_Base_Start_IT(&htim2);
/* Infinite loop */
for(;;)
{
switch(ulTaskNotifyTake(pdTRUE, portMAX_DELAY))
{
case 3:
runCALC();
//MORE USER CODE HERE for timer 3
break;
case 2:
//MORE USER CODE HERE for timer 2
break;
default:
//MORE USER CODE HERE for other timers
break;
}
}
/* USER CODE END StartSecondaryThread */
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
BaseType_t xHigherPriorityTaskWoken = pdFalse;
switch((uint32_t)htim->Instance)
{
case (uint32_t)TIM6:
HAL_IncTick();
break;
case (uint32_t)TIM2:
xTaskNotifyFromISR( xThreadHndl, 2, eSetValueWithOverwrite, &xHigherPriorityTaskWoken );
break;
case (uint32_t)TIM3:
xTaskNotifyFromISR( xThreadHndl, 3, eSetValueWithOverwrite, &xHigherPriorityTaskWoken );
break;
}
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
I am programming a DSPIC33EP512GM710-I_PT TQFP using MPLABX
The problem is that I generate 2 similar interrupts the MPlabX it writes the same function name in several .c files so the compiler generate an error because 2 or more functions with same name are created
Here are 2 possible solutions:
Solution-1: Change interrupt name, but then I o not know if the interrupt will be reached
Solution-2: Comment all but one of the interrupt functions and in that interrupt ask for registers to look what was the real interruption
Note: I have seen the post: What does __attribute__((__interrupt__, no_auto_psv)) do?
And also the manual: (https://www.mouser.com/datasheet/2/268/MPLAB_XC16_v1_10_C_Compiler_UG_52081-477561.pdf page 128)
But I do not know what does micro when reaches an interruption if it look sequentially in all the attribute((interrupt, no_auto_psv)) codes
Here is my code corresponding to solution-2:
void __attribute__ ( ( interrupt, no_auto_psv ) ) _CM1Interrupt(void)
{
/*
* ************* ALTERADO PARA ACEPTAR OTROS COMPARADORES ************************
// CMP1 callback function
CMP1_CallBack();
// Clear the CEVT bit to enable further interrupts
CMP1_EventStatusReset();
*/
if (CM1CONbits.CEVT)
{
//all up mosfets to 0
aplica_mapa(0,6); //todo a 0
delay_us1(5);
CMP1_EventStatusReset();
}
if (CM2CONbits.CEVT)
{
//all up mosfets to 0
aplica_mapa(1,6);
delay_us1(5);
CMP2_EventStatusReset();
}
if (CM5CONbits.CEVT)
{
//all up mosfets to 0
aplica_mapa(3,6); //todo a 0
delay_us1(5);
CMP5_EventStatusReset();
}
// clear the CMP1 interrupt flag
IFS1bits.CMIF = 0;
}
So, I am doing a very simple time triggered pattern based on ARM Cortex M3.
The idea is:when SysTick is serviced, the task array index is incremented at systick, and so is a function pointer to the task. PendSV handler is called, and calls the task. I am using a Atmel ICE JTAG to debug it.
What happens is that it stucks at first task, and does not even increment the counter. It does not go anywhere.
Code pattern:
#include <asf.h> // atmel software framework. cmsis and board support package.
#define NTASKS 3
typedef void (*TaskFunction)(void);
void task1(void);
void task2(void);
void task3(void);
TaskFunction run = NULL;
uint32_t count1 = 0; //counter for task1
uint32_t count2 = 0; // 2
uint32_t count3 = 0; // 3
TaskFunction tasks[NTASKS] = {task1, task2, task3};
volatile uint8_t tasknum = 0;
void task1(void)
{
while(1)
{
count1++;
}
}
void task2(void)
{
while(1)
{
count2++;
}
}
void task3(void)
{
while(1)
{
count3++;
}
}
void SysTick_Handler(void)
{
tasknum = (tasknum == NTASKS-1) ? 0 : tasknum+1;
run = tasks[tasknum];
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
void PendSV_Handler(void)
{
run();
}
int main(void)
{
sysclk_init();
board_init();
pmc_enable_all_periph_clk();
SysTick_Config(1000);
while(1);
}
This design pattern is fundamentally flawed, I'm afraid.
At the first SysTick event, task1() will be called from within the PendSV handler, which will consequently not return. Further SysTick events will interrupt the PendSV handler and set the PendSV bit again, but unless the running task ends and the PendSV handler is allowed to return it can't possibly be invoked again.
The good news is that a proper context switch on the M3 is only a small amount of assembly language - perhaps 10 lines. You need to do some setup too, to get user mode code to use the process stack pointer and so on, and you need to set up a stack per task, but it's not really all that involved.
If you really want to cancel the running task when a SysTick arrives and launch another, they could all share the same stack; but it would be much easier if this was the process stack so that its stack pointer can be reset from within PendSV without affecting the return from handler mode. You'd also need to do some stack poking to convince PendSV to 'return' to the start of the next task you wanted to run.
The code snippets below come from the manufacturer. I am completely confused as to what they are trying to do. In particular, anything to do with InterruptHandler.
Please comment on the code in the globals section. Does this look right?
Also the various APIs included all have sticking points for me. Are they trying to use a void pointer? ... Plenty to ask, please comment mainly on how they are suggesting the timer2 would work.
...
/**
Section: Global Variables Definitions
*/
void (*TMR2_InterruptHandler)(void);
...
/**
Section: TMR2 APIs
*/
void TMR2_Initialize(void)
{
// Set TMR2 to the options selected in the User Interface
// T2CKPS 1:2; T2OUTPS 1:2; TMR2ON off;
T2CON = 0x11;
// T2CS LFINTOSC;
T2CLKCON = 0x04;
// T2PSYNC Not Synchronized; T2MODE Software control One shot; T2CKPOL Rising Edge; T2CKSYNC Not Synchronized;
T2HLT = 0x08;
// T2RSEL TMR4_postscaled;
T2RST = 0x02;
// PR2 8;
T2PR = 0x08;
// TMR2 0;
T2TMR = 0x00;
// Clearing IF flag before enabling the interrupt.
PIR4bits.TMR2IF = 0;
// Enabling TMR2 interrupt.
PIE4bits.TMR2IE = 1;
// Set Default Interrupt Handler
TMR2_SetInterruptHandler(TMR2_DefaultInterruptHandler);
// Start TMR2
TMR2_Start();
}
void TMR2_ISR(void)
{
// clear the TMR2 interrupt flag
PIR4bits.TMR2IF = 0;
if(TMR2_InterruptHandler)
{
TMR2_InterruptHandler();
}
}
void TMR2_SetInterruptHandler(void* InterruptHandler)
{
TMR2_InterruptHandler = InterruptHandler;
}
void TMR2_DefaultInterruptHandler(void)
{
// add your TMR2 interrupt custom code
// or set custom function using TMR2_SetInterruptHandler()
}
/**
End of File
*/
Thanks,
Steve
edit:
The manufacturer code includes a routine (I can't seem to add it here) for managing high-level interrupts that calls TMR2_ISR if (PIE4bits.TMR2IE == 1 && PIR4bits.TMR2IF == 1) This would seem to negate any TMR2_DefaultInterruptHandler would it not?
Thanks again for your help.
The comments of Garr Godfrey and Martin James provided helpful hints. Thus, I will try to fill the possible gaps:
void (*TMR2_InterruptHandler)(void); is a global variable with a function pointer providing the current interrupt handler.
I would read TMR2_ISR() as "interrupt service routine". From the presented code, it's not clear from where it is called. Suspecting from it's name, it's reasonable to assume that it's called when the interrupt is triggered. It checks whether the global interrupt handler function pointer TMR2_InterruptHandler is not NULL and calls the pointed function in case of success.
If TMR2_Initialize() has been called before then TMR2_InterruptHandler points to function TMR2_DefaultInterruptHandler(). Thus, in this case, the latter is called from TMR2_ISR().
void TMR2_SetInterruptHandler(void* InterruptHandler) is used in TMR2_Initialize(). It sets the current interrupt handler. (Surprise.)
I'm a little bit puzzled because I would make it:
void TMR2_SetInterruptHandler(void (*InterruptHandler)())
This is because my daily work with C++ drilled me for "clean typing" but I remember that C is much more lax about this. (void* is compatible to any pointer.)
As Garr Godfrey already said: "put your code in TMR2_DefaultInterruptHandler"
where it is recommended by the comment
// add your TMR2 interrupt custom code
I'm using hal can library for stm32L476vg mcu. I'm debugging my can transmission app and I can observe something apparently wrong in HAL_CAN_Transmit() sequence execution and its returned value, specially when it musts return HAL_TIMEOUT value.
Main code:
#include "main.h"
#include "Pm03_SystemConfig.h"
#include "App_Task_CAN.h"
uint8_t charBuffer[10]={0x00};
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
Pm03_SystemClock_Config();
App_Task_CAN_reset();
App_Task_CAN_init();
charBuffer[0]=0xCA;
charBuffer[1]=0xFE;
while (1)
{
App_Task_CAN_sendData(&charBuffer[0]);
}
}
My TX higher level function implementation that calls hal_can transmit and init for can configuration in App_Task_CAN.c file:
uint8_t App_Task_CAN_sendData(uint8_t* cBuffer)
{
for (uint8_t index=0; index< DATABTXLONG; index ++)
{
HCAN_Struct.pTxMsg->Data[index]= cBuffer[index];
}
if (HAL_CAN_Transmit(&HCAN_Struct, TIMEOUT)!=HAL_OK)
{
TaskCan_Error_Handler();
}
}
void App_Task_CAN_init(void)
{
static CanTxMsgTypeDef TxMessage;
static CanRxMsgTypeDef RxMessage;
/*configuracion timing para obtener 500kb/s*/
HCAN_Struct.Instance = CAN1;
HCAN_Struct.pTxMsg = &TxMessage;
HCAN_Struct.pRxMsg = &RxMessage;
HCAN_Struct.Init.Prescaler = 1;
HCAN_Struct.Init.Mode = CAN_MODE_NORMAL;//values defined in different hal libraries
HCAN_Struct.Init.SJW = CAN_SJW_1TQ;
HCAN_Struct.Init.BS1 = CAN_BS1_6TQ;//segment point at 87.5%
HCAN_Struct.Init.BS2 = CAN_BS2_1TQ;
HCAN_Struct.Init.TTCM = DISABLE;
HCAN_Struct.Init.ABOM = DISABLE;
HCAN_Struct.Init.AWUM = DISABLE;
HCAN_Struct.Init.NART = DISABLE;
HCAN_Struct.Init.RFLM = DISABLE;//Fifo locked mode disabled
HCAN_Struct.Init.TXFP = DISABLE;//prioridad de tx por id (más bajo más prioridad)
if (HAL_CAN_Init(&HCAN_Struct) != HAL_OK)
{
TaskCan_Error_Handler();
}
//TX
HCAN_Struct.pTxMsg->StdId = 0x321;
HCAN_Struct.pTxMsg->ExtId = 0x01;//29 bits
HCAN_Struct.pTxMsg->IDE = CAN_ID_STD;//values defined in different hal libraries
HCAN_Struct.pTxMsg->RTR = CAN_RTR_DATA;//values defined in different hal libraries
HCAN_Struct.pTxMsg->DLC = DATABTXLONG;//1-9
}
some defines used above, declared in App_Task_CAN.h:
/* Timeout for can operations in bit time units */
#define TIMEOUT 100
/* param for config mesg frames */
#define DATABTXLONG 2
I was debugging and going step by step through all the hal transmit function sequence (concerned piece of code below) and I can see how, after first TXRQ ->
Part of hal_can_transmit function in stm32l4xx_hal_can.c file:
HAL_StatusTypeDef HAL_CAN_Transmit(CAN_HandleTypeDef* hcan, uint32_t Timeout)
{
...
/* Request transmission */
hcan->Instance->sTxMailBox[transmitmailbox].TIR |= CAN_TI0R_TXRQ;
...
/* Check End of transmission flag */
while(!(__HAL_CAN_TRANSMIT_STATUS(hcan, transmitmailbox)))
{
/* Check for the Timeout */
if(Timeout != HAL_MAX_DELAY)
{
if((Timeout == 0) || ((HAL_GetTick()-tickstart) > Timeout))
{
hcan->State = HAL_CAN_STATE_TIMEOUT;
/* Process unlocked */
__HAL_UNLOCK(hcan);//line in which thread breaks
return HAL_TIMEOUT;//ignored line during debug
}
}
}
...
}
(NOTE: This next piece of code belongs to hal library provided by ST)
-> once the thread passes through the above transmit call, after TXRQ line this happens:
the thread waits for end Tx flag and
after timeout value expires, the threads goes into timeout state line
it arrives until hal unlock() macro line
after this, it exits/breaks the execution and returns to the line where this function was called ignoring the next line "return HAL_TIMEOUT".
The thread is broken just before arriving to this line (red text line). It returns to my transmit call a HAL_OK value! I recieve HAL_OK value! I confirm this after mounting a switch case in order to see what value it was reciving instead of HAL_TIMEOUT.
It happens from the first time transmit is called. I don't know if it is normal behavior. Obviously it is a hal_can code fact and I can not do anything from code user to fix it.
Has someone else, who worked with hal can for stm32, faced this situation?
My Tx does not work and I was looking for the possible fail causes through debugging. Hence I have seen this. Maybe there is more than this code problem to solve. But I think my code is very simple to fail: I only call send data in a infinite loop (of course after can init). By debugging I can see how data is arriving to the mailboxes correctly, but flag of TXOk never sets.
So, maybe this hal_can strange behavior is part of the cause of my transmission fail.
It seems to be a third party bug. Has someone deal with same situation? Any suggestion about my user code? is there something to improve?