2024-11-19 02:45 AM - edited 2024-11-19 02:47 AM
hello,
I am developing an application which makes use of LIN break interrupts. At the moment I am using two STM32 dev boards:
STM32C0116-DK ( which uses STM32C011F6U6)
STM32 NUCLEO G531 (which has STM32G431KBT)
We are using the LL drivers interface.
Although one processor is Cortex-M0+ and the other is M4, the description of the USART operation seems the same for each processor, as does the low level interface.
However, we are seeing very odd behaviour on the C11 (Cortex M0+) platform - we get the LBD interrupt, but it happens a considerable time after the break.. On the M4 platform we see the interrupt a few us after the break is done.
UART config and ISR implementation is the same on each platform. (Most of it is generated by Cube.) Here is the code for UART initialisation:
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK1);
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
/**USART1 GPIO Configuration
PB7 ------> USART1_RX
PC14-OSCX_IN (PC14) ------> USART1_TX
*/
GPIO_InitStruct.Pin = LOCIBUS_RX_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(LOCIBUS_RX_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LOCIBUS_TX_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
LL_GPIO_Init(LOCIBUS_TX_GPIO_Port, &GPIO_InitStruct);
/* USART1 interrupt Init */
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = 19200;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_SetRXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_8);
LL_USART_DisableFIFO(USART1);
LL_USART_SetLINBrkDetectionLen(USART1, LL_USART_LINBREAK_DETECT_10B);
LL_USART_ConfigLINMode(USART1);
/* USER CODE BEGIN WKUPType USART1 */
// we need BREAK interrupts enabled
// we cannot use FIFOs because we are in LIN mode
// so we use single byte interrupts on RX
// and implement our own rx buffer in locibus_uart.c
LL_USART_EnableIT_LBD( USART1 ); // LIN BREAK detect
LL_USART_EnableIT_RXNE_RXFNE( USART1 ); // RX not empty
// TX interrupts are enabled only when there is something
LL_USART_DisableIT_TXE_TXFNF( USART1 ); // TX not full
// everything else disabled
LL_USART_DisableIT_TC( USART1 );
LL_USART_DisableIT_CTS( USART1 );
LL_USART_DisableIT_IDLE( USART1 );
LL_USART_DisableIT_PE( USART1 );
LL_USART_DisableIT_ERROR( USART1 );
LL_USART_DisableIT_CM( USART1 );
LL_USART_DisableIT_RTO( USART1 );
LL_USART_DisableIT_EOB( USART1 );
LL_USART_DisableIT_TXFE( USART1 );
LL_USART_DisableIT_RXFF( USART1 );
LL_USART_DisableIT_WKUP( USART1 );
LL_USART_DisableIT_TXFT( USART1 );
LL_USART_DisableIT_TCBGT( USART1 );
LL_USART_DisableIT_RXFT( USART1 );
LL_USART_EnableLIN(USART1);
/* USER CODE END WKUPType USART1 */
LL_USART_Enable(USART1);
/* Polling USART1 initialisation */
while((!(LL_USART_IsActiveFlag_TEACK(USART1))) || (!(LL_USART_IsActiveFlag_REACK(USART1))))
{
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
And here is the ISR:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
// we just clear and ignore framing and overrun errors
if( LL_USART_IsActiveFlag_ORE(USART1) )
{
LL_USART_ClearFlag_ORE(USART1);
return;
}
// BREAK can also have Framing error, so process it first and clear it too
if( LL_USART_IsActiveFlag_LBD(USART1) )
{
debug_io_iface(2, 1);
LL_USART_ClearFlag_LBD(USART1);
LL_USART_ClearFlag_FE(USART1);
return;
}
if( LL_USART_IsActiveFlag_FE(USART1) )
{
debug_io_iface(1, 1);
LL_USART_ClearFlag_FE(USART1);
LL_USART_RequestRxDataFlush(USART1);
return;
}
if ( LL_USART_IsActiveFlag_RXNE_RXFNE(USART1) ) // received byte?
{
locibus_uart_receive_one_byte();
LL_USART_RequestRxDataFlush(USART1);
return;
}
if ( LL_USART_IsActiveFlag_TXE_TXFNF(USART1) ) // transmit done?
{
LL_USART_ClearFlag_TXFE(USART1);
LL_USART_ClearFlag_TC(USART1);
LL_USART_ClearFlag_TCBGT(USART1);
uart_send_one_byte();
return;
}
/* USER CODE END USART1_IRQn 0 */
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
Some explanation : as we expect Framing errors during the break (this is documented in the datasheet) we "throw away" these interrupts.
The calls to debug_io_iface() just generate some narrow pulses on spare GPIO pins which we look at with the analyser to check timing. We generate a "break" pulse of about 600us (baud rate is 19200) every 100ms This is what we see for the Cortex M4 chip. As expected we see the Framing Int during the break, it is discarded, then the BREAK int is raised just after the rising edge of BREAK as expected.
Here is the zoomed out image showing the interrupts of rthe M0+ chip. We get the FE at pretty much the same time as for the M4, but the BREAK interrupt does not happen for another 7ms ! Much later than expected.
What is going on?
I should also note that I have seen some other odd behaviour with the M0+ chip - sometimes GPIOs seemed to stop working and then were OK again, things like that. It is almost as if something basic in the setup of the chip is wrong - but I checked everythingI could (chip selection, clocking ... ) and all seems OK. Also CubeMX generates code with no warnings.
Any ideas? Thanks in advance for suggestions.
Solved! Go to Solution.
2024-11-20 03:55 AM
OK, I discovered the issue - there seems to be a signal issue on my board. Thanks for the suggestions, and apologies for wasting your time.
2024-11-19 08:12 AM
can anyone from STM shed any light on this please? I am looking at adding some functionality to an existing product, it would be extremely useful to know if I am making some kind of basic error or not. Thank you.
2024-11-19 08:49 PM
Hi,
I have only used the LIN interface on the F469/ F429 devices. I cannot recall any issue with Break detection there. But I didn't use either HAL or LL...
Have you read the Errata sheets on both yours devices?
Kind regards
Pedro
2024-11-20 12:57 AM - edited 2024-11-20 01:17 AM
hi Pedro - thanks for the response. Good idea to look at Errata - nothing LIN relevant for the C11 as far as I can see.
I find it very puzzling, for an interrupt to occur 7ms after the event occurs, something very odd is going on. It is also very strange that the same code works fine on the STM32G device, but gives strange behaviour on C11.
AN5969 "Migrating between STM32 G0 and STM32C0" makes this statement (page 18):
5.14 USART, FDCAN, CRC, CRS, IWDG, WWDG, and I2C
Both series share the same peripheral and they are fully compatible
However that is not what I am seeing.
2024-11-20 01:17 AM
Hi,
Have you check for other IRQs that maybe causing an overall system lag?
Kind regards
Pedro
2024-11-20 02:28 AM - edited 2024-11-20 02:32 AM
hi Pedro, indeed, that was also one of my suspicions - I have even turned off the SysTick interrupt, also removed pretty much every piece of code that can be. My main loop is just a while(1) as well.
I've also checked that the first (Framing Error) int returns quickly (tried removing the UART flush and also added a pulse right before the return, that is not the issue.
I'm still looking to see if I missed something *** - happens to the best of us, but I do not see it. I keep looking.
2024-11-20 02:37 AM
Hi,
Can you toggle a pin inside your while(1) loop, and check the iteration time?
Then turn SysTick back On, and check again?
Kind regards
Pedro
2024-11-20 02:43 AM
it is about the same either way - very short, like 800ns.
2024-11-20 03:05 AM
Hi,
Can you move the Pin toggle to inside the SysTick Interrupt handler, and check it again?
Kind regards
Pedro
2024-11-20 03:55 AM
OK, I discovered the issue - there seems to be a signal issue on my board. Thanks for the suggestions, and apologies for wasting your time.