cancel
Showing results for 
Search instead for 
Did you mean: 

How to send multiple(more than 3) sequential messages via CAN bus

HolySh*tItWorks
Associate II

HI all,

as the title says, I am investigating how to correctly send more than 3 consecutive messages via CAN Bus to the network. As per now, the network has only two devices connected, which one only sends and the other only receives.

The transmitter is a custom board with STM32F722VET MCU, the receiver a STM32L432KC. Both have a transceiver.

It is all working well, I can connect, send and receive packets between the two nodes, but the problem arises when I want to send more than 3 packets sequentially. I know that the stm32 CAN has 3 mailboxes used to send the messages, and in fact once the mailboxes are full, nothing is sent, and the mailboxes are cleared once the TX gets the ack.

My needs are:

1) transmit 12 packets in a row every 3ms

2) I cannot use a queueing system because I cannot mix packets from a loop with packets from another loop (those packets represents spatial coordinates in time, so different loops represents different "epochs")

Actually I am trying to reimplement the receiver with a STM32F7 Nucleo board, hoping that a 216MHz clocked board would be faster enough to send ack back to the TX.

I'd like to know also you opinion, community. Below my code for initialization, tx and rx handling.

//CAN INIT TX node
CAN_Handle.Instance = CAN1;
    CAN_Handle.Init.Prescaler =3;
    CAN_Handle.Init.Mode = CAN_MODE_NORMAL;
    CAN_Handle.Init.SyncJumpWidth = CAN_SJW_1TQ;
    CAN_Handle.Init.TimeSeg1 = CAN_BS1_15TQ
    CAN_Handle.Init.TimeSeg2 = CAN_BS2_2TQ;
    CAN_Handle.Init.TimeTriggeredMode = DISABLE;
    CAN_Handle.Init.AutoBusOff = ENABLE;
    CAN_Handle.Init.AutoWakeUp = DISABLE;
    CAN_Handle.Init.AutoRetransmission = DISABLE;
    CAN_Handle.Init.ReceiveFifoLocked = DISABLE;
    CAN_Handle.Init.TransmitFifoPriority = DISABLE;
    
    HAL_CAN_Init(&CAN_Handle);

the RX init(STM32L432KC):

hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 2;
  hcan1.Init.Mode = CAN_MODE_NORMAL;
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_13TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
  hcan1.Init.TimeTriggeredMode = DISABLE;
  hcan1.Init.AutoBusOff = ENABLE;// DISABLE;
  hcan1.Init.AutoWakeUp = DISABLE;
  hcan1.Init.AutoRetransmission = DISABLE;
  hcan1.Init.ReceiveFifoLocked = DISABLE;
  hcan1.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }

the TX send:

 //this snippet is called every 3 ms                               
{
 DGNSTC_SendMsg(G_XY_ID,&p);
 DGNSTC_SendMsg(G_ZT_ID,&p);
 DGNSTC_SendMsg(R_XY_ID,&p);
 DGNSTC_SendMsg(R_ZT_ID,&p);
 DGNSTC_SendMsg(S_XY_ID,&p);
 DGNSTC_SendMsg(O_XY_ID,&p);
}    
 
//and here the definition of the above method:
void 
DGNSTC_SendMsg(DGNSTC_Msg_Id_t id,Pos_2D_t* payload)
{
	memset(buffer,0x0U,DGNSTC_MSG_LEN);
	SRLZ_Serialize2DPos(payload,buffer);
	CAN_SendMsg(id,buffer,DGNSTC_MSG_LEN); //this is the send method, uses HAL_CAN_AddTxMessage
}                          

on RX side, the snippet used to receive packets:

while (1)
  {
    /* USER CODE END WHILE */
			if(HAL_CAN_GetRxFifoFillLevel(&hcan1,CAN_RX_FIFO0))
			{
				HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&head,data);
				SRLZ_Deserialize(data,&pos);
				memset(asd,0x0,sizeof(char)*50);				
				HAL_UART_Transmit(&huart2,(uint8_t*)asd,50,10);
                        }
  }

Oh, and I forgot to mention that the receiver forwards to a UART at 921600Baud the received messages.

Thanks for the help!

4 REPLIES 4

Perhaps queue the things and send them as one of the available three slots frees up?

How would queuing not enforce desired ordering/grouping? Perhaps take a mutex if the threads run concurrently.

Pretty sure calling every 3ms and blocking are at some point going to be mutually exclusive and you'll deadlock. Probably going to need to fail/drop gracefully if you run out of bandwidth, or the end node(s) stop acknowledging.

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

Hi CLive, thank you for the reply.

Now I've modified the approach, similarly as you told:

1)I need in reality 8 msgs each 3ms, so I've created a queue (depth 😎 containing my messages

2) every time I need to send a packet, I enqueue it and I try to send them, until there are free mailboxes or until the queue is not empty

3) I tried to hook the TxMailbox0Complete TxMailbox2Complete TxMailbox2Complete callbacks, in order to monitor when a free slot is available.

4) if it is available and If there are messages to be sent, send it

My actual problem is that no interrupt is fired, and I do not know why it does not work. I've dig the forum and google, and followed all the rules found, and it seems that the configuration is correct, but no it is fired.

my init code:

HAL_CAN_Init();
HAL_CAN_ApplyFilter(0,0,0,0);
	HAL_CAN_RegisterCallback(CAN_GetHandle(),HAL_CAN_TX_MAILBOX0_COMPLETE_CB_ID,DGNSTC_HandleMailboxComplete);
	HAL_CAN_RegisterCallback(CAN_GetHandle(),HAL_CAN_TX_MAILBOX1_COMPLETE_CB_ID,DGNSTC_HandleMailboxComplete);
	HAL_CAN_RegisterCallback(CAN_GetHandle(),HAL_CAN_TX_MAILBOX2_COMPLETE_CB_ID,DGNSTC_HandleMailboxComplete);
HAL_CAN_ActivateNotification(CAN_GetHandle(),CAN_IT_TX_MAILBOX_EMPTY);
 
HAL_CAN_Start(CAN_GetHandle());

Then I've checked also if the IRQwere activated, and Yes, I've activated them:

void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
    __HAL_RCC_CAN1_CLK_ENABLE();
    CAN_PinSetup();
    
    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 4, 0);
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
    HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 4, 0);
    HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
}

in debug mode, no interrupt shows up. I've tried to activate all the possible it for CAN, in order to see if at least something was fired, but nothing showed up.

What do you think about?

best regards!

HolySh*tItWorks
Associate II

EDIT: I've also found out that, if I do transfer in this way:

while (queue_msg.used >0)
{
 while(GetTxMailboxFreeLevel(pCAN)) == 0){}; // do nothing if no mailbox is free
CAN_SendMsg();
}

I see as output not what I'd expect. What I'd expect is that, after first three msgs sent, the while would wait for the first free slot, and then sends the packet, and so on. I've numbered from 1 to 8 my packets (IDs), and at the receiver, I can only see 1,2,3,6,8 packets, in this order, so the packets 4,5 and 7 are somehow lost somewhere. I'd expected to see 1,2,3,4,5,6,7,8.

T J
Lead

I use this code in the foreground and do not access this from any other thread to be thread safe.

in the systick interrupt callback, I set this flag:

void HAL_SYSTICK_Callback(void) {

   //   SysTick_Handler_Flag = true;

   mS++;

   mSint = true;

   if (mS == 1000) {

       secondInt = true;

       mS = 0;

   }

.....

I pass though this boxed code below after this statement in the foreground, so its only checked every mS, you can speed that up if you wish.

   if(mSint) {

       mSint = false;

if(!canTxMsgTableEMPTY) {
  if ((canTxMsgOUT != canTxMsgIN) || canTxMsgTableFULL)  // non-ordered packet transmitter
  {        	            
    if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) 	// (1) 
        {  // fifo is idle
            if (showRxCanFrames)
                sendCanTxFrametoConsole();
                        
            CAN->sTxMailBox[0].TDTR = canTxMsgLength[canTxMsgOUT];     // (2) length
            CAN->sTxMailBox[0].TDLR = canTxMsgBottomWord[canTxMsgOUT];   // (3) 4bytes
            CAN->sTxMailBox[0].TDHR = canTxMsgTopWord[canTxMsgOUT];      // (3) 4bytes
            	// send it now if the line is idle  // will queue up until line is idle
            CAN->sTxMailBox[0].TIR  = ((uint32_t)canTxMsgID[canTxMsgOUT] << 21 | CAN_TI0R_TXRQ);  // (4)
 
            // destroy old data to be sure we only transmit fresh data
            // not needed
            canTxMsgLength[canTxMsgOUT] 	= 0;// this buffered packet has been sent			
            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) 
            {
                if (showRxCanFrames)
                    sendCanTxFrametoConsole();				
                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) 
            {
                if (showRxCanFrames)
                    sendCanTxFrametoConsole();				
                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;
    }
  }
}