cancel
Showing results for 
Search instead for 
Did you mean: 

How can I send more than 3 CAN messages in a row using HAL library

Posted on October 07, 2017 at 14:24

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
12 REPLIES 12
Posted on October 07, 2017 at 16:15

One interrupt might cover multiple events.

Callbacks must not block.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Richard Lowe
Senior III
Posted on October 07, 2017 at 23:37

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 );
}
�?�?�?�?

T J
Lead
Posted on October 08, 2017 at 03:54

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;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Posted on October 08, 2017 at 12:48

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.

Posted on October 08, 2017 at 15:54

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Franco Spediacci
Associate II
Posted on October 09, 2017 at 10:11

Hi Paul,

Try to check here

https://community.st.com/0D50X00009Xkg2KSAR

Posted on October 09, 2017 at 20:49

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.

Posted on October 11, 2017 at 04:26

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.

Posted on October 11, 2017 at 04:46

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..