2017-10-07 05: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 #can2017-10-07 07:15 AM
One interrupt might cover multiple events.
Callbacks must not block.
2017-10-07 02: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 );
}
�?�?�?�?
2017-10-07 06: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;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
2017-10-08 03: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.
2017-10-08 08: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.
2017-10-09 01:11 AM
Hi Paul,
Try to check here
2017-10-09 01: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.
2017-10-10 07: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.
2017-10-10 09: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.