简介: CAN 是控制器局域网络(Controller Area Network, CAN)的简称, 是由以
研发和生产汽车电子产品著称的德国 BOSCH 公司开发的, 并最终成为国际标准
(ISO 11898) , 是国际上应用最广泛的现场总线之一。 在北美和西欧, CAN 总
线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线, 并且
拥有以 CAN 为底层协议专为大型货车和重工机械车辆设计的 J1939 协议。
特点及其优越性:
特性:
Can 总线有 2 根线, 是半双工通信(差分信号) , 异步(没有时钟线) 收发
数据需要既定的速率收发, 整个相同 LAN 按相同速率收发
隐形电平(1) : CAN_H = CAN_L
显性电平(0) : CAN_H - CAN_L ~=(约等于) |2.0|
单位: 以 Messages(报文) 为单位传输
报文由几种类型的帧组成: 数据帧, 远程帧, 错误帧, 过载帧, 间隔帧。
仲裁方式: 以消息内容 ID 为仲裁。 每个发送节点必须去采集, 如果发现采
集到的数据与自己发送的不同, 就 Loser, 就变成接收者。
ISO11898 及ISO11519 标准化:
数据帧:
1. 帧起始:1 个位的显性电平表示,标准帧和扩展帧相同
2. 仲裁段:表示数据优先级的段,标准帧和扩展帧格式在本段有所区
别
3. 控制段:控制段由 6 个位构成,表示数据段的字节数。标准帧和扩展帧的控
制段稍有不同
4. 数据段:该段可包含 0~8 个字节的数据。从MSB(最高位)开始输
出,标准帧和扩展帧此段相同。
5. CRC段:该段用于检查帧传输错误。由 15 个位的 CRC 顺序和 1
个位的 CRC 界定符(用于分隔的位)构成,标准帧和扩展帧此段也是相
同的。
6. ACK段:此段用来确认是否正常接收。由 ACK 槽(ACK Slot)和 ACK
界定符 2 个位构成。标准帧和扩展帧此段也是相同的。
7. 帧结束:它表示该帧的结束的段,由 7 个位的隐性位构成。标准帧
和扩展帧此帧是相同的。
CAN仲裁:
在总线空闲态,最先开始发送消息的单元获得发送权。当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。
位时序,波特率:
code:
//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为42M,如果设置CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_7tq,6,CAN_Mode_LoopBack);
//则波特率为:42M/((6+7+1)*6)=500Kbps
void CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
//使能相关时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
//引脚复用映射配置
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1); //GPIOA11复用为CAN1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1); //GPIOA12复用为CAN1
//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;
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(GPIOA, &GPIO_InitStructure);//初始化PA11,PA12
//CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; //使用报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode; //模式设置
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2;//Tbs2范围CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
//配置过滤器
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
}
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 CAN1_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; // 标准标识符为0
TxMessage.ExtId=0x12; // 设置扩展标示符(29位)
TxMessage.IDE=0; // 使用扩展标识符
TxMessage.RTR=0; // 消息类型为数据帧,一帧8位
TxMessage.DLC=len; // 发送两帧信息
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i]; // 第一帧信息
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束
if(i>=0XFFF)return 1;
return 0;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<RxMessage.DLC;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}
int main()
{
u8 i=0,j=0;
u8 key;
u8 mode=0;
u8 res;
u8 tbuf[8];
u8 rbuf[8];
SysTick_Init(168);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(115200);
KEY_Init();
CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal);//500Kbps波特率
while(1)
{
key=KEY_Scan(0);
if(key==KEY_UP_PRESS) //模式切换
{
mode=!mode;
CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);
if(mode==0)
{
printf("Normal Mode\r\n");
}
else
{
printf("LoopBack Mode\r\n");
}
}
if(key==KEY1_PRESS) //发送数据
{
for(j=0;j<8;j++)
{
tbuf[j]=j;
}
res=CAN1_Send_Msg(tbuf,8);
if(res)
{
printf("Send Failed!\r\n");
}
else
{
printf("发送数据:");
for(j=0;j<8;j++)
{
printf("%X ",tbuf[j]);
}
printf("\r\n");
}
}
res=CAN1_Receive_Msg(rbuf);
if(res)
{
printf("接收数据:");
for(j=0;j<8;j++)
{
printf("%X ",rbuf[j]);
}
printf("\r\n");
}
i++;
if(i%20==0)
{
LED1=!LED1;
}
delay_ms(10);
}
}