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号相同的情况下,“标准遥控帧”的优先级一定高于“扩展遥控帧”。

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

7、过滤器
STM32F103系列只有1个CAN控制器,STM32F105/STM32F107互联型有两个CAN控制器
STM32F103系列的CAN控制器,支持CAN 2.0A和CAN 2.0B Active版本协议。
CAN 2.0A只能处理"标准数据帧",不能处理"扩展帧";
CAN 2.0B Active可以处理"标准数据帧"和"扩展数据帧";
CAN 2.0B Passive只能处理"标准数据帧",但会忽略扩展帧的内容。
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 */     // PA11
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11;            //选择PIN11,是CAN1的RXD
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	         //设置引脚为输入上拉	  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);

	/* Configure CAN1 pin: TX */   // PA12
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_12;            //选择PIN12,是CAN1的TXD
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       //设置引脚为复用推挽输出	  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //设置引脚的最高工作速率为50MHz
  GPIO_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 */     // PB8
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;          //选择PIN8,是CAN1的RXD
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;      //设置引脚为输入上拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  /* Configure CAN1 pin: TX */   // PB9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;         //选择PIN9,是CAN1的TXD
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //设置引脚为复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  //#define GPIO_Remap_CAN1    GPIO_Remap1_CAN1
  GPIO_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;//抢占优先级6
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级0
	NVIC_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;//抢占优先级6
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;
	//中断源选择CAN1发送中断USB_HP_CAN1_TX_IRQn
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7;//抢占优先级7
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级0
	NVIC_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;   //指定了待初始化的过滤器,号码为tmpCANFilterNumber
	CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_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为0
		CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
		CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFC;
		//由于“远程请求位RTR位于bit1”,保留位R0位于bit0,所以对于是“数据帧”还“扩展帧”,不作过滤
	}

	if(tmpFIFO_Number==0) CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;//设定了指向过滤器的FIFO0
	else CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO1;//设定了指向过滤器的FIFO1

	CAN_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 500Kbps
	CAN_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_DEBUG
	CAN_InitStructure.CAN_Mode= CAN_Mode_LoopBack;
	//设置CAN1的LOOP模式
	//在LOOP模式中,CPU会将CAN_Rx和CAN_Tx引脚短路
	//在测试LOOP模式时,需要将CPU外部的CAN芯片拆除,防止IO被持续拉高导致烧毁IO口
#else
  CAN_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映射到PA12
	CAN1_GPIO_Config_PB8_PB9(); //将CAN1的CAN_RX映射到B9,将CAN1的CAN_RX映射到PB9
	CAN1_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”为0x6f1
	TxMessage.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++;//发送报文计数器加1
	if(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 Interrupt

	return 1;
}

//启动CAN1发送“标准遥控帧”:"Hi!"
//发起方发送遥控帧后,接收方若收到与其ID相符的遥控帧,需要立即回复一个数据帧。
int CAN1_Tx_Standard_Remote_Frame(uint32_t id)
{
	CanTxMsg TxMessage;
	u8 TransmitMailbox = 0,i=0;

设置“仲裁段”
	TxMessage.StdId=id;     //设置“标准标识符ID”为0x6f1
	TxMessage.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++;//发送报文计数器加1
	if(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 Interrupt

	return 1;
}

//启动CAN1发送“标准数据帧”,以应答遥控帧:"OK!"
int CAN1_Tx_Answer_Standard_Remote_Frame(uint32_t id)
{
	CanTxMsg TxMessage;
	u8 TransmitMailbox = 0,i=0;

设置“仲裁段”
	TxMessage.StdId=id;     //设置“标准标识符ID”为0x6f1
	TxMessage.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 Interrupt

	return 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++;//发送报文计数器加1
	if(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 Interrupt

	return 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++;//发送报文计数器加1
	if(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 Interrupt

	return 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 Interrupt

	return 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++;//报文计数器加1
		if(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++;//报文计数器加1
		if(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: //接收FIFO0
			if(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 message
				Sub_CAN1_RX_Inerrupt_Func(RxMessage);//解析数据
			}
			break;
		case 1: //接收FIFO1
			if(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总线波特率:1Mbps

extern 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);//设置系统中断优先级分组4
	USART1_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为i
		  vTaskDelay(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为i
		  vTaskDelay(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为i
		  vTaskDelay(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为i
		  vTaskDelay(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再次发送
		  }
	  }
#endif

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

相关推荐

  1. CAN】知识点:CAN故障与错误详解

    2024-05-03 02:58:05       29 阅读
  2. CAN,SPI,IIC,USART每的组成

    2024-05-03 02:58:05       57 阅读
  3. MAC<span style='color:red;'>帧</span>

    MAC

    2024-05-03 02:58:05      27 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-03 02:58:05       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-03 02:58:05       101 阅读
  3. 在Django里面运行非项目文件

    2024-05-03 02:58:05       82 阅读
  4. Python语言-面向对象

    2024-05-03 02:58:05       91 阅读

热门阅读

  1. C++容器库概览

    2024-05-03 02:58:05       34 阅读
  2. 论文辅助笔记:TEMPO之feature.py

    2024-05-03 02:58:05       29 阅读
  3. 智能数据分析平台待修复BUG以及待完成需求

    2024-05-03 02:58:05       41 阅读
  4. WPS文字页面横向

    2024-05-03 02:58:05       33 阅读
  5. 6、FreeCAD的设计

    2024-05-03 02:58:05       32 阅读
  6. MySQL-笔记-09.存储过程及触发器的使用

    2024-05-03 02:58:05       33 阅读
  7. fastjson组件的使用

    2024-05-03 02:58:05       29 阅读
  8. python 如何判断是函数还是方法 (function or method)

    2024-05-03 02:58:05       29 阅读
  9. windows版本的epoll

    2024-05-03 02:58:05       30 阅读
  10. 全面解析Unity至Unreal的项目迁移流程

    2024-05-03 02:58:05       34 阅读
  11. 常用的路径抽稀算法

    2024-05-03 02:58:05       32 阅读
  12. npm一篇通

    2024-05-03 02:58:05       36 阅读