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

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

[复制链接]
STMCU小助手 发布时间:2022-4-10 22:53
modbus命令码表:& z- D: E+ H* q

) e+ d; G* W, v8 v+ I9 L/ ~2 t
b8032deb4d154657acd2f7f2942369d4.jpg
7 p+ _3 E3 s1 U5 I- d' C9 L9 K
2 ~5 \. U0 |/ K, m7 o( b4 ^( eFreeModbus文件说明
! U, K. @. w% E
      解压freemodbus文件后打开,我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。
$ K% S9 h; s/ H0 J 20200313143257958.png " Y, D; Q+ Q* G  J* M9 F' i
% d5 w5 P; k/ C0 \$ n1 s
      mobus文件夹就是完整的源码,包含rtu、ascii、tcp:. G! v, |+ o3 w) d( b

$ ?( c) j4 J- y, h ZM%DLXPRB8$%[NN2%4Z1N.png - M; H) G0 V+ N# T+ u

1 A" ^8 `# Q: v, r" l+ y. l2 G/ W- V      我为了移植时在keil添加源文件和头文件方便,就把modbus所有的头文件和源文件放到了一个文件夹下,并创建了一个port.c文件,用于编写modbus所必需的回调处理函数:4 n0 X! [0 K( M( f/ S% J
5 f9 `0 O8 f3 E7 L
20200313144036579.png
' u8 q- a( B  x( d
$ |2 y8 ]2 M! e! r* t: S' a: iSTM32CUBEMX配置
# u. P2 U' F# N3 `" ^+ U3 p- F
时钟配置,设置主频工作在72MHz下:
, p6 h, v  [% Q% I# o0 k! ~; r9 E
/ X9 q0 x* r: }/ {' |
JU1~N33M}I7K1(6K@JFN5~R.png
( c3 ^( k* e  d% X' f) N1 {$ D: ^2 _) ]( D: }3 {
配置串口1,这里随便配置就行,在modbus移植过程中还会对串口重新初始化:
0 q; I% n& `, B$ D: N2 Z% ~
. m" n  F2 J% n0 H
U25OQ_FHYD5WEXLFMI4V@RU.png
0 W: j: u( E1 i3 S) R7 @0 n. `7 Q3 \" y& R' f1 ]% B( B  W
配置定时器4,用于3.5个字符的定时检测,这里随便配置就行,在modbus移植过程中还会对定时器重新初始化:

9 o3 f9 C" y' x6 A0 o6 ?! t
+ Y2 [: o7 u; ]- T% F2 m3 ^ H6{)WT`**CIHDAM02558GK.png
/ w3 y( d% p4 ?; [- j3 c8 f! Y) ?0 J2 ~- R
中断配置,这里注意,串口的优先级是要比定时器优先高的:

2 R, w6 e3 A  d9 {, t- v
' N* v6 U) d1 B, ^4 O3 z/ ~4 k8 D G(RP`74TR)NPJFUK``Z%I2L.png ' E) ^5 E/ ?/ x8 v
取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序:. b$ P9 C) |7 K
, T0 r, n# [7 A+ [4 g
ALHMK5Y615K6(`M{0[]YJ.png
. \8 Y  g% m; p  t8 S( z/ H8 D% e4 }- j
移植代码修改
6 L% S' }/ s1 k1 I
生成代码后将modbus放到工程目录下:
; z, l" J; L/ e" `& l. ~3 K$ }, N6 |$ {2 |1 K& _
5%(_LMGIM_8_YGL`]JGAB67.png + _) Z4 |1 j/ J3 H# u( u8 c5 }+ D
5 t2 i" M( ^% V9 R! f5 w
打开keil工程添加modbus源码:

, |, r1 M$ Y, b& L- ]9 s+ A, j8 e3 p6 z1 O; _+ s8 g1 a
F`8_YFACMNOAP28~2VHD{CO.png
* m' s6 s; K0 q3 B% u9 v) |7 X
2 h, ^* A+ H& p添加包含头文件:
/ C5 C& J6 L4 [! H( |
+ J6 a! Q# E0 k  M
`7BMVXA`R9SQBTGT`B@{]X7.png + M" T# k- |. A& K) {* e9 H' ~% M

4 f4 V+ w4 ~. I! \  P修改modbus定时器初始化源代码porttimer.c文件6 J' r! z4 I/ v( i- u0 k
       定时器的修改比较容易,将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达;由于我们在STM32CUBEMX中取消掉了定时器和串口的中断服务函数程序,所以我们在该文件中添加定时器的中断服务程序,修改后的代码如下:
! }# \; K% I* P7 E# W0 p$ v" \' y, E3 s" _) b
  1. BOOL
    6 N) _7 S2 F) U- u
  2. xMBPortTimersInit( USHORT usTim1Timerout50us )' `2 ~* m! k& {$ o6 f3 h
  3. {$ y" o, R" @! T# `( C/ {- ~6 E
  4.     TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    1 ~2 ~& V/ @2 i/ Q) U" M, L7 l2 v, Q, U
  5.     TIM_MasterConfigTypeDef sMasterConfig = {0};
    : }7 ?( T0 J6 J; c

  6. % l" h% l- V4 b0 d' z, C, f, C5 u
  7.     htim4.Instance = TIM4;5 ^6 i1 F7 l4 Y7 y! H
  8.     htim4.Init.Prescaler = 3599;                                                                // 50us记一次数
      F# ~( V' i; a. H4 Z2 P
  9.     htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
    ) l: q, \1 S$ x( |0 o% j- {
  10.     htim4.Init.Period = usTim1Timerout50us - 1;                                        // usTim1Timerout50us * 50即为定时器溢出时间
    + c6 \! R: Q( r4 ~1 s
  11.     htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    2 q4 ]9 `% b9 R7 m- U1 ]
  12.     htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;, I5 |8 H" s% i: f6 g
  13.     if (HAL_TIM_Base_Init(&htim4) != HAL_OK)( T  o; C4 K* Q4 B2 X) ~+ S
  14.     {  a1 @" \: Q9 c  X$ h3 C6 t
  15.         return FALSE;+ L! \0 S! |6 u+ x. J) y% C
  16.     }. J0 x' D/ C! i4 _# M; R* ~, K% k
  17.     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
      Z4 G! n! d$ ~9 w# C
  18.     if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
    + f' k3 @! d. m
  19.     {& G% A5 V2 z& f2 G
  20.         return FALSE;
    , o# `2 _; Q0 A" d- l7 G0 T! x
  21.     }
    - c& w8 u9 [; m# S
  22.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;" U4 J4 J1 z" s; _. i/ B6 F* v
  23.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;* P' F8 I& U, P1 \
  24.     if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)2 P% K6 p# s2 j  Z  b0 B+ b
  25.     {
    8 V+ d% c9 R' n) e
  26.         return FALSE;0 ^! C# k* L  Q" p+ j. f
  27.     }
    2 m9 _9 s5 {2 N+ T
  28. ' }0 H% A9 n5 O0 x9 H) O% d
  29.     __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);                                        // 使能定时器更新中断
    - e. _- T; U" d( V! L
  30.     return TRUE;- Q) r( P9 o0 i- f# ~2 Q4 H
  31. }
      I% }% q9 c' Z5 V* x
  32. 5 M0 S5 j7 j  x' m
  33. inline void6 @# z" o) L$ m5 r1 I2 G
  34. vMBPortTimersEnable(  )6 D4 C/ E9 C: |  W
  35. {4 Q8 A- C& [) A  [: K/ f
  36.     __HAL_TIM_SET_COUNTER(&htim4, 0);                // 清空计数器
    + c' p4 Z2 e% ?( X- e
  37.     __HAL_TIM_ENABLE(&htim4);                                // 使能定时器' K0 ?5 w' M% n& r/ V6 O2 ]* H
  38. }
    ; z) k& o; k* j, s0 ]8 }

  39. # e& k9 d/ Q9 @: Z/ |- E4 _
  40. inline void
    & X' Q$ A* t4 e! h  Z( Q5 S
  41. vMBPortTimersDisable(  )7 ~5 N( J0 `& {. k9 B
  42. {
    # L; D9 L' ?  e. Q6 }- z5 r2 g
  43.     __HAL_TIM_DISABLE(&htim4);                                // 禁能定时器% x/ w" n1 e- v( X
  44. }4 ?( a! ~: C, ^! l! v

  45. 4 O) o; i: y/ A
  46. /* Create an ISR which is called whenever the timer has expired. This function
    ' _6 E! n) U* \' U3 E- F
  47. * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
    1 |- A: {) g4 O
  48. * the timer has expired.- ?# A0 e- [" {( v, r3 V) @  J
  49. */
    ) y/ T( w$ \6 Z7 E' h' F8 R( Q
  50. static void prvvTIMERExpiredISR( void )
    ! O4 G) `& d4 l# g( u+ x! L7 y
  51. {2 e& \- }2 H/ h6 p$ `+ F( S
  52.     ( void )pxMBPortCBTimerExpired(  );
    ; l- U% Z  v- a5 |! L* D% `
  53. }
    ' T4 U) a" k  I/ Y, v( O

  54. - D' w, h  p: E0 T
  55. /// 定时器4中断服务程序
    7 L# m% L* I/ p) I$ s
  56. void TIM4_IRQHandler(void)+ I- i. K# j: h0 H# V
  57. {. o1 x% R8 Q" b1 [9 E; c
  58.     if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))                        // 更新中断标记被置位
    0 u9 B3 R" o. }7 b7 E' M/ D/ w  O
  59.     {
    5 T% q! X) l/ E
  60.         __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);                // 清除中断标记
    + s3 N( b4 K4 A- [' Z
  61.         prvvTIMERExpiredISR();                                                                // 通知modbus3.5个字符等待时间到
    , T7 b4 B: U( h: M' {+ d- \8 A0 |
  62.     }% Y1 I( a/ S% P% j
  63. }
    * y! _, f) D  d: B
  64. - k* s7 D: C1 e. H1 O' ?; }4 _

  65. - g+ K- T) t+ a" Z5 N: i! `3 k
复制代码
3 P8 P; k& U) l* u
修改modbus串口初始化源代码portserial.c文件
8 G* ?+ }$ ~- V* x1 z9 I         ~~~~~~~~        在该文件中实现串口1的中断服务程序,prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了,后者的作用为通知modbus串口1有数据到达,修改后的代码如下:6 p) l) g/ C# Z
" p5 Q0 n  [- J* I
  1. /*
    1 x  M6 w7 x: D  }( F) ]6 E& B, z) O
  2. * FreeModbus Libary: BARE Port
    0 [  L' M- T+ l. ~5 G
  3. * Copyright (C) 2006 Christian Walter <<a href="mailto:wolti@sil.at">wolti@sil.at</a>>! k# K' x0 l0 }
  4. *9 E. }$ f6 o# {' Q# X2 y
  5. * This library is free software; you can redistribute it and/or% ~/ w8 f0 c/ H" c0 `$ Q; p& n
  6. * modify it under the terms of the GNU Lesser General Public+ J- }. T$ I# q: G) P* y. K! G
  7. * License as published by the Free Software Foundation; either
      m7 |0 C9 V! ?% r8 j
  8. * version 2.1 of the License, or (at your option) any later version.
    : y$ g' t6 k$ }# k) o# [
  9. *2 I0 G, ~1 \. [$ f
  10. * This library is distributed in the hope that it will be useful,
    1 T% C$ y1 ]! l; p1 z
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
    : ?: C+ J2 Y  s( b! P0 \' x% |2 n
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU! c0 }) K2 h) i0 X
  13. * Lesser General Public License for more details.1 b% ?; O# `. f; Q5 S
  14. *
    9 E' t+ B5 n1 P- j
  15. * You should have received a copy of the GNU Lesser General Public0 T* }. t# v" N" T1 c$ h4 M
  16. * License along with this library; if not, write to the Free Software
    $ p0 u. H( ]4 d
  17. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    # E, g+ L0 q- e6 i
  18. *; K1 T) @, Y. R' h7 Y
  19. * File: $Id$- e3 p+ b9 X' y
  20. */
    + i9 K+ _" Z" p$ j5 g

  21. 1 P! l. L+ b2 a  Q) y: \: j; w: n  h
  22. #include "port.h"
    & H) O, {$ p- d4 g/ w3 [, [
  23. #include "usart.h"6 X8 [3 K  U# o9 V- p3 H

  24. 7 c2 {1 K- O; g
  25. /* ----------------------- Modbus includes ----------------------------------*/% m" s3 @( g4 j- I5 y0 L
  26. #include "mb.h"
    + |% o2 k! E7 y# ]: z
  27. #include "mbport.h"
    : b" ~) [* k0 O' V  d

  28. $ L: K# I9 f8 p, s
  29. /* ----------------------- static functions ---------------------------------*/- `3 o- X3 t  ^0 v& X" k: K
  30. static void prvvUARTTxReadyISR( void );
    1 s3 |  U: I% E! D6 d
  31. static void prvvUARTRxISR( void );/ d! m3 M; t) g  O- C, q- ]

  32. 9 b/ p3 d/ E& c+ I- m
  33. /* ----------------------- Start implementation -----------------------------*/
    8 S1 {2 _- Y# h2 m" q5 w0 [
  34. void9 `$ _2 ^! W* _  S1 A
  35. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )2 y4 P. p* T+ q% z- t2 }
  36. {/ i, B$ I$ w6 S% ]- U
  37.     if(xRxEnable)
    9 u' M) `) ]8 v3 v6 P! O) ?: r
  38.     {
    ; H: E6 A6 \- A5 P
  39.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断/ n4 O3 K8 g$ W4 }
  40.     }
    : a; L- q+ l$ C, k
  41.     else7 M: \0 E; b& `' _  ~5 C8 D2 W- c. N
  42.     {$ Y* r+ t' I& y6 `  E
  43.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断
    , X) j' Z+ z$ }. x7 w& O
  44.     }/ t0 _$ E( J; M! g, |- ~
  45. & |$ B; @0 r0 H& ]; w9 g1 }! r
  46.     if(xTxEnable)
    ) ?' H+ k. `" L9 T+ Y4 `
  47.     {
    ; P) t6 T  }* b; g, D1 s
  48.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断
    4 \" g6 p& A7 Q1 ^) q7 {
  49.     }
    5 _4 w' [' @' f  `* G8 D% ~
  50.     else# g/ T( M. u4 e* i
  51.     {" [7 g! a2 K9 {
  52.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断' V! u8 c0 X$ k
  53.     }
    3 L7 i6 e/ Q. y, P/ \/ H. Q. w# ?
  54. }! ^& Y$ X  c$ d0 w& }$ B: @3 z
  55. . \6 |9 z& N3 K0 W* f
  56. BOOL
    - n4 l9 U! F" c/ g, h5 N
  57. xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ), {4 F- o& K9 v/ d7 g9 I& ]5 |+ K9 Y
  58. {
    1 A' q6 T6 f% c8 I
  59.     huart1.Instance = USART1;, S2 [* T" }; F( \
  60.     huart1.Init.BaudRate = ulBaudRate;* Y6 A; V* r0 t- O
  61.     huart1.Init.StopBits = UART_STOPBITS_1;% |$ R0 H  s# a- K6 O8 @
  62.     huart1.Init.Mode = UART_MODE_TX_RX;2 a/ \- |' w$ W5 x+ E: J
  63.     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    3 _4 j; \! M% ~; c1 ]& @
  64.     huart1.Init.OverSampling = UART_OVERSAMPLING_16;: I. l/ o) O" f! y# g

  65. 1 ?" A/ f! g& o0 @" h' n. K% R: C" a
  66.     switch(eParity)
    3 S( i- \1 L9 e9 |0 m( {: V: L, U
  67.     {
    + t+ a5 x/ V8 Z: c  `8 w
  68.         // 奇校验5 A3 U" ^# ?) n" e) r
  69.     case MB_PAR_ODD:; x2 a- b% C% s  G, K% A
  70.         huart1.Init.Parity = UART_PARITY_ODD;% |* y% K2 X1 X5 c# ^3 y
  71.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits! F- d  ~* b: h, N1 k( }) @. N
  72.         break;
    $ l- \8 `( ]3 x- ~) [
  73.         
    ) g' I) b( c" f! \( ]( b+ M- C. [
  74.         // 偶校验
    + g+ ]0 b/ e1 W+ k/ E
  75.     case MB_PAR_EVEN:, y+ n4 t5 K- H; M/ s
  76.         huart1.Init.Parity = UART_PARITY_EVEN;/ y' p- K# I) @8 k' Z# E, _/ U
  77.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        // 带奇偶校验数据位为9bits
    & L8 j- `* c* Y- v( H9 C
  78.         break;
    4 C0 {6 [; p' e+ v& ~6 A. n" Q; @  t
  79.         , e8 I7 F0 k2 d5 e5 g) j2 E
  80.         // 无校验
    ( V( O: _% P# z' l, a: D2 w1 h
  81.     default:
    ! [1 @( X1 N: q% ~; h$ N
  82.         huart1.Init.Parity = UART_PARITY_NONE;
    * k( S9 d  H. K
  83.         huart1.Init.WordLength = UART_WORDLENGTH_8B;                        // 无奇偶校验数据位为8bits3 A9 S& I8 {7 E4 O+ B! N$ k
  84.         break;
    2 H* F; x5 l% s6 t9 \5 K
  85.     }& ^5 U5 R+ j' \2 h- H
  86.     return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;
    . s1 A, t6 w0 \4 J
  87. }
    1 F6 i; i6 M2 j7 w, i

  88. * L3 Z; J3 l* X
  89. BOOL2 o3 o9 ?5 w  z) }' t
  90. xMBPortSerialPutByte( CHAR ucByte )
    7 k4 h- Y) n/ d- U1 f4 U
  91. {
    * q# h5 _; h2 ?8 g' W
  92.     USART1->DR = ucByte;
    , ?5 ^# Q: p/ C9 Q* O
  93.     return TRUE;$ v. S: d% W9 _8 I/ H
  94. }1 i& a- k' _/ w4 C  U

  95. * f6 o* L$ Z0 h3 \$ s' N: B
  96. BOOL# s# `, \( k! J' W6 `
  97. xMBPortSerialGetByte( CHAR * pucByte )
    % `( N, T/ Q8 y: `; a. t
  98. {
    ! |& f3 d. v0 i. `% l9 N8 D: y* W
  99.     *pucByte = (USART1->DR & (uint16_t)0x00FF);
    2 S  k# _: v6 v) T/ X5 K8 @8 ]% \
  100.         return TRUE;& n1 C. w/ Y' g  A% ?
  101. }8 T7 Y( `6 o1 P8 R( {
  102. 0 x6 y( m' v/ z& r# l. F8 E
  103. /* Create an interrupt handler for the transmit buffer empty interrupt' E" D" J- L6 M5 a5 ~# v
  104. * (or an equivalent) for your target processor. This function should then" {7 j, Q4 w, L8 q; z6 T
  105. * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that. l6 V7 ~5 `& S" ?) r6 q' d) Q
  106. * a new character can be sent. The protocol stack will then call% {# I# Q4 v2 {" h, `: a# X
  107. * xMBPortSerialPutByte( ) to send the character.. d! X6 a+ N! ^# ~: Z. g
  108. */( K$ [6 B' `' q4 F2 g3 O8 w3 m! g( }* |
  109. static void prvvUARTTxReadyISR( void )
    2 a  w3 D7 }0 A
  110. {
    ; e' }$ b+ t% C
  111.     pxMBFrameCBTransmitterEmpty(  );
    ; p8 S. D& i+ \, D# d
  112. }; t1 j9 e( o# }  U! N
  113. & u, a! j6 R' G3 m
  114. /* Create an interrupt handler for the receive interrupt for your target
    ' I6 X1 U1 c+ T7 u3 m
  115. * processor. This function should then call pxMBFrameCBByteReceived( ). The  ]. z6 y5 {7 T2 [
  116. * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
    0 F* g* y( G. w$ ]$ L# _# J5 f
  117. * character.# e* Y! V* N5 y
  118. */$ s$ h( t2 ]& e' Z: H" j( A; k8 ^
  119. static void prvvUARTRxISR( void )' U/ A& h9 G4 F" g* R6 Q( H: Q" {
  120. {5 U' \4 U3 M7 E
  121.     pxMBFrameCBByteReceived(  );
    5 z4 q6 R5 m. y
  122. }: x( x& u. [; a6 s9 r2 ]/ _; N
  123. - }9 y/ r2 `6 z' G, v6 ^0 r
  124. void USART1_IRQHandler(void)
    - y2 H% p0 o: Q6 t1 W: T. Z
  125. {) n% f3 t. n$ N( l
  126.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))                        // 接收非空中断标记被置位6 I* X4 L9 {' e2 w. Q
  127.     {% O# p# R" }8 y) ]4 Z
  128.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);                        // 清除中断标记
    ) o) a! M, E& o% a# O
  129.         prvvUARTRxISR();                                                                                // 通知modbus有数据到达
    % R  i% T! w( v. u) Q
  130.     }
    $ J3 P" g9 Z* V8 H5 h
  131. $ m5 t" n- T  K# |4 ]3 l% [+ r8 G
  132.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))                                // 发送为空中断标记被置位  e2 a/ N+ b# `6 M
  133.     {: B, b7 e8 V* H, J$ y) y
  134.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);                        // 清除中断标记
    ( z7 H5 H4 {; S" h
  135.         prvvUARTTxReadyISR();                                                                        // 通知modbus数据可以发松
    + E9 x3 p7 s& W7 E0 J/ T$ o# {7 p
  136.     }5 u- ~2 S. v- N# d7 i8 d9 u1 c
  137. }
复制代码

6 D. Q9 {* E- H* s* m注意一点,一般如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样:
$ K# `; |; K9 b7 c) h
8 H. Q) r2 v9 @3 U, }- n2 w4 d
  1. void( Q7 e$ K/ E* X1 C
  2. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
    6 C% H4 C, P  e
  3. {
    $ U1 M* |0 H$ r: T
  4.     if(xRxEnable)
    * q+ L# O+ L+ s
  5.     {( ]4 ~( v$ Q; b. r) T
  6.             //
    ( X# ?; l0 ~% n% H7 u3 E
  7.         // 在此处将485芯片设置为接收模式
    0 ~# ?2 ~" j/ g% t' a& R0 I
  8.         //
    / a; J: q$ ?; S$ _- l$ V
  9.                 /* do something */7 F8 f8 u. x* u2 E
  10.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                // 使能接收非空中断( m. A9 B/ n  W3 o: T: R
  11.     }* n  f: M" F+ J! X. c* t
  12.     else$ Y9 ], v: x% I
  13.     {: I( t- U: Q  U& a3 ]
  14.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                // 禁能接收非空中断
    0 W% A, d( \& A  n
  15.     }: r& x( W$ W( X0 Q; \

  16. 0 v* s; [4 ]$ @5 z( L* r
  17.     if(xTxEnable)
    4 _7 u0 g! N+ N2 e( s$ I
  18.     {# X1 `; ?) _! O' F, H* D
  19.             //3 B) Q! n' o7 P7 m  W% y# Q
  20.         // 在此处将485芯片设置为发送模式- c3 d+ {$ y" w/ _4 h+ g5 D
  21.         //
    4 k$ \8 f8 e. p* i  j% u4 f
  22.         /* do something */2 E1 b. I2 I( \6 U$ m: X
  23.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        // 使能发送为空中断0 L6 s" k  [  [  L0 v4 y3 e$ P9 t$ @
  24.     }, X+ U9 f2 P, m
  25.     else; U7 H1 |; I5 m5 Y0 I  ?$ o: [  T$ s
  26.     {) `, H) i. L$ J; g
  27.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                // 禁能发送为空中断9 N# E8 L; S# C' v
  28.     }- F! i% ]& c& L5 \9 N( i
  29. }
复制代码
# Y# U$ ]5 w. i! K) y$ h- Y- V4 n
编写modbus命令处理回调函数port.c文件; w( a6 _; E  u9 A% B0 X
本例程只实现了读取输入寄存器和保持寄存器的功能,详细代码如下:1 X: ]- I" c- F; B: J$ u4 I$ A
4 Q3 f3 ?& n3 O6 R4 f
  1. #include "mb.h"# }# ?/ t2 `1 L
  2. #include "mbport.h"
    0 p% B5 e3 t$ c3 f' g9 {
  3. , Q5 B% ~7 y: Z; N( n2 z
  4. 5 z# N6 G  ?& X
  5. // 十路输入寄存器" X2 p) h8 W. k& C
  6. #define REG_INPUT_SIZE  10
    3 \# [# }0 A6 Q5 ^
  7. uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];) G  u1 I7 Y7 B2 q. `$ m
  8. + H; S; n  {5 F) p0 R2 ]5 I

  9. ' b; s. x$ s5 ?  v: ?8 N! h' }
  10. // 十路保持寄存器
    + F: `1 Q! e' b1 W4 h
  11. #define REG_HOLD_SIZE   10
    ' H( J; `  Z- Q1 |, z' ]+ E2 l9 A
  12. uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];/ T' p, v# M* n" S" _, b$ P
  13. 2 M# s% W/ m) T3 D
  14. 3 ]0 q/ z8 R- I! w( m& a
  15. // 十路线圈/ f$ f2 r# r2 {" f5 }
  16. #define REG_COILS_SIZE 10) h$ y0 U+ w( @# m
  17. uint8_t REG_COILS_BUF[REG_COILS_SIZE];
    ' s+ ~8 R) V7 @2 H8 F3 _( `2 t9 r  M
  18. 9 `+ r4 Y3 R  Y+ z- b8 k

  19. 3 R5 C. o+ P: v: ]$ K( ~8 q# b
  20. // 十路离散量" U- l* F( Z5 V5 x5 B6 x) v* V+ O# |
  21. #define REG_DISC_SIZE  10, D$ q8 K3 _6 ~
  22. uint8_t REG_DISC_BUF[10];
    $ G4 T1 ~1 d& Z9 W& j$ g" q5 R; q

  23. 3 h2 N8 i7 L3 I

  24. & L, u9 n5 H6 o$ o6 Z+ Y
  25. /// CMD4
    . h" b$ p' L# w4 n( g5 n
  26. eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )7 G/ V( G6 Y! g1 r  Z% x
  27. {; @( {9 @2 n. ]: {1 P" o
  28.     USHORT usRegIndex = usAddress - 1; " w; U' A* @0 q, d) o$ n( T' S

  29. ; K  H! @6 s: u( e- l3 V; t- N
  30.     // 非法检测
    4 @+ W; Y- C9 h4 j" S& K$ I! p
  31.     if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
    9 r8 ^/ v' b  c2 i
  32.     {" B+ ~5 a( k/ @) T# p; T3 T
  33.         return MB_ENOREG;
    / w1 m* h% w# l0 o0 Y! x  d
  34.     }
    2 ~( G. |) H% x' I# g

  35.   t( U4 q: F* g0 d2 N& n
  36.     // 循环读取% e* f* R2 j+ Z7 j
  37.     while( usNRegs > 0 )$ Q* X" o0 F: t. T9 h" B% O, b2 X
  38.     {! a3 W0 |$ n% j' g( W. |
  39.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );$ U/ c& f/ U+ ]  Y: W+ c
  40.         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
    " j! B  G) F6 e* }5 E0 \2 r$ y
  41.         usRegIndex++;( ]( w% f3 S5 h& z  A. o' _
  42.         usNRegs--;
    " O7 T0 }5 |5 b9 `: j; y  E3 f
  43.     }
    - T% l# ~. x/ c7 s1 i0 c
  44.   Z$ M6 q1 _: T$ J
  45.     // 模拟输入寄存器被改变9 J% S9 C- C7 X+ z! o
  46.     for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)2 q7 S9 g" T5 D  T3 H
  47.     {: _  X& n/ ^: u- I9 I0 `; C
  48.         REG_INPUT_BUF[usRegIndex]++;
    $ `/ m9 d6 @0 O* V: E
  49.     }! a4 {$ \/ {  E1 ]8 D$ r; ?8 i3 _

  50. 4 y4 X. v+ P) p# D& d2 u/ @- ^" n
  51.     return MB_ENOERR;2 @/ z+ [# p& k) |( b& s& H
  52. }0 T) {* m4 ?0 v, P6 p' a$ w
  53. ! L/ u0 L# U, x9 ]( V
  54. /// CMD6、3、16. Z) T# |- x0 W* [
  55. eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )! g- {, |0 e: r2 a! t) ^5 A6 t
  56. {
    5 }* H: ], R2 q% c  d% X
  57.     USHORT usRegIndex = usAddress - 1;  
    * t* K4 G# H8 [  g4 e) R. ?

  58. 2 [: K8 S8 ?* {! W' A3 T
  59.     // 非法检测5 N$ s8 [$ h7 |' U0 s  T
  60.     if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
    ( h+ t8 U. o7 m' j
  61.     {
    + h6 `) f8 m1 o
  62.         return MB_ENOREG;
    . m+ N9 _/ y! A  C3 V
  63.     }, U( ^$ G7 J/ F: R4 p! w5 \
  64. 0 i9 n9 w- \) W& t# ^
  65.         // 写寄存器6 z' [" s: r! u
  66.     if(eMode == MB_REG_WRITE)
    5 \0 I; h* [, k! g# v! L1 \
  67.     {- G7 i1 T( V+ e( h2 F, }
  68.         while( usNRegs > 0 )1 [2 {/ n5 r5 n! K2 J- `$ v
  69.         {
    ( G& g( _' h& [+ k$ j. ~
  70.                         REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
    2 @4 m% n) i$ d# w! W
  71.                         pucRegBuffer += 2;
    ' I, ]# r8 q- {7 K
  72.             usRegIndex++;
    " q' X# \& q& F. f7 ~' P
  73.             usNRegs--;
    ' M/ M- k4 ~8 v% z' F7 j& K
  74.         }
    4 q( H+ P0 i$ S" W) G
  75.     }
    ; p# k' T9 t" N" ~2 C% B: j2 h
  76.         6 ^4 w5 y& x4 y1 S9 s
  77.         // 读寄存器
    $ I% D0 u* A5 o7 t$ a& B
  78.     else
    / q* D  d) G4 x( U
  79.     {" k* p0 i% R8 v
  80.         while( usNRegs > 0 )/ Z1 [: y' c6 V( m. ?7 ?5 X
  81.         {, |! o2 G( Q# r0 N' {
  82.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
    6 ^7 Y5 a( V. g( J, y
  83.             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
    : n, E! k. K6 M! |+ E
  84.             usRegIndex++;% q& ?7 ]3 Y" t7 [
  85.             usNRegs--;
    # I+ ^2 M8 a  k
  86.         }
    9 T" o; D$ Y' n
  87.     }
    0 x% e' V1 h* V% W& A
  88. 1 S1 M3 g  D! Q$ z( P- A! W6 Q9 S
  89.     return MB_ENOERR;
    / i2 H# }, g3 r9 `5 c. ^
  90. }2 z+ ?, X! Y) M7 ]4 s9 q8 ~: R& O9 B9 v
  91. 4 c8 X' w' d7 B6 e% }
  92. /// CMD1、5、15
    ) N0 a8 M6 c% t
  93. eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )8 y1 q9 [/ J/ m  z3 o" D) @
  94. {
      }+ e, E) G- X4 n
  95.     USHORT usRegIndex   = usAddress - 1;
    , X$ Y# X0 d* a( F) x5 C3 d
  96.     USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
    4 ?% c2 r, D# s2 }* b
  97.     UCHAR  ucStatus     = 0;
    % E# f! y) m# h7 F* m3 `5 c! \
  98.     UCHAR  ucBits       = 0;1 X6 k% w. G$ y+ S0 g' \+ \
  99.     UCHAR  ucDisp       = 0;
    1 w3 M( q: _, W. q

  100. ) u) E' @) _" X* F1 @" k' t
  101.     // 非法检测
    ; \3 R1 T( M  {( m3 R& A  b4 Q
  102.     if((usRegIndex + usNCoils) > REG_COILS_SIZE)
    1 k, D  [8 m5 i1 [5 n! g, [1 S: q% E2 G
  103.     {
    / [7 V/ P7 x! h; w; a) O
  104.         return MB_ENOREG;8 T& ~7 y: C3 X( o9 H
  105.     }& C8 F. x, W# Q" ~" K6 h

  106. # I9 ^+ {1 f2 t! m, i
  107.     // 写线圈) H% ^9 S+ n7 a" X7 y& t
  108.     if(eMode == MB_REG_WRITE)
    ! A! a. }4 z' a/ x$ B
  109.     {) z/ d+ o7 _$ o$ Y1 A3 N
  110.         while(usCoilGroups--)# O6 o- |( O. G# p; I# y
  111.         {
    & B2 F; d. G/ g
  112.             ucStatus = *pucRegBuffer++;; W' V6 W, d. R; V# A# O7 V) u! O
  113.             ucBits   = 8;. W1 Y$ K2 H2 L" i
  114.             while((usNCoils--) != 0 && (ucBits--) != 0)+ G, v8 h  `# [" T
  115.             {
    4 a7 w& G, V/ ~) K+ e2 L' J( D
  116.                 REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;% e' t7 y' N# J! ^( W
  117.                 ucStatus >>= 1;( }( \, P# S* {' d! o
  118.             }: s. F% ~% _  A% a8 b: g2 \# d% @
  119.         }) T0 D2 O6 y1 z* |0 Z0 r4 G
  120.     }
    ! q6 f& Z+ e6 V
  121. 3 }0 R" w  g+ g4 y! x( Z0 p
  122.     // 读线圈6 Q9 W6 |. h% c1 x( R, m' q- O) i
  123.     else
    6 g! h& [3 B/ K
  124.     {
    . s& u0 x2 m0 b; {% p4 s2 N
  125.         while(usCoilGroups--)7 I4 w) |* E, ?$ D& O
  126.         {
    - ^6 r3 A4 Z" @8 V5 z: V
  127.             ucDisp = 0;- s3 `, \! b/ C9 i* h0 R% A4 {
  128.             ucBits = 8;8 e+ S$ E' g; }0 G# p8 k
  129.             while((usNCoils--) != 0 && (ucBits--) != 0)5 b& y) F" l; D1 B
  130.             {. N; N% r, k) O# g4 J
  131.                 ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));  C6 x& w  o) E
  132.             }
    ( O2 x1 z1 H4 s7 F
  133.             *pucRegBuffer++ = ucStatus;
    : w, U/ R+ a) L) I3 r1 E' |
  134.         }0 v0 `' P2 o+ I+ T
  135.     }
    % ~* t0 T) p# h) d
  136.     return MB_ENOERR;. |2 J% t5 ~  ^2 x9 R: D0 C
  137. }
    4 U) I5 a0 u: W, V% _9 d0 U2 ]% z! R

  138. ! Q* l2 ]; [. I9 X% p2 S5 [4 G

  139. 2 q* G1 @1 K) X% R3 m
  140. /// CMD40 s' f8 ]( [: L9 a3 f
  141. eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )5 I( z9 t+ r& o. Y7 H5 x
  142. {& d7 I, W. P: ?# ~, M9 a
  143.     USHORT usRegIndex   = usAddress - 1;% C6 R" E& J7 m7 e2 ~: Q9 T
  144.     USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);) S3 R  [$ r0 A- c) X$ `
  145.     UCHAR  ucStatus     = 0;. `0 V/ e& q. ]" P4 j2 i
  146.     UCHAR  ucBits       = 0;
    0 N6 p, V% `& M# W% ?# `! \
  147.     UCHAR  ucDisp       = 0;6 _. U' j* f2 G5 h
  148. % ?" w  j, ?( o* d& ]
  149.     // 非法检测
    2 i- n) L5 ]( V
  150.     if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)
    8 N  Z; d2 n" G* u1 F& o4 }; Y
  151.     {7 G$ g/ H2 }0 L6 k4 C" u+ w' o
  152.         return MB_ENOREG;+ F5 p3 i  N! J; T  _7 s
  153.     }1 k" m. N; L# I0 J0 [, l* G
  154. % y& j0 R: \' m' S0 i7 @2 W
  155.         // 读离散输入+ ]  ^% e" }# `" y+ q3 v
  156.         while(usCoilGroups--)
    ; F! v' [% K- t
  157.         {
    3 P  @4 R' `0 P% i
  158.                 ucDisp = 0;
    9 s$ @7 y. k3 k' p
  159.                 ucBits = 8;; {8 R3 Y/ ]( x. a# @
  160.                 while((usNDiscrete--) != 0 && (ucBits--) != 0): A: h0 C" V2 ^, Z
  161.                 {/ o6 {) U4 i) g  ^6 h, e" V
  162.                         if(REG_DISC_BUF[usRegIndex])
    ) G7 ?/ ?5 s+ c8 Q; T& d
  163.                         {  F% N- Y7 w* N! K+ [& C! V  _
  164.                                 ucStatus |= (1 << ucDisp);8 F5 n1 E- r) N3 M: H* q- t
  165.                         }
    ; o1 U7 t: c& |! t# x2 W
  166.                         ucDisp++;
    ! `% |( c" X( U+ u4 M
  167.                 }
    - o2 Z3 R# Z- w3 a+ T0 b" F* X& m
  168.                 *pucRegBuffer++ = ucStatus;
    + [7 W5 |' V4 l) {4 A$ n' F! Q& B
  169.         }
    , U+ `8 f+ c. c! E
  170. & D$ H  D5 M& Q) h# I: n, w5 O) K
  171.     // 模拟改变
    4 m: B* [7 ]4 b' C7 b/ U- f7 D6 r
  172.     for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)$ O' N( I( H8 T# b* m0 C
  173.     {
    $ E' a8 p: I. O6 f- U! S
  174.         REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];3 J- W6 c2 z# _+ \
  175.     }
    0 ^- u, d  `0 }' Y

  176. - j3 a  [; C( T4 L( t0 U
  177.     return MB_ENOERR;
    # E5 A: k7 Y6 J5 S+ e+ j( w, E
  178. }
复制代码
! h8 ^& o3 x/ G& {9 |
主函数
# I6 Y% D4 s7 `% |1 A& k0 y
  1. int main(void)
    1 C- A* Q$ F6 J. Z
  2. {
    $ ~8 e# E1 U/ o
  3.     HAL_Init();
    . x: y1 T4 M0 O2 P
  4.     SystemClock_Config();; R& }; H6 ?1 c/ g) k9 ~+ k1 t
  5.     MX_GPIO_Init();
    , D# |3 ?4 T& Y1 {+ ^
  6.     eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD);                // 初始化modbus为RTU方式,波特率9600,奇校验0 s: K: p+ S3 U6 E# g
  7.     eMBEnable();                                                                        // 使能modbus协议栈& ^# K' ^4 d" q7 r3 H5 O' @
  8. - U' M; \0 u* J) ^/ \
  9.     for( ;; ): [' w% P' ~4 P' D# j
  10.     {0 {- _+ t, X: g# K# Q+ s
  11.         eMBPoll();                                                                        // 轮训查询4 w5 X! M! q3 M; u- B* @
  12.     }
    7 c6 c4 V9 x5 K' g& m( t- g  q
  13. }
复制代码
/ C* ^- R! c- I- S  d% f
移植测试# S- }2 `$ ~3 W% b6 m8 @

% O1 j' g+ J2 ~8 o7 x 20200313154857173.gif , F! a- @" ~; j
8 l, _( A  r7 w3 n* [  z# r  T  ^
ends…
2 Q$ z7 ^' P# u4 z; W; _% r6 R8 V- p' |

+ Q8 x5 F" ?1 L
/ k$ f' F6 w, U2 \& 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

<br /> <br />

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

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