modbus命令码表:
6 T5 Y7 F2 \ s: r: x; i2 a) W+ l$ v, W: T
5 b7 o* t* B6 B4 ~# q' h
0 A! E$ C; `. }- z1 P4 ~# ^8 H- LFreeModbus文件说明" v4 f+ r U( T; t
解压freemodbus文件后打开,我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。6 N1 A% I6 |' y8 r: ?
E2 k" ]. S7 [5 m4 }, O" O6 q
" [1 n9 m) r1 ]' f) K g; v
mobus文件夹就是完整的源码,包含rtu、ascii、tcp:
2 x9 R* C0 M4 j$ `
# \+ f8 y0 k6 F7 I4 D, W
% D" @ u2 C! _ L Z, s, _
5 L9 i+ e! v' }- r+ n
我为了移植时在keil添加源文件和头文件方便,就把modbus所有的头文件和源文件放到了一个文件夹下,并创建了一个port.c文件,用于编写modbus所必需的回调处理函数:3 t' c/ h: f4 l4 ?# } `$ r
$ O6 |# O' ?- n$ M U) a& G
5 w2 ?( N; q B0 @2 N
4 {# Q$ Z: B8 X: y" ~, z% I, MSTM32CUBEMX配置
6 s$ v5 g- ?9 E& A# o+ e( X1 f时钟配置,设置主频工作在72MHz下:" [: K& |% t# |) N" X
. ~- I( {! b( p3 l, N
( @! u( f% S* O( g
3 {% f& |8 @( r o
配置串口1,这里随便配置就行,在modbus移植过程中还会对串口重新初始化:. c* z" v3 {* I- q% A0 k: o
2 z: s5 m' U6 |) ]4 g" b; C+ I3 O
/ U; L3 S# w7 @) N! M& c0 i6 X4 b# }" l4 Q: F! a0 ?
配置定时器4,用于3.5个字符的定时检测,这里随便配置就行,在modbus移植过程中还会对定时器重新初始化:6 |" K# C, N( J4 n
6 f& H% ?, @6 Y; D% H6 V* O+ R2 x+ P
8 u/ ?8 u6 n4 j+ Z3 g- o3 `/ C
8 A3 Q% E7 N8 t) x7 l
中断配置,这里注意,串口的优先级是要比定时器优先高的:: F, u, Z" c. u2 r
" N8 o3 p T# H8 S/ s. T/ Q0 y
& Q9 S( Z2 A% q* {取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序:
1 p: w" c$ ]* r' R+ I( r b
, H/ A% o' L# a5 r; j5 ~
% E9 S% E" i: W4 b% L2 }
8 V' o% b* a' e2 b+ R0 ^移植代码修改
" p% [9 |& h2 W0 W! {生成代码后将modbus放到工程目录下:+ L T& g# ]9 y& D o: U9 u( o- o
4 i+ K B/ v$ r+ b8 K- C
/ B, o( w, U R, J x/ S
* Y. a3 \4 d/ J9 s( \" g打开keil工程添加modbus源码:2 Z5 `" U5 t) H7 T' \6 ?; }
: j3 r; Y7 ~6 c9 k5 l8 E
, f* j5 `8 ]. i+ n
. c$ g0 Y/ T* p& H' e* ]/ g
添加包含头文件:
# F0 k! ~/ b0 ?- e8 g3 p9 W: w- j+ F2 Q/ D6 I7 K
4 v: ?/ @! J' ~) j
, q1 n1 D4 y2 _9 b; \7 x6 C( `
修改modbus定时器初始化源代码porttimer.c文件! @1 N8 I. Y; \7 R& |- o5 l: i
定时器的修改比较容易,将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达;由于我们在STM32CUBEMX中取消掉了定时器和串口的中断服务函数程序,所以我们在该文件中添加定时器的中断服务程序,修改后的代码如下:& w9 Z- Q2 w q$ p- I
% b* E$ p) I, b4 g8 U+ [ E- BOOL
) s& s; C0 V: ^" e* V+ p8 h( m- C - xMBPortTimersInit( USHORT usTim1Timerout50us )
$ ^3 H) Q6 p( @" @+ S8 S - {7 U( {+ U, w/ D6 p& X
- TIM_ClockConfigTypeDef sClockSourceConfig = {0}; [% a) b1 U6 k# l0 d7 {) Z& X
- TIM_MasterConfigTypeDef sMasterConfig = {0};8 E" z5 S! b9 H7 l; o
! r3 [! u, R$ n8 ^- htim4.Instance = TIM4;
6 e ~% N: S9 h' o - htim4.Init.Prescaler = 3599; // 50us记一次数
0 F: g6 S# J- T6 y5 t, l! l: e - htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
$ Q, o$ w6 H# ?! n9 c3 s - htim4.Init.Period = usTim1Timerout50us - 1; // usTim1Timerout50us * 50即为定时器溢出时间6 Z* Q6 r- g8 S
- htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
1 B/ Y K3 H5 w L - htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
9 B" N' w; ^) b, _ - if (HAL_TIM_Base_Init(&htim4) != HAL_OK)5 c( Q$ V5 T" M6 {6 }! A
- {3 m x. v7 X- m$ ~
- return FALSE;8 l0 z# p# O1 N4 E: X: g/ s
- }
( [- f. I8 c0 P2 |) C - sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;7 a; I6 }; D1 k
- if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
" n% a# H* h7 {$ B: c5 M( ~ - {# q u* B g% H) G7 N
- return FALSE;
2 u! q( G8 r6 A - }
* f. V" }: L: P/ G# I( ^ - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
7 ^, s# e! n! G8 C$ `* w - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
) K* j4 ^* ?5 Y - if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK): H' P8 p3 \4 L- n
- {
# T0 w; ?5 ~$ Y - return FALSE;
' l5 Q s! q# G/ ^8 Y2 ^ - }
3 _, _7 c5 F# w+ B* ~
( f6 l" e- }; K, R- __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // 使能定时器更新中断
7 ]% q0 W" c! E - return TRUE;) o: n; p. C5 f8 V1 ^, r- ]
- }
: a: Y8 o k0 \ - $ l7 k4 G. p" |1 @6 j+ n) H; `
- inline void
6 e' H% {3 {+ H/ r: M; b$ W0 Z - vMBPortTimersEnable( )
3 {/ l- n P/ Q - {+ g" [( C0 v6 W) R
- __HAL_TIM_SET_COUNTER(&htim4, 0); // 清空计数器5 H. ?# M9 r" s& Z$ v/ t' R; v
- __HAL_TIM_ENABLE(&htim4); // 使能定时器
8 S8 v# j. a9 [* H - }
: D! ]! v2 R9 \" Q7 B# i
+ X: S' U& }2 r" S+ f- inline void5 W& e7 X( {, N! K! Q2 i5 c
- vMBPortTimersDisable( ). ?: h0 _6 k+ F
- {2 q4 N2 h* z$ H+ f' E
- __HAL_TIM_DISABLE(&htim4); // 禁能定时器! q" k2 M- y" P3 _" L. N
- }: G, u% C% Z. u' w9 a
- X9 A' W+ Z! ^- /* Create an ISR which is called whenever the timer has expired. This function* ^! \6 @ A1 e- e6 F$ J7 h& A1 F* A
- * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that; c, L5 M; K1 E4 ]9 S1 {5 ~
- * the timer has expired.
9 o+ x/ I5 p \6 Z$ d: ^# z - */
' A4 d. m8 s4 o+ F3 z9 o. S - static void prvvTIMERExpiredISR( void )
) [' h7 s% r. h" G, }/ J- c - {! g" g3 J. o/ ]' m
- ( void )pxMBPortCBTimerExpired( );$ b; w0 w4 R7 H- j/ x3 P- S
- }( F* V9 v) ~- O9 _
- 3 o3 F4 m, _2 y8 A3 L3 C
- /// 定时器4中断服务程序
; D" o$ V2 W; \. q+ _* ]; R - void TIM4_IRQHandler(void). z5 g! w. M$ v2 m: ?
- {; W6 `9 G9 S/ n* V, ^7 a- [
- if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) // 更新中断标记被置位
1 k+ D( x! a4 k+ @# b' r - {
: f. V6 q: {: j3 k/ B- o& b q# [ - __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE); // 清除中断标记
" Y: z: p$ v P9 H3 Z; ]) |4 R - prvvTIMERExpiredISR(); // 通知modbus3.5个字符等待时间到$ C: _1 ^* }) }, a7 M1 ~6 u+ D& l' b
- }' T0 ?9 h6 D9 c$ z( b( P, g
- }
, ~) i3 t* A: ~1 F; t
& a1 ?1 @3 p) U! a* I: R5 a7 ]! \. x& ]- 9 ~$ m9 P# |' E) g! l
复制代码
! j% l# X% ^( S, u+ q修改modbus串口初始化源代码portserial.c文件
$ e! `! O" ? O. M4 I! k ~~~~~~~~ 在该文件中实现串口1的中断服务程序,prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了,后者的作用为通知modbus串口1有数据到达,修改后的代码如下:
) ?( k# T! b- Z# y% e" l. Z- G; p4 X: x% V T/ H8 v1 w
- /*
9 k3 O. o; d: \ - * FreeModbus Libary: BARE Port
1 ]1 o3 d$ V* \) y3 _- P - * Copyright (C) 2006 Christian Walter <<a href="mailto:wolti@sil.at">wolti@sil.at</a>>) H) w$ I2 l7 Y- d( d* D
- *
: E; J& O& [5 r6 C( P/ ^$ [ - * This library is free software; you can redistribute it and/or4 {' W5 o/ a: P& K1 C" r X
- * modify it under the terms of the GNU Lesser General Public
. S% V: C* \% c1 | | - * License as published by the Free Software Foundation; either( {( M: D) z% ^) P$ d
- * version 2.1 of the License, or (at your option) any later version.
! z+ r& N8 N" h9 U' n: ? - *! i, Z9 N- ~0 c ? o5 c
- * This library is distributed in the hope that it will be useful,4 l$ t. Z3 e8 G
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ P9 n) K# @9 }) M - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
$ E5 w% ?$ z$ A7 L, M. S5 W - * Lesser General Public License for more details.* c1 C7 V! Q$ B/ T5 u7 V
- *% p" V4 l0 N0 ?9 c
- * You should have received a copy of the GNU Lesser General Public8 c' {. y' m# G. _) n/ ?3 z4 c- g4 }
- * License along with this library; if not, write to the Free Software
+ ?$ ?0 F, l# X; G - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA# w& X& V! c4 R) U! E1 `
- *
" W3 D, a/ S$ A- {. I* I - * File: $Id$
. E, }: x) P4 b; `# ~2 o' J) Q - */! p6 \, w" N9 c+ J- }1 x: ]
5 w+ i" U: U& a/ `. }- #include "port.h": t8 F* X6 W. \5 E! K. K3 F6 E" o
- #include "usart.h" \) H5 h* `- ~# M+ q
1 {1 {* N( K0 H" w4 m' Y- /* ----------------------- Modbus includes ----------------------------------*/
/ j; f2 k: J) m2 P+ [: Q# [0 g# A - #include "mb.h"
" e& N6 y9 {5 W0 _: K - #include "mbport.h"+ X& J% X1 c+ u- r, Z+ b0 i4 j* G
' K! O7 u- X$ ]& ]+ b6 ]- /* ----------------------- static functions ---------------------------------*/
8 v) S. ]9 l8 i* N! Y$ k - static void prvvUARTTxReadyISR( void );( G! }6 z" ^/ O7 q( u, \5 `' t' f
- static void prvvUARTRxISR( void );( L' l w5 u W, k: n$ U
! c+ B1 W9 Y% y0 u' i9 e+ } t! R- /* ----------------------- Start implementation -----------------------------*/
$ ]: {( F# K. d" m: k, z* X& L - void5 ]# v: q, r0 F$ Z$ }2 Z0 D
- vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
" k. T$ O8 L0 g; S9 ` - {& [ ]( f! S& ~+ ^2 g
- if(xRxEnable)& [' i8 n; G- L8 J9 C$ `: n
- {
. g4 Y/ B& n' p, ? - __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // 使能接收非空中断
* a3 g* z# z' J. @ - }; f* S, ^" |+ A
- else O5 n1 q' Z8 g* K' a" L( \: |4 `
- {. ~8 V0 ]; i0 s$ T* C
- __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE); // 禁能接收非空中断( { _- W6 [# u {/ \9 k9 z
- }
' Y2 h$ @9 F7 y' } - ; m% Z, \9 b1 ]! h& U0 f; B
- if(xTxEnable)
! A( z* y9 u {: m0 ~( a9 t* ], I - {
( t9 [8 g8 _7 q/ ~; Z5 e - __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); // 使能发送为空中断' n5 t) P1 }+ q: j! N/ l0 S [& Y
- }' o) q' l/ \# M9 d
- else
3 F" C/ d( p( }6 N. Z$ Q - {
) W. E% r+ \0 R - __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); // 禁能发送为空中断
% R+ [: F. m) B# B" L - }
- P) p1 A. L( x/ m# D0 r1 @ - }
/ G# J& W* @( }9 _: p
( K. U n5 W0 }3 R2 r" `- BOOL2 A$ L) E- f. B
- xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
0 f" _. R* b, W5 c" Z3 I/ X - {9 r z" U2 K* S3 |2 M) Z
- huart1.Instance = USART1;
2 r C5 ]: U% a) ^6 g1 K# W - huart1.Init.BaudRate = ulBaudRate;! _, v" K, X" M" K, r D
- huart1.Init.StopBits = UART_STOPBITS_1;
* ^( F/ p; r7 @, [1 z - huart1.Init.Mode = UART_MODE_TX_RX;
/ p: P* U$ l `3 w9 H6 |8 X$ y8 p - huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; ^( u: @/ S, V* ?% m4 [8 K9 `# b
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;$ ?5 d# X i+ q, ]0 p9 ?: o* n' U
2 j; |. G1 e3 r& h8 {, l$ ?3 ~- switch(eParity)
& f1 v4 V9 W# N6 G9 Y9 ` - {
2 c( @" [4 m% d1 \, ]6 j# S6 O; g - // 奇校验* {4 |0 d- e- w3 S
- case MB_PAR_ODD:
4 ^( d" u" \8 d3 {7 Y3 | - huart1.Init.Parity = UART_PARITY_ODD;$ h! \3 }+ T) y2 c( `7 V1 Z, G
- huart1.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits& {% P0 S& \* P7 K% u1 k/ o
- break;' O! S3 }4 |) b2 N* Q
-
* J4 O/ t# c2 r$ y - // 偶校验
( V; h! _2 i# n5 B: M o4 \' M* Z" N2 h - case MB_PAR_EVEN:0 _; I# z- @0 d; n- O6 C% G
- huart1.Init.Parity = UART_PARITY_EVEN;) W0 I9 A$ d0 n) W8 w
- huart1.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
, t2 C6 b* i( c t - break;* K! Y9 W+ }- I* t+ q8 k5 T
- " K! K& w' P' @( u& V% B9 M
- // 无校验
! R$ q' o, ^" }# u! \ - default:
% }" K" R+ W' P* a - huart1.Init.Parity = UART_PARITY_NONE;
- B, e/ P5 {/ O8 N0 D' w - huart1.Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits5 ?* O; k: U1 s" }
- break;
1 a: ?# w/ z( v) V+ K! N - }( w2 r6 a" P$ K: S: i1 C
- return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;) P- q; |& D4 T7 r* U1 l
- }4 ?5 N; M* j+ D8 b
* Y7 r. }7 @3 @( ? J- BOOL
4 x; A! _1 H7 D4 F - xMBPortSerialPutByte( CHAR ucByte )$ w4 k* v9 ~4 K
- {+ S: Q8 Q6 v! _5 ^7 P' H
- USART1->DR = ucByte;8 J2 I2 `* f$ I/ ?- f
- return TRUE;2 ^; a9 D/ p+ t1 w
- }" K# w" N5 m/ O% H) b+ Y
0 }( T4 x1 R; F( s# r- BOOL
. ]" ?8 U& h; r9 ?" P! S/ ^ - xMBPortSerialGetByte( CHAR * pucByte ) q# E5 f) C0 w) _* o2 F
- {
/ @* w* V9 L; `9 s& e( X - *pucByte = (USART1->DR & (uint16_t)0x00FF);& I+ E! n" w* y5 ^; \2 N
- return TRUE;( a6 p/ o9 u" O5 s" j
- }$ G4 {0 G5 Z- _: G9 j, c
- 8 z& B$ d% ?" @: g0 L
- /* Create an interrupt handler for the transmit buffer empty interrupt3 _3 P+ }- w; ?8 L
- * (or an equivalent) for your target processor. This function should then4 ?; V7 K* B. J" M; p
- * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that5 v p- ?! |3 x6 {
- * a new character can be sent. The protocol stack will then call0 n6 o2 R4 ]! Q, Y$ l
- * xMBPortSerialPutByte( ) to send the character.
) i& Z& L, A! }' @. Z - */
9 |5 R. L2 y3 w* c" h - static void prvvUARTTxReadyISR( void )
' T! R. S9 U( O& a - {
6 `1 [' U |# F' A7 x' v - pxMBFrameCBTransmitterEmpty( );
* s0 w& ^2 d1 i, u - }
9 g; T: @) T: d7 Z ?+ p% S9 l - % o6 n2 G8 f& h- d H: j
- /* Create an interrupt handler for the receive interrupt for your target3 L9 ^2 z# w/ J9 g: s# h
- * processor. This function should then call pxMBFrameCBByteReceived( ). The W8 { L9 x7 l A9 O7 \! ]* r
- * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
6 E9 H1 n# M6 R& j. k# f - * character." H5 G* s. H* m+ @! z8 |
- */' t6 P! r/ W8 O6 L! o2 z
- static void prvvUARTRxISR( void )
' `$ N3 N5 z+ ]+ w* a' k - {
' _7 t+ Y) e$ L3 A- W) R, M3 R - pxMBFrameCBByteReceived( );( `4 n% w, G5 s$ ^
- }* B1 \$ L) D8 l8 Y+ T h
! t+ U* [; M; q2 [2 K- void USART1_IRQHandler(void)
; x7 X! `+ m4 m( r) l3 P - {3 H6 I% Q- t6 @2 [
- if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) // 接收非空中断标记被置位
1 k& Y7 w! C) t/ X, |+ }. K4 l - {
* ^# F) o( d% M$ s, H4 N - __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE); // 清除中断标记6 f: p \' @1 R6 u- P
- prvvUARTRxISR(); // 通知modbus有数据到达* v: i: \1 `5 u3 O3 q. b/ y
- }# y1 ^- ^8 O) w. b
- 9 \4 l/ k- F# ]+ ~9 n u2 v
- if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE)) // 发送为空中断标记被置位+ C1 K* z8 r4 t' e3 s2 n
- {: o! I' J4 L% F
- __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE); // 清除中断标记
; m* w/ p. m2 ~7 ?/ D - prvvUARTTxReadyISR(); // 通知modbus数据可以发松& }1 i+ D' w8 A6 D( g
- }; H, K% B, V' @& o3 b4 l7 s
- }
复制代码
) w- D: b9 z; [ S- t注意一点,一般如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样:. Y# j7 f9 ?; r" q: i } r- \
' Q& `& u+ E* g' N0 b
- void; Q7 ~- X2 s9 V y8 {
- vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )1 e: M$ V) }$ w+ ^3 x5 F, s7 i
- {
m6 X) J; \* r - if(xRxEnable)' Z6 H1 y3 L t1 C- o7 d" P
- {* g0 q9 U! m+ X. [5 M& m0 k
- //
2 T0 I7 u% V9 J( g - // 在此处将485芯片设置为接收模式 q& M" ^! X h
- //
8 o3 q6 o" }. u - /* do something */
7 R# a/ h) @* W" d0 A* }# E - __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // 使能接收非空中断
" y' P0 K# N" O v% M4 f- X: ` - }+ ]5 p J5 O: y( ^4 {
- else; L& x7 [% U z' E: D" o
- {
( w0 w- t% D) e" s - __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE); // 禁能接收非空中断) b$ D6 y, ]* M( d& R/ w
- }) \1 n9 ` s& p8 H
- 8 E- {5 ~7 |2 l" e( L
- if(xTxEnable)
8 j: E; m6 r5 R - {
& h2 c' K7 l W) ^8 } - //
, J$ t; K# h1 \* d1 G' C - // 在此处将485芯片设置为发送模式" N! l) ~- P" `0 S% H4 Z
- //1 A" F' M4 C2 o- g L# c L
- /* do something */4 \; N8 K$ e3 Z; B
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); // 使能发送为空中断* L* n* J) J& N# G1 X) d
- }
9 E0 r+ v$ [$ M4 R6 x S - else% ^0 m! M7 T9 E* | ^8 y
- {& C5 S) P/ R! B( R! B+ t8 Z8 Z8 Q
- __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); // 禁能发送为空中断' R3 h+ J9 }7 ^9 Y' X5 {" `( `
- }! d+ E ]. O, p' I% h. ?& t
- }
复制代码 # f7 u$ A/ d6 u: Z
编写modbus命令处理回调函数port.c文件3 M+ a5 U; G( W! p
本例程只实现了读取输入寄存器和保持寄存器的功能,详细代码如下:% q: Z$ Y' f- E% ^
; I8 ^" D. K) {/ \0 Q" I4 f- #include "mb.h"% y! W! L/ p# A0 Y4 y! G8 X
- #include "mbport.h"
; N: z4 a9 ^ R0 I& m+ Q1 X/ [( G
" W2 o$ v4 q. i% o: _% M* ?
& T6 N+ D" _6 @- // 十路输入寄存器
8 o. B. q) s6 C) N: E - #define REG_INPUT_SIZE 10
. f8 g; }# K7 d8 V- n! f - uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];; |- g2 z' W0 {8 G! g2 g
- c( t( O: R6 a$ Q( e' B: r3 N
. m$ Z* R7 `4 |- // 十路保持寄存器
* O$ S+ E/ E' @ - #define REG_HOLD_SIZE 10
8 |2 H3 G' B8 L' h( Z - uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];- i! ?& ]0 ~$ C+ ^% V
- v6 v: J z9 I+ E& \ \, |
- ! v/ n) M& \, `& M9 K
- // 十路线圈7 q6 m ]9 R! ^' a/ j4 a8 Z' o& x/ v
- #define REG_COILS_SIZE 101 z# D) H( v* G8 u9 t) W3 j
- uint8_t REG_COILS_BUF[REG_COILS_SIZE];
9 |6 D9 M3 D* w9 Y
5 H; S. q3 X% W0 a- 4 X3 j1 S* }( E+ f; u3 T& j" y
- // 十路离散量: u: \, ~ s3 g& A( Q; n
- #define REG_DISC_SIZE 10
+ M& v k* {8 B+ k8 c& C! v& n( Y) W# d - uint8_t REG_DISC_BUF[10];" @) }$ ? w5 A
" h$ }* Y' U8 X" E
% x, q* L% W# n, [/ J W/ J- /// CMD4
& f) `! O/ m1 w% A! { - eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
9 h- l) ~! u5 R6 z( e - {
$ m4 c8 W' G# w* }3 V, M; R/ V - USHORT usRegIndex = usAddress - 1;
! d! a$ v+ ?" @! p
7 B( J0 H! f0 E7 M- // 非法检测/ ]: z: q' g( F U) Z, F2 ~6 q
- if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
$ q/ z z6 {5 M; H: f - {5 f% x% J& u8 g
- return MB_ENOREG;/ y* }0 D; ^+ M+ E. ^* u: _# b
- }% y- d3 p( {2 @) z0 ^: `
2 G ?. w w! @! P& D. O- // 循环读取2 \$ c- g* H/ n+ Y( D
- while( usNRegs > 0 )
2 y7 F d6 v8 G* ^4 n - {
! \3 t' m' F/ W2 O/ e% s) R6 \+ q - *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );" N# g ^9 Q" F
- *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
0 s$ T! J# ~3 g% s - usRegIndex++;" U/ P6 D% p, m4 e; }9 n8 x- u
- usNRegs--;9 J/ ^+ F8 n$ V2 n2 H8 }& G+ U
- }/ y0 G* ~# v) q7 H
7 \/ ?6 e2 O4 l; Z$ c: R2 j) v- // 模拟输入寄存器被改变$ E% L" R& X" z% n$ @
- for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)8 f B8 k6 v# A/ r
- {
6 a% k( p6 [+ l5 s - REG_INPUT_BUF[usRegIndex]++;, @, X. q- ~% I1 Y8 ~% \; ]
- }2 n+ q: [2 g7 e5 G8 p4 E
# |0 b, G6 d. {8 i' }. x: J% E- return MB_ENOERR;* l, w6 F7 S: Z- p
- }
3 w( y" _% A7 j+ a0 p% J1 M - / I' Z4 q& p5 ` a0 D# w: p7 `
- /// CMD6、3、16
0 y: n9 X" v4 U' b - eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
2 q. |% D6 D0 [5 L7 p+ T: c7 a1 |% } - {5 a& a& [9 E0 r( f
- USHORT usRegIndex = usAddress - 1; 5 n# A% }: }+ p& U# ~
6 _' u8 e2 ~% ]0 j \; Z' f2 I Y- // 非法检测; E) f4 \! ?6 u7 B% O: |% W+ f
- if((usRegIndex + usNRegs) > REG_HOLD_SIZE)9 Z( w; d/ p8 Y! g( n1 {! Q; k
- {$ a) Z* y& R: a7 I
- return MB_ENOREG;
3 X' p6 M/ h9 v7 Q) q - }
c1 s( x- D# q2 c8 y5 [0 x
6 C+ H! h5 Z) O8 O) X- // 写寄存器1 F: T# N% f$ d
- if(eMode == MB_REG_WRITE)+ c. o L7 Q) t3 k
- { n6 N! S2 ]- Z
- while( usNRegs > 0 )
% f7 \' H3 Y0 J& |: I) C! W - {+ s8 P/ G2 Y! _* e+ ~ [
- REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
+ D2 ]- k8 T; ?4 S - pucRegBuffer += 2;0 Y7 Y% Q- M; F% O
- usRegIndex++;
. P7 N$ q/ Y0 U5 K3 _- F! P - usNRegs--;6 }: x+ b n% O( l# s# n2 D6 n
- }; c& O( j r! K7 A
- }# F& o) `; k9 ^$ Z! E8 K
-
4 b7 P( {2 o4 m1 m0 C8 L( c2 g - // 读寄存器
% u7 g4 v: n& h) ?( i - else: J. v0 i; d) l
- {
* S) _) T* ?/ {3 U* i: t. R9 f. _ - while( usNRegs > 0 )
1 [/ [1 d4 \6 x [; X4 B - {/ m! f3 q2 j1 k
- *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
% i( x$ ^: [# v3 s t7 L - *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
0 B$ J6 l& ]3 u% o0 H9 B% }; ~. P - usRegIndex++;
+ Z3 W' } Y) e1 O+ R - usNRegs--;. `" C* i) ]: q7 L1 q" M" y
- }
2 z" B6 _: a: z _3 F - }
& s7 D/ Z( Q! C* [ - . N% g" e2 v& L: l
- return MB_ENOERR;8 L5 v+ f1 W4 I- @; {# s+ l
- }
) u" K& h+ H8 u3 f t1 R1 s - $ F) [' y$ s9 Q
- /// CMD1、5、15
# m6 p6 I" H j+ Z) d - eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )! a3 ~+ k9 \+ h
- {
- T7 ~7 P3 x, T) f+ V( y - USHORT usRegIndex = usAddress - 1;1 R) }9 f5 L; }2 r$ v6 k% F1 M
- USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);5 p- p5 D. y h0 i# G8 c
- UCHAR ucStatus = 0;
: B2 k! Y; ^# s - UCHAR ucBits = 0;! T/ a: a5 ~; f7 Q0 D7 N! K# u
- UCHAR ucDisp = 0;1 G" ^8 L% w( d
& t' T7 Y3 \& ^3 { ^/ m$ U9 Y- // 非法检测5 E( J" L$ }0 J; A% q3 z) o, Y
- if((usRegIndex + usNCoils) > REG_COILS_SIZE)
9 w+ O0 _9 @% T3 Y9 F1 ? o& @ - {% P9 @: a; |+ q% ^+ \- Y
- return MB_ENOREG;
+ U! q# R% K1 s0 \7 [/ g4 }9 A; z - }
5 X1 L# ~0 V* ]" q - 8 \- D# g4 F; Z! Y! |
- // 写线圈
$ `+ B9 G$ @ v M$ ` - if(eMode == MB_REG_WRITE)
9 ?& w: z1 w) m; G( r) d - {
: j, U( _3 d5 i' D$ a3 S - while(usCoilGroups--)% f5 o' }+ W+ q1 c
- {& g8 G/ F+ r+ x6 Q2 W
- ucStatus = *pucRegBuffer++;
; F; q3 t+ `; ~ - ucBits = 8;! T5 ~( v) _; x! F. V: P
- while((usNCoils--) != 0 && (ucBits--) != 0)
: U& Z9 F; l. R0 F) I# m3 s9 S/ V" C+ r' I - {! m. }7 e) z& Q2 R2 F3 V# d
- REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;& M6 n" V9 A+ P. h5 m$ ^9 S$ M
- ucStatus >>= 1;
" e; }' t( R7 P( w - }
1 V( T1 p z) q - }
( ?, Y# q- S1 Q* R! a# i - }
* O7 G; q- ~# x( e) R [% n
5 g+ U! {8 u" |' u; b( Q- // 读线圈
( s0 y" p- @& o) |5 A9 z# u - else
& o% o& c7 g, g( L" X" v4 N/ R - {# H K. z2 X Q$ H1 J u& u* h
- while(usCoilGroups--)
9 s! M) o% O- i! t - {; X- W9 D& V/ B. @6 f
- ucDisp = 0;5 D) T& f" P% r7 n
- ucBits = 8;6 O5 s# W: X2 L* A: R3 t) C
- while((usNCoils--) != 0 && (ucBits--) != 0)
* E) M- F6 y6 D* n; H( f - {
, O) b4 H2 N5 A - ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));0 [9 C3 n6 t7 j
- }
( m$ f v* `% Y; O1 D) Y. p - *pucRegBuffer++ = ucStatus;+ F8 S/ c" g4 O9 C. v, P1 X1 ~3 f$ E
- }
: ]8 m' x* N% d* K - }! \( z1 i/ z8 Q9 [
- return MB_ENOERR;
! b! r) p1 o3 g - }
- w0 Y# l4 D1 u8 p4 M8 Z8 {3 A9 ~ - : r4 m7 d. N! h P
- $ k. [, u, `* q0 `
- /// CMD4
6 K1 T2 z" ^4 D3 L& T' H' ^) O - eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
5 A( Y" K9 N; \% p# q: d. E! K - {
* v3 ]6 m) L. f7 ]: b; b - USHORT usRegIndex = usAddress - 1;4 W6 c# O$ z2 F
- USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
- s$ i+ |$ k% s9 N, ? - UCHAR ucStatus = 0;. Y% o0 E% U# ]3 n# X* b. d. J
- UCHAR ucBits = 0;& [' U; _" ?0 |2 X) a0 o" j5 A4 [
- UCHAR ucDisp = 0;( V* P* O# B9 @& y% a- U" h4 W& G
# Y) ]3 Z- L, e2 {- // 非法检测" E: k( k- L. m% r. a
- if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)/ h9 \1 s! h+ ]% p! R3 V: \ u
- {0 Q& R( K+ O7 P% r/ C* i8 X
- return MB_ENOREG;' l" r `7 f9 m& u
- }" ^! m- n: w, c3 l& w6 }' i% |
) N' I* w- s* R+ V9 z. ^, j: x- // 读离散输入2 ~5 e* @4 q4 O. `* Z2 m- c* l; W
- while(usCoilGroups--)
4 o2 w" v& [& Z. i$ v - {
. v& A# ?: Y' N, L# [5 |$ P - ucDisp = 0;& v7 X. T1 t9 X) @0 x- A, \6 {
- ucBits = 8;
Z9 \8 |$ O* }$ [& T- v, U* ^8 F - while((usNDiscrete--) != 0 && (ucBits--) != 0)
5 Y) G0 ~% P: F# i - {( Z" _' i6 I% @, j
- if(REG_DISC_BUF[usRegIndex])
1 P, N% A! p% ?) E& @ - {! g8 Z; D8 M0 o' ] Z- Y
- ucStatus |= (1 << ucDisp);" B; {9 `% U* y+ r* n
- } x# {7 U4 @' x N
- ucDisp++;3 @- t' M% W: ?! n% k7 k
- }$ k2 o' ~2 n d& R0 D- _
- *pucRegBuffer++ = ucStatus;
" C3 |$ s8 l0 O O" o - }! U. g2 K3 ^( O. S3 v5 @* C
- 9 u- z m% e3 P. p6 F: B9 l
- // 模拟改变
3 Z. r& A/ w+ L9 M! @ - for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)3 o" [3 |; w( t8 g% h# B/ W
- {
+ q: l) _' E" i# P; a - REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
- |1 {9 R! e$ C4 N) j9 D6 ?) z - }
+ k6 I2 D: ?( {* ? I# N( D
. R) y6 e) M1 X" _- return MB_ENOERR;
. \/ q. c. D, J: @( E5 k) p - }
复制代码
5 l+ p' c+ c% U1 }3 G9 T) J主函数
+ m/ u: Y5 z* N* h5 _$ ^* H; c- int main(void)
9 u% x8 f7 D: U- M! {/ } Q, R& { - {
9 A: n6 I, x7 N5 G4 x4 ~$ s - HAL_Init();
1 u- k& R4 M' K6 j" z# I) x - SystemClock_Config();, G( c* V5 d% t+ }4 L$ A: H+ |: t
- MX_GPIO_Init();
5 g( R6 i2 M* I7 s+ V: m - eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD); // 初始化modbus为RTU方式,波特率9600,奇校验! x. r. h4 n% J7 S9 y
- eMBEnable(); // 使能modbus协议栈+ Z! O3 K: G% d0 V |
- 9 t4 v% E% G7 C# ^! g0 K' W! G
- for( ;; )0 J% Y0 X+ F* n) g7 u. K
- {1 i9 P- F; {* S' @! {
- eMBPoll(); // 轮训查询
8 E8 q! I$ Y' `& _ - }6 ~( a" I1 d) y- n# ~
- }
复制代码 + M" B0 ^) l0 D( F1 X+ x
移植测试
4 K* w/ }# u' Z" z( r' V. s# c; r4 Q7 ?) y# U
8 x) w1 J3 d' ?6 U# }
7 p% v/ ? D3 m" M. Rends…
: G4 A; E$ l; L7 C" i5 v
% K4 h+ ?( k, e" m4 z3 N ^9 L9 `7 c5 R+ p" b8 a+ c5 M
3 V- W' ` a, }) T# V
|
我的是stm32g071系列,移植上去不行怎么办