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