Bootstrap

【通信协议】CAN总线通信协议的学习,(三)stm32f103系列单片机,can通信的代码实现

目录

1、CAN 的基本知识

2、CAN的cubemx配置

3、CAN的代码实现

3.0、初始化,认识函数及变量

3.1、CAN发送

3.1.1、代码1

3.1.2、代码2

3.1.3、代码3

3.2、CAN中断接收

3.2.1、代码1

3.2.2、代码2

3.2.3、代码3

3.3、过滤器

3.3.1、代码1 

3.3.2、代码2


1、CAN 的基本知识

参考:

【通信协议】CAN总线通信协议的学习(一)基础理论知识学习-CSDN博客

2、CAN的cubemx配置

参考:

[通信协议】CAN总线通信协议的学习,(二)stm32f103芯片cubemx配置,CAN总线配置,参数计算及配置-CSDN博客

3、CAN的代码实现

3.0、初始化,认识函数及变量

初始化,在main函数中进行,

1、CAN滤波器的配置、2、启动CAN控制器、3、使能CAN控制器接收中断:

 /* USER CODE BEGIN 2 */
    /* 1. CAN Filter Config */
    CANFilter_Config();
    
    /* 2. CAN Start */
    if (HAL_CAN_Start(&hcan1) != HAL_OK) {
        Error_Handler();
    }
    
    /* 3. Enable CAN RX Interrupt */
    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
        Error_Handler();
    }
  /* USER CODE END 2 */

 定义CAN发送/接收消息的结构体,在main中或者can.c中都可以,定义全局变量

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
static CAN_TxHeaderTypeDef        TxMessage;    //CAN发送的消息的消息头
static CAN_RxHeaderTypeDef        RxMessage;    //CAN接收的消息的消息头
/* USER CODE END PV */

3.1、CAN发送

在can.c函数中,添加

3.1.1、代码1

/* CAN 发送数据测试函数 */
void CAN1_Send_Test()
{
    uint8_t data[4] = {0x01, 0x02, 0x03, 0x04};
    uint32_t TxMailbox; 
    
    TxMessage.IDE = CAN_ID_STD;     //设置ID类型,标准
	TxMessage.StdId = 0x222;        //设置ID号
    TxMessage.RTR = CAN_RTR_DATA;   //设置传送数据帧
	TxMessage.DLC = 4;              //设置数据长度
    
	if (HAL_CAN_AddTxMessage(&hcan1, &TxMessage, data, (uint32_t*)CAN_TX_MAILBOX0) != HAL_OK) {
        Error_Handler();
    }
}

上述代码所使用的一个关键函数

HAL_CAN_AddTxMessage 函数的主要功能是将一个待发送的 CAN 消息添加到发送邮箱中,以便通过 CAN 总线进行传输。

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox);
  1. hcan:指向 CAN_HandleTypeDef 结构体类型的指针,该结构体包含了 CAN 控制器的相关配置和状态信息,通过它可以访问特定的 CAN 控制器实例。
  2. pHeader:指向 CAN_TxHeaderTypeDef 结构体类型的指针,这个结构体定义了要发送的 CAN 消息的头部信息,包括:
    • StdId 或 ExtId:标准帧标识符或扩展帧标识符,用于确定消息的唯一性。
    • IDE:标识符类型,区分标准帧和扩展帧。
    • RTR:远程传输请求标志,用于请求远程节点发送数据。
    • DLC:数据长度代码,指示消息中数据的长度。
  3. aData:指向要发送的数据缓冲区的指针,数据以字节数组的形式存储。
  4. pTxMailbox:指向一个 uint32_t 类型的变量的指针,该变量用于接收发送邮箱的索引。如果发送成功,函数会将使用的发送邮箱的索引存储在这个变量中

3.1.2、代码2

 
		HAL_CAN_Start(&hcan);
		CAN_TxHeaderTypeDef TxHeader;
		uint8_t TxData[8] = {0x23, 0x81, 0x60, 0x00, 0x55, 0x55, 0x08, 0x00};
		uint32_t TxMailbox; 
		uint32_t std_id = 0x601;  
 
		TxHeader.RTR = CAN_RTR_DATA;
		TxHeader.IDE = CAN_ID_STD;            
		TxHeader.StdId=std_id;
		TxHeader.TransmitGlobalTime = DISABLE;
		TxHeader.DLC = 8;
						
		if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox) != HAL_OK)
		{
			 /* Transmission request Error */
			 Error_Handler();
		}

3.1.3、代码3

这个是stm32 标准库代码,和上面两种hal库代码不同

不过,代码的思路可以借鉴

void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{
	CanTxMsg TxMessage;      //构建一个发送消息的结构体
	TxMessage.StdId = ID;
	TxMessage.ExtId = ID;
	TxMessage.IDE = CAN_Id_Standard;		//CAN_ID_STD
	TxMessage.RTR = CAN_RTR_Data;
	TxMessage.DLC = Length;
	for (uint8_t i = 0; i < Length; i ++)
	{
		TxMessage.Data[i] = Data[i];
	}
	
	uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);
	
	uint32_t Timeout = 0;
	while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok)
	{
		Timeout ++;
		if (Timeout > 100000)
		{
			break;
		}
	}
}

以下是注释,解释
/** 
  * @brief  CAN Tx message structure definition  
  */

typedef struct
{
  uint32_t StdId;  /*!< Specifies the standard identifier.
                        This parameter can be a value between 0 to 0x7FF. */

  uint32_t ExtId;  /*!< Specifies the extended identifier.
                        This parameter can be a value between 0 to 0x1FFFFFFF. */

  uint8_t IDE;     /*!< Specifies the type of identifier for the message that 
                        will be transmitted. This parameter can be a value 
                        of @ref CAN_identifier_type */

  uint8_t RTR;     /*!< Specifies the type of frame for the message that will 
                        be transmitted. This parameter can be a value of 
                        @ref CAN_remote_transmission_request */

  uint8_t DLC;     /*!< Specifies the length of the frame that will be 
                        transmitted. This parameter can be a value between 
                        0 to 8 */

  uint8_t Data[8]; /*!< Contains the data to be transmitted. It ranges from 0 
                        to 0xFF. */
} CanTxMsg;

3.2、CAN中断接收

在main.c文件的最后编写CAN接收中断处理函数:

3.2.1、代码1

/* USER CODE BEGIN 4 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    uint8_t  data[8];
    HAL_StatusTypeDef	status;
    
    if (hcan == &hcan1) {	
        status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
        if (HAL_OK == status){                              			
            printf("--->Data Receieve!\r\n");
            printf("RxMessage.StdId is %#x\r\n",  RxMessage.StdId);
            printf("data[0] is 0x%02x\r\n", data[0]);
            printf("data[1] is 0x%02x\r\n", data[1]);
            printf("data[2] is 0x%02x\r\n", data[2]);
            printf("data[3] is 0x%02x\r\n", data[3]);
            printf("<---\r\n");
            
        }
    }
}
/* USER CODE END 4 */

上述代码所使用的一个关键函数

HAL_CAN_GetRxMessage函数用于从指定的 CAN 控制器接收邮箱中获取接收到的 CAN 消息。

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
  1. hcan:指向CAN_HandleTypeDef结构体类型的指针,该结构体包含了 CAN 控制器的相关配置和状态信息,通过它可以访问特定的 CAN 控制器实例。
  2. RxFifo:指定接收 FIFO(FIFO0 或 FIFO1)的编号。在 CAN 控制器中,通常有两个接收 FIFO,用于缓存接收到的消息。
  3. pHeader:指向CAN_RxHeaderTypeDef结构体类型的指针,这个结构体用于存储接收到的 CAN 消息的头部信息,包括:
    • StdIdExtId:标准帧标识符或扩展帧标识符,取决于接收到的消息类型。
    • IDE:标识符类型,区分标准帧和扩展帧。
    • RTR:远程传输请求标志,用于判断接收到的消息是否为远程请求。
    • DLC:数据长度代码,指示接收到的消息中数据的长度。
    • FilterMatchIndex:过滤器匹配索引,用于确定接收到的消息是通过哪个过滤器接收的。
  4. aData:指向用于存储接收到的数据的缓冲区的指针。数据以字节数组的形式存储在这个缓冲区中。

3.2.2、代码2

 标准库,中断接收

void USB_LP_CAN1_RX0_IRQHandler(void)
{
	if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) == SET)
	{
		CAN_Receive(CAN1, CAN_FIFO0, &MyCAN_RxMsg);
		MyCAN_RxFlag = 1;  //主函数中,通过判断状态标志,进一步处理数据
	}
}

3.2.3、代码3

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	CAN_RxHeaderTypeDef CAN_RX;
	uint8_t aData[8]; 
	if(hcan->Instance ==CAN1)
	{
		HAL_CAN_GetRxMessage(hcan,CAN_FilterFIFO0,&CAN_RX,aData);//从上位机获取数据
		HAL_UART_Transmit(&huart1,aData,CAN_RX.DLC,HAL_MAX_DELAY);//把获取的数据通过串口打印出来
		
	}

}

3.3、过滤器

 关于过滤器的讲解视频,可以参考:视频的18.13进度,有个示例讲解。

[2-2] STM32 CAN外设(下)_哔哩哔哩_bilibili

一、IDE(Identifier Extension)

  • 当 IDE = 0 时,表示标准帧。标准帧的标识符长度为 11 位。
  • 当 IDE = 1 时,表示扩展帧。扩展帧的标识符长度为 29 位。

二、RTR(Remote Transmission Request)

  • 当 RTR = 0 时,表示数据帧。数据帧是由发送节点主动发送数据的帧类型。
  • 当 RTR = 1 时,表示远程帧。远程帧是用于请求远程节点发送相应的数据帧。发送远程帧的节点希望总线上某个具有特定标识符的节点能够响应并发送包含相应数据的数据帧。

 过滤器掩码中,0表示不对ID做任何反应,1表示必须与ID的位号相同。

3.3.1、代码1 

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* CAN过滤器配置函数 这个过滤器没有进行任何过滤   */
static void CANFilter_Config(void)
{
    CAN_FilterTypeDef  sFilterConfig;
    
    sFilterConfig.FilterBank = 0;                       //CAN过滤器编号,范围0-27
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;   //CAN过滤器模式,掩码模式或列表模式
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;  //CAN过滤器尺度,16位或32位
    sFilterConfig.FilterIdHigh = 0x000 << 5;			//32位下,存储要过滤ID的高16位
    sFilterConfig.FilterIdLow = 0x0000;					//32位下,存储要过滤ID的低16位
    sFilterConfig.FilterMaskIdHigh = 0x0000;			//掩码模式下,存储的是掩码
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = 0;				//报文通过过滤器的匹配后,存储到哪个FIFO
    sFilterConfig.FilterActivation = ENABLE;    		//激活过滤器
    sFilterConfig.SlaveStartFilterBank = 0;
    
    if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
        Error_Handler();
    }
}
/* USER CODE END 0 */

3.3.2、代码2

 标准库,过滤器初始化的代码

	CAN_FilterInitTypeDef CAN_FilterInitStructure;
	CAN_FilterInitStructure.CAN_FilterNumber = 0;
	CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
	CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
	CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
	CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
	CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
	CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
	CAN_FilterInit(&CAN_FilterInitStructure);

参考:

STM32CubeMX STM32F103C8-CAN_stm32103 配置can速率500k-CSDN博客

STM32F103c8t6 hal库 CubeMX CAN 双机通信_stm32f103 hal can-CSDN博客

STM32CubeMX工程配置——以STM32F103C8T6为例_stm32f103c8t6 cubemx-CSDN博客

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;