配置CAN$ z& }' ]; W3 e0 ~
先了解几个关键词简称:& r$ a$ _8 q& I
( f. g! Y' |; u- E6 q+ f最小时间单位(Tq,Time Quantum)2 }. Z i. E5 d! J# b! p
同步段(SS,Synchronization Segment)1tq7 P& V+ j/ z' o: z" \
传播时间段(PTS,Propagation Time Segment)1~8tq0 |: M+ q. G( H1 c
相位缓冲段1(PBS1,Phase Buffer Segment1)1~8tq
' _0 e6 Y! [4 Y1 k4 e. `, [8 F相位缓冲段2(PBS2,Phase Buffer Segment2)2~8tq. s- r2 \$ Q( v; _0 `
再同步补偿宽度(SJW,reSynchronization Jump Width)1~4tq
& g# b7 _1 ?" J# c0 y7 [+ R* P" U波特率分频器(BRP,Baud Rate Prescaler)
2 s( T* d: t0 ^STM32把传播时间段(PTS)和相位缓冲段1(PBS1)合并了,形成了时间段1(TS1)。' O4 \5 e/ G8 [2 ^. j
r; ]: R. s- ?/ \0 Y8 FCAN位时序寄存器(CAN_BTR)用于设置TS1、TS2、BRP、SJW等参数,这些参数直接决定CAN的波特率。
. _5 K2 z: w; R8 r* r, Z: Y4 N9 T1 @
0 k) s2 |$ g( U- y
( F* @* F4 @5 l2 {3 [2 z
SJW[1:0]再同步补偿宽度+ M U* i. Y" x. W) G- M0 T9 E0 J
+ m( g2 K6 K9 V5 ^
TS1[3:0]时间段1
2 o8 {& u' S! s* z3 h- G$ I) I q0 q+ @" @4 R7 y
TS2[2:0]时间段2
9 r) n) W' l& C1 w8 B; H; R0 I6 k
$ P" i6 b* A! V; zBRP[9:0]波特率分频器
. y b" U$ C9 l* Z% X
$ ]& ?) M( C1 O4 ?; s可以看到没有同步段(SS段),这是因为STM32已经将SS段固化为1。
! z* d8 F6 f% Y' z8 n6 q* C& F, d& h8 A# Y- s L
除去这几个参数,还需要设置分频,STM32F1系列一般是跑到72M主频,CAN是挂在36M的APB1时钟上,设置CAN的分频就是分的这个36M。
& l& m2 R% {9 }! J; f8 s1 X% I* m4 O. [, J- F: k8 l9 c
下面这张图是波特率计算公式:) V' W2 c# O ]5 z0 A$ `6 \% R
" R( \5 ?0 K4 }7 z v# ]
4 S" s- }4 y5 o% b
9 B) W6 M% e0 O$ C+ M
整合一下波特率计算公式就是这样的:6 c( |0 h: @, z. x' F% P
. R" D- p- _0 e8 W5 Y) |, a5 {
波特率 = APB1 / [(1 + (TS1+1) + (TS2+1)) * (BRP+1)]: ^% V; `) r' q5 j0 r
L5 T/ p P" _+ `
在简化就是:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + ss)
0 F/ E# V/ K" U. S1 i
7 }8 X% \( Q2 X( _) p1 i其中SS就是同步段,已经恒为1,所以:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + 1)! B5 D1 D# C. V
- l k8 c4 U( h4 \4 t
下面我们开始实际设置波特率,这里要注意,CAN的波特率最大为1Mbps。
) I: o, h4 O8 r* b8 E( l9 N" A) f; f2 i/ ~5 ~$ E+ y4 \- Q4 F
stm32f103的CAN的时钟主频是36M,分9频就是4M,设置tq1=5,tq2=2,ss恒等于1,那么:波特率 = 36MHz / 9分频 /(5 + 2 + 1) = 500KHz
0 ]) w* r, |( N6 `& @, }. ~7 \' j$ A! @& a1 v
另外还有一向是:再同步补偿宽度(reSynchronization Jump Width) 这个参数,其实就是一个由数个Tq组成的一个段,用来对同步误差进行补偿,可以简单理解为为了提高精准度的,例如两个CAN进行通讯时由于两个板子的晶振可能存在误差从而导致CAN的波特率没有500K那么精准,所以就需要设置一个补偿参数去修正,这个参数就需要根据你实际的板子情况去调整了。
! ^/ N: Z3 x+ ]9 D
# h) Z% M$ K" W: o- X5 X, t2 k注意:stm32cubemx生成的CAN代码是不带过滤器的,需要自己手动添加。+ c8 D. D9 V9 M" H8 k- ]' e
- \- r+ i) t% D- T$ w
, W8 P f% J$ p p& X
5 y8 H$ X$ V# _: _ H, t
6 `7 p8 r' c" _% n- J( |+ N- E# T- ^9 x) v
代码修改. |/ Y: _6 W; ^1 g6 b' C
STM32CUBEMX生成的CAN配置代码是没有过滤器设置的,需要手动添加。" B; K8 V4 q/ N6 s5 s ^% V5 e
. u i) v: D" G' T: v0 A- typedef struct) |3 Q0 h2 @: S N4 V. `; w
- {1 N0 U( o! w n' {
- uint32_t mailbox;! T7 M! V) k* F1 z+ B k5 G" ^
- CAN_TxHeaderTypeDef hdr;( F& ]! e" O3 f% J. |: E: Q
- uint8_t payload[8];- q |( O" d5 `% \, m) o
- }CAN_TxPacketTypeDef;5 Z! P x1 m |8 f& \) W! J
- & s( f# @ l p& x9 R$ _; h& \
- typedef struct
; M$ g% o, S6 b3 R; u: ` - {9 N, J& p. S: S4 d- D: h
- CAN_RxHeaderTypeDef hdr;# I' |* P n2 I1 _! _; O
- uint8_t payload[8];
0 ?# N2 ]- \5 g, U7 A0 w S4 ~ - }CAN_RxPacketTypeDef;
复制代码- /// CAN过滤器寄存器位宽类型定义
) g. T* P" U6 y7 Z0 W( I - typedef union1 W! }! l* C6 J d1 I4 Z
- {
1 B" R2 y/ |: _/ m - __IO uint32_t value;
4 T! M" q8 e3 W4 {! F0 ? - struct
% v0 A7 |( s8 Z% ]" e - { V' U: m2 Y5 N# A' S, \( e! a' _
- uint8_t REV : 1; ///< [0] :未使用& N3 O- ^# W- w( G# r
- uint8_t RTR : 1; ///< [1] : RTR(数据帧或远程帧标志位)
, X) I; P' i4 L# S. e& s q4 V1 _ - uint8_t IDE : 1; ///< [2] : IDE(标准帧或扩展帧标志位)
9 O; B1 c/ R n, s% x1 F T - uint32_t EXID : 18; ///< [21:3] : 存放扩展帧ID
0 E5 c( X$ D8 _4 s+ A$ O - uint16_t STID : 11; ///< [31:22]: 存放标准帧ID
4 d" g: a6 j9 K# y - } Sub;6 s1 q! }1 a) O! R Y
- } CAN_FilterRegTypeDef;- m0 I& n; s0 F% e4 w
4 T( }* y/ s) ~ v4 o8 J
. }. {3 Y4 ?3 c' I& G* z5 g8 l, O- #define CAN_BASE_ID 0 ///< CAN标准ID,最大11位,也就是0x7FF' L4 N9 j1 g, X( t- H
- $ f/ Q4 X& b0 w9 w5 j
- #define CAN_FILTER_MODE_MASK_ENABLE 1 ///< CAN过滤器模式选择:=0:列表模式 =1:屏蔽模式
7 }/ f N6 y( z9 s - + P3 q' s; o+ D" `0 a) U
- #define CAN_ID_TYPE_STD_ENABLE 1 ///< CAN过滤ID类型选择:=1:标准ID,=0:扩展ID0 Z& C0 P) l5 b _7 z
- 1 V7 g* a+ q/ p' o/ `% Z2 K
- void CAN_Filter_Config(void)* h+ _+ d4 d0 ]& g1 `
- {
R5 N( A3 w/ ? - CAN_FilterTypeDef sFilterConfig;
L% i/ r+ X8 L+ @( P - CAN_FilterRegTypeDef IDH = {0};
7 l) l' I/ ~) p0 M/ U) @+ n2 G - CAN_FilterRegTypeDef IDL = {0};0 I+ j! G$ G% T9 l
- ; B9 ]+ q" x w* _
- #if CAN_ID_TYPE_STD_ENABLE/ i" t- i# F8 Q4 Y, W" m4 M
- IDH.Sub.STID = (CAN_BASE_ID >> 16) & 0xFFFF; // 标准ID高16位
( G6 H. E9 z8 J; u: x* V - IDL.Sub.STID = (CAN_BASE_ID & 0xFFFF); // 标准ID低16位! N$ T+ `2 U0 I: [
- #else5 z! b1 J8 [& Q# y8 y# d
- IDH.Sub.EXID = (CAN_BASE_ID >> 16) & 0xFFFF; // 扩展ID高16位
! k6 U9 ~6 e3 e8 h2 E - IDL.Sub.EXID = (CAN_BASE_ID & 0xFFFF); // 扩展ID低16位
" [9 ^8 N! o$ {- |0 S* J - IDL.Sub.IDE = 1; // 扩展帧标志位置位6 E! `7 e* _ P1 |( ?* G5 C
- #endif
6 B0 a, o- |1 C( I- d - sFilterConfig.FilterBank = 0; // 设置过滤器组编号2 n6 _9 O4 t; J8 P3 q
- #if CAN_FILTER_MODE_MASK_ENABLE+ R, X0 {7 v+ ~! L9 X. P1 M3 x2 m
- sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式/ N- Y. N: I) y- h2 ~ g* g) |
- #else
! o/ N5 f7 }0 n" t' ?5 h - sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
- W+ a( X0 G4 `# I, P - #endif
. p* `4 Q ?; C' F7 }5 T - sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
" v; ?8 p4 q0 i3 z5 G/ x - sFilterConfig.FilterIdHigh = IDH.value; // 标识符寄存器一ID高十六位,放入扩展帧位
7 s: \) H5 U9 y# V- l4 Z9 U# Q n - sFilterConfig.FilterIdLow = IDL.value; // 标识符寄存器一ID低十六位,放入扩展帧位+ ^' Q- g1 x4 @; P$ }% W
- sFilterConfig.FilterMaskIdHigh = IDH.value; // 标识符寄存器二ID高十六位,放入扩展帧位) M) J$ Y; v# b- b
- sFilterConfig.FilterMaskIdLow = IDL.value; // 标识符寄存器二ID低十六位,放入扩展帧位
% _5 S& f! Q/ R1 d8 y - sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器组关联到FIFO04 `% e# O% s! H% U4 E4 h3 {* }. W
- sFilterConfig.FilterActivation = ENABLE; // 激活过滤器& T; d* u% V/ c) U! y1 W4 O) g* P6 [
- sFilterConfig.SlaveStartFilterBank = 14; // 设置从CAN的起始过滤器编号,本单片机只有一个CAN,顾此参数无效
0 J1 }# o* ]- Q# A& r% _' g/ Z, n - if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
( ?/ r c/ N5 y& u! e+ j - {; [' Z0 _: Y& V+ |0 h$ x! A4 r( H
- Error_Handler();
9 R$ g i U7 Z - }
2 Y/ s- H/ c8 _' {# u% d - }' T( C0 Y F- X' A4 z$ N
2 K% J2 _, j0 N( @- uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet)2 V& N3 p: P( y
- {
* q( U; [8 T- ]- [ - if(HAL_CAN_AddTxMessage(&hcan, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)3 |3 D0 I+ [) e! N2 Q* v# ]
- return 1; D( j4 x. x% B f! P u* c# u
- return 0;4 L [, ~6 m! l) P3 I7 \2 V: p
- }
# X, x l& u: ~* Y. `+ Y# z$ b - $ u3 W4 ?. q0 c* ?. I2 t
- void CAN_Init(void)
& m! J+ O, q w v0 u ` - {
6 b2 g: a. g. V4 T - MX_CAN_Init();
% V1 j+ r7 ^3 G% `1 H - CAN_Filter_Config();/ G- w8 ~& g8 L Q
- HAL_CAN_Start(&hcan);& Z+ k; i0 A- C
- HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能CAN接收中断# t9 B k4 v3 m2 a1 ^
- }% x& i X9 G+ `, j
- 6 i6 B; w8 ~9 C: i: w
- void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
' Z$ a$ B$ @% k; I- B" ]/ r - {
8 B/ H2 X+ T/ }- g* n- J, o - static CAN_RxPacketTypeDef packet;
9 O, ~- }# L8 z b8 q8 h4 d - ; Q: N6 r' x2 `! b$ m0 _
- // CAN数据接收2 k3 b9 U, X0 T2 ?& n3 w% X
- if (canHandle->Instance == hcan.Instance); p2 i$ X8 a- U; [% `7 i) h
- {
* e% W% |+ e8 B0 T+ \/ y - if (HAL_CAN_GetRxMessage(canHandle, CAN_RX_FIFO0, &packet.hdr, packet.payload) == HAL_OK) // 获得接收到的数据头和数据! i8 B s' s2 P! F& j( f+ A
- {# r; _, t: J9 A, } C! w3 W
- printf("\r\n\r\n\r\n################### CAN RECV ###################\r\n");% t+ d$ d+ M; N: D+ Q2 H
- printf("STID:0x%X\r\n",packet.hdr.StdId);+ |) ]/ _. G! H/ D! A
- printf("EXID:0x%X\r\n",packet.hdr.ExtId);
' @$ E: Z( O: {6 N* T: C - printf("DLC :%d\r\n", packet.hdr.DLC);
* x- ~, N1 y# p# N8 H% q+ z9 t - printf("DATA:");
9 b3 X& J$ x2 C - for(int i = 0; i < packet.hdr.DLC; i++)# w) ~# q& O3 i* n" H
- {
" l7 v/ x0 Z* c - printf("0x%02X ", packet.payload<i>);: P& ]% } t# r9 T
- }
' D7 H' H3 X* n( k - HAL_CAN_ActivateNotification(canHandle, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中断
# {. y( V# z/ c4 X: D, X4 ^ - }# o7 j; j' H; ?1 I
- }
8 G9 O6 c7 x4 n4 o2 R1 Q. V2 U - }</i>
复制代码- 6 i1 f' v3 o4 z$ k6 D. ^
- CAN_TxPacketTypeDef g_CanTxPacket;
3 a/ q5 r2 }, Q - : a f2 r- p" x2 s
- void CAN_SetTxPacket(void)9 ~ R0 {/ }, i1 E
- {
% s( n* I+ Y2 ^+ [& V; e - g_CanTxPacket.hdr.StdId = 0x321; // 标准ID
$ Q# N" X! Z& S+ A& X& [ - // g_CanTxPacket.hdr.ExtId = 0x10F01234; // 扩展ID' |. y; m; J7 i$ c
- g_CanTxPacket.hdr.IDE = CAN_ID_STD; // 标准ID类型
4 x# I W9 P% r, D* `: i9 R - // g_CanTxPacket.hdr.IDE = CAN_ID_EXT; // 扩展ID类型
; E! O; h6 e+ T* c G0 Y; x - g_CanTxPacket.hdr.DLC = 8; // 数据长度
- `+ v- I' Q# q. g+ z& ~! W% F - g_CanTxPacket.hdr.RTR = CAN_RTR_DATA; // 数据帧
; T: e+ H% i! ` ^; g$ J9 q8 l - // g_CanTxPacket.hdr.RTR = CAN_RTR_REMOTE; // 远程帧8 b8 r# D7 `, O; k& c# u# \, \( Y
- g_CanTxPacket.hdr.TransmitGlobalTime = DISABLE;
/ ?0 `# x4 n/ Q) B -
) E, W9 P- I( l* ]; o# y3 E4 Y - for(int i = 0; i < 8; i++)4 o/ w2 J: V# o
- {
5 x4 c2 W0 T% o; i9 l, l - g_CanTxPacket.payload<i> = i;9 n/ x) S, W2 N9 X0 V+ t2 D, t
- }
: G( b9 R! ~5 l% p+ ~. Z - }
1 Z$ j/ h* t7 F6 a5 o/ P
# L' }1 M# J9 [: G( [' ^. h- int main()* A) K. T0 U- h
- {
# o7 v8 h, J( w8 L& K f6 N5 s - HAL_Init();
1 Z4 d# a5 N1 R' Y6 j1 n2 W - SystemClock_Config();( i7 T2 c6 s1 S9 H
- MX_GPIO_Init();
8 h4 S d6 ^6 }7 e' S" ] ]+ S - MX_USART1_UART_Init();
5 \- ^7 P8 h' x; L+ B. v - CAN_Init();+ U* ?- _! L0 y0 Y- t+ F! X' T
- printf("----------------------------------------\r\n");. N& v- }9 T3 r; k+ K
9 ^. q6 h# v$ g; k- CAN_SetTxPacket();
, e6 u. |: s) g$ _* l/ C - d+ \% @+ a1 o, l2 [/ {
- while(1)
$ n" l+ M% }4 X' e. [0 G - {" x- @. B1 }6 n8 A! K
- if(CAN_Transmit(&g_CanTxPacket) != 0)
7 g9 L" K6 y$ W. x5 C - printf("failed\r\n");& A9 C2 C$ d6 [' \/ R" U* _
- HAL_Delay(1000);8 F9 E3 v- |+ q
- }0 j1 m/ Y# y( g1 E. _% E
- }
4 }+ I7 a2 o& ?
$ e- ?" x `: S3 ^7 t
复制代码 ! g4 O7 z, X! o& r) I7 B# }( y
$ ?% O6 v# B* i9 q, g |