I'm using STM32F4 with HAL. It is configured as a simple counter as it counts pulses via EXTI interrupt. I want to asynchronously ask for the current number of impulses via UART.
Highest frequency of EXTI pulses is 250kHz, uC clock frequency is 180MHz. I think the problem is, sometimes the frequency of pulses on EXTI input is so high that a single UART Rx callback is unable to complete before a new pulse arrives on EXTI input. EXTI has a higher priority, so it interrupts UART Rx interrupt and the UART's response is rubbish.
I'd like this setup to work in a way that EXTI indeed has a higher priority, because it cannot skip any impulse. But the UART should, on rx interrupt, grab the value of number of pulses (global variable) and send it's message 'Pulse counter = <pulseCounter>' without TX buffer being filled with rubbish values. Doesn't matter if another higher priority interrupt arrives or not.
Is there a flaw in my understanding of this problem, or is there a way to fix the code?
Here is an example of UART response: enter image description here
Here is the code initializing interrupts
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
HAL_UART_Receive_IT(&huart1, &uartMessage, 1);
HAL_NVIC_SetPriority(USART1_IRQn, 1, 0);
Here is the code of interrupt callbacks:
EXTI:
void getCounterValue()
{
*pGatePulseCounter = __HAL_TIM_GET_COUNTER(&htim6);
pGatePulseCounter++;
if (pGatePulseCounter == &gatePulseCounter[SAMPLE_TABLE_SIZE]) {
pGatePulseCounter = gatePulseCounter;
__HAL_TIM_SET_COUNTER(&htim6, 0);
}
pulseCounter++;
}
UART:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
uint8_t data[50] = {0};
if (uartMessage == 'p') {
sprintf((char*)data, "Pulse counter = <%d>\n\r", pulseCounter);
HAL_UART_Transmit_IT(&huart1, data, sizeof(data));
pulseCounter = 0;
} else if (uartMessage == 'f') {
sprintf((char*)data, "Current frequency = <%d>\n\r", pulseFrequency_Hz);
for (int i=0; i<SAMPLE_TABLE_SIZE; i++) {
gatePulseCounter[i] = 0;
}
HAL_UART_Transmit_IT(&huart1, data, sizeof(data));
} else {
}
HAL_UART_Receive_IT(&huart1, &uartMessage, 1);
}
global variables:
int gatePulseCounter[SAMPLE_TABLE_SIZE];
int* pGatePulseCounter = gatePulseCounter;
int pulseFrequency_Hz;
unsigned long int pulseCounter = 0;
unsigned long int pulseCounterBuffer = 0;
uint8_t uartMessage;
1 Answer 1
Ok, I solved it.
The easiest thing to change that solves my issue is replacing:
HAL_UART_Transmit_IT(&huart1, data, sizeof(data));
with:
HAL_UART_Transmit(&huart1, data, sizeof(data), uartTimeout);
in UART RX callback.
That means I'm sending entire UART response inside UART RX callback instead of creating another interrupt request. I didn't notice any malfunctions during operation.
That being said, I still don't entirely understand why UART TX interrupt did not work correctly.
Based on the comments, I also tried it with a basic circular buffer. That means that my UART IRQ handler now looks like this:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (isFull(pQueue)) {
return;
} else {
enqueue(pQueue, uartMessage);
}
HAL_UART_Receive_IT(&huart5, &uartMessage, 1);
}
and the response is generated and sent back in the while loop (a simple backgroud-foreground architecture). Note that it is now possible to properly use TX interrupt (HAL_UART_Transmit_IT()
) in spite of a blocking version (HAL_UART_Transmit()
):
if (isEmpty(pQueue)) {
} else {
char msg = dequeue(pQueue);
uint8_t data[50] = {0};
if (msg == 'p') {
*pPulseCounterBuffer = *pPulseCounter;
*pPulseCounter = 0;
sprintf((char*)data, "Pulse counter = <%d>\n", *pPulseCounterBuffer);
// HAL_UART_Transmit(&huart5, data, sizeof(data), uartTimeout);
HAL_UART_Transmit_IT(&huart5, data, sizeof(data));
} else if (msg == 'f') {
for (int i=0; i<SAMPLE_TABLE_SIZE; i++) {
gatePulseCounter[i] = 0;
}
sprintf((char*)data, "Current frequency = <%d>\n", pulseFrequency_Hz);
// HAL_UART_Transmit(&huart5, data, sizeof(data), uartTimeout);
HAL_UART_Transmit_IT(&huart5, data, sizeof(data));
} else {
sprintf((char*)data, "Wrong command. Use <p> or <f>\n");
// HAL_UART_Transmit(&huart5, data, sizeof(data), uartTimeout);
HAL_UART_Transmit_IT(&huart5, data, sizeof(data));
}
}
-
\$\begingroup\$
HAL_UART_Transmit_IT
is non-blocking and the stack local buffer gets trashed while still in use on return. The code still suffers from races and possible performance issues though. It looks like one possibility would be to remove the counter IRQ and use the hardware counter directly instead without__HAL_TIM_SET_COUNTER
resets. Record the last__HAL_TIM_GET_COUNTER
value during sampling and compute results relative to the prior value. Do you need to process requests inside interrupts or could you punt the work to the main loop and leave the interrupts to only manage circular buffers? \$\endgroup\$doynax– doynax2020年08月20日 12:22:07 +00:00Commented Aug 20, 2020 at 12:22
TIMx_CNT
register you will have how much changes was counted on pin. Also do not recommending sending UART messages by blocking approach. \$\endgroup\$