本帖最后由 ywlzh 于 2017-9-15 11:47 编辑
这个帖子先在单片机stm32上实现CAN通信,如果你对CAN一点都不了解,建议先大概的了解下基本内容。
stm32,CAN通信例子多了去。我这就记录下ID过滤,中断处理远程帧和数据帧。
先说ID过滤吧
1.软处理
跑过例程了,都应该知道,CAN接收都是放在主程序while(1),或者开个任务不断的查询对应FIFO是否有消息。
- CanRxMsg RxMessage;
- CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
复制代码
然后针对RxMessage去解析ID,发现ID不对,就不处理这一帧数据。
这种处理有个弊端就是每一帧数据都接收,都处理,增加的CPU负载。
2.硬处理
这个就得看CAN初始化程序了
- u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- CAN_InitTypeDef CAN_InitStructure;
- CAN_FilterInitTypeDef CAN_FilterInitStructure;
- #if CAN_RX0_INT_ENABLE
- NVIC_InitTypeDef NVIC_InitStructure;
- #endif
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
-
- //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; //模式设置: mode:0,普通模式;1,回环模式; //
- //设置波特率
- CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
- CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq
- CAN_InitStructure.CAN_BS2=tbs2;//Tbs2=tbs2+1个时间单位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=CAN_ID<<5;////32位ID
- CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
- CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xffff;//32位MASK
- CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xfff8;
- CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
- CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器0
- CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
- #if CAN_RX0_INT_ENABLE
-
- CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO0消息挂号中断允许.
-
- NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
-
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- #endif
- return 0;
- }
复制代码
这个程序是原子战舰板子CAN程序的例程,我对ID做了处理,原先例程就是用软处理ID过滤的,这不适合需求。在使用这个初始化前,要事先定义板子ID编号
#define CAN_ID 5 //板子ID为5 这个宏全局使用
重点介绍 ID过滤吧
CAN_FilterInitStructure.CAN_FilterIdHigh=CAN_ID<<5;////32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xffff;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xfff8;
再结合这本书
STM32CAN发送和接收过滤详解.pdf
(252.93 KB, 下载次数: 254)
我这再给个例子说明一下,如果还不懂,那我也是没有办法让你懂了
例子: 我要设置自身ID为2,但我想接收ID为0x002和ID为0x402,那么就得这么设置
#define CAN_ID 2
CAN_FilterInitStructure.CAN_FilterIdHigh=CAN_ID<<5;////32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x7fff;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xfff8;
就可以了,不要被图中红色部分4个数字一组给搞糊涂了,因为上位机调试助手做了移位处理,在不使用扩展ID的情况下,ID的排序对应图片中的例子就为:
011 0001 0111
111 1111 1111
那么接收ID为0x002和ID为0x402,对应下来就是
ID: 100 0000 0010
ID: 000 0000 0010
过滤:011 1111 1111
也就是CAN_FilterMaskIdHigh为什么要为0x7fff了。
实验下发送数据帧看看
正确无误。图中让上位机向0x403发送,板子是不接收这个ID的数据,所以不接收数据。就没有往上回传数据了。
再强调一遍波特率的设置吧,传参数(x,y,z,e,mode);
计算波特率就为 36M/(((x+1)+(y+1)+(z+1))*e)
所以想要设定为500Kb的速度 就可以设置参数(0,0,6,8,0); //36M/72 = 500K
还不明白,再好好体会!
最后说一下CAN数据接收,我是不赞同额外开个任务去查询对应FIFO消息的,使用中断
- CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0;
- CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO0消息挂号中断允许.
- NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
复制代码
至于使用FIFO0还是FIFO1,看你想法了,自己改好对应关系就可以了
再看看中断吧
- #if CAN_RX0_INT_ENABLE //使能RX0中断
- //中断服务函数
- void USB_LP_CAN1_RX0_IRQHandler(void)
- {
- CanRxMsg RxMessage;
- CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
- if(RxMessage.RTR == CAN_RTR_Remote){
- /* 远程帧 返回信息*/
- u8 Canbuf[8]={0};
- Can_Send_Msg(Canbuf,8);
- }
- else if(RxMessage.RTR == CAN_RTR_Data){
- /* 数据帧 */
- if((RxMessage.Data[0] == 0x01)&&(RxMessage.Data[1]==0x01)){
- if(RxMessage.Data[2] == 0x01) Rate.Flag = 1;
- else if(RxMessage.Data[2] == 0x00) Rate.Flag = 0;
- Can_Send_Msg(RxMessage.Data,8);
- }
- }
- //for(i=0;i<8;i++)
- //printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
- }
- #endif
复制代码
就这么简单了,这个帖子目前就处理远程帧和数据帧。至于其他的帧,我还没开始实验,还有上位机的开发也没开始实验
敬请期待。
此内容由EEWORLD论坛网友ywlzh原创,如需转载或用于商业用途需征得作者同意并注明出处