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