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