can通信
can 是controller area network 的缩写(以下称为can),该通信使用的是iso11898标准,该标准的物理层特征如下图所示。
can协议是通过以下5种类型的帧进行的:
数据帧
摇控帧
错误帧
过载帧
帧间隔
另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(id),扩展格式有29 个位的id。
大部分系统使用的都是数据帧 ,我这里使用的也是数据帧。
数据帧一般由7个段构成,即:
(1) 帧起始。表示数据帧开始的段。
(2) 仲裁段。表示该帧优先级的段。
(3) 控制段。表示数据的字节数及保留位的段。
(4) 数据段。数据的内容,一帧可发送0~8个字节的数据。
(5) crc段。检查帧的传输错误的段。
(6) ack段。表示确认正常接收的段。
(7) 帧结束。表示数据帧结束的段。
明确了数据帧概念,还需要理解一下过滤器的作用。
stm32的标识符屏蔽滤波目的是减少了cpu处理can通信的开销。stm32的过滤器组最多有28个(互联型),但是stm32f103zet6只有14个(增强型),每个滤波器组x由2个32为寄存器,can_fxr1和can_fxr2组成。
stm32每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:
1个32位过滤器,包括:stdid[10:0]、extid[17:0]、ide和rtr位
2个16位过滤器,包括:stdid[10:0]、ide、rtr和extid[17:15]位
此外过滤器可配置为,屏蔽位模式和标识符列表模式。
在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。相关文章:can总线详解。
一般也都是使用标识符列表模式,这里使用的也是标识符列表模式。滤波过程举例如下:
在程序中就是:
//要过滤的id高位 can_filterinitstructure.can_filteridhigh=0x00; //要过滤的id低位 can_filterinitstructure.can_filteridlow= (((u32)0x1314<<3)|can_id_ext|can_rtr_data)&0xffff; //过滤器屏蔽标识符的高16位值can_filterinitstructure.can_filtermaskidhigh=0xffff; //过滤器屏蔽标识符的低16位值 can_filterinitstructure.can_filtermaskidlow=0xffff;
这里的can_filterid和can_filtermaskid是配合使用的,意思是can_filterid指出需要屏蔽id的什么内容,什么格式;can_filtermaskid是指can_filterid的每一位是否需要过滤,若can_filtermaskid在某位上是1的话,id对应位上的数值就必须和can_filterid该位上的一样,保持一致,反之则是“不关心”。
上述程序的设置的含义就是:只接收来自0x1314的数据,屏蔽其他id的数据。
程序思路
这里准备做一个主机与从机的通信,主要用扩展标识符extid来区分,分配的标识符是: 主机:0x1314 从机:0x1311
主机负责接收所有从机的数据,不需要过滤,用扩展标识符extid来区分不同从机的数据;主机还可以向不同从机发送信息。而从机则只接收来自主机的数据,同样用扩展标识符extid来区分是否是发向自己的数据;同时,也能够向主机发送信息。
相关代码
代码也是非常简单的,这里贴出了主机和从机的can.c和can.h两个文件。
从机相关代码
can.c文件:
#include can.h/* 在中断处理函数中返回 *///__io uint32_t ret = 0;//接收数据缓冲器u8 rxbuf[5];u8 rx_flag=0;void can1_init(void){ gpio_inittypedef gpio_initstructure; nvic_inittypedef nvic_initstructure; can_inittypedef can_initstructure; can_filterinittypedef can_filterinitstructure; /* 复用功能和gpiob端口时钟使能*/ rcc_apb2periphclockcmd(rcc_apb2periph_afio | rcc_apb2periph_gpiob, enable); /* can1 模块时钟使能 */ rcc_apb1periphclockcmd(rcc_apb1periph_can1, enable); /* configure can pin: rx */ // pb8 gpio_initstructure.gpio_pin = gpio_pin_8; gpio_initstructure.gpio_mode = gpio_mode_ipu; //上拉输入 gpio_init(gpiob, &gpio_initstructure); /* configure can pin: tx */ // pb9 gpio_initstructure.gpio_pin = gpio_pin_9; gpio_initstructure.gpio_mode = gpio_mode_af_pp; //复用推挽输出 gpio_init(gpiob, &gpio_initstructure); //#define gpio_remap_can gpio_remap1_can1 本实验没有用到重映射i/o gpio_pinremapconfig(gpio_remap1_can1, enable); //can_nvic_configuration(); //can中断初始化 /* configure the nvic preemption priority bits */ nvic_prioritygroupconfig(nvic_prioritygroup_0); #ifdef vect_tab_ram /* set the vector table base location at 0x20000000 */ nvic_setvectortable(nvic_vecttab_ram, 0x0); #else /* vect_tab_flash */ /* set the vector table base location at 0x08000000 */ nvic_setvectortable(nvic_vecttab_flash, 0x0); #endif /* enabling interrupt */ nvic_initstructure.nvic_irqchannel=usb_lp_can1_rx0_irqn;; nvic_initstructure.nvic_irqchannelpreemptionpriority = 0; nvic_initstructure.nvic_irqchannelsubpriority = 0; nvic_initstructure.nvic_irqchannelcmd = enable; nvic_init(&nvic_initstructure); //can_init();//ca初始化n模块 /* can register init */ can_deinit(can1); //将外设can的全部寄存器重设为缺省值 can_structinit(&can_initstructure); //把can_initstruct中的每一个参数按缺省值填入 /* can cell init */ can_initstructure.can_ttcm=disable; //没有使能时间触发模式 can_initstructure.can_abom=disable; //没有使能自动离线管理 can_initstructure.can_awum=disable; //没有使能自动唤醒模式 can_initstructure.can_nart=disable; //没有使能非自动重传模式 can_initstructure.can_rflm=disable; //没有使能接收fifo锁定模式 can_initstructure.can_txfp=disable; //没有使能发送fifo优先级 can_initstructure.can_mode=can_mode_normal; //can设置为正常模式 can_initstructure.can_sjw=can_sjw_1tq; //重新同步跳跃宽度1个时间单位 can_initstructure.can_bs1=can_bs1_3tq; //时间段1为3个时间单位 can_initstructure.can_bs2=can_bs2_2tq; //时间段2为2个时间单位 can_initstructure.can_prescaler=60; //时间单位长度为60 can_init(can1,&can_initstructure); //波特率为:72m/2/60(1+3+2)=0.1 即波特率为100kbps // can filter init 过滤器,注意,只接收主机发过来的数据,屏蔽其他数据 can_filterinitstructure.can_filternumber=1; //指定过滤器为1 can_filterinitstructure.can_filtermode=can_filtermode_idmask; //指定过滤器为标识符屏蔽位模式 can_filterinitstructure.can_filterscale=can_filterscale_32bit; //过滤器位宽为32位 //can_filterinitstructure.can_filteridhigh= (((u32)0x131416; can_filterinitstructure.can_filteridhigh=0x00; //要过滤的id高位 can_filterinitstructure.can_filteridlow= (((u32)0x1314<<3)|can_id_ext|can_rtr_data)&0xffff; //要过滤的id低位 can_filterinitstructure.can_filtermaskidhigh=0xffff; //过滤器屏蔽标识符的高16位值 can_filterinitstructure.can_filtermaskidlow=0xffff; //过滤器屏蔽标识符的低16位值 can_filterinitstructure.can_filterfifoassignment=can_fifo0; //设定了指向过滤器的fifo为0 can_filterinitstructure.can_filteractivation=enable; //使能过滤器 can_filterinit(&can_filterinitstructure); //按上面的参数初始化过滤器 /* can fifo0 message pending interrupt enable */ can_itconfig(can1,can_it_fmp0, enable); //使能fifo0消息挂号中断}/* 发送两个字节的数据*/u8 can_setmsg(u8 data1,u8 data2){ u8 mbox; u16 i=0; cantxmsg txmessage; txmessage.stdid=0x0000; //标准标识符为0x00 txmessage.extid=0x1311; //扩展标识符0x1311,可以更改该标识符以示区分不同从机 txmessage.ide=can_id_ext; //使用扩展标识符 txmessage.rtr=can_rtr_data; //为数据帧 txmessage.dlc=2; //消息的数据长度为2个字节 txmessage.data[0]=data1; //第一个字节数据 txmessage.data[1]=data2; //第二个字节数据 //发送数据 mbox= can_transmit(can1, &txmessage); while((can_transmitstatus(can1, mbox)==can_txstatus_failed)&&(i=0xfff) return 0; return 1;}u8 can_getmsg(u8 *msg1,u8 *msg2){ if(rx_flag == 1)//发现数据 { *msg1=rxbuf[0]; *msg2=rxbuf[1]; rx_flag=0;//数据已经取走,可以更新数据 return 1; }else return 0;}/* usb中断和can接收中断服务程序,usb跟can公用i/o,这里只用到can的中断。 */void usb_lp_can1_rx0_irqhandler(void){ canrxmsg rxmessage; rxmessage.stdid=0x00; rxmessage.extid=0x00; rxmessage.ide=0; rxmessage.dlc=0; rxmessage.fmi=0; rxmessage.data[0]=0x00; rxmessage.data[1]=0x00; can_receive(can1,can_fifo0, &rxmessage); //接收fifo0中的数据 if(rx_flag == 0)//数据已取走或者缓冲器为空 { rxbuf[0]=rxmessage.data[0]; rxbuf[1]=rxmessage.data[1]; rx_flag=1;//数据已经备好,等待取走 }}
can.h文件
主机相关代码
这里主机代码大部分是和从机类似的,就只贴出不同的地方了。 can.c文件:
#include can.h/* 在中断处理函数中返回 *///__io uint32_t ret = 0;void can1_init(void){ ......//以上与从机部分相同 //can filter init 过滤器,已经设置为任意,可以通过extid标识符区分从机代号 can_filterinitstructure.can_filternumber=1; //指定过滤器为1 can_filterinitstructure.can_filtermode=can_filtermode_idmask; //指定过滤器为标识符屏蔽位模式 can_filterinitstructure.can_filterscale=can_filterscale_32bit; //过滤器位宽为32位 can_filterinitstructure.can_filteridhigh=0x0000; //过滤器标识符的高16位值 can_filterinitstructure.can_filteridlow=can_id_ext|can_rtr_data;//过滤器标识符的低16位值 can_filterinitstructure.can_filtermaskidhigh=0x0000; //过滤器屏蔽标识符的高16位值 can_filterinitstructure.can_filtermaskidlow=0x0000; //过滤器屏蔽标识符的低16位值 can_filterinitstructure.can_filterfifoassignment=can_fifo0; //设定了指向过滤器的fifo为0 can_filterinitstructure.can_filteractivation=enable; //使能过滤器 can_filterinit(&can_filterinitstructure); //按上面的参数初始化过滤器 /* can fifo0 message pending interrupt enable */ can_itconfig(can1,can_it_fmp0, enable); //使能fifo0消息挂号中断}//接收数据缓冲器u8 can_rx_buf[can_rx_len]={0}; //接收缓冲,最大usart_rec_len个字节.//接收标志位u8 rx_flag=0;/* usb中断和can接收中断服务程序,usb跟can公用i/o,这里只用到can的中断。 */void usb_lp_can1_rx0_irqhandler(void){ u8 i=0; canrxmsg rxmessage; rxmessage.stdid=0x00; rxmessage.extid=0x00; rxmessage.ide=0; rxmessage.dlc=0; rxmessage.fmi=0; can_receive(can1,can_fifo0, &rxmessage); //接收fifo0中的数据 if(rx_flag == 0)//数据已取走或者缓冲器为空 { if((rxmessage.dlc) == 2)//是否收到2位字节数据 { can_rx_buf[0]=rxmessage.data[0]; can_rx_buf[1]=rxmessage.data[1]; } }} /* 发送两个字节的数据*/u8 can_sendmsg(u8* data1, u8* data2){ u8 mbox; u16 i=0; cantxmsg txmessage; txmessage.stdid=0x0000; //标准标识符为0x00 txmessage.extid=0x1314; //扩展标识符0x0000 txmessage.ide=can_id_ext; //使用扩展标识符 txmessage.rtr=can_rtr_data; //为数据帧 txmessage.dlc=2; //消息的数据长度为2个字节 txmessage.data[0]=data1; //第一个字节数据 txmessage.data[1]=data2; //第二个字节数据 //发送数据 mbox= can_transmit(can1, &txmessage); while((can_transmitstatus(can1, mbox)==can_txstatus_failed)&&(i=0xfff) return 0;//发送失败 return 1;//发送成功 }u8 can_getmsg(u8 *msg1,u8 *msg2){ if(rx_flag == 1)//发现数据 { *msg1=can_rx_buf[0]; *msg2=can_rx_buf[1]; rx_flag=0;//数据已经取走,可以更新数据 return 1; }else return 0;}void clear_canbuffer(void){ rx_flag=0;//清楚接收标志位 memset(can_rx_buf, 0, sizeof(u8)*can_rx_len);//清空缓冲区}u8 check_canrx(void){ return (rx_flag == 6);}
can.h文件:
#ifndef __can_h#define __can_h#include sys.h#include string.h#define can_rx_len 30 //定义最大接收字节数 extern u8 can_rx_buf[can_rx_len]; //接收缓冲,最大usart_rec_len个字节.末字节为换行符 void can1_init(void);u8 can_sendmsg(u8* data1, u8* data2);u8 can_getmsg(u8 *msg1,u8 *msg2);#endif /* __can_h */
楼宇能耗管理平台,打造绿色节能建筑
差分信号的优缺点有哪些
干电池和锂电池的区别 锂电池电压为0如何激活?
解决软件开发四大痛点,飞算全自动软件工程平台带来新解法
LED日光灯驱动电源自制详解
使用STM32F103做CAN的收发通信
C语言数组和指针的区别
PACK气密性测试,PACK电池包气密性测试方案
如何选优质内存条 内存PCB要求更严格
美国对华为“临时许可”延长90天 华为回应美国没有改变打压中国高科技公司实质
蓝魔M7评测 复古的风格适合怀旧的人
什么是ArcoWork
关于MDT推Z轴TMR传感器的性能分析和应用介绍
东芝开发出SoC省电技术,工作中也能深度休眠
40G,40G是什么意思
通用Cruise获50亿美元的投资,全力研发无人驾驶出租车业务
回顾第十一届中国制造业MES应用年会内容和介绍
河南省5G产业发展行动方案为5G应用孵化和产业落地提供了良好的机会
无桥Boost PFC电路的EMI实例分析
华为物联网操作系统 LiteOS内核教程03-任务管理