请选择 进入手机版 | 继续访问电脑版

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

【经验分享】STM32 基于 Nucleo 板 CAN 总线的建立

[复制链接]
STMCU小助手 发布时间:2022-2-12 18:48
前言0 h3 R! a& V- {, ~, S" r
STM32 NUCLEO 开发平台是 ST 最新发布的易用性好、可扩展性佳的低成本平台。开发平台具有 mbed 功能支持 Arduino 接口,同时还提供 ST Morpho 扩展排针,可连接微控制器的所有周边外设,可以利用 Arduino 巨大生态系统优势,便于快速实现STM32 学习和评估! 这儿我们评估它的 CAN 外设功能。0 T: c" J& J  `- Z# c5 z; K
一、环境搭建: ~+ g: H) H8 i8 S% R  ^9 G
1、软件:5 Z& \7 g  M; m. a( |4 t- h+ d& @/ P
STM32Cube\Repository\STM32Cube_FW_F1_V1.3.0\Projects\STM3210E_EVAL\Examples\CAN\CAN_Networking\EWARM/ {- S; g2 Z+ N
2、硬件:! w% I+ Q3 x8 ~$ ]
NUCLEO-F103RB(STM32F103RBT6). g0 u& H6 k. y0 o% N9 b
3、原理图如下:, ]5 i1 R* z2 o9 F
: O/ o, s8 M5 o; L4 F" S# h
ZZ]5XV{X7BN4S[QE9CA1F6G.png
, ]! U/ R& }, p: p0 O. `1 d) f0 l+ T+ L7 a6 x8 w
上面原理图是针对 SN65HVD230 的,9 x* |% _- ]; J( Q
因为 PB8 是 CAN_TX,是 MCU 端的发送,需要到 CAN transfer 的输入引脚,即引脚 D(Driverinput);' Q5 {! i: y; `
因为 PB9 是 CAN_RX,是 MCU 端的接收,是 CAN transfer 的输出引脚,即引脚 R(Recv output);
, \) R, `0 Z0 v5 \) W+ B. Y
: m* x6 D8 g: n0 W! O S)3(A{UFVJQQ0LKRP8PCBQ7.png
0 J* J- k+ R; ~0 V* p/ s% Y( q6 W& G3 i- B( j& v
二、Porting
6 O' \* z% R' Z5 }由于参考的是 STM3210E_EVAL 的示例程序,在用到 STM32F103RBT6 的 Nucleo 板子上的时候,需要做一些 porting 的工
/ O! l, x  ]+ t) t1 b2 U' {& z' n作。
' h- Q% r5 T, P  U: d6 _$ @2 X1、系统时钟& G& O1 g) p- d
在 10E 的 EVAL 板子上,使用的是 HSE,而 Nucelo 上默认的是没有焊接 HSE,所以使用到的是 HIS;利用 CubeMX 生成代码:系统时钟为 36MHz;5 i7 Q) D8 S. ?2 z" H2 ~

) m0 w' \& y; m 1A(AYMMZY%I4I11OE70N877.png - _0 j/ U. V4 y* `) Q3 ?

! J6 t1 y$ @! T7 a供给 CAN 外设的时钟:是 APB1 的时钟 18MHz;
0 }8 T1 c+ B# F3 `  `* s! I7 a
  1. void SystemClock_Config(void)9 E9 k* I+ F9 O5 Y# r8 w9 F2 M
  2. {
    3 \" A8 X5 Q) F
  3. RCC_OscInitTypeDef RCC_OscInitStruct;. z- v6 ^$ T9 Z8 L3 E
  4. RCC_ClkInitTypeDef RCC_ClkInitStruct;* e- G& g0 a2 e( f* l: j) ~& W. B
  5. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    : w" @( d) z7 g6 X! Q4 r* R
  6. RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    $ o% P2 T* r. g6 B
  7. RCC_OscInitStruct.HSICalibrationValue = 16;
    - H6 a1 I& H2 z; m8 X
  8. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    # g% L+ q6 A( |8 _$ T
  9. RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;1 f! N" ~  I% S; T# u! k, z: x: a
  10. RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;( K2 F. M0 @( s
  11. HAL_RCC_OscConfig(&RCC_OscInitStruct);
    ) f8 F6 e+ k+ n4 g4 f. I
  12. RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
      t2 j( C3 n% [4 B
  13. |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    1 B9 d6 h$ @' ?2 E! p
  14. RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;7 m& H9 ]  ]# j4 V
  15. RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    1 h9 H9 H9 b& F* m# X% P
  16. RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    1 @; |' }* k5 K/ v
  17. RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;0 {/ T2 T8 s& [6 C7 n
  18. HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
    & w  H# m8 U# ?" Q2 I$ v) S
  19. HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
    2 j6 n% v, d3 [% [6 E$ Q$ q
  20. HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);: y8 [8 M1 A0 E5 s/ @
  21. /* SysTick_IRQn interrupt configuration */% N, z& `+ \4 h. ]! W
  22. HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
    ; A$ y! Z2 `, Z+ F) j. ^. _' K
  23. }
复制代码

& p# _, l; O+ y5 k& n! N2、CAN 的接收/发送引脚( r8 a6 Y0 c) G; A. m; u
仍然可以都为 PB8 (TX) 和 PB9 (RX) ,不需要改变;
, ~4 p* _: c% |5 Z7 v3、CAN 的波特率
& i0 y" ]) W2 b+ h8 L* k(自己想设置的是 500K):
+ i1 h2 x9 z7 n% g6 P. [* v$ J& C, s
3%]@KA5XRL8X0)BS8]@V.png ' }0 S9 E1 c' |1 |( Q+ n3 A

* [, ~, B/ j8 k4 G/ @3 {' ?; i6 v @FK:Z$RE2V`4FI06OUD(X.png 0 \) \1 K" K* R* L8 E) u
& S+ l. O. M9 |- n) K
  1. CanHandle.Init.Mode = CAN_MODE_NORMAL;
    7 E6 v# U4 }3 C* V) y. ^& A
  2. CanHandle.Init.SJW = CAN_SJW_1TQ;
    1 Y/ f5 J9 o& K, e# U3 e  s
  3. CanHandle.Init.BS1 = CAN_BS1_3TQ; // TS1[3:0] + 1
    4 l) D7 ^9 b) o9 Z5 |) ^
  4. CanHandle.Init.BS2 = CAN_BS2_5TQ; // TS2[2:0] + 1* p; ]& d) O4 `% T5 Q% r* }# G3 j7 T6 c
  5. CanHandle.Init.Prescaler = 4; // BRP[9:0] + 1
    ( c( x/ I! A2 _) X  l
  6. CanHandle.Init.NART = ENABLE;
复制代码
4 E2 q' {$ t7 d
所以,理论上,根据计算公式,
9 _- H4 p( F/ r. zNominalBitTime = 1 × tq + tBS1 + tBS2 = (TS1[3:0] + 1 + TS2[2:0] + 1 + 1)* (BRP[9:0] + 1) x tPCLK;: A" [! z- p( A# S4 r1 m
所以,NominalBitTime = 9* 4* tPCLK; = Freq(APB1)/36 = 18/36 = 0.5MHz = 500K;$ m  p0 y3 l5 w. V( x4 t
4、User 部分' ?% W: ]& O3 k. M# S% F3 U
程序中设计到:( H! q5 w# q) z% S9 u
  1. while (BSP_PB_GetState(BUTTON_KEY) != KEY_NOT_PRESSED)
复制代码
/ c; q, _( y5 i
在 10E-EVAL 板子上,用到的是 PG.08,而在 nucleo 上使用到的是 PC.13/ t8 ?$ q7 w  {
  1. #if 0
    2 z' `$ S1 H/ d. q3 b# `: j
  2. #define KEY_BUTTON_PIN GPIO_PIN_8 /* PG.08*/
    * ?7 \6 Q' m. U$ d9 K+ Z/ v$ b
  3. #define KEY_BUTTON_GPIO_PORT GPIOG) l9 ]( c1 X1 R0 y, _
  4. #define KEY_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
    % G8 S! w4 q# P% \9 E
  5. #define KEY_BUTTON_GPIO_CLK_DISABLE() __HAL_RCC_GPIOG_CLK_DISABLE()8 k$ y& \7 A4 p
  6. #define KEY_BUTTON_EXTI_IRQn EXTI9_5_IRQn
    ; o/ E- i2 }0 P2 d. G1 K4 a$ n
  7. #endif! s# N3 T4 k) @. B8 Q
  8. #if 1
    . z7 k8 V% T3 z) ]  q
  9. #define KEY_BUTTON_PIN GPIO_PIN_13 /* PC.13*/
    " U: t' o/ i( n. W/ w7 v
  10. #define KEY_BUTTON_GPIO_PORT GPIOC
    , Z! z/ v6 o: M
  11. #define KEY_BUTTON_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()' o+ j% l7 F' z, \3 O7 X
  12. #define KEY_BUTTON_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()' B  t% E9 q, c& A+ B- l
  13. #define KEY_BUTTON_EXTI_IRQn EXTI15_10_IRQn$ t: O* ~+ t! U* x
  14. #endif
复制代码
3 c. `  q$ t) J6 u7 \3 w
RMDZ8MBAIQQD(NT`5EGYU.png
% N2 N$ {1 z& q7 {/ r4 q
& e( J4 [/ _; O6 L至此,移植好了;6 @) `  O' X: O$ j8 S" K

, j! K( E: X1 e+ U4 O三、全速运行
: X' ]7 L% r0 W! c/ D0 j9 ?1 Y1、按下 USER Button,会发出 CAN 报文,CAN 的 PC 端软件能够收到。7 P1 q2 I2 k4 O

+ \0 ]! R5 D  y SH[I_1~IWL[]C98HQ59CY8S.png * F; F0 {8 a# g! \; T) r3 \5 z7 g

% n2 u1 I8 K7 I4 y, i. f! s2、CAN 的 PC 端软件发送报文,软件中的中断函数也会进入中断。
8 k! K6 W/ \5 T
  x2 Z0 Y4 a8 ~; J6 k (3H6T$R@C`Q@~UH(_UCBE(O.png " ?) S2 Y: Q1 K
# I8 E5 Y' S; P1 y1 S
说明,CAN 的发送和接收这一基本的操作已经完成了。对于 CAN 的复杂的运用特点,可以在该基础上进一步衍生。
$ @1 W' x7 ~, K/ Q( O附录:* @  s# ^  K$ I6 ^& O4 ?1 p  Q
1、针对现有的 CAN 的总线协议:在数据区域只有固定的 8Byte;也就是说一个 CAN 报文发送的数据只有 8 个,我们的单片机的寄存器也只提供了 8 个寄存器,符合当前的 CAN 的协议;如果客户想发送多个>8 的数据,需要在其上层协议中,用软件去多次发送。也许在下一代的 CAN 总线中,会对这一特点进行改变。
/ c; r4 g% L# d9 x4 ?! h% C  M+ Q- p( S7 v, H
SDWPZ(GFX74NPP]IX$SB~SW.png
# |* f) L1 f3 o+ x7 u
8 n2 ]4 ^- `; d1 D  S2、CanHandle.Init.NART = ENABLE 的说明;  J) U: G1 |7 k; U) G8 ]% x
在基于"STM32Cube_FW_F4_V1.10.0
; \1 [/ K$ z$ b. ?8 |0 u0 }\Projects\STM324x9I_EVAL\Examples\CAN\CAN_Networking" , 如果只用一块 STM32F429-EVAL 调用HAL_CAN_Transmit()的发送函数,会发现 CAN Controller 会不断的发送数据, 这是因为在我们提供的示例中,是需要两块板子互联的,在 CAN 协议中,如果消息没有被正确的接收,它将会 be retransmitted infinitely by the transmitter until it will be acknowledged by the receiver ,而正我们的环境中,只有一块板子,而没有 receiver。5 v7 B6 A9 E( Y0 `4 ~
/ H0 P2 y2 }4 C# [3 z. N% z
收藏 评论0 发布时间:2022-2-12 18:48

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版