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

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

[复制链接]
STMCU小助手 发布时间:2022-4-10 22:53
modbus命令码表:8 M; L- {3 y- y) b

# C+ g: j+ _! G6 W! Z4 c0 W
b8032deb4d154657acd2f7f2942369d4.jpg
- |8 H0 t3 B* C9 M% }) x/ M  Q
& k' o" q; C7 |" s( G) ?FreeModbus文件说明
2 ?, m* j9 Q5 ]$ `% i
      解压freemodbus文件后打开,我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。
- s" Q4 P2 e; N# U 20200313143257958.png
3 K$ P. e7 R7 A5 O2 [) [' ~. }- Z0 _% ^3 t5 w1 l! V
      mobus文件夹就是完整的源码,包含rtu、ascii、tcp:
8 \' V1 T  |. p* L
; c  }+ ]" d7 w# \- z* ` ZM%DLXPRB8$%[NN2%4Z1N.png * G4 S2 g0 _5 K8 C- a

3 P! f2 a( }+ E( C      我为了移植时在keil添加源文件和头文件方便,就把modbus所有的头文件和源文件放到了一个文件夹下,并创建了一个port.c文件,用于编写modbus所必需的回调处理函数:
2 R- |- F% z( a( d/ y
" B# w4 j* y- c% s8 M' o 20200313144036579.png
. Y0 [) V" q6 r. Y# b( {: }" E1 S: W  I! H! V
STM32CUBEMX配置
# ^+ m, _4 Q" N7 o
时钟配置,设置主频工作在72MHz下:
6 a/ H7 k# |8 J0 i2 I
7 z* V4 Z# i) M9 N" @  {
JU1~N33M}I7K1(6K@JFN5~R.png
) t) }9 j6 l( j! _# t& {: ~3 A& L' t. Q/ V  s
配置串口1,这里随便配置就行,在modbus移植过程中还会对串口重新初始化:

3 n# R( d; x( B5 E  V' ~/ j0 C
3 P% W2 e* |% s% t1 p- r9 v1 M U25OQ_FHYD5WEXLFMI4V@RU.png # V, t$ n0 g: }1 x3 p; H$ _% z

6 j2 b2 J7 {8 C0 F配置定时器4,用于3.5个字符的定时检测,这里随便配置就行,在modbus移植过程中还会对定时器重新初始化:

) Q& f+ h3 N, ?5 X- H0 t* j0 `7 |3 y0 P) w5 U1 o/ o
H6{)WT`**CIHDAM02558GK.png 6 p8 m( W8 J. i$ v& V
2 U5 r6 m$ h3 O" g2 E
中断配置,这里注意,串口的优先级是要比定时器优先高的:

$ T6 i" b1 Z: \# _5 G4 q& ?& b: v) q' _0 V$ Y' B
G(RP`74TR)NPJFUK``Z%I2L.png
+ S! L5 t) K2 s) l+ J8 C- g+ Z& v取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序:
2 k, }  t" N- s3 L2 X! [
& z/ W& a9 k7 {0 Q2 c ALHMK5Y615K6(`M{0[]YJ.png % x6 i4 }. ], z) g+ G
2 k+ B- f1 C/ U6 P6 S  p1 ~
移植代码修改

# w* e# s9 Z) a- k( c0 [! j  t# P生成代码后将modbus放到工程目录下:
2 o2 w. a) t4 T
" f) k1 {# [" p( c! B
5%(_LMGIM_8_YGL`]JGAB67.png
, o/ ?. I9 \( x/ D) N$ X9 D$ D3 X- x3 t2 v5 V
打开keil工程添加modbus源码:

+ |5 H0 Y2 k0 C  ]" Z. U! q7 J
5 J# x" b7 O& `5 J/ E F`8_YFACMNOAP28~2VHD{CO.png " A1 g/ ?$ g9 Q% y3 C2 k" s

: w- ^. ^* J" h) O! @添加包含头文件:

7 F) C- m0 t; t4 J: P! w5 w( ?  R1 ^$ {( e" E4 ~* A5 f! h
`7BMVXA`R9SQBTGT`B@{]X7.png
0 G, G8 e1 {8 ^  o
) O9 p! k; R( Q& k" K" z* x修改modbus定时器初始化源代码porttimer.c文件
7 g- R2 k" L! d) n  ]       定时器的修改比较容易,将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达;由于我们在STM32CUBEMX中取消掉了定时器和串口的中断服务函数程序,所以我们在该文件中添加定时器的中断服务程序,修改后的代码如下:
% e7 \: b2 u6 f6 F% y; R, E; V4 C& |! w- {
  1. BOOL3 `, o4 j" W# d" f
  2. xMBPortTimersInit( USHORT usTim1Timerout50us )3 n% S9 T  [6 \- O2 }+ a5 [
  3. {5 [+ E$ i) o+ N. I8 ?$ N) M
  4.     TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    $ J7 B5 p: ~: a* r/ B
  5.     TIM_MasterConfigTypeDef sMasterConfig = {0};
    - D0 K2 p9 A2 r2 |0 r+ v) O% e

  6. 9 p. D8 i! {7 M
  7.     htim4.Instance = TIM4;
      y7 R' C( s" K" K0 B
  8.     htim4.Init.Prescaler = 3599;                                                                // 50us记一次数' N* X2 Q# ]; s% ~
  9.     htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
    % d. e2 t6 h; X0 u# P
  10.     htim4.Init.Period = usTim1Timerout50us - 1;                                        // usTim1Timerout50us * 50即为定时器溢出时间
    9 _7 V! ~; ?* D- g  ]
  11.     htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    : D* o0 \4 K7 ?& B$ t
  12.     htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;: P' v, Y$ _9 Y/ {9 n2 @+ q
  13.     if (HAL_TIM_Base_Init(&htim4) != HAL_OK)# t) U, @' Y. k3 _' A1 M
  14.     {/ X' M/ R4 y" K& {, |9 [
  15.         return FALSE;
    ( o/ t+ U9 {9 N6 P3 F
  16.     }
    & H% m- t) B. s" L
  17.     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    8 b1 L+ ~& N) R
  18.     if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
    & m; ~, U# i0 [/ Y$ b
  19.     {$ d1 F: D% E" |, H
  20.         return FALSE;1 X7 u6 u8 c0 l! K4 A5 g: v% {
  21.     }
    5 h, p1 C9 {0 R5 o( T' X
  22.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;7 p* y2 n6 V8 f) s
  23.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;: k' F7 l, e+ S* Q! T5 }
  24.     if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
    : s& s6 Q0 M7 X/ k* C4 Z% W/ o7 T
  25.     {
    . t8 R% F( ~4 J
  26.         return FALSE;
    + S& {: s* P/ x5 O$ {2 M5 t
  27.     }  `2 R& H% f5 N  w
  28. 5 V% A9 O: u& w: D5 n8 u9 L
  29.     __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);                                        // 使能定时器更新中断
    ( q7 a( S6 a- Q3 W' f
  30.     return TRUE;2 \2 }6 v8 b3 S
  31. }6 j4 V; s' |; v% e$ X* S# o6 `
  32. " e) W9 c& S* |
  33. inline void4 L" f- O; o! m4 p% c. b5 y
  34. vMBPortTimersEnable(  )4 F, P) L7 \: D. u' u- z0 t1 y
  35. {" I' w& V/ B  o' a8 `
  36.     __HAL_TIM_SET_COUNTER(&htim4, 0);                // 清空计数器7 w7 o! E, l1 ?, W2 ^& E0 L8 h
  37.     __HAL_TIM_ENABLE(&htim4);                                // 使能定时器: H( t) C1 ^- h$ V4 f
  38. }3 l( N7 r; f: @. E: P

  39. 3 a0 B7 \: }: C* L, ?/ P) {9 x; P  M- F; R
  40. inline void
      v0 N3 A+ {/ b( y$ U
  41. vMBPortTimersDisable(  )" z9 X( c9 e. u7 u
  42. {
    $ n6 `9 Q7 m$ J% y+ Q
  43.     __HAL_TIM_DISABLE(&htim4);                                // 禁能定时器4 c; K7 _4 S) r: ~, x- b
  44. }- T, [7 M% i' I( y1 }# C3 A9 y3 S

  45. 2 g3 Z5 t! A1 ^( m! c. X& q( e
  46. /* Create an ISR which is called whenever the timer has expired. This function
    5 N/ E/ o1 z7 k1 H
  47. * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that' f9 ~+ f  M3 R! c
  48. * the timer has expired.
    ; P3 i0 L$ m  T! V! e
  49. */' d0 L! F! I5 W
  50. static void prvvTIMERExpiredISR( void )
    " v" [4 o7 m3 m' ]4 H2 _
  51. {
    $ T8 c8 c" V/ L9 k
  52.     ( void )pxMBPortCBTimerExpired(  );3 A7 O1 j* E" y1 ]
  53. }1 r7 P5 d: ?, W# q: t) k

  54. . w3 g+ D( v6 K3 i. h! C
  55. /// 定时器4中断服务程序
    + X( \$ h" s" u7 _6 y
  56. void TIM4_IRQHandler(void)
      h+ B: Y8 }: r
  57. {/ u6 }& K" ^2 O& b1 `
  58.     if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))                        // 更新中断标记被置位
    , M+ _* D! d& C. V! Q' \9 \, E
  59.     {
    ! m( V. ^1 |" k" C% M8 H& ]
  60.         __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);                // 清除中断标记
    ( U  }' `  R, g% ]5 b
  61.         prvvTIMERExpiredISR();                                                                // 通知modbus3.5个字符等待时间到
    ' u% U$ G6 y) Q* G4 D; I8 c( }
  62.     }( P: T% x# F) L( W% a
  63. }
    8 l) _2 u  P# R1 }

  64. % a6 q! f; L+ ]/ ]
  65. ; i5 p- q2 X- r0 m: }
复制代码
$ Z2 W+ j5 Q! @. h" \( J0 \
修改modbus串口初始化源代码portserial.c文件7 \. ^/ Z5 Y. |1 P( d9 G$ l+ J
         ~~~~~~~~        在该文件中实现串口1的中断服务程序,prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了,后者的作用为通知modbus串口1有数据到达,修改后的代码如下:
  Q2 F0 R" c/ \2 _) H- x# x  i4 w+ A" I0 U% S
  1. /*& I# `1 o- C9 M" b/ L0 U* O! A
  2. * FreeModbus Libary: BARE Port# U( @6 _3 }. h1 E1 c
  3. * Copyright (C) 2006 Christian Walter <<a href="mailto:wolti@sil.at">wolti@sil.at</a>>
    * I  p: v4 x! ]( m) b8 z+ N1 b+ W
  4. *4 t" l6 b. `; k4 w3 u
  5. * This library is free software; you can redistribute it and/or
    * _; f" j0 m" C4 o: T6 t' s
  6. * modify it under the terms of the GNU Lesser General Public
    ! n$ j7 _* p7 B3 b) u
  7. * License as published by the Free Software Foundation; either
    : I  ^, l  _* c* v& N7 ]
  8. * version 2.1 of the License, or (at your option) any later version.
    . N* g& s. D2 F* E! s  O/ D) H) F
  9. *. X+ p* j6 Y3 }( s- @6 v9 `
  10. * This library is distributed in the hope that it will be useful,
    4 Q- X# F4 T+ V# V* o& o7 c4 q
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of: l* [1 M4 c- X6 g: a6 H
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU. w: Q1 k+ t3 w6 I% r6 j8 g; Q+ \0 e
  13. * Lesser General Public License for more details./ }+ z1 J8 U3 S/ K4 m
  14. *) ?/ P% n( o- r1 l
  15. * You should have received a copy of the GNU Lesser General Public
      d& a2 l/ J; f; l( c( ?8 Z
  16. * License along with this library; if not, write to the Free Software
    ' }4 |6 H6 ^9 L% M. z
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA7 G4 p! r: n8 f+ P6 ?$ U
  18. *8 y; ]: l; D8 e6 N% R
  19. * File: $Id$8 U4 I& x3 n. M4 A8 O9 P
  20. */1 u  w, t( w; j
  21. ( L3 b( D) X% i- }: r9 d' a: Q0 p
  22. #include "port.h"3 J1 j2 q- A2 J% I- k7 {2 o: r) ^
  23. #include "usart.h"! {0 m* Z, D- j# H. r

  24. 3 P! n; K0 C0 e* [; Z
  25. /* ----------------------- Modbus includes ----------------------------------*/+ ~) O' e0 _0 f  n; t
  26. #include "mb.h"
    , x- _, X# X+ w. C
  27. #include "mbport.h", {' U- P- h/ Z& M

  28. $ e7 u% e, k' L: l9 @- W* x2 x+ _
  29. /* ----------------------- static functions ---------------------------------*/
    ) f8 X) X6 C# A
  30. static void prvvUARTTxReadyISR( void );
    % R1 j( `9 E6 j6 E
  31. static void prvvUARTRxISR( void );( u- D& L* q$ M: x! k/ L

  32. " B4 _, C) |* U2 [  ~+ T; v
  33. /* ----------------------- Start implementation -----------------------------*/6 D* _. v3 o) y  |" u' R
  34. void/ k. y) n& y( C5 q+ \
  35. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
    - z7 k( _0 C3 |! s
  36. {; Y5 r3 V. v  z2 E) Z
  37.     if(xRxEnable)
    ! S- C  d1 @" g4 R
  38.     {
    6 ?1 b6 o+ y! A
  39.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断- i2 B( a7 p% [
  40.     }5 _7 L! L; ^5 s% V
  41.     else0 W% \3 {3 M8 O4 R6 Q
  42.     {, s4 U- G! V4 f
  43.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断+ @$ `  H+ z- N. @( ]* t
  44.     }6 L: p: ]4 |- a! l. P: @8 `

  45. ( T9 B6 |% J+ D1 j, d! X% D
  46.     if(xTxEnable)4 m4 `) x$ y& {& \2 |/ Q" U
  47.     {
    ' G* T* {% y4 f+ m! X
  48.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断7 X5 ~1 r+ S' `4 G! z- e8 D
  49.     }+ m3 a  }4 @( I# D) g" P' }7 X8 b
  50.     else
    7 C6 K. A$ T! b4 O
  51.     {7 y. y8 l: b8 K) |, Z" b7 T
  52.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断% H& |( y) [/ v( Y& n! k
  53.     }: n9 [, \6 O2 y6 D2 Y1 H
  54. }
    : m/ j  u( o* }) n* R: g' W
  55. + L) v+ D" i5 G! M* @9 Q4 ~
  56. BOOL
    1 k, q1 p4 g/ y& F3 z  o
  57. xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )% H+ ]+ ?, [% U
  58. {! }* q6 I* J* }  d) p
  59.     huart1.Instance = USART1;5 u% n3 w$ h: W/ f0 W+ n
  60.     huart1.Init.BaudRate = ulBaudRate;+ P5 L2 n) o9 y2 A# [2 p$ z
  61.     huart1.Init.StopBits = UART_STOPBITS_1;4 ^2 \5 P& i' m  ]
  62.     huart1.Init.Mode = UART_MODE_TX_RX;
    3 m2 R) d1 g4 X9 E: }' M- z
  63.     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    ; q# w8 N1 q' W% I$ D3 r
  64.     huart1.Init.OverSampling = UART_OVERSAMPLING_16;8 f  O7 ^" S7 \" T" w8 [! z
  65. . M, J1 b) n$ `: v( K$ ?1 D
  66.     switch(eParity)5 m2 G+ f9 S: U3 B' t' l1 a6 b
  67.     {9 Q: p9 M% A8 M7 F( Y
  68.         // 奇校验
    * B' u2 F6 {& r) G0 h2 w, g
  69.     case MB_PAR_ODD:
    # I* ?5 O  k5 V, W! u2 d
  70.         huart1.Init.Parity = UART_PARITY_ODD;' F5 I( C( Y) w) i. h- z- S  a' }
  71.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits
    * [  a" R  ^& \! j# M5 e
  72.         break;
    . \% X. Z2 L; Z5 _# K2 V1 _
  73.         - z+ d/ Z$ I! Y& V' @' U
  74.         // 偶校验
    1 ^4 K! c' p% [5 d. a% a8 ]3 `
  75.     case MB_PAR_EVEN:
    1 H6 z: m7 R5 i/ p: G
  76.         huart1.Init.Parity = UART_PARITY_EVEN;
    & }- H7 D, q: d$ h8 F! C
  77.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits& `: [9 M/ I- u9 Z! g. a% `
  78.         break;# Q9 U9 h& H/ r
  79.         + _3 q0 @; l; @
  80.         // 无校验
    5 i! l- E$ s# }; g) j% u% E
  81.     default:: f, X6 T$ ^9 l! U
  82.         huart1.Init.Parity = UART_PARITY_NONE;
    8 ?7 A! j7 R. E( g
  83.         huart1.Init.WordLength = UART_WORDLENGTH_8B;                        // 无奇偶校验数据位为8bits
    . Y. U6 V2 W2 Q6 _+ M7 Y& b- N
  84.         break;* `1 L3 p9 z" r5 o& {
  85.     }
    1 _# r$ z; t9 A; z2 Q
  86.     return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;# f9 h  b. D% ^- Z% J
  87. }5 ^3 |* l: c# i9 W

  88. % B0 }0 _4 Z9 c% k8 k
  89. BOOL; h5 M0 |! }0 Q1 k
  90. xMBPortSerialPutByte( CHAR ucByte )- p' U: g2 |: J+ w' I
  91. {
    . B+ s- g% ~  q" o1 H+ }( i4 a7 g
  92.     USART1->DR = ucByte;
    & X. T" A& A. f1 n
  93.     return TRUE;  I2 g2 I% R4 D, U, d* {) i* ?
  94. }: l( C( `. \8 U1 B6 n# M8 ?
  95. 9 ~) r4 R* A. F% O
  96. BOOL
    6 d2 l) G; |* M. [9 K8 M. D, [
  97. xMBPortSerialGetByte( CHAR * pucByte )
    $ c/ A* ^# M) ^) W) \7 b. R
  98. {. Z% O& A: r7 r& {& Q. i- G  y
  99.     *pucByte = (USART1->DR & (uint16_t)0x00FF);
    ! `5 n; j2 u  a* X5 E+ ]
  100.         return TRUE;! W$ [$ n: C0 z& E
  101. }! L1 A0 Q( t" Z; e  [

  102. : z, _& }4 E! t& x6 K2 ~
  103. /* Create an interrupt handler for the transmit buffer empty interrupt
      @. P: R2 ?. S+ T5 V' H7 ]0 F
  104. * (or an equivalent) for your target processor. This function should then( C' H+ B) h7 ^1 G8 ~1 \: |3 {
  105. * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
    3 C7 ?. b* V; d: l* ~6 w6 G1 |( _
  106. * a new character can be sent. The protocol stack will then call: G3 A  v& T# F) h4 I# @5 K
  107. * xMBPortSerialPutByte( ) to send the character.
    / H! X7 [3 i; \; R
  108. */' B0 N4 |$ ?3 H0 G/ w
  109. static void prvvUARTTxReadyISR( void )5 x/ n7 {* |: r# X. g
  110. {
    ) a5 i. R9 \# z  e) O
  111.     pxMBFrameCBTransmitterEmpty(  );
    6 e( ~( ~( l/ P6 b. ?8 s( e
  112. }) f7 l' i4 [, i# S

  113. 1 z4 V1 P# e5 y' q+ H" @# z( G$ h
  114. /* Create an interrupt handler for the receive interrupt for your target
    : I' Z* _8 c1 H' ^
  115. * processor. This function should then call pxMBFrameCBByteReceived( ). The
    * @8 |1 Z% Y: C& c2 e9 B
  116. * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the  s8 r( F* i% m, P8 b
  117. * character.
    # \) }4 \4 I5 @+ S5 Z& `1 s# w
  118. */, `* \, V8 h3 o
  119. static void prvvUARTRxISR( void )% P3 z' A9 V0 s7 e+ [
  120. {
    ) k$ ~% p4 I5 E) _8 J8 P, o& m1 d
  121.     pxMBFrameCBByteReceived(  );
    3 z  y  Q1 ~3 h5 e* k
  122. }
    % N+ b* w6 e% E; H* f4 g

  123. $ S0 [- c  {5 y& G
  124. void USART1_IRQHandler(void)( _* l; I: u7 G& p( p& L
  125. {* \0 J) a) `/ c: d, N9 o  d' x
  126.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))                        // 接收非空中断标记被置位! P' z1 N3 w8 x9 h5 `3 J, R
  127.     {: X3 `* O% T! _9 ?2 b
  128.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);                        // 清除中断标记: M1 p- G; W0 T
  129.         prvvUARTRxISR();                                                                                // 通知modbus有数据到达
    8 V4 [2 I' C( J5 N
  130.     }* N/ l/ T5 j9 j9 n$ h; d' _

  131. & V7 i  h$ U7 Z4 K2 `4 Y
  132.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))                                // 发送为空中断标记被置位
    ! x$ u( ^2 L6 h0 I* W( q( r, M( t
  133.     {
    : D$ U6 E9 L/ p* j' {5 i# P5 P
  134.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);                        // 清除中断标记
    . j% E. u1 T6 |6 u
  135.         prvvUARTTxReadyISR();                                                                        // 通知modbus数据可以发松
    % l$ B" @% o; o  ^$ {3 l2 N
  136.     }
      b7 R+ n% D5 l" z" s% o( h5 l
  137. }
复制代码

* X7 {( P( n6 X# @7 [/ _注意一点,一般如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样:; s1 [& ~- T3 C: h0 G) p1 H4 x4 a
+ C  e3 b& ~8 [; r. e
  1. void8 R* t0 D0 l3 a$ Q3 W
  2. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
    , l+ m2 _+ v" Q2 A: ]
  3. {
    ( {; q% r8 j; W; o* Q4 Q
  4.     if(xRxEnable)
    8 k. z# g5 a& s' j& w
  5.     {
    6 L: y! z( m, P; N  n' }" J
  6.             //  \3 ^: [- b* H( a* q2 a9 O
  7.         // 在此处将485芯片设置为接收模式
    + @7 O# r6 V, ?0 U0 ]5 D: L2 f) ?
  8.         //
    6 V* ^" F, O- i) O
  9.                 /* do something */& I8 n9 x: o# E' x( j% s% ^- b& e
  10.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断& K3 p* z! M  H
  11.     }" n. m" O- x7 z
  12.     else4 ]% R- f. E0 F. C
  13.     {
    - y0 p2 n% o9 F0 z
  14.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断4 j1 o1 c9 s" I/ X4 S& c1 a- H0 M
  15.     }
    3 f( P3 M2 o) F: Z
  16. # P2 F+ s2 D7 r7 C' u
  17.     if(xTxEnable)% y3 U2 D& }* y: Q; b7 K8 |. \2 M
  18.     {1 W; T% A. F  u1 {
  19.             //
    " u, k. E& A; a7 K7 ~8 o& G, j
  20.         // 在此处将485芯片设置为发送模式+ B! h& B, n& T* x! d; X; @
  21.         //
    * K+ K3 F& h# j+ A; ?5 P
  22.         /* do something */
    9 h# i; F/ s7 R: L
  23.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断: i, D1 h8 P5 n3 ?
  24.     }
    1 e: E* U) u; Z' ~
  25.     else( ^7 F: n" u$ z- W! n
  26.     {
    4 ~3 d# T) n5 S7 s: E7 a0 E& F& n
  27.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断
    : W" K& E# X, P; |, _$ |
  28.     }
    8 ?+ U* H4 `- w3 \, l
  29. }
复制代码

$ B5 ^. r1 g+ L' J8 U  k3 ]
编写modbus命令处理回调函数port.c文件
# N( D( ^/ j0 c本例程只实现了读取输入寄存器和保持寄存器的功能,详细代码如下:& o3 P) v* p) E

& L) }# r# Q0 `9 i. r0 b
  1. #include "mb.h"
    7 s2 p# A  S- u' i* ?- T. U- v0 D
  2. #include "mbport.h"
    7 y+ q  z# J6 w3 ?6 h( Q/ s7 k

  3. 1 k. ]' O! r0 u

  4. , X3 N( N, [' o
  5. // 十路输入寄存器
    - y2 r, C6 {5 M6 l
  6. #define REG_INPUT_SIZE  10
    ) n" g" L: q/ B
  7. uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];# [4 ?7 Y4 ^/ Z! f/ W5 I

  8. 0 O2 w" ]3 w! r3 ?
  9. - h' ~. c  y" [* y
  10. // 十路保持寄存器. W- u* c6 U2 B0 p/ U
  11. #define REG_HOLD_SIZE   10
    6 R3 n2 g- A2 l
  12. uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
    ' T7 u* {6 s$ X5 t, b

  13. 3 t6 ~& |  G0 d7 L  J* G5 [
  14. ! ?% b3 p. ^! r0 @7 T" P. E8 t/ c
  15. // 十路线圈# Y/ t  B9 b0 J
  16. #define REG_COILS_SIZE 10
    ) i- ^4 v4 k% G) }4 Q1 U7 b6 h
  17. uint8_t REG_COILS_BUF[REG_COILS_SIZE];
    5 k% S: _  C* }5 J7 x' G+ H
  18. 9 E# {. V+ s  t8 U, Z+ X

  19. , ?% X1 \- c1 l1 u
  20. // 十路离散量( S7 s+ E! ]- a
  21. #define REG_DISC_SIZE  102 V& `3 }/ e  ^! r; \' s
  22. uint8_t REG_DISC_BUF[10];4 Q+ d4 n  ?) g, d( p
  23. $ `3 C2 V+ D2 b+ D* q

  24. + C7 F9 L1 ]) z+ ^' x) K
  25. /// CMD46 J0 o. I6 J( X& U) x  ^
  26. eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )# J# q+ J; E0 Q+ a1 `
  27. {
    . j% G) W. S7 r0 m5 q' ^
  28.     USHORT usRegIndex = usAddress - 1; . u  g* r6 u7 b" X" D9 ]% m+ L
  29. , C5 g) A- |$ I+ z. v
  30.     // 非法检测
    : {/ D9 s- n" q- g2 h8 z# B" O
  31.     if((usRegIndex + usNRegs) > REG_INPUT_SIZE)9 M3 E8 s8 I+ z  _
  32.     {8 f) {0 Z0 N: C1 {( t+ U. s
  33.         return MB_ENOREG;0 R. T4 s6 D9 r& {8 G' V) {6 P" z
  34.     }) f# T9 ?0 Z. _6 `# o( P$ s" Z
  35. . O6 \" L3 ^/ V- s3 i; B& Z0 z  ?
  36.     // 循环读取
    8 M  q0 ~* |/ V% ^
  37.     while( usNRegs > 0 )) L! U, Y8 i. o
  38.     {5 V, O8 {* d4 f& g
  39.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );5 W' k7 f) c! _7 A5 ]
  40.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );: g' d' }6 t' v
  41.         usRegIndex++;
    0 S1 [- H' P- J4 {4 t3 o; W
  42.         usNRegs--;; t1 p# a" d- c2 R2 i  f: J+ R
  43.     }+ n  @" h7 W5 i8 A0 o

  44. , o) l' D& n7 n2 G; {2 \
  45.     // 模拟输入寄存器被改变
    2 a/ u2 @& ?; Q
  46.     for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
    0 C  K- R( P4 q, w5 b# K0 D
  47.     {; E/ t+ w* }) R3 \* y# b
  48.         REG_INPUT_BUF[usRegIndex]++;
    4 t1 ~4 r" `$ ^0 b' L# d2 a* q
  49.     }3 O% |7 y+ t6 X- P+ I, }

  50. - D( U5 N  O' Q) Z  V0 t
  51.     return MB_ENOERR;
    # T( `, B1 v, t* R! \
  52. }4 ^& l, z; L6 v1 |. O- o4 o

  53. 5 D7 e# |$ ^! E( U% L! \  x
  54. /// CMD6、3、16
    ; c8 V' H7 ^5 D; W
  55. eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
    ! T* W7 ~' ^- N' n
  56. {/ y4 {8 ]9 D7 Z. w
  57.     USHORT usRegIndex = usAddress - 1;  
    9 P1 L5 _6 f5 A

  58. 0 q( I0 o# ]/ E5 g& T! ~, Z8 w
  59.     // 非法检测
    ) Y( @2 {0 B9 J' K3 U' Q: Q! T
  60.     if((usRegIndex + usNRegs) > REG_HOLD_SIZE)+ ]0 c0 n% }4 o# w; T9 E& I
  61.     {
    * @: ?: A' {% x! {
  62.         return MB_ENOREG;
    / q3 q; H1 N7 d, p# O! d, n
  63.     }
    7 k" B( Q* \+ Z3 S7 ]& {+ }

  64. 6 y# t7 k1 C: T
  65.         // 写寄存器
    2 C1 F3 n- q+ `: V% m
  66.     if(eMode == MB_REG_WRITE). p" C# E; U& @. b0 Z
  67.     {1 n9 E# {  L2 e9 U, M. A- E. l! Z
  68.         while( usNRegs > 0 )5 x  k# e  @6 J
  69.         {
    ! i/ _6 ]5 q4 D4 p/ |4 A2 A
  70.                         REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
    : M% J+ m; J. _/ F* d+ ]; c+ m' @
  71.                         pucRegBuffer += 2;9 {, Y9 v4 @: D5 W3 F
  72.             usRegIndex++;3 ~8 h3 M- B$ ~' w
  73.             usNRegs--;
    , a. S2 ?8 R6 \7 I
  74.         }
    . t" n* t  c# a, M+ @
  75.     }/ I0 x8 E2 g) e1 F% ], |* V. k8 G
  76.         
    : @0 h3 |( a: N# F# U/ E# v
  77.         // 读寄存器; {4 l; z# b/ X) F1 M
  78.     else
    6 z& E0 L1 w" H) a0 I
  79.     {
    ! n4 g: W6 \. T4 `0 O5 h
  80.         while( usNRegs > 0 )9 J8 Z0 ]1 |& j# P
  81.         {
    - t. J/ J6 g3 t: B7 |) ~
  82.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
    + Y7 C! B+ |; M
  83.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
    / g. q& U# Y1 |, ^2 u7 _& m
  84.             usRegIndex++;" ~3 U& Q& K( z2 q! b
  85.             usNRegs--;* b. k2 @' j0 [. L7 I: D
  86.         }8 t/ h+ a$ y; v( N* \
  87.     }
    ! ~8 s& ]9 F/ I1 o' _$ k. n

  88. ' B( e$ e- ]- G3 D
  89.     return MB_ENOERR;
    - G- o5 O* Q& G
  90. }  m' O9 K; }% ?4 Z) |% r/ s

  91. # T3 p; B4 |6 m, p6 y: i
  92. /// CMD1、5、152 d& o/ X( ~# W
  93. eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
    6 b  S1 K- O' G% G% _3 B/ @8 u' Q
  94. {' C. k, Y  N, T4 H9 k: t
  95.     USHORT usRegIndex   = usAddress - 1;- Y0 G7 q% Y) c" _2 T
  96.     USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);, N# A& m/ k  v8 I/ {: k
  97.     UCHAR  ucStatus     = 0;+ i0 a& l* J% ?
  98.     UCHAR  ucBits       = 0;* P. i" V# Z/ U3 W. v. `) m5 ~
  99.     UCHAR  ucDisp       = 0;2 p. {! V+ N8 j" A! S5 k8 I% E
  100. # Q" P2 ^" f( L. h
  101.     // 非法检测1 l! C. ~  @8 T' X# H
  102.     if((usRegIndex + usNCoils) > REG_COILS_SIZE)# r1 B; L' u/ s0 ?
  103.     {5 w% V: d- t% {2 e% r& X
  104.         return MB_ENOREG;
    8 X- Z2 e" W5 y1 M
  105.     }
      h( s! k7 d' {" g9 k6 y
  106. 0 g: t, v) X6 a( K% x, h
  107.     // 写线圈1 A# [7 U5 b9 @: j' [; c
  108.     if(eMode == MB_REG_WRITE)
    6 j" j4 q6 s  y( y
  109.     {7 W8 }1 e' U! r5 X
  110.         while(usCoilGroups--)
    . L3 r3 g& |- o
  111.         {: @$ c; n( p# `# X& @% X
  112.             ucStatus = *pucRegBuffer++;7 {6 S1 e! p0 w* L2 F7 Y( A
  113.             ucBits   = 8;
    0 U- }7 s' d' k2 @; Q% H$ T4 L# {2 b
  114.             while((usNCoils--) != 0 && (ucBits--) != 0)* l$ h; z9 [& W7 H0 q, S' s
  115.             {
    ! K6 Z  `0 v- ^, l  S& k
  116.                 REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;' @9 D1 P+ `8 |
  117.                 ucStatus >>= 1;3 h9 \% z% N/ x1 a* ^6 `4 S& m2 W
  118.             }
    , K. h/ z# A6 z) G" p* P
  119.         }
    $ U3 M4 z5 Q$ m3 H
  120.     }: t3 y4 T4 L4 d9 q( B  P3 u
  121. 3 G* K# k7 C5 g, ]6 v9 ?3 _# h
  122.     // 读线圈4 f" ^( [; V  V. S
  123.     else4 K& I' u9 G1 B3 n6 H
  124.     {
    : b4 t( m; W; n( t+ E
  125.         while(usCoilGroups--)
    ! I! t" c# c9 w& p0 D1 C
  126.         {( o" ~$ M* z9 z- g9 j% z
  127.             ucDisp = 0;  ?$ m  X  K7 l( V
  128.             ucBits = 8;
    + c$ S4 H- p  D: p
  129.             while((usNCoils--) != 0 && (ucBits--) != 0)/ N5 O5 b" \9 A$ ^9 M+ @$ O
  130.             {* K3 b. ~5 L( D* J  x
  131.                 ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));0 W. T# \& o) n- _
  132.             }& J) g& J# s0 B
  133.             *pucRegBuffer++ = ucStatus;% y* m' q: C/ d* m
  134.         }
    9 H. \4 h* b" [4 X0 r
  135.     }$ j& W% x2 f7 h& T0 @+ t& E! N; r- T
  136.     return MB_ENOERR;
    5 l" Q3 q! R* S) ?  ~: u7 L, c' p
  137. }8 v( N  s: [! n9 T) o# H$ A
  138. 5 U3 \$ U( r5 N8 g. h# C$ q

  139.   `) t  C) l/ S! r, w2 _/ b5 ~0 P
  140. /// CMD4
    4 g7 w/ @3 h) R
  141. eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )/ r/ i4 ~' U# e, g# H( Y% h, `8 p# t
  142. {
    1 r( E3 g9 h5 \( @4 r  z1 M& z
  143.     USHORT usRegIndex   = usAddress - 1;8 J: Q5 [4 q' T6 E, y8 ]0 k& _: |
  144.     USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);/ V. ^" u! R0 b9 P
  145.     UCHAR  ucStatus     = 0;3 _: Q4 s/ }3 I% O  r) l6 u) _! Q
  146.     UCHAR  ucBits       = 0;
    ; \+ `1 E2 w4 h( a
  147.     UCHAR  ucDisp       = 0;6 U8 V& W0 s: D

  148. ) o! V- x/ s; t* z3 |: U7 G
  149.     // 非法检测' }# {0 _4 k2 A$ x+ F, Q& E+ |0 p8 w, E
  150.     if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)/ }! C, G, U9 Z9 g
  151.     {
    + V: P* R3 D% `0 G
  152.         return MB_ENOREG;+ G- I1 O# [/ X/ t
  153.     }
    8 K% L8 [/ W6 k" j( C3 E; A# p4 T# O) M
  154. 7 q" V1 Q3 D9 T: q
  155.         // 读离散输入
    9 z) t# w; K3 Y3 v5 _9 T
  156.         while(usCoilGroups--)# X7 H  y8 ], ~: M6 y+ C
  157.         {( n$ o/ b, E' A  k9 t
  158.                 ucDisp = 0;
    + S7 d% K* Z& q9 l( U7 W
  159.                 ucBits = 8;9 j, {# z3 b6 u9 R
  160.                 while((usNDiscrete--) != 0 && (ucBits--) != 0)% O! G5 {& F6 e& J  G# U& o0 w
  161.                 {
    ) R6 F, [  Q9 {4 m8 q
  162.                         if(REG_DISC_BUF[usRegIndex])" b. O9 ~  i) r1 R: V3 O) \/ m! {* D
  163.                         {
    / {# b; C! n5 T9 J$ f
  164.                                 ucStatus |= (1 << ucDisp);
    . c; N/ S6 L" g
  165.                         }
    7 o5 `' C4 p* n1 H& j
  166.                         ucDisp++;( p2 |/ c' u4 }& ], j# N% b; C$ B
  167.                 }
    % {( \9 j+ N* \/ }
  168.                 *pucRegBuffer++ = ucStatus;( H$ I# t; C5 M* x6 p
  169.         }$ c" d2 y5 B( N/ c5 p

  170. 1 n) ^4 m& B- K  P4 H
  171.     // 模拟改变
    + u7 J0 |; L. J, E% H$ z
  172.     for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
    / r: n! G( x1 p) j2 e, o9 b. f% d
  173.     {
    : E* l3 d  a" H/ M* [% Z
  174.         REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
      Q: ?6 d2 T9 ], I) h
  175.     }
    $ Q6 t5 P; }' S" ^

  176. - r1 V4 g2 Z7 s; `& O$ x
  177.     return MB_ENOERR;. |1 `. I2 c$ G
  178. }
复制代码

6 M4 |. }4 D- U1 ?2 U主函数

( s# ]' f3 T" z
  1. int main(void)" |" I; H: C, ~4 [
  2. {
    7 y# t  R( Q4 q1 a, ]* y1 Y! \
  3.     HAL_Init();
    ; Q1 ^$ r! a* ~2 _3 m( h
  4.     SystemClock_Config();
    , P+ B+ C: r( n: H( J" k0 L
  5.     MX_GPIO_Init();
    : k. g; F0 v( c" `4 M. q' v" T
  6.     eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD);                // 初始化modbus为RTU方式,波特率9600,奇校验# X0 {( S6 q* z$ J# M+ |
  7.     eMBEnable();                                                                        // 使能modbus协议栈
    6 V, [# k! I' o, T6 t3 l
  8. 7 v% n; j- Y4 c* h' J& ?
  9.     for( ;; )
    , B* ^8 h# {8 K, @( X5 g
  10.     {
    7 ?. F- n+ W* _7 m- C* @
  11.         eMBPoll();                                                                        // 轮训查询' |, i. n) N6 F  x! Q: ~9 |
  12.     }: }- G. `' j: @2 `
  13. }
复制代码

" ]1 @$ T: `" L/ q; h# L& Z移植测试# Z3 F; J0 b! U' p' n
+ N2 D: V" J5 X
20200313154857173.gif ) E& B8 |( ^: j5 o1 {

) s+ M. H. J2 E' h9 Y7 vends…
) q) }# s$ [) ]# c) B2 g8 K
$ r4 E3 ~+ Y2 A! P+ D5 ^6 Y3 ~& w# d8 \, c
9 c* J1 Z# s4 ?" f9 K. b

点评

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 手机版