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