STM32之CAN通讯(十一)

STM32F407 系列文章 - CAN通讯(十一)


目录

前言

一、CAN

二、CAN驱动电路

三、CAN软件设计

1.CAN状态初始化

2.头文件相关定义

3.接收中断服务函数

4.用户层使用

1.用户层相关定义

2.发送数据

3.接收数据

1.查询方式处理

2.中断方式处理

3.总结

4.其它功能函数

总结


前言

一般STM32F407芯片都会自带2路CAN接口,分别为CAN1和CAN2,其通讯速度高达1Mb/s,每个CAN总线发送端具备三个发送邮箱,用来区别发送优先级,接收端具备两个具有三级深度的接收 FIFO,用来存储数据。一般从407芯片端口输出的CAN信号抗干扰性比较差,不足以保证通讯的稳定性和可靠性,这时我们就需要通过添加驱动电路,可以增强信号的驱动能力,确保信号在传输过程中不受干扰或衰减,从而提高通讯的稳定性和可靠性。一般市场上所卖的板子都带这一功能的,因此要实现CAN总线通讯功能,需准备STM32F407开发板一块和CAN-Tool分析仪工具一个。


一、CAN

控制器局域网总线(CAN,Controller Area Network)是ISO国际标准化的串行通信协议总线,使用双绞线来传输信号,具有高性能、高可靠性和易于扩展的特点,广泛应用于汽车、工业控制、机器人控制等领域,是世界上应用最广泛的现场总线之一。‌CAN总线协议_百度百科 (baidu.com)

CAN通讯是一种多主机串行异步通信总线,允许网络中的各个节点(设备)进行无中心控制的通信。每个节点都可以在总线上发送报文,当多个节点同时发送报文时,CAN总线会使用仲裁机制决定哪个报文优先发送,优先级高的报文会先发送,低优先级的报文则会停止发送。CAN总线的通信过程分为发送报文、仲裁机制、数据传输和错误检测与处理四个阶段。

要了解更为详细的CAN总线协议及其报文构成,可参考CAN总线通信协议-优快云博客,讲的挺全面的。

二、CAN驱动电路

STM32单片机在进行CAN通讯时加驱动电路是为了增强信号传输能力、提供总线保护以及满足CAN总线物理层规范。这些措施有助于提高通讯的稳定性和可靠性,确保单片机与CAN总线上的其他设备之间的正常通讯。下面提供一款国产驱动芯片SIT1050,详细的芯片参数及引脚特性参考其datasheet。

SIT1050是一款应用于CAN协议控制器和物理总线之间的接口芯片,可应用于卡车、公交、小汽车、工业控制等领域,速率可达到1Mbps,具有在总线与CAN协议控制器之间进行差分信号传输的能力,设计电路原理如下。

三、CAN软件设计

CAN软件包括底层代码和用户层代码,底层代码主要完成CAN状态的初始化工作,主要涉及到对底层硬件引脚、时钟、中断的定义及调用;用户层代码主要完成对CAN总线上数据消息的解析和处理。

关于底层代码的实现可以通过调用HAL官方库文件,或者在可视化工具STM32CubeMX上配置,然后一键画生成底层代码。不管那种方式其实都是在以CAN_TypeDef *结构完成CAN寄存器的配置,在stm32f407xx.h文件上可查看。

1.CAN状态初始化

通过函数can1_init()完成,主要为CAN1的设置,包括波特率、工作模式设置、底层驱动配置( 包括引脚配置、时钟配置、中断配置)、过滤器设置、CAN总线外围设备、CAN中断使能设置等等,该函数被main()函数调用。代码中有详细的介绍,代码如下(示例):

CAN_HandleTypeDef hcan1;                /* CAN1句柄 */
CAN_HandleTypeDef hcan2;                /* CAN2句柄 */     

/**
 * @brief       CAN初始化
 *  @note       Prescaler    : 重新同步跳跃时间单元.范围: 1~3;
 *              TimeSeg2    : 时间段2的时间单元.范围: 1~8;
 *              TimeSeg1    : 时间段1的时间单元.范围: 1~16;
 *              Prescaler     : 波特率分频器.范围: 1~1024;
 *              以上4个参数, 在函数内部会减1, 所以, 任何一个参数都不能等于0
 *              CAN挂在APB1上面, 其输入时钟频率为 Fpclk1 = PCLK1 = 42Mhz
 *              tq     = Prescaler * tpclk1;
 *              波特率 = Fpclk1 / ((TimeSeg1 + TimeSeg2 + 1) * Prescaler);
 *              已知42M时钟和500Kbps要求, 根据波特率公式
 *              配置TimeSeg1 = 7, TimeSeg2 = 6 , 为Prescaler = 6
 *              得出CAN波特率为: 42M / ((6 + 7 + 1) * 6) = 500Kbps
 *
 * @param       mode    : CAN_MODE_NORMAL,  普通模式;
                          CAN_MODE_LOOPBACK,回环模式;
 * @retval      0,  初始化成功; 其他, 初始化失败;
 */
uint8_t can1_init(uint32_t mode)
{
	/*** 1.完成CAN1的波特率和模式设置
	*    这里也可以通过外部选择波特率配置can1_init(uint32_t bps, uint32_t mode)
	*    case 125:
	*    case 500:
  *    case 1000:
	***/
	hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 6;                 /* 分频系数(Fdiv)为Prescaler+1 */
  hcan1.Init.Mode = mode;                   /* 模式设置 */
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;   /* 重新同步跳跃宽度为SyncJumpWidth+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ */
  hcan1.Init.TimeSeg1 = CAN_BS1_7TQ;        /* 范围CAN_BS1_1TQ~CAN_BS1_16TQ */
  hcan1.Init.TimeSeg2 = CAN_BS2_6TQ;        /* 范围CAN_BS2_1TQ~CAN_BS2_8TQ */
  hcan1.Init.TimeTriggeredMode = DISABLE;   /* 非时间触发通信模式 */
  hcan1.Init.AutoBusOff = ENABLE;           /* 软件自动离线管理 */
  hcan1.Init.AutoWakeUp = DISABLE;          /* 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) */
  hcan1.Init.AutoRetransmission = ENABLE;   /* 禁止报文自动传送 */
  hcan1.Init.ReceiveFifoLocked = DISABLE;   /* 报文不锁定,新的覆盖旧的 */
  hcan1.Init.TransmitFifoPriority = ENABLE; /* 优先级由报文标识符决定 */
	// 2.完成CAN1的底层驱动配置 包括引脚配置、时钟配置、中断配置
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
		return 1;
  }
	// 3.完成ID号为1#设备的过滤器设置
	CAN_Filter_Config(&hcan1, 1);
	// 4.启动CAN1总线外围设备
#if CAN1_iSOpen
	if(HAL_CAN_Start(&hcan1)!=HAL_OK) {
		Error_Handler();
		return 1;
	}
	// 5.使能CAN1中断
	else
		Enable_CAN1_Interrupts();
#endif
    return 0;
}

上面can1_init()函数包含了HAL_CAN_Init()、CAN_Filter_Config()、HAL_CAN_Start()、Enable_CAN1_Interrupts(),这4个函数分别完成如下功能:

  • HAL_CAN_Init(),此函数为HAL库函数,主要是调用HAL_CAN_MspInit()函数,以完成CAN1的底层驱动配置 包括引脚配置、时钟配置、中断配置。函数HAL_CAN_MspInit()同样为HAL库函数,但是其被定义为若函数,可以用来被重写的,代码如下(示例)。
/**
 * @brief       CAN底层驱动,引脚配置,时钟配置,中断配置
                此函数会被HAL_CAN_Init()调用
 * @param       hcan:CAN句柄
 * @retval      无
 */
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
		GPIO_InitTypeDef gpio_init_struct = {0};
    if (CAN1 == hcan->Instance)
    {
				/* CAN1 clock enable */
        __HAL_RCC_CAN1_CLK_ENABLE();    /* 使能CAN1时钟 */
				/**CAN1 GPIO Configuration
				PA11     ------> CAN1_RX
				PA12     ------> CAN1_TX
				*/
        gpio_init_struct.Pin = GPIO_PIN_11|GPIO_PIN_12;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        gpio_init_struct.Alternate = GPIO_AF9_CAN1;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* CAN1_RX和CAN1_TX脚 模式设置 */
			  /* CAN1 interrupt Init */
				HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 1);
				HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
				HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 1);
				HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
    }
		else if(CAN2 == hcan->Instance)
		{
				/* CAN2 clock enable */
        __HAL_RCC_CAN2_CLK_ENABLE();    /* 使能CAN2时钟 */
				/**CAN2 GPIO Configuration
				PB12     ------> CAN2_RX
				PB13     ------> CAN2_TX
				*/
				gpio_init_struct.Pin = GPIO_PIN_12|GPIO_PIN_13;
				gpio_init_struct.Mode = GPIO_MODE_AF_PP;
				gpio_init_struct.Pull = GPIO_NOPULL;
				gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
				gpio_init_struct.Alternate = GPIO_AF9_CAN2;
        HAL_GPIO_Init(GPIOB, &gpio_init_struct); /* CAN2_RX和CAN2_TX脚 模式设置 */
			  /* CAN2 interrupt Init */
				HAL_NVIC_SetPriority(CAN2_RX0_IRQn, 1, 1);
				HAL_NVIC_EnableIRQ(CAN2_RX0_IRQn);
				HAL_NVIC_SetPriority(CAN2_RX1_IRQn, 1, 1);
				HAL_NVIC_EnableIRQ(CAN2_RX1_IRQn);
		}
}
  • CAN_Filter_Co
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值