目录
STM32F427芯片的CAN通讯解析及驱动代码
一、关于CAN通讯
CAN是控制器局域网络(Controller Area Network)的简称,它是由研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO11519以及ISO11898),是国际上应用最广泛的现场总线之一。 CAN总线由于在通讯上具有高可靠性以及良好的错误检测能力,目前逐渐成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线。
二、CAN通讯网络的拓扑结构
上图摘录自STM32F4系列的中文参考手册,从图中我们可以知道: 1、CAN网络的通讯节点由CAN控制器和CAN收发器组成。 2、CAN控制器是包含在MCU内部的,对于STM32F427来说就是我们配置的CAN外设。而我们通常使用的CAN芯片,例如TJA1050,这类芯片就是一种CAN总线收发器。 3、CAN控制器使用CAN_TX和CAN_RX两根信号线与CAN收发器相连。 4、CAN收发器使用CAN_HIGH和CAN_LOW两根线连接到CAN总线上。 5、CAN通讯网络不同于I2C,SPI等总线,它在通讯时没有使用时钟线,因此它属于异步通信。 6、CAN通讯网络中的节点个数理论上不受限制,只要总线的负载足够即可,并且可以通过中继器来增强负载。 这里需要特别注意的是: 与CAN总线相连的CAN_HIGH和CAN_LOW是一对差分线。也就是说CAN总线上的高低电平信号是通过差分线之间的电压差来表示的。之所以使用差分线来传输数据,主要是因为差分线的抗干扰能力强,同时能够有效抑制外部的电磁干扰。
三、CAN总线的电平
1、CAN总线上的电平有显性电平和隐性电平两种。其中显性电平对应着逻辑0,隐性电平对应逻辑1。 2、以高速CAN为例,在CAN协议中,表示逻辑0(显性电平)时,CAN_HIGH的电平为3.5V,CAN_LOW的电平为1.5V,它们之间的电压差为2V。而表示逻辑1(隐性电平)时,CAN_HIGH和CAN_LOW的电平均为2.5V,它们之间的电压差为0V。 3、总而言之,对于高速CAN来说,可以简单理解为: 当使用CAN总线发送逻辑0时,就让两根总线之间电压差为2V,而发送逻辑1时,就让两根总线的电压差为0即可。 4、从之前的分析我们知道,CAN总线网络上可以挂载很多节点。而在同一个时间点,假设一个节点输出显性电平,另外的一个节点输出隐性电平,由于CAN总线有“线与”的特性,因此,最后总线上的表现的电平是显性电平。即连接CAN总线的许多节点中,只要有一个节点使得总线电平为显性电平,那么总线就表现为显性电平。 5、由于CAN总线使用差分线之间的电压差表示逻辑信号,因此在一个时间点只能表示一个信号,因此CAN通讯是半双工的,数据的收发需要分时进行。在整个网络的同一时刻,只能有一个节点发送,其他的节点都只能接收。
四、CAN协议分析
CAN通讯没有时钟线,属于异步通信。因此节点之间进行通讯时会约定好波特率。同时也会采用“位同步”的方式来确保通讯的正常进行。
在CAN协议中,把每个数据位分为4段,分别为SS段,PTS段,PBS1段和PBS2段(每段的时间单位为Tq)。这四段的总长度为一个CAN总线数据位的长度。如下图表示为一个位数据的时序:
SS段: SS是同步段,若通讯节点检测到总线上信号的跳变沿被包含在SS段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。SS段的大小固定为1Tq。 PTS段: PTS是传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS段的大小可以为1~8Tq。 PBS1段: PBS1是相位缓冲段,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长。PBS1段的初始大小可以为1~8Tq。 PBS2段: PBS2是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。PBS2段的初始大小可以为2~8Tq。
CAN的数据同步功能分为硬同步和重新同步。 1、硬同步: 简单来讲,硬同步就是某个节点想要通过总线发送数据时,它会发送一个帧起始信号,表示通讯开始。而总线上的其他节点不发送数据时会时刻监测总线上的信号变化。当监测到总线起始信号不在内部时序的SS段内时,就把自己的SS段平移到帧起始信号的位置获得同步。这种同步一般在存在帧起始信号时才会起作用。 2、重新同步: 2.1 当传输数据的过程中,总线上的信号与节点本身的信号可能会有偏移。此时就需要使用重新同步了。重新同步是使用普通数据位从高到低的跳变沿来同步的。 2.2 它根据相位超前和相位滞后两种不同的情况采取不同的处理方式。 2.3 简单来说就是,当节点本身的信号的相位超前于总线上的信号相位时,控制器会在下一个位时序中的PBS1段增加对应的时间长度,使得节点与总线时序重新同步。而当节点本身的信号的相位滞后于总线上的信号相位时,控制器会在前一个位时序中的PBS2段减少对应的时间长度,从而获得同步。而PBS1和PBS2中增加或减少的这段时间长度被定义为 “重新同步补偿宽度SJW(reSynchronization Jump Width)。一般来说控制器会限制补偿宽度的最大值,因此每次同步时调整的时间宽度不能超过该值。若有需要,控制器会通过多次调整来进行同步。
五、CAN协议帧
CAN协议一共规定了5种类型的帧,它们分别为数据帧、遥控帧、错误帧、帧间隔和过载帧。如下图所示:
这里我们主要看数据帧。数据帧的结构如下图:
从上图可以知道,数据帧的组成为: 起始帧SOF + 仲裁字段 + Ctrl字段 + 数据字段 + CRC校验字段 + Ack应答字段 + 结束帧EOF 其中: 1、起始帧SOF是一个显性位(逻辑0)。主要用于通知总线上的各个节点即将有数据传输,同时可以使得各个节点实现硬同步。 2、标识符ID分为标准标识符和扩展标识符。标准标识符为11位,扩展标识符有29位。在CAN协议中,标识符ID决定着数据帧发送的优先级,也决定着其它节点是否会接收这个数据帧。即对于重要的信息,给它打包上一个优先级高的ID,使它能够及时地发送出去。也正因为它这样的优先级分配原则,使得CAN的扩展性大大加强,在总线上增加或减少节点并不影响其它设备。报文的优先级,是通过对ID的仲裁来确定的。根据前面的分析我们知道如果总线上同时出现显性电平和隐性电平,总线的状态会被置为显性电平,CAN正是利用这个特性进行仲裁的。若多个节点同时竞争CAN总线的占有权,当它们发送报文时,首先出现显性电平的将拥有对总线的占有权,其他的节点仲裁失败,由发送状态转向接收状态。 3、仲裁段由标识符ID + 远程传输请求位RTR组成,RTR位用于区分数据帧和遥控帧,当它为显性电平(逻辑0)时表示数据帧,隐性电平(逻辑1)时表示遥控帧。 4、IDE标识符扩展位,它用于区分标准格式与扩展格式,当它为显性电平时表示使用标准格式,隐性电平时表示使用扩展格式。 5、扩展格式中的SRR位,用于替代标准格式中的RTR位。由于扩展帧中的SRR位为隐性位,RTR在数据帧为显性位,所以在两个标识符ID相同的标准格式报文与扩展格式报文中,标准格式的优先级较高。 6、在控制段中的r1和r0为保留位,默认设置为显性位。 7、控制段中最主要的是DLC段(数据长度码),它由4个数据位组成,用于表示本报文中的数据段含有多少个字节,DLC 段表示的数字为0~8。 8、数据段为数据帧的核心内容,它是节点要发送的原始信息,由0~8个字节组成,MSB先行。 9、为了保证报文的正确传输,CAN的报文采用CRC校验码进行数据校验,一旦接收节点算出的CRC码跟接收到的CRC码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC部分的计算一般由CAN控制器硬件完成,出错时的处理则由软件控制最大重发数。 9、在CRC校验码之后,跟着一位CRC界定符,它为隐性位,主要作用是把CRC校验码与后面的ACK段间隔起来。 10、ACK段包括一个ACK槽位和ACK界定符位。在节点之间进行通讯时,在ACK槽位中,发送节点发送的是隐性位,而接收节点则在这一位中发送显性位以示应答。在ACK槽和帧结束之间由ACK界定符间隔开。 11、帧结束EOF段,帧结束段由发送节点发送的7个隐性位表示结束。
六、STM32F427内部的CAN控制器
如上图所示,为STM32F427的双CAN框图。由上图可知: 1、STM32F427有CAN1和CAN2两个CAN控制器。 2、CAN1和CAN2控制器各有3个发送邮箱和2个三级深度的接收FIFO。即最多可以缓存3个待发送的报文和6个接收到的报文。当接收到报文时,FIFO的报文计数器会自增,而STM32F427读取FIFO数据之后,报文计数器会自减,我们通过状态寄存器可获知报文计数器的值,通过前面主控制寄存器的RFLM位,可以设置锁定模式,锁定模式下FIFO溢出时会丢弃新报文,非锁定模式下FIFO溢出时新报文会覆盖旧报文。 3、CAN1和CAN2共用28个筛选器。 4、两个CAN控制器中,CAN1为主外设,CAN2为从外设。需要注意的是框图中的“存储访问控制器”是由CAN1控制的,CAN2 无法直接访问,所以使用CAN2的时候必须使能CAN1外设的时钟。 5、CAN外设的控制状态配置模块中包含了控制CAN外设工作的各种寄存器及状态寄存器。其中主要的寄存器为CAN主控制寄存器和位时序寄存器。
上图所示为CAN主控制寄存器的各个位。在后续编写CAN驱动时,需要设置该寄存器的对应位。 其中: 1、DBF调试冻结功能 该位可以设置CAN处于工作状态或禁止收发的状态,禁止收发时仍可访问接收FIFO中的数据。这两种状态是当STM32芯片处于程序调试模式时才使用的,平时使用并不影响。 2、TTCM时间触发模式 该位用于配置CAN的时间触发通信模式,在此模式下,CAN使用它内部定时器产生时间戳,并把它保存在CAN_RDTxR、CAN_TDTxR寄存器中。内部定时器在每个CAN位时间累加,在接收和发送的帧起始位被采样,并生成时间戳。 3、ABOM自动离线管理 该位用于设置是否使用自动离线管理功能。当节点检测到它发送错误或接收错误超过一定值时,会自动进入离线状态,在离线状态中,CAN不能接收或发送报文。处于离线状态的时候,可以软件控制恢复或者直接使用这个自动离线管理功能,它会在适当的时候自动恢复。 4、AWUM自动唤醒 CAN外设可以使用软件进入低功耗的睡眠模式,如果使能了这个自动唤醒功能,当CAN检测到总线活动的时候,会自动唤醒。 5、NART禁止自动重传 该位用于设置报文自动重传功能,该位为0时,当报文发送失败时会自动重传至成功为止。该位为1时,无论发送结果如何,消息只发送一次。 6、RFLM锁定模式 该位用于设置锁定模式,该功能用于锁定接收FIFO。锁定后,当接收FIFO溢出时,会丢弃下一个接收的报文。若不锁定,则下一个接收到的报文会覆盖原报文。 7、TXFP报文发送优先级的判定方法 该位用于设置报文发送优先级的判定方法,当CAN外设的发送邮箱中有多个待发送报文时,本功能可以控制它是根据报文的 ID优先级还是报文存进邮箱的顺序来发送。
上图所示为CAN位时序寄存器的各个位。在后续编写CAN驱动时,需要设置该寄存器的对应位。 其中: SILM:静默模式 0:正常工作 1:静默模式 LBKM:环回模式 0:禁止环回模式 1:使能环回模式 STM32F427的CAN外设有四种模式,分别为正常模式、静默模式、回环模式和回环静默模式。在正常进行CAN通讯时使用正常模式。各个模式的主要特点如下: 正常模式: 可以通过总线正常收发数据。 静默模式: 只可以向总线发送数据1,不能发送数据0,可以正常从总线上接收数据。 回环模式: 发送的数据直接到接收端,不能从总线接收数据。(总线上可以监测到发送的数据) 回环静默模式: 发送的数据直接到接收端,不能从总线接收数据。(总线上不能监测到发送的数据)
七、CAN通讯波特率计算
如上图为STM32F427的CAN外设位时序,可以看到它和标准的四段CAN时序有所不同。STM32F427的CAN外设位时序中分别是同步段SYNC_SEG、位段1 BS1及位段2 BS2,采样点位于BS1和BS2的交界处。其中SYNC_SEG段固定长度为1Tq,BS1和BS2段的长度可以在位时序寄存器CAN_BTR中进行设置,它们可以在重新同步期间增长或缩短,该长度SJW也可在位时序寄存器中配置。通过配置位时序寄存器CAN_BTR的TS1[3:0]和TS2[2:0]寄存器位设定BS1与BS2的长度后,我们就可以确定每个CAN数据位的时间了。
BS1段的时间 = Tq * (TS1[3:0] + 1) BS2段的时间 = Tq * (TS2[2:0] + 1) 因此一个数据位的时间为: T = 1Tq + BS1段的时间 + BS2段的时间 Tq时间片和STM32F427使用的CAN外设所挂载的总线频率及分频系数有关,我们知道,STM32F427的两个CAN外设CAN1和CAN2都是挂载在APB1总线上的,APB1总线频率 = 1/4 * SYSCLK = 1/4 * 180MHz = 45MHz。 通过以上的分析,我们就可以对CAN外设使用的波特率进行计算了: 假设,BS1段的时间为2Tq,BS2段的时间为3Tq,分频系数为15分频,因此,CAN外设的波特率为: Bandrate = 45MHZ / (15 * (1 + 2 + 3)) = 500Kbit/s。 CAN外设波特率的一般计算公式为: CAN外设波特率 = CAN外设时钟 / ((BS1 + BS2 + 1) * 分频系数)
八、CAN外设的筛选器
前面知道,CAN1和CAN2共用28个筛选器,在CAN协议中,发送节点将报文广播给所有接收节点时,接收节点会根据报文标识符的值来确定软件是否需要该消息,为了简化软件的工作,STM32的CAN外设接收报文前会先使用验收筛选器检查,只接收需要的报文到FIFO中。 STM32F427根据过滤方法不同有两种不同的筛选模式,标识符列表模式和掩码模式。 根据筛选的标识符ID的长度不同又分为32位标识符筛选和16位标识符筛选。 标识符列表模式 标识符列表模式简单来说就是给想要接收的标识符ID设置白名单,只有发送节点发送的报文中的ID与白名单上的ID匹配时才允许接收此报文。 掩码模式 掩码模式可以简单理解为只要发送节点发送的报文中的ID的某几位与自己想要接收的标识符ID相同,那么就允许接收此报文。掩码为1表示该位必须与标识符ID的对应位相同,掩码为0表示该位不必与标识符ID的对应位相同,可以为1也可以为0。 每组筛选器包含2个32位的寄存器,分别为CAN_FxR1和CAN_FxR2,它们用来存储要筛选的ID或掩码。 了解了筛选器的不同分类后就可以知道,按照筛选的ID长度不同及两种不同的筛选模式,可以组合为以下4种筛选方式: 32位的列表模式: CAN_FxR1和CAN_FxR2各保存一个ID,一共可以筛选2个ID。 32位的掩码模式: CAN_FxR1中保存要筛选的ID,CAN_FxR2中保存哪些位要与CAN_FxR1保存的ID保持一致,一共可以筛选很多个符合要求的ID。 16位的列表模式: CAN_FxR1和CAN_FxR2各可以保存两个ID,一共可以筛选4个ID。 16位的掩码模式: CAN_FxR1中高16位保存要筛选的ID,低16位保存哪些位要与CAN_FxR1高16位保存的ID保持一致, CAN_FxR2中高16位保存要筛选的ID,低16位保存哪些位要与CAN_FxR2高16位保存的ID保持一致, 一共可以筛选很多个符合要求的ID。
九、CAN外设驱动(标准库函数)
以STM32F427主控芯片的CAN2外设为例进行说明(这里选择以CAN2为例是由于CAN2配置和CAN1有所不同,在使用时需要特别注意),外部的CAN收发器使用SIT1051。 PB4引脚为CAN收发器SIT1051的模式选择脚,低电平为高速模式,高电平为静音模式。这里我们使用高速模式。 PB5: CAN2_TX PB6: CAN2_RX
CAN2的驱动示例代码如下:
void SIT1051_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);//使能CAN2时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化PB4
GPIO_ResetBits(GPIOB,GPIO_Pin_4);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化PB5,PB6
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_CAN2); //GPIOB5复用为CAN2
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_CAN2); //GPIOB6复用为CAN2
}
void SIT1051_Mode_Init(uint8_t tsjw,uint8_t tbs2,uint8_t tbs1,uint16_t psc,uint8_t mode)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//注意,当使用CAN2时,必须同时使能CAN1和CAN2的时钟,否则CAN2无法正常工作
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);//使能CAN2时钟
CAN_DeInit(CAN2);
CAN_StructInit(CAN_InitStructure);
//CAN控制器配置
CAN_InitStructure.CAN_TTCM = DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM = DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART = ENABLE; //禁止报文自动传送(0:自动重传 1:禁止自动重传)
CAN_InitStructure.CAN_RFLM = DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP = DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode = mode; //模式设置
//CAN波特率配置
CAN_InitStructure.CAN_SJW = tsjw; //重新同步跳跃宽度
CAN_InitStructure.CAN_BS1 = tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2 = tbs2; //Tbs2范围CAN_BS2_1tq ~CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler = psc; //分频系数
CAN_Init(CAN2, &CAN_InitStructure); //初始化CAN2
//CAN筛选器配置(使用标准标识符) 这部分可以根据实际情况进行配置
CAN_FilterInitStructure.CAN_FilterNumber = 14; //筛选器14
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //掩码模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //位宽32位
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; //要筛选ID的高16位
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; //要筛选ID的低16位
//对应的32位掩码,0表示对应位的掩码可以为0也可以为1,这里设置为0,表示所有的ID均会接收
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //激活筛选器
CAN_FilterInit(&CAN_FilterInitStructure);//筛选器初始化
CAN_ITConfig(CAN2,CAN_IT_FMP0,ENABLE);//FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = CAN2_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 主优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
注意: 1、当使用CAN2时,必须同时使能CAN1和CAN2的时钟,否则CAN2无法正常工作。 2、对于F427来说,CAN的波特率 = CAN外设时钟 / (psc * ((1+tbs1) + (1+tbs2) + 1))。 3、对于筛选器来说,我们知道F427的CAN1和CAN2共用28个筛选器,一般CAN1使用的筛选器序号为0-13,CAN2使用的筛选器序号为14-27。这里配置CAN2的筛选器序号为14。 4、前面我们知道,F427的CAN1和CAN2各有2个3级FIFO用于存储接收到的报文。因此在配置时需指定使用哪个FIFO,选定FIFO后,需使能对应的中断才能在中断服务函数中获取数据。其对应关系如下,CAN1也是一样。 FIFO0-->CAN_Filter_FIFO0-->CAN_IT_FMP0-->CAN2_RX0_IRQn-->CAN2_RX0_IRQHandler FIFO1-->CAN_Filter_FIFO1-->CAN_IT_FMP1-->CAN2_RX1_IRQn-->CAN2_RX1_IRQHandler
void SIT1051_Init(void)
{
SIT1051_GPIO_Init();
SIT1051_Mode_Init(CAN_SJW_1tq,CAN_BS2_3tq,CAN_BS1_2tq,15,CAN_Mode_Normal);
}
注意: 1、在主程序main函数中直接调用SIT1051_Init函数进行初始化即可。 2、CAN2外设挂载在APB1总线上,APB1总线的时钟为系统时钟的四分之一,即180MHz / 4 = 45MHz。 3、这里的CAN_BS2_3tq值为2,CAN_BS1_2tq值为1,分频系数为15分频。 4、波特率 = 45MHz / (15 * ((CAN_BS1_2tq +1) + (CAN_BS2_3tq + 1) + 1)) = 500Kbit/s。 5、CAN的模式有4中,正常通讯时使用CAN_Mode_Normal模式。
//定义CAN通讯时用于发送消息与接收消息的结构体变量
CanTxMsg TxMessage;
CanRxMsg RxMessage;
void CAN2_RX0_IRQHandler(void)
{
//这里是CAN2使用FIFO0时的接收中断服务函数,在这里面进行数据的接收与处理
if(CAN_GetITStatus(CAN2,CAN_IT_FMP0) != RESET)
{
CAN_Receive(CAN2,0,&RxMessage); //接收数据
}
CAN_ClearITPendingBit(CAN2,CAN_IT_FMP0);
//分析处理数据
}
关于使用CAN发送数据,需要先组装数据到发送消息结构体中,然后使用CAN_Transmit()函数进行发送。示例代码如下。
uint8_t CAN2_Send_Message(uint8_t id,uint8_t *msg,uint8_t len)
{
uint8_t mbox;
uint16_t i = 0;
TxMessage.IDE = CAN_ID_STD; //使用标准标识符
TxMessage.StdId = id; //发送消息的标识符id
TxMessage.RTR = CAN_RTR_Data; //消息类型为数据帧
//数据长度,这里len最大值为8,关于数据多于8个的,可以构造函数进行多次发送
TxMessage.DLC = len;
for(i=0; i<len; i++)
{
TxMessage.Data[i] = msg[i]; //装填消息
}
mbox = CAN_Transmit(CAN2,&TxMessage);
i = 0;
while((CAN_TransmitStatus(CAN2,mbox) == CAN_TxStatus_Failed) && (i < 0xFFF))
{
i++;
}
if(i > 0xFFF)
return 1;
return 0;
}
十、补充
参考博客链接:
https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522DA50B527-5D05-41FF-891F-A8A9057C30B5%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=DA50B527-5D05-41FF-891F-A8A9057C30B5&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-116206252-null-null.142^v100^pc_search_result_base8&utm_term=can&spm=1018.2226.3001.4187
完结。。。