配置CAN$ P# n1 h, I+ G: A6 S
先了解几个关键词简称:
q) w0 w1 P/ H! G7 _$ F' E+ Y) K0 ?4 I K9 [- ]/ c
最小时间单位(Tq,Time Quantum)
$ c' b% C; B% d" _2 e, _: k同步段(SS,Synchronization Segment)1tq2 I9 _) @" g: k* G) z
传播时间段(PTS,Propagation Time Segment)1~8tq" m- b' p5 X$ z
相位缓冲段1(PBS1,Phase Buffer Segment1)1~8tq: e% H; i2 [' R' b$ w3 L" C1 E0 B& E
相位缓冲段2(PBS2,Phase Buffer Segment2)2~8tq
0 {5 c ?* P* \, n- O! {再同步补偿宽度(SJW,reSynchronization Jump Width)1~4tq
# z7 r. z" C7 m C. J3 J波特率分频器(BRP,Baud Rate Prescaler)
9 u- I! L6 d( gSTM32把传播时间段(PTS)和相位缓冲段1(PBS1)合并了,形成了时间段1(TS1)。, t) r- m7 R$ H4 Z
, k4 M8 H7 P3 y* z3 u; b$ u
CAN位时序寄存器(CAN_BTR)用于设置TS1、TS2、BRP、SJW等参数,这些参数直接决定CAN的波特率。6 _* w, C) d+ E6 `4 B" M8 B
) k3 ~/ d( w1 d0 P8 y8 Y
, Y# ^( z( G( g- Y1 H
4 J# o# g0 C9 K7 f
SJW[1:0]再同步补偿宽度- ^! \. L2 k C2 H! C
9 T& r% L9 ]: z6 a* M
TS1[3:0]时间段13 V# P/ o- n X: y8 S
4 j ?* Y8 j6 r9 _' |1 x* B) bTS2[2:0]时间段2; x0 X" F! ~; [9 S& L9 t( K2 F
5 G) b+ `5 ^3 x% G
BRP[9:0]波特率分频器
8 f; c: ^( |" z& `# ^9 E; K
, [9 }/ u2 ~( z! W! w% a7 n6 b- r可以看到没有同步段(SS段),这是因为STM32已经将SS段固化为1。7 y) }7 r; M. m- O4 C) \' G" ]
M0 f, w- s" u2 H. l, Z除去这几个参数,还需要设置分频,STM32F1系列一般是跑到72M主频,CAN是挂在36M的APB1时钟上,设置CAN的分频就是分的这个36M。
# K; r, K: r1 u5 L) B0 b6 _& D5 M5 i* i9 v/ i+ E3 Q) @6 Z
下面这张图是波特率计算公式:
- @. j. F+ W$ m" j
5 G+ ]$ c: ~9 _+ ^3 r
* t% x$ d! v1 q/ y$ ?& T3 B; {9 L O4 k6 I0 b3 b+ w6 G1 V! p$ B( C
整合一下波特率计算公式就是这样的:& v& Q# J9 i0 b4 u {
7 q2 n% a3 t/ Z4 n; ^- T( H
波特率 = APB1 / [(1 + (TS1+1) + (TS2+1)) * (BRP+1)]' @$ ^1 e4 w2 s, ]- t; `* ~- b
0 H& R% I4 i- c5 G/ v6 S
在简化就是:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + ss)
: z8 l0 k! s* d# Z. p: e6 H% Y& [/ g; S" O
其中SS就是同步段,已经恒为1,所以:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + 1)+ E9 j* h4 K3 ~7 s
0 r# y7 e$ G- D7 ^8 n+ U2 ~
下面我们开始实际设置波特率,这里要注意,CAN的波特率最大为1Mbps。
5 F: z1 l' H, R& b7 D- I) q4 b9 F6 p$ O8 @ P
stm32f103的CAN的时钟主频是36M,分9频就是4M,设置tq1=5,tq2=2,ss恒等于1,那么:波特率 = 36MHz / 9分频 /(5 + 2 + 1) = 500KHz I/ E8 {9 L, W1 s0 Y# `' P2 n( Q
9 |% t# T, \/ e8 ]另外还有一向是:再同步补偿宽度(reSynchronization Jump Width) 这个参数,其实就是一个由数个Tq组成的一个段,用来对同步误差进行补偿,可以简单理解为为了提高精准度的,例如两个CAN进行通讯时由于两个板子的晶振可能存在误差从而导致CAN的波特率没有500K那么精准,所以就需要设置一个补偿参数去修正,这个参数就需要根据你实际的板子情况去调整了。
5 l- W E( H4 v
4 E" d" M w" b: J7 B注意:stm32cubemx生成的CAN代码是不带过滤器的,需要自己手动添加。
! ]) n) d! _7 K Z/ T. C/ B6 W9 Q$ ^$ g; Y: `2 M
! H3 n4 Z2 k9 i F: A
) A8 h0 V4 T2 ]
" K' {( _% L) ~) V7 ^1 {' {
4 [0 e* `& W" J) t: j3 A代码修改
; S9 I- f) j+ {7 o Y8 e# [6 }( JSTM32CUBEMX生成的CAN配置代码是没有过滤器设置的,需要手动添加。
; C; _# j) u8 Q1 @- x/ r2 V4 c) s! F1 b: {6 C+ H5 W. R
- typedef struct9 X& Z9 ?- m F7 i
- {% T4 C4 L8 t3 N, Y) V
- uint32_t mailbox;
$ {$ ~/ c- C: Y, g3 K - CAN_TxHeaderTypeDef hdr;
; A+ j/ s. r- ?& u _ - uint8_t payload[8];
4 [" ^% g- o) a5 D6 B7 _% _$ Y h N - }CAN_TxPacketTypeDef; {+ ?1 g. C8 [+ k% O
- 5 E5 q$ W4 q( B- `5 S. `' X
- typedef struct" v+ P/ r2 x+ N$ L0 c8 p
- {
j' k' K! B Z5 j7 |6 j - CAN_RxHeaderTypeDef hdr;- `1 s, q9 d, y$ i% m. [
- uint8_t payload[8];
' }, B7 p4 Z3 g3 [ - }CAN_RxPacketTypeDef;
复制代码- /// CAN过滤器寄存器位宽类型定义) k( b8 i- Q' R* T, C; B8 @
- typedef union1 J, N& |+ w1 r/ L% I% V C: d4 }
- {3 y( Q8 n: D1 U, e$ z% j
- __IO uint32_t value;
. \8 h d& K* R5 T - struct
6 @8 \9 d2 n& E0 d - {; I' o6 X3 j' y8 y I
- uint8_t REV : 1; ///< [0] :未使用4 F2 G7 v8 a* E8 _
- uint8_t RTR : 1; ///< [1] : RTR(数据帧或远程帧标志位)
% [1 H8 B5 s8 ?/ v: ? - uint8_t IDE : 1; ///< [2] : IDE(标准帧或扩展帧标志位)7 }0 U; z* b9 X# F0 X
- uint32_t EXID : 18; ///< [21:3] : 存放扩展帧ID6 e9 a7 g( O2 ~, \ V9 f
- uint16_t STID : 11; ///< [31:22]: 存放标准帧ID `9 J1 h: s( V6 ~3 U; D
- } Sub;
; h s# y2 D- V4 H - } CAN_FilterRegTypeDef;2 ]! `' w& U$ x) X7 J& `
- 0 h0 f" I7 S, S" l
0 P' v6 U& s% n" _6 @- #define CAN_BASE_ID 0 ///< CAN标准ID,最大11位,也就是0x7FF
5 w$ ~4 @; `5 |( t) ]
1 E# B& Y" v" |% J) F$ A- #define CAN_FILTER_MODE_MASK_ENABLE 1 ///< CAN过滤器模式选择:=0:列表模式 =1:屏蔽模式0 Y. o# P- Z0 }6 D6 V
- 3 R) K X! o0 h$ |! B$ O$ o
- #define CAN_ID_TYPE_STD_ENABLE 1 ///< CAN过滤ID类型选择:=1:标准ID,=0:扩展ID6 t7 o, W8 D+ E- s
6 d! d1 r% ?9 e5 m) Z {- void CAN_Filter_Config(void)- p; `, K0 G! u
- {
! @( F. f; f7 w; g4 j- n) Y - CAN_FilterTypeDef sFilterConfig;
( ^, i/ q7 U/ k8 `$ {# q) Y- G - CAN_FilterRegTypeDef IDH = {0};
7 c" Y3 O$ s: H2 A1 i - CAN_FilterRegTypeDef IDL = {0};$ U! u$ N8 B& |6 ~" H
1 X2 X3 Q1 D" e; `- #if CAN_ID_TYPE_STD_ENABLE3 l3 M% w3 a. j1 p! r _& Y) e
- IDH.Sub.STID = (CAN_BASE_ID >> 16) & 0xFFFF; // 标准ID高16位
% \4 ]% [# L$ t5 Y - IDL.Sub.STID = (CAN_BASE_ID & 0xFFFF); // 标准ID低16位
) t5 @9 C8 R# W8 ^+ R( V0 s - #else4 l1 ?/ U9 d: i
- IDH.Sub.EXID = (CAN_BASE_ID >> 16) & 0xFFFF; // 扩展ID高16位2 a9 A; r* W* H8 s
- IDL.Sub.EXID = (CAN_BASE_ID & 0xFFFF); // 扩展ID低16位0 `0 J4 E) h% y3 M& e
- IDL.Sub.IDE = 1; // 扩展帧标志位置位$ X6 g* I3 Q- b4 n4 f$ ^
- #endif: H! Z+ C9 D+ L6 \, U
- sFilterConfig.FilterBank = 0; // 设置过滤器组编号
: E7 X1 P1 Y1 _# Z i0 o - #if CAN_FILTER_MODE_MASK_ENABLE
9 i9 J$ C" |$ T6 H8 ~' @; X - sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
; I* p2 O- U- Z: ^ - #else
7 _6 t. m P, @1 m3 o# j( D$ n* z/ a - sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
5 _# x0 D. f4 t - #endif+ [2 W& ]& [$ N4 ?2 U
- sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽) D+ E; s; B( M0 h1 y
- sFilterConfig.FilterIdHigh = IDH.value; // 标识符寄存器一ID高十六位,放入扩展帧位
- G4 f! T! b5 `! E - sFilterConfig.FilterIdLow = IDL.value; // 标识符寄存器一ID低十六位,放入扩展帧位' ~) j0 _! `2 e% d* d
- sFilterConfig.FilterMaskIdHigh = IDH.value; // 标识符寄存器二ID高十六位,放入扩展帧位
8 X# F u$ Q3 S( a6 @' }6 f - sFilterConfig.FilterMaskIdLow = IDL.value; // 标识符寄存器二ID低十六位,放入扩展帧位
$ G4 n- | G; j0 O6 C6 [ - sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器组关联到FIFO0
7 ]7 B5 T7 e- Y8 ~. i; j6 O/ G - sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
4 A9 n2 s% m) t0 x+ b, Q. W/ U. r - sFilterConfig.SlaveStartFilterBank = 14; // 设置从CAN的起始过滤器编号,本单片机只有一个CAN,顾此参数无效
9 J- T% ?* K( S9 y - if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
: u# U; ^$ B7 x) F# {( U! [0 g - {
4 L" r% L. J7 d3 Y. W - Error_Handler();
# [( P5 F( D4 L2 W! `( X' }- ~ - }0 z7 K4 i8 V" E/ \. k j) t8 \
- }4 V0 L( Z* z, ?8 M# L! [
- 4 b; W$ s2 j8 i' J& s
- uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet); |) T' ?5 F! [5 Z G8 N. h; l- I8 }
- {! V' l s; J2 `8 ]' Q$ N
- if(HAL_CAN_AddTxMessage(&hcan, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)
- S$ T0 F8 V- @0 O! f% F6 n X( m8 q - return 1;/ ~. D2 j/ ]& D! ^
- return 0;
$ f3 R0 a" j! p/ b, j9 K) b - }
, ]' L( ` M/ G - ( e9 L$ k L9 o
- void CAN_Init(void)- @: F3 |$ I2 E. U1 G
- {
z/ W* [3 x" h/ s. U1 P4 ]! c9 J6 \ - MX_CAN_Init();
M) M* c" D9 g - CAN_Filter_Config();
+ w$ k* y8 y' [ - HAL_CAN_Start(&hcan);
1 }1 ]. J. F0 s$ K- H3 ?8 B - HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能CAN接收中断. P3 d! U n0 J1 o1 [
- }- X. ^4 _! r& F: J& g+ m o
- 3 o5 I2 X6 r( i' M6 w" D
- void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
4 G! \4 w% j; R - {6 `1 d: L2 Z+ M: Y7 Y8 k
- static CAN_RxPacketTypeDef packet;' k) i" x [, c$ M$ |
-
. k" d5 M! V+ W" L, B - // CAN数据接收
$ d5 q, H' l8 s" L8 |3 b6 E - if (canHandle->Instance == hcan.Instance)$ V# M6 S. T; R( C- }
- {
! I3 V/ A, t9 e& U - if (HAL_CAN_GetRxMessage(canHandle, CAN_RX_FIFO0, &packet.hdr, packet.payload) == HAL_OK) // 获得接收到的数据头和数据
/ }. g9 T8 I) i9 g+ q - {+ l! |' M0 z) ]2 D$ Z
- printf("\r\n\r\n\r\n################### CAN RECV ###################\r\n");7 g0 J: I1 J/ a3 u ]8 O: B
- printf("STID:0x%X\r\n",packet.hdr.StdId);7 Y/ A3 K' |: f8 d
- printf("EXID:0x%X\r\n",packet.hdr.ExtId);
* n8 Q" ]' g7 |; n9 @% g+ x - printf("DLC :%d\r\n", packet.hdr.DLC);/ K+ k$ d9 p3 q8 }8 n
- printf("DATA:");/ X6 ^5 B. n9 M, K! w. C" {
- for(int i = 0; i < packet.hdr.DLC; i++)
. X7 j% [2 j7 T! A7 b% V6 Y, T - {" B. l% }) `$ i. i) @
- printf("0x%02X ", packet.payload<i>);
1 M# B. l3 b& r- r - }
" Y5 x- U9 _, i6 t$ {+ @5 Q+ d - HAL_CAN_ActivateNotification(canHandle, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中断
7 G4 t0 v% w7 B - }
7 w7 B* s- T, r- N0 W - }
+ u9 s( v4 [" D# z+ e" b - }</i>
复制代码
/ _8 T. _6 j% G( x& r+ V! o! t- CAN_TxPacketTypeDef g_CanTxPacket;
+ X9 [$ m i- j - 5 u, o* \* ]) K4 i: V3 {
- void CAN_SetTxPacket(void)- b$ u3 c2 R& k, o7 z( t5 I
- {; t/ S' F& x' J; x
- g_CanTxPacket.hdr.StdId = 0x321; // 标准ID
: [! `0 \2 \% Q! ^! k9 f$ {5 _+ J- x - // g_CanTxPacket.hdr.ExtId = 0x10F01234; // 扩展ID
; s9 O1 B9 ^) l% K! I6 X - g_CanTxPacket.hdr.IDE = CAN_ID_STD; // 标准ID类型& N% r# H: Q7 }8 u- t& f
- // g_CanTxPacket.hdr.IDE = CAN_ID_EXT; // 扩展ID类型
; f1 V2 k+ @$ G, K4 ^/ n6 f - g_CanTxPacket.hdr.DLC = 8; // 数据长度
$ E4 o+ ~% J' X4 J/ o. x! }$ h - g_CanTxPacket.hdr.RTR = CAN_RTR_DATA; // 数据帧
) K J. w% n) x& C9 s - // g_CanTxPacket.hdr.RTR = CAN_RTR_REMOTE; // 远程帧( t9 P* s+ G4 X: t
- g_CanTxPacket.hdr.TransmitGlobalTime = DISABLE;+ i$ A4 T) p" P) b+ x
-
_. O. q, ~" m5 t - for(int i = 0; i < 8; i++)+ j8 W& v6 d0 W* \8 D+ C5 }, E
- {
7 _2 N1 g6 U9 B; Q/ g5 d - g_CanTxPacket.payload<i> = i;$ u& }, ~6 r6 ^/ k# N
- }
4 u1 G$ S/ O8 Y, U8 S7 K - }
% }0 a# b: t, M5 p - # A- q J) }% q$ c w
- int main()' Z7 U5 t: l7 _ {' N; ]2 a8 U6 Z l
- {3 _1 K8 F' P# M( S5 q# H
- HAL_Init();* G& [' [/ k0 v% n
- SystemClock_Config();
5 E/ N$ N% I: f$ |) l; E6 H - MX_GPIO_Init();
: R! c* |" F n' f, E$ P - MX_USART1_UART_Init();5 _+ `" o/ ]* e6 P! \, H! G* x
- CAN_Init();
. {5 W% |3 O) S$ M" D - printf("----------------------------------------\r\n");7 g& Y1 @) c" {8 V9 e$ F' M
- - q9 Q- {- R4 p+ V( c3 t
- CAN_SetTxPacket();: @& B7 k; V( B+ s- w/ N/ `$ ^, `
- 4 L" b2 A& j1 v0 ]3 I! E, {8 z
- while(1) I0 I4 [4 A( _ v8 S* X
- {' C( p3 Q# b, h: f3 `- S6 f) O8 ?
- if(CAN_Transmit(&g_CanTxPacket) != 0)) d- r9 [( X7 v# _+ r
- printf("failed\r\n");
, e$ X; z g, b& x9 e- i - HAL_Delay(1000);4 B5 F* v% l) X+ W. A) D+ |! h
- }
; | j/ ]2 e5 b7 \# W: L - }
) Y# |8 m" U' ?2 Z- M: c
v; u7 j4 _0 x3 ~4 L1 H
复制代码 ! G$ m' v4 H6 z5 L, @0 R
, T' O+ @+ Y" G0 E( f( I |