How can I send more than 3 CAN messages in a row using HAL library
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-07 5:24 AM
I am using the latest HAL library to transmit CAN messages using interrupts. I am aware of the 3 mailboxes limitation but I would like to know if someone had implemented a queue system that transmit most of the CAN messages.
I tried to follow the HAL_StatusTypeDef HAL_CAN_Transmit_IT(CAN_HandleTypeDef* hcan) function.
So putting counters on every return of this function equals 1 vs the TX interrupt, my counters are not matching.
To enable the TX interrupts, I simply set:
HAL_NVIC_SetPriority(CAN1_TX_IRQn,3, 0);
HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);Do I need to set another flag? from my understanding, using the HAL_CAN_Transmit_IT will generate an interrupt, is that correct?
#hal #can- Labels:
-
CAN
-
STM32Cube MCU Packages
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-07 7:15 AM
One interrupt might cover multiple events.
Callbacks must not block.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-07 2:37 PM
Here's the approach I used to utilize all 3 mailboxes. Segmented frames, very fast.
void can_tx_process( void const * argument )
{
osEvent evt;
for( ;; )
{
evt = osMessageGet( can_tx_handle, osWaitForever );
if( evt.status == osEventMessage )
{
message_t *msg = (message_t*) evt.value.p;
if( ( msg->frame <= REQUEST_APPROVED ) && ( msg->msg_len < HOST_RX_BUFFER ) )
{
// 1. Find out how many bytes are going on the data section
uint8_t data_index = 0; // Offset to the data section
uint8_t frame_count = ( msg->msg_len / 8 ) + ( ( msg->msg_len % 8 ) ? 1 : 0 );
do
{
// 2. Wait to write to hardware Tx buffer
osSemaphoreWait( can_tx_semHandle, osWaitForever );
// 3. Setup the Message ID encoding
// Setup each message segment
// 4. Attach data
hcan1.pTxMsg->IDE = CAN_ID_EXT;
hcan1.pTxMsg->RTR = ( msg->frame != REQUEST_EVENT ) ? CAN_RTR_DATA : CAN_RTR_REMOTE;
hcan1.pTxMsg->DLC = ( msg->msg_len >= 8 ) ? 8 : ( msg->msg_len );
memcpy( (void*) hcan1.pTxMsg->Data, (void*)&msg->buffer[data_index], ( hcan1.pTxMsg->DLC ) );
msg->msg_len -= ( hcan1.pTxMsg->DLC );
data_index += ( hcan1.pTxMsg->DLC );
// 5. Transmit and enable interrupts
HAL_CAN_Transmit_IT( &hcan1 );
osDelay(1);
if( frame_count )
frame_count--;
} while( frame_count );
}
osPoolFree( message_pool, msg );
}
HAL_CAN_Sleep( &hcan1 );
}
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
From there I have a counting semaphore in the interrupt, 3, for each mailbox.
void HAL_CAN_TxCpltCallback( CAN_HandleTypeDef* hcan )
{
osSemaphoreRelease( can_tx_semHandle );
}
�?�?�?�?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-07 6:54 PM
I check this every 10mS approx, : polling method
as you can see if there are no messages to send, we exit in under 200nSecond.
void checkCanTxMsgs(void) {// this is a non-ordered packet transmitter.
//int canMsgCounter =0;
if (!canTxMsgTableEMPTY)
if ((canTxMsgOUT != canTxMsgIN) || canTxMsgTableFULL ) // non-ordered packet transmitter
if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) // (1)
{
CAN->sTxMailBox[0].TDTR = canTxMsgLength[canTxMsgOUT];// (2) length
CAN->sTxMailBox[0].TDLR = canTxMsgBottomWord[canTxMsgOUT]; // (3) 4bytes
CAN->sTxMailBox[0].TDHR = canTxMsgTopWord[canTxMsgOUT]; // (3) 4bytes
CAN->sTxMailBox[0].TIR = ((uint32_t)canTxMsgID [canTxMsgOUT] << 21 | CAN_TI0R_TXRQ); // (4)// send it now if the line is idle
// destroy old data to be sure we only transmit fresh data
// not needed
canTxMsgLength[canTxMsgOUT] = 0;
canTxMsgBottomWord[canTxMsgOUT] = 0;
canTxMsgTopWord[canTxMsgOUT]= 0;
canTxMsgID [canTxMsgOUT]= 0;
++canTxMsgOUT &=0x3F;// 64 buffer elements
canTxMsgTableFULL = false;
canTxMsgOverrun = false;
//canMsgCounter ++;
}
if ((canTxMsgOUT != canTxMsgIN) || canTxMsgTableFULL)
if ((CAN->TSR & CAN_TSR_TME1) == CAN_TSR_TME1) // (1)
{
CAN->sTxMailBox[1].TDTR = canTxMsgLength[canTxMsgOUT];// (2) length
CAN->sTxMailBox[1].TDLR = canTxMsgBottomWord[canTxMsgOUT]; // (3) 4bytes
CAN->sTxMailBox[1].TDHR = canTxMsgTopWord[canTxMsgOUT]; // (3) 4bytes
CAN->sTxMailBox[1].TIR = ((uint32_t) canTxMsgID [canTxMsgOUT] << 21 | CAN_TI1R_TXRQ); // (4)// send it now if the line is idle
// destroy old data to be sure we only transmit fresh data
// not needed
canTxMsgLength[canTxMsgOUT] = 0;
canTxMsgBottomWord[canTxMsgOUT] = 0;
canTxMsgTopWord[canTxMsgOUT]= 0;
canTxMsgID [canTxMsgOUT]= 0;
++canTxMsgOUT &=0x3F;// 64 buffer depth
canTxMsgOverrun = false;
canTxMsgTableFULL = false;
//canMsgCounter ++;
}
if ((canTxMsgOUT != canTxMsgIN) || canTxMsgTableFULL)
if ((CAN->TSR & CAN_TSR_TME2) == CAN_TSR_TME2) // (1)
{
CAN->sTxMailBox[2].TDTR = canTxMsgLength[canTxMsgOUT];// (2) length
CAN->sTxMailBox[2].TDLR = canTxMsgBottomWord[canTxMsgOUT]; // (3) 4bytes
CAN->sTxMailBox[2].TDHR = canTxMsgTopWord[canTxMsgOUT]; // (3) 4bytes
CAN->sTxMailBox[2].TIR = ((uint32_t) canTxMsgID[canTxMsgOUT] << 21 | CAN_TI2R_TXRQ); // (4)// send it now if the line is idle
// destroy old data to be sure we only transmit fresh data
// not needed
canTxMsgLength[canTxMsgOUT] = 0;
canTxMsgBottomWord[canTxMsgOUT] = 0;
canTxMsgTopWord[canTxMsgOUT]= 0;
canTxMsgID [canTxMsgOUT]= 0;
++canTxMsgOUT &=0x3F;// 64 buffer elements
canTxMsgOverrun = false;
canTxMsgTableFULL = false;
//canMsgCounter ++;
}
if (canTxMsgOUT == canTxMsgIN)canTxMsgTableEMPTY = true;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-08 3:48 AM
Thats possible. But how can I check easily how many mailboxes are available, using a interrupt base Queue. not sure if I should be able to transmit 3 messages in a row when all the mailboxes are available.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-08 8:54 AM
Well here I'd check which ones are empty via CAN_TSR and fill those. The question is not can I stuff three, but rather which ones can I stuff from the queue available for transmission.
Couldn't you simply do something like this?
uint32_t HAL_CAN_TransmitFree(CAN_HandleTypeDef* hcan)
{ uint32_t i = 0;if ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0)
i++;if ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1)
i++;if ((hcan->Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2)
i++;return(i);
}Review the Reference Manual to understand how the hardware functions.
Review the Library Source to understand how that works.
Up vote any posts that you find helpful, it shows what's working..
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-09 1:11 AM
Hi Paul,
Try to check here
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-09 1:49 PM
Exactly what I use as well:
/*
* HAL style:
* Check whether there's at least one tx mailbox free
*/
bool HAL_CAN_txRdy(CAN_HandleTypeDef* handle) {
if (((handle->Instance->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) || ((handle->Instance->TSR & CAN_TSR_TME1) == CAN_TSR_TME1)
|| ((handle->Instance->TSR & CAN_TSR_TME2) == CAN_TSR_TME2))
return true;
return false;
}�?�?�?�?�?�?�?�?�?�?
Which in turn can be used e.g. like this:
void CAN_Writer::wait_ip_rdy(uint32_t timeout_ms) {
while (!HAL_CAN_txRdy(&(this->handle))) {
if (inISR() || !timeout_ms) /* ignore timeout in ISR */
throw Error('TX timeout!');
else {
timeout_ms--;
HAL_Delay(1);
}
}
}�?�?�?�?�?�?�?�?�?�?
So the thread wanting to send a message would wait until a tx mailbox becomes free or throws.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-10 7:26 PM
I implemented something like this:
int CCanBus::HAL_CAN_TransmitFree()
{ int i=0; if ((CanHandle.Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) i++; if ((CanHandle.Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) i++; if ((CanHandle.Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2) i++; return(i);}I also implement a TX CAN Queue which runs every 1ms and uses the above function to transmit one or more CAN messages based on the mailboxes available:
void CCanOpenSlave::ProcessQueue()
{ CAN_Message txMsg; int tmp; //Check for Empty CAN TX mailboxes tmp = canBus.HAL_CAN_TransmitFree(); if(tmp>0) { for( uint32_t i = 0; i<tmp-1; i++ ) { if(canBus.GetMessageInQueue(&txMsg)) { canBus.can_Write(txMsg); //cantx_counter++; } } }}The odd thing is even when I get that there are 3 mailboxes available, the HAL Transmit reports error:
if(HAL_CAN_Transmit_IT(&CanHandle) != HAL_OK)
{ cantx_counter++; return 0; } else { packetsend_counter++; return 1; }the variable cantx_counter increments very often through my program running. Based on the implementation it throws an error when all mailboxes are busy which it is confusing because I do the check before transmitting a message from the queue.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2017-10-10 9:46 PM
Then you'll need to walk the code to understand why it got an error, or if you have a race condition where you start transmission at multiple points, or in multiple threads.
Up vote any posts that you find helpful, it shows what's working..
