HAL库配置CAN通信

一、CAN总线波特率计算

CAN总线通信的各节点通信时会产生相位差,所以要进行位同步,两个节点保持步调一致。
CAN_SJW:重新同步跳跃宽度(SJW) 。定义了在每位中可以延长或缩短多少个时间单元的上限。其值可以编程为1到4个时间单元。
CAN_BS1:时间段1(BS1):定义采样点的位置。其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。
CAN_BS2:时间段2(BS2):定义发送点的位置。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。
CAN_Prescaler:直观理解就是分频率。
CAN总线的波特率是取自于总线APB1(PCLK1),通过函数RCC_PCLK1Config给PCLK1配置频率。设置了以上的四个值之后,
CAN总线的波特率=PCLK1/((CAN_SJW +CAN_BS1 + CAN_BS2)*CAN_Prescaler)
假设PCLK1=36MHz、CAN_SJW=1、CAN_BS1=8、CAN_BS2=7、CAN_Prescaler=9
则CAN总线的波特率=PCLK1/((1 + 8 + 7) * 9) = 36MHz / 16 / 9 = 250Kbits

二、查看对应数据手册(stm32F407ZG),看CAN挂载在哪条总线上面,以及对应的引脚 

三、CAN过滤器介绍

typedef struct
{
  __IO uint32_t FR1;           //标识符屏蔽位模式中标识符寄存器,配置ID
  __IO uint32_t FR2;           //标识符屏蔽位模式中屏蔽寄存器
}CAN_FilterRegister_TypeDef;  //标识符列表模式中为2个标识符寄存器,配置ID

例如:屏蔽位寄存器的bit15=1,标识符寄存器的bit15=0,那么接受的Message里面的标识符的bit15必须为0才可能被硬件接受。如果屏蔽位寄存器的bit15=0,Message里面的标识符的bit15无论为什么值,bit15都能匹配通过。

32位屏蔽位模式下:1个过滤器。FR2指定需要关心哪些位,FR1指定这些位的标准值
32位列表模式下:2个过滤器,FR1指定过滤器0的标准值,FR2指定过滤器1的标准值
16位屏蔽位模式下:2个过滤器。FR1高位配置过滤器0标准值,低位配置给过滤器1的标准值。FR2高位配置过滤器0关心的位,低位配置给过滤器1的关心的位。
16位列表模式下:4个过滤器。FR1低位配置过滤器0,高位配置过滤器1。FR2配置过滤器2和过滤器3

IDE=0为标准ID,IDE=1为扩展ID。
RTR=0代表数据帧,RTR=1代表远程帧。
MASKID设置屏蔽码,ID设置标准值。

四、CAN过滤器配置

只有配置了CAN过滤器才能接收到数据,下面配置过滤器是所有ID都可以接收。加入到CAN初始化的函数中

	CAN_FilterTypeDef sFilterConfig;
	sFilterConfig.FilterBank = 0;   //过滤器0
	sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;   //标识符屏蔽位模式
	sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;  //过滤器位宽为单个32位
	sFilterConfig.FilterIdHigh = 0x0000;  //标识符寄存器   
	sFilterConfig.FilterIdLow = 0x0000;   //标识符寄存器   
	sFilterConfig.FilterMaskIdHigh = 0x0000;   //屏蔽寄存器,只存在于标识符屏蔽位模式中,在标识符列表模式中为标识符寄存器 
	sFilterConfig.FilterMaskIdLow = 0x0000;    //屏蔽寄存器                                 
	sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; //FIFO0的中断和FIFO1的中断是不一样的,这里是把接收到的报文放入到FIFO0中
	sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;   //enable filter
	sFilterConfig.SlaveStartFilterBank = 0;    
    
	/* 过滤器配置 */
	if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }

    /* 启动CAN外围设备 */
    if (HAL_CAN_Start(&hcan1) != HAL_OK)
    {
        Error_Handler();
    }

五、HAL库初始化

发送函数代码具体如下: 

CAN_TxHeaderTypeDef g_canx_txheader;    /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader;    /* 接收参数句柄 */

/**
* @brief CAN 发送一组数据
* @note 发送格式固定为: 标准 ID, 数据帧
* @param id : 标准 ID(11 位)
* @retval 发送状态 0, 成功; 1, 失败;
*/
uint8_t can_send_msg(uint32_t id, uint8_t *msg, uint8_t len)
{
    uint32_t TxMailbox = CAN_TX_MAILBOX0;
    g_canx_txheader.StdId = id; /* 标准标识符 */
    g_canx_txheader.ExtId = id; /* 扩展标识符(29 位) */
    g_canx_txheader.IDE = CAN_ID_STD; /* 使用标准帧 */
    g_canx_txheader.RTR = CAN_RTR_DATA; /* 数据帧 */
    g_canx_txheader.DLC = len;
    if (HAL_CAN_AddTxMessage(&g_canx_handler, &g_canx_txheader,msg, &TxMailbox) != HAL_OK) 
    {
        return 1;
    }
    /* 等待发送完成,所有邮箱为空(3 个邮箱) */
    while (HAL_CAN_GetTxMailboxesFreeLevel(&g_canx_handler) != 3);
    return 0;
}

在 CAN 初始化时,我们对于过滤器的配置是不过滤任何报文 ID,也就是说可以接收全部
报文。但是我们可以编写接收函数时,使用软件的方式过滤报文 ID,通过形参来跟接收到的报
文 ID 进行匹配。接收函数代码具体如下: 

/**
* @brief CAN 接收数据查询
* @note 接收数据格式固定为: 标准 ID, 数据帧
* @param id : 要查询的 标准 ID(11 位)
* @param buf : 数据缓存区
* @retval 接收结果
* @arg 0 , 无数据被接收到;
* @arg 其他, 接收的数据长度
*/
uint8_t can_receive_msg(uint32_t id, uint8_t *buf)
{
    if (HAL_CAN_GetRxFifoFillLevel(&g_canx_handler, CAN_RX_FIFO0) != 1)
    {
        return 0;
    }
    if (HAL_CAN_GetRxMessage(&g_canx_handler, CAN_RX_FIFO0, &g_canx_rxheader,buf) !=HAL_OK)
    {
        return 0;
    }
    /* 接收到的 ID 不对 / 不是标准帧 / 不是数据帧 */
    if (g_canx_rxheader.StdId!= id || g_canx_rxheader.IDE != CAN_ID_STD ||g_canx_rxheader.RTR != CAN_RTR_DATA)
    {
        return 0;
    }
    return g_canx_rxheader.DLC;
}

 六、验证代码

(1)将CAN配置为静默回环模式,配置过滤器不过滤

/* CAN1 init function */
void MX_CAN1_Init(void)
{

  /* USER CODE BEGIN CAN1_Init 0 */

  /* USER CODE END CAN1_Init 0 */

  /* USER CODE BEGIN CAN1_Init 1 */

  /* USER CODE END CAN1_Init 1 */
  hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 16;
  hcan1.Init.Mode = CAN_MODE_SILENT_LOOPBACK;//静默回环模式
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_4TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_3TQ;
  hcan1.Init.TimeTriggeredMode = DISABLE;
  hcan1.Init.AutoBusOff = 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();
  }
  /* USER CODE BEGIN CAN1_Init 2 */
	CAN_FilterTypeDef sFilterConfig;
	sFilterConfig.FilterBank = 0;   //过滤器0
	sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;   //标识符屏蔽位模式
	sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;  //过滤器位宽为单个32位
	sFilterConfig.FilterIdHigh = 0x0000;  //标识符寄存器   
	sFilterConfig.FilterIdLow = 0x0000;   //标识符寄存器   
	sFilterConfig.FilterMaskIdHigh = 0x0000;   //屏蔽寄存器,只存在于标识符屏蔽位模式中,在标识符列表模式中为标识符寄存器 
	sFilterConfig.FilterMaskIdLow = 0x0000;    //屏蔽寄存器                                 
	sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; //FIFO0的中断和FIFO1的中断是不一样的,这里是把接收到的报文放入到FIFO0中
	sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;   //enable filter
	sFilterConfig.SlaveStartFilterBank = 0;    
    
	/* 过滤器配置 */
	if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }

    /* 启动CAN外围设备 */
    if (HAL_CAN_Start(&hcan1) != HAL_OK)
    {
        Error_Handler();
    }
  /* USER CODE END CAN1_Init 2 */

}

(2)加入CAN数据收发代码

(3)仿真验证数据是否接收成功

	uint8_t buf[10]="aaa";
	uint8_t buf1[10]={0};
	can_send_msg(0x12,buf,3);
	can_receive_msg(0x12,buf1);
  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用HAL库进行CAN通信控制电机,需要先配置CAN总线和电机控制器的硬件连接,并初始化CAN总线的参数。在初始化CAN总线后,可以使用HAL库提供的CAN发送和接收函数来与电机控制器进行通信。 以下是一个使用HAL库进行CAN通信控制电机的示例代码: ```c #include "stm32f4xx_hal.h" #include "can.h" CAN_HandleTypeDef hcan; void MX_CAN_Init(void) { hcan.Instance = CAN1; hcan.Init.Prescaler = 4; hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_12TQ; hcan.Init.TimeSeg2 = CAN_BS2_1TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = ENABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = ENABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } } void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if (canHandle->Instance == CAN1) { __HAL_RCC_CAN1_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**CAN1 GPIO Configuration PB8 ------> CAN1_RX PB9 ------> CAN1_TX */ GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_CAN1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } } void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan) { /* Handle CAN RX Interrupt */ } void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan) { /* Handle CAN TX Interrupt */ } void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) { /* Handle CAN Error Interrupt */ } void send_can_frame(uint32_t id, uint8_t data[8]) { CAN_TxHeaderTypeDef tx_header; tx_header.StdId = id; tx_header.RTR = CAN_RTR_DATA; tx_header.IDE = CAN_ID_STD; tx_header.DLC = 8; if (HAL_CAN_AddTxMessage(&hcan, &tx_header, data, &tx_mailbox) != HAL_OK) { Error_Handler(); } } void receive_can_frame(void) { CAN_RxHeaderTypeDef rx_header; uint8_t data[8]; uint32_t fifo; if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &rx_header, data) != HAL_OK) { Error_Handler(); } /* Handle received CAN frame */ } int main(void) { /* Initialize CAN bus */ MX_CAN_Init(); /* Loop forever */ while (1) { /* Send CAN frame to control motor */ uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0}; send_can_frame(0x123, data); /* Receive CAN frame from motor */ receive_can_frame(); } } ``` 在上述代码中,MX_CAN_Init函数用于初始化CAN总线,HAL_CAN_MspInit函数用于配置CAN总线的GPIO管脚,HAL_CAN_RxCpltCallback和HAL_CAN_TxCpltCallback函数分别用于处理CAN接收和发送中断,HAL_CAN_ErrorCallback函数用于处理CAN错误中断。 send_can_frame函数用于发送CAN帧,receive_can_frame函数用于接收CAN帧。在主函数中,通过循环不断地发送和接收CAN帧,以控制电机的运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值