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

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

[复制链接]
STMCU小助手 发布时间:2022-4-10 22:53
modbus命令码表:. J7 L( U# j3 H' x( z  E  ^# V% Q
& c0 j% G7 K1 L- n/ K) i
b8032deb4d154657acd2f7f2942369d4.jpg & Q% h. V$ V7 i: X: o
9 i* D$ U7 m. `( h8 n, A& V$ X
FreeModbus文件说明
+ @/ w9 v' \3 I+ B( h. Q
      解压freemodbus文件后打开,我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。
$ w' u0 g9 g$ _1 [ 20200313143257958.png 7 I# ?* x8 k8 [' S9 V- B

; l& J% a4 t- {5 T8 T% ?      mobus文件夹就是完整的源码,包含rtu、ascii、tcp:/ e! p( G/ P3 U  G  R

3 L! s& `$ {& M# j/ Y& O" W5 ? ZM%DLXPRB8$%[NN2%4Z1N.png ' }/ O1 O1 P  k6 y8 U" y, e
7 |6 b4 M5 v9 [7 o0 a8 W5 D' w
      我为了移植时在keil添加源文件和头文件方便,就把modbus所有的头文件和源文件放到了一个文件夹下,并创建了一个port.c文件,用于编写modbus所必需的回调处理函数:
5 Y) O, ^+ `! W8 z' x& |
: m% a# o# w7 {1 [ 20200313144036579.png
3 h* l; ]. H, ~2 M4 {  K3 \5 y+ X6 `- v" n" M4 [
STM32CUBEMX配置
' l" j# b. w6 |6 ^& G7 q- @3 @
时钟配置,设置主频工作在72MHz下:
4 I3 }' |6 {4 E( Q# a# K4 Y

8 b; c9 q  h  A+ Y, e JU1~N33M}I7K1(6K@JFN5~R.png
8 i6 `) `9 R) X, y9 g8 g
$ B) @" @6 N7 E; t# g配置串口1,这里随便配置就行,在modbus移植过程中还会对串口重新初始化:

( b# F7 p$ T) f  O2 S9 _, T$ g: L, s( w# B6 m
U25OQ_FHYD5WEXLFMI4V@RU.png 3 B# `) C" K6 z# G6 G
- C8 T# r1 t! [3 y$ v6 c) X
配置定时器4,用于3.5个字符的定时检测,这里随便配置就行,在modbus移植过程中还会对定时器重新初始化:
( w4 V% ]/ V# B6 u- v
8 Q4 D5 W1 x! |
H6{)WT`**CIHDAM02558GK.png
7 A6 K5 j1 f2 v( q) V7 c/ l  [& t
7 w8 k/ {$ X$ f中断配置,这里注意,串口的优先级是要比定时器优先高的:

* c+ y% L4 D( E
& c' V# Z2 z( y/ L$ T G(RP`74TR)NPJFUK``Z%I2L.png
% h5 ^8 p* v4 l8 ]2 l取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序:
2 z2 }* `3 n- f4 F" {
  S: e" p% A* i5 n ALHMK5Y615K6(`M{0[]YJ.png , K0 @6 ]$ S4 J' j5 t7 i; g
1 K+ J0 C  G' q
移植代码修改

1 {/ z5 \8 w$ t2 m生成代码后将modbus放到工程目录下:/ A+ D4 A9 J$ B+ C9 Z% R
) w1 S  X. k2 E9 o
5%(_LMGIM_8_YGL`]JGAB67.png   V& x6 j3 E: J2 W7 R9 L7 S
# o0 O1 I% F1 J# B" @
打开keil工程添加modbus源码:
% t* w. a2 i) K8 E

6 @2 L1 m  T3 E/ g8 m  { F`8_YFACMNOAP28~2VHD{CO.png
+ v+ w% F8 m' r" @# Q3 }2 z; S( Q% D3 o: o3 X0 K  M/ G
添加包含头文件:
9 F! M' Z" w' P6 t! D$ ?5 g# \- k  Q
; F- s8 F. P4 M1 N: z! E
`7BMVXA`R9SQBTGT`B@{]X7.png
+ l) h: ]! \  N7 o, r, X% v% x
; H$ ?& a  \' W修改modbus定时器初始化源代码porttimer.c文件0 B, J. F/ ^6 h: {5 c9 V+ p# R3 H
       定时器的修改比较容易,将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达;由于我们在STM32CUBEMX中取消掉了定时器和串口的中断服务函数程序,所以我们在该文件中添加定时器的中断服务程序,修改后的代码如下:4 t* h7 w' E) a* }1 B9 x0 F

3 ^  T2 J0 _) U) o- g
  1. BOOL
    " P$ {7 B# K0 j& G) ~' U. ^
  2. xMBPortTimersInit( USHORT usTim1Timerout50us )) w7 b0 O0 {5 Q/ a( ~" N
  3. {
    0 o. a8 }- p8 t, w/ H" n4 a
  4.     TIM_ClockConfigTypeDef sClockSourceConfig = {0};5 O, N+ n1 `; ^2 v0 E# s$ l
  5.     TIM_MasterConfigTypeDef sMasterConfig = {0};
    7 |: K% M  {9 ~, k
  6. 0 t" _1 |' o# V
  7.     htim4.Instance = TIM4;( {* G7 A3 ^: H" I) O% X
  8.     htim4.Init.Prescaler = 3599;                                                                // 50us记一次数
    6 K6 C( Y, G7 T( S; @. N8 l" t
  9.     htim4.Init.CounterMode = TIM_COUNTERMODE_UP;2 p: h3 o) Z0 r( G, z
  10.     htim4.Init.Period = usTim1Timerout50us - 1;                                        // usTim1Timerout50us * 50即为定时器溢出时间" F- a6 z1 k" X2 T- Q
  11.     htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;4 ]8 h- v- c' K8 T) d9 A: x3 w
  12.     htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;. ^+ ^  X6 U: D, j# g7 b
  13.     if (HAL_TIM_Base_Init(&htim4) != HAL_OK)+ E" g, S# Y7 ?  @# E4 t  M# ~
  14.     {" r) p7 }! G& p9 t5 w* D
  15.         return FALSE;' @+ l: H2 g- s5 u' T
  16.     }+ i* N6 _! p- {7 i
  17.     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
    % C3 Z4 g0 F, Q$ M8 X3 b: z
  18.     if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
    , B2 R; d' o6 A# K/ s9 D9 _
  19.     {* ^" u+ O2 Y, y
  20.         return FALSE;
    , p' Q7 u3 f$ c6 y: q: x) S' t5 ~9 L
  21.     }
    2 N/ ?* I- T" L; H
  22.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;1 s7 W( n) s# J) {
  23.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;" H0 r) n* [# b% S# }
  24.     if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
    # ?4 W# N. V( }
  25.     {# ~: j/ @/ H4 A8 |% m
  26.         return FALSE;* [# ?( Z/ Y3 w) u: k8 A1 H
  27.     }( c$ C8 `* _: x4 h. H" F7 Q; H# s! g
  28. - ~4 i- _6 k$ t6 t! s% W6 N( G* H
  29.     __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);                                        // 使能定时器更新中断
    " L; i4 [# s) W& U1 o
  30.     return TRUE;
    ( B- T2 y- G' H/ k$ H8 F5 q: E
  31. }. \. ?7 R7 E, A3 l1 v2 V1 f) L
  32. : e3 e! `# A3 l$ Z& Y
  33. inline void
    3 Z; k- I* [! W% d* v
  34. vMBPortTimersEnable(  )2 i, i; m, u6 F3 m
  35. {
    ) P  D0 k, U4 C" f9 D+ ?+ y  b2 v
  36.     __HAL_TIM_SET_COUNTER(&htim4, 0);                // 清空计数器! H/ h3 C$ U" X* ~+ z. p& P
  37.     __HAL_TIM_ENABLE(&htim4);                                // 使能定时器
    4 U& v- K- t" X4 i# C5 T
  38. }$ X# M) S- q% N- h; L5 w$ N

  39. 2 T! \6 d/ E! q2 H4 P0 a+ S/ |/ l
  40. inline void
    5 N* Y$ i3 A2 L4 C+ J7 l6 b
  41. vMBPortTimersDisable(  )4 I6 ~- T' c0 p5 b: E
  42. {
    + V6 C0 l1 x3 s7 f- W5 q
  43.     __HAL_TIM_DISABLE(&htim4);                                // 禁能定时器2 L8 K% v) s; K1 l# p$ b, n
  44. }
    ' d  ~! t4 g. x) s& r" w

  45. & }0 m' p( e9 i; K- a, J# M  [
  46. /* Create an ISR which is called whenever the timer has expired. This function4 m2 ]) A4 J7 Z0 q2 P9 c0 S
  47. * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that; L9 B1 e# u# ]1 L# Z+ V: E# J
  48. * the timer has expired.
    5 H: t  |  k8 T: M1 \, M
  49. */% u0 }8 D' k- `3 r7 J
  50. static void prvvTIMERExpiredISR( void )! }! R* K1 a4 J3 r$ _& N. [
  51. {
    6 c0 ?: U6 M2 G5 g  d
  52.     ( void )pxMBPortCBTimerExpired(  );4 E  d; ~0 l" P/ d0 m) u* B
  53. }! h6 h4 E; Q& M9 H  E: V( I7 P) D3 I
  54.   `; a) }$ g: A1 \
  55. /// 定时器4中断服务程序
    $ Q! B1 ?3 I$ i$ {4 L8 o5 h: l3 ?
  56. void TIM4_IRQHandler(void)
    9 W/ u: Y. O' i; N: c: n+ @
  57. {& b+ e2 U$ J0 ]/ h8 [
  58.     if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))                        // 更新中断标记被置位. G9 }) v7 e( Z7 q
  59.     {0 M" L; s8 d" v! y
  60.         __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);                // 清除中断标记
    7 n5 N9 v  a  H, B1 w! @: G( [
  61.         prvvTIMERExpiredISR();                                                                // 通知modbus3.5个字符等待时间到4 J* h* |/ j+ q9 k) a" ?
  62.     }/ K# T5 ?+ ?& C; U9 A( C9 W
  63. }, X5 Z3 ^' t! u: _4 u; P) Q0 o6 {
  64. , \' B% R; h3 t. o

  65. ; A% f- B7 ?& P  H3 V% [& |
复制代码
' r! J. }# o: s' v. j' z- i3 x( ^
修改modbus串口初始化源代码portserial.c文件
) X7 A: X* j; J: l4 r         ~~~~~~~~        在该文件中实现串口1的中断服务程序,prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了,后者的作用为通知modbus串口1有数据到达,修改后的代码如下:' ]) G" f; _7 W/ M5 c
8 B; \4 ?' u, x* {3 A: M# Z
  1. /*
    1 Y0 j7 t; E, Z
  2. * FreeModbus Libary: BARE Port
    + Q; J( A- x! \$ i
  3. * Copyright (C) 2006 Christian Walter <<a href="mailto:wolti@sil.at">wolti@sil.at</a>>& M/ l7 Y+ h& c2 x
  4. *
    & Q6 [3 ~& C0 N* v- r& }: V
  5. * This library is free software; you can redistribute it and/or+ _# |3 l2 D# I* t5 W* @
  6. * modify it under the terms of the GNU Lesser General Public2 U1 q* W- i6 o" W
  7. * License as published by the Free Software Foundation; either
    2 k! y! g/ v& D. m9 ?
  8. * version 2.1 of the License, or (at your option) any later version.- L+ e  i/ O5 ^2 Y% A
  9. *& l3 u" F& q1 |- k  W/ ~
  10. * This library is distributed in the hope that it will be useful,
    0 X4 G& X! F- B1 z
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2 a; b9 ~/ [' E/ u! b9 a! F: t
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU2 E  T- n! w: _7 P/ ]! T& W
  13. * Lesser General Public License for more details.
    & m) R1 b( P* U1 k& E
  14. *9 {3 k% y# Y. W
  15. * You should have received a copy of the GNU Lesser General Public
    : P2 ~9 G- |9 }7 a+ k5 s$ B
  16. * License along with this library; if not, write to the Free Software6 ~  W( F4 j9 X
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA8 I4 L7 |+ w& R$ O3 ]0 J7 F" N( W. c
  18. ** U2 S+ y6 a8 B% S% T$ F
  19. * File: $Id$
    / ^3 [& H3 Z5 w, {
  20. */
    1 H! n7 w; \9 A- f# @( P
  21. , n. e" p( I6 H# G) H
  22. #include "port.h"  V( e  t8 c, f3 |- n5 f
  23. #include "usart.h"' ]$ C( d" M! b9 I* j! v" g- i2 ^
  24. 5 s9 v! c, {1 F9 H
  25. /* ----------------------- Modbus includes ----------------------------------*/$ [2 `& p. l" d
  26. #include "mb.h"
    - ^9 u# j' }8 S: {) g
  27. #include "mbport.h"
    3 k; b' Q5 C6 P( Y0 w

  28. 6 E0 P; \$ l2 Z4 o& ?9 f. D2 n: ^/ k
  29. /* ----------------------- static functions ---------------------------------*/
    6 C7 e# p2 q( @3 z' [9 i
  30. static void prvvUARTTxReadyISR( void );0 a& l. i) }$ n, O9 C. e1 T
  31. static void prvvUARTRxISR( void );
    . ]0 V; j) R; l; R5 c
  32. " m+ H' h9 T9 v5 r
  33. /* ----------------------- Start implementation -----------------------------*/
    * }0 G* a5 \. _& k+ O0 q. S
  34. void# O! J4 q. p% m& @
  35. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
    8 o* F% m+ q9 {5 K0 J- X1 k5 |# ~) V
  36. {
    . s$ i9 B, Q; J9 f: i
  37.     if(xRxEnable)
    1 d# b; l9 f/ ?3 G
  38.     {% g7 l; g" _! N! `+ z
  39.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断# |2 a* S, ~( f
  40.     }
    / b& i4 a. e) e" H( f$ w! K
  41.     else
    * |& N4 C/ m4 k! x. y. C1 C% A
  42.     {
    / G1 S' x. C( J% w  N: u/ m8 o) f
  43.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断2 Z5 Q& O" S- }
  44.     }$ E! f6 ?- D2 {% s; r
  45. - K  G1 k6 x# r/ C* v9 |& t+ Q1 N
  46.     if(xTxEnable)
    5 ^1 ?7 I7 {% `4 v2 T: ^% T' j
  47.     {4 m$ D' E; C8 \3 Y
  48.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断
    1 a. s9 A" e4 q3 P3 V4 W! ]
  49.     }
    ; r, I  D% \! j" T/ V0 O& @# Z
  50.     else% \) j3 A) x8 c3 _
  51.     {6 p# c$ F) W- a3 f7 n& s
  52.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断
    # K+ ]& O: C& Y% B0 l- h
  53.     }
    ! `& h9 g$ P. u9 M8 |# J
  54. }0 K1 F  h- w$ I. V+ O

  55. 4 K  E9 J* T) ?5 V& `2 J
  56. BOOL
    7 ~" t7 S. k- K: Z, H1 k, |
  57. xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
    $ \, K5 q6 ?* T& f
  58. {+ K% t; b3 T/ @5 s/ |% |
  59.     huart1.Instance = USART1;# c9 j% A; _7 ?  ^6 Y# ?. K
  60.     huart1.Init.BaudRate = ulBaudRate;
    - S" ]: B$ F1 o9 \( G
  61.     huart1.Init.StopBits = UART_STOPBITS_1;( u0 W! X0 e# l( F' }
  62.     huart1.Init.Mode = UART_MODE_TX_RX;
    9 m* Y2 w5 e; V3 \
  63.     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;. W8 Q* }: y& ~1 I  Y, o
  64.     huart1.Init.OverSampling = UART_OVERSAMPLING_16;) z8 z- J. C9 `

  65. $ w, v1 x0 x9 h  ]3 {# h6 S8 n
  66.     switch(eParity)# Y3 U9 Q) B, I3 m
  67.     {
    # _9 U* v$ A; M  p9 k2 e# I
  68.         // 奇校验( D: H! y' H( w$ D- l$ }
  69.     case MB_PAR_ODD:
    / e/ n5 H3 f" S, S, |4 W* P3 s: T
  70.         huart1.Init.Parity = UART_PARITY_ODD;4 z. w  o0 ]* D5 t
  71.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits( L7 g. u+ ~% \- p
  72.         break;
    . z: C, G6 d. Q" ?* a( ]( I4 b
  73.         
      |* p0 V/ m% n. r
  74.         // 偶校验
    ( G1 f/ N6 O* Q& d* ]
  75.     case MB_PAR_EVEN:
    # N) Y6 j2 h1 l3 S+ z/ N, Z2 k
  76.         huart1.Init.Parity = UART_PARITY_EVEN;5 [- l+ g8 U/ D5 ^6 o* w! B8 j
  77.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits
    3 s" K. z$ _; R8 {* L9 R. A
  78.         break;+ f* i8 g  c9 U8 U3 ?% o
  79.         
    3 P$ E& r: Z1 r& [0 P8 s4 p  ?8 {
  80.         // 无校验1 \& p/ x5 H' V' c5 k
  81.     default:; I( l# o/ y) {8 `- ?9 }
  82.         huart1.Init.Parity = UART_PARITY_NONE;# A5 B( d* C1 |' A
  83.         huart1.Init.WordLength = UART_WORDLENGTH_8B;                        // 无奇偶校验数据位为8bits9 H, j9 Q5 {  T9 ]/ [7 I
  84.         break;
    + j! I+ e( s- n
  85.     }
    5 n5 R; d7 @( D! z, g, P( ]( b# F' V
  86.     return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;7 M8 d5 r! h- ]8 O- Z7 r' G9 r$ a
  87. }
    . r: r7 L% N# K3 c
  88. 5 l5 P1 K3 B6 a& Y& D
  89. BOOL
    4 Q% o# @" N& q) p8 Q
  90. xMBPortSerialPutByte( CHAR ucByte )* {2 d) ?- j6 l' D/ p
  91. {
    ! j' m, R% O! B7 z' ~7 `
  92.     USART1->DR = ucByte;8 z6 J' I0 H( J6 r% c6 p9 ^; E' M
  93.     return TRUE;
    2 ^* J; |# I- ?# o8 }' L
  94. }" }, x; X1 G8 Q; K) u

  95. * Z- u: N) A9 j3 V* A
  96. BOOL
    6 g1 e! u/ Q* s; w
  97. xMBPortSerialGetByte( CHAR * pucByte )
    + d3 y* h- \* a8 Z, I) ?
  98. {
    * W0 ?. u& |9 W' a# R0 g
  99.     *pucByte = (USART1->DR & (uint16_t)0x00FF);
    + V* r- E4 k2 s
  100.         return TRUE;; v; ]2 [# c7 N8 q6 O' O& z, D
  101. }/ M3 i; V0 d9 P7 |' k3 a* t4 a

  102.   y7 R9 r! z4 H0 X3 v7 @8 y& C
  103. /* Create an interrupt handler for the transmit buffer empty interrupt
    1 Y. S4 e5 x: a+ W
  104. * (or an equivalent) for your target processor. This function should then
      z1 l( j0 v9 m( B$ A1 A4 j+ E+ V: S3 b1 C
  105. * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
    " |5 ?  O6 P% o" ]# t7 w: b
  106. * a new character can be sent. The protocol stack will then call3 v1 ^1 F3 ]6 w  A6 D- d
  107. * xMBPortSerialPutByte( ) to send the character.9 \( h; U( ~9 U( ^* P9 d: |6 G0 W
  108. */4 Z4 m( U( e! R! t4 r
  109. static void prvvUARTTxReadyISR( void )4 q7 c/ v5 S6 c& T9 L  z
  110. {& S, X) A. \" W) U# a; M" z9 ]- X& B
  111.     pxMBFrameCBTransmitterEmpty(  );, ], f" k- U+ Q* |# q% h0 d
  112. }5 L" l! S8 e) I$ |" u# |

  113. ! n; x4 l( N# S2 t" _
  114. /* Create an interrupt handler for the receive interrupt for your target, x. P5 b' j( Y+ g
  115. * processor. This function should then call pxMBFrameCBByteReceived( ). The
    6 C% x+ {. x7 G: J6 ~5 c+ u% u$ s
  116. * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
    , r* ]& g7 S& b* J% R7 o) f
  117. * character.
    + e! }5 J1 w# x* Z! U
  118. */+ h0 x  n9 B) q4 w2 e
  119. static void prvvUARTRxISR( void )
    - U8 d% P5 D3 w, o
  120. {
    1 W9 [' A9 u- Z/ ]' ~5 t
  121.     pxMBFrameCBByteReceived(  );
    ) t  i% \+ B1 {; H2 f& o, S
  122. }
    " Z5 d& Z% h' M$ Q' a2 ~* p" u

  123. % R! w( @, [- @' e9 O! z/ w
  124. void USART1_IRQHandler(void)
    9 k) {9 r* H: k  C- }7 H3 Q
  125. {
    9 i0 c, r3 A* h8 a4 o
  126.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))                        // 接收非空中断标记被置位. {' ~' B* r: {4 M( c
  127.     {
      L! d  u/ g9 a) F  D$ A+ ]# j
  128.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);                        // 清除中断标记
    + a# r& y  O3 [0 I7 n
  129.         prvvUARTRxISR();                                                                                // 通知modbus有数据到达
    ) O5 _- w" M* C! s  M) |
  130.     }! l- `, m0 g/ N4 @  T4 A

  131. ; B, K4 W4 f1 s# }+ T2 c
  132.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))                                // 发送为空中断标记被置位, G7 D! X- @# e" [" k% ]) q% g
  133.     {- s! U4 u4 ^# u+ D3 _
  134.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);                        // 清除中断标记) `0 x5 A3 `6 E# D/ S9 m5 Y# E2 u" I
  135.         prvvUARTTxReadyISR();                                                                        // 通知modbus数据可以发松
      Q9 J! k6 M! c9 B
  136.     }) q: F- I2 B1 b9 d
  137. }
复制代码

) P6 G2 b9 \/ X注意一点,一般如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样:
  e* N+ q* Z  I2 u2 z! `! Y7 K# m0 m1 C
  1. void8 i8 Y) ~# l# _8 v( E1 |" j
  2. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )) A! I* q6 i/ e# A
  3. {
    , j( ]* p% |4 N0 \1 S3 b! Y( M
  4.     if(xRxEnable)
    ( U6 F: W# c2 U6 Q* [: u
  5.     {
    ; G" {# D  ~7 _+ n* [
  6.             //
    - a% }! u7 ~8 o* }  _
  7.         // 在此处将485芯片设置为接收模式
    ) _# E* y5 w# |- J& r2 V
  8.         //
      O+ c, U7 p3 W* E
  9.                 /* do something */( `  V( o9 _6 G. T9 Z+ I/ I
  10.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断0 }5 m5 X. m( s" Q4 V( u9 |0 K
  11.     }
    3 X7 \% e6 v1 _, {
  12.     else/ i$ o! Y  d! V7 j: I* Z$ K
  13.     {
    , T& r0 w# d$ L) o- ~( x
  14.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断" r& m( c9 O. X
  15.     }
    4 _/ A5 a" s/ }$ e5 M/ b

  16. # n, Y1 X- Q5 s! Y1 S
  17.     if(xTxEnable)
    - t6 U' W2 K* N3 j7 t
  18.     {
    " ~- o* N; b/ q# n6 ?. K' @
  19.             //9 O' ~+ q# _: u3 {7 q2 W/ a
  20.         // 在此处将485芯片设置为发送模式. ^' e/ e$ J; W6 r1 h  [- U* `
  21.         //7 e& K9 b' L$ R$ k4 N; c1 L4 D7 X6 c6 s
  22.         /* do something */
    " [: Y, f  s7 v* z/ Y
  23.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断
    " b( t# p  e, d
  24.     }# x3 u1 y6 H: c: @- `
  25.     else
    8 ^! U" w2 l2 ~/ T9 u8 g8 p
  26.     {
    , ^# F9 I0 Y9 N" N3 Y( W) n# B
  27.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断* K$ o& R$ v* t
  28.     }
    + e& \# {( Y; _" d
  29. }
复制代码

7 S% P  G) C0 F* M8 r7 \
编写modbus命令处理回调函数port.c文件8 v) ^! V6 F. X  D( \
本例程只实现了读取输入寄存器和保持寄存器的功能,详细代码如下:  j8 G* K% Q* {

- K- \3 U# X& w& A, P2 o: n! I0 Y
  1. #include "mb.h"
    $ ]% x' C: ^! b
  2. #include "mbport.h"
    2 Y6 i2 q) i: R+ o3 Y

  3. ( _/ t( y& K$ ~1 a
  4. 7 ?; q) b3 ?. e) A, L
  5. // 十路输入寄存器
    ! P5 w+ a2 A4 {: z% [
  6. #define REG_INPUT_SIZE  10
    8 b& n8 I# r; }  e
  7. uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
    2 e( A. m- ~4 H) K7 D' D& ^, ]

  8.   W) L  Z' |6 L
  9. + J+ B0 \' r6 w1 T, t. h8 \. f0 X- H
  10. // 十路保持寄存器
    ' Q: U0 R+ l0 l2 l$ \. p
  11. #define REG_HOLD_SIZE   10
    1 U* |. |( g' b* J$ F8 q
  12. uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
    ; T' @3 {! a. i0 H
  13. 8 W7 [% [. R( Y% \4 T; @% |
  14. ' o' H0 B( v  s: _4 X" T* ?6 H
  15. // 十路线圈6 o. N( [, d% b0 T9 X# q
  16. #define REG_COILS_SIZE 10
    8 c4 s. `( N3 q! \8 S
  17. uint8_t REG_COILS_BUF[REG_COILS_SIZE];9 r8 _* m; y% o  Y% ~2 v0 S3 \
  18. : G1 X1 F! D/ {& s2 w) S- x- ^2 g

  19. 7 Z, `+ H2 ^" A" H3 }) Z$ Z2 @" j
  20. // 十路离散量
    ' Q  ]9 ]9 p% q; e, x
  21. #define REG_DISC_SIZE  102 f2 G  C4 f; B
  22. uint8_t REG_DISC_BUF[10];. K( t' {2 y3 w! X0 F) v4 J+ L0 y

  23. . d2 n: E6 Z6 ?% g6 G9 w* k8 C
  24. ( n: U! v3 [( A2 R6 P3 B$ i! A
  25. /// CMD4
    9 T6 B" S& Q2 l0 K
  26. eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
    & z+ |; t- X6 u7 ?  z  i
  27. {# S, x$ e8 c1 g2 |0 z
  28.     USHORT usRegIndex = usAddress - 1;
    0 d+ H' G, ^1 ^6 A1 ~

  29. ) ^' q1 ~2 Y3 t# Q; g9 b1 e
  30.     // 非法检测
    : i  A" _2 }* a; t  L
  31.     if((usRegIndex + usNRegs) > REG_INPUT_SIZE)) ^- g* f4 v2 j4 u
  32.     {
    % V3 s9 v& N% w5 B
  33.         return MB_ENOREG;
      g  M) ?6 J2 u6 q
  34.     }4 O. W7 }6 ]' ]

  35. / p% f. v6 w5 q9 |4 b; y
  36.     // 循环读取
    0 b- l& I) ?6 \! f; S+ i) a  T8 z
  37.     while( usNRegs > 0 )$ {3 _6 v9 [& z% z2 |
  38.     {7 \, _" E( H* d
  39.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
    8 c' p! b- L) T5 z* j
  40.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );& v* [9 P7 b( R: g7 }
  41.         usRegIndex++;1 p7 a: {- \  D5 c! T
  42.         usNRegs--;
    % p" u' V6 W, t1 b5 f- ~3 g
  43.     }4 R' m' m5 V2 e7 e
  44. : {; W, J) F9 l. a) ]# g
  45.     // 模拟输入寄存器被改变6 v( ^' {: g, C' s/ g  g% {
  46.     for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
    " _& w4 t- {" |, M& b4 o  M
  47.     {
    - Z3 q+ \8 g6 t3 H' A
  48.         REG_INPUT_BUF[usRegIndex]++;
    1 u) S# w1 ]; Q- X" b
  49.     }
    0 C0 p' H: M, R
  50. ! o/ s% }% w# }8 u4 s
  51.     return MB_ENOERR;! T8 ]& Z/ ?0 {0 f' Q2 J
  52. }! f- X8 y6 s% K' }$ Y4 N  r+ s

  53.   A& ^! j4 C+ f  n0 P; t( a: I
  54. /// CMD6、3、163 i& D5 [9 o0 W3 J5 B8 _+ j
  55. eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
    5 p# S# p" S3 Q  v5 Q
  56. {! o8 H' u) w" j3 |
  57.     USHORT usRegIndex = usAddress - 1;  
    " K: o. t; {( v( |& i; L

  58. . O. m$ c9 W; T! z
  59.     // 非法检测
    # n# p2 z: l* }; M0 U/ F, e- P
  60.     if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
    6 t0 Q: q6 h' |6 z' s+ ^
  61.     {) P) ^( [. ]& u" ]% u) }5 k
  62.         return MB_ENOREG;
    * U7 D4 ~* [0 V- R* R
  63.     }7 c$ u6 n; t2 N! R
  64.   H( f. M) q5 _- s( X- y; X, H
  65.         // 写寄存器) W4 {# I. N" ]% e/ \: J
  66.     if(eMode == MB_REG_WRITE): l+ s' D* z! G- G0 m$ b! o
  67.     {8 f/ Z6 B$ \( B
  68.         while( usNRegs > 0 )" _! F* q5 ?; ?' B4 d# W0 @
  69.         {- i$ F  o' R- r' F1 B; Y
  70.                         REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
    , w3 o( a0 M3 M) ~& h
  71.                         pucRegBuffer += 2;
    9 d' M3 L. L6 _* l+ m7 e
  72.             usRegIndex++;
    7 C8 Q3 A7 @4 o4 z
  73.             usNRegs--;; V$ _0 ]" ^4 i) l$ {: V& S, O
  74.         }
    / j  A/ v8 L7 ]9 W3 H# k; o
  75.     }+ ]( ]6 G* I3 @7 l. O* P5 S. s. s
  76.         
    7 B; a$ y$ G3 M7 K4 x4 G. V* Z. m, @
  77.         // 读寄存器# S5 \& w+ f/ @1 Z# V! ~+ q
  78.     else; r+ e. t- P# V0 ]
  79.     {
    ' A7 \+ ~- }- b( Z, u* T8 g
  80.         while( usNRegs > 0 )
    - m( p9 l  n! y  ]3 w6 D4 w% o
  81.         {& @) W  n# B& B0 v6 \3 Y
  82.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );6 \0 f$ W4 m, K' N
  83.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );' n) s& s7 }) o6 P. B* L, w2 W- R) H
  84.             usRegIndex++;4 b8 Q$ ?2 O0 D9 V( S0 n" ^
  85.             usNRegs--;5 s/ r( F" q" e( H
  86.         }) K( j% |* n+ q
  87.     }; B2 W% h0 F: q- W, ~  o0 q
  88. + b, u. n( f/ t' s( e
  89.     return MB_ENOERR;, x9 Y3 p/ b- K& A6 ^3 Q$ O
  90. }1 |! k# }, b4 R( E( O

  91. 8 u/ S  s3 c% K- P3 o7 N
  92. /// CMD1、5、15
    & F6 B, Y/ }0 l- \" c( [4 R; R
  93. eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
    5 F' i, l9 w2 D% J1 l, n# D3 f
  94. {
    # W0 s" U0 }  q
  95.     USHORT usRegIndex   = usAddress - 1;
    0 p% ]' F' P2 X
  96.     USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);# Z: y( u6 l+ K# u6 C
  97.     UCHAR  ucStatus     = 0;
    0 A, v/ S1 {2 ]% d
  98.     UCHAR  ucBits       = 0;
    # T4 g2 p9 B) ~0 c8 X# W% }9 b
  99.     UCHAR  ucDisp       = 0;4 V# G- F. n( i5 L/ ~6 l$ ?

  100. & z1 S9 {) W4 ?# A  i0 s
  101.     // 非法检测) v; D' Q& z5 H" c, P6 O) G
  102.     if((usRegIndex + usNCoils) > REG_COILS_SIZE)
    5 \* d, g$ N4 o  g- U
  103.     {9 y8 Z: A# x. L% w
  104.         return MB_ENOREG;
    + Z  x) H: y8 ]& P# m$ u5 O4 a& M  b: l
  105.     }% w+ y. `- p# O  A/ b, {5 J! m

  106. 7 P5 e8 h# w0 S: E
  107.     // 写线圈& s# Z( e% S* Q0 o2 G6 d
  108.     if(eMode == MB_REG_WRITE)
    , L' G  A9 T- H# K
  109.     {
    , b. b$ g# r( Z+ l$ V
  110.         while(usCoilGroups--)
    + ^- k1 i& @$ t- g2 n- L% _
  111.         {1 _- v# y# k4 j1 ~, q' ~* _6 f
  112.             ucStatus = *pucRegBuffer++;  h! W) \4 |/ L
  113.             ucBits   = 8;
    ) ]. T6 J0 C) C4 P( _( u; v9 R
  114.             while((usNCoils--) != 0 && (ucBits--) != 0)
    ! n: s7 B0 A  C" g( P. k
  115.             {
    8 `; V  I( Q, S/ M
  116.                 REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;
    ! q. g6 W/ r& d7 n
  117.                 ucStatus >>= 1;
    ; ~7 v+ f1 }4 t/ U+ B8 f$ W
  118.             }  O, v+ [5 D" h9 M
  119.         }  }# r2 m, g6 ^# h( k" Q9 |6 r
  120.     }- R5 p3 K; I$ {- t" X5 {1 X
  121. 7 y9 C! x2 j" `/ n3 o4 a9 S  S; J+ E
  122.     // 读线圈
    2 z% U% b9 T! K2 R9 k- k' G# m
  123.     else
    ! C; }4 P5 X' L0 @
  124.     {
    : a: [& P8 ]& B
  125.         while(usCoilGroups--)) n9 y( f2 Y. C! a
  126.         {
    7 N- M  h' k3 r8 K! h* `
  127.             ucDisp = 0;0 Y, D: d& ^. s. o2 Z
  128.             ucBits = 8;
    0 ]% P9 |$ c' h2 D2 o! I7 s
  129.             while((usNCoils--) != 0 && (ucBits--) != 0)
    # b$ g4 ?0 {( z
  130.             {! ]0 }2 [+ e6 d
  131.                 ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
    8 X/ C! y- }; w; I/ G$ M
  132.             }
    " }) ~- S  M" y  a4 l2 e
  133.             *pucRegBuffer++ = ucStatus;
    ! v4 K1 w0 e. `' d/ y# H' d$ K
  134.         }% Z2 d' T* m8 M5 k
  135.     }
    4 q# o; G0 I3 I' g9 x
  136.     return MB_ENOERR;# u3 h5 v3 q; E/ |' q5 T
  137. }  g- Z1 S3 u% b) w! R
  138. " u! p* C% b7 a; _, m

  139. & s) \6 g8 }6 Z; Y
  140. /// CMD4
    8 u5 f/ L0 W6 ?. ~8 O' z
  141. eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ), L3 a. Z7 ]/ n9 T1 H4 u5 [
  142. {
    6 O! v- W$ a$ j; k; e7 o( w! q( a" i
  143.     USHORT usRegIndex   = usAddress - 1;  l# W, L# ^- J1 m/ k
  144.     USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);5 w* n2 V% U+ s6 ?; p: F. E0 k& r
  145.     UCHAR  ucStatus     = 0;
    % ~9 r  [$ E/ j7 U, ?
  146.     UCHAR  ucBits       = 0;; G3 F/ B! t7 G3 R: K0 h2 C& V
  147.     UCHAR  ucDisp       = 0;' X; ~5 r0 a  [( n0 C
  148. " j# F$ ~; \* K
  149.     // 非法检测" |! I  K4 a8 W. K5 \
  150.     if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)- p: p; S: a7 _1 P3 P4 C% p
  151.     {1 d- c, i6 ~% I' ^" j
  152.         return MB_ENOREG;/ s6 \+ O  B$ G! A
  153.     }
    & Q/ u+ D) Q" m0 E6 O  B
  154. - X& O7 a5 h3 L2 }9 }; M! t2 d
  155.         // 读离散输入
    ; E# [' {" V0 [7 E
  156.         while(usCoilGroups--)
    7 M. z# D1 C7 |, l* E" c
  157.         {
    7 r( S# E4 W1 O" N0 n
  158.                 ucDisp = 0;* W* n6 F, w) M+ x7 h
  159.                 ucBits = 8;+ N- r% s/ C+ L5 n
  160.                 while((usNDiscrete--) != 0 && (ucBits--) != 0)# d: _. d# C3 g) i
  161.                 {
    5 r1 J5 F) e& J/ {0 Q+ r6 g
  162.                         if(REG_DISC_BUF[usRegIndex])
    5 c6 f% K8 y2 A0 i
  163.                         {3 Y7 |  \2 n6 j; h& L) V
  164.                                 ucStatus |= (1 << ucDisp);
    ( E5 Y, W* A; ~% q
  165.                         }
    & A8 j2 p2 l: y4 X0 \7 F. W
  166.                         ucDisp++;) h! N! @- u1 C0 L( W4 |, F
  167.                 }
    + @2 r5 o0 ?1 p9 }; t
  168.                 *pucRegBuffer++ = ucStatus;: |9 n7 [  v# L1 H' H5 ?4 H
  169.         }  q0 W' x* y1 L9 m: s/ _4 o( l
  170. 4 e" w6 T3 i; m/ e& G" c2 P% @# R
  171.     // 模拟改变
    - U! R* G& P- t5 C6 j9 m
  172.     for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
    5 U  W" q- X9 f  C$ [
  173.     {
    & e  \9 I, M  O& n- s, {* y
  174.         REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];* [+ U$ D2 j6 u! b
  175.     }
    ' V! `% \4 L" c+ I0 [3 ^  W3 h

  176. , M& W* z: C( Y6 f5 H
  177.     return MB_ENOERR;- n# Y0 o% p7 f  n
  178. }
复制代码

0 v4 H: N& d$ U- ]: Z. o主函数

  z$ i5 e: T5 U$ v: Y
  1. int main(void)* y) s5 Y+ M9 |- {
  2. {
    $ o" A: X& z: ?: l+ K  i
  3.     HAL_Init();
    9 _* n. m: H7 {; k8 K0 ?& o7 g1 X
  4.     SystemClock_Config();
    0 t. j5 ]& |! P: F$ m, i
  5.     MX_GPIO_Init();
    % _0 J8 I  K' d% X5 v
  6.     eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD);                // 初始化modbus为RTU方式,波特率9600,奇校验3 E8 ]% _$ D+ @! i2 ^
  7.     eMBEnable();                                                                        // 使能modbus协议栈' q, L' B* W: y0 m  F' d
  8. 6 w1 g5 x2 a# f1 y. F: m
  9.     for( ;; )" E  F* q- I  ?! `7 n6 x
  10.     {1 k6 C' Y6 w6 t% A& ^1 e
  11.         eMBPoll();                                                                        // 轮训查询( F# k' T; S/ x/ Q0 t8 |) `9 U) E
  12.     }
    0 Z5 V/ s9 U0 i& L! m5 U0 P
  13. }
复制代码

* j' r: y3 e6 R% j- K: {移植测试
" n' L- r& j; a; K3 \2 j, i5 B
% M4 w4 n* _$ I/ r% a; @! V/ J$ w 20200313154857173.gif
, e; G1 \, n0 M- R% R7 c$ O# Z
2 K: u/ D" f" R: ]5 H6 sends…
9 [3 k. h4 O' P6 |3 O" U( o+ ~  I, G5 y0 D* }" y6 u" N
- `) n9 a; S' e3 p7 q
  Z  u/ n( f5 F

点评

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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版