CAN标准帧和扩展帧

1、CAN总线有两个ISO国际标准:
ISO11519和ISO11898。
1)、ISO11519定义低速CAN通信标准:通信速率为10~125Kbps,属于开环总线;
传输速率为40Kbps时,总线长度可达1000米;
低速CAN是一个“开环网络”,每根总线上个串联一个2.2KΩ的电阻。

2)、ISO11898定义高速CAN通信标准:通信速率为125Kbps~1 Mbps的,属于闭环总线;
传输速率可达1Mbps,总线长度≤40米;
高速CAN是一个“闭环网络”,总线的两端各需串联一个120Ω的电阻用于阻抗匹配。

高速CAN主要应用在发动机、变速箱等对实时性、传输速度要求高的场景。
低速CAN主要应用在车身控制系统等可靠性要求高的场景,低速CAN在断掉其任一导线后,仍可以继续接收数据。
因此在汽车发生交通事故时,使用低速CAN能更大提高设备正常接收数据工作的可能性,提高安全性。

2、CAN电平定义
1)、低速CAN电平:
当CAN_H和CAN_L电位差小于-0.3V,则表示隐性电平,对应逻辑1;
当CAN_H和CAN_L电位差大于0.3V,表示显性电平,对应逻辑0;
终端电阻范围R>2.09K,典型值为2.2K

2)高速CAN电平:
当CAN_H和CAN_L电位差小于0.05V,则表示隐性电平,对应逻辑1;
当CAN_H和CAN_L电位差大于1.5V,表示显性电平,对应逻辑0;
终端电阻范围85<R<130,典型值为120欧姆

3、了解标识符ID,RTR位和IDE位
1) 标识符ID
在标准帧中,0<=ID<0x7FF;
在扩展帧中,0<=ID<0x1FFFFFFF

2) RTR远程请求位
0,数据帧;1, 遥控帧;

数据帧:用于“发送设备”向“接收设备”传送数据的帧;RTR位,占1位,逻辑0表示该帧为数据帧
遥控帧:用于“接收设备”向具有相同ID的“发送设备”请求数据的帧;RTR位,占1位,逻辑1表示该帧为遥控帧

注意:发起方发送遥控帧后,接收方若收到与其ID相符的遥控帧,需要立即回复一个数据帧。
在CAN通讯中,A设备发送遥控帧给B设备,则B设备必须以“数据帧”回答,流程如下:
如果A设备需要B设备发送数据,则A设备可以用B设备的ID,发送一个“遥控帧”,
B设备收到A设备的“遥控帧”后,则B设备就发送“数据帧”给A设备
“遥控帧”就像一个命令,它会命令相应的设备返回一个“数据帧”。
主要用来请求某个指定设备的数据。由于CAN总线仲裁时,数据帧发送的优先级高于遥控帧,因此可避免CAN总线冲突。
因此,请求数据时,需要用发送遥控帧,回答时,需要发送数据帧。

3) SRR是扩展帧中的“替代远程请求位”
设置为1(隐性电平),了解一下就可以了;

4) IDE标识符选择位
0,标准帧;1,扩展帧;

4、帧
CAN总线以“帧”(Frame)的形式进行通信。CAN总线协议规定了5种帧,分别是数据帧、遥控帧、错误帧、超载帧以及帧间隔。
重点学习数据帧和遥控帧。


帧格式:


1)、帧起始:占1位,显性信号,对应逻辑0;

2)、仲裁段:包括标识符位(ID位)和远程发送请求位(RTR位,占1位);
标识符位:其中标准帧的ID位是11位,扩展帧的ID位是29位
远程发送请求位:RTR位,占1位,逻辑0表示该帧为数据帧,逻辑1表示该帧为遥控帧;它和“扩展帧的SRR位”正好对应,SRR恒为隐性位1;
因此,“标准数据帧”的优先级高于“数据遥控帧”和“扩展帧”;
我们发现:“扩展帧的IDE位”正好对应“标准帧的IDE位”,在扩展帧中IDE为恒为隐性1,在标准帧中,IDE恒为显性0,
因此,根据0优先的原则,“数据遥控帧”的优先级高于“扩展帧”。

3)、控制段
标准帧的控制段:由扩展标识符位(IDE,占1位)、保留位0(R0,占1位)、数据长度编码位(DLC,占4位)组成;
扩展帧的控制段:由两个保留位、数据长度编码位(DLC,占4位)组成;

4)、数据段
数据段里是发送数据的内容,最多8个字节,长度为"数据长度编码位"。
数据段可以包含0~8个字节的数据,从MSB(最高位)开始输出;

5)、CRC段
循环校验段包括循环校验序列(CRC位,占15位)和界定符(DEL位,占1位);
CRC段包含CRC校验序列和CRC界定符;
CRC用于校验传输是否正确;
界定符DEL用于表示循环校验序列是否结束;

6)、ACK段
ACK段包含ACK槽和ACK界定符两个位;
发送设备在ACK段发送两个隐性位,即发送方发出的报文中ACK槽为隐性1;与此同时,接收设备在接收到正确的报文之后,会在ACK槽发送显性位0,告诉发送设备接收结束。
所谓接收到正确的报文指的是接收到的报文没有填充错误、格式错误、CRC错误。

7)、帧结束
帧结束段表示该帧报文的结束,由7个隐性位1构成;

5、电平优先级
“显性位0”的优先级比“隐性位1”的优先级要高:
在某一个时刻,A设备向CAN总线发送了一个显性位0,B设备也向CAN总线发送了一个隐性位1,那么在该时刻,总线上的电平为显性0;

6、CAN总线逐位仲裁机制:
1)、ID号绞小的设备,会优先获取CAN总线的控制权;
在CAN总线空闲时,总线上为隐性电平,就在这个时候A设备和B设备同时向总线上发送数据,假定A设备发送的ID10~ID0是0x001,
B设备发送的ID10~ID0是0x002,由于ID1在CAN总线显示为0,所以B设备道自己在争夺CAN总线的仲裁中失败了,那么它就会主动地
转换为接收状态,不再发出信息,这样,A设备完全占据了CAN总线的控制权。

2)在ID号相同的情况下,“数据帧”的优先权高于“遥控帧”
在数据帧中,RTR位恒为显性位0,在遥控帧中,恒为隐性1。所以在ID号相同的情况下,一定是数据帧仲裁获胜。

3)在前11位ID号相同的情况下,“标准数据帧”的优先级高于“扩展帧”
在扩展帧(数据帧或遥控帧)中,SRR恒为隐性位1,并且可以发现,“扩展帧的SRR位”正好对应“标准帧的RTR位”,由于“标准数据帧的RTR位”为显性位0,所以“标准数据帧”的优先级高于“扩展帧”

4)在前11位ID号相同的情况下,“标准遥控帧”的优先级一定高于“扩展遥控帧”
在前11位相同时,“扩展帧的SRR位”正好对应“标准帧的RTR位”,“扩展帧的IDE位”正好对应“标准帧的IDE位”
在扩展数据帧或扩展遥控帧中,SRR恒为隐性位1;在“标准遥控帧”中RTR位也为隐性位1,相当于前12位是相同的。在扩展帧中IDE为恒为隐性1在标准帧中,IDE恒为显性0,“扩展帧的IDE位”正好对应“标准帧的IDE位”,由于显性0优先,因此,在前11位ID号相同的情况下,标准遥控帧”的优先级一定高于“扩展遥控帧”。 

由于“在ID号相同的情况下,“数据帧”的优先权高于“遥控帧”,因此,标准帧优于级高于扩展帧

当CAN总线网络中有多个CAN节点设备时,只有一个CAN设备发出数据帧,总线上所有设备(无过滤时)都获取该数据帧中仲裁段中的ID;
如果是自己关注ID的数据,则获取数据段的内容,完成数据的传输。

7、过滤器
STM32F103系列只有1个CAN控制器,STM32F105/STM32F107互联型有两个CAN控制器
STM32有2个3级深度的接收缓冲区:FIFO0和FIFO1,每个FIFO都可以存放3个完整的报文,它们完全由硬件来管理。
如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。
如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。

CAN1默认引脚是PA11和PA12,如果CAN1引脚用的是PB8和PB9,需要将引脚重新映射;
由于PA11和PA12也是USB的引脚,所以非互联型且带CAN控制器的微控制器的库文件在起名字时用了USB_LP_CAN1_RX0_IRQn;

根据CAN外设数量的不同,过滤器的数量如下:
只有CAN1:CAN1有14个过滤器,FIFO0和FIFO1共享这14个过滤器,过滤器编号范围:从0到13
有CAN1和CAN2:CAN1和CAN2共享28个过滤器
有CAN1,CAN2和CAN3:CAN1和CAN2共享28个过滤器,CAN3有独立的14个过滤器

8、CAN1.c程序

1)、在LOOP模式(环回模式)中测试时,需要将外部的CAN芯片拆除,将CPU的CAN_TX和CAN_RX短接。

2)、在运行模式中,需要两个带有CAN芯片的CPU,才可以完成测试,将CAN芯片的CANH和CANL对应连接。

注意:

#define CAN1_DEBUG     1
//CAN1总线调试:

//0=运行

//1=LOOP模式(环回模式)
//在LOOP模式中,CPU会将CAN_Rx和CAN_Tx引脚短路

#include "CAN1.h"
#include "string.h"
#include "stdio.h"unsigned int CAN1_ID=0x6f1;//CAN标识符ID
//unsigned char CAN1_addr = 0;
unsigned char  CAN1_Tx_Count =0;//发送报文计数器
unsigned char  CAN1_Rx_Count =0; //接收报文计数器
unsigned char  CAN1_TX_Complete_Flag =1;//若CAN1发送完成标志为0,则允许发送
unsigned char  CAN1_RX_Complete_Flag =0;//CAN1接收完成标志为1,则表示接收到新数据unsigned char   CAN1_TX_Buf[10]={'H','e','l','l','o','\0'};
unsigned char   CAN1_RX_Buf[10]={0};
static  u8 CAN1_msg_num[MAX_MAIL_NUM];
//每个FIFO都可以存放3个完整的报文,由硬件来管理,一个报文相当于一个邮箱
//CAN1_msg_num[]中的元素为1,表示该邮箱中的报文已经发送void CAN1_Interface_Enable(unsigned int baud_rate);int CAN1_Tx_Standard_Data_Frame(uint32_t id);
int CAN1_Tx_Standard_Remote_Frame(uint32_t id);
int CAN1_Tx_Answer_Standard_Remote_Frame(uint32_t id);int CAN1_Tx_Extended_Data_Frame(uint32_t id);
int CAN1_Tx_Extended_Remote_Frame(uint32_t id);
int CAN1_Tx_Answer_Extended_Remote_Frame(uint32_t id);//函数功能:将CAN1的CAN_RX映射到PA11,将CAN1的CAN_RX映射到PA12
void CAN1_GPIO_Config_PA11_PA12(void) 
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);   //设置CAN1的APB1外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟/* Configure CAN1 pin: RX */     // PA11GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11;            //选择PIN11,是CAN1的RXDGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	         //设置引脚为输入上拉	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);/* Configure CAN1 pin: TX */   // PA12GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_12;            //选择PIN12,是CAN1的TXDGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       //设置引脚为复用推挽输出	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);
}//函数功能:将CAN1的CAN_RX映射到B9,将CAN1的CAN_RX映射到PB9
void CAN1_GPIO_Config_PB8_PB9(void) 
{GPIO_InitTypeDef GPIO_InitStructure;/* CAN1  */RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//设置CAN1的APB1外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);/* Configure CAN1 pin: RX */     // PB8GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;          //选择PIN8,是CAN1的RXDGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;      //设置引脚为输入上拉GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure);/* Configure CAN1 pin: TX */   // PB9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;         //选择PIN9,是CAN1的TXDGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //设置引脚为复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure);//#define GPIO_Remap_CAN1    GPIO_Remap1_CAN1GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE); 
}//函数功能:CAN1的NVIC配置
//测试时间:2018年12月26日
void CAN1_NVIC_Cpnfig(void)
{NVIC_InitTypeDef NVIC_InitStructure;//NVIC_PriorityGroup_4设置NVIC中断分组4:表示抢占优先级为4位,取值为0~15,没有响应优先级,取值为0//NVIC_PriorityGroup_3设置NVIC中断分组3:表示抢占优先级为3位,取值为0~7,响应优先级只有1位,取值为0~1//NVIC_PriorityGroup_2设置NVIC中断分组3:表示抢占优先级为2位,取值为0~3,响应优先级只有2位,取值为0~3//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4/* Enable CAN1 RX0 interrupt IRQ channel */NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;//中断源选择CAN1接收中断USB_LP_CAN1_RX0_IRQn//如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。//如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;//抢占优先级6NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级0NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //IRQ通道使能NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX1_IRQn;//中断源选择CAN1接收中断CAN1_RX1_IRQn//如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。//如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;//抢占优先级6NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级0NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //IRQ通道使能NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;//中断源选择CAN1发送中断USB_HP_CAN1_TX_IRQnNVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;//抢占优先级7NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级0NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //IRQ通道使能NVIC_Init(&NVIC_InitStructure);
}/*
根据CAN外设数量的不同,过滤器的数量如下:
只有CAN1:CAN1有14个过滤器;
有CAN1和CAN2:CAN1和CAN2共享28个过滤器;
有CAN1,CAN2和CAN3:CAN1和CAN2共享28个过滤器,CAN3有独立的14个过滤器;
*/
//函数功能:CAN FIFO接收过滤器初始化
//0<=tmpCANFilterNumber<14
//若是标准帧,则0<=id<0x7FF;若是扩展帧,则0<=id<0x1FFFFFFF
//tmpFIFO_Number=0表示对FIFO0接收过滤器初始化,否则对FIFO1接收过滤器初始化
//当tmpFilterMode=0时,初始化过滤器后,FIFO0或FIFO1可以接收任何标识符数据
//CAN_FIFO_Filter_Init(0,0,1,3);  //使用FIFO0使用过滤器0接收标识符为1的标准帧
//CAN_FIFO_Filter_Init(0,1,2,3);  //使用FIFO0使用过滤器1接收标识符为2的标准帧
//CAN_FIFO_Filter_Init(0,2,3,3);  //使用FIFO0使用过滤器2接收标识符为3的标准帧
//CAN_FIFO_Filter_Init(0,3,4,3);  //使用FIFO0使用过滤器3接收标识符为4的标准帧
//CAN_FIFO_Filter_Init(0,4,5,3);  //使用FIFO0使用过滤器4接收标识符为5的标准帧
//CAN_FIFO_Filter_Init(0,5,6,3);  //使用FIFO0使用过滤器5接收标识符为6的标准帧
//CAN_FIFO_Filter_Init(0,6,7,3);  //使用FIFO0使用过滤器6接收标识符为7的标准帧
//CAN_FIFO_Filter_Init(0,7,8,3);  //使用FIFO0使用过滤器7接收标识符为8的标准帧
//CAN_FIFO_Filter_Init(0,8,9,3);  //使用FIFO0使用过滤器8接收标识符为9的标准帧
//CAN_FIFO_Filter_Init(0,9,10,3); //使用FIFO0使用过滤器9接收标识符为10的标准帧
//CAN_FIFO_Filter_Init(0,10,11,3);//使用FIFO0使用过滤器10接收标识符为11的标准帧
//CAN_FIFO_Filter_Init(0,11,12,3);//使用FIFO0使用过滤器11接收标识符为12的标准帧
//CAN_FIFO_Filter_Init(0,12,13,3);//使用FIFO0使用过滤器12接收标识符为13的标准帧
//CAN_FIFO_Filter_Init(0,13,14,3);//使用FIFO0使用过滤器13接收标识符为14的标准帧
void CAN_FIFO_Filter_Init(uint8_t tmpFIFO_Number,uint8_t tmpCANFilterNumber,uint32_t id,uint8_t tmpFilterMode)
{CAN_FilterInitTypeDef  CAN_FilterInitStructure;CAN_FilterInitStructure.CAN_FilterNumber=tmpCANFilterNumber;   //指定了待初始化的过滤器,号码为tmpCANFilterNumberCAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;  //指定了过滤器将被初始化到的模式为标识符屏蔽位模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //给出了过滤器位宽1个32位过滤器//CAN FIFO0不过滤,可以接收任何标识符数据/if(tmpFilterMode==0){CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;//用来设定过滤器标识符(2位位宽时为其高段位,16位位宽时为第一个)CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;;//用来设定过滤器标识符(32位位宽时为其低段位,16位位宽时为第二个)CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//用来设定过滤器屏蔽标识符或者过滤器标识符(32位位宽时为其高段位,16位位宽时为第一个)CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;//用来设定过滤器屏蔽标识符或者过滤器标识符(32位位宽时为其低段位,16位位宽时为第二个)//CAN_FilterMask屏蔽寄存器所有位都是0,对应标示符全为“不关心”,也就是接收到数据的ID(标示符)不用与CAN_Filter寄存器的任何一位进行匹配。}/对标准数据帧过滤:(只接收标准数据帧)/if(tmpFilterMode==1)//只接收标识符为id的标准数据帧{CAN_FilterInitStructure.CAN_FilterIdHigh=(((u32)id<<21)&0xffff0000)>>16;//将ID10移动至最高位bit31,然后保留高16位,再右移16位,则原来的ID10~ID0占据bit15~bit5,bit4~bit0为0CAN_FilterInitStructure.CAN_FilterIdLow=(((u32)id<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xffff;//将ID10移动至最高位bit31,修改RTR,IDE和R0后,再保留低16位,则bit15~bit0为0x0000//标识符选择位IDE位: bit2=0,表示标准帧//远程请求位RTR为:bit1为0,表示数据帧//保留位: bit0为0CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;}/对标准遥控帧过滤:(只接收标准遥控帧)/if(tmpFilterMode==2)//只接收标识符为id的标准遥控帧{CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)id<<21)&0xffff0000)>>16;//将ID10移动至最高位bit31,然后保留高16位,再右移16位,则原来的ID10~ID0占据bit15~bit5,bit4~bit0为0CAN_FilterInitStructure.CAN_FilterIdLow=(((u32)id<<21)|CAN_ID_STD|CAN_RTR_REMOTE)&0xffff;//将ID10移动至最高位bit31,修改RTR,IDE和R0后,再保留低16位,则bit15~bit0为0x0000//标识符选择位IDE位: bit2=0,表示标准帧//远程请求位RTR为:bit1为1,表示遥控帧//保留位: bit0为0CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;}/对标准帧进行过滤:(只接收标准帧)/if(tmpFilterMode==3)//只接收标识符为id的标准帧{CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)id<<21)&0xffff0000)>>16;//将ID10移动至最高位bit31,然后保留高16位,再右移16位,则原来的ID10~ID0占据bit15~bit5,bit4~bit0为0CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)id<<21)|CAN_ID_STD)&0xffff;//将ID10移动至最高位bit31,修改RTR,IDE和R0后,再保留低16位,则bit15~bit0为0x0000//标识符选择位IDE位: bit2=0,表示标准帧//远程请求位RTR为:bit1为0,表示数据帧//保留位: bit0为0CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFC;//由于“远程请求位RTR位于bit1”,保留位R0位于bit0,所以对于是“数据帧”还“扩展帧”,不作过滤}/对扩展数据帧进行过滤:(只接收扩展数据帧)/if(tmpFilterMode==4)//只接收标识符为id的扩展数据帧{CAN_FilterInitStructure.CAN_FilterIdHigh= (((u32)id<<3)&0xFFFF0000)>>16;//将ID28移动至bit31,然后保留高16位,再右移16位,则原来的ID28~ID13占据bit15~bit0,//要过滤的ID高位CAN_FilterInitStructure.CAN_FilterIdLow= ( ((u32)id<<3) | CAN_ID_EXT | CAN_RTR_DATA )&0xFFFF;//将ID28移动至最高位bit31,修改RTR,IDE和R0后,再保留低16位,则ID12~ID0占据bit15~bit3//标识符选择位IDE位: bit2=1,表示扩展帧//远程请求位RTR为:bit1为0,表示数据帧//保留位: bit0为0CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;//过滤器高16位每位必须匹配CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;//过滤器低16位每位必须匹配}/对扩展遥控帧过滤:(只接收扩展遥控帧)/if(tmpFilterMode==5)//只接收标识符为id的扩展遥控帧{CAN_FilterInitStructure.CAN_FilterIdHigh   = (((u32)id<<3)&0xFFFF0000)>>16;//将ID28移动至bit31,然后保留高16位,再右移16位,则原来的ID28~ID13占据bit15~bit0,//要过滤的ID高位CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)id<<3)|CAN_ID_EXT|CAN_RTR_REMOTE)&0xFFFF;//将ID28移动至最高位bit31,修改RTR,IDE和R0后,再保留低16位,则ID12~ID0占据bit15~bit3//标识符选择位IDE位: bit2=1,表示扩展帧//远程请求位RTR为:bit1为1,表示遥控帧//保留位: bit0为0CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;}//对扩展帧进行过滤:(扩展帧不会被过滤掉)/if(tmpFilterMode==6)//只接收标识符为id的扩展帧{CAN_FilterInitStructure.CAN_FilterIdHigh   =(((u32)id<<3)&0xFFFF0000)>>16;//将ID28移动至bit31,然后保留高16位,再右移16位,则原来的ID28~ID13占据bit15~bit0,//要过滤的ID高位CAN_FilterInitStructure.CAN_FilterIdLow  = (((u32)id<<3)|CAN_ID_EXT)&0xFFFF;//将ID28移动至最高位bit31,修改RTR,IDE和R0后,再保留低16位,则ID12~ID0占据bit15~bit3//标识符选择位IDE位: bit2=1,表示扩展帧//远程请求位RTR为:bit1为0,表示数据帧//保留位: bit0为0CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFC;//由于“远程请求位RTR位于bit1”,保留位R0位于bit0,所以对于是“数据帧”还“扩展帧”,不作过滤}if(tmpFIFO_Number==0) CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;//设定了指向过滤器的FIFO0else CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO1;//设定了指向过滤器的FIFO1CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//使能过滤器CAN_FilterInit(&CAN_FilterInitStructure);
}//函数功能:配置CAN1的工作模式
void CAN1_Mode_Config(unsigned char baudrate)
{CAN_InitTypeDef        CAN_InitStructure;/* CAN1 register init */CAN_DeInit(CAN1);CAN_StructInit(&CAN_InitStructure);/* CAN cell init */ //36MHz 500KbpsCAN_InitStructure.CAN_TTCM=DISABLE;//禁止时间触发通信模式CAN_InitStructure.CAN_ABOM=DISABLE;//软件对CAN_MCR寄存器的INRQ位进行置1随后清0后,一旦硬件检测//到128次11位连续的隐性位,就退出离线状态CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过清除CAN_MCR寄存器的SLEEP位,由软件唤醒CAN_InitStructure.CAN_NART=DISABLE;//CAN报文是否只发1次,不管发送的结果如何(成功/出错或仲裁丢失)CAN_InitStructure.CAN_RFLM=DISABLE;//在接收到溢出时FIFO未被锁定,当接收到FIFO报文未被读出,下一个收到的报文会覆盖原有的报文CAN_InitStructure.CAN_TXFP=DISABLE;//发送的FIFO优先级由报文的标识符来决定#if CAN1_DEBUGCAN_InitStructure.CAN_Mode= CAN_Mode_LoopBack;//设置CAN1的LOOP模式//在LOOP模式中,CPU会将CAN_Rx和CAN_Tx引脚短路//在测试LOOP模式时,需要将CPU外部的CAN芯片拆除,防止IO被持续拉高导致烧毁IO口
#elseCAN_InitStructure.CAN_Mode= CAN_Mode_Normal;//设置CAN1的工作模式
#endif//传输波特率if(baudrate ==  CAN1_BaudRate_250kbps)//CAN1总线波特率:250kbps{CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度1个时间单位CAN_InitStructure.CAN_BS1=CAN_BS1_12tq;//时间段1为9个时间单位CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;//时间段2为8个时间单位CAN_InitStructure.CAN_Prescaler= 9;//36M/(1+12+3)/9= 250kbps//36M/(1+5+2)/9 = 500kbps//36M(1+2+1)/9 = 1M}else if(baudrate == CAN1_BaudRate_500kbps)//CAN1总线波特率:500kbps{CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度1个时间单位CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;//时间段1为9个时间单位CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;//时间段2为8个时间单位CAN_InitStructure.CAN_Prescaler= 9;//36M/(1+5+2)/9 = 500kbps}else//CAN1总线波特率:1Mbps{CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度1个时间单位CAN_InitStructure.CAN_BS1=CAN_BS1_2tq;//时间段1为9个时间单位CAN_InitStructure.CAN_BS2=CAN_BS2_1tq;//时间段2为8个时间单位CAN_InitStructure.CAN_Prescaler= 9;//36M(1+2+1)/9 = 1M}CAN_Init(CAN1,&CAN_InitStructure);#if TestStandardExtendedFrame == 0 //标准数据帧CAN_FIFO_Filter_Init(0,0,1,3);  //使用FIFO0使用过滤器0接收标识符为1的标准帧CAN_FIFO_Filter_Init(0,1,2,3);  //使用FIFO0使用过滤器1接收标识符为2的标准帧CAN_FIFO_Filter_Init(0,2,3,3);  //使用FIFO0使用过滤器2接收标识符为3的标准帧CAN_FIFO_Filter_Init(0,3,4,3);  //使用FIFO0使用过滤器3接收标识符为4的标准帧CAN_FIFO_Filter_Init(0,4,5,3);  //使用FIFO0使用过滤器4接收标识符为5的标准帧CAN_FIFO_Filter_Init(0,5,6,3);  //使用FIFO0使用过滤器5接收标识符为6的标准帧CAN_FIFO_Filter_Init(0,6,7,3);  //使用FIFO0使用过滤器6接收标识符为7的标准帧CAN_FIFO_Filter_Init(0,7,8,3);  //使用FIFO0使用过滤器7接收标识符为8的标准帧CAN_FIFO_Filter_Init(0,8,9,3);  //使用FIFO0使用过滤器8接收标识符为9的标准帧CAN_FIFO_Filter_Init(0,9,10,3); //使用FIFO0使用过滤器9接收标识符为10的标准帧CAN_FIFO_Filter_Init(0,10,11,3);//使用FIFO0使用过滤器10接收标识符为11的标准帧CAN_FIFO_Filter_Init(0,11,12,3);//使用FIFO0使用过滤器11接收标识符为12的标准帧CAN_FIFO_Filter_Init(0,12,13,3);//使用FIFO0使用过滤器12接收标识符为13的标准帧CAN_FIFO_Filter_Init(0,13,14,3);//使用FIFO0使用过滤器13接收标识符为14的标准帧
#endif#if TestStandardExtendedFrame == 1 //标准遥控帧CAN_FIFO_Filter_Init(1,0,15,3); //使用FIFO1使用过滤器0接收标识符为15的标准帧CAN_FIFO_Filter_Init(1,1,16,3); //使用FIFO1使用过滤器1接收标识符为16的标准帧CAN_FIFO_Filter_Init(1,2,17,3); //使用FIFO1使用过滤器2接收标识符为17的标准帧CAN_FIFO_Filter_Init(1,3,18,3); //使用FIFO1使用过滤器3接收标识符为18的标准帧CAN_FIFO_Filter_Init(1,4,19,3); //使用FIFO1使用过滤器4接收标识符为19的标准帧CAN_FIFO_Filter_Init(1,5,20,3); //使用FIFO1使用过滤器5接收标识符为20的标准帧CAN_FIFO_Filter_Init(1,6,21,3); //使用FIFO1使用过滤器6接收标识符为21的标准帧CAN_FIFO_Filter_Init(1,7,22,3); //使用FIFO1使用过滤器7接收标识符为22的标准帧CAN_FIFO_Filter_Init(1,8,23,3); //使用FIFO1使用过滤器8接收标识符为23的标准帧CAN_FIFO_Filter_Init(1,9,24,3); //使用FIFO1使用过滤器9接收标识符为24的标准帧CAN_FIFO_Filter_Init(1,10,25,3);//使用FIFO1使用过滤器10接收标识符为25的标准帧CAN_FIFO_Filter_Init(1,11,26,3);//使用FIFO1使用过滤器11接收标识符为26的标准帧CAN_FIFO_Filter_Init(1,12,26,3);//使用FIFO1使用过滤器12接收标识符为27的标准帧CAN_FIFO_Filter_Init(1,13,27,3);//使用FIFO1使用过滤器13接收标识符为28的标准帧
#endif#if TestStandardExtendedFrame == 2 //扩展数据帧CAN_FIFO_Filter_Init(0,0,1,6);  //使用FIFO0使用过滤器0接收标识符为1的扩展帧CAN_FIFO_Filter_Init(0,1,2,6);  //使用FIFO0使用过滤器1接收标识符为2的扩展帧CAN_FIFO_Filter_Init(0,2,3,6);  //使用FIFO0使用过滤器2接收标识符为3的扩展帧CAN_FIFO_Filter_Init(0,3,4,6);  //使用FIFO0使用过滤器3接收标识符为4的扩展帧CAN_FIFO_Filter_Init(0,4,5,6);  //使用FIFO0使用过滤器4接收标识符为5的扩展帧CAN_FIFO_Filter_Init(0,5,6,6);  //使用FIFO0使用过滤器5接收标识符为6的扩展帧CAN_FIFO_Filter_Init(0,6,7,6);  //使用FIFO0使用过滤器6接收标识符为7的扩展帧CAN_FIFO_Filter_Init(0,7,8,6);  //使用FIFO0使用过滤器7接收标识符为8的扩展帧CAN_FIFO_Filter_Init(0,8,9,6);  //使用FIFO0使用过滤器8接收标识符为9的扩展帧CAN_FIFO_Filter_Init(0,9,10,6); //使用FIFO0使用过滤器9接收标识符为10的扩展帧CAN_FIFO_Filter_Init(0,10,11,6);//使用FIFO0使用过滤器10接收标识符为11的扩展帧CAN_FIFO_Filter_Init(0,11,12,6);//使用FIFO0使用过滤器11接收标识符为12的扩展帧CAN_FIFO_Filter_Init(0,12,13,6);//使用FIFO0使用过滤器12接收标识符为13的扩展帧CAN_FIFO_Filter_Init(0,13,14,6);//使用FIFO0使用过滤器13接收标识符为14的扩展帧
#endif#if TestStandardExtendedFrame == 3 //扩展遥控帧CAN_FIFO_Filter_Init(1,0,15,6); //使用FIFO1使用过滤器0接收标识符为15的扩展帧CAN_FIFO_Filter_Init(1,1,16,6); //使用FIFO1使用过滤器1接收标识符为16的扩展帧CAN_FIFO_Filter_Init(1,2,17,6); //使用FIFO1使用过滤器2接收标识符为17的扩展帧CAN_FIFO_Filter_Init(1,3,18,6); //使用FIFO1使用过滤器3接收标识符为18的扩展帧CAN_FIFO_Filter_Init(1,4,19,6); //使用FIFO1使用过滤器4接收标识符为19的扩展帧CAN_FIFO_Filter_Init(1,5,20,6); //使用FIFO1使用过滤器5接收标识符为20的扩展帧CAN_FIFO_Filter_Init(1,6,21,6); //使用FIFO1使用过滤器6接收标识符为21的扩展帧CAN_FIFO_Filter_Init(1,7,22,6); //使用FIFO1使用过滤器7接收标识符为22的扩展帧CAN_FIFO_Filter_Init(1,8,23,6); //使用FIFO1使用过滤器8接收标识符为23的扩展帧CAN_FIFO_Filter_Init(1,9,24,6); //使用FIFO1使用过滤器9接收标识符为24的扩展帧CAN_FIFO_Filter_Init(1,10,25,6);//使用FIFO1使用过滤器10接收标识符为25的扩展帧CAN_FIFO_Filter_Init(1,11,26,6);//使用FIFO1使用过滤器11接收标识符为26的扩展帧CAN_FIFO_Filter_Init(1,12,26,6);//使用FIFO1使用过滤器12接收标识符为27的扩展帧CAN_FIFO_Filter_Init(1,13,27,6);//使用FIFO1使用过滤器13接收标识符为28的扩展帧
#endif
}/*
BaudRate = 1 / NominalBitTime
NominalBitTime = 1tq + tBS1 + tBS2
tq = (BRP[9:0] + 1) x tPCLK
tPCLK = CAN's clock = APB1's clock
1Mbps 速率下,采用点的位置在6tq位置处,BS1=5, BS2=2
500kbps 速率下,采用点的位置在8tq位置处,BS1=7, BS2=3
250kbps 速率下,采用点的位置在14tq位置处,BS1=13, BS2=2
125k, 100k, 50k, 20k, 10k 的采用点位置与 250K 相同
*/
void CAN1_Interface_Enable(unsigned int baud_rate)
{//CAN1_GPIO_Config_PA11_PA12();//将CAN1的CAN_RX映射到PA11,将CAN1的CAN_RX映射到PA12CAN1_GPIO_Config_PB8_PB9(); //将CAN1的CAN_RX映射到B9,将CAN1的CAN_RX映射到PB9CAN1_NVIC_Cpnfig();         //CAN1的NVIC配置CAN1_Mode_Config(baud_rate);//配置CAN1的工作模式CAN_ITConfig(CAN1,CAN_IT_FMP0 | CAN_IT_FF0 | CAN_IT_FOV0, ENABLE);//fifo0中断//如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。//如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。CAN_ITConfig(CAN1,CAN_IT_FMP1 | CAN_IT_FF1 | CAN_IT_FOV1, ENABLE);//fifo1中断//如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。//如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。CAN_ITConfig(CAN1,CAN_IT_TME, DISABLE);//不使能CAN1发送中断CAN_ITConfig(CAN1,CAN_IT_EWG | CAN_IT_EPV | CAN_IT_BOF | CAN_IT_LEC | CAN_IT_ERR | CAN_IT_WKU | CAN_IT_SLK, ENABLE);//使能ERR中断// CAN1缓存初始化memset(CAN1_msg_num,0,MAX_MAIL_NUM);//将"发送邮箱标记"清零//每个FIFO都可以存放3个完整的报文   
}//启动CAN1发送“标准数据帧”:"Hi!"
int CAN1_Tx_Standard_Data_Frame(uint32_t id)
{CanTxMsg TxMessage;u8 TransmitMailbox = 0,i=0;设置“仲裁段”TxMessage.StdId=id;     //设置“标准标识符ID”为0x6f1TxMessage.RTR=CAN_RTR_DATA;//设置“远程发送请求”RTR位,指定该帧为“数据帧”;设置“控制段”
//标准帧的控制段:由扩展标识符位(IDE,占1位)、保留位0(R0,占1位)、数据长度编码位(DLC,占4位)组成TxMessage.IDE=CAN_ID_STD;//设置“CAN标识符类型”IDE位,指定该帧的CAN标识符类型为“标准帧”;TxMessage.DLC=7; //设置“数据长度编码位”DLC位,最大数据长度为8;设置“数据段”开始
/准备发送数据//CAN1_Tx_Count++;//发送报文计数器加1if(CAN1_Tx_Count > 99) CAN1_Tx_Count =1;sprintf( (char*)CAN1_TX_Buf,"%02u",CAN1_Tx_Count );CAN1_TX_Buf[2]=' ';CAN1_TX_Buf[3]='H';CAN1_TX_Buf[4]='i';CAN1_TX_Buf[5]='!';CAN1_TX_Buf[6]='\0';/装载发送数据//for(i=0;i < TxMessage.DLC;i++)//装载发送报文{TxMessage.Data[i] = CAN1_TX_Buf[i];}
设置“数据段”结束printf("Ready send: %s\r\n",CAN1_TX_Buf);TransmitMailbox = CAN_Transmit(CAN1,&TxMessage);//开始传送一条信息,并返回报文的号码if(CAN_NO_MB == TransmitMailbox){//发送失败,没有空邮箱return 0;}else{CAN1_TX_Complete_Flag=0;//准备发送CAN1_msg_num[TransmitMailbox] = 1;//将"发送邮箱标记"置1//每个FIFO都可以存放3个完整的报文 }CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE);//Transmit mailbox empty Interruptreturn 1;
}//启动CAN1发送“标准遥控帧”:"Hi!"
//发起方发送遥控帧后,接收方若收到与其ID相符的遥控帧,需要立即回复一个数据帧。
int CAN1_Tx_Standard_Remote_Frame(uint32_t id)
{CanTxMsg TxMessage;u8 TransmitMailbox = 0,i=0;设置“仲裁段”TxMessage.StdId=id;     //设置“标准标识符ID”为0x6f1TxMessage.RTR=CAN_RTR_REMOTE;//设置“远程发送请求”RTR位,指定该帧为“遥控帧”;设置“控制段”
//标准帧的控制段:由扩展标识符位(IDE,占1位)、保留位0(R0,占1位)、数据长度编码位(DLC,占4位)组成TxMessage.IDE=CAN_ID_STD;//设置“CAN标识符类型”IDE位,指定该帧的CAN标识符类型为“标准帧”;TxMessage.DLC=7; //设置“数据长度编码位”DLC位,最大数据长度为8;设置“数据段”开始
/准备发送数据//CAN1_Tx_Count++;//发送报文计数器加1if(CAN1_Tx_Count > 99) CAN1_Tx_Count =0;sprintf( (char*)CAN1_TX_Buf,"%02u",CAN1_Tx_Count );CAN1_TX_Buf[2]=' ';CAN1_TX_Buf[3]='H';CAN1_TX_Buf[4]='i';CAN1_TX_Buf[5]='!';CAN1_TX_Buf[6]='\0';/装载发送数据//for(i=0;i < TxMessage.DLC;i++)//装载发送报文{TxMessage.Data[i] = CAN1_TX_Buf[i];}
设置“数据段”结束printf("Ready send: %s\r\n",CAN1_TX_Buf);TransmitMailbox = CAN_Transmit(CAN1,&TxMessage);//开始传送一条信息,并返回报文的号码if(CAN_NO_MB == TransmitMailbox){//发送失败,没有空邮箱return 0;}else{CAN1_TX_Complete_Flag=0;//准备发送CAN1_msg_num[TransmitMailbox] = 1;//将"发送邮箱标记"置1//每个FIFO都可以存放3个完整的报文 }CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE);//Transmit mailbox empty Interruptreturn 1;
}//启动CAN1发送“标准数据帧”,以应答遥控帧:"OK!"
int CAN1_Tx_Answer_Standard_Remote_Frame(uint32_t id)
{CanTxMsg TxMessage;u8 TransmitMailbox = 0,i=0;设置“仲裁段”TxMessage.StdId=id;     //设置“标准标识符ID”为0x6f1TxMessage.RTR=CAN_RTR_DATA;//设置“远程发送请求”RTR位,指定该帧为“数据帧”;设置“控制段”
//标准帧的控制段:由扩展标识符位(IDE,占1位)、保留位0(R0,占1位)、数据长度编码位(DLC,占4位)组成TxMessage.IDE=CAN_ID_STD;//设置“CAN标识符类型”IDE位,指定该帧的CAN标识符类型为“标准帧”;TxMessage.DLC=7; //设置“数据长度编码位”DLC位,最大数据长度为8;设置“数据段”开始
/准备发送数据//sprintf( (char*)CAN1_TX_Buf,"%02u",CAN1_Tx_Count );CAN1_TX_Buf[2]=' ';CAN1_TX_Buf[3]='O';CAN1_TX_Buf[4]='k';CAN1_TX_Buf[5]='!';CAN1_TX_Buf[6]='\0';/装载发送数据//for(i=0;i < TxMessage.DLC;i++)//装载发送报文{TxMessage.Data[i] = CAN1_TX_Buf[i];}
设置“数据段”结束TransmitMailbox = CAN_Transmit(CAN1,&TxMessage);//开始传送一条信息,并返回报文的号码if(CAN_NO_MB == TransmitMailbox){//发送失败,没有空邮箱return 0;}else{CAN1_TX_Complete_Flag=0;//准备发送CAN1_msg_num[TransmitMailbox] = 1;//将"发送邮箱标记"置1//每个FIFO都可以存放3个完整的报文 }CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE);//Transmit mailbox empty Interruptreturn 1;
}//启动CAN1发送“扩展数据帧”:"Hi!"
int CAN1_Tx_Extended_Data_Frame(uint32_t id)
{CanTxMsg TxMessage;u8 TransmitMailbox = 0,i=0;设置“仲裁段”TxMessage.ExtId=id;      //设置扩展标示符(29位)TxMessage.IDE=CAN_ID_EXT; //设置扩展帧标识TxMessage.RTR=CAN_RTR_DATA;//设置“远程发送请求”RTR位,指定该帧为“数据帧”;设置“控制段”TxMessage.DLC=7; //设置“数据长度编码位”DLC位,最大数据长度为8;设置“数据段”开始
/准备发送数据//CAN1_Tx_Count++;//发送报文计数器加1if(CAN1_Tx_Count > 99) CAN1_Tx_Count =1;sprintf( (char*)CAN1_TX_Buf,"%02u",CAN1_Tx_Count );CAN1_TX_Buf[2]=' ';CAN1_TX_Buf[3]='H';CAN1_TX_Buf[4]='i';CAN1_TX_Buf[5]='!';CAN1_TX_Buf[6]='\0';/装载发送数据//for(i=0;i < TxMessage.DLC;i++)//装载发送报文{TxMessage.Data[i] = CAN1_TX_Buf[i];}
设置“数据段”结束printf("Ready send: %s\r\n",CAN1_TX_Buf);TransmitMailbox = CAN_Transmit(CAN1,&TxMessage);//开始传送一条信息,并返回报文的号码if(CAN_NO_MB == TransmitMailbox){//发送失败,没有空邮箱return 0;}else{CAN1_TX_Complete_Flag=0;//准备发送CAN1_msg_num[TransmitMailbox] = 1;//将"发送邮箱标记"置1//每个FIFO都可以存放3个完整的报文 }CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE);//Transmit mailbox empty Interruptreturn 1;
}//启动CAN1发送“扩展遥控帧”:"Hi!"
//发起方发送遥控帧后,接收方若收到与其ID相符的遥控帧,需要立即回复一个数据帧。
int CAN1_Tx_Extended_Remote_Frame(uint32_t id)
{CanTxMsg TxMessage;u8 TransmitMailbox = 0,i=0;设置“仲裁段”TxMessage.ExtId=id;      //设置扩展标示符(29位)TxMessage.IDE=CAN_ID_EXT; //设置扩展帧标识TxMessage.RTR=CAN_RTR_REMOTE;//设置“远程发送请求”RTR位,指定该帧为“遥控帧”;设置“控制段”TxMessage.DLC=7; //设置“数据长度编码位”DLC位,最大数据长度为8;设置“数据段”开始
/准备发送数据//CAN1_Tx_Count++;//发送报文计数器加1if(CAN1_Tx_Count > 99) CAN1_Tx_Count =1;sprintf( (char*)CAN1_TX_Buf,"%02u",CAN1_Tx_Count );CAN1_TX_Buf[2]=' ';CAN1_TX_Buf[3]='H';CAN1_TX_Buf[4]='i';CAN1_TX_Buf[5]='!';CAN1_TX_Buf[6]='\0';/装载发送数据//for(i=0;i < TxMessage.DLC;i++)//装载发送报文{TxMessage.Data[i] = CAN1_TX_Buf[i];}
设置“数据段”结束printf("Ready send: %s\r\n",CAN1_TX_Buf);TransmitMailbox = CAN_Transmit(CAN1,&TxMessage);//开始传送一条信息,并返回报文的号码if(CAN_NO_MB == TransmitMailbox){//发送失败,没有空邮箱return 0;}else{CAN1_TX_Complete_Flag=0;//准备发送CAN1_msg_num[TransmitMailbox] = 1;//将"发送邮箱标记"置1//每个FIFO都可以存放3个完整的报文 }CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE);//Transmit mailbox empty Interruptreturn 1;
}//启动CAN1发送“扩展数据帧”,以应答遥控帧:"OK!",应答“遥控帧”
int CAN1_Tx_Answer_Extended_Remote_Frame(uint32_t id)
{CanTxMsg TxMessage;u8 TransmitMailbox = 0,i=0;设置“仲裁段”TxMessage.ExtId=id;      //设置扩展标示符(29位)TxMessage.IDE=CAN_ID_EXT; //设置扩展帧标识TxMessage.RTR=CAN_RTR_DATA;//设置“远程发送请求”RTR位,指定该帧为“数据帧”;设置“控制段”TxMessage.DLC=7; //设置“数据长度编码位”DLC位,最大数据长度为8;设置“数据段”开始
/准备发送数据//sprintf( (char*)CAN1_TX_Buf,"%02u",CAN1_Tx_Count );CAN1_TX_Buf[2]=' ';CAN1_TX_Buf[3]='O';CAN1_TX_Buf[4]='k';CAN1_TX_Buf[5]='!';CAN1_TX_Buf[6]='\0';/装载发送数据//for(i=0;i < TxMessage.DLC;i++)//装载发送报文{TxMessage.Data[i] = CAN1_TX_Buf[i];}
设置“数据段”结束TransmitMailbox = CAN_Transmit(CAN1,&TxMessage);//开始传送一条信息,并返回报文的号码if(CAN_NO_MB == TransmitMailbox){//发送失败,没有空邮箱return 0;}else{CAN1_TX_Complete_Flag=0;//准备发送CAN1_msg_num[TransmitMailbox] = 1;//将"发送邮箱标记"置1//每个FIFO都可以存放3个完整的报文 }CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE);//Transmit mailbox empty Interruptreturn 1;
}//发送完中断函数
void CAN1_TX_Inerrupt_Func(void)
{if(CAN1_msg_num[0])//如果发送的是“报文0”{if(CAN_GetITStatus(CAN1,CAN_IT_RQCP0)){CAN_ClearITPendingBit(CAN1,CAN_IT_RQCP0);CAN_ITConfig(CAN1,CAN_IT_TME, DISABLE);CAN1_msg_num[0] = 0;//将"发送邮箱标记"清零//每个FIFO都可以存放3个完整的报文 }}if(CAN1_msg_num[1])//如果发送的是“报文1”{if(CAN_GetITStatus(CAN1,CAN_IT_RQCP1)){CAN_ClearITPendingBit(CAN1,CAN_IT_RQCP1);CAN_ITConfig(CAN1,CAN_IT_TME, DISABLE);CAN1_msg_num[1] = 0;}}if(CAN1_msg_num[2])//如果发送的是“报文2”{if(CAN_GetITStatus(CAN1,CAN_IT_RQCP2)){CAN_ClearITPendingBit(CAN1,CAN_IT_RQCP2);CAN_ITConfig(CAN1,CAN_IT_TME, DISABLE);CAN1_msg_num[2] = 0;}}CAN1_TX_Complete_Flag=1;//CAN发送完成标志,CAN1_TX_Complete_Flag=1;
}//解析CAN接收到的数据
void  Sub_CAN1_RX_Inerrupt_Func(CanRxMsg RxMessage)
{u8  i =0;CAN1_RX_Complete_Flag=0;if( (RxMessage.StdId<=28) && (RxMessage.IDE==CAN_ID_STD) )//接收标准帧{for(i=0;i < RxMessage.DLC;i++)//装载报文到CAN1_RX_Buf[]{CAN1_RX_Buf[i] =  RxMessage.Data[i];}CAN1_Rx_Count++;//报文计数器加1if(CAN1_Rx_Count > 99)CAN1_Rx_Count =0;CAN1_RX_Complete_Flag=1;//CAN1接收完成标志为1,则表示接收到新数据}if( (RxMessage.ExtId<=28) && (RxMessage.IDE==CAN_ID_EXT) )//接收扩展帧{for(i=0;i < RxMessage.DLC;i++)//装载报文到CAN1_RX_Buf[]{CAN1_RX_Buf[i] =  RxMessage.Data[i];}CAN1_Rx_Count++;//报文计数器加1if(CAN1_Rx_Count > 99)CAN1_Rx_Count =0;CAN1_RX_Complete_Flag=2;//CAN1接收完成标志为2,则表示接收到新数据}
}//接收中断函数
void CAN1_RX_Inerrupt_Func(unsigned char num)
{CanRxMsg RxMessage;switch(num){case 0: //接收FIFO0if(CAN_GetITStatus(CAN1,CAN_IT_FF0))//FIFO 0 full Interrupt{CAN_ClearITPendingBit(CAN1,CAN_IT_FF0);}else if(CAN_GetITStatus(CAN1,CAN_IT_FOV0))//FIFO 0 overrun Interrupt{CAN_ClearITPendingBit(CAN1,CAN_IT_FOV0);}else if(CAN_GetITStatus(CAN1,CAN_IT_FMP0))//FIFO 0 message pending Interrupt{CAN_Receive(CAN1,CAN_FIFO0, &RxMessage);//Receives a messageSub_CAN1_RX_Inerrupt_Func(RxMessage);//解析数据}break;case 1: //接收FIFO1if(CAN_GetITStatus(CAN1,CAN_IT_FF1))//FIFO 1 full Interrupt{CAN_ClearITPendingBit(CAN1,CAN_IT_FF1);}else if(CAN_GetITStatus(CAN1,CAN_IT_FOV1))//FIFO 1 overrun Interrupt{CAN_ClearITPendingBit(CAN1,CAN_IT_FOV1);}else if(CAN_GetITStatus(CAN1,CAN_IT_FMP1))//FIFO 1 message pending Interrupt{CAN_Receive(CAN1,CAN_FIFO1, &RxMessage);//解析数据Sub_CAN1_RX_Inerrupt_Func(RxMessage);}break;}
}//CAN1 RX0,关联FIFO0
//如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。
//如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。
void USB_LP_CAN1_RX0_IRQHandler(void)
{CAN1_RX_Inerrupt_Func(0);//接收FIFO0
}//CAN1 RX1,关联FIFO1
//如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。
//如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理。
void CAN1_RX1_IRQHandler(void)
{CAN1_RX_Inerrupt_Func(1);//接收FIFO1
}void USB_HP_CAN1_TX_IRQHandler(void)
{CAN1_TX_Inerrupt_Func();
}

9、CAN1.h程序

#ifndef __CAN1_H
#define	__CAN1_H#include "stm32f10x.h"//#define TestStandardExtendedFrame  0  //用来测试标准数据帧
//#define TestStandardExtendedFrame  1  //用来测试标准遥控帧
//#define TestStandardExtendedFrame  2  //用来测试扩展数据帧
#define TestStandardExtendedFrame  3  //用来测试扩展遥控帧#define CAN1_DEBUG     1
//CAN1总线调试:0=运行 1=LOOP模式(环回模式)
//在LOOP模式中,CPU会将CAN_Rx和CAN_Tx引脚短路#define MAX_MAIL_NUM   3  //每个FIFO都可以存放3个完整的报文
#define CAN1_BaudRate  0  //CAN1总线波特率:0=250kbps,1=500kbps,2=1Mbps#define CAN1_BaudRate_250kbps  0  //定义CAN1总线波特率:250kbps
#define CAN1_BaudRate_500kbps  1  //定义CAN1总线波特率:500kbps
#define CAN1_BaudRate_1Mbps    2  //定义CAN1总线波特率:1Mbpsextern unsigned char  CAN1_TX_Complete_Flag;
extern unsigned char  CAN1_Tx_Count;
extern unsigned char  CAN1_TX_Buf[10];extern unsigned char  CAN1_RX_Complete_Flag;
extern unsigned char  CAN1_Rx_Count;
extern unsigned char  CAN1_RX_Buf[10];extern void CAN1_Interface_Enable(unsigned int baud_rate);extern int CAN1_Tx_Standard_Data_Frame(uint32_t id);
extern int CAN1_Tx_Standard_Remote_Frame(uint32_t id);
extern int CAN1_Tx_Answer_Standard_Remote_Frame(uint32_t id);extern int CAN1_Tx_Extended_Data_Frame(uint32_t id);
extern int CAN1_Tx_Extended_Remote_Frame(uint32_t id);
extern int CAN1_Tx_Answer_Extended_Remote_Frame(uint32_t id);#endif /* __CAN1_H */

10、Air724UG_Task.c

#include "Air724UG_Task.h"
#include "string.h" //使能strcpy(),strlen(),memset()
#include "My_Task_Priority.h"
#include "Task_Variable.h"#include "CAN1.h"uint8_t GPRSTask_cnt;void Air724UG_Task(void *pvParameters);const char GPRS_And_ZigBee_rn_REG[]="\r\n";
const char Air724UG_Task_Initialise_REG[]="Air724UG Task Initialise";
const char GPRSTask_cnt_REG[]="GPRSTask_cnt=";void Air724UG_Task(void *pvParameters)
{u8 i;delay_init();      //延时函数初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4USART1_Serial_Interface_Enable(115200);SysRstSrcRecord();//系统复位记录INTX_ENABLE();//开启所有中断CAN1_Interface_Enable(CAN1_BaudRate_250kbps);GPRS串口初始化开始/printf("%s",GPRS_And_ZigBee_rn_REG);printf("%s",Air724UG_Task_Initialise_REG);GPRSTask_cnt=0;CAN1_TX_Complete_Flag =0;//若CAN1发送完成标志为0,则允许发送while(1){
#if TestStandardExtendedFrame == 0 //标准数据帧for(i=1;i<15;i++)//测试FIFO0使用过滤器接收数据{CAN1_Tx_Standard_Data_Frame(i);//启动发送标准数据帧,接收方ID为ivTaskDelay(100);if(CAN1_RX_Complete_Flag ==1 )//接收到“新数据”{printf("\r\nCAN1 TX: %s\r\n",CAN1_TX_Buf);printf("CAN1 RX: %s\r\n\r\n",CAN1_RX_Buf);CAN1_RX_Complete_Flag=0;//允许CAN1再次接收CAN1_TX_Complete_Flag=0;//允许CAN1再次发送}}
#endif#if TestStandardExtendedFrame == 1 //标准遥控帧for(i=15;i<28;i++)//测试FIFO0使用过滤器接收数据{CAN1_Tx_Standard_Remote_Frame(i);//启动发送“标准遥控帧”,接收方ID为ivTaskDelay(100);if(CAN1_TX_Complete_Flag) printf("\r\nCAN1 TX: %s\r\n",CAN1_TX_Buf);if(CAN1_RX_Complete_Flag ==1 )//在回环模式下,需要自己给自己发送数据帧以求应答{//若发送“标准遥控帧”正确,则以“标准数据帧”应答CAN1_Tx_Answer_Standard_Remote_Frame(i);}vTaskDelay(100);if(CAN1_RX_Complete_Flag ==1 )//接收到“新数据”{printf("CAN1 RX: %s\r\n\r\n",CAN1_RX_Buf);CAN1_RX_Complete_Flag=0;//允许CAN1再次接收CAN1_TX_Complete_Flag=0;//允许CAN1再次发送}}
#endif#if TestStandardExtendedFrame == 2 //扩展数据帧for(i=1;i<15;i++)//测试FIFO1使用过滤器接收数据{CAN1_Tx_Extended_Data_Frame(i);//启动发送扩展数据帧,接收方ID为ivTaskDelay(100);if(CAN1_RX_Complete_Flag ==2 )//接收到“新数据”{printf("\r\nCAN1 TX: %s\r\n",CAN1_TX_Buf);printf("CAN1 RX: %s\r\n\r\n",CAN1_RX_Buf);CAN1_RX_Complete_Flag=0;//允许CAN1再次接收CAN1_TX_Complete_Flag=0;//允许CAN1再次发送}}
#endif#if TestStandardExtendedFrame == 3 //扩展遥控帧for(i=15;i<28;i++)//测试FIFO1使用过滤器接收数据{CAN1_Tx_Extended_Remote_Frame(i);//启动发送“扩展遥控帧”,接收方ID为ivTaskDelay(100);if(CAN1_TX_Complete_Flag ==1 ) printf("\r\nCAN1 TX: %s\r\n",CAN1_TX_Buf);if(CAN1_RX_Complete_Flag ==2 ) //在回环模式下,需要自己给自己发送数据帧以求应答{//若发送“扩展遥控帧”正确,则以“扩展数据帧”应答CAN1_Tx_Answer_Extended_Remote_Frame(i);}vTaskDelay(100);if(CAN1_RX_Complete_Flag ==2 )//接收到“新数据”{printf("CAN1 RX: %s\r\n\r\n",CAN1_RX_Buf);CAN1_RX_Complete_Flag=0;//允许CAN1再次接收CAN1_TX_Complete_Flag=0;//允许CAN1再次发送}}
#endifGPRSTask_cnt++;if(GPRSTask_SecondFlag){
//			DSP_HeapSize();/每秒种任务执行多少次///printf("%s",GPRS_And_ZigBee_rn_REG);printf("%s",GPRSTask_cnt_REG);printf("%u",GPRSTask_cnt);printf("%s",GPRS_And_ZigBee_rn_REG);GPRSTask_cnt=0;GPRSTask_SecondFlag=FALSE;}vTaskDelay(1500);IWDG_ReloadCounter();  //喂狗}
}

CAN通讯终于调试好了。在网上,搜索了很多资料,都是东一句,西一句,七拼八凑的比较多。
好歹可以测试标准帧和扩展帧,以及里面的数据帧和遥控帧。至于其它错误帧,没有找到资料,估计很少用到吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1410784.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 麒麟系统安装

国产麒麟系统官网地址&#xff1a; https://www.openkylin.top/downloads/ 下载该镜像后&#xff0c;使用VMware部署一个虚拟机&#xff1a; 完成虚拟机创建。点击&#xff1a;“开启此虚拟机” 选择“试用试用开放麒麟而不安装&#xff08;T&#xff09;”&#xff0c;进入op…

同步互斥问题模型

目录 一. 生产者-消费者问题二. 多生产者-多消费者问题三. 吸烟者问题四. 读者-写者问题五. 哲学家进餐问题 \quad 一. 生产者-消费者问题 \quad 问题分析 \quad 如何实现 \quad \quad V操作不会导致进程阻塞&#xff0c;因此两个v操作顺序可以交换。 \quad 二. 多生产者-多消费…

[答疑]Eric Evans这样画是真不懂还是有特别考虑

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 albert 2024-5-2 10:14 您觉得Evans这样画是真不懂还是有特别的考虑&#xff1f; &#xff08;补注&#xff1a;指的是“《领域驱动设计》里的这个不变式是不是也是错的”中提到的图…

重看Spring聚焦BeanDefinition分析和构造

目录 一、对BeanDefinition的理解 &#xff08;一&#xff09;理解元信息 &#xff08;二&#xff09;BeanDefinition理解分析 二、BeanDefinition的结构设计分析 &#xff08;一&#xff09;整体结构体会 &#xff08;二&#xff09;重要接口和类分析 三、构造 BeanDef…

16、ESP32 Web

Web 服务器具有移动响应能力&#xff0c;可以使用本地网络上的任何设备作为浏览器进行访问。 示例功能&#xff1a; 构建 Web 服务器控制连接到 ESP32 的 LED在本地网络的浏览器上输入 ESP32 IP 地址访问 Web 服务器通过单击 Web 服务器上的按钮&#xff0c;更改 LED 状态 //…

triton之normalization教程

一 前向 在上式中,x是代表一个tensor import torchimport triton import triton.language as tltry:# This is https://github.com/NVIDIA/apex, NOT the apex on PyPi, so it# should not be added to extras_require in setup.py.import apexHAS_APEX = True except Module…

怎样通过Java语言实现远程控制8路控制器/断路器

怎样通过Java语言实现远程控制8路控制器/断路器呢&#xff1f; 本文描述了使用Java语言调用HTTP接口&#xff0c;实现控制8路控制器/断路器&#xff0c;支持8路输出&#xff0c;均可独立控制&#xff0c;可接入各种电器。 可选用产品&#xff1a;可根据实际场景需求&#xff0…

zabbix监控Tongweb7企业版(by lqw+sy)

此贴参考zabbix通过jmx监控Tongweb7企业版&#xff08;by lqw&#xff09;&#xff0c;是在此帖子的基础和同事整理的文档基础上重新部署验证的优化版&#xff0c;使用的是centos7。 优点&#xff1a; 1.不需要通过jmx配置进行监控。&#xff08;jmx配置需要修改tongweb的配置…

前端基础学习html(1)

1.标题标签.h1,h2...h6 2.段落标签p 换行标签br 3.加粗strong(b) /倾斜em(i) /删除 del(s) /下划线ins(u) 4.盒子&#xff1a;div //一行一个 span//一行多个 5.img :src alt title width height border 图片src引用&#xff1a;相对路径 上级/同级/中级 绝对路径&#xff…

【CTF-Crypto】修复RSA证书入门汇总

证书修复 文章目录 证书修复基础知识Truncated 1Truncated 2Jumbled 基础知识 为什么要引入证书&#xff1f; 在正常题目中&#xff0c;大部分直接给出了数字&#xff0c;但是数字在现实世界中传输不稳定&#xff0c;容易在某处出现错误&#xff0c;所以我们将所有的数字信息…

移动机器人系统与技术:自动驾驶、移动机器人、旋翼无人机

这本书全面介绍了机器人车辆的技术。它介绍了道路上自动驾驶汽车所需的概念。此外&#xff0c;读者可以在六足机器人的构造、编程和控制方面获得宝贵的知识。 这本书还介绍了几种不同类型旋翼无人机的控制器和空气动力学。它包括各种旋翼推进飞行器在不同空气动力学环境下的模…

【c++leetcode】35. Search Insert Position

问题入口 二分搜索 时间复杂度O(logn) class Solution { public:int searchInsert(vector<int>& nums, int target) {int start 0;int end nums.size() - 1;while (start < end){int mid (start end) / 2;if (nums[mid] target){return mid;}else if(nums…

【C++】1.贪心算法:零钱兑换的奇妙之旅

欢迎来CILMY23的博客 本篇主题为 贪心算法&#xff1a;零钱兑换的奇妙之旅 个人主页&#xff1a;CILMY23-CSDN博客 个人专栏&#xff1a; Python | C | C语言 | 数据结构与算法 上一篇C博客&#xff1a;掌握C函数重载和引用开启代码优化的新篇章 感谢观看&#xff0c;支…

电机控制器电路板布局布线参考指导(七)电流检测模块布局布线

电机控制器电路板布局布线参考指导&#xff08;七&#xff09;电流检测模块布局布线 1.高侧电流检测2.低侧电流监测3.两相和三相电流检测4.关键元器件选型要求5.布局6.布线7.工具设置8.输入和输出滤波9.注意事项 很多电机驱动器产品系列包括内置了电流感测功能的器件&#xff0…

用keras识别狗狗

一、需求场景 从照片从识别出狗狗 from keras.applications.resnet50 import ResNet50 from keras.preprocessing import image from keras.applications.resnet50 import preprocess_input, decode_predictions import numpy as np# 加载预训练的ResNet50模型 model ResNet5…

Postgresql的安装教程dbever的连接pgAdmin4的连接

最近在学习Postgresql. 首先&#xff0c;我去官网上下载了Community DL Page12.18这个版本&#xff0c;低版本比较稳定而且文档比较多 https://www.cnblogs.com/xy-ouyang/p/12009503.html 接下来&#xff0c;我去上面的链接参考了连接。打开了postgresql的服务器之后&#x…

计算机毕业设计python在线交友系统django+vue

Flask 是一个轻量级的 Web 框架&#xff0c;使用 Python 语言编写&#xff0c;较其他同类型框架更为灵活、轻便且容易上手&#xff0c;小型团队在短时间内就可以完成功能丰富的中小型网站或 Web 服务的实现。 本在线交友系统管理员功能有个人中心&#xff0c;用户管理&#xff…

初识C++ · 类和对象(下)

目录 1 再谈构造函数 2 类中的隐式类型转换 3 Static成员 4 友元和内部类 5 匿名对象 6 编译器的一些优化 1 再谈构造函数 先看一段代码&#xff1a; class Date { public :Date(int year, int month, int day){_year year;_month month;_day day;} private:int _ye…

日志行为分析

1、恶意IP请求带有多个身份操作 可以看到上述日志中&#xff0c;某IP对登录了邮件并进行了相关操作&#xff0c;可以看到其登录了不同的账户&#xff0c;那么这个时候怎么判断其是正常的请求还是恶意请求呢&#xff1f; (1)、威胁情报&#xff0c;查找请求IP相关的威胁情报信息…

MySQL 8.4 版本(LTS) 发布,一睹为快

前言 Oracle 前几天发布了 MySQL 8.4 版本(LTS)&#xff0c; 该版本是创新版的第一个长期支持版本。详细规划&#xff0c;请移步 技术译文 | 一文了解 MySQL 全新版本模型 关于 MySQL 的版本发布规划 Oracle MySQL 官方开发团队推出的新版本将过渡到新的 MySQL 版本模型。MyS…