零基础STM32单片机编程入门(二十九) CAN通信功能详解及源码

文章目录

一.概要

CAN全称是(Controller Area Network),控制器局域网络,是ISO国际标准化的串行通信协议,CAN是国际上应用最广泛的现场总线之一。

CAN通信只有两根信号线,分别是CAN_High和CAN_Low,CAN 控制器根据这两根线上的电位差来判断总线电平。总线申平分为显性电平和隐性申平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。

CAN总线在车上应用非常广泛:

本文介绍了STM32单片机CAN口的基本概念,内部结构,波特率配置,接收滤波配置以及用CAN口进行数据通讯的例程。

二.CAN网络基本组成

一个典型的CAN网络由多个节点组成,每个节点至少包含一个CAN控制器和一个CAN收发器。此外,网络还需要适当的物理介质,如电缆和连接器来连接各个节点。

STM32F103单片机是自带CAN控制器,只要外面挂一个CAN收发器(TJA1050/TJA1042等)就能组成一个CAN节点,实现跟其他节点CAN通讯。

三.STM32单片机CAN结构与特点

1.STM32F1单片机CAN基本结构图

CAN内核

是CAN控制器的核心,包含了一系列的控制和状态寄存器,通过配置这些寄存器可以设定CAN控制器的工作模式、波特率、错误检测和恢复策略等关键参数。内核还负责处理总线上的仲裁、错误检测、帧发送和接收等工作。

发送邮箱

是用于临时存储待发送报文的缓冲区,每个邮箱可以容纳一个完整的CAN报文,并附带相关控制信息。在STM32的某些系列产品中,如F1、F4和F7等,提供了至少3个发送邮箱,这样可以同时准备多个报文并发发送,提高通信效率。

接收FIFO

是用于暂存从CAN总线上接收到的有效报文。当CAN控制器接收到报文时,会将其放入接收FIFO中,应用程序可以从FIFO中取出并处理这些报文。FIFO设计可以避免在接收过程中丢失数据,特别是在短时间内连续接收到多个报文的情况下。

接收过滤器

是用于筛选从CAN总线上接收到的报文,只有经过过滤器匹配的报文才会被存入接收FIFO中,这样可以减少不必要的处理器负载,并允许系统只关注感兴趣的特定报文。过滤器可以根据标识符、标识符掩码等多种规则进行配置。

2.STM32F1单片机CAN基本特点

STM32F1芯片自带bxCAN 控制器 (Basic Extended CAN),只需要外加CAN收发器,收发器一般都是8个引脚的芯片,比如TJA1050芯片就可与 CAN 网络进行交互,它支持 2.0A 和 B 版本的CAN 协议。

STM32F1的bxCAN有以下特点

• 支持 CAN 协议 2.0A 和 20B 主动模式

• 波特率最高达 1Mbps

• 支持时间触发通信

• 具有 3 个发送邮箱

• 具有 3 级深度的 2 个接收 FIFO

• 可变的过滤器组(STM32F103ZET6有14个)

bxCAN模块可以完全自动地接收和发送CAN报文,且完全支持标准标识符(11位)和扩展标识符(29位)。

四.CAN协议帧格式

数据帧是使用最多的帧,结构上由7段组成,其中根据仲裁段ID码长度的不同,分为标准帧(CAN2.0A)和扩展帧(CAN2.0B),标识符(ID)长度不同:标准格式为11位,扩展格式为29位。

数据帧格式:

1.起始域

SOF=帧开始

2.仲裁域

ID=标识符

RTR=远程请求位,区分数据帧和遥控帧

3.控制域

IDE=扩展标志位,区分标准格式和扩展格式

r0=保留位,为后续协议升级留下空间

DLC=数据长度

4.数据域

0~8字节的长度

5.CRC域

CRC=循环几余校验码

6.ACK域

ACK=响应位

7.结束域

EOF=帧结束

CAN通信标准帧还是扩展帧,选择使用哪种帧类型取决于具体的应用需求。如果需要传输较小数据量且标识符长度不超过11位,可以选择CAN标准帧。而对于需要传输更大数据量和更长标识符的应用,则应选用CAN扩展帧,一般CAN通信双方之间会先约定好采用标准帧还是扩展帧协议。

五.STM32F103C8T6的CAN通讯波特率

由于CAN属于异步通讯,没有时钟信号线,连接在同一个总线网络中的各个节点会像串口异步通讯那样,节点间使用约定好的波特率进行通讯。

同时,CAN还使用"位同步"的方式来抗干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。

如下所述把名义上的每位时间分为3段:

● 同步段(SYNC_SEG):通常期望位的变化发生在该时间段内。其值固定为1个时间单元(1 x tCAN)。

● 时间段1(BS1):定义采样点的位置。它包含CAN标准里的PROP_SEG和PHASE_SEG1。

其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。

● 时间段2(BS2):定义发送点的位置。它代表CAN标准里的PHASE_SEG2。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。

我们以STM32F103C8T6为例,CAN时钟是挂在APB1上,APB1时钟是36M,

BRP[9:0]: 波特率分频器 (Baud rate prescaler) ,这个寄存器我们配置成23。

TS2[2:0]: 时间段2 (Time segment 2) ,这个寄存器我们配置成1。

TS1[3:0]: 时间段1 (Time segment 1) ,这个寄存器我们配置成2。

根据波特率公式:

tBS1 = tq * (TS1[3:0] + 1)。

tBS2 = tq * (TS2[2:0] + 1)。

tq = (BRP[9:0] + 1) x tPCLK。

tPCLK=1/36000000。

波特率=1/(tq + tBS1+tBS2)

=1/tq*(1+(TS1[3:0] + 1)+(TS2[2:0] + 1))

=36000000/((BRP[9:0] + 1)*(1+(TS1[3:0] + 1)+(TS2[2:0] + 1)))

波特率计算结果就是

36000000/((23+1)*(1+(1+1)+(2+1)))=250000bps。

一般CAN波特率都是250k,500k,1M,125k,我们只要改下波特率分频器就很方便能改大该小。

六.STM32F103C8T6的CAN接收过滤器配置

CAN的过滤器的配置是对CAN接收到的报文进行过滤的配置,在STM32芯片中,可以对CAN的报文进行过滤,从而省略MCU的处理过程。在CAN协议里,报文的标识符不代表节点的地址,而是跟报文的优先级相关的。发送者以广播的形式把报文发送给所有的接收者。节点在接收报文时,根据标识符的值决定是否需要该报文,如果需要,就拷贝到SRAM里,如果不需要,报文就被丢弃且无需软件的干预。

为满足这一需求,CAN为应用程序提供了14个位宽可变的、可配置的过滤器组(13~0),每个过滤器组由2个32位寄存器,CAN_FxR1和CAN_FxR2组成。以便只接收那些软件需要的报文。硬件过滤的做法节省了MCU开销,否则就必须由软件过滤从而占用一定的MCU开销。

每个过滤器组的位宽都可以单独设置,以满足应用程序的不同需求。根据位宽的不同,可以配置为1个32位过滤器和2个16位过滤器。

● 1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位

● 2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位

CAN过滤器还可以分为屏蔽位模式和标识符列表模式。

其中:

  1. 标识符屏蔽位模式:FxR1为标识符匹配值,FxR2为屏蔽码。解释为FxR2中某1位为1,FxR1中相应的位置必须与收到的帧的标识符中的相应位置吻合才能通过过滤器。FxR2中为0的位表示FxR1的相应位可不必与收到的帧进行匹配。

  2. 标识符列表模式:FR1和FR2都是要匹配的标识符,收到的帧的标识符必须与其中一个吻合才能通过过滤器,所有的过滤器都是并联的,一个报文只要能通过一个过滤器就算是有效的。

例如:

匹配ID为0x200,掩码为0x000,则所有ID号都能通过

匹配ID为0x200,掩码为0x700,则ID为0x200~0x2FF均可通过

匹配ID为0x200,掩码为0x7FF,则只有0x200能通过

一般初学,掩码配置为0就可以了,就可以接收CAN总线上的所有帧。

七.CubeMX配置一个CAN数据收发例程

用4根杜邦线把模块与开发板相连
板子5V-----模块VCC
板子B9-----模块TX
板子B8-----模块RX
板子G------模块GND

用2根杜邦线把模块与CAN盒相连
模块CANH 与 CAN盒CANH 相连
模块CANL 与 CAN盒CANL 相连
CAN 盒通过USB线连电脑。

打开STM32CubeMX软件,新建工程

Part Number处输入STM32F103C8,再双击就创建新的工程

配置下载口引脚

配置外部晶振引脚

配置系统主频

配置CAN引脚,波特率

配置CAN中断,采用中断接收方式

配置工程文件名,保存路径,KEIL5工程输出方式

生成工程

用Keil5打开工程

添加代码


c 复制代码
uint8_t SendBuff[8]={0,1,2,3,4,5,6,7};
uint32_t CAN_ID=0x321;
//CAN 发送函数
void CAN_SendMsg(uint16_t msgID, uint8_t *Data)
{
    CAN_TxHeaderTypeDef   TxHeader;
	TxHeader.StdId = msgID;			//stdID
	TxHeader.RTR = CAN_RTR_DATA;		//数据帧,CAN_RTR_DATA
	TxHeader.IDE = CAN_ID_STD;		//标准格式
	TxHeader.DLC =8;   				//数据长度
	TxHeader.TransmitGlobalTime = DISABLE;
	uint8_t  TxData[8];		//最多8个字节
	TxData[7] = *(Data+0);
	TxData[6] = *(Data+1);
	TxData[5] = *(Data+2);
	TxData[4] = *(Data+3);    
	TxData[3] = *(Data+4);
	TxData[2] = *(Data+5);
	TxData[1] = *(Data+6);
	TxData[0] = *(Data+7);
	while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan) < 1) {
	} //等待有可用的发送邮箱
	uint32_t TxMailbox;		//临时变量, 用于返回使用的邮箱编号
	/*  发送到邮箱,由CAN模块负责发送到CAN总线   */
	if(HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox) != HAL_OK)
    {
        
    }
}
// 设置Filter过滤,只使能FIFO0,并且不过滤任何消息
uint8_t bsp_can1_filter_config(void)
{
    CAN_FilterTypeDef filter = {0};
    filter.FilterActivation = ENABLE;
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    filter.FilterBank = 0;
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    filter.FilterIdLow = 0;
    filter.FilterIdHigh = 0;
    filter.FilterMaskIdLow = 0;
    filter.FilterMaskIdHigh = 0;
    HAL_CAN_ConfigFilter(&hcan, &filter);
    
}
static CAN_RxHeaderTypeDef RxMessage; //CAN接收的消息的消息头
//CAN中断接收回调函数,接收到的帧头存RxMessage,数据存data数组
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{

		uint8_t data[8];
		HAL_StatusTypeDef status;
		
		status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
		if (HAL_OK == status){
			CAN_ID=RxMessage.StdId;//标准帧ID复制
			memcpy(SendBuff,data,8);//接收到的数据拷贝到发送数组
		}
	
}
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_CAN_Init();
  /* USER CODE BEGIN 2 */

  bsp_can1_filter_config();//滤波器设置,接收所有帧
	HAL_CAN_Start(&hcan);//启动CAN
	HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);//中断接收使能
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		CAN_SendMsg(CAN_ID, SendBuff);//发送数据
    HAL_Delay(200);
  }
  /* USER CODE END 3 */
}

实验效果

打开电脑上位机,通过电脑CAN上位机发送标准帧给板子,ID是0x321,内容是0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,板子就会改变发送的标准帧内容,发回给电脑上位机。红色的是电脑发送的数据,绿色的是板子发送的数据。

八.CubeMX工程源代码下载

通过百度网盘分享的文件:29.CAN通信实验.rar

链接:https://pan.baidu.com/s/1IWb_KWvRI3nCJy1Rewr4Yw

提取码:nykt

如果链接失效,可以联系博主给最新链接

程序下载下来之后解压就行
CSDN代码下载地址

九.小结

CAN协议因其高性能、可靠性和灵活性而逐渐被广泛应用于工业控制、航空航天、船舶、医疗设备、智能家居等领域。

相关推荐
kyle~2 小时前
电控三周速成计划参考
单片机·嵌入式硬件
CoreMaker-lab4 小时前
e2studio开发RA2E1(5)----GPIO输入检测
单片机·mcu·gpio·e2studio·瑞萨ra·r7fa2e1a72dfl
charlie1145141915 小时前
从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(绘图设备封装)
c语言·stm32·单片机·学习·oled·嵌入式软件
2401_843785239 小时前
STM32 DMA数据转运
stm32·单片机·嵌入式硬件
水饺编程9 小时前
简易CPU设计入门:指令单元(二)
linux·嵌入式硬件·fpga开发·硬件工程
cherry_rainyyy16 小时前
51单片机 02 独立按键
单片机·嵌入式硬件·51单片机
SY师弟20 小时前
蓝桥杯单片机第七届省赛
c语言·c++·单片机·嵌入式硬件·职场和发展·蓝桥杯
云山工作室21 小时前
基于WiFi的智能照明控制系统的设计与实现(论文+源码)
单片机·毕业设计·毕设
天外高人21 小时前
实验六 项目二 简易信号发生器的设计与实现 (HEU)
单片机·嵌入式硬件·fpga开发·实验
文科比理科更擅长的工科男1 天前
012-51单片机CLD1602显示万年历+闹钟+农历+整点报时
单片机·嵌入式硬件·51单片机