can协议部分
- 逻辑信号和电平信号
先贴上CAN信号在物理信号线上的查分信号表示形式
显性电平: 电压差范围为1.5-2.5v。 对应的逻辑电平是0
隐性电平: 其他 对应的逻辑电平是1
为什么显性电平对应的逻辑电平值为0,而隐性电平对应的逻辑电平是1?
因为在总线上,需要令显性位具有“覆盖”隐性位的能力。
在线与逻辑关系下,0才具有这种能力(1·1·1·……·1·1·0 = 0)。
所以才会将0定义为dominant,而将1定义为recessive。例如总线上10个节点发隐性位而1个节点发显性位的时候,总线上呈现出的是显性位。
电气连接图如下:(显性电平时逻辑电平为0,隐性电平时逻辑电平为1.具备线与的能力)
- 报文协议
CAN的协议报文由上面的逻辑电平组成;(如1111101111011101111101111)
灰色为显性,白色为隐性,浅蓝色表示可以是显性也可以是隐性。
起始帧(SOF) : 起始的一个显性位
仲裁段(Identify SRR IDE RTR):
- canid填充位
- RTR 区分数据帧和远程帧 显性为数据帧,隐性为远程帧
- SRR 区分仲裁遥控帧的标准格式与扩展格式的优先级,比较标准格式的RTR位与扩展格式的SRR位,标准格式的RTR位恒为显性,扩展格式的SRR位恒为隐性。故前11位ID号相同时,标准数据帧优先级高于扩展数据帧。
- IDE 区分仲裁数据帧的标准格式与扩展格式的优先级,看IDE位,扩展格式的IDE位恒为隐性,标准格式的IDE位在控制段,恒为显性。故前11位ID号相同时,标准遥控帧优先级高于扩展遥控帧。
控制段(r1 r0 DLC):r1和r0保留,DLC控制数据段的长度。
数据段(Data): 数据段可包含0-8个字节的数据,从MSB(最高位)开始输出。遥控帧没有此段。
CRC段:由15个位的CRC序列和1个位的CRC界定符(用于分隔位)构成。CRC界定符恒为隐性。
ACK段: 由ACK槽(ACK Slot)和ACK界定符2个位构成。 发送端两个呈现隐性电平。
- 位时序设置
举一个具体的寄存器说明:
需要设置的值有四个:SJW TS2 TS1 BRP
计算方式参考:
SJW必须小于PBS1和PBS2的最小值。
这个寄存器中这些值使用的单位为tq。
brp :波特率分频器
tq=(brp)*tpclk1
baud rate=Fpclk1/((tbs1+tbs2+1)*brp)
=42M/((7+6+1)*3)
=1Mbps
上面对CAN协议相关进行说明,后面针对具体STM32F4XX的例子进行分析说明。
u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
u16 i=0;
if(tsjw==0||tbs2==0||tbs1==0||brp==0)return 1;
tsjw-=1; //Subtract 1 before setting //先减去1.再用于设置
tbs2-=1;
tbs1-=1;
brp-=1;
//使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能PORTB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化PB8 PB9
// //引脚复用映射配置
GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_CAN1); //GPIOB8复用为CAN1
GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_CAN1); //GPIOB9复用为CAN1
CAN1->MCR=0x0000; //Exit sleep mode (setting all bits to 0 at the same time) //退出睡眠模式(同时设置所有位为0)
CAN1->MCR|=1<<0; //Request CAN1 to enter initialization mode //请求CAN1进入初始化模式
while((CAN1->MSR&1<<0)==0)
{
i++;
if(i>100)return 2; //Failed to enter initialization mode //进入初始化模>式失败
}
//Non-time triggered communication mode
//非时间触发通信模式
CAN1->MCR|=0<<7;
//Software automatic offline management
//软件自动离线管理
CAN1->MCR|=0<<6;
//Sleep mode is awakened by software (clear CAN1- >;
//睡眠模式通过软件唤醒(清除CAN1->MCR的SLEEP位)
CAN1->MCR|=0<<5;
//Disallow automatic message transmission
//禁止报文自动传送
CAN1->MCR|=1<<4;
//Messages are not locked, the new overwrites the old
//报文不锁定,新的覆盖旧的
CAN1->MCR|=0<<3;
//The priority is determined by the message identifier
//优先级由报文标识符决定
CAN1->MCR|=0<<2;
//Clear the original Settings
//清除原来的设置
CAN1->BTR=0x00000000;
//Mode set to 0, normal mode;1. Loop mode;
//模式设置 0,普通模式;1,回环模式;
CAN1->BTR|=mode<<30;
//Resynchronization jump width (TSJW) is TSJW +1 time unit
//重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位
CAN1->BTR|=tsjw<<24;
//Tbs2= Tbs2 +1 time unit
//Tbs2=tbs2+1个时间单位
CAN1->BTR|=tbs2<<20;
//Tbs1= Tbs1 +1 time unit
//Tbs1=tbs1+1个时间单位
CAN1->BTR|=tbs1<<16;
//Frequency division coefficient (Fdiv) is brp +1, boulder rate: Fpclk1/((Tbs1+Tbs2+1)*Fdiv)
//分频系数(Fdiv)为brp+1,波特率:Fpclk1/((Tbs1+Tbs2+1)*Fdiv)
CAN1->BTR|=brp<<0;
//Request CAN1 to exit initialization mode
//请求CAN1退出初始化模式
CAN1->MCR&=~(1<<0);
while((CAN1->MSR&1<<0)==1)
{
i++;
if(i>0XFFF0)return 3; //Failed to exit initialization mode //退出初始化>模式失败
}
/*** Filter initialization || 过滤器初始化 ***/
//Filter groups work in initialization mode
//过滤器组工作在初始化模式
CAN1->FMR|=1<<0;
//Filter 0 is not active
//过滤器0不激活
CAN1->FA1R&=~(1<<0);
//The filter bit width is 32 bits
//过滤器位宽为32位
CAN1->FS1R|=1<<0;
//Filter 0 works in identifier masking bit mode
//过滤器0工作在标识符屏蔽位模式
CAN1->FM1R|=0<<0; //Filter 0 is associated with FIFO0
//过滤器0关联到FIFO0
CAN1->FFA1R|=0<<0;
//32 bit ID
//32位ID
CAN1->sFilterRegister[0].FR1=0X00000000;
//32-bit MASK
//32位MASK
CAN1->sFilterRegister[0].FR2=0X00000000;
//Activation filter 0
//激活过滤器0
CAN1->FA1R|=1<<0;
//Filter group enters normal mode
//过滤器组进入正常模式
CAN1->FMR&=0<<0;
#if CAN1_RX0_INT_ENABLE
//Enable to interrupt reception //使能中断接收
//FIFO0消息挂号中断允许
CAN1->IER|=1<<1;
//Configure CAN to receive interrupts
//配置CAN接收中断
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
//Preemption priority
//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;
//Son priority
//子优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
//IRQ channel enablement
//IRQ通道使能
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//Initializes the VIC register according to the specified parameters
//根据指定的参数初始化VIC寄存器
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
#endif
return 0;
}
上面为一段CAN的初始化代码,下面具体分析:
if(tsjw0||tbs20||tbs10||brp0)return 1;
tsjw-=1; //Subtract 1 before setting //先减去1.再用于设置
tbs2-=1;
tbs1-=1;
brp-=1;
因为寄存器中的每个变量都进行了加一,所以我们这里我们基于给定值做-1处理。
//使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能PORTB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
使能GPIO时钟及CAN外设时钟。GPIO由AHB总线管理,CAN控制器由APB总线管理。
//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化PB8 PB9
// //引脚复用映射配置
GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_CAN1); //GPIOB8复用为CAN1
GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_CAN1); //GPIOB9复用为CAN1
将CAN引脚设置为功能复用模式,类型为推挽输出,上拉电阻为100MHz,最后将引脚映射为can功能引脚。
CAN1->MCR=0x0000; //Exit sleep mode (setting all bits to 0 at the same time) //退出睡眠模式(同时设置所有位为0)
CAN1->MCR|=1<<0; //Request CAN1 to enter initialization mode //请求CAN1进
入初始化模式
while((CAN1->MSR&1<<0)==0)
{
i++;
if(i>100)return 2; //Failed to enter initialization mode //进入初始化模>式失败
}
//Non-time triggered communication mode
//非时间触发通信模式
CAN1->MCR|=0<<7;
//Software automatic offline management
//软件自动离线管理
CAN1->MCR|=0<<6;
//Sleep mode is awakened by software (clear CAN1- >;
//睡眠模式通过软件唤醒(清除CAN1->MCR的SLEEP位)
CAN1->MCR|=0<<5;
//Disallow automatic message transmission
//禁止报文自动传送
CAN1->MCR|=1<<4;
//Messages are not locked, the new overwrites the old
//报文不锁定,新的覆盖旧的
CAN1->MCR|=0<<3;
//The priority is determined by the message identifier
//优先级由报文标识符决定
CAN1->MCR|=0<<2;
上面是对MCR寄存器进行设置,MCR是CAN 主控制寄存器。
CAN1->MCR=0x0000; //Exit sleep mode (setting all bits to 0 at the same time) //退出睡眠模式(同时设置所有位为0)
CAN1->MCR|=1<<0; //Request CAN1 to enter initialization mode //请求CAN1进
入初始化模式
while((CAN1->MSR&1<<0)==0)
{
i++;
if(i>100)return 2; //Failed to enter initialization mode //进入初始化模>式失败
}
INRQ: 进入初始化模式,在初始化模式下对CAN的寄存器进行配置。并等待进入初始化模式完成。
SLEEP: 这里是退出休眠模式。所以置0
TXFP: 一般发送优先级都是消息标识符确定。所以置0
RFLM: 当接收FIFO满后,选择覆盖消息而不是丢弃。所以置0.
NART: 无论发送结果如何消息只会发送一次,不会一直发送直到发送成功。所以置1.
AWUM: 需要在SLEEP位置0后,就退出休眠模式。所以置0
ABOM: 当节点busoff后,等待128次11个隐性位后,置0可以让控制器先进入到初始化状态然后进入正常状态。所以置0.
TTCM: 不用使能CAN控制器内部的时间计数器(用于生成时间戳)。所以置0
//Clear the original Settings
//清除原来的设置
CAN1->BTR=0x00000000;
//Mode set to 0, normal mode;1. Loop mode;
//模式设置 0,普通模式;1,回环模式;
CAN1->BTR|=mode<<30;
//Resynchronization jump width (TSJW) is TSJW +1 time unit
//重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位
CAN1->BTR|=tsjw<<24;
//Tbs2= Tbs2 +1 time unit
//Tbs2=tbs2+1个时间单位
CAN1->BTR|=tbs2<<20;
//Tbs1= Tbs1 +1 time unit
//Tbs1=tbs1+1个时间单位
CAN1->BTR|=tbs1<<16;
//Frequency division coefficient (Fdiv) is brp +1, boulder rate: Fpclk1/((Tbs1+Tbs2+1)Fdiv)
//分频系数(Fdiv)为brp+1,波特率:Fpclk1/((Tbs1+Tbs2+1)Fdiv)
CAN1->BTR|=brp<<0;
这里就是设置波特率以及分频系数,在位时序中已经说明。
//请求CAN1退出初始化模式
CAN1->MCR&=~(1<<0);
while((CAN1->MSR&1<<0)==1)
{
i++;
if(i>0XFFF0)return 3; //Failed to exit initialization mode //退出初始化>模式失败
}
退出初始化模式进入到正常模式
/* Filter initialization || 过滤器初始化 ***/
//Filter groups work in initialization mode
//过滤器组工作在初始化模式
CAN1->FMR|=1<<0;
进入筛选器初始化模式后对筛选器进行设置。
//Filter 0 is not active
//过滤器0不激活
CAN1->FA1R&=~(1<<0);
一共有28个筛选器,选择激活哪个筛选器。
//The filter bit width is 32 bits
//过滤器位宽为32位
CAN1->FS1R|=1<<0;
设置筛选器位宽尺寸。
//Filter 0 works in identifier masking bit mode
//过滤器0工作在标识符屏蔽位模式
CAN1->FM1R|=0<<0;
可以选择筛选器是屏蔽模式还是列表模式,屏蔽模式表示屏蔽哪些CANID,列表模式表示哪些列表可以通过。一般选屏蔽模式。
//Filter 0 is associated with FIFO0
//过滤器0关联到FIFO0
CAN1->FFA1R|=0<<0;
关联到哪个FIFO,有FIFO0和FIFO1可以选择。
//32 bit ID
//32位ID
CAN1->sFilterRegister[0].FR1=0X00000000;
//32-bit MASK
//32位MASK
CAN1->sFilterRegister[0].FR2=0X00000000;
//Activation filter 0
设置过滤ID
//激活过滤器0
CAN1->FA1R|=1<<0;
激活过滤器0
//Filter group enters normal mode
//过滤器组进入正常模式
CAN1->FMR&=0<<0;
过滤器进入到正常状态。
//FIFO0消息挂号中断允许
CAN1->IER|=1<<1;
FIFO 开始时处于空状态,在接收的第一条有效消息存储在其中后,变为 Pending_1 状态。
硬件通过将 CAN_RFR 寄存器的 FMP[1:0] 位置为 01b 来指示该事件。消息将在 FIFO 输出
邮箱中供读取。软件将读取邮箱内容,并通过将 CAN_RFR 寄存器的 RFOM 位置 1,来将
邮箱释放。FIFO 随即恢复空状态。如果同时接收到新的有效消息,FIFO 将保持 Pending_1
状态,新消息将在输出邮箱中供读取。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/0b6d3a23af4e4d989a7a571230dffbaf.png#pic_center
允许有新消息挂起时产生中断。
//Configure CAN to receive interrupts
//配置CAN接收中断
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
//Preemption priority
//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;
//Son priority
//子优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
//IRQ channel enablement
//IRQ通道使能
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//Initializes the VIC register according to the specified parameters
//根据指定的参数初始化VIC寄存器
NVIC_Init(&NVIC_InitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
上面是设置CAN的中断通道,及中断抢占和响应优先级,最后使能中断。
接收部分主要代码:
void CAN1_Rx_Msg(u8 fifox,u32 *id,u8 *ide,u8 *rtr,u8 *len,u8 *dat)
{
*ide=CAN1->sFIFOMailBox[fifox].RIR&0x04; //Gets the value of the identifier selection bit //得
到标识符选择位的值
if(*ide==0) //Standard identifier //标准标识符
{
*id=CAN1->sFIFOMailBox[fifox].RIR>>21;
}else //Extended identifier //扩展标识符
{
*id=CAN1->sFIFOMailBox[fifox].RIR>>3;
}
*rtr=CAN1->sFIFOMailBox[fifox].RIR&0x02; //Gets the remote send request value //得到远>程发送请求值
*len=CAN1->sFIFOMailBox[fifox].RDTR&0x0F; //Get the DLC //得到DLC
//*fmi=(CAN1->sFIFOMailBox[FIFONumber].RDTR>>8)&0xFF; //Get the FMI //得到FMI
//Receive data //接收数据
dat[0]=CAN1->sFIFOMailBox[fifox].RDLR&0XFF;
dat[1]=(CAN1->sFIFOMailBox[fifox].RDLR>>8)&0XFF;
dat[2]=(CAN1->sFIFOMailBox[fifox].RDLR>>16)&0XFF;
dat[3]=(CAN1->sFIFOMailBox[fifox].RDLR>>24)&0XFF;
dat[4]=CAN1->sFIFOMailBox[fifox].RDHR&0XFF;
dat[5]=(CAN1->sFIFOMailBox[fifox].RDHR>>8)&0XFF;
dat[6]=(CAN1->sFIFOMailBox[fifox].RDHR>>16)&0XFF;
dat[7]=(CAN1->sFIFOMailBox[fifox].RDHR>>24)&0XFF;
if(fifox==0)CAN1->RF0R|=0X20; //Free the FIFO0 mailbox //释放FIFO0邮箱
else if(fifox==1)CAN1->RF1R|=0X20; //Free the FIFO1 mailbox //释放FIFO1邮箱
}
上面函数入参有FIFO号,上面设置了FIFO为FIFO0.其他的都是出参。
ide 用来区分是正常帧还是扩展帧。寄存器为RIR。
RDTR寄存器可以获取数据大小,大小为0-8.
RDLR寄存器可以获取实际的数据。
RF0R 寄存器可以释放FIFO,以供下次填充。
发送部分主要代码:
u8 CAN1_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
mbox=CAN1_Tx_Msg(0X601,0,0,len,msg);
while((CAN1_Tx_Staus(mbox)!=0X07)&&(i<0XFFF))i++; //Waiting for the end of sending //等待发送结束
if(i>=0XFFF)return 1; //Send failure //发送失败
return 0; //Send a success //发送成功
}
u8 CAN1_Tx_Msg(u32 id,u8 ide,u8 rtr,u8 len,u8 *dat)
{
u8 mbox;
if(CAN1->TSR&(1<<26))mbox=0; //Mailbox 0 is empty //邮箱0为空
else if(CAN1->TSR&(1<<27))mbox=1; //Mailbox 1 is empty //邮箱1为空
else if(CAN1->TSR&(1<<28))mbox=2; //Mailbox 2 is empty //邮箱2为空
else return 0XFF; //No empty mailbox, cannot send //无空邮箱,无法发送
CAN1->sTxMailBox[mbox].TIR=0; //Clear the previous Settings //清除之前的设置
if(ide==0) //The standard frame //标准帧
{
id&=0x7ff; //Take the low 11 bit STDID //取低11位stdid
id<<=21;
}else //Extend the frame //扩展帧
{
id&=0X1FFFFFFF; //Take a low 32-bit extid //取低32位extid
id<<=3;
}
CAN1->sTxMailBox[mbox].TIR|=id;
CAN1->sTxMailBox[mbox].TIR|=ide<<2;
CAN1->sTxMailBox[mbox].TIR|=rtr<<1;
len&=0X0F; //Get lower 4 bits //得到低四位
CAN1->sTxMailBox[mbox].TDTR&=~(0X0000000F);
CAN1->sTxMailBox[mbox].TDTR|=len; //Set the DLC //设置DLC
//The data to be sent is stored in the mailbox
//待发送数据存入邮箱
CAN1->sTxMailBox[mbox].TDHR=(((u32)dat[7]<<24)| ((u32)dat[6]<<16)| ((u32)dat[5]<<8)| ((u32)dat[4]));
CAN1->sTxMailBox[mbox].TDLR=(((u32)dat[3]<<24)| ((u32)dat[2]<<16)| ((u32)dat[1]<<8)| ((u32)dat[0]));
CAN1->sTxMailBox[mbox].TIR|=1<<0; //Request to send mailbox data//请求发送邮箱数据
return mbox;
}
mbox=CAN1_Tx_Msg(0X601,0,0,len,msg);
while((CAN1_Tx_Staus(mbox)!=0X07)&&(i<0XFFF))i++; //Waiting for the end of sending //等待发送结束
if(i>=0XFFF)return 1; //Send failure //发送失败
上面两行比较简单,发送,并且等待发送完成。
if(CAN1->TSR&(1<<26))mbox=0; //Mailbox 0 is empty //邮箱0为空
else if(CAN1->TSR&(1<<27))mbox=1; //Mailbox 1 is empty //邮箱1为空
else if(CAN1->TSR&(1<<28))mbox=2; //Mailbox 2 is empty //邮箱2为空
else return 0XFF; //No empty mailbox, cannot send //无空邮箱,无法发送
CAN1->sTxMailBox[mbox].TIR=0; //Clear the previous Settings //清除之前的设置
if(ide==0) //The standard frame //标准帧
{
id&=0x7ff; //Take the low 11 bit STDID //取低11位stdid
id<<=21;
}else //Extend the frame //扩展帧
{
id&=0X1FFFFFFF; //Take a low 32-bit extid //取低32位extid
id<<=3;
}
发送一共有三个邮箱,判断哪个邮箱有空,就选择哪个邮箱进行发送,根据ide判断是标准帧还是扩展帧。然后进行位填充。
CAN1->sTxMailBox[mbox].TIR|=id;
CAN1->sTxMailBox[mbox].TIR|=ide<<2;
CAN1->sTxMailBox[mbox].TIR|=rtr<<1;
len&=0X0F; //Get lower 4 bits //得到低四位
CAN1->sTxMailBox[mbox].TDTR&=~(0X0000000F);
CAN1->sTxMailBox[mbox].TDTR|=len; //Set the DLC //设置DLC
//The data to be sent is stored in the mailbox
//待发送数据存入邮箱
CAN1->sTxMailBox[mbox].TDHR=(((u32)dat[7]<<24)|
((u32)dat[6]<<16)|
((u32)dat[5]<<8)|
((u32)dat[4]));
CAN1->sTxMailBox[mbox].TDLR=(((u32)dat[3]<<24)|
((u32)dat[2]<<16)|
((u32)dat[1]<<8)|
((u32)dat[0]));
CAN1->sTxMailBox[mbox].TIR|=1<<0; //Request to send mailbox data//请求发送邮箱数据
填充仲裁段,控制端,数据段数据。最后请求发送邮箱中的数据。