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

【经验分享】STM32 HAL库移植FreeModbus详细步骤

[复制链接]
STMCU小助手 发布时间:2022-4-10 22:53
modbus命令码表:/ ~' A6 l7 e( Z+ b8 q2 @

) O+ e- n. B% e( A
b8032deb4d154657acd2f7f2942369d4.jpg / w. D( N5 J' k6 \3 i

4 ^$ s4 B( G" y. v2 }FreeModbus文件说明

  N0 O9 j+ k5 f7 w      解压freemodbus文件后打开,我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。
( m- A7 J. a. _; A 20200313143257958.png
" Z3 Z1 y# E0 q. S; |  @! @2 P
: U, p# o& d0 n( i& L3 k7 ^      mobus文件夹就是完整的源码,包含rtu、ascii、tcp:" m0 A" F  y# @; {3 u
8 w( u6 ~- a+ l5 W7 F2 {+ C
ZM%DLXPRB8$%[NN2%4Z1N.png . c' |, _; W! e* A9 f! ]1 n1 |* t  s
5 G6 f* P0 n# ]/ H
      我为了移植时在keil添加源文件和头文件方便,就把modbus所有的头文件和源文件放到了一个文件夹下,并创建了一个port.c文件,用于编写modbus所必需的回调处理函数:5 m" _& L0 g6 _

4 K0 y8 H% N2 I+ E 20200313144036579.png , L4 j& t7 T7 e# h6 ?* Q1 [
1 N1 ]3 I" Z; u+ R7 `( V9 o
STM32CUBEMX配置
0 ~% e9 V& N9 d" ]
时钟配置,设置主频工作在72MHz下:
3 ^9 k+ X0 _+ |
+ z# W( _: o  L+ U: q' Z/ L( n
JU1~N33M}I7K1(6K@JFN5~R.png . x; Q) |2 k/ S6 B, E/ V$ V2 V3 d
  P2 A% I* k1 ?0 v
配置串口1,这里随便配置就行,在modbus移植过程中还会对串口重新初始化:

# s5 j# e5 i& r6 _% k) v0 V: n% F3 \7 a* @. a& B% D9 X0 D
U25OQ_FHYD5WEXLFMI4V@RU.png & B) M8 F! m9 }# b$ \2 g0 x3 e

5 E( P) w2 d6 e# m" ?配置定时器4,用于3.5个字符的定时检测,这里随便配置就行,在modbus移植过程中还会对定时器重新初始化:

" f& G" z. b& U
/ ]6 i  s% }$ L/ t! q  P H6{)WT`**CIHDAM02558GK.png % s' j6 N7 n5 s3 J2 Z3 g& M
4 x: S; G& K4 }& o( z8 @9 [. }
中断配置,这里注意,串口的优先级是要比定时器优先高的:

( {2 _; r6 Z) v7 t) k
+ ^; ?& l: c9 y+ h6 \) ] G(RP`74TR)NPJFUK``Z%I2L.png " X# w% I/ r6 X/ W
取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序:
+ h2 A# R0 c, V' b  `( \, `& |3 Y% [
ALHMK5Y615K6(`M{0[]YJ.png ! Q1 f" R( B6 a7 r
/ M, r  y% p5 w+ M$ [9 _$ P  r, R1 Z( [
移植代码修改

1 E: n1 T  O9 K1 ~) A* \* C- q/ W2 s生成代码后将modbus放到工程目录下:
& k' {' m( K+ E& _) V5 k
: Z/ }2 B' n! O6 t% c8 i
5%(_LMGIM_8_YGL`]JGAB67.png
* q. r/ m5 ]( E4 l- f, s/ |8 ^4 x* v: z: @
打开keil工程添加modbus源码:
" I" ]8 Q5 W; X/ r5 H( k

7 _6 e# p; Z( d3 j F`8_YFACMNOAP28~2VHD{CO.png / V/ ]* z# o* G

* C* _! d/ N/ E( S: K& d添加包含头文件:

( ?: u7 g9 ]. f6 l6 J! W6 ^5 P: k9 X) n! i: _
`7BMVXA`R9SQBTGT`B@{]X7.png
1 g( X+ S9 U$ N
, L/ K! E0 K6 K2 D( I$ _修改modbus定时器初始化源代码porttimer.c文件" p) {, V" c" X6 @9 ?
       定时器的修改比较容易,将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达;由于我们在STM32CUBEMX中取消掉了定时器和串口的中断服务函数程序,所以我们在该文件中添加定时器的中断服务程序,修改后的代码如下:
1 p! }0 ~/ x7 h8 x* X' c* H+ e, l0 I7 n' I3 b5 ~, ^& d; R
  1. BOOL
    - x% q6 Z: g3 `: b* u1 L4 A0 `8 _
  2. xMBPortTimersInit( USHORT usTim1Timerout50us )
    & u& }0 A9 N: s1 ?' M& T( d" p
  3. {! F7 z9 I  z, R! n/ C" C+ L, o& k
  4.     TIM_ClockConfigTypeDef sClockSourceConfig = {0};% Y) r' J0 y+ c* V$ U9 J
  5.     TIM_MasterConfigTypeDef sMasterConfig = {0};
    . X/ N* \, @- |; ^7 [, H
  6. & r' \! m  ^+ P+ G
  7.     htim4.Instance = TIM4;
    2 O1 _! W2 ]5 F0 f9 s0 V7 H
  8.     htim4.Init.Prescaler = 3599;                                                                // 50us记一次数
    / E3 Z; x( n' N9 S4 q4 D+ r6 M: c
  9.     htim4.Init.CounterMode = TIM_COUNTERMODE_UP;# A( u5 m* J: q1 G. m1 O
  10.     htim4.Init.Period = usTim1Timerout50us - 1;                                        // usTim1Timerout50us * 50即为定时器溢出时间
    % m/ V7 _4 }6 V' e; `
  11.     htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;: r4 F9 ]1 f( ?) y
  12.     htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    5 ?- @- u' o2 d2 ^5 V4 n6 h9 q
  13.     if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
    , @0 o4 }7 W9 Q7 C4 B; Q
  14.     {
    1 G9 b6 [. s6 o( E8 @
  15.         return FALSE;  w1 a7 Y7 t7 |4 n, X! y3 L' `
  16.     }
    ( v9 `0 I( e% A) f% w
  17.     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;! L/ K; g& ?/ I, ^% g$ p' L8 u
  18.     if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
    , y4 _1 d$ h2 i2 Y' R0 _
  19.     {0 ?8 \/ Z4 N0 e! M
  20.         return FALSE;$ X! {- M5 E9 |) {4 W: T% ^1 ~1 ^: O
  21.     }
    0 t) T$ _  y5 o( f- ^* H
  22.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;1 x* m; b- F# z
  23.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;1 x5 \8 x  ~* |
  24.     if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)- K- x# H% [3 J6 C% a" Z
  25.     {) g/ W1 \1 J( Q
  26.         return FALSE;
    5 R3 [- N3 S% [: Z% P
  27.     }+ {7 A3 s: |5 i6 I

  28. ; k% N. P% u  }! i* c' b; l6 a
  29.     __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);                                        // 使能定时器更新中断# g* I9 m2 Z/ e! I7 N5 x5 s
  30.     return TRUE;
    9 L/ v: B( [; V0 _
  31. }$ v1 N( O3 q7 P. y, N0 \
  32. ( r' {  f7 g- ^2 r6 q% V
  33. inline void
    * W) d9 t% u2 f! s$ R- M3 N0 E
  34. vMBPortTimersEnable(  )
    2 e* `( J, b2 K  B2 j6 A- t" ]
  35. {0 x% [) _/ Y, i, a  A; {! b
  36.     __HAL_TIM_SET_COUNTER(&htim4, 0);                // 清空计数器; M! [3 H4 d3 x' }
  37.     __HAL_TIM_ENABLE(&htim4);                                // 使能定时器0 d6 {; m) Q( K, f, a9 h
  38. }
    $ w: i0 j' o6 [  T' O
  39. 5 g, S- h4 C4 z# D$ S0 x
  40. inline void
    & ~4 u# h3 f$ W# [9 Y% ]
  41. vMBPortTimersDisable(  )
    . i& p$ G* \3 `, a# g) f1 N
  42. {
      h- x1 ?& s3 e$ ^
  43.     __HAL_TIM_DISABLE(&htim4);                                // 禁能定时器; T, O! r3 D$ o2 M3 G) r0 n
  44. }5 K  P, l* o( ]* r& e# ~
  45. 9 _* s3 b6 b+ c" C
  46. /* Create an ISR which is called whenever the timer has expired. This function
    2 m& O. A" L& R3 ~( o: E& _( b' i' x
  47. * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that+ y2 m3 V* B: Y- c; W/ O! s
  48. * the timer has expired.$ Z4 ~# O# s8 \$ \- \) C. O
  49. */8 P8 R0 x# `0 a! ^& E
  50. static void prvvTIMERExpiredISR( void )
    ( H5 E6 ]; F) M4 Q  K
  51. {' s/ x4 t( O8 w7 M& B( W: t! e
  52.     ( void )pxMBPortCBTimerExpired(  );; Q6 P+ v5 @. [1 e
  53. }( \6 C5 l6 j9 q. W1 C) O

  54. + W7 \! L! f, W$ H
  55. /// 定时器4中断服务程序- i1 C! l8 C& S  @+ t" E# e
  56. void TIM4_IRQHandler(void)9 z$ m6 }) S" q" H8 d8 i
  57. {9 i# B* o8 W' X
  58.     if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))                        // 更新中断标记被置位
    2 l- E) D- C+ n9 W8 B% C
  59.     {0 z/ ^& w/ ?6 o# x
  60.         __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);                // 清除中断标记& c( }5 o# O; H2 ^, y# P3 \. o9 P
  61.         prvvTIMERExpiredISR();                                                                // 通知modbus3.5个字符等待时间到
    . X' Z: {7 a. w# ^2 ]( ^& ^
  62.     }
    $ R# i5 i; U% i$ h% ~
  63. }5 V- r+ A6 @1 X& {/ S

  64. 1 f( q1 M/ d  E  B: N/ K& t5 G
  65. : n2 X( y  ~! X3 Y3 L4 i
复制代码

  H4 b" u4 R1 i1 f2 d, ?修改modbus串口初始化源代码portserial.c文件
% B6 I9 z$ o- `/ \         ~~~~~~~~        在该文件中实现串口1的中断服务程序,prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了,后者的作用为通知modbus串口1有数据到达,修改后的代码如下:
8 n9 n0 m/ X) V, `; |: y$ T: y
  1. /*  X8 h3 v) m# p2 i
  2. * FreeModbus Libary: BARE Port" Y% B2 c4 [! c1 m
  3. * Copyright (C) 2006 Christian Walter <<a href="mailto:wolti@sil.at">wolti@sil.at</a>>2 ~$ y' k) M. F3 E/ n& t
  4. *  M" V8 _& Z# g& {/ b, ^
  5. * This library is free software; you can redistribute it and/or
    9 W/ s! y  B3 u' M! ~" i
  6. * modify it under the terms of the GNU Lesser General Public
    , w$ O/ K4 e( y+ e# L) V+ f! k
  7. * License as published by the Free Software Foundation; either$ T0 X- `/ c% Z) O
  8. * version 2.1 of the License, or (at your option) any later version.
    $ P* e8 F3 [' i4 U  S! S. _8 {
  9. *
    7 v  D& B* c2 M0 p* u0 R
  10. * This library is distributed in the hope that it will be useful,
    " M0 @& {/ k; M- U: ?1 ]9 L
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of# J4 ]' Y$ V3 V9 L( `" R
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU! l" E* |) W0 P0 d2 e- V. j" o* {
  13. * Lesser General Public License for more details.
    / {9 `4 C  r4 F2 J3 A9 ?. m
  14. *
    6 E) B; q: H1 ~2 |) v. C# z* X6 c* O% H0 ^
  15. * You should have received a copy of the GNU Lesser General Public
      L2 t/ S) o7 u, Z) O7 K6 |8 r( I
  16. * License along with this library; if not, write to the Free Software
    ; E6 d' B$ r! Y5 g& }) @5 f
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    ) g, X1 [) t8 B1 N* ^3 K
  18. *' ^% M2 m5 F5 s; z' c
  19. * File: $Id$! @1 K0 @9 {6 ]! s7 F! b0 M
  20. */3 l" Z: s* U3 p5 X
  21. ' G% D/ J" y) S4 `
  22. #include "port.h"8 p  ?; r1 N4 ?0 w  h4 G
  23. #include "usart.h"- R  L7 }' l' m/ ?
  24. 1 G4 i  X& c" b" x& l3 c9 f8 r
  25. /* ----------------------- Modbus includes ----------------------------------*/% C/ i3 `* c8 `$ R* b8 O1 [! o
  26. #include "mb.h", l& D6 o. O3 b  E: g' r* D
  27. #include "mbport.h"' V) c6 a+ b  D/ ]
  28. 9 b' [; C/ p' U* C! \
  29. /* ----------------------- static functions ---------------------------------*/
    : e+ ^9 V8 _5 f
  30. static void prvvUARTTxReadyISR( void );
    1 d8 j" e6 z; b
  31. static void prvvUARTRxISR( void );/ R4 }. v* j2 n! Y' h

  32. # r5 I3 l5 g/ s4 r2 }/ f" {& V
  33. /* ----------------------- Start implementation -----------------------------*/; ]0 _% X1 h9 ?5 H2 r4 Y
  34. void, d! _2 S7 R# s5 A' r3 d: M- {
  35. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ); `# Q- _' C0 y# x. ?
  36. {+ F5 Z# J. V# K: a' ?. h. _
  37.     if(xRxEnable)/ g0 K9 e& S+ f( k( {! j9 c
  38.     {
    7 X* }$ C8 Z; S7 `0 _( H
  39.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断/ o( L6 U' F2 I3 J9 H# D: d1 p
  40.     }6 o- q/ W0 g, B+ y4 U$ E
  41.     else
    ) ?0 S0 j" c9 c6 s( a6 u1 @
  42.     {
    2 @+ h# U& F+ g( [
  43.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断* i2 m- G! y. l# ?" u
  44.     }
    4 \; f% P2 O. t
  45. ( z) j7 p. b) r' I6 ?% Q% U! w
  46.     if(xTxEnable)
    # _" l& U& n' l6 I
  47.     {
    ' x) F/ t2 Y: V  ?0 [# O
  48.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断- b/ ], f9 I. q9 J
  49.     }
    ' ~/ Z4 n9 S$ Z5 s
  50.     else) L$ g* h7 Q# i- s) d0 O6 P
  51.     {4 C$ `% _9 y  e' I/ E+ Z
  52.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断& m! E8 K7 a! ?. u
  53.     }9 `, U6 p( [! S# b# C1 E8 q
  54. }' V% D" Y/ u0 r) D3 x3 i" v

  55. ( L9 T7 I1 ^- M0 o4 N1 y( |
  56. BOOL) l% {. [+ W$ M' u8 p
  57. xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )* v5 N9 d8 A( t7 _3 q
  58. {
    9 a, h2 H7 Y  o/ s) A
  59.     huart1.Instance = USART1;$ j$ b& v. p& @0 Y
  60.     huart1.Init.BaudRate = ulBaudRate;
    0 `& D+ d8 W; G
  61.     huart1.Init.StopBits = UART_STOPBITS_1;
    5 ^8 J- V/ e) Q: w9 ~% M
  62.     huart1.Init.Mode = UART_MODE_TX_RX;. z. v' L) D& ^) l
  63.     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;% A9 N; ~0 U4 u4 K
  64.     huart1.Init.OverSampling = UART_OVERSAMPLING_16;
      e% ~8 m: \/ f/ j* N4 x5 \& f
  65. ) ]$ H2 F5 D- U- R2 B/ y- ^
  66.     switch(eParity)
    0 q1 f+ ]& @( H- W8 \
  67.     {3 C3 \7 t4 D, b9 U) W# H
  68.         // 奇校验
    * W% G/ \5 ]% ]. T- I
  69.     case MB_PAR_ODD:% X5 d2 G9 {% }0 Q8 N* v, e
  70.         huart1.Init.Parity = UART_PARITY_ODD;
    0 F8 i( V* a5 f+ F
  71.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits8 ^1 F; h* U5 E- O7 r
  72.         break;
    : S& Y2 s5 K# C" Q: A; q4 ]3 y
  73.         
    ! T* w  O3 p/ v; K# F: @
  74.         // 偶校验
    3 R3 h" W) w3 g$ g4 }
  75.     case MB_PAR_EVEN:
    % j2 g* `# a* {3 A2 n! l' E: X
  76.         huart1.Init.Parity = UART_PARITY_EVEN;
    - W8 a' l. z: u8 n( a( {
  77.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits0 {% X% d% J5 z; h
  78.         break;- r* @* R. k0 R, k. O
  79.         , A& c- d$ S' f
  80.         // 无校验
    1 y3 {' N& B3 p1 v3 l' R1 T! h
  81.     default:
    * @3 f  g& ?5 z2 P
  82.         huart1.Init.Parity = UART_PARITY_NONE;' \6 G, N! V0 X# L/ H* [6 p
  83.         huart1.Init.WordLength = UART_WORDLENGTH_8B;                        // 无奇偶校验数据位为8bits8 Q3 L# W: y4 v" U
  84.         break;
    1 |3 g3 B5 |  D: K1 t
  85.     }, ~, `6 H7 |3 C6 U
  86.     return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;& Q7 H& d3 M/ z% D
  87. }( ~5 X$ s& h5 J' E! \, W1 \# _

  88. 9 m8 Z* ?! `/ C. `$ B
  89. BOOL
    ' X# o5 A& s- y' V
  90. xMBPortSerialPutByte( CHAR ucByte )
    : I( Z2 w' n  g: C7 k) {
  91. {
    2 f7 P' ^: N' {( l5 s
  92.     USART1->DR = ucByte;- [( u; U7 `' k
  93.     return TRUE;2 M' i3 g& W4 S) N; b* [
  94. }
    : C/ ?( M1 G$ W1 ~( ]- y8 y& Y: _

  95. ( A3 Q3 ^3 ~7 a) E- v
  96. BOOL5 j( }1 n; w4 o4 Q4 r% n" Q
  97. xMBPortSerialGetByte( CHAR * pucByte )) L9 F2 u( r! q7 R
  98. {
    # A5 v( n$ r3 t2 t# _1 J/ j9 M) ]
  99.     *pucByte = (USART1->DR & (uint16_t)0x00FF);1 K  l( W. W! I  r0 ^3 Y1 ^
  100.         return TRUE;
    ; s+ y1 j2 ?5 X* L( I4 [
  101. }# u5 ]$ z1 B1 O8 i$ n
  102. ' V5 z8 v+ r/ q# P1 P, G/ f
  103. /* Create an interrupt handler for the transmit buffer empty interrupt
    ' z$ I3 W* D4 M- ~' G8 X- P7 g
  104. * (or an equivalent) for your target processor. This function should then, J% g9 W8 V9 y8 }$ A& N$ O# g
  105. * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
    6 c" V/ ~5 U# X  B
  106. * a new character can be sent. The protocol stack will then call
    1 K- ^" z6 H2 A) Y4 [- S
  107. * xMBPortSerialPutByte( ) to send the character.9 F. o% _# g6 Q( z0 M+ v2 J# b
  108. */
    / p: R  ]# _' N1 O- h. T/ F  U( J
  109. static void prvvUARTTxReadyISR( void )4 j! }& `9 B, ?2 g7 ^0 X6 W
  110. {/ `' o& {0 c1 g$ Q  J& G5 ]
  111.     pxMBFrameCBTransmitterEmpty(  );4 i- _) R# O8 b5 J0 w, M
  112. }
    * ~, \8 E- b' M

  113. 7 a. g# ?0 ~# ]& R) t
  114. /* Create an interrupt handler for the receive interrupt for your target
    9 {! {& L6 m0 `$ G3 y
  115. * processor. This function should then call pxMBFrameCBByteReceived( ). The# \" h! B  X6 B& v: w. Z  b1 V
  116. * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the/ N# t: c8 Y4 C; W
  117. * character.
    : N1 Y) U+ Y* y+ S
  118. */
    # O) X6 A% W' W7 r2 _% c
  119. static void prvvUARTRxISR( void )+ R$ ?2 S, |7 P, c  j
  120. {7 h2 e8 G/ M% O) [$ l0 Y
  121.     pxMBFrameCBByteReceived(  );; ?* t1 A1 C0 P  x/ b. D: V+ S: P
  122. }5 U  ]+ |# m5 U  d: Z

  123. ! t+ g, j( b1 t( E  ^
  124. void USART1_IRQHandler(void)
    ! y* n9 g  n, }7 M  h# l2 `. |/ c9 Y
  125. {
    9 t" j3 W+ s" s  C5 K7 c# Y2 R
  126.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))                        // 接收非空中断标记被置位' ~3 n* }9 Y  y& v2 c" \- n1 Z$ g
  127.     {/ }0 M9 J: Y7 n; Q" ?
  128.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);                        // 清除中断标记
    ' y* G$ y4 F2 y4 @' k
  129.         prvvUARTRxISR();                                                                                // 通知modbus有数据到达2 f) w) ~+ |& x. h1 d- t
  130.     }& Q) L- F2 |- P3 R2 }; F

  131. " J% b, S; y4 ^; D3 ]
  132.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))                                // 发送为空中断标记被置位
    8 k6 _5 I* ~! H5 u; F; E
  133.     {% F- y. V1 i( U, e1 U- w  J
  134.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);                        // 清除中断标记
    * k1 p0 Q6 w8 x8 c6 E2 ]& V
  135.         prvvUARTTxReadyISR();                                                                        // 通知modbus数据可以发松8 o2 r$ D  T; L" }5 d- Z
  136.     }
    ( a& y8 s/ z# x: @' `) @
  137. }
复制代码
9 V9 u6 ~1 C  }9 k& ]: Y2 P
注意一点,一般如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样:
# j% ]  o1 d. }5 b" p& e# v# y9 D# k- h& [% A0 `+ h' k5 w+ [
  1. void4 c1 Y% D$ B* a" |
  2. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )- s* J' L5 h* o- {
  3. {: w* j1 `- o8 D+ P0 r) d. p2 L  [
  4.     if(xRxEnable)
    ' w7 S' C% F. U9 H. V3 L
  5.     {
    " q% e# Y4 k2 T$ m) k* i  H8 |
  6.             //! M) c) m* C+ j+ J
  7.         // 在此处将485芯片设置为接收模式( t, N4 S& n1 l( B$ h) }1 z
  8.         //
    / I* I9 v- G& I# O  w
  9.                 /* do something */
    9 x8 |6 S; g2 A* m; ]" U9 X" |
  10.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断2 v6 M% N/ M6 z8 f; e" o, f6 G0 r
  11.     }# s. T8 K5 f: n; T
  12.     else# H$ D# U! q/ X+ ^, P9 P. ~
  13.     {7 }4 }+ c4 C  I! s/ p4 [
  14.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断
    & C! V8 U  y3 A& \% ]
  15.     }9 E3 K* S) ~2 r9 z) M# M/ u# f
  16. % B3 L6 E  G2 S3 {0 e& z& L+ `& H6 H
  17.     if(xTxEnable)7 b( Z# B' G6 |1 g5 f( t
  18.     {8 f7 p( Q8 G- l" R. X
  19.             //. I6 b6 b) ^3 C& I" E! g% n" w
  20.         // 在此处将485芯片设置为发送模式
    5 W) D' D' u% w1 u3 J- ~" m
  21.         //
    - ]! g* d2 G6 F, S/ z/ Q5 h  V$ G
  22.         /* do something */: Y, @" y( ^' ^- }! g2 U& [1 C
  23.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断
    / g+ B; e: {$ t& P
  24.     }) L* _1 M. T6 z, E$ v
  25.     else: s, S& {* x( G
  26.     {
    * Y% Y$ X' l) t2 N& l+ M5 q
  27.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断
    - ]: v- Z' ]2 ]. v
  28.     }( l' V, t/ Y3 ~2 t: C4 ^1 u. w
  29. }
复制代码
% k7 ?) E3 a/ P" Y! F. W: W, R1 h
编写modbus命令处理回调函数port.c文件
/ o1 Z9 P6 E4 }* v; Q本例程只实现了读取输入寄存器和保持寄存器的功能,详细代码如下:
; V( y0 E0 E4 |  K; E- j* N

& g6 L+ A6 p1 p/ a& j; x# j: [
  1. #include "mb.h"% v9 r5 h6 C( k2 V) K( D
  2. #include "mbport.h"1 n. B, W# g! Y2 c1 U
  3. 6 W8 {' ^/ V6 {. q" M+ K3 \
  4. % [7 w" y# R6 f9 I8 I* a
  5. // 十路输入寄存器3 D4 T' d; d- @( ]; v
  6. #define REG_INPUT_SIZE  10, r; D9 d6 t! w* ^$ {( D7 w% w
  7. uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];+ G' @; X+ I+ k8 t' P

  8. # d' b+ L- Q. Z5 O! h- K: V

  9. ! ^  e( m1 u, n/ b% n
  10. // 十路保持寄存器
    5 W$ L# \/ h& C: |8 C
  11. #define REG_HOLD_SIZE   10
    0 S. j) w) @' t! k7 F0 Q4 N7 V
  12. uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
    + q# S) r- b" g4 Z1 k, ^1 h* `% \: D
  13. & h! }# M5 U* C" b

  14. + q. s" Y% |4 V/ z$ V
  15. // 十路线圈/ C  ~! s+ C5 j+ u; K
  16. #define REG_COILS_SIZE 107 Y! L: r  Y% t* ~
  17. uint8_t REG_COILS_BUF[REG_COILS_SIZE];* }& N! b4 j6 Q: Y- h. R) G' j
  18. + Z0 z1 R. _" k' m! R- U
  19. : q/ ~, K9 s' ?
  20. // 十路离散量
    % U) `" f3 A6 F8 B1 r
  21. #define REG_DISC_SIZE  100 n0 k/ _! ]9 I! Z
  22. uint8_t REG_DISC_BUF[10];: ^* F0 m: W( T- }

  23. ; V1 Q0 Q1 @3 c- g

  24. ; G: L; B, f/ ^( Q0 n* R# a" Z
  25. /// CMD4
    $ f0 R' `, D9 q
  26. eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )9 D4 p1 J, ~! c7 d4 Q8 ~. t
  27. {
    2 j; X( y2 m! |4 x* l9 A# K
  28.     USHORT usRegIndex = usAddress - 1; 2 {' s, Q2 M2 a6 K6 w/ \$ u5 x2 T3 r
  29. : p( j# \# Q+ c. z$ I
  30.     // 非法检测
    ' A+ P0 N# |% ]* p/ }) ^
  31.     if((usRegIndex + usNRegs) > REG_INPUT_SIZE); n- e) E: o% p% Y( W6 P
  32.     {
    $ i0 H2 [6 b  u: ^3 e! o2 p" D
  33.         return MB_ENOREG;8 n! g5 x7 h: Q: l9 C- R* {$ Z! L
  34.     }4 p" q: |4 I/ ^% U, G8 S

  35. 4 [+ i# j+ j: d
  36.     // 循环读取4 S+ e! K/ _* |9 ^
  37.     while( usNRegs > 0 )* ^6 J# M1 F( P/ y
  38.     {
    3 t- j$ }6 J' H/ b9 [6 u" I9 G
  39.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
    3 l1 J% i* N, F
  40.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );  N* d( r1 W9 F- i' Q& x* @
  41.         usRegIndex++;4 g7 A9 ^) q$ G* s! E0 x% c$ I
  42.         usNRegs--;$ \4 W: b! t. f4 C  v  E, ?* p
  43.     }
    + p; T5 ?( B, e1 }" o  }- ?6 ~- `

  44. , h4 V7 c, ~3 t
  45.     // 模拟输入寄存器被改变4 B7 W% I9 K1 B! i% Z' ?" {
  46.     for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)7 [0 i% P' o' v; U: T3 w
  47.     {
    & [$ ^3 V  B8 P' |* T9 n
  48.         REG_INPUT_BUF[usRegIndex]++;
    0 s% y1 L7 Y- C
  49.     }) H9 {0 N- m2 q( k$ E

  50. 7 n2 T7 i' ~8 y
  51.     return MB_ENOERR;: T; `" k0 ~) E1 L8 x) ?
  52. }
    # P  [" d; T; A, ?9 P( P3 H1 F7 Q
  53. 9 q) z8 ?5 [8 t$ P
  54. /// CMD6、3、16  X% C* m& w+ S1 {8 O* v* R7 \
  55. eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )& G( {( W8 T0 d( b
  56. {7 R4 K$ }' t* ?$ [) N
  57.     USHORT usRegIndex = usAddress - 1;  3 c% J: w. L! v, w) P" A) M+ |

  58. ; E: n2 ]8 t5 F* u# k
  59.     // 非法检测
    ) P; S# M* J% }: R7 E
  60.     if((usRegIndex + usNRegs) > REG_HOLD_SIZE)! R4 D8 A1 S7 m% n: |  o
  61.     {8 v& V& d& l7 N& }) g) M" j, \
  62.         return MB_ENOREG;( w: _7 X# I- W5 Y$ u2 z# R5 {
  63.     }+ O9 \, w2 e7 H3 e0 r+ V
  64. " ^* D( x( I( _" c/ n
  65.         // 写寄存器
    / B! o. [4 z/ ?5 V2 n: C% ?; z
  66.     if(eMode == MB_REG_WRITE)! k) z; T! B. |8 a' j
  67.     {
    7 B% E5 d$ B6 C/ g
  68.         while( usNRegs > 0 )
      L, y0 @  ^, b8 {& I$ g
  69.         {
    ! ~# @" \1 n. |
  70.                         REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];# A4 B  |' g" X
  71.                         pucRegBuffer += 2;* Q. F5 J' h  w" X) h9 }7 i
  72.             usRegIndex++;9 F/ q1 q% ]- `. {" E9 C) l
  73.             usNRegs--;
    : r( {2 o! A0 {
  74.         }+ v: I9 `+ C( g9 n# S
  75.     }5 T& [$ P* `  O8 i! r
  76.         3 y5 ]3 s; L: m& k+ }
  77.         // 读寄存器' l- |0 [- H5 u) e5 r% @3 Z5 c
  78.     else
    2 t. y+ P! O1 y8 x, O' n) f2 o* s
  79.     {! y. {- b/ r7 g) ]. d' w7 V
  80.         while( usNRegs > 0 )% o2 k- Q/ W( p; k
  81.         {
    7 g5 ^, A* ?6 f- E* k2 H& K
  82.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
    + [. }7 p5 }! W/ }$ o5 S
  83.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
    : ?+ E0 u8 ]* l. Q4 i, T. u* k( y% J
  84.             usRegIndex++;
    % I1 l# B& i4 _. {/ |9 F
  85.             usNRegs--;1 o) j. N5 B  |8 G
  86.         }
    . Q6 z" T4 f; l  W, P
  87.     }
    " H  {3 n' }  D! T" `* s1 z) M

  88. . O) h0 Y6 p2 V# j
  89.     return MB_ENOERR;
    + v# R# i+ D$ Z3 O* \
  90. }6 g5 ?: t+ H" C0 V0 G  O
  91. 0 o- p* M+ d' r$ F7 j. i) E
  92. /// CMD1、5、15
    / a  W- ?2 L8 C) Y
  93. eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
    + B1 @* S4 l# K+ }5 H& ~7 H- L
  94. {# o' S$ B6 f2 `7 V
  95.     USHORT usRegIndex   = usAddress - 1;) V- P5 ~' m8 S( q3 o0 H
  96.     USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
    & v: n" Z$ J9 f8 r! o
  97.     UCHAR  ucStatus     = 0;4 p. N: l- V- g2 s; ?
  98.     UCHAR  ucBits       = 0;
    " A" P, G6 d) J* a2 A3 v5 @
  99.     UCHAR  ucDisp       = 0;, v; N3 i) w5 Q

  100. ! O  ^! W  B8 M1 w+ L* L
  101.     // 非法检测
    5 e: L/ ?% a$ g0 s' s% [% `$ W
  102.     if((usRegIndex + usNCoils) > REG_COILS_SIZE)' [# j2 Y' w9 u4 d5 j9 o
  103.     {
    % ^" n$ q* N, F1 |9 O
  104.         return MB_ENOREG;  b2 s) B7 f. q  [$ O# x  S8 y4 @
  105.     }
    1 ~: F) E) ~) b# D* N9 Y5 Z

  106. % Z  X7 w7 \9 [  E3 i! X" K9 I% b/ m+ n
  107.     // 写线圈# V3 u4 @$ r5 w6 M) r, v2 S
  108.     if(eMode == MB_REG_WRITE)% R. Z8 o5 @. |& Y  s
  109.     {: B1 v4 e4 _9 d3 K3 X
  110.         while(usCoilGroups--)
    , ~6 ~3 f+ ~9 v+ m
  111.         {4 D6 ?/ I3 C* Z( s0 u$ K
  112.             ucStatus = *pucRegBuffer++;
    ' o7 @; I, o! h
  113.             ucBits   = 8;0 E4 C: H+ e" e9 z5 ^
  114.             while((usNCoils--) != 0 && (ucBits--) != 0)  N' R, `  x! X9 H1 ^
  115.             {. r6 ~4 `8 ?( F2 Y. H' M1 b" p9 r
  116.                 REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;' J1 U9 K- U" g: q& K1 X
  117.                 ucStatus >>= 1;
    - E. P! a" \# E, N& Y9 N
  118.             }
    . i9 t' Y/ t- [# I
  119.         }
    ( |7 X- Z4 N, m, R* l6 H; d
  120.     }
    # m( ~7 R, I. Q. X8 T' y4 J$ V
  121. 6 W: T. ]- |& u4 G; ]; R
  122.     // 读线圈
    ) D9 E6 @: Y! H9 {6 K5 N3 l
  123.     else
    ( C9 l4 `3 f9 I! Q3 w7 a% g+ H5 }0 X
  124.     {# e% \- S5 f9 H# A1 D! @# n% \/ V
  125.         while(usCoilGroups--)3 a( n6 n# P! _8 \9 c( c( f5 `
  126.         {
    6 \0 J" Y) i" u7 g
  127.             ucDisp = 0;
    # r* B9 ~' S- Q: U3 O
  128.             ucBits = 8;
    6 x6 I2 ^& c) Y2 j$ p) L
  129.             while((usNCoils--) != 0 && (ucBits--) != 0). m- C! ~7 j+ s# s4 P
  130.             {
    ; @! x7 a2 B/ s" e% O& i
  131.                 ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));7 G' u; l" d/ b' H% V
  132.             }
    2 O9 k3 m# ~$ ~2 n" d
  133.             *pucRegBuffer++ = ucStatus;/ S9 J9 H5 C: v& X. y/ f' P
  134.         }
    $ Z  T7 p. `6 V7 ]& j
  135.     }* t; U$ ?# V- U9 ^' N0 M
  136.     return MB_ENOERR;+ c) a7 d! {; [* N/ k' Z
  137. }
    * W8 L! }. Y* H# T4 x: f
  138. 7 ~1 m6 e# \' W* C. |

  139. 4 C9 z  Y/ Y, J* Q7 a2 u+ f
  140. /// CMD45 ~, F' N; P+ }) V" Y
  141. eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
    & v4 T2 g1 b% |& [7 f  p
  142. {1 A5 q7 [; O8 A- W, X& |" A
  143.     USHORT usRegIndex   = usAddress - 1;
    ) u- O' H7 D, |9 m8 {. M
  144.     USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);8 ?; P+ I; T8 \
  145.     UCHAR  ucStatus     = 0;, w- \! d/ c& |+ K" |6 c6 L
  146.     UCHAR  ucBits       = 0;
    7 z$ S: b: b1 Q: R' W' p# A
  147.     UCHAR  ucDisp       = 0;
    & c3 C" @# y' f5 V

  148. + e& ~9 Y6 ^; [7 J
  149.     // 非法检测
    5 ^: y* x# O9 k+ @& n9 n. K
  150.     if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)3 J6 r7 |4 K: g+ ^
  151.     {
    - y* t4 E9 h* D: R4 m
  152.         return MB_ENOREG;
    7 V3 s8 K0 k' {9 W, O: e9 C# [
  153.     }
    5 M/ f0 e# R9 \" Y7 J

  154. - y. O5 a9 w% \0 L- y
  155.         // 读离散输入
    . @! M5 |- M9 m+ w, T2 E9 G
  156.         while(usCoilGroups--)* t0 O; K! e: G+ Y' D! L
  157.         {( U3 n3 Q9 h; y. I! e) V
  158.                 ucDisp = 0;
    : G8 g" S, P& f; s
  159.                 ucBits = 8;, L) m1 w% X* R/ Q7 g  }; c
  160.                 while((usNDiscrete--) != 0 && (ucBits--) != 0)
    ' T8 W0 ?1 ~  ]( q# C
  161.                 {
    / o; W' P, f1 s* T
  162.                         if(REG_DISC_BUF[usRegIndex])
    : y- M8 I% ]" @. n" Y
  163.                         {2 c8 P/ D+ H1 {: k, j& ?
  164.                                 ucStatus |= (1 << ucDisp);
    : C; w9 `; B5 g. T  y
  165.                         }
    9 S* D2 ]* z1 Q/ k
  166.                         ucDisp++;
    4 t- \" ~7 c7 \8 F) j
  167.                 }
    3 \0 F. F. ^7 C) e6 g" P
  168.                 *pucRegBuffer++ = ucStatus;
    9 a7 P/ B. f. F
  169.         }
    3 G* |4 D" s6 {5 ~

  170. / e/ X) t5 C7 L: \: q9 m
  171.     // 模拟改变5 p8 c6 j' |/ D$ u* f
  172.     for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)6 {( I/ ?2 F# _; F
  173.     {! f/ e  U  _. O" f" x
  174.         REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];1 o1 e/ j# @. f2 w. s
  175.     }5 Z; c% r1 B. N: p/ q

  176. ; o0 `0 U# |/ e
  177.     return MB_ENOERR;
    7 T- U9 M0 @, Y* z8 P! z
  178. }
复制代码
7 V' n0 V, Y) t5 s1 M
主函数

* S' w0 g- u% ?
  1. int main(void)
    8 W. ?/ G' a2 ], `, {
  2. {& z+ X6 P) W7 p
  3.     HAL_Init();
    ; G+ w7 T4 Q6 N5 g# p
  4.     SystemClock_Config();
    : @9 Z& y" X. d8 q! g5 D" z' ]/ [
  5.     MX_GPIO_Init();9 f0 g/ p" x+ \( g  _% M% {
  6.     eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD);                // 初始化modbus为RTU方式,波特率9600,奇校验' D) `) u) R( m
  7.     eMBEnable();                                                                        // 使能modbus协议栈, ^* L' }' B% R$ k/ A# X6 c

  8. 1 C. u  J1 g0 g7 G: C
  9.     for( ;; )/ m+ @/ B9 v" D7 m
  10.     {7 ?( J2 d3 c6 s. k3 f
  11.         eMBPoll();                                                                        // 轮训查询, |/ B+ ~2 ^7 |# B. h  Q
  12.     }4 n1 w% j9 o7 I8 d
  13. }
复制代码
8 Q  B( `5 X; K1 R
移植测试
& m6 q$ Z' y2 L
( X' D& O* r1 t9 N6 k' L! z 20200313154857173.gif 6 ]+ s1 z$ b8 ^0 i
6 L  z) F1 I& O7 M% L, t6 @
ends…) E( G; U' \3 v3 `9 r: ^$ u
4 h& R4 B* J$ e0 x9 f
4 P" t, \" ^$ Z, ]% _4 S

. }) F3 f2 E& W3 L7 E

点评

Mxn
备注:我的问题是,移植完之后,主机发数据,串口没反应,程序直接卡死  发表于 2026-7-2 11:09
Mxn
在vMBPortSerialEnable函数新增 __HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_TXE); // 新增:清除残留标志 位置在 这个下面 __HAL_UART_DISABLE_IT(&huart3, UART_IT_TXE);  发表于 2026-7-2 10:48
新人小白按此步骤移植后程序跑不起来,请各位高工指点  发表于 2026-6-29 09:43
1 收藏 2 评论7 发布时间:2022-4-10 22:53

举报

7个回答
zzzff 回答时间:2024-10-26 10:31:57

我的是stm32g071系列,移植上去不行怎么办

Mxn 回答时间:2026-6-30 14:14:24

使用天空星主板,STM32F407VETR的芯片,配置完成之后,修改USART1为USART3,修改了TIM4的预分频为4199,连接上串口之后,PC发数据没有反馈,这个大概率是什么原因引起的。

Mxn 回答时间:2026-7-2 10:45:43

Mxn 发表于 2026-6-30 14:14
使用天空星主板,STM32F407VETR的芯片,配置完成之后,修改USART1为USART3,修改了TIM4的预分频为4199 ...

[md]问题找出来了,接收第一个字节正常进入到了中断,但是第二个字节直接进入了

if(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE))// 发送为空中断标记被置位

导致程序认为接收已经结束,然后通知modbus可以发送数据,但是此时的标志位 不是STATE_RX_IDLE,而是STATE_RX_RCV,所以程序被 assert( eRcvState == STATE_RX_IDLE ) 杀死了,

修改:在使用中断的时候,清楚以下标志位

void

vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )

{

if(xRxEnable)

{

MODBUS_RX_EN();

__HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);                // 使能接收非空中断

}

else

{

__HAL_UART_DISABLE_IT(&huart3, UART_IT_RXNE);                // 禁能接收非空中断

}

if(xTxEnable)

{

MODBUS_TX_EN();

__HAL_UART_ENABLE_IT(&huart3, UART_IT_TXE);                  // 使能发送为空中断

}

else

{

__HAL_UART_DISABLE_IT(&huart3, UART_IT_TXE);                // 禁能发送为空中断

__HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_TXE);              // 新增:清除残留标志

} }

lvgl 回答时间:2026-7-6 14:27:02

你好,可以分享一下工程源码吗?

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版