2022-11-04 08:42 AM
Hello,
we have a custom board with a STM32L051R6T6 MCU.
And we try to to receive and to transmit on an UART.
The telegrams have 40bytes lenght and the UART works on 9600 baud. The remote station sends one every 50ms.
If processing of the rx telegram was successful, we want to answer with a 40byte ackknowledge telegram.
Receiving and transmitting are done via interrupt in 40byte blocks.
If I comment out transmit, everything works. every telegram is received and processed.
But when I uncomment transmitting, a lot of rx telegramms are lost.
I also added a timer to debug the amount of time, which the uart needs to transmit (from HAL_UART_Transmit_IT to the HAL_UART_TxCpltCallback). This time is not as long as the time, where no are telegrams received.
I think that its not possible to send and receive parallel. But if tx is finished, should rx not run immediately again? It looks as the UART makes a break after sending.
I think, since rx telegram and tx telegram have the same length, I should maximum miss one rx telegram, not ~ten of them.
Can someone explain this behavior?
Thank you.
2022-11-04 09:51 AM
>>Can someone explain this behavior?
Not from prose
Can you see the interaction on a scope or logic analyzer?
Something is blocking.
The UART is capable of transmitting and receiving concurrently.
And should be handled independently. Use buffering that facilitates that.
I don't care for the HAL interrupt/callback paradigm, not something I'm looking to debug.
2022-11-05 03:28 AM
Try transmit by polling, and receive by interrupt as a quick workaround to debug.
Check your core frequency, how long to transmit one byte, all inclusive, same for receive. 40 bytes duration check. If your core is slowed for lower power, or if there are other long interrupts or simply receive and transmit queued, duration may double. Simultaneous rx and tx should work fine in typical condition.
2022-11-05 09:27 AM
Post your code showing how you're receiving the 40 bytes, how you're processing it and transmitting back 40 bytes.
2022-11-06 11:14 PM
Hello and thank you for replys.
Yes I can see the signals on my oszi exactly as written here.
The Tx and Rx buffers which are provided to HAL are completely independent.
@Tesla DeLorean And I know, you are not a fan of HAL.
There is a gap between incoming telegrams. But its not long enough to send an ack. So if I want to reply every rx telegram, the UART has to do it parallel. This is not necessary, but it would be nice to get every second rx telegram and reply it.
Sadly I'm not really expert enough to debug, what happens in HAL API.
Is there a way to get the status of the UART in TxCompleteInterrupRoutine, to see whats the problem? A register or statusvalue, what tells me more about whats the UART doing in this moment?
The code is quite extensive, but I try to extract the essential parts:
2022-11-06 11:26 PM
//receiving part
//called from RxInterrupt
void GlobalsUartRxInterruptCallback(UART_HandleTypeDef *uartHandle){
struct UartAcu* uartInstance = GlobalsFindAcuUartFromHandle(uartHandle);
if (uartInstance != NULL){
UartAcuProcessRxData(uartInstance);
UartAcuStartInterrupt(uartInstance);
}
}
//Detection of the RXTelegram start (detetion byte is the 2. in telegram)
void UartAcuProcessRxData(struct UartAcu* uartAcu){
//trying to find starting position of Telegram
//find the Address of EmergencyCommand, which is on position FromAcuMasterAddress
uint32_t emergencyAddressIndex = 0xFFFFFFFF;
for (uint32_t index = 0; index < uartAcu->syncForeward; ++index){
if ( uartAcu->baseUart.rxData[index] == EmergencyAddress){
emergencyAddressIndex = index;
break;
}
}
switch (emergencyAddressIndex){
case 0xFFFFFFFF: //is possible if we didnt read a whole TelegramAcuFrom::FromAcuLength ( syncForeward < TelegramAcuFrom::FromAcuLength )
if ( uartAcu->syncForeward == FromAcuLength) //not found in TelegramAcuFrom::FromAcuLength bytes, but should be there somewhere: this is a fatal error
uartAcu->errorNoStartingPositon = true;
uartAcu->syncForeward = FromAcuLength;
break;
case FromAcuMasterAddress: //Telegram is in sync to buffer (to be validated by CRC later)
UartAcuSaveTelegram(uartAcu);
uartAcu->syncForeward = FromAcuLength;
break;
default: //Telegram out of sync to buffer
//calculate count of bytes to read foreward to get in sync
if (emergencyAddressIndex > FromAcuMasterAddress){
uartAcu->syncForeward = emergencyAddressIndex - FromAcuMasterAddress;
}else{
uartAcu->syncForeward = FromAcuLength + (emergencyAddressIndex - FromAcuMasterAddress);
}
if (uartAcu->syncForeward > 40){
uartAcu->baseUart.syncForewardFault++;
}
uartAcu->baseUart.testCounter++;
break;
}
}
//if in sync, checkt telegram count and CRC and put it in inboundqueue
void UartAcuSaveTelegram(struct UartAcu* uartAcu){
bool counterRxCorrect = false;
struct TelegramAcuFrom telegramAcuFrom; //Workingcopy
TelegramAcuFromInit(&telegramAcuFrom, uartAcu->baseUart.rxData);
//++Artea::Globals::self->telegramCount;
uint8_t telegramCounterRx = TelegramAcuFromCounter(&telegramAcuFrom);
if ( UartAcuCounterRxInitialized(uartAcu) ){
if ( UartAcuGetCounterRx(uartAcu) == telegramCounterRx ){
counterRxCorrect = true;
StatusResetInternalFault(internalFaultsTelegramsRxSequence);
HAL_GPIO_WritePin(OUT_LEDR_GPIO_Port, OUT_LEDR_Pin, UartId(&uartAcu->baseUart) == 1 ? GPIO_PIN_RESET : GPIO_PIN_SET);
}else{
UartAcuCounterRxInitialize( uartAcu, telegramCounterRx );
char convertbuffer1[16];
char convertbuffer2[16];
char* telegramCounterRxText = itoa(telegramCounterRx, convertbuffer1, 10);
char* counterRxText = itoa(UartAcuPeekCounterRx(uartAcu), convertbuffer2, 10);
char extraText[strlen("Acu:") + strlen(telegramCounterRxText) + strlen(" E'Command:") + strlen(counterRxText) + 1];
strcpy(extraText, "Acu:");
strcat(extraText, telegramCounterRxText);
strcat(extraText, " E'Command:");
strcat(extraText, counterRxText );
StatusSetInternalFault( internalFaultsTelegramsRxSequence, extraText);
}
}else{
UartAcuCounterRxInitialize( uartAcu, telegramCounterRx );
counterRxCorrect = true;
}
//If CRC is the last DWord, Polynomialdevision over the whole telegramm should be zero: Doesn't work (why not?)
//so we calculate CRC of netto data and compare with CRC from telegramAcuFrom, this works
crc crcRx = CrcHardware(TelegramData(&telegramAcuFrom.baseTelegram), TelegramLength(&telegramAcuFrom.baseTelegram) - sizeof(crc));
bool crcCorrect = crcRx == TelegramAcuFromCrc(&telegramAcuFrom);
if (crcCorrect && counterRxCorrect){
//Copy Data from Working copy to queue
TelegramAcuFromInitCopy(&uartAcu->inboundQueue[uartAcu->inboundQueue_WriteIndex], &telegramAcuFrom);
UartAcuIncrementQueueIndex(&uartAcu->inboundQueue_WriteIndex);
//TODO: Trigger a watchdog for the Uart, to monitor regular reception
}
}
2022-11-06 11:35 PM
//Processing ist done in main cycle with a delay of 20ms at the moment
//We have 2 Uarts, they should receive equal telegrams (execpt of the telegram counter and CRC of //course)
//Transmit is done, if comarison of both telegrams was successful (which works nice)
void GlobalsDoWork(){
//checking Fault pin of MaximIC
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(IN_FAULT_GPIO_Port, IN_FAULT_Pin)){
StatusSetInternalFault(internalFaultsMaxim5320EAI, "FaultPin is active");
}else{
StatusResetInternalFault(internalFaultsMaxim5320EAI);
}
//
//read as long until inboundQueue is empty to get the most actual ACU states
struct TelegramAcuFrom* lastReceivedTelegram1 = UartAcuPeekNewestTelegram(&globals.uarts[0]);
struct TelegramAcuFrom* lastReceivedTelegram2 = UartAcuPeekNewestTelegram(&globals.uarts[1]);;
if (lastReceivedTelegram1 == NULL || lastReceivedTelegram2 == NULL){ //at least one channel has not received
++globals.noTelegramsReceivedCounter;
if (globals.noTelegramsReceivedCounter > globals.noTelegramsReceivedCounterMax){//No Telegram from one channel for more than noTelegramsReceivedCounterMax cycles
char uartName[strlen(UartName(&globals.uarts[0].baseUart)) + strlen(UartName(&globals.uarts[1].baseUart)) + 1];
strcpy( uartName, (lastReceivedTelegram1 == NULL) ? UartName(&globals.uarts[0].baseUart) : "");
strcat( uartName, (lastReceivedTelegram2 == NULL) ? UartName(&globals.uarts[1].baseUart) : "");
char convertbuffer[16];
char* noTelegramsReceivedCounterText = itoa(globals.noTelegramsReceivedCounter, convertbuffer,10);
uint32_t length = strlen("On ")+ strlen(uartName) + strlen(" for ") + strlen(noTelegramsReceivedCounterText) + strlen("cycles");
char extraText[length +1];
strcpy(extraText, "On ");
strcat(extraText, uartName);
strcat(extraText, " for ");
strcat(extraText, noTelegramsReceivedCounterText);
strcat(extraText, "cycles");
StatusSetInternalFault( internalFaultsNoTelegramsReceived , extraText);
}
}else{//Compare both channels state
StatusResetInternalFault( internalFaultsNoTelegramsReceived );
globals.noTelegramsReceivedCounter = 0;
if (!TelegramAcuFromContentEquals(lastReceivedTelegram1, lastReceivedTelegram2)){
++globals.telegramsNotEqualCounter;
if (globals.telegramsNotEqualCounter > globals.telegramsNotEqualCounterMax){
char convertbuffer[16];
char* telegramsNotEqualCounterText = itoa(globals.telegramsNotEqualCounter, convertbuffer,10);
uint32_t length = strlen("ACU-Channels have different content since ") + strlen(telegramsNotEqualCounterText)+ strlen(" cycles");
char extraText[length + 1];
strcpy(extraText, "ACU-Channels have different content since ");
strcat(extraText, telegramsNotEqualCounterText);
strcat(extraText, " cycles");
StatusSetInternalFault( internalFaultsTelegramsNotEqual, extraText);
}
}else{
StatusResetInternalFault( internalFaultsTelegramsNotEqual );
globals.telegramsNotEqualCounter = 0;
if (!GlobalsProcessTelegram(lastReceivedTelegram1)){ //Error on setting Outputs
StatusSetInternalFault( internalFaultsTelegramProcessing , "Setting outputs from telegram went wrong" );
}else{
StatusResetInternalFault( internalFaultsTelegramProcessing );
++globals.rxProcessSuccess;
}
char convertBuffer1[16];
char convertBuffer2[16];
const char* telegrammCounterText1 = (lastReceivedTelegram1 == NULL) ? "-" : itoa(TelegramAcuFromCounter(lastReceivedTelegram1), convertBuffer1, 10);
const char* telegrammCounterText2 = (lastReceivedTelegram2 == NULL) ? "-" : itoa(TelegramAcuFromCounter(lastReceivedTelegram2), convertBuffer2, 10);
char logText[strlen("1:") + strlen(telegrammCounterText1) + strlen(" 2:") + strlen(telegrammCounterText2) + 1];
strcpy(logText, "1:");
strcat(logText, telegrammCounterText1);
strcat(logText, " 2:");
strcat(logText, telegrammCounterText2);
LogMessage( logText);
//Both equal telegrams have been processed (successfull or not), so they have to be deleted
UartAcuDeleteActualInboundTelegram(&globals.uarts[0]);
UartAcuDeleteActualInboundTelegram(&globals.uarts[1]);
}
}
char logText1[256]={'\0'};
char convertBuffer4[16];
strcpy(logText1, "t:");
strcat(logText1, itoa(__HAL_TIM_GET_COUNTER(globals.timDebugHandle), convertBuffer4, 10));
strcat(logText1, " rxc1:");
strcat(logText1, itoa(globals.uarts[0].counterRx, convertBuffer4, 10));
strcat(logText1, " rxc2:");
strcat(logText1, itoa(globals.uarts[1].counterRx, convertBuffer4, 10));
LogMessage( logText1);
if (globals.rxProcessSuccess > 0){//if there where new valid telegrams in each channel and processing was successful
globals.rxProcessSuccess = 0;
struct TelegramAcuTo telegramAcuTo; //Working copy
//Put the actual Data into telegram
GlobalsFillTelegramAcuTo(&telegramAcuTo);
//Getting the actual writing telegram instance from outboundqueues
struct TelegramAcuTo* queueTelegram1 = UartAcuGetNextWriteTelegram(&globals.uarts[0]);
struct TelegramAcuTo* queueTelegram2 = UartAcuGetNextWriteTelegram(&globals.uarts[1]);
//Copy Data to queueTelegram
TelegramAcuToInitCopy( queueTelegram1, &telegramAcuTo);
TelegramAcuToInitCopy( queueTelegram2, &telegramAcuTo);
Transmit(&globals.uarts[0]);
Transmit(&globals.uarts[1]);
}
}
//actual processing to IOs
bool GlobalsProcessTelegram( struct TelegramAcuFrom* telegramAcuFrom ){
bool success = true;
struct Telegram* telegram = &telegramAcuFrom->baseTelegram;
//Set AcuFlags1 in Status
uint8_t acuFlags1;
TelegramAcuGetByte(telegram, &acuFlags1, FromAcuFlags1);
StatusSetAcuFlags1(acuFlags1);
//Set AcuStatus in Status
uint8_t acuStatus;
TelegramAcuGetByte(telegram, &acuStatus, FromAcuStatus);
StatusSetAcuStatus(acuStatus);
//special Modification of LED Signals in case of acuFlags1Fault
bool acuFault = StatusGetAcuFlag1(acuFlags1Fault);
// on faultAcu: set only EmergencyStops, Ready, Locked and Unlocked has to be off in this case
bool acuUnlocked = acuFault ? false : StatusGetAcuFlag1(acuFlags1Unlocked);
bool acuLocked = acuFault ? false : StatusGetAcuFlag1(acuFlags1Locked);
bool acuReady = acuFault ? false : StatusGetAcuFlag1(acuFlags1Ready);
//OUT1_STOERUNG_Pin will be set by timerBlink
HAL_GPIO_WritePin(OUT1_BEREIT_GPIO_Port, OUT1_BEREIT_Pin, acuReady ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(OUT1_NEO_GPIO_Port, OUT1_NEO_Pin, StatusGetAcuFlag1(acuFlags1EmergencyLimitTop) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(OUT1_NEU_GPIO_Port, OUT1_NEU_Pin, StatusGetAcuFlag1(acuFlags1EmergencyLimitBottom) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(OUT1_ENTRIEG_GPIO_Port, OUT1_ENTRIEG_Pin, acuUnlocked ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(OUT1_VERRIEG_GPIO_Port, OUT1_VERRIEG_Pin, acuLocked ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(OUT1_LM_GPIO_Port, OUT1_LM_Pin, StatusGetAcuFlag1(acuFlags1LM) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(OUT1_RESERV_GPIO_Port, OUT1_RESERV_Pin, StatusGetAcuFlag1(acuFlags1ReserveFlag1) ? GPIO_PIN_SET : GPIO_PIN_RESET);
return success;
}
2022-11-06 11:46 PM
//last not least Transmit from outboundqueue via Interrupt and TxInterruptroutine
//Tx is disabled as long as TxCompleteInterreupt occures
void Transmit(struct UartAcu* uartAcu){
char logText[256] = {'\0'};
char convertBuffer[16];
char idText[4];
itoa(UartAcuGetId(uartAcu), idText, 10);
if(UartAcuGetTxInProgress(uartAcu)){
strcpy(logText, "t:");
strcat(logText, itoa(__HAL_TIM_GET_COUNTER(globals.timDebugHandle), convertBuffer, 10));
strcat(logText, " ntx");
strcat(logText, idText);
strcat(logText, ":");
strcat(logText, itoa( TelegramAcuToCounter(&uartAcu->outboundQueue[uartAcu->outboundQueue_ReadIndex]), convertBuffer, 10));
}else{
struct TelegramAcuTo* transmitTelegram = UartAcuPeekNewestSendTelegram(uartAcu);
if (transmitTelegram != NULL){
if(!UartAcuPrepareTelegram(uartAcu, transmitTelegram)){
StatusSetInternalFault( internalFaultsTransmitPrepare, UartName(&uartAcu->baseUart) );
}else{
StatusResetInternalFault( internalFaultsTransmitPrepare );
//log the TxStart- and TxStopTime from last Tx
strcpy(logText, "t:");
strcat(logText, itoa(__HAL_TIM_GET_COUNTER(globals.timDebugHandle), convertBuffer, 10));
strcat(logText, " tx");
strcat(logText, idText);
strcat(logText, ":");
strcat(logText, itoa( TelegramAcuToCounter(&uartAcu->outboundQueue[uartAcu->outboundQueue_ReadIndex]), convertBuffer, 10));
strcat(logText, " tst:");
strcat(logText, itoa( uartAcu->timeTxStart, convertBuffer, 10));
strcat(logText, " tsp:");
strcat(logText, itoa( uartAcu->timeTxStop, convertBuffer, 10));
uartAcu->timeTxStart = __HAL_TIM_GET_COUNTER( globals.timDebugHandle);
if(!UartAcuTransmit(uartAcu, transmitTelegram)){
StatusSetInternalFault( internalFaultsTransmit, UartName(&uartAcu->baseUart) );
strcat(logText, " err");
}else{
StatusResetInternalFault( internalFaultsTransmit);
strcat(logText, " ok");
}
}
UartAcuDeleteActualOutboundTelegram( uartAcu );//dont care about transmit success, next cycle generates a new sendtelegram
}
}
LogMessage( logText);
}
//actual transmitting called on both UARTs from outboundqueues
bool UartAcuTransmit(struct UartAcu* uartAcu, struct TelegramAcuTo* telegram){
//dequeueing and sending the actual telegram from outboundQueue
bool success = false;
UartAcuTxInProgress(uartAcu, true); //Disable next Tx until IRQ triggers
if ( HAL_UART_Transmit_IT( uartAcu->baseUart.handle, TelegramData(&telegram->baseTelegram) , TelegramLength(&telegram->baseTelegram)) == HAL_OK ){
success = true;
}
return success;
}
void GlobalsUartTxInterruptCallback(UART_HandleTypeDef *uartHandle){
struct UartAcu* uartInstance = GlobalsFindAcuUartFromHandle(uartHandle);
if (uartInstance != NULL){
uartInstance->timeTxStop = __HAL_TIM_GET_COUNTER( globals.timDebugHandle);
UartAcuTxInProgress(uartInstance, false); //enable for next Tx
}
}
2022-11-06 11:56 PM
The inboundqueue and outboundqueue are 16 telegrams long each and taken from the stack
#define queueLength 16
struct UartAcu {
struct Uart baseUart;
struct TelegramAcuFrom inboundQueue[queueLength];
struct TelegramAcuTo outboundQueue[queueLength];
....
}
I dont use heap at all, which was a recommendation from STCommunity and I convertet the whole project fom C++ to C, which was also recommended in earlier questions. (And in this process I have found two bugs in indexing, which made my C++ unstable)
But one can see that the code came fom C++ : all ist organized in structs, and I have some base-structs for special structs, which have been the base classes of the derived classes. Function names start with the struct Names, they work on.
2022-11-07 12:18 AM
This is the logging with transmit:
*: t:7926 rxc1:111 rxc2:51
*: 1:112 2:52 //successful processing rx telegrams in main cycle
*: t:10208 rxc1:112 rxc2:52 //main cycle logging of the last received telegram counters
*: t:10641 tx1:45 tst:41796 tsp:45704 ok //since we have success in receve we send on both UARTs
*: t:11036 tx2:45 tst:42189 tsp:46098 ok // tst and tsp are timers from !last! tx, (not this)
*: 1:113 2:53 //successful processing rx telegrams in main cycle
*: t:13489 rxc1:113 rxc2:53 //main cycle logging of the last received telegram counters
*: t:13919 ntx1:0 //cant tx, because tx from t:10641 and t:11036 are still running
*: t:14102 ntx2:0 //so i protect tx buffer until txCompleteIrq occures, this is normal behave
*: t:16162 rxc1:113 rxc2:53 //next maincycle report, that there is no rx happend
*: t:18318 rxc1:113 rxc2:53 //for a long time
*: t:20475 rxc1:113 rxc2:53 //look at the timestamps and compare to
*: t:22643 rxc1:113 rxc2:53 //next tx operation (tsp:14575 tsp:14970)
*: t:24894 rxc1:113 rxc2:53 //which locks tsp from t:10641 tx operation
*: t:27144 rxc1:113 rxc2:53 //txCompleteInterrupt have already occured
*: t:29393 rxc1:113 rxc2:53//but rx is still not working
*: t:31644 rxc1:113 rxc2:53
*: t:33893 rxc1:113 rxc2:53
*: t:36144 rxc1:113 rxc2:53
*: t:38394 rxc1:117 rxc2:57 //first successfull rx after last tx
*: t:40644 rxc1:117 rxc2:57 //one maincycle without new rx interrupt can be normal
*: t:42892 rxc1:117 rxc2:58 //second successfull rx after last tx, so the telegram counter step is valid
*: 1:118 2:58 //we can process
*: t:45270 rxc1:118 rxc2:58
*: t:45704 tx1:46 tst:10666 tsp:14575 ok
*: t:46100 tx2:46 tst:11062 tsp:14970 ok
*: 1:119 2:59