你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32模拟汽车OBD接口处的CAN收发信号实验

[复制链接]
STMCU小助手 发布时间:2022-11-20 15:30
CAN总线是汽车电子上不可缺少的技术,虽然现在有些造车新势力开始采用以太网来逐步取代CAN总线的地位,但是CAN总线先天的优势(成本低,安全性好,稳定性好),让其霸占汽车总线的巅峰,也必然有着其过人之处。个人比较看好未来是由CAN总线和以太网两者互相并存的车载网络解决方案。
20201226171835790.png

话题扯得有点远,只是想表个态度,即使现在才从零学习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;

20201226175611799.png

下图是STM32F103ZET6开发板的新品接线,PA11和PA12对应CAN_RX和CAN_TX;

20201226180742687.png

将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广播发送的代码,另外还包括转速和车速的变化代码,具体如下:

  1. /************************************************
  2. 函数名称:int main()
  3. 函数功能:主函数入口
  4. 入口参数:无
  5. 返回参数:int
  6. 开发作者:闲人Ne
  7. *************************************************/
  8. #define CAN_500K 4
  9. u16 can_rpm = 2800;                            // 广播用,模拟的转速, 初始化700rpm,那么700*4=2800
  10. u8  can_v   =   10;                            // 收发用,模拟的车速,初始化10kph
  11. u8  canbuf_broadcast[8] = {0,0,0,0,0,0,0,0};   // 广播用, 模拟接收到转速ID号里的8位数据,转速数据位于[3:4]位
  12. u8  canbuf_rs[8]        = {0,0,0,0,0,0,0,0};   // 收发用,模拟接收到车速ID号里的8位数据,车速数据位于[3] 位
  13. int main(void)
  14. {
  15.         u8 t=0;        
  16.         u8 can_upper_rpm;                    // can_rpm的高8位
  17.         u8 can_lower_rpm;              // can_rpm的低8位
  18.         u8 res;                    // 错误号
  19.         delay_init();              // 初始化延时函数   
  20.         LED_Init();                // 初始化LED函数
  21.         CAN_Mode_Init(CAN_500K);   // CAN初始化函数
  22.          while(1)
  23.         {
  24.                 can_lower_rpm = can_rpm;                           // can_lower取can_rpm低8位
  25.                 can_upper_rpm = can_rpm >> 8;                      // can_upper取can_rpm高8位
  26.                 canbuf_broadcast[3] = can_upper_rpm;               // 更新转速ID号里的第[3]位数据;
  27.                 canbuf_broadcast[4] = can_lower_rpm;                           // 更新转速ID号里的第[4]位数据;
  28.                 res=Can_Send_Msg_broadcast(canbuf_broadcast,8);    // 发送转速数据,模拟汽车CAN广播发送
  29.                 if(res)D2=0;                                                           // 发送失败时,LED1灯亮
  30.                 canbuf_rs[3] = can_v;                                                           // 更新车速ID号里的第[3]位数据;
  31.                 can_rpm = can_rpm + 40;                           
  32.                 if(can_rpm > 16000)can_rpm =2800;        
  33.                   can_v = can_v + 10;
  34.                 if(can_v > 160)can_v =10;        
  35.                 t++;
  36.                 if(t==1)
  37.                 {
  38.                         D1=!D1;                                       //提示系统正在运行//        
  39.                         t=0;
  40.                 }        
  41.                 delay_ms(1000);               
  42.         }
  43. }
  44. /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码

1.2 stm32f10x_it.c里的中断服务函数
USB_LP_CAN1_RX0_IRQHandler()函数主要模拟接收到STM32F103ZET6开发板的CAN信号时,发送一帧CAN车速信号(模拟收发模式下发送CAN信号),具体代码如下:

  1. /************************************************
  2. 函数名称:USB_LP_CAN1_RX0_IRQHandler()
  3. 函数功能:USB_LP_CAN1接收中断服务函数
  4. 入口参数:无
  5. 返回参数:无
  6. 开发作者:闲人Ne
  7. *************************************************/
  8. #define sr_stdid 0x7DF    // 这是从STM32F103ZET6开发板接收到的CAN信号里的ID号
  9. extern u8 canbuf_rs[8];   // 收发用,模拟接收到车速ID号里的8位数据,车速数据位于[3] 位
  10. void USB_LP_CAN1_RX0_IRQHandler(void)
  11. {
  12.         u8 err=0;
  13.         u8 canbuf_v[8];            
  14.          CanRxMsg RxMessage;
  15.         int i=0;
  16.         CAN_Receive(CAN1, 0, &RxMessage);
  17.         if(sr_stdid == RxMessage.StdId)                                         // 判断接收到的CAN信号的ID号是否是0x7DF
  18.                 for(i=0;i<8;i++)canbuf_v<i>=RxMessage.Data<i>;      
  19.                  if(canbuf_v[0]==2&canbuf_v[1]==1&canbuf_v[2]==13)  // 判断接收到的CAN信号的数据第0,第1和第2位是否是0x21D
  20.                         err = Can_Send_Msg_rs(canbuf_rs,8);                   // 发送一帧CAN车速信号
  21.                 if(err)D2=0;                                                                       
  22. }
  23. /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/</i></i>
复制代码

1.3 CAN发送一帧信号函数
主要包括两个函数,分别模拟广播模式下发送CAN信号和收发模式下发送CAN信号,具体代码如下:

  1. /********************************************************
  2. 函数名称:u8 Can_Send_Msg_broadcast(u8* msg,u8 len)
  3. 函数功能:广播用,CAN发送一组数据        
  4. 入口参数:u8* msg,数据指针,最大为8个字节; u8 len 数据长度
  5. 返回参数:0,成功;1,失败
  6. 开发作者:闲人Ne
  7. *********************************************************/
  8. u8 Can_Send_Msg_broadcast(u8* msg,u8 len)
  9. {        
  10.         u8 mbox;
  11.         u16 i=0;
  12.         CanTxMsg TxMessage_b;
  13.         TxMessage_b.StdId=0x17C;                          // 标准标识符
  14.         TxMessage_b.ExtId=0x18DB33F1;              // 扩展标示符
  15.         TxMessage_b.IDE=CAN_Id_Standard;           // 标准帧
  16.         TxMessage_b.RTR=CAN_RTR_Data;                  // 数据帧
  17.         TxMessage_b.DLC=len;                                  // 数据长度
  18.         for(i=0;i<len;i++)
  19.                 TxMessage_b.Data=msg;                                 
  20.         mbox= CAN_Transmit(CAN1, &TxMessage_b);   
  21.         i=0;
  22.         while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;        
  23.         if(i>=0XFFF)return 1;
  24.         return 0;         
  25. }
  26. /********************************************************
  27. 函数名称:u8 Can_Send_Msg_rs(u8* msg,u8 len)
  28. 函数功能:收发用,CAN发送一组数据        
  29. 入口参数:u8* msg,数据指针,最大为8个字节; u8 len 数据长度
  30. 返回参数:0,成功;1,失败
  31. 开发作者:闲人Ne
  32. *********************************************************/
  33. u8 Can_Send_Msg_rs(u8* msg,u8 len)
  34. {        
  35.         u8 mbox;
  36.         u16 i=0;
  37.         CanTxMsg TxMessage_r;
  38.         TxMessage_r.StdId=0x7E8;                          // 标准标识符
  39.         TxMessage_r.ExtId=0x18DB33F1;                  // 扩展标示符
  40.         TxMessage_r.IDE=CAN_Id_Standard;           // 标准帧
  41.         TxMessage_r.RTR=CAN_RTR_Data;                  // 数据帧
  42.         TxMessage_r.DLC=len;                                  // 数据长度
  43.         for(i=0;i<len;i++)
  44.                 TxMessage_r.Data=msg;                                 
  45.         mbox= CAN_Transmit(CAN1, &TxMessage_r);   
  46.         i=0;
  47.         while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;        
  48.         if(i>=0XFFF)return 1;
  49.         return 0;         
  50. }
复制代码

2. STM32F103ZET6开发板的主要代码
前面也说了,STM32F103ZET6开发板用来模拟汽车电子设备接收汽车OBD发送的CAN信号。
2.1 main.c主函数
主函数主要包括系统初始化,while循环里主要包含了按键按下时,发送CAN请求信号,具体如下:

  1. /************************************************
  2. 函数名称:int main()
  3. 函数功能:主函数
  4. 入口参数:无
  5. 返回参数:int
  6. 开发作者:闲人Ne
  7. *************************************************/
  8. int main(void)
  9. {         
  10.         u8 canbuf_v[8]={2,1,13,0,0,0,0,0};                 // 0x 21D0 0000
  11.         u8 key;
  12.         u8 i=0,t=0;
  13.         u8 res;
  14.         u8 mode=CAN_Mode_Normal;                           //CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式
  15.         delay_init();                                               //延时函数初始化  
  16.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
  17.         uart_init(115200);                                            //串口初始化为115200
  18.         LED_Init();                                                             //初始化与LED连接的硬件接口
  19.         KEY_Init();                                                           //按键初始化                 
  20.         CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal);  //CAN初始化环回模式,波特率500Kbps           
  21.          while(1)
  22.         {
  23.                 key=KEY_Scan(0);
  24.                 if(key==KEY0_PRES)                          
  25.                 {
  26.                         printf("\n发送CAN数据为:");
  27.                         for(i=0;i<8;i++)
  28.                                 printf("\n[%x]",canbuf_v);
  29.                         printf("\r\n");
  30.                         res=Can_Send_Msg(canbuf_v,8);              // 发送CAN请求信号                          
  31.                         if(res)printf("\n发送失败!\r\n");               
  32.                 }
  33.                 t++;
  34.                 delay_ms(10);
  35.                 if(t==20)
  36.                 {
  37.                         LED0=!LED0;                                                         
  38.                         t=0;
  39.                 }                  
  40.         }
  41. }
  42. /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码

2.2 stm32f10x_it.c里的中断服务函数
USB_LP_CAN1_RX0_IRQHandler()函数主要模拟接收到STM32F103C8T6开发板的CAN信号,并根据ID号判断是接收了广播模式下的转速信号还是收发模式下的车速信号,具体代码如下:

  1. u8 canbuf[8];            
  2. #define stdid_broad 0x17C
  3. #define stdid_rs          0x7E8
  4. void USB_LP_CAN1_RX0_IRQHandler(void)
  5. {
  6.         u16 rpm;
  7.         u8 v;
  8.           CanRxMsg RxMessage;
  9.         int i=0;
  10.           CAN_Receive(CAN1, 0, &RxMessage);
  11.         if(RxMessage.StdId == stdid_broad)                // 判断接收到的CAN信号的ID号是否为 0x17C
  12.         {
  13.                 for(i=0;i<8;i++)canbuf=RxMessage.Data;
  14.                 rpm = (canbuf[3]<<8|canbuf[4])/4;
  15.                 printf("\n接收到的汽车转速为:%d rpm \r\n",rpm);
  16.         }
  17.         else
  18.                 if(RxMessage.StdId == stdid_rs)              // 判断接收到的CAN信号的ID号是否为 0x7E8
  19.                 {
  20.                         for(i=0;i<8;i++)canbuf=RxMessage.Data;
  21.                         v = canbuf[3];
  22.                         printf("\n接收到的汽车车速为:%d kph \r\n",v);        
  23.                 }                        
  24. }
复制代码

2.3 STM32F103ZET6开发板还包含了串口通讯函数,主要用来与电脑通讯,显示运行结果。这些知识可以参考我以前写的文章。

实验结果

实验结果如下图所示,系统运行后,将STM32F103ZET6开发板的串口接到电脑上,可实时看到从STM32F103C8T6工控板传来的转速信号,当按下STM32F103ZET6开发板的按键时,可从STM32F103C8T6工控板接收到一次车速信号。
20201226190722343.png

备注:
1)对于CAN的基础知识可以参考之前我写的文章;
2)汽车CAN总线知识参考ISO 15765-4,ISO 15031-5,SAE J1939这些标准。
————————————————
版权声明:天亮继续睡



收藏 评论0 发布时间:2022-11-20 15:30

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版