STM32 learning from scratch -- detailed explanation of CAN communication code

Posted by kante on Wed, 09 Feb 2022 22:35:14 +0100

STM32 learning from scratch @EnzoReventon

Detailed explanation of CAN communication code

Related links:

STM32 learning from scratch - physical layer of CAN communication protocol
CAN bus specification v2 0 version
Introduction to CAN bus
Zhou Ligong - Chinese version of CAN protocol

reference material:
[wildfire EmbedFire] practical guide for STM32 Library Development -- Based on wildfire Decepticon development board
[punctual atom] STM32F4 Development Guide - library function version_ V1. two
[ST] STM32F4xx Chinese Reference Manual
CAN bus specification v2 0 version
Introduction to CAN bus
Zhou Ligong - Chinese version of CAN protocol

======================================================
Some time ago, due to the busy affairs of the school, the shift was interrupted for some time. We understand that we will continue to share our learning notes in the future~

stay STM32 learning process from scratch - CAN related structures above In, we have introduced the relevant structures to be used in CAN communication in detail, so we won't repeat them here.
This paper mainly introduces the relevant codes of CAN communication experiment in detail. The development board used is the F4 development board of punctual atomic explorer. If there are deficiencies, please give us your advice~

Experimental purpose

  1. Two communication modes of CAN are used to transmit and receive data.
  2. Display the received and sent data and mode status by using the serial port debugging assistant.
  3. The data content sent is a random number, which is an increasing number of 0-255.
  4. Press KEY_RES realizes the switching of CAN mode.
  5. Press KEY_1. Realize the sending and receiving of primary data.
  6. The flashing LED indicates that the program is running normally.

hardware design

  1. First, consult the chip Manual of STM32F407ZGT6 to understand the transceiver pin of CAN communication. In this experiment, we use PA11 and PA12 as RX and TX functions of CAN communication. The rest UART, keys and LED lights are ready-made on the development board. Users can choose according to the actual situation of their own development board. This configuration will not be explained in detail in this paper.

  2. Connect PA11 and can with jumper cap_ RX connection, PA12 and CAN_TX connection. As shown in the figure below.

  3. Connect JLINK and serial port to facilitate debugging.

Software design process

  1. CAN.c
  • Initialize RCC clock
  • Initialize GPIO
  • Pin reuse
  • Initialize CAN related structures
  • Configure filter
  • Initialization interrupt (if used)
  • The transceiver structure of CAN is set with cantxmsg & canrxmsg
  1. main.c
  • Call corresponding peripheral initialization functions (LED, UART, KEY, interrupt, CAN)
  • Write the corresponding transceiver function

Code explanation

  1. can.h
//CAN1 receive RX0 interrupt enable
#define CAN1_RX0_INT_ENABLE 	                 0 				// 0, not enabled; 1. Enable								    
										 							 				    
u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode);	//CAN initialization
 
u8 CAN1_Send_Msg(u8* msg,u8 len);							//send data

u8 CAN1_Receive_Msg(u8 *buf);								//receive data 
#endif
  1. can.c
  • CAN initialization function.
//CAN initialization
//tsjw: resynchronize jump time unit Range: CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2: time unit of time period 2 Range: CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1: time unit of time period 1 Range: CAN_BS1_1tq ~CAN_BS1_16tq
//BRP: baud rate divider Range: 1~1024; tq=(brp)*tpclk1
//Baud rate = Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal, normal mode; CAN_Mode_LoopBack, loopback mode;
//The clock of Fpclk1 is set to 42M during initialization. If Can1 is set_ Mode_ Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,CAN_Mode_LoopBack);
//Baud rate: 42M/((6+7+1)*6)=500Kbps
//Return value: 0, initialization OK;
//       Other, initialization failed; 

u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
	//Define structure variables
  	GPIO_InitTypeDef       GPIO_InitStructure;    	 	//GPIO initialization structure
	CAN_InitTypeDef        CAN_InitStructure;	   		//CAN initialization structure
  	CAN_FilterInitTypeDef  CAN_FilterInitStructure;		//CAN filter structure

	//Interrupt enable function, if you need to use interrupt, in can H is enabled in the header file, then the structure takes effect.
	#if CAN1_RX0_INT_ENABLE 
	   	NVIC_InitTypeDef  NVIC_InitStructure;
	#endif


	//Enable correlation clock
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);				//Enable GPIOA clock	                   											 

  	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);				//Enable CAN1 clock	

	//Initialize GPIO
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;						//Reuse function
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//Push pull output
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//Pull up
    GPIO_Init(GPIOA, &GPIO_InitStructure);//Initialize PA11,PA12

	//Pin multiplexing mapping configuration
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1); 				//GPIOA11 multiplexed to CAN1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1); 				//GPIOA12 multiplexed to CAN1

	//CAN unit setting
   	CAN_InitStructure.CAN_TTCM=DISABLE;									//Non time triggered communication mode   
  	CAN_InitStructure.CAN_ABOM=DISABLE;									//Software automatic offline management	  
  	CAN_InitStructure.CAN_AWUM=DISABLE;									//SLEEP mode wakes up through software (clear SLEEP bit of can - > MCR)
  	CAN_InitStructure.CAN_NART=ENABLE;									//Automatic message transmission is prohibited 
  	CAN_InitStructure.CAN_RFLM=DISABLE;									//The message is not locked, and the new one overwrites the old one  
  	CAN_InitStructure.CAN_TXFP=DISABLE;									//The priority is determined by the message identifier 
  	CAN_InitStructure.CAN_Mode= mode;	 								//Mode setting 
  	CAN_InitStructure.CAN_SJW=tsjw;										//The resynchronization jump width (Tsjw) is tsjw+1 time unit_ SJW_ 1tq~CAN_ SJW_ 4tq
  	CAN_InitStructure.CAN_BS1=tbs1; 									//Tbs1 range CAN_BS1_1tq ~CAN_BS1_16tq
  	CAN_InitStructure.CAN_BS2=tbs2;										//Tbs2 range CAN_BS2_1tq ~ 	 CAN_BS2_8tq
  	CAN_InitStructure.CAN_Prescaler=brp;  								//The frequency division coefficient (Fdiv) is brp+1	
  	CAN_Init(CAN1, &CAN_InitStructure);   								// Initialize CAN1 

	//Configure filter
 	CAN_FilterInitStructure.CAN_FilterNumber=0;	  						//Filter 0
  	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; 		//Set filter mode, mask mode
  	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; 		//32 bit 
  	CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;					//32-bit ID (high)
  	CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;						//32-bit ID (low)
  	CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;				//32-bit MASK (high)
  	CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;					//32-bit MASK (low)
   	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;	//Filter 0 is associated to FIFO0
  	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; 				//Activate filter 0
  	CAN_FilterInit(&CAN_FilterInitStructure);							//Filter initialization

	//Interrupt initialization function. If an interrupt needs to be used, it is in can If h is enabled in the header file, the initialization structure takes effect.
	#if CAN1_RX0_INT_ENABLE
		CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO 0 message registration interruption is allowed		    
	  
	  	NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
	  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // Primary priority is 1
	  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            // The secondary priority is 0
	  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	  	NVIC_Init(&NVIC_InitStructure);
	#endif

	return 0;
}   
  • The interrupt service function also needs to be enabled in the header file if it is used. The same function is used to receive the transmitted data by using interrupt.
#if CAN1_RX0_INT_ENABLE 	// Enable rx0 interrupt
//Interrupt service function			    
void CAN1_RX0_IRQHandler(void)
{
  	CanRxMsg RxMessage;
	int i=0;
    CAN_Receive(CAN1, 0, &RxMessage);
	for(i=0;i<8;i++)
	printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
  • CAN sending function: CAN1_Send_Msg
//can sends a group of data (fixed format: ID 0X12, standard frame, data frame)	
//len: data length (max. 8)				     
//msg: data pointer, maximum 8 bytes
//Return value: 0, successful;
//		 Others, failure;
u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
  u8 mbox;
  u16 i=0;
  CanTxMsg TxMessage;
  TxMessage.StdId=0x12;	 								// The standard identifier is 0
  TxMessage.ExtId=0x12;	 								// Set extension identifier (29 bits)
  TxMessage.IDE=0;		  								// Use extended identifier
  TxMessage.RTR=0;		  								// The message type is data frame, 8 bits per frame
  TxMessage.DLC=len;							 		// Send two frames of information
  for(i=0;i<len;i++)
  TxMessage.Data[i]=msg[i];								// First frame information          
  mbox= CAN_Transmit(CAN1, &TxMessage);   
  i=0;
  while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;	//Wait for sending to end
  if(i>=0XFFF)return 1;
  return 0;		
}
  • CAN receiving function: CAN1_Receive_Msg
//can port receiving data query
//buf: data buffer;	 
//Return value: 0, no data received;
//		 Other, the length of received data;
u8 CAN1_Receive_Msg(u8 *buf)
{		   		   
 	u32 i;
	CanRxMsg RxMessage;
    if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;		//No data received, exit directly 
    CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);				//Read data	
    for(i=0;i<RxMessage.DLC;i++)
    buf[i]=RxMessage.Data[i];  
	return RxMessage.DLC;	
}
  1. main.c
    Main function
int main(void)
{ 
	u8 key;
	u8 i=0,t=0;
	u8 cnt=0;
	u8 canbuf[8];
	u8 res;
	u8 mode=1;										//CAN working mode; 0, normal mode; 1. Loopback mode
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//Set system interrupt priority group 2
	delay_init(168);   								//Initialization delay function
	uart_init(115200);								//The baud rate of initialization serial port is 115200
	LED_Init();										//Initialize LED 
 	LCD_Init();										//LCD initialization 
	KEY_Init(); 									//Key initialization  
	
	CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,CAN_Mode_LoopBack);//CAN initialization loopback mode, baud rate 500Kbps  

while(1)
	{
		key=KEY_Scan(0);
		if(key==KEY0_PRES)							//Press KEY0 to send data once
		{
			for(i=0;i<8;i++)
			{
				canbuf[i]=cnt+i;					//Fill send buffer
				if(i<4)
				{
					printf("\r\nSend Data[%d]:  ",i);
					printf(" %d ",canbuf[i]);
				}
				else 
				{
					printf("\r\nSend Data[%d]:  ",i);
					printf(" %d ",canbuf[i]);
				}
 			}
			res=CAN1_Send_Msg(canbuf,8);			//Send 8 bytes 
			if(res)
			{
				printf("\rSend Data: Failed");
				printf("\r=======================");
			}
			else 
			{
				printf("\rSend Data: OK");
				printf("\r=======================");
			}
		}
		else if(key==WKUP_PRES)						//WK_ Press up to change the working mode of CAN
		{	   
			mode=!mode;
			CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,mode);	//CAN normal mode initialization, normal mode, baud rate 500Kbps
			if(mode==0)								//Ordinary development board, 2 required
			{
				printf("\r=======================");
				printf("\n\rMODE: Nnormal Mode ");	 
				printf("\r=======================");
			}
			else 									//Loopback mode, a development board can be tested
			{
				printf("\r=======================");
				printf("\n\rMODE: LoopBack Mode");
				printf("\r=======================");
			}
 			POINT_COLOR=BLUE;						//Set font to blue 
		}		 
		key=CAN1_Receive_Msg(canbuf);
		if(key)										//Data received
		{	
			printf("\r=/=/=/=/=/=/=/=/=/=/=/=");	
 			for(i=0;i<key;i++)
			{							    
				if(i<4)
				{
				printf("\r\nReceive Data[%d]:  ",i);
				printf(" %d ",canbuf[i]);
				}
				else 
				{	
				printf("\r\nReceive Data[%d]:  ",i);
				printf(" %d ",canbuf[i]);
				}
 			}
			printf("\r=/=/=/=/=/=/=/=/=/=/=/=");	
		}
		t++; 
		delay_ms(10);
		if(t==20)
		{
			LED0=!LED0;								//Prompt that the system is running	
			t=0;
			cnt++;
			printf("\n %d ",cnt);
		}		   
	} 
} 

Effect display

  1. Normal mode
    Counter 0-255 to count, press wak_ Press the up button to switch to the normal mode, and press the key0 button to realize one-time data transmission, as shown in the figure below.
    Because it is only sent by a single machine, the information cannot be received.
  2. Loopback mode:
    Press the wak-0 counter to count_ Press the up button to switch to the loop mode, and press the key0 button to realize the sending and receiving of primary data, as shown in the figure below.

Topics: Embedded system stm32 ARM can