1.CAN简介
CAN是Controller Area Network的缩写(简称为CAN),是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。
CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。
CAN的可靠性已经得到广泛认可,被应用于工业自动化、船舶、医疗设备、工业设备等方面。
2.CAN物理层
CAN是一种异步通讯,只有CAN_High 和 CAN_Low两条信号线,共同构成一组差分信号线,以差分信号的形式进行通讯。
2.1 闭环总线网络
CAN物理层的形式主要有两种,如图 38-1是其中的一种“闭环网络”,它允许总线最大长度为40m,最高速度为1Mbps,可以看到总线的两端各有一个120欧的电阻,这是规定。节点就是不同的设备,连接到一个闭环总线上面。
2.2 开环总线网络
这是另一种物理层的形式,简单的说就是另一种的连接方法,如图38-2,最大传输距离为1Km,最高通讯速率为125Kbps,这里的两根线是独立的,每根线上串联一个2.2欧的电阻。
2.3 通讯节点
CAN节点是指能够挂接在CAN总线上的单元,并能通过CAN总线实现各个节点间的通信,以实现复杂的控制过程。理论上 CAN总线连接的节点可达 110个,但实际上收到总线上的时间延迟及电气负载的限制。当连接多节点时,降低通信速率,可连接的节点增加;提高通信速率,则可连接的节点数减少。
CAN通讯节点由一个CAN控制器及CAN收发器,CAN控制器用于实现实现CAN总线的协议底层以及数据链路层,用于生成CAN帧并以二进制码流的方式发送,在此过程中进行位填充、添加 CRC校验、应答检测等操作;将接收到的二进制码流进行解析并接收,在此过程中进行收发比对、去位填充、执行CRC校验等操作。此外还需要进行冲突判断、错误处理等诸多任务。
CAN收发器(有时也叫做驱动器),用于将二进制码流转换为差分信号发送,将差分信号转换为二进制码流接收。
我们已经知道CAN控制器和收发器的概念,观察图38-1和图38-2,为接线方式,控制器和收发器通过普通的类似 TTL逻辑信号来连接,CAN_Low和CAN_High是一对差分信号线。
当CAN节点需要发送数据时,控制器把要发送的二进制编码通过通过CAN_Tx线发送到收发器,然后由收发器把这个普通的逻辑电平信号转换成差分信号,通过差分线CAN_High 和 CAN_Low线输出到CAN总线网络。而通过收发器接收总线上的数据的到控制器时,是相反的过程。
2.4 CAN协议中的差分信号
差分信号又称为差模信号,具体的含义这里不多介绍了。CAN协议中对它使用的CAN_High以及CAN_Low表示的差分信号做了规定,如图 38-3,可以看到,显性电平对应逻辑“0”,隐性电平对应逻辑“1”。当表示为隐性电平(逻辑”1”)时,CAN_High 和 CAN_Low的电压均为2.5V,此时它们的电压差为OV;当表示显性电平(逻辑“0”)时,CAN_High的电压为3.5V,CAN_Low的电压为1.5v,它们的电压差为2v。举个例子:当CAN收发器从CAN_Tx 线接收到来自CAN控制器的低电平信号(逻辑“0”)时,它会使CAN_High输出3.5v,同时CAN_Low输出1.5V。
在总线上显性电平具有优先权,只要有一个单元(也就是节点〉输出显性电平,总线上即为显性电平。而隐性电平则具有包容的含义,只有所有的单元都输出隐性电平,总线上才为隐性电平。那么可以知道,CAN通讯是半双工的,收发数据需要分开来进行,由于是公用总线,在整个网络中,同一时刻只能由一个通讯节点发送信号,其余的节点在该时刻只能接收。
3.CAN协议层
3.1 CAN的位时序以及同步
由于CAN属于异步通讯,没有时钟信号线,那么节点间就需要约定好波特率进行通讯。同时,为实现正确的总线电平采样,确保通讯正常,由此引出“位时序”的概念。
3.2 位时序的组成
CAN协议将每一个数据位的时序分解成4段,即:ss段、PTS段、PBS1段、PBS2段,这4段加起来就是一个CAN数据位的长度。而分解后的最小时间单位是Tq,一个完整的位由8~25个Tq组成。如图38-4是总线电平下每个位分解。
- ss段: 称为同步段,用于使总线上的各个节点同步,要求有一个跳变沿位于此段内,该段的长度为1Tq;
- PTS 段,称为传播时间段,用于补偿网络内的物理延时,它是信号在总线上传播时间,输入比较器延时和输出驱动器延时之和的两倍,该段长可以为1~8Tq;
- PBS1段,称为相位缓冲段,主要用来补偿变压阶段的误差,它的实际长度在重新同步的时候可以加长,该段大小可以为1~8Tq;
- PBS2段,称为另一个相位缓冲段,也是用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长,该段大小可以为2~8Tq。
3.3 通讯的波特率
总线上的各个通讯节点只要约好1个Tq的时间长度以及每一个数据位占据多少个Tq,就可以确定CAN通讯的波特率。
假设图38-4中 1Tq=1us,而有19个Tq,则传输一位数据需要的实际为19us。这样每秒可以传输的数据位个数为:1*106 / 19=52631.6 (bps),即为波特率。3.4 CAN同步机制
CAN规范定义了自己独有的同步方式: 硬同步和重同步。同步与位时序密切相关。同步是由节点自身完成的,节点将检测到来自总线的沿与其自身的位时序相比较,并通过硬同步或重同步适配(调整)位时序。
CAN总线的位同步只有在节点检测到“隐性位(逻辑1)”到”显性位(逻辑o)”的跳变时才会产生,我们知道在同步段需要跳变沿,当跳变沿不处于此同步段内时,那么就会产生相位误差,该相位误差就是跳变沿与同步段结束位置直接的距离,产生误差的原因有多种,那么针对此种误差,也就是上面说到的硬同步和重同步。
硬同步只在总线空闲时通过一个下降沿(下面将讲到的帧起始)来完成,此时不管有没有相位误差,所有节点的位时序将重新开始。强迫引起硬同步的跳变沿位于重新开始的位时间的同步段之内。
☆ 一个位时间内只允许一种同步方式,要么硬同步要么重同步;
☆ 任何一个从“隐性”到“显性”的下降沿 都可以用于同步;
☆ 硬同步发生在报文的SOF位就是起始帧,所有接收节点调整各自当前位的同步段,使其位于发送的SOF位内;
☆ 重同步发生在一个报文SOF位之外的其它段,当下降沿落在了同步段之外时发生重同步;
☆ 在SOF到仲裁场发送的时间段内,如果有多个节点同时发送报文,那么这些发送节点对跳变沿不进行重同步
重同步,在消息帧的随后位中,每当有从“隐性位”到“显性位”的跳变,并且该跳变段落在了同步段之外,就会引起一次重同步。重同步机制可以根据跳变沿增长或者缩短位时序以调整采样点的位置,保证正确采样。
重同步发生在一个报文SOF位之外的其它位场内,当接收节点Node_B当前位的下降沿落在了发送节点Node_A当前位的同步段之外时发生重同步。重同步会导致相位缓冲段1的延长或者相位缓冲段2的缩短,从而保证采样点的准确。
4. CAN报文
和 SPI和串口协议不同的是,CAN使用两条差分信号线,因而只能表示一个信号,于是为了实现完整的传输信号功能,CAN协议便对数据、操作命令以及同步信号进行打包,打包后的内容便称为报文。
4.1 CAN报文格式
CAN一共规定了5种类型的帧,分别是: 数据帧、遥控帧、错误帧、过载帧、帧间隔,作用见表38-1。另外,数据帧和遥控帧有标准格式和扩展格式两个,唯一的区别是标识符(ID)不同,在下面数据帧的结构讲解中,我们会对两个格式进行介绍。
数据帧是CAN通信中最中意、最复杂的报文,以一个显性位(逻辑0)开始,以一个7个连续隐性位(逻辑1)结束。在它们之间分为仲裁段、控制段、数据段、CRC段和 ACK段,加上起始和结束总共有7个段。
在图38-5中,在标准帧和扩展帧除了ID和DLC之外,还有RTR、IDE、SRR位。首先是RTR位,翻译为远程传输请求位,用显性电平和隐性电平来区分数据帧和遥控帧。然后是IDE位,为标识符扩展位,用显性电平和隐性电平来区分标准格式还是扩展格式。最后是SRR位,只存在于扩展格式,它用于替代标准格式中的RTR位,由于扩展帧中的SRR位为隐性位,RTR是数据帧为显性位,所以在两个ID相同的标准格式报文与扩展格式报文中,标准格式的优先级更高。
- 帧起始
SOF段(Start Of Frame),翻译为帧起始,标准帧和扩展帧都是通过一个显性电平表示帧起始。它用于通知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步。
- 仲裁段
简单来说就是表示优先级的段。仲裁段的内容主要为本数据帧的ID信息,数据帧具有标志格式和扩展格式两种,具体的区别就是ID信息的长度,标准格式的ID为11位,扩展格式的ID为29位,观察图38-5应该也发现了。
在CAN协议中,ID起着重要的作用,它决定着数据帧发送的优先级,也决定着其它节点是否会接收到这个数据帧。CAN的数据通信没有主从之分,任意一个节点可以向其他(一个或多个)节点发起数据通信,而对总线的占有权是由信息的重要性决定的,也就是说重要的信息,对它打包上一个优先级高的ID,使它能够及时地发送出去,也正是由于这样优先级的分配原则,使CAN 的扩展性大大加强,在总线上增加或减少节点并不会影响其它的设备。
那么怎么仲裁呢?让我们联系前面的内容,显性电平的优先权和隐性电平的包容性,CAN正是利用这个特性进行仲裁。若两个节点同时竞争CAN总线的占有权,当它们发送报文时,若首先出现隐性电平,则会失去对总线的占有权,进入接收状态。
- 控制段
r1和r0为保留位,默认置为显性,它最主要的是由4位组成的DLC段,MSB先行,DLC段表示的数字为0~8。
- 数据段
为数据帧的核心内容,它是节点要发送的原始信息,由0~8个字节组成,MSB先行。
- CRC段
为保证报文的正确传输,CAN的报文包含了一段15位的CRC校验码,若检验出错则向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC计算一般由CAN控制器硬件完成,而出错时处理由软件控制最大的重发数。在CRC校验码之后,有一个CRC界定符,为隐性,将CRC校验码与后面的ACK段分隔开。
- ACK段
包括一个ACK槽位和ACK界定符,在ACK槽位中,发送端发送的为隐性位,接收端在这一位中发送显性位以表示应答,在 ACK界定槽和帧结束之间由ACK界定符分隔开。
- 帧结束段
5. STM32的CAN外设简介
STM32具有bxCAN(基本CAN),是基本扩展CAN,它支持CAN协议2.OA和2.0B。它的设计目标是以最小的CPU负荷来高效处理大量接收到的报文。也支持报文发送的优先级要求(通过软件配置)。bxCAN提供所有支持时间触发通信模式所需的硬件功能。
该CAN控制器支持最高的通讯速率为1Mb/s;它可以自动地接收和发送CAN报文,支持使用标准ID和扩展ID的报文;外设中具有3个发送邮箱;具有2个3级深度的接收FIFO,可使用过滤功能只接收或不接收某些ID号的报文。具有双CAN,CAN1是主bxCAN,CAN2为从bxCAN。
STM32有两组CAN控制器,其中 CAN1为主设备,负责管理bxCAN 和512字节的SRAM存储器之间的通信;
CAN2为从设备,它不能直接访问SRAM存储器。在使用CAN2的时候必须先使能CAN1外设的时钟。
可以看到图38-6中包含CAN控制内核、发送邮箱、接收FIFO以及接收滤波器。

5.1.CAN控制内核
框图中标号①处的CAN控制内核包含了各种控制器及状态寄存器,通过这些寄存器可以配置CAN的参数,如波特率;请求发送报文;处理报文的接收;管理相关中断、获取诊断信息等。这里主要讲解的是主控制器寄存器和位时序控制器。
主控制寄存器(CAN_MCR)
DBF调试冻结(DBF)
可设置为调试时的工作状态或者禁止收发的状态,禁止收发时仍然可以访问接收FIFO的中的数据。
- 时间触发通信模式(TTCM)
用于运行和禁止时间触发通信模式。在该模式下,CAN硬件的内部定时器被激活,并且产生时间截,分别存储在CAN_RDTxR/CAN_TDTxR寄存器中。内部定时器在接收和发送的帧起始位的采样点位置被采样,并生成时间戳。
- 自动离线管理(ABOM)
该位决定CAN硬件在什么条件下可以退出离线状态,为“1”时,一旦硬件检测到128次11位的连续隐性位,则自动退出离线状态:为“0”””时,软件对CAN_MCR寄存器的INRQ位进行置1随后清公’后,一旦硬件检测到128次11位连续的隐性位,则退出离线状态。
- 自动唤醒(AWUM)
CAN外设可以使用软件进入低功耗的睡眠模式,使能此功能后,当CAN检测到总线活动的时候,会自动唤醒。
- 自动重传(NART)
设置为”0”时,CAN硬件发送报文时会一直重传,直达成功为止:为”1”时,CAN报文只被发送一次,不管有没成功。
- 接收FIFO锁定模式(RFLM)
该功能用于锁定接收FIFO,锁定时,接收FIFO溢出时,会丢弃下一个接收到的报文;不锁定,则下一个接收到的报文会覆盖原报文。
- 发送 FIFO 优先级(TXFP)
当发送邮箱中有多个报文同时在等待发送时,该位控制是报文的ID来决定还是发送请求的顺序来决定。
CAN位时序寄存器(CAN_BTR)
为了方便用于调试,CAN通过设置SILM和 LBKM位可以将模式设置为正常模式、静默模式、回环模式和禁止回环模式。各种模式的介绍可以参考《STM32F10xxx Cortex-M3编程手册》中第22.5章节测试模式内容。这里我们重点是分析位时序以及波特率的问题。
STM32中的CAN位时序和我们前面解读的有些许的区别,如图38-7,三段分别为:同步段(SYNc_SEG)、时间段1(BS1)、时间段2(BS2)。可以发现,相比起前面讲述的CAN 时序,这里是少了一段,理解STM32的CAN时序时,BS1段是CAN标准协议中的PTS段和 PBs1段的结合,而BS2段就相当于PBS2段。
在图38-7中,波特率的计算公式。我们需要的是配置好位时序寄存器CAN_BTR的TS1[3:0]位和TS1[2:0]位来设定BS1和 BS2段的长度,就可以确定每个CAN数据位的时间,这里分别设置为6tq和3tq。
BRP[9:0]位是的设定CAN外设时钟的分频值,设置为4。由于CAN是挂APB1时钟上,频率为 36MHz,t=(4*1/36)=1/9M;则正常位时间=1+6+3=10tq;可以得到波特率为:(9/10)Mbps。
APB1Clock=36Mhz, CAN_BS1=3, CAN_BS2=5, CAN_Prescaler=16
36000k/9/16=250k/s
5.2 CAN发送邮箱
在图38-6中的标号②是CAN外设的发送邮箱,它一共有3个发送邮箱,即可以缓存3个待发送的报文。由发送跳段根据优先级决定哪个邮箱的报文先被发送。
每个发送邮箱含有标记符寄存器( CAN_TlxR)、数据长度控制寄存器(CAN_TDTxR)和两个数据寄存器CAN_TDLxR、CAN_TDHxR。
当我们要发送报文时,把报文的各个段分解,按位置写入到这些寄存器中,并将CAN_TlxR中的发送请求寄存器位TMIDxR_TXRQ置1,即可完成发送,使用HAL库的话,会更加的简单,在程序分析里面会讲解。
5.2 CAN接收FIFO
CAN外设有2个接收FIFo,每个FIFO有3个邮箱,即可以缓存6个接收到的报文。当接收到报文时,FIFO 的报文计数器会自增,而STM32内部读取FIFO数据之后,报文计数器会自减,我们便可以通过状态寄存器获知报文计数器的值。
和发送邮箱类似,也有4个寄存器,类型也是一样的。通过中断或者状态寄存器知道接收FIFO有数据后,再读取这些寄存器的值即可把接收到的报文加载到STM32的内存中。
5.3. 接收过滤器
在CAN 协议中,消息的标识符与节点地址无关,但是和消息的内容有关。因此,接收节点会根据标识符的值来确定软件是否需要该消息。STM32在的CAN外设接收报文前会先使用接收过滤器进行检查,只接收需要的报文到FIFO中。
CAN过滤器的寄存器有多个,可以自己看手册进行相应的配置,这里就不多讲解了。过滤的方法有两种模式分别是ID列表模式和掩码模式。
5.4 CAN通讯硬件电路设计
YS-F1Pro开发板使用SN65HVD230作为CAN驱动。CAN接口采用5.08mm的间距的接线端子引出,如图38-8。
CAN总线上缺省贴装了120欧的匹配电阻,如果将开发板作为CAN总线的一个中间节点使用,那么可能需要去掉这哥120欧的匹配电阻.对于SN65HVD230,第8脚为高电平时,IC将关闭发送,只能接收。当第8脚的电压低于1.2v时,收发器处于允许发送状态(也可以接收)。R36电阻的阻值可以控制波形的斜率阻值越小,波形的上升沿和下降沿越陡。SN65HVD230的第4脚是接收到的信号输出,为推挽输出模式,因此和 STM32相连时,无需外加上拉电阻。
5. 使用CubeMX生产工程
5.1.CubeMx配置工程
配置报文优先级的判断方法
使能中断->接收即可
GPIO->默认即可
5.2 结构体分析
1.CAN句柄
typedef struct __CAN_HandleTypeDef
{
//选择CAN1 还是CAN2
CAN_TypeDef *Instance; /*!< Register base address */
//初始化的结构体变量,如下面
CAN_InitTypeDef Init; /*!< CAN required parameters */
//状态
__IO HAL_CAN_StateTypeDef State; /*!< CAN communication state */
//错误编码
__IO uint32_t ErrorCode; /*!< CAN Error code.
This parameter can be a value of @ref CAN_Error_Code */
#if USE_HAL_CAN_REGISTER_CALLBACKS == 1
void (* TxMailbox0CompleteCallback)(struct __CAN_HandleTypeDef *hcan);/*!< CAN Tx Mailbox 0 complete callback */
void (* TxMailbox1CompleteCallback)(struct __CAN_HandleTypeDef *hcan);/*!< CAN Tx Mailbox 1 complete callback */
void (* TxMailbox2CompleteCallback)(struct __CAN_HandleTypeDef *hcan);/*!< CAN Tx Mailbox 2 complete callback */
void (* TxMailbox0AbortCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Tx Mailbox 0 abort callback */
void (* TxMailbox1AbortCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Tx Mailbox 1 abort callback */
void (* TxMailbox2AbortCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Tx Mailbox 2 abort callback */
void (* RxFifo0MsgPendingCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Rx FIFO 0 msg pending callback */
void (* RxFifo0FullCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Rx FIFO 0 full callback */
void (* RxFifo1MsgPendingCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Rx FIFO 1 msg pending callback */
void (* RxFifo1FullCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Rx FIFO 1 full callback */
void (* SleepCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Sleep callback */
void (* WakeUpFromRxMsgCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Wake Up from Rx msg callback */
void (* ErrorCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Error callback */
void (* MspInitCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Msp Init callback */
void (* MspDeInitCallback)(struct __CAN_HandleTypeDef *hcan); /*!< CAN Msp DeInit callback */
#endif /* (USE_HAL_CAN_REGISTER_CALLBACKS) */
} CAN_HandleTypeDef;
typedef struct
{
//设置分频,和波特率有关
uint32_t Prescaler; /*!< Specifies the length of a time quantum.
This parameter must be a number between Min_Data = 1 and Max_Data = 1024. */
//设置模式
uint32_t Mode; /*!< Specifies the CAN operating mode.
This parameter can be a value of @ref CAN_operating_mode */
//设置起始跳变宽度,重新同步跳跃宽
uint32_t SyncJumpWidth; /*!< Specifies the maximum number of time quanta the CAN hardware
is allowed to lengthen or shorten a bit to perform resynchronization.
This parameter can be a value of @ref CAN_synchronisation_jump_width */
//时间段1
uint32_t TimeSeg1; /*!< Specifies the number of time quanta in Bit Segment 1.
This parameter can be a value of @ref CAN_time_quantum_in_bit_segment_1 */
//时间段2
uint32_t TimeSeg2; /*!< Specifies the number of time quanta in Bit Segment 2.
This parameter can be a value of @ref CAN_time_quantum_in_bit_segment_2 */
//时间触发通信模式
FunctionalState TimeTriggeredMode; /*!< Enable or disable the time triggered communication mode.
This parameter can be set to ENABLE or DISABLE. */
//自动离线管理
FunctionalState AutoBusOff; /*!< Enable or disable the automatic bus-off management.
This parameter can be set to ENABLE or DISABLE. */
//自动唤醒
FunctionalState AutoWakeUp; /*!< Enable or disable the automatic wake-up mode.
This parameter can be set to ENABLE or DISABLE. */
//报文自动重传
FunctionalState AutoRetransmission; /*!< Enable or disable the non-automatic retransmission mode.
This parameter can be set to ENABLE or DISABLE. */
//锁定FIIFO
FunctionalState ReceiveFifoLocked; /*!< Enable or disable the Receive FIFO Locked mode.
This parameter can be set to ENABLE or DISABLE. */
//发送FIFO优先级 DISABLE-优先级取决于报文标符 即ID
FunctionalState TransmitFifoPriority;/*!< Enable or disable the transmit FIFO priority.
This parameter can be set to ENABLE or DISABLE. */
} CAN_InitTypeDef;
2.CAN过滤器配置
typedef struct {
uint32_t FilterldHigh;
uint32_t FilterldLow;
uint32_t FilterMaskldHigh;
uint32_t FilterMaskldLow;
uint32_t FilterFIFOAssignment;
#if defined(STM32F105xC)ll defined(STM32F107xC)
uint32_t FilterNumber;
#else
uint32_t FilterNumber;
#endif /* STM32F105xC[ | STM32F107xC*/
uint32_t FilterMode;
uint32_t FilterScale;
uint32_t FilterActivation;
uint32_t BankNumber;
typedef struct
{
uint32_t FilterIdHigh; /*!< Specifies the filter identification number (MSBs for a 32-bit
configuration, first one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterIdLow; /*!< Specifies the filter identification number (LSBs for a 32-bit
configuration, second one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterMaskIdHigh; /*!< Specifies the filter mask number or identification number,
according to the mode (MSBs for a 32-bit configuration,
first one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterMaskIdLow; /*!< Specifies the filter mask number or identification number,
according to the mode (LSBs for a 32-bit configuration,
second one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
//设置FIFO和过滤器的关联关系; FIFO0 FIFO1
uint32_t FilterFIFOAssignment; /*!< Specifies the FIFO (0 or 1U) which will be assigned to the filter.
This parameter can be a value of @ref CAN_filter_FIFO */
//指定哪个过滤器 有14个
uint32_t FilterBank; /*!< Specifies the filter bank which will be initialized.
For single CAN instance(14 dedicated filter banks),
this parameter must be a number between Min_Data = 0 and Max_Data = 13.
For dual CAN instances(28 filter banks shared),
this parameter must be a number between Min_Data = 0 and Max_Data = 27. */
//过滤模式
uint32_t FilterMode; /*!< Specifies the filter mode to be initialized.
This parameter can be a value of @ref CAN_filter_mode */
//过滤的宽度 16位 或者32位
uint32_t FilterScale; /*!< Specifies the filter scale.
This parameter can be a value of @ref CAN_filter_scale */
//使能过滤器
uint32_t FilterActivation; /*!< Enable or disable the filter.
This parameter can be a value of @ref CAN_filter_activation */
//双CAN使用的过滤器分配
uint32_t SlaveStartFilterBank; /*!< Select the start filter bank for the slave CAN instance.
For single CAN instances, this parameter is meaningless.
For dual CAN instances, all filter banks with lower index are assigned to master
CAN instance, whereas all filter banks with greater index are assigned to slave
CAN instance.
This parameter must be a number between Min_Data = 0 and Max_Data = 27. */
} CAN_FilterTypeDef;
第一至第四个是用来设置过滤器的 32位ID以及32位掩码ID,分别通过2个16位来组合。需要说明的是CAN_FilterMaskldHigh和 CAN_FilterMaskldLow,当在掩码模式时,拿CAN_FilterMaskldHigh来说,它们存储的是与CAN_FilterldHigh对应的掩码,与CAN_FilterldHigh和组成一组过滤器,CAN_FilterMaskldLow也类似。
https://blog.csdn.net/qq_35480173/article/details/98878309
3.发送及接收结构体
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
uint32_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 */
uint32_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 */
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
FunctionalState TransmitGlobalTime; /*!< Specifies whether the timestamp counter value captured on start
of frame transmission, is sent in DATA6 and DATA7 replacing pData[6] and pData[7].
@note: Time Triggered Communication Mode must be enabled.
@note: DLC must be programmed as 8 bytes, in order these 2 bytes are sent.
This parameter can be set to ENABLE or DISABLE. */
} CAN_TxHeaderTypeDef;
typedef struct
{
//存储的是报文的11位标准标识符。范围是0~Ox7FF;
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
//存储的是报文的29位标准扩展标识符,范围是0~Ox1FFFFFFF
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
//区别标准帧和扩展帧,表示标准帧时,使用Stdld 成员存储报文ID,表示扩展帧时。使用Exdld成员存储报文。
uint32_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 */
//存储的是报文类型标志RTR位,区分数据帧和遥控帧。
uint32_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 */
//设置数据帧数据长度,范围为0~8;
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
//在开始接受的时候先获得时间戳
uint32_t Timestamp; /*!< Specifies the timestamp counter value captured on start of frame reception.
@note: Time Triggered Communication Mode must be enabled.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFFFF. */
//过滤器编号/过滤器匹配序号
//筛选是否是改过滤器匹配到的数值
uint32_t FilterMatchIndex; /*!< Specifies the index of matching acceptance filter element.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF. */
} CAN_RxHeaderTypeDef;
4.CAN-双机通信测试编程流程分析
1)先对CAN使用的两个引脚进行初始化,并使能接收中断;
2)CAN单元初始化,设置好波特率分频器,相关工作模式以及两个时间段;
3)过滤器初始化,选用过滤器组、位宽、要过滤的ID高位和低位;
4)设置CAN通信报文内容,也就是发送邮箱;
5)主机端的话,将发送邮箱内容发送一遍,然后开启接收中断,在中断中处理
接收数据。
https://www.freesion.com/article/9764388674/
main.c
#include "main.h"
#include "can.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
MX_GPIO_Init();
MX_CAN_Init();
MX_USART1_UART_Init();
while (1)
{
//test();
/* USER CODE END WHILE */
//sendMsg(); //发送
//printf("发送");
//HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
can.c
/**
******************************************************************************
* @file can.c
* @brief This file provides code for the configuration
* of the CAN instances.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "can.h"
/* USER CODE BEGIN 0 */
#include "stdio.h"
#include "usart.h"
CAN_FilterTypeDef CAN_FilterInitStructure;
CAN_TxHeaderTypeDef TxHeader;
CAN_RxHeaderTypeDef RxHeader;
uint8_t TxData[8] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
uint8_t RxData[8];
uint32_t TxMailbox;
void sendMsg()
{
HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox);
}
//设置发送和接受格式,接受格式好像是用来判断的,并不需要设置
void CAN_SetTxMsg(void)
{
TxHeader.StdId = 0x123; //标准ID
TxHeader.ExtId = 0x123; //扩展ID
TxHeader.RTR = CAN_RTR_DATA; //数据帧
TxHeader.IDE = CAN_ID_STD; //使用标准ID
TxHeader.DLC = 8; //字节长度为8
TxHeader.TransmitGlobalTime = DISABLE; //时间戳
RxHeader.StdId = 0x321;
RxHeader.ExtId = 0x321;
RxHeader.IDE = 0;
RxHeader.RTR = 0;
RxHeader.DLC = 8;
}
/* USER CODE END 0 */
CAN_HandleTypeDef hcan;
/* CAN init function */
void MX_CAN_Init(void)
{
hcan.Instance = CAN1;
hcan.Init.Prescaler = 4;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SyncJumpWidth = CAN_SJW_2TQ;
hcan.Init.TimeSeg1 = CAN_BS1_3TQ;
hcan.Init.TimeSeg2 = CAN_BS2_6TQ;
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = ENABLE;
hcan.Init.AutoWakeUp = ENABLE;
hcan.Init.AutoRetransmission = DISABLE;
hcan.Init.ReceiveFifoLocked = DISABLE;
hcan.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
Error_Handler();
}
CAN_FilterInitStructure.FilterActivation = ENABLE;//使能过滤器
CAN_FilterInitStructure.FilterBank = 1;//指定过滤器为1
CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK;//指定过滤器为标识符屏蔽位模式
CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT;//过滤器位宽为32位
CAN_FilterInitStructure.FilterFIFOAssignment = CAN_FILTER_FIFO0;//设定了指向过滤器的FIFO
CAN_FilterInitStructure.FilterIdHigh =0x0000 ;//要过滤的ID高位
CAN_FilterInitStructure.FilterIdLow = 0x0000;//要过滤的ID低位
CAN_FilterInitStructure.FilterMaskIdHigh = 0x0000;//过滤器屏蔽标识符的高16位值
CAN_FilterInitStructure.FilterMaskIdLow = 0x0000; //过滤器屏蔽标识符的低16位值
HAL_CAN_ConfigFilter(&hcan,&CAN_FilterInitStructure);
CAN_SetTxMsg();
/* Start the CAN peripheral */
//开启CAN
if (HAL_CAN_Start(&hcan) != HAL_OK)//这个函数和下面的函数是cubemx没有给出的,需要手动添加
{
/* Start Error */
Error_Handler();
}
//使能中断
/* Activate CAN RX notification */
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//选择fifo1中断
{
/* Notification Error */
Error_Handler();
}
}
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspInit 0 */
/* USER CODE END CAN1_MspInit 0 */
/* CAN1 clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**CAN GPIO Configuration
PB9 ------> CAN_TX
PB8 ------> CAN_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_AFIO_REMAP_CAN1_2();
/* CAN1 interrupt Init */
HAL_NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
/* USER CODE BEGIN CAN1_MspInit 1 */
/* USER CODE END CAN1_MspInit 1 */
}
}
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspDeInit 0 */
/* USER CODE END CAN1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_CAN1_CLK_DISABLE();
/**CAN GPIO Configuration
PB9 ------> CAN_TX
PB8 ------> CAN_RX
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9|GPIO_PIN_8);
/* CAN1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
/* USER CODE BEGIN CAN1_MspDeInit 1 */
/* USER CODE END CAN1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
//中断接收
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan1)
{
HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, RxData); //此处的fifo0也要注意
printf("接收到数据\n");
printf("RX the CAN data: %02X%02X%02X%02X%02X%02X%02X%02X\r\n",RxData[0],RxData[1],RxData[2],RxData[3],RxData[4],RxData[5],RxData[6],RxData[7]);
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/