CAN总线是汽车电子上不可缺少的技术,虽然现在有些造车新势力开始采用以太网来逐步取代CAN总线的地位,但是CAN总线先天的优势(成本低,安全性好,稳定性好),让其霸占汽车总线的巅峰,也必然有着其过人之处。个人比较看好未来是由CAN总线和以太网两者互相并存的车载网络解决方案。
& R+ J3 B1 o+ x: {
) j* @4 `/ l! j4 A% Y6 ?4 ? ]) s8 f/ ], Q* W) J8 p
话题扯得有点远,只是想表个态度,即使现在才从零学习CAN总线,它也不是什么快要“没落”的技术,不仅仅在汽车领域上,在工业领域、医疗领域等都有CAN总线的一席之地。; F# h: h. q* O
4 E' z; c; ?/ ?! W- e- h, X
今天分享一个简单的CAN通讯实验,用STM32F103C8T6工控板模拟汽车OBD发送CAN信号,再用另一款STM32F103ZET6开发板模拟汽车电子设备来接收CAN信号,主要实现两个功能:
$ b+ z/ S3 B2 U/ b( _ t0 m2 F/ s# s) k
1)STM32F103C8T6工控板上电后,会自动发送模拟转速信号,具体的:该转速信号的ID号为0x17c,其数据位的第三和第四位为转速数据。STM32F103ZET6开发板上电后,利用CAN中断服务函数不停的接收该转速信号(这一功能模拟汽车电子设备接收汽车OBD发送的CAN广播信号);
/ I6 s- o7 x) y0 Z2)当按下STM32F103ZET6开发板的按键时,会主动发送一帧CAN信号至STM32F103C8T6工控板,STM32F103C8T6工控板接收到后,会发送一次汽车车速信号(这一功能模拟汽车电子设备接收汽车OBD发送的CAN收发信号)。/ s& N0 M; s6 d; ]2 ^( X
' n, w T) w) I" I
硬件部分
& P, V6 U B( G$ o6 x3 b; a1. CAN的接线. d a; @- y& s& g# f+ Y
下图是STM32F103C8T6工控板的芯片接线,PB8和PB9对应CAN_RX和CAN_TX;) e ]! {+ J3 s' j$ O4 f
9 q! k9 [7 l4 P
0 a+ \0 {1 {+ ~2 H
9 j* l# m. V0 E! r5 x, w. u! E% ~8 |下图是STM32F103ZET6开发板的新品接线,PA11和PA12对应CAN_RX和CAN_TX;& }' w% \- C0 T$ L3 H% ]7 h, f
# i0 l7 Q @2 e* B
' e J$ @8 _+ J
& R/ z; w% B" p8 r7 f将STM32F103C8T6工控板的CAN_H和STM32F103ZET6开发板的CAN_H相连;
$ c2 V( L+ u R5 }将STM32F103C8T6工控板的CAN_L和STM32F103ZET6开发板的CAN_L相连。
5 H$ w2 w* C- V$ s( q' H f' t
+ C2 i& P$ d9 p6 z' {& N. i. O软件部分 _7 f! j( k% Y1 U
1. STM32F103C8T6工控板的主要代码( {' {% D/ c; s# _, y
前面也说了,STM32F103C8T6工控板用来模拟汽车OBD发送CAN信号,涉及广播发送CAN信号和收发模式下发送CAN信号。
% }) B1 K0 }$ A$ x1.1 main.c主函数
9 Q& Y& t, l% p/ H- s% U主函数主要包括系统初始化,while循环里主要包含了模拟CAN广播发送的代码,另外还包括转速和车速的变化代码,具体如下:
/ `1 [+ W% m" c4 z3 T) X5 `4 U& }$ T" X ]. r
- /************************************************' r" r- y# a- I) {
- 函数名称:int main()
; h+ {, c$ e; {# P4 p - 函数功能:主函数入口
7 Q0 ?7 ?" I+ O) I8 ]0 r - 入口参数:无7 \9 L. m+ Y. t) `- \: d
- 返回参数:int
4 r6 q6 y' U; b" A0 h1 a, T - 开发作者:闲人Ne% T4 P( i) C3 o& X0 @1 h5 l
- *************************************************/9 X, r* E! z3 h
- #define CAN_500K 4+ `) n" ]8 N9 Z) U! v: `
- u16 can_rpm = 2800; // 广播用,模拟的转速, 初始化700rpm,那么700*4=2800
% U& o% I; R- H9 Z& n - u8 can_v = 10; // 收发用,模拟的车速,初始化10kph' n3 ^" f: i, _: ]) w8 p- W. s
- u8 canbuf_broadcast[8] = {0,0,0,0,0,0,0,0}; // 广播用, 模拟接收到转速ID号里的8位数据,转速数据位于[3:4]位
- G$ d/ c8 ] [+ { - u8 canbuf_rs[8] = {0,0,0,0,0,0,0,0}; // 收发用,模拟接收到车速ID号里的8位数据,车速数据位于[3] 位$ Z$ e! F8 Z# C2 I$ C! B3 A, T
- int main(void)
# M. \5 U& Y) e& t - {: P. q" g& R% q: @
- u8 t=0; , ]6 r+ ~) o0 p6 F" d6 G% o f* H& L
- u8 can_upper_rpm; // can_rpm的高8位! F+ v- O8 [5 N K
- u8 can_lower_rpm; // can_rpm的低8位 - O' n# t( ?0 ~/ u
- u8 res; // 错误号
3 | Y9 F4 b3 X+ G; m - delay_init(); // 初始化延时函数 5 q$ _& a- L/ |' R2 }
- LED_Init(); // 初始化LED函数
3 J: C' C8 I5 N2 b - CAN_Mode_Init(CAN_500K); // CAN初始化函数
% Z- Y* ]' |& S; `3 W3 C6 U: L - while(1)4 K! {, U/ N6 o) v8 n2 q! |
- {7 k9 e# M' j- R' @6 p' M
- can_lower_rpm = can_rpm; // can_lower取can_rpm低8位
! ?+ M% y9 N6 h: p: ] - can_upper_rpm = can_rpm >> 8; // can_upper取can_rpm高8位3 x8 n, k) `! M
- canbuf_broadcast[3] = can_upper_rpm; // 更新转速ID号里的第[3]位数据;
$ P# J0 ~' \" x! W- w - canbuf_broadcast[4] = can_lower_rpm; // 更新转速ID号里的第[4]位数据;7 z1 X( q/ e" a1 [0 C4 Z, B; r
- res=Can_Send_Msg_broadcast(canbuf_broadcast,8); // 发送转速数据,模拟汽车CAN广播发送& f# e; A' `! B0 P9 A4 n* N
- if(res)D2=0; // 发送失败时,LED1灯亮
. t8 ?+ C6 F+ t. |- B. ^ - canbuf_rs[3] = can_v; // 更新车速ID号里的第[3]位数据;. e% ?+ A7 i4 g8 F: h
- can_rpm = can_rpm + 40;
- b2 M! h6 n' S - if(can_rpm > 16000)can_rpm =2800;
- |! |0 x+ m1 r7 k5 T - can_v = can_v + 10;% X g8 C- h( N; P
- if(can_v > 160)can_v =10;
' g- k4 H( q! f; o$ [6 t# s - t++;( [, V3 J: Z* K6 k1 e2 B: u, T
- if(t==1)
- O5 ~5 f* X9 F - {
' h1 N1 B* L" ~ - D1=!D1; //提示系统正在运行//
' t6 Z$ z* s c9 g4 M - t=0;
O: a" z4 K$ P3 i - }
$ O2 [/ K4 q" X" v - delay_ms(1000);
/ |4 l! i. V9 N$ R& G - }
3 a y! \0 f; {8 R* v - }
$ ]2 X+ W- `1 t- y! N+ X6 |6 u - /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
9 m) l9 ^4 z& q4 q7 r/ m0 x1.2 stm32f10x_it.c里的中断服务函数
9 f5 y: ^: V9 E% y; P2 ]8 Q$ SUSB_LP_CAN1_RX0_IRQHandler()函数主要模拟接收到STM32F103ZET6开发板的CAN信号时,发送一帧CAN车速信号(模拟收发模式下发送CAN信号),具体代码如下: z3 Y# d1 A* `
0 n% F& {, Y: _7 Z) b
- /************************************************' v# \0 b- Y8 M/ ? E
- 函数名称:USB_LP_CAN1_RX0_IRQHandler()0 v) I5 E4 J' Q' T: ~* y; Q0 m
- 函数功能:USB_LP_CAN1接收中断服务函数
1 K! O+ o( s/ e/ p1 _) O - 入口参数:无! t6 @3 P5 a7 ^" i
- 返回参数:无( r6 y% u. R+ E0 e
- 开发作者:闲人Ne0 J: C& M3 L+ d, `( }5 a3 i! o. v5 j7 r
- *************************************************/
0 M8 D9 X2 I( v/ I- I: ^ - #define sr_stdid 0x7DF // 这是从STM32F103ZET6开发板接收到的CAN信号里的ID号. k; i: S8 |6 W- y& O& H
- extern u8 canbuf_rs[8]; // 收发用,模拟接收到车速ID号里的8位数据,车速数据位于[3] 位
" r& n& C( C4 S) O/ q0 q - void USB_LP_CAN1_RX0_IRQHandler(void)
4 h. K3 q9 u- t - {& e; z( Q, a2 n$ X8 @
- u8 err=0;6 { R9 o8 y5 G$ a* o- J
- u8 canbuf_v[8];
% |3 G% T. q8 r; O4 C) ^3 Y - CanRxMsg RxMessage;
1 _ f6 W$ e: Q9 g" E% W - int i=0;
9 r9 K& c! X. y - CAN_Receive(CAN1, 0, &RxMessage);
! |2 ^4 s% T4 x, O$ e* K - if(sr_stdid == RxMessage.StdId) // 判断接收到的CAN信号的ID号是否是0x7DF* }9 D/ w& F* t; T
- for(i=0;i<8;i++)canbuf_v<i>=RxMessage.Data<i>;
4 ^( r7 F$ @( O: Q1 }. r1 o% u - if(canbuf_v[0]==2&canbuf_v[1]==1&canbuf_v[2]==13) // 判断接收到的CAN信号的数据第0,第1和第2位是否是0x21D
; i% Y" M3 Q7 D6 o* S9 R - err = Can_Send_Msg_rs(canbuf_rs,8); // 发送一帧CAN车速信号" u. X! i* B) l0 s# f1 t
- if(err)D2=0; - }" u& \6 G/ P6 L( i- Y& s5 a* k" h
- }
2 \0 c# j. X( q1 z. b0 v+ M7 L - /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/</i></i>
复制代码 ) J! j. S% M i, q3 ^9 P
1.3 CAN发送一帧信号函数- F7 A2 j2 O" l
主要包括两个函数,分别模拟广播模式下发送CAN信号和收发模式下发送CAN信号,具体代码如下: W: D# v2 d: [* I
/ W/ _* u- ^" a. W- /********************************************************2 V' A" M$ Z) Z, l5 N
- 函数名称:u8 Can_Send_Msg_broadcast(u8* msg,u8 len)
* ]- W( s+ l$ i$ d3 e# C+ O - 函数功能:广播用,CAN发送一组数据
* W! X0 ` W. _, H c) F0 y - 入口参数:u8* msg,数据指针,最大为8个字节; u8 len 数据长度 n" t/ B! \9 h' b1 G2 y+ K. N9 ^
- 返回参数:0,成功;1,失败
; |7 T7 y5 k1 K! D - 开发作者:闲人Ne% I6 `# F/ M k2 r# {, G
- *********************************************************/6 l- {6 _4 a9 I+ s
- u8 Can_Send_Msg_broadcast(u8* msg,u8 len)! K$ B4 v1 d) k
- {
0 \8 T" r: N, O8 B, K. F4 A - u8 mbox;
$ C7 Z6 _' }+ |- C - u16 i=0;
' l1 N, d% S6 L - CanTxMsg TxMessage_b;
$ k" d1 z" M2 |5 T" l - TxMessage_b.StdId=0x17C; // 标准标识符
% i/ i J, i |' `" Y6 [ - TxMessage_b.ExtId=0x18DB33F1; // 扩展标示符
" l0 ^1 ?/ i! I+ p( z$ k - TxMessage_b.IDE=CAN_Id_Standard; // 标准帧
! c0 t3 C0 C# B5 O: u - TxMessage_b.RTR=CAN_RTR_Data; // 数据帧
" F8 r. m+ O( o4 d3 b6 F* H: j( s - TxMessage_b.DLC=len; // 数据长度
$ F9 c+ ~% B* {: S( V4 L, r2 O - for(i=0;i<len;i++)
" z) D: g2 }' r& S5 d5 k: S! q - TxMessage_b.Data=msg;
& S5 u' M! l' J) W( t8 p# ] - mbox= CAN_Transmit(CAN1, &TxMessage_b); 0 Q M7 s0 J. S9 A' r& |
- i=0; " T" ]0 }0 e6 l- B
- while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; : h2 y+ P2 V3 x' `. |. O/ e
- if(i>=0XFFF)return 1;; O8 @! |: ~! H, W5 U
- return 0; 7 K# n$ H T6 }! ]
- }+ n2 {: v8 n3 W+ g
- /********************************************************
1 F7 B! c9 F; `# D - 函数名称:u8 Can_Send_Msg_rs(u8* msg,u8 len)2 j) }& I2 R$ s& r
- 函数功能:收发用,CAN发送一组数据
9 h, e+ R1 U4 C' V ]9 Q - 入口参数:u8* msg,数据指针,最大为8个字节; u8 len 数据长度
% [1 e5 g$ Y s- G4 e) b - 返回参数:0,成功;1,失败
' _- u8 q% l$ P d& U- |$ r. V - 开发作者:闲人Ne
, C* }. T2 O6 X5 I' v - *********************************************************/
+ K- m0 T/ G0 `7 R1 w9 h2 s - u8 Can_Send_Msg_rs(u8* msg,u8 len)
3 [/ S! j3 }/ R" n" ^ v7 e! y0 s - {
# v; S; X# C6 w' V) N! M7 | - u8 mbox;: ~/ O% Q" R5 J1 C/ \
- u16 i=0;
! [2 }- X! r' z# {/ J7 ^ - CanTxMsg TxMessage_r;" Z9 K0 t5 b8 t% L- h
- TxMessage_r.StdId=0x7E8; // 标准标识符5 ~+ V6 Z5 U7 D/ |) S* J! q( L
- TxMessage_r.ExtId=0x18DB33F1; // 扩展标示符+ C, {3 p) q8 R* D1 d4 \
- TxMessage_r.IDE=CAN_Id_Standard; // 标准帧
' U0 b7 p0 f$ ]( Y' h4 J; ] - TxMessage_r.RTR=CAN_RTR_Data; // 数据帧3 ?1 J5 `; R3 q+ }4 r2 p
- TxMessage_r.DLC=len; // 数据长度! T5 n# K5 [# t/ U& u; H$ P
- for(i=0;i<len;i++)6 p0 h2 q) I) I E2 a8 u& Q# {
- TxMessage_r.Data=msg; 4 t! q6 z" V5 d6 S& ?9 c
- mbox= CAN_Transmit(CAN1, &TxMessage_r);
4 h4 k! r: J- l( w# c - i=0;
7 }. m2 T& A5 ]+ b' k - while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; % R; c* |& U3 S' D' p: m
- if(i>=0XFFF)return 1;$ G& A. H* z! Q! }0 ~
- return 0; 8 `! ]' o4 y! h+ g3 w' k) N: G
- }
复制代码
3 z; Q9 |) @# r$ U* Q4 b( `; r2. STM32F103ZET6开发板的主要代码; N7 Z2 l0 x% N$ B& Y- k
前面也说了,STM32F103ZET6开发板用来模拟汽车电子设备接收汽车OBD发送的CAN信号。+ E( q& E+ B" h- B* a- J
2.1 main.c主函数
' V7 S5 B3 L+ G) q8 c9 U; m主函数主要包括系统初始化,while循环里主要包含了按键按下时,发送CAN请求信号,具体如下:+ S. F# w* f% O1 f$ ~: v6 Z% A* i' u
J7 h& _. ^( K- ?6 B- p- /************************************************
; l8 N& g1 o/ s- R - 函数名称:int main()! \. M6 q7 [4 W' E
- 函数功能:主函数
8 V" _$ l3 c6 F& J3 @1 g! r - 入口参数:无
# A, v* v/ c! ]# g+ F1 S6 L - 返回参数:int
( a+ R' B+ w6 l9 T - 开发作者:闲人Ne
s$ {4 D2 g& M, b R - *************************************************/
g N/ y8 D1 a0 A- ~( i. _3 ]& z - int main(void)) o! e/ r6 H: Y8 ~9 a4 T+ J4 D
- { ( a% _# z+ _6 l. L" C3 x
- u8 canbuf_v[8]={2,1,13,0,0,0,0,0}; // 0x 21D0 0000
4 |. C# T- R$ w& ?8 ? - u8 key;* b- s* W% B8 d
- u8 i=0,t=0; T. ?2 P3 F" e: k& u: h
- u8 res;
o5 ?7 k2 _5 G- h7 ? - u8 mode=CAN_Mode_Normal; //CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式2 H4 I( }7 K6 r3 J1 h% ?
- delay_init(); //延时函数初始化 # N% Q: l4 g) _( ?
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
0 I% y' K* m* k& M4 @: }+ p: g - uart_init(115200); //串口初始化为115200
F4 B( B3 G% z4 b. d. x5 S/ Z3 k - LED_Init(); //初始化与LED连接的硬件接口
5 w8 k( C e- f) m h. q% D% e - KEY_Init(); //按键初始化
8 K) k: x7 M& c& H4 c" ^5 t0 V - CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal); //CAN初始化环回模式,波特率500Kbps
; e+ Q8 M6 I2 L* s- L - while(1)
. [4 {/ P/ j0 K* ^0 w1 Q6 | - {
9 u" N, D1 [* b3 N9 ]& S - key=KEY_Scan(0);- F* T# p- o4 J- v' T) p# @0 S8 B
- if(key==KEY0_PRES)
) Z- ~/ d: u9 t! F( g1 ?# J - {
6 ?) J. y2 j" Z; v' } - printf("\n发送CAN数据为:");
9 z4 b# L8 v" h" K5 q* U" m - for(i=0;i<8;i++)) v& i5 M9 A% u$ c9 J( k
- printf("\n[%x]",canbuf_v);4 Z+ O3 @$ H8 r4 {9 h
- printf("\r\n");0 Y% K" o6 m2 Z7 z/ D
- res=Can_Send_Msg(canbuf_v,8); // 发送CAN请求信号
$ J7 J7 d" c1 [, ~4 X k0 z - if(res)printf("\n发送失败!\r\n");
, S- R$ d5 @/ u0 e - }% C- H. D$ p3 n& k6 U; B4 D5 ]
- t++; : K) r- x+ \# {$ }. Z8 u0 z$ i
- delay_ms(10);8 I& [& T4 n4 n5 E+ U% @' }
- if(t==20)- O, j7 P) q5 Y9 c. ~
- {& ~: p1 f; L" m* h
- LED0=!LED0; $ G8 x$ G) ]$ o9 W1 S- r
- t=0;
% r' H( E# O' Z6 U2 s - }
- y0 z' `. \+ u; p - }. ?+ P" `1 k, f/ D& s/ b, r
- }
9 H1 a+ h# z1 x" h' |: h - /****** Copyright (C)2020 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 % N" V" e& m1 v8 r
2.2 stm32f10x_it.c里的中断服务函数1 Y$ W' ~/ i5 Y' b+ t# t8 ]
USB_LP_CAN1_RX0_IRQHandler()函数主要模拟接收到STM32F103C8T6开发板的CAN信号,并根据ID号判断是接收了广播模式下的转速信号还是收发模式下的车速信号,具体代码如下:
! l$ M Q/ ]: J2 \3 S
' y$ T4 d! R, U- J+ z/ E- u8 canbuf[8];
; N# b0 f4 c/ x0 u, t/ Z - #define stdid_broad 0x17C
! G8 r; u2 D6 Q1 m - #define stdid_rs 0x7E8# h! |* v1 s6 H5 A
- void USB_LP_CAN1_RX0_IRQHandler(void)2 g) `+ N5 z0 |9 ]
- {3 J2 s6 ?% V: @
- u16 rpm;: \0 m! P% d0 u
- u8 v;8 }- H7 x* r& a! z4 D$ p
- CanRxMsg RxMessage; / j" v, z/ c) x8 B3 Q- k5 ~$ D
- int i=0;
6 l+ r9 q( V' S( `; ` - CAN_Receive(CAN1, 0, &RxMessage);
- X6 C% |3 o2 n4 ~4 O$ t - if(RxMessage.StdId == stdid_broad) // 判断接收到的CAN信号的ID号是否为 0x17C
. @& \1 m# ?3 I9 q$ m - {* g& }# E- b/ G; ^: G
- for(i=0;i<8;i++)canbuf=RxMessage.Data;( Z+ D) u1 j4 v3 l! F0 g+ d' Z( B
- rpm = (canbuf[3]<<8|canbuf[4])/4;/ D: E6 a; N7 @4 c
- printf("\n接收到的汽车转速为:%d rpm \r\n",rpm);
/ ^# ] Q" s, f% t: Y( B# j* m/ c - }
5 n0 C' s% V' D/ @) A7 I- S7 H+ S% X9 F - else
% a/ u" q H* g6 @2 M* c1 S' C - if(RxMessage.StdId == stdid_rs) // 判断接收到的CAN信号的ID号是否为 0x7E8 o& ^- g8 c/ J: W2 Q5 e
- {5 I& c v9 C3 [% |5 j
- for(i=0;i<8;i++)canbuf=RxMessage.Data;. d, `3 |. j' B: C0 }3 d0 U, Q
- v = canbuf[3];
$ D- U6 a: U* T- D - printf("\n接收到的汽车车速为:%d kph \r\n",v);
2 p( J! m3 S4 U, D - } % s {4 V7 ^5 ]7 C- P# k
- }
复制代码 & ^1 |/ T9 I2 y2 R
2.3 STM32F103ZET6开发板还包含了串口通讯函数,主要用来与电脑通讯,显示运行结果。这些知识可以参考我以前写的文章。% @6 q0 `; C) @" p6 H
; l+ r3 i0 @! j/ G- K实验结果) I' `$ [9 E: O8 v- G
实验结果如下图所示,系统运行后,将STM32F103ZET6开发板的串口接到电脑上,可实时看到从STM32F103C8T6工控板传来的转速信号,当按下STM32F103ZET6开发板的按键时,可从STM32F103C8T6工控板接收到一次车速信号。" C3 D% G ?: V* F
. d( l1 Z" ~+ O' A
& t3 f0 e# {% q/ b1 s+ _ X备注:
Y4 i) _# p+ B% n" }( q1)对于CAN的基础知识可以参考之前我写的文章;
d2 @7 @& W4 P0 e) {: n6 |2)汽车CAN总线知识参考ISO 15765-4,ISO 15031-5,SAE J1939这些标准。
, n+ }1 z0 o4 Y6 S# y0 N————————————————
) Z0 h% K$ _0 J( ~* z) V9 c' g: ?版权声明:天亮继续睡
. w# ^: `/ w5 M/ }+ _6 q0 z8 a! s5 d1 ` E* M& g- s
% e# \+ C# o2 r- Z% `
~( s$ ^0 r: w& } |