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.