CAN总线是汽车电子上不可缺少的技术,虽然现在有些造车新势力开始采用以太网来逐步取代CAN总线的地位,但是CAN总线先天的优势(成本低,安全性好,稳定性好),让其霸占汽车总线的巅峰,也必然有着其过人之处。个人比较看好未来是由CAN总线和以太网两者互相并存的车载网络解决方案。
话题扯得有点远,只是想表个态度,即使现在才从零学习CAN总线,它也不是什么快要“没落”的技术,不仅仅在汽车领域上,在工业领域、医疗领域等都有CAN总线的一席之地。
今天分享一个简单的CAN通讯实验,用STM32F103C8T6工控板模拟汽车OBD发送CAN信号,再用另一款STM32F103ZET6开发板模拟汽车电子设备来接收CAN信号,主要实现两个功能:
1)STM32F103C8T6工控板上电后,会自动发送模拟转速信号,具体的:该转速信号的ID号为0x17c,其数据位的第三和第四位为转速数据。STM32F103ZET6开发板上电后,利用CAN中断服务函数不停的接收该转速信号(这一功能模拟汽车电子设备接收汽车OBD发送的CAN广播信号);
2)当按下STM32F103ZET6开发板的按键时,会主动发送一帧CAN信号至STM32F103C8T6工控板,STM32F103C8T6工控板接收到后,会发送一次汽车车速信号(这一功能模拟汽车电子设备接收汽车OBD发送的CAN收发信号)。
硬件部分
1. CAN的接线
下图是STM32F103C8T6工控板的芯片接线,PB8和PB9对应CAN_RX和CAN_TX;
下图是STM32F103ZET6开发板的新品接线,PA11和PA12对应CAN_RX和CAN_TX;
将STM32F103C8T6工控板的CAN_H和STM32F103ZET6开发板的CAN_H相连;
将STM32F103C8T6工控板的CAN_L和STM32F103ZET6开发板的CAN_L相连。
软件部分
1. STM32F103C8T6工控板的主要代码
前面也说了,STM32F103C8T6工控板用来模拟汽车OBD发送CAN信号,涉及广播发送CAN信号和收发模式下发送CAN信号。
1.1 main.c主函数
主函数主要包括系统初始化,while循环里主要包含了模拟CAN广播发送的代码,另外还包括转速和车速的变化代码,具体如下:
- /************************************************
- 函数名称:int main()
- 函数功能:主函数入口
- 入口参数:无
- 返回参数:int
- 开发作者:闲人Ne
- *************************************************/
- #define CAN_500K 4
- u16 can_rpm = 2800; // 广播用,模拟的转速, 初始化700rpm,那么700*4=2800
- u8 can_v = 10; // 收发用,模拟的车速,初始化10kph
- u8 canbuf_broadcast[8] = {0,0,0,0,0,0,0,0}; // 广播用, 模拟接收到转速ID号里的8位数据,转速数据位于[3:4]位
- u8 canbuf_rs[8] = {0,0,0,0,0,0,0,0}; // 收发用,模拟接收到车速ID号里的8位数据,车速数据位于[3] 位
- int main(void)
- {
- u8 t=0;
- u8 can_upper_rpm; // can_rpm的高8位
- u8 can_lower_rpm; // can_rpm的低8位
- u8 res; // 错误号
- delay_init(); // 初始化延时函数
- LED_Init(); // 初始化LED函数
- CAN_Mode_Init(CAN_500K); // CAN初始化函数
- while(1)
- {
- can_lower_rpm = can_rpm; // can_lower取can_rpm低8位
- can_upper_rpm = can_rpm >> 8; // can_upper取can_rpm高8位
- canbuf_broadcast[3] = can_upper_rpm; // 更新转速ID号里的第[3]位数据;
- canbuf_broadcast[4] = can_lower_rpm; // 更新转速ID号里的第[4]位数据;
- res=Can_Send_Msg_broadcast(canbuf_broadcast,8); // 发送转速数据,模拟汽车CAN广播发送
- if(res)D2=0; // 发送失败时,LED1灯亮
- canbuf_rs[3] = can_v; // 更新车速ID号里的第[3]位数据;
- can_rpm = can_rpm + 40;
- if(can_rpm > 16000)can_rpm =2800;
- can_v = can_v + 10;
- if(can_v > 160)can_v =10;
- t++;
- if(t==1)
- {
- D1=!D1; //提示系统正在运行//
- t=0;
- }
- delay_ms(1000);
- }
- }
- /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
1.2 stm32f10x_it.c里的中断服务函数
USB_LP_CAN1_RX0_IRQHandler()函数主要模拟接收到STM32F103ZET6开发板的CAN信号时,发送一帧CAN车速信号(模拟收发模式下发送CAN信号),具体代码如下:
- /************************************************
- 函数名称:USB_LP_CAN1_RX0_IRQHandler()
- 函数功能:USB_LP_CAN1接收中断服务函数
- 入口参数:无
- 返回参数:无
- 开发作者:闲人Ne
- *************************************************/
- #define sr_stdid 0x7DF // 这是从STM32F103ZET6开发板接收到的CAN信号里的ID号
- extern u8 canbuf_rs[8]; // 收发用,模拟接收到车速ID号里的8位数据,车速数据位于[3] 位
- void USB_LP_CAN1_RX0_IRQHandler(void)
- {
- u8 err=0;
- u8 canbuf_v[8];
- CanRxMsg RxMessage;
- int i=0;
- CAN_Receive(CAN1, 0, &RxMessage);
- if(sr_stdid == RxMessage.StdId) // 判断接收到的CAN信号的ID号是否是0x7DF
- for(i=0;i<8;i++)canbuf_v<i>=RxMessage.Data<i>;
- if(canbuf_v[0]==2&canbuf_v[1]==1&canbuf_v[2]==13) // 判断接收到的CAN信号的数据第0,第1和第2位是否是0x21D
- err = Can_Send_Msg_rs(canbuf_rs,8); // 发送一帧CAN车速信号
- if(err)D2=0;
- }
- /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/</i></i>
复制代码
1.3 CAN发送一帧信号函数
主要包括两个函数,分别模拟广播模式下发送CAN信号和收发模式下发送CAN信号,具体代码如下:
- /********************************************************
- 函数名称:u8 Can_Send_Msg_broadcast(u8* msg,u8 len)
- 函数功能:广播用,CAN发送一组数据
- 入口参数:u8* msg,数据指针,最大为8个字节; u8 len 数据长度
- 返回参数:0,成功;1,失败
- 开发作者:闲人Ne
- *********************************************************/
- u8 Can_Send_Msg_broadcast(u8* msg,u8 len)
- {
- u8 mbox;
- u16 i=0;
- CanTxMsg TxMessage_b;
- TxMessage_b.StdId=0x17C; // 标准标识符
- TxMessage_b.ExtId=0x18DB33F1; // 扩展标示符
- TxMessage_b.IDE=CAN_Id_Standard; // 标准帧
- TxMessage_b.RTR=CAN_RTR_Data; // 数据帧
- TxMessage_b.DLC=len; // 数据长度
- for(i=0;i<len;i++)
- TxMessage_b.Data=msg;
- mbox= CAN_Transmit(CAN1, &TxMessage_b);
- i=0;
- while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;
- if(i>=0XFFF)return 1;
- return 0;
- }
- /********************************************************
- 函数名称:u8 Can_Send_Msg_rs(u8* msg,u8 len)
- 函数功能:收发用,CAN发送一组数据
- 入口参数:u8* msg,数据指针,最大为8个字节; u8 len 数据长度
- 返回参数:0,成功;1,失败
- 开发作者:闲人Ne
- *********************************************************/
- u8 Can_Send_Msg_rs(u8* msg,u8 len)
- {
- u8 mbox;
- u16 i=0;
- CanTxMsg TxMessage_r;
- TxMessage_r.StdId=0x7E8; // 标准标识符
- TxMessage_r.ExtId=0x18DB33F1; // 扩展标示符
- TxMessage_r.IDE=CAN_Id_Standard; // 标准帧
- TxMessage_r.RTR=CAN_RTR_Data; // 数据帧
- TxMessage_r.DLC=len; // 数据长度
- for(i=0;i<len;i++)
- TxMessage_r.Data=msg;
- mbox= CAN_Transmit(CAN1, &TxMessage_r);
- i=0;
- while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;
- if(i>=0XFFF)return 1;
- return 0;
- }
复制代码
2. STM32F103ZET6开发板的主要代码
前面也说了,STM32F103ZET6开发板用来模拟汽车电子设备接收汽车OBD发送的CAN信号。
2.1 main.c主函数
主函数主要包括系统初始化,while循环里主要包含了按键按下时,发送CAN请求信号,具体如下:
- /************************************************
- 函数名称:int main()
- 函数功能:主函数
- 入口参数:无
- 返回参数:int
- 开发作者:闲人Ne
- *************************************************/
- int main(void)
- {
- u8 canbuf_v[8]={2,1,13,0,0,0,0,0}; // 0x 21D0 0000
- u8 key;
- u8 i=0,t=0;
- u8 res;
- u8 mode=CAN_Mode_Normal; //CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式
- delay_init(); //延时函数初始化
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
- uart_init(115200); //串口初始化为115200
- LED_Init(); //初始化与LED连接的硬件接口
- KEY_Init(); //按键初始化
- CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal); //CAN初始化环回模式,波特率500Kbps
- while(1)
- {
- key=KEY_Scan(0);
- if(key==KEY0_PRES)
- {
- printf("\n发送CAN数据为:");
- for(i=0;i<8;i++)
- printf("\n[%x]",canbuf_v);
- printf("\r\n");
- res=Can_Send_Msg(canbuf_v,8); // 发送CAN请求信号
- if(res)printf("\n发送失败!\r\n");
- }
- t++;
- delay_ms(10);
- if(t==20)
- {
- LED0=!LED0;
- t=0;
- }
- }
- }
- /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
2.2 stm32f10x_it.c里的中断服务函数
USB_LP_CAN1_RX0_IRQHandler()函数主要模拟接收到STM32F103C8T6开发板的CAN信号,并根据ID号判断是接收了广播模式下的转速信号还是收发模式下的车速信号,具体代码如下:
- u8 canbuf[8];
- #define stdid_broad 0x17C
- #define stdid_rs 0x7E8
- void USB_LP_CAN1_RX0_IRQHandler(void)
- {
- u16 rpm;
- u8 v;
- CanRxMsg RxMessage;
- int i=0;
- CAN_Receive(CAN1, 0, &RxMessage);
- if(RxMessage.StdId == stdid_broad) // 判断接收到的CAN信号的ID号是否为 0x17C
- {
- for(i=0;i<8;i++)canbuf=RxMessage.Data;
- rpm = (canbuf[3]<<8|canbuf[4])/4;
- printf("\n接收到的汽车转速为:%d rpm \r\n",rpm);
- }
- else
- if(RxMessage.StdId == stdid_rs) // 判断接收到的CAN信号的ID号是否为 0x7E8
- {
- for(i=0;i<8;i++)canbuf=RxMessage.Data;
- v = canbuf[3];
- printf("\n接收到的汽车车速为:%d kph \r\n",v);
- }
- }
复制代码
2.3 STM32F103ZET6开发板还包含了串口通讯函数,主要用来与电脑通讯,显示运行结果。这些知识可以参考我以前写的文章。
实验结果
实验结果如下图所示,系统运行后,将STM32F103ZET6开发板的串口接到电脑上,可实时看到从STM32F103C8T6工控板传来的转速信号,当按下STM32F103ZET6开发板的按键时,可从STM32F103C8T6工控板接收到一次车速信号。
备注:
1)对于CAN的基础知识可以参考之前我写的文章;
2)汽车CAN总线知识参考ISO 15765-4,ISO 15031-5,SAE J1939这些标准。
————————————————
版权声明:天亮继续睡
|