CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO 国际标准化的串行通信协议。
ECU集成了供电系统 单片机 驱动系统,是汽车里面最小的控制模块。
CAN就是为了让ECU进行通讯而设计。组成局域网进行通讯。
CAN 有两种标准
异步通讯
高速网络
传输速率与传输距离
通讯节点
电气特性
差分线使用两根线表示一个信号。
线与特性
终端电阻
需要注意
CAN 协议是通过以下 5 种类型的帧进行的:
数据帧和遥控帧有标准格式和扩展格式两种格式。
数据帧一般由 7 个段构成,即:
(1) 帧起始。表示数据帧开始的段。
(2) 仲裁段。表示该帧优先级的段。ID
(3) 控制段。表示数据的字节数及保留位的段。
(4) 数据段。数据的内容,一帧可发送 0~8 个字节的数据。
(5) CRC 段。检查帧的传输错误的段。
(6) ACK 段。表示确认正常接收的段。
(7) 帧结束。表示数据帧结束的段。
帧起始(Start Of Frame),这个比较简单,标准帧和扩展帧都是由 1 个位的显性电平表示帧起始。
仲裁段,表示数据优先级的段,当同时有两个报文被发送时,总线会根据仲裁段的内容决定哪个数据包能被传输,标准帧和扩展帧格式在本段有所区别
根据识别码ID就可以识别到底是发给哪个设备。
控制段,由 6 个位构成,表示数据段的字节数。标准帧和扩展帧的控制段稍有不同
数据段,该段可包含 0~8 个字节的数据。从最高位(MSB)开始输出,标准帧和扩展帧在这个段的定义都是一样的。
CRC 段,该段用于检查帧传输错误。由 15 个位的 CRC 顺序和 1 个位的 CRC 界定符(用于分隔的位)组成,标准帧和扩展帧在这个段的格式也是相同的。
ACK 段,此段用来确认是否正常接收。由 ACK 槽(ACK Slot)和 ACK 界定符 2 个位组成。标准帧和扩展帧在这个段的格式也是相同的。
在 ACK 槽和帧结束之间由 ACK 界定符间隔开。
帧结束(End Of Frame),这个段也比较简单,标准帧和扩展帧在这个段格式一样,由 7 个位的隐性位组成,7 个隐性位表示结束。1111111
STM32 自带的是 bxCAN(Basic Extended CAN),即基本扩展 CAN。
支持 CAN 协议 2.0A 和 2.0B。
波特率最高达 1Mbp
支持时间触发通信
自动地接收和发送 CAN 报文,支持使用标准 ID 和扩展 ID 的报文;
支持报文发送的优先级要求(优先级特性可软件配置)。
具有 3 个发送邮箱
§ 每个发送邮箱中包含有标识符寄存器 CAN_TIxR、数据长度控制寄存器 CAN_TDTxR及 2 个数据寄存器 CAN_TDLxR、 CAN_TDHxR。CAN相关的寄存器
§ 我们要使用 CAN 外设发送报文时,把报文的各个段分解,按位置写入到这些寄存器中,并对标识符寄存器 CAN_TIxR 中的发送请求寄存器位 TMIDxR_TXRQ 置 1,即可把数据发送出去。
具有 3 级深度的 2 个接收 FIFO
可变的过滤器组(最多 28 个)
不支持使用 DMA 进行数据收发。
STM32 互联型产品中,带有 2 个 CAN 控制器。双 CAN 的框图:
我们使用的 STM32F103ZET6 属于增强型,不是互联型,只有 1 个 CAN 控制器。
由发送单元在非同步的情况下发送的每秒钟的位数称为位速率,就是波特率。
采样点,是指读取总线电平,并将读到的电平作为位值的点。位置在 PBS1 结束处。
○ 通过控制各段的长度,可以对采样点的位置进行偏移,以便准确地采样。
改变采样点:
具体在同步CAN 的数据同步分为硬同步和重新同步。
若某个 CAN 节点通过总线发送数据时,它会发送一个表示通讯起始的信号(帧起始信号),该信号是一个由高变低的下降沿。
ü 挂载到 CAN 总线上的通讯节点在不发送数据时,会时刻检测总线上的信号。
ü 某节点检测到总线的帧起始信号不在节点内部时序的 SS 段范围,所以判断它自己的内部时序与总线不同步,因而这个状态的采样点采集得的数据是不正确的。
ü 所以节点以硬同步的方式调整,把自己的位时序中的 SS段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。
如果在一帧很长的数据内,节点信号与总线信号相位有偏移时,硬同步方式就无能为力了。
ü 因而引入重新同步方式,它利用普通数据位的高至低电平的跳变沿来同步(帧起始信号是特殊的跳变沿)。
ü 重新同步与硬同步方式相似的地方是它们都使用 SS 段来进行检测,同步的目的都是使节点内的 SS段把跳变沿包含起来。
○ 重新同步的方式分为超前和滞后两种情况,以总线跳变沿与 SS 段的相对位置进行区分。
§ 第一种相位超前的情况如图 43-7,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前 2Tq,这时控制器在下一个位时序中的 PBS1 段增加 2Tq 的时间长度,使得节点与总线时序重新同步。
第二种相位滞后的情况如图 43-8,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后 2Tq,这时控制器在前一个位时序中的 PBS2 段减少 2Tq 的时间长度,获得同步。
在重新同步的时候, PBS1 和 PBS2 中增加或减少的这段时间长度被定义为“重新同步补偿宽度 SJW (reSynchronization Jump Width)”。
§ 一般来说 CAN 控制器会限定 SJW 的最大值,如限定了最大 SJW=3Tq 时,单次同步调整的时候不能增加或减少超过 3Tq 的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。
§ 当控制器设置的 SJW 极限值较大时,可以吸收的误差加大,但通讯的速度会下降。
在总线空闲态,最先开始发送消息的单元获得发送权。当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。
根据线与特性: 若两个节点同时竞争 CAN 总线的占有权,当它们发送报文时, 若首先出现隐性电平(1),则会失去对总线的占有权,进入接收状态。
程序选择 1 个空置的邮箱(TME=1)→设置标识符(ID),数据长度和发送数据→设置 CAN_TIxR 的 TXRQ 位为 1,请求发送→邮箱挂号(等待成为最高优先级)→预定发送(等待总线空闲)→发送→邮箱空置。
整个流程如图
CAN 接收到的有效报文,被存储在 3 级邮箱深度的 FIFO 中。
ü FIFO 完全由硬件来管理,从而节省了 CPU 的处理负荷。
ü 应用程序只能通过读取 FIFO输出邮箱,来读取 FIFO 中最先收到的报文。
○ 这里的有效报文是指那些正确被接收的(直到 EOF都没有错误)且通过了标识符过滤的报文。前面我们知道 CAN 的接收有 2 个 FIFO,我们每个滤波器组都可以设置其关联的 FIFO,通过 CAN_FFA1R 的设置,可以将滤波器组关联到FIFO0/FIFO1。
CAN 接收流程为:FIFO 空→收到有效报文→挂号_1(存入 FIFO 的一个邮箱,这个由硬件控制,我们不需要理会)→收到有效报文→挂号_2→收到有效报文→挂号_3→收到有效报文→溢出。
我们必须在 FIFO 溢出之前,读出至少 1 个报文,否则下个报文到来,将导致 FIFO 溢出,从而出现报文丢失。每
读出 1 个报文,相应的挂号就减 1,直到 FIFO 空。
FIFO 接收到的报文数,我们可以通过查询 CAN_RFxR 的 FMP 寄存器来得到,只要 FMP不为 0,我们就可以从 FIFO 读出收到的报文。
STM32 外设定义的位时序与我们前面解释的 CAN 标准时序有一点区别。 STM32 把传播时间段和相位缓冲段 1(STM32 称之为时间段 1)合并了,所以 STM32 的 CAN 一个位只有 3 段:
○ 同步段(SYNC_SEG)
○ 时间段 1(BS1),PTS 段与 PBS1 段合在一起的。
§ STM32 的 BS1 段可以设置为 1~16 个时间单元,刚好等于我们上面介绍的传播时间段和相位缓冲段 1 之和。
○ 时间段2(BS2)。
图中给出了 CAN 波特率的计算公式
○ 我们只需要知道 BS1 和 BS2 的设置,以及 APB1的时钟频率(一般为 36Mhz),就可以方便的计算出波特率。
○ 比如设置 TS1=6、TS2=7 和 BRP=4,在 APB1 频率为 36Mhz 的条件下,即可得到 CAN 通信的波特率=36000/[(7+8+1)*5]=450Kbps。每秒可传输的数据位的个数即为通讯中的波特率。
上述为将波特率设置为1Mbps的方式。
对 STM32F103ZET6 来说,只有[13:0]这 14 个位有效。
对 28 个滤波器组的工作模式,都可以通过该寄存器设置,不过该寄存器必须在过滤器处于初始化模式下(CAN_FMR 的 FINIT 位=1),才可以进行设置。
过滤器工作的两种模式
每组筛选器包含 2 个 32 位的寄存器,分别为 CAN_FxR1 和 CAN_FxR2,它们用来存储要筛选的 ID 或掩码
§ 举个简单的例子,我们设置过滤器组 0 工作在:1 个 32 为位过滤器-标识符屏蔽模式,然后设置 CAN_F0R1=0XFFFF0000,CAN_F0R2=0XFF00FF00。
§ 存放到 CAN_F0R1 的值就是期望收到的 ID,即我们希望收到的映像(STID+EXTID+IDE+RTR)最好是:0XFFFF0000。
§ 0XFF00FF00 就是设置我们需要必须关心的 ID,表示收到的映像,其位[31:24]和位[15:8]这 16个位的必须和 CAN_F0R1 中对应的位一模一样,而另外的 16 个位则不关心,都认为是正确的 ID,即收到的映像必须是 0XFFxx00xx,才算是正确的(x 表示不关心)。
该寄存器用于设置分频、Tbs1、Tbs2以及 Tq 等非常重要的参数,直接决定了 CAN 的波特率。另外该寄存器还可以设置 CAN 的工作模式。
位19-16:Tbs1
○ //tbs1:时间段 1 的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq
位22-20: Tbs2
○ //tbs2:时间段 2 的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq;
位9-0:Tq,CAN1 和 CAN2 外设都是挂载在 APB1 总线上的,而位时序寄存器 CAN_BTR 中的 BRP[9:0]寄存器位可以设置 CAN 外设时钟的分频值。
○ PCLK 指 APB1 时钟,默认值为 45MHz。AHB预分频器
○ //tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
○ //brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
25-24:SJW,本成员可以配置 SJW 的极限长度,即 CAN 重新同步时单次可增加或缩短的最大长度,它可以被配置为 (CAN_SJW_1/2/3/4tq)。
位30LBKM:环回模式
○ STM32 提供了两种测试模式,环回模式和静默模式,当然他们还可以组合成环回静默模式。
○ 在环回模式下,bxCAN 把发送的报文当作接收的报文并保存(如果可以通过接收过滤)在接收邮箱里。也就是环回模式是一个自发自收的模式
○ 环回模式可用于自测试。为了避免外部的影响,在环回模式下 CAN 内核忽略确认错误(在数据/远程帧的确认位时刻,不检测是否有显性位)。
○ 在环回模式下,bxCAN 在内部把 Tx 输出回馈到 Rx 输入上,而完全忽略 CANRX 引脚的实际状态。
位31SILM:静默模式
○ 静默模式下,它自己的输出端的逻辑 0 数据会直接传输到它自己的输入端,逻辑1 可以被发送到总线
○ 所以它不能向总线发送显性位(逻辑 0),只能发送隐性位(逻辑 1)。
○ 输入端可以从总线接收内容。由于它只可发送的隐性位不会强制影响总线的状态,所以把它称为静默模式。
○ 这种模式一般用于监测,它可以用于分析总线上的流量,但又不会因为发送显性位而影响总线。
回环静默模式
○ 回环静默模式是以上两种模式的结合
○ 自己的输出端的所有内容都直接传输到自己的输入端
○ 并且不会向总线发送显性位影响总线,不能通过总线监测它的发送内容。
○ 输入端只接收自己发送端的内容,不接收来自总线上的内容。
○ 这种方式可以在“热自检”时使用,即自我检查的时候,不会干扰总线。
(x=0~2),该寄存器我们本章仅用来设置数据长度,即最低 4 个位。
(x=0~2),该寄存器各位
该寄存器用来存储将要发送的数据,这里只能存储低 4 个字节,另外还有一个寄存器CAN_TDHxR,该寄存器用来存储高 4 个字节,这样总共就可以存储 8 个字节。CAN_TDHxR的各位描述同 CAN_TDLxR 类似
该寄存器各位对应滤波器组和前面的几个寄存器类似,这里就不列出了,对对应位置 1,即开启对应的滤波器组;置 0 则关闭该滤波器组。
该寄存器设置报文通过滤波器组之后,被存入的 FIFO,如果对应位为 0,则存放到 FIFO0;如果为 1,则存放到 FIFO1。
STM32的bxCAN
(x=0/1),该寄存器各位描述同 CAN_TIxR 寄存器几乎一模一样,只是最低位为保留位,该寄存器用于保存接收到的报
文标识符等信息,我们可以通过读该寄存器获取相关信息。
x(CAN_FiRx)(互联产品中 i=0~27,其它产品中 i=0~13;x=1/2)
根据过滤器位宽和模式的不同设置,这两个寄存器的功能也不尽相同。
我们通过 WK_UP 按键选择 CAN 的工作模式(正常模式/环回模式),然后通过 KEY0控制数据发送,并通过查询的办法,将接收到的数据显示在 LCD 模块上。
如果是环回模式,我们不需要 2 个开发板。如果是正常模式,我们就需要 2 个战舰开发板,并且将他们的 CAN 接口对接起来,然后一个开发板发送数据,另外一个开发板将接收到的数据显示在 LCD 模块上。
CAN 相关的固件库函数和定义分布在文件 stm32f10x_can.c 和头文件 stm32f10x_can.h 文件中。
第一步就要使能 CAN 的时钟。其次要设置 CAN 的相关引脚为复用输出,设置 PA11 为上拉输入(CAN_RX 引脚)PA12 为复用输出(CAN_TX 引脚),并使能 PA 口的时钟。使能 CAN1 时钟的函数是:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟
具体函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能 PORTA 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 IO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 IO
CAN是挂载在APB1之下的。
//使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能 PORTA 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟
//初始化 GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 PA11,PA12
//引脚复用映射配置
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1);//PA11 复用为 CAN1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1);//PA12 复用为 CAN1
基本上还是一致的
先设置 CAN_MCR 寄存器的 INRQ 位,让 CAN 进入初始化模式,然后设置CAN_MCR 的其他相关控制位。
再通过 CAN_BTR 设置波特率和工作模式(正常模式/环回模式)等信息。 最后设置 INRQ 为 0,退出初始化模式。
参考:
在库函数中,提供了函数 CAN_Init()用来初始化 CAN 的工作模式以及波特率。
CAN_Init()函数体中,在初始化之前,会设置 CAN_MCR 寄存器的 INRQ 为 1 让其进入初始化模式,然后初始化 CAN_MCR 寄存器和 CRN_BTR 寄存器之后,会设置 CAN_MCR 寄存器的 INRQ 为 0让其退出初始化模式。
CAN_Init()函数的定义:
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);
第一个参数就是 CAN 标号,这里我们的芯片只有一个 CAN,所以就是 CAN1。
第二个参数是 CAN 初始化结构体指针,结构体类型是 CAN_InitTypeDef,下面我们来看看这个结构体的定义:
typedef struct
{
uint16_t CAN_Prescaler;
uint8_t CAN_Mode;
uint8_t CAN_SJW;
uint8_t CAN_BS1;
uint8_t CAN_BS2;
FunctionalState CAN_TTCM;
FunctionalState CAN_ABOM;
FunctionalState CAN_AWUM;
FunctionalState CAN_NART;
FunctionalState CAN_RFLM;
FunctionalState CAN_TXFP;
} CAN_InitTypeDef;
前面 5 个参数是用来设置寄存器 CAN_BTR,用来设置模式以及波特率相关的参数。
其他设置波特率相关的参数 CAN_Prescaler,CAN_SJW,CAN_BS1 和 CAN_BS2 分别用来设置波特率分频器,重新同步跳跃宽度以及时间段 1 和时间段 2 占用的时间单元数。
后面 6 个成员变量用来设置寄存器 CAN_MCR,也就是设置 CAN 通信相关的控制位。
初始化实例为:
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= CAN_Mode_LoopBack; //模式设置: 1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度为个时间单位
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq; //时间段 1 占用 8 个时间单位
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;//时间段 2 占用 7 个时间单位
CAN_InitStructure.CAN_Prescaler=5; //分频系数(Fdiv)
CAN_Init(CAN1, &CAN_InitStructure); // 初始化 CAN1
103与407在CAN的参数设置上是一样的
CAN_BTR位时序寄存器
我们将使用滤波器组 0,并工作在 32 位标识符屏蔽位模式下。
先设置 CAN_FMR的 FINIT 位,让过滤器组工作在初始化模式下,然后设置滤波器组 0 的工作模式以及标识符 ID和屏蔽位。最后激活滤波器,并退出滤波器初始化模式。
在库函数中,提供了函数 CAN_FilterInit ()用来初始化 CAN 的滤波器相关参数,CAN_Init()函数体中,在初始化之前,会设置 CAN_FMR 寄存器的 INRQ 为 INIT 让其进入初始化模式,然后初始化 CAN 滤波器相关的寄存器之后,会设置 CAN_FMR 寄存器的 FINIT 为 0 让其退出初始化模式。
CAN_FilterInit ()函数的定义:
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);
这个函数只有一个入口参数就是 CAN 滤波器初始化结构体指针,结构体类型为CAN_FilterInitTypeDef,下面我们看看类型定义:
typedef struct
{
uint16_t CAN_FilterIdHigh;
uint16_t CAN_FilterIdLow;
uint16_t CAN_FilterMaskIdHigh;
uint16_t CAN_FilterMaskIdLow;
uint16_t CAN_FilterFIFOAssignment;
uint8_t CAN_FilterNumber;
uint8_t CAN_FilterMode;
uint8_t CAN_FilterScale;
FunctionalState CAN_FilterActivation;
} CAN_FilterInitTypeDef;
结构体一共有 9 个成员变量
第 1 个至第 4 个是用来设置过滤器的 32 位 id 以及 32 位 mask id,分别通过 2 个 16 位来组合的
CAN_FilterIdHigh
○ 用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的高 16 位;
○ 若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。
CAN_FilterIdLow
○ 也是用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的低 16 位;
○ 若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。
CAN_FilterMaskIdHigh
○ 存储的内容分两种情况,当筛选器工作在标识符列表模式时,它的功能与 CAN_FilterIdHigh 相同,都是存储要筛选的 ID;
○ 当筛选器工作在掩码模式时,它存储的是 CAN_FilterIdHigh 成员对应的掩码,与 CAN_FilterIdLow 组成一组筛选器。
CAN_FilterMaskIdLow
○ 存储的内容也分两种情况,当筛选器工作在标识符列表模式时,它的功能与 CAN_FilterIdLow 相同,都是存储要筛选的 ID;
○ 而当筛选器工作在掩码模式时,它存储的是 CAN_FilterIdLow 成员对应的掩码,与CAN_FilterIdLow 组成一组筛选器。
CAN_FilterFIFOAssignment
○ 用来设置 FIFO 和过滤器的关联关系,当报文通过筛选器的匹配后,该报文会被存储到哪一个接收 FIFO
CAN_FilterNumber
○ 用来设置初始化的过滤器组编号,取值范围为 0~13。
FilterMode
○ 用来设置过滤器组的模式,取值为标识符列表模式CAN_FilterMode_IdList 和标识符屏蔽位模式CAN_FilterMode_IdMask。
FilterScale
○ 用来设置过滤器的位宽为 2 个 16 位 CAN_FilterScale_16bit 还是 1 个32 位 CAN_FilterScale_32bit。
CAN_FilterActivation
○ 用来激活该过滤器。
过滤器初始化参考实例代码:
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器 0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32 位
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;32 位 ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32 位 MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;// FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器 0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
如果用到中断,就还需要进行中断相关的配置,本章因为没用到中断,所以就不作介绍了。
STM32的bxCAN
CAN_FMR 寄存器
在初始化 CAN 相关参数以及过滤器之后,接下来就是发送和接收消息了。库函数中提供了发送和接受消息的函数。发送消息的函数是:
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);
接受消息的函数是:
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);
发送结构体
*/
typedef struct {
uint32_t StdId; /*存储报文的标准标识符 11 位, 范围是0-0x7FF. */
uint32_t ExtId; /*存储报文的扩展标识符 29 位, 0-0x1FFFFFFF. */
//ExtId 与 StdId 这两个成员根据下面的 IDE 位配置,只有一个是有效的。
uint8_t IDE; /*存储 IDE 扩展标志 */
//当它的值为宏 CAN_ID_STD 时表示本报文是标准帧,使用 StdId 成员存储报文 ID;
//当它的值为宏 CAN_ID_EXT 时表示本报文是扩展帧,使用 ExtId 成员存储报文 ID。
uint8_t RTR; /*存储 RTR 远程帧标志*/
//当它的值为宏 CAN_RTR_Data 时表示本报文是数据帧;
//当它的值为宏 CAN_RTR_Remote 时表示本报文是遥控帧,由于遥控帧没有数据段,所以当报文是遥控帧时,下面的 Data[8]成员的内容是无效的。
uint8_t DLC; /*存储报文数据段的长度, 0-8 */
uint8_t Data[8]; /*存储报文数据段的内容 */
} CanTxMsg;
/**
* @brief CAN Rx message structure definition
* 接收结构体
*/
typedef struct {
uint32_t StdId; /*存储了报文的标准标识符 11 位, 0-0x7FF. */
uint32_t ExtId; /*存储了报文的扩展标识符 29 位, 0-0x1FFFFFFF. */
uint8_t IDE; /*存储了 IDE 扩展标志 */
uint8_t RTR; /*存储了 RTR 远程帧标志*/
uint8_t DLC; /*存储了报文数据段的长度, 0-8 */
uint8_t Data[8]; /*存储了报文数据段的内容 */
uint8_t FMI; /*存储了 本报文是由经过筛选器存储进 FIFO 的, 0-0xFF */
//它存储了筛选器的编号,表示本报文是经过哪个筛选器存储进接收 FIFO 的,可以用它简化软件处理。
} CanRxMsg;
发送和接收代码
u8 CAN1_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; // 标准标识符为 0
TxMessage.ExtId=0x12; // 设置扩展标示符(29 位)
TxMessage.IDE=0; // 使用扩展标识符
TxMessage.RTR=0; // 消息类型为数据帧,一帧 8 位
TxMessage.DLC=len; // 发送两帧信息
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i]; // 第一帧信息
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;
if(i>=0XFFF)return 1;
return 0;
}
//can 口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到; 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{ u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;//没有接收到数据,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<RxMessage.DLC;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC; //返回接受到的数据长度
}
如果需要的话
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0 消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; / 主优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为 0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,
CAN_Mode_LoopBack); //CAN 初始化环回模式,波特率 500Kbps