STM32CubeMX and HAL Library Learning -- simple CAN loopback test

Posted by dough boy on Mon, 20 Dec 2021 15:34:28 +0100

preface

I, Xiaobai, recently read some information about CAN protocol and bxCAN peripherals of STM32, and simply do a CAN loop test exercise. This is just a simple practice record for beginners. If you study, you still need to see the corresponding tutorial.

Tools and versions used:
STM32CubeMX version: 6.3 0
HAL Library: stm32cubef4 firmware package v1 twenty-six point one
MDK-ARM: V5.32.0.0
Development board: Wildfire Decepticon development board V2 (the main control chip is STM32F407ZGT6)

The approximate block diagram is as follows:

STM32CubeMX generates initialization code

Clock and serial port configuration are omitted.
About CAN configuration:

Open CAN1 RX0 interrupt permission in NVIC and set the priority.

Edit code in MDK-ARM

The setting of CAN has not been completed. The CubeMX only sets the mode. Next, set the filter. I configured the list mode and filtered the extension ID 0x2233 and standard ID 0.

/*
 * Function name: CAN_Filter_Config
 * Description: CAN filter configuration
 * Input: None
 * Output: None
 * Call: internal call
 */
static void CAN_Filter_Config(void)
{
	CAN_FilterTypeDef  CAN_FilterTypeDef;

	/*CAN Filter initialization*/
	CAN_FilterTypeDef.FilterBank=0;						//Filter group 0
	CAN_FilterTypeDef.FilterMode=CAN_FILTERMODE_IDLIST;	//Working in list mode
	CAN_FilterTypeDef.FilterScale=CAN_FILTERSCALE_32BIT;	//The filter bit width is a single 32-bit.
	/* The enable filter is compared and filtered according to the content of the flag. If the extension ID is not as follows, it will be discarded. If yes, it will be stored in FIFO0. */

	CAN_FilterTypeDef.FilterIdHigh= ((((uint32_t)0x2233<<3)|
										 CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;		//ID to filter 
	CAN_FilterTypeDef.FilterIdLow= (((uint32_t)0x2233<<3)|
									     CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //ID to filter 
	CAN_FilterTypeDef.FilterMaskIdHigh= 0;		//High order of the second ID
	CAN_FilterTypeDef.FilterMaskIdLow= 0;			//Low order of the second ID
	CAN_FilterTypeDef.FilterFIFOAssignment=CAN_FILTER_FIFO0 ;	//The filter is associated to FIFO0
	CAN_FilterTypeDef.FilterActivation=ENABLE;			//Enable filter
	HAL_CAN_ConfigFilter(&hcan1,&CAN_FilterTypeDef);
}

Both transmitted and received data have corresponding structures, which describe the frame ID, frame category, data length, etc. (CAN_RxHeaderTypeDef, CAN_TxHeaderTypeDef). When transmitting, the transmitting structure needs to be configured according to the data characteristics, and when receiving, the characteristics of the received data are stored in the specified receiving structure.
Message sending function

Get receive message function

Because it's just a simple test, I set them up in the initialization phase.
Use a CAN_Config completes the function of CAN.

__IO uint32_t CAN_RxFlag = 0;		 //It is used to mark whether data is received and assign a value in the interrupt function

CAN_TxHeaderTypeDef TxMes;		//Send structure
CAN_RxHeaderTypeDef RxMes;		//Receiving structure

uint8_t CAN_TxDate[9]="CAN LOOP";								//Send buffer
uint8_t CAN_RxDate[9];								//Receive buffer

uint32_t TxMailbox;			//Hal used to indicate can message sending function_ CAN_ Which mailbox does addtxmessage use to send data


/**
  * @brief  Initialize Rx Message data structure
  * @param  RxMessage: Point to the data structure to initialize
  * @retval None
  */
void CAN_RxMesInit(CAN_RxHeaderTypeDef* RxMessage)
{
  
	/*Clear the receiving structure*/
  (*RxMessage).StdId = 0x00;
  (*RxMessage).ExtId = 0x00;
  (*RxMessage).IDE = CAN_ID_STD;
  (*RxMessage).DLC = 0;
  (*RxMessage).RTR = 0;
  (*RxMessage).FilterMatchIndex = 0;
	(*RxMessage).Timestamp = 0;
}

/*
 * Function name: CAN_TxMsgInit
  * @brief  Initialize TxMessage data structure
  * @param  TxMessage: Point to the data structure to initialize
  * @retval None
 */	 
void CAN_TxMsgInit(CAN_TxHeaderTypeDef* TxMessage)
{	  
  (*TxMessage).StdId=0x00;						 
  (*TxMessage).ExtId=0x2233;					 //Extension ID used
  (*TxMessage).IDE=CAN_ID_EXT;				  //Extended mode
  (*TxMessage).RTR=CAN_RTR_DATA;				 //Sending data
  (*TxMessage).DLC=8;							 //The data length is 8 bytes	
}

/*
 * Function name: CAN_Config
 * Description: fully configure the functions of CAN
 * Input: None
 * Output: None
 * Call: MX CAN initialization call
 */
void CAN_Config(void)
{
  CAN_Filter_Config();
  CAN_TxMsgInit(&TxMes); 
  CAN_RxMesInit(&RxMes);
	HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);				//Enable FIFO0 to receive data interrupt
	HAL_CAN_Start(&hcan1);		//Open CAN1
}
			

Call the CAN_ that you wrote in the CAN1 initialization function generated by CubeMX. Config(), complete the setting of can and turn it on.

Interrupt callback function and CAN error callback of data received by FIFO0.

/**
  * @brief  CAN Receive completion interrupt (non blocking) 
  * @param  hcan: CAN Handle pointer
  * @retval nothing
  */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan)
{
	/* Is the comparison ID 0x2233 */
	HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&RxMes,CAN_RxDate);
	if((RxMes.ExtId==0x2233) && (RxMes.IDE==CAN_ID_EXT) && (RxMes.DLC==8) )
	{
		CAN_RxFlag = 1; //Received successfully  
	}
	else
	{
		CAN_RxFlag = 0; //Receive failed
	}
}
/**
  * @brief  CAN Error callback function
  * @param  hcan: CAN Handle pointer
  * @retval nothing
  */
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
	printf("\r\nCAN error\r\n");
}

main function.

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern __IO uint32_t CAN_RxFlag;		 //It is used to mark whether data is received and assign a value in the interrupt function

extern uint8_t CAN_TxDate[8];								//Send buffer
extern uint8_t CAN_RxDate[8];								//Receive buffer

extern CAN_TxHeaderTypeDef TxMes;		//Send structure
extern CAN_RxHeaderTypeDef RxMes;		//Receiving structure
extern uint32_t TxMailbox;					//Hal used to indicate can message sending function_ CAN_ Which mailbox does addtxmessage use to send data
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_CAN1_Init();

  /* Initialize interrupts */
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */
	printf("CAN LOOP TEST\r\n");
	HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox);			//send data
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		if(CAN_RxFlag)
		{
			CAN_RxFlag = 0;
			printf("Transmit message:%s\r\n",CAN_TxDate);
			printf("Receive message:%s\r\n",CAN_RxDate);
		}
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

effect

other

Initially, I set the length of the send and receive arrays to 8

uint8_t CAN_TxDate[8]="CAN LOOP";								//Send buffer
uint8_t CAN_RxDate[8];								//Receive buffer

The result is:

Not surprisingly, these two arrays are in memory as follows:

If it is only used to store CAN data, it is no problem, but if they are output as a character array with printf, it may cross the boundary as shown in the figure.
Therefore, I define the length of these two arrays as 9. 0 ~ 7 bytes are data, and the 8th byte is always 0 (the compiler initializes it to 0), so as to prevent printf output from crossing the boundary.

Topics: stm32