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

【经验分享】STM32F103单片机modbus通信示例

[复制链接]
STMCU小助手 发布时间:2022-3-20 21:44
      前两天在研究STM32F103单片机的串口空闲中断时,突然想起来Modbus通信非常适合用空闲中断来处理。先看看Modbus RTU模式下的通信规范。
& ?* \6 t9 S, _7 c3 V% M
8 s$ a" q% N) G X}VKZB)JFT7OP1MK]D~`QKS.png % g# Z$ y* c: ?- k1 u4 P
* i; c  K# _& @4 K/ z7 y% i- f: X
       可以看到Modbus RTU通信模式下,数据的开始和结束是由空闲字符间隔时间来区分的,而STM32F103单片机自带串口空闲模式检测。
" R) }1 X% v9 X6 c, ]# y$ p0 D3 X+ N& f+ v
}(H52OS$DGQ%ZC95O5%4X3R.png 3 `1 a! n* q2 V+ a' i

; q, a# H) m  q' a9 @        在通常情况下Modbus通信一帧数据的检测可以用时间判断,不停的去读取接收数据的长度是否发送变化,如果在一定时间内,接收数据的长度没有发生变化,就认为一帧数据结束完毕。% V" X  k% [% n+ I. X/ I
: l" u0 C- y6 g' M' U3 C
        通过时间来判断一帧数据是否接收完成,会导致单片机不停的进入中断。而STM32F103单片机自带了空闲数据检测功能,这个功能是由单片机内部的硬件完成的,不需要程序去处理,这样单片机的就有机会去执行更多了任务了。为了让单片机在处理Modbus RTU通信时更简单快速,可以用 串口DMA加上空闲中断的方式来实现。
* C$ q2 Q; G) M7 `6 _
- P/ f3 ^8 h$ l4 A        下面直接用代码说明具体的实现过程,首先初始化串口。
  U8 @8 L8 g7 ]3 ]5 ~) E( h+ m
+ @& W% b, [" B$ q4 I+ D5 @
  1. #define UART2_DMA  1                                                                                                                                                                //使用串口2  DMA传输- d  g; h0 O& A; J) k8 O1 N; u
  2. #define  CHECK_NONE_ONE_STOP    1 //无校验位  1个停止位  1有效  0 无效
    % C0 k. q' ^- f1 O( T7 T
  3. #define  CHECK_NONE_TWO_STOP    0 //无校验位  2个停止位  1有效  0 无效
    1 \/ G7 D  h3 x& \
  4. #define  CHECK_EVEN    0          //偶数校验   1有效  0 无效
    5 n" i, e/ b( n5 o
  5. #define  CHECK_ODD     0          //奇数校验   1有效  0 无效
    / B' P! b$ F2 w9 W, g) }
  6. 9 k5 A4 _. G$ g) X
  7. + X& O2 S0 e$ J# p% m5 A( ~
  8. void  uart2_init( u16 baud )
    ' f' T4 _* ^+ d9 M0 @
  9. {
    + O4 V' R1 g+ \) m7 F# o- H
  10.     GPIO_InitTypeDef GPIO_InitStructure;
    1 |) t! h' h' \, X- r
  11.     USART_InitTypeDef USART_InitStructure;
    # b0 M) ?  J; w1 g
  12.     NVIC_InitTypeDef  NVIC_InitStructure;5 a+ L  J7 o/ A' G
  13. * i: V0 N; x( S: B% v7 c5 k
  14.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    7 Q% j5 R) L3 K2 O, o) p1 W
  15.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );1 _' N& s) v/ r8 l5 m  m3 e; c
  16.   u- B/ t6 y, e. S( P
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    6 p: R  ]+ L7 y6 r  H; |
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                                                                                                 //推挽复用模式
    7 Q3 S, r3 l# A; K8 V
  19.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    * s% E0 ^, d1 |9 N* C) l# O5 O
  20.     GPIO_Init( GPIOA, &GPIO_InitStructure );, x; R, _0 D. o* k' d

  21. ( R% G0 J; a, r; o# B
  22.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    1 H2 c( d& `4 t* m
  23.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                                                                         //浮空输入模式. ?, }5 m3 X, [2 u
  24.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 Q2 n$ W5 j+ l5 b8 i5 P+ {
  25.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    2 s' k$ e* L" g9 }! s. E

  26. 1 j; J2 r; h. V- T9 M+ N3 B9 Q
  27.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    6 |; m/ P  {7 I; h, h" p# ^
  28.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;+ b, N4 ~: A* w6 G2 M
  29.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    ! b' W1 u% a3 ~+ ~0 B" M/ U
  30.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    ' m7 D) p5 t! k1 R  k
  31. $ T/ i9 t  I% x& U
  32.     NVIC_Init( &NVIC_InitStructure );
    9 r" A1 }: r3 @0 Q/ a! h( A
  33. 4 N  A5 ^$ s+ ?% `( o9 A2 m; e; M
  34.     USART_InitStructure.USART_BaudRate = baud;
    ' \# u7 r. u4 h' b& U$ F% E
  35.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;! j* c8 ^( S; _  t" n; s3 d5 ]; r
  36.     USART_InitStructure.USART_StopBits = USART_StopBits_1;/ N- G% a0 p( o& P5 W
  37.     USART_InitStructure.USART_Parity = USART_Parity_No;0 V8 O" R1 t3 B4 O& L
  38.                
    0 [6 H! U5 F" b9 O
  39. #if(CHECK_EVEN == 1)                                                                                                                                                                                                                          //如果定义了偶校验  数据位长度要改为9位
    ! K1 \$ L6 M$ S  ~9 X+ E
  40.     USART_InitStructure.USART_WordLength = USART_WordLength_9b;
    . f: k8 l: B3 Q, u
  41.     USART_InitStructure.USART_Parity = USART_Parity_Even;
    ; g3 H9 H; c1 G2 s5 ]. X% R: {* p% X
  42. #endif: O# {$ o$ G4 X$ E

  43. & g1 R5 s6 k5 a9 V
  44. #if(CHECK_ODD == 1)                                                                                                                                                                                                                          //如果定义了奇校验  数据位长度要改为9位
    : d$ P/ b* W, h* N4 Z
  45.     USART_InitStructure.USART_WordLength = USART_WordLength_9b;" O/ O& ?6 t9 U1 W
  46.     USART_InitStructure.USART_Parity = USART_Parity_Odd;
    % `6 d( \' y3 r
  47. #endif7 D( j7 G4 m5 T

  48. & M9 a! j) k, X
  49. #if(CHECK_NONE_ONE_STOP==1)                                                                                                                                                                                           //停止位为 一位
    $ E( \$ \3 d# H' w0 v
  50.     USART_InitStructure.USART_StopBits = USART_StopBits_1;2 E( y! [! d0 v; x% t; x
  51. #endif
    " y& G: X! X& x9 @

  52. ( `1 i; Z' \4 a  v7 L1 }* m6 ?9 z
  53. #if(CHECK_NONE_TWO_STOP==1)                                                                                                                                                                                                 //停止位为 两位                                                                                                                                        1 U& |" Y3 Y+ q- B
  54.     USART_InitStructure.USART_StopBits = USART_StopBits_2;
    % ?/ n! m8 P7 ]+ X7 z
  55. #endif                / C0 F1 l% ]) S: c; C- F( [8 d2 X
  56.                
    ' |: j( b. j- a2 |6 R
  57.                 ( _% p  F, D1 c: t
  58.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;2 w' |4 J$ e" U6 e# @0 V
  59.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;1 U0 Z2 t7 j# ~3 h
  60. : @$ Z' `) p" `, }" a: f6 C
  61.     USART_Init( USART2, &USART_InitStructure );# O) r( k- J& \. l0 @
  62.                
    9 R8 a* ^8 o9 l. x4 @$ p( l  V
  63. #if (UART2_DMA == 1)
    6 G# j/ e& J  ]: [2 P9 x
  64.     USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );                                                                                         //使能串口空闲中断9 @/ n# ~7 h# t* C' l9 W6 |
  65.     USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );                                                                                         //使能串口2 DMA接收
    # T0 Z6 B) u% {# r( w* \& u9 E
  66.     uartDMA_Init();                                                                                                                                                                                                                                 //初始化 DMA
    % p9 ]/ p2 R( Q
  67. #else# |. e8 ~8 w& i" u4 Q& S5 R
  68.     USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );                                                                                   //使能串口RXNE接收中断; Z5 q% u' h: k$ a
  69. #endif
      Y% T" y! L* j! I3 `9 ^
  70.     USART_Cmd( USART2, ENABLE );                                                                                                                                                                         //使能串口2( L$ S9 M  k/ r) M' L+ t" Q. R
  71.                 & Q1 _& t5 b8 E2 I: D4 W/ U
  72. //RXNE中断和IDLE中断的区别?6 |! V! m3 _7 q
  73. //当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
    0 ?+ h2 h& s* t, G  Q) O# d, G7 |+ t
  74. }
复制代码
) Q& d( {' d; j
        在Modbus通信时,需要对每个字节的数据进行校验,所以这里使用宏定义来初始化串口数据使用的是那种校验方式。如果需要偶校验就将CHECK_EVEN 设置为1,如果需要奇校验就将CHECK_ODD设置为1,如果不需要校验位,就将这两个都设置为0。在串口初始化的时候,需要开启空闲中断和DMA功能。& N( V) e: G( j3 U  j& B2 @1 C

3 b. Y9 G- b. ]        下来初始化 串口2的DMA功能
2 p  M, C" W0 g6 X4 ]5 ^' q' [7 ]+ j" t* G4 x! ]0 B3 b0 k
  1. void uartDMA_Init( void )
    % C  _4 {. ~0 ]! d' l2 s
  2. {) g5 E! W' \8 W  I8 l4 `$ H
  3.     DMA_InitTypeDef  DMA_IniStructure;) `& D6 {% r; U4 Y" S) P
  4. 4 b* I! V) N3 ^% G) @1 T# Z
  5.     RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );                                                                          //使能DMA时钟
    4 S) w* |1 @' A% O9 \# e: P7 \

  6. & ~. g1 \  d, f9 a9 c! b- K
  7.     DMA_DeInit( DMA1_Channel6 );                                                                                                                                                             //DMA1通道6对应 USART2_RX% a: e+ K$ [+ c( ]  h/ ~
  8.     DMA_IniStructure.DMA_PeripheralBaseAddr = ( u32 )&USART2->DR;                                         //DMA外设usart基地址2 `' |* K" E# u& V3 y
  9.     DMA_IniStructure.DMA_MemoryBaseAddr = ( u32 )dma_rec_buff;                                                  //DMA内存基地址. U$ ]; S7 b- s1 W
  10.     DMA_IniStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                                                                         //数据传输方向,从外设读取发送到内存
    & z" s6 x- J9 ~+ Z- k
  11.     DMA_IniStructure.DMA_BufferSize = DMA_REC_LEN;                                                                                                 //DMA通道的DMA缓存的大小  也就是 DMA一次传输的字节数
    1 S+ [6 x! O/ N. M2 }
  12.     DMA_IniStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                 //外设地址寄存器不变( k+ p( l/ ^8 N
  13.     DMA_IniStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                                 //数据缓冲区地址递增# Y1 y/ q" l5 H
  14.     DMA_IniStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设以字节为单位搬运9 B. z; R7 g# M/ r% c
  15.     DMA_IniStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                                  //数据缓冲区以字节为单位搬入) @5 t5 {) Q# D
  16.     DMA_IniStructure.DMA_Mode = DMA_Mode_Normal;                                                                                                         //工作在正常缓存模式
    ! C# t4 Z' q) \& Q
  17.     DMA_IniStructure.DMA_Priority = DMA_Priority_Medium;                                                                         //DMA通道 x拥有中优先级
    - p& X' L% j  Q# f& @* M
  18.     DMA_IniStructure.DMA_M2M = DMA_M2M_Disable;                                                                                                                 //DMA通道x没有设置为内存到内存传输
    ' S+ |! \. A# s

  19. 5 o' |4 Q9 j$ N/ @! I3 P
  20.     DMA_Init( DMA1_Channel6, &DMA_IniStructure );
    1 G1 S" g( |' ~, o" @# K
  21.     DMA_Cmd( DMA1_Channel6, ENABLE );( |  W3 d; d+ [5 @1 X$ ?
  22. }
复制代码
$ s- K: U9 |9 x- w# H, B. m
GT9L~X56176YOAZQS$LM5QR.png
1 L& x; B' G7 n  C
0 K! ^. x# k  g. i/ M# B7 [        串口2的接收对应的是DMA的通道6,所以DMA的通道选择通道6,然后将串口2的数据寄存器作为外设基地址,将数组dma_rec_buff作为DMA的内存地址。外设地址不自增,内存地址自增。这样串口2每接收一个字节的数据,DMA就将接收到的数据存入数组dma_rec_buff中,同时指向数组的地址增加一位,这样串口2接收到的数据就会被一直存放在数组dma_rec_buff中。
: m0 g% d9 T5 Y$ G6 S4 x0 O# r' t
0 @7 Z2 @* F$ W3 t        这里要注意一点,数组的缓存区要大于串口2接收到的最长数据个数,否则如果接收到串口数据比较长,而数组比较小,数组中的数据就会被覆盖,导致接收到的数据错误。2 G& O6 P  k" O7 K

* C0 x. _& R+ a0 s( f9 T     接下来就是串口中断的实现了
4 |4 }" ?9 k3 F) Y7 h9 D
8 w2 V3 @2 u& H. |" r
  1. void USART2_IRQHandler( void )
    1 Q+ {  }/ z6 f
  2. {& k( I, m  L& i6 }! Y' K" I
  3.     u8 tem = 0;- y+ {7 C8 s4 }: j  G8 V4 c
  4. #if (UART2_DMA == 1)                        //如果使能了 UART2_DMA 则使用串口空闲中断和 DMA功能接收数据
    ' o! n$ |, j/ o$ a
  5.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )                                                                                   //空闲中断  一帧数据发送完成0 J) ~+ Z# i1 C% Z) N8 e
  6.     {' P# G, Q  M% O# @
  7.         USART_ReceiveData( USART2 );                                                                                                                                                                                 //读取数据注意:这句必须要,否则不能够清除空闲中断标志位。
    ' K" \' _1 e' C$ B
  8.         DMA_Cmd( DMA1_Channel6, DISABLE );                                                                                                                                                         //关闭 DMA 防止后续数据干扰- y) W; m  c9 a9 q
  9.         uart2_rec_cnt = DMA_REC_LEN - DMA_GetCurrDataCounter( DMA1_Channel6 );  //DMA接收缓冲区数据长度减去当前 DMA传输通道中剩余单元数量就是已经接收到的数据数量
    : q1 ^4 K9 o9 |/ I, C+ {7 |
  10. # N0 R+ a; M) l  h
  11.         copy_data( dma_rec_buff, uart2_rec_cnt );                                                                                                                    //备份数据
    3 E$ ^0 O& C; K  W, P" t! _3 I. Z8 l( T
  12.         receiveOK_flag = 1;                                                                                                                                                                                                                        //置位数据接收完成标志位8 N9 j2 o/ F/ @* v8 b. |
  13.         USART_ClearITPendingBit( USART2, USART_IT_IDLE );                                                                                                 //清除空闲中断标志位
    8 B+ w3 ^: J5 P  F2 T  \8 s
  14.         myDMA_Enable( DMA1_Channel6 );                                                                                                                                                                         //重新恢复 DMA等待下一次接收
    3 R/ v. C4 k& k- i- F* |3 }. L4 K
  15.     }
    % @  i5 L$ M' z3 n, P4 |0 O# s: K
  16. #else                                                                                //如果未使能 UART2_DMA 则通过常规方式接收数据  每接收到一个字节就会进入一次中断
    + S2 ^$ {& Z2 V* M4 H% P+ u
  17.     if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )                                                 //接收中断
    / ~1 T! l5 j2 a. M- J5 A
  18.     {
    7 s. f0 N$ o/ Z7 {6 h) f: q& F
  19.         tem = USART_ReceiveData( USART2 );
    8 e. O. J1 c, h( U5 ]) s0 T
  20.         USART_SendData( USART2, tem );
      U0 V) A+ x, \' B
  21.     }* `, @" q/ ^( _: z6 w5 E2 h7 D  r
  22. #endif; {1 {! A4 b% N/ D) h
  23. }
复制代码
$ M! [( \+ A$ e( s: d
        串口进入空闲中断后,说明一帧数据已经接收完成。关闭DMA通道,开始处理数据。获取DMA中接收到的数据长度,然后将数据拷贝到备份数组中,然后置接收完成标志位。开启 DMA进行下一次数据接收。
0 l: r( i7 K" C" `0 C" v3 {& a6 x9 u! ~0 z$ t/ z7 e- R- X7 @% n6 @
        在主程序中如果检测到了数据接收完成标志时,就开始处理接收到的数据。这样就可以将串口接收数据和处理数据分开,在处理上一笔数据的时候,就可以继续接收下一笔数据。提高了系统的实时性。
  W- j; w2 T2 S( u4 {; b
, s- |/ w$ p, }1 W8 j, L        这里如果直接使用DMA接收数组去处理数据的话,有可能会出现,旧的一笔数据未处理完成时,DMA接收到了新的一笔数据,然后存储到了 DMA缓存数组中。这样在处理数据过程中,旧的数据就会被新的数据覆盖掉,导致系统异常。如果Modbus的通信速率没有那么快,在下一次数据来临之前,系统完全有时间处理完上一笔数据,那么直接使用DMA的缓存数据也是可以的。1 f8 D' i0 q+ G: ~6 N# b1 N
: ]7 t5 g2 r% k& M8 H. y
       不过上面这种将数据拷贝倒其他数据中处理的方法也是有风险的。比如第一笔数据还没处理完成,第二笔数据已经接收完成,此时进入串口空闲中断,系统依然会将第二笔数据拷贝到备份数组中去。这样备份数组中的数据依然会被覆盖。但是这种情况如果出现的话,就说明接收数据的频率高于系统处理数据的频率,系统本身就存在风险,需要重新设计。
' q- N7 \+ R( X6 q" N! ?; t9 N$ F- p' t; W8 A& L4 M
     接收数据完成后,就需要在主程序中调用数据处理函数。
/ D6 i1 T2 L6 @+ [7 ?6 m6 D, [
  1. int main(void)4 t3 O3 {8 w& D6 Q7 M$ p; z
  2. {" m4 Z/ M( f; d7 O, s! |# T
  3.     u8  j = 0;/ _) n2 T- y# a: [& R; f% p
  4.         
    9 u4 G5 P! F2 g2 N
  5.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    ( f1 [" I/ j$ Z; p% ]" I
  6.         
    # Q1 L  y- c  H3 _& [- E
  7.     delay_init();       //延时函数初始化
    4 q, X- D# X' q' D% N6 ]
  8.     LED_Init();         //初始化与LED连接的硬件接口
    4 n, M. h% s- r: t& d: C/ g
  9.     uart2_init(9600);# a6 Z9 G! A' W/ J
  10.     while(1)
    ) h! O5 {7 d7 n/ t* B* O1 q
  11.     {
    2 I* _* R2 A; }: _& e3 v4 N) a
  12. * A' B- v' y, P/ I
  13.         if( receiveOK_flag )                                //一帧数据接收完成后开始处理数据& y, u5 E( M7 y
  14.         {
    % Y/ J( t, `5 H+ m  m" Z+ X% T
  15.             receiveOK_flag = 0;0 \1 W7 p$ W( }; U: ]  O
  16.             DisposeReceive();                                //处理modbus通信
    ' E) Q; c1 I  b1 m1 h5 Z0 @
  17.         }' I$ M+ f  a, r+ c3 V& H( @
  18.                         
    : B; a: S' t. t5 M* `
  19.         j++;! @  b% S" \  S
  20.         if(j > 50)2 R& P! D# O* J$ G$ ]4 h
  21.         {4 ^) K8 B; V) a) Y
  22.             j = 0;- |7 g# f. Z" N$ z
  23.             LED = !LED;* }3 l# m) B/ i, W: i9 s! t- X
  24.         }  H3 w0 K/ w7 H5 J9 N
  25.         delay_ms(10);
    ! _8 A* N* t+ N4 o* j# ?: k  c+ d
  26.     }
    . R6 Y' A) h, S# E/ b3 o, u( U& F- G
  27. }
复制代码
! z, _5 z  ?  W. M' E
最后是Modbus的协议解析7 L( T8 ^& F7 l& E6 \( c, d
  1. void DisposeReceive( void )
    ! m. [6 I0 ?% G/ A5 m7 r. ~2 ]
  2. {7 }9 ]: q7 D6 [6 _
  3.     u16 CRC16 = 0, CRC16Temp = 0;
    : U/ q1 U7 E0 ~( n" W. M
  4.     if( data_backup[0] == SlaveID )                                                                         //地址等于本机地址 地址范围:1 - 32) e. Z, F7 L; e9 A7 O7 v
  5.     {
    . x4 p$ r# [* u2 ^2 g
  6.         CRC16 = App_Tab_Get_CRC16( data_backup, dataLen_backup - 2 );                                  //CRC校验 低字节在前 高字节在后 高字节为报文最后一个字节
    ; \- W% o; K4 H
  7.         CRC16Temp = ( ( u16 )( data_backup[dataLen_backup - 1] << 8 ) | data_backup[dataLen_backup - 2] );, a, n# @* c2 S* h0 ]
  8.         if( CRC16 != CRC16Temp ), a  ~- b# m0 T! T4 f/ V  r0 a
  9.         {% w/ D- v4 X5 T* L8 G; o' z' K
  10.             err = 4;                                                                                               //CRC校验错误2 o& p, P' h% D7 x8 K
  11.         }
    6 ^' s0 u( B1 {4 q2 z9 z
  12.         StartRegAddr = ( u16 )( data_backup[2] << 8 ) | data_backup[3];8 C" @1 q* Z& Y# z
  13.         if( StartRegAddr > (HoldRegStartAddr + HoldRegCount - 1) ) : r* z. @" J7 s  s; N; N
  14.         {
    . }4 d4 {' t6 G) h8 e. \8 \* a9 H* Z
  15.             err = 2;                                                                                               //起始地址不在规定范围内 00 - 07    1 - 8号通道6 w8 l& V$ b/ y$ ]1 `) i
  16.         }
      l  m$ H5 _2 i. X$ o9 x
  17.         if( err == 0 )% k2 n+ N: t+ _$ ^1 `
  18.         {
    2 h% D/ f7 [( D3 F
  19.             switch( data_backup[1] )                                                                        //功能码8 t+ o1 W8 ^8 j. Y  S) x: T- y
  20.             {
    7 w% J1 t4 u6 h8 A2 Q
  21.                 case 3:                                                                                            //读多个寄存器
    ! @, K2 j8 Q+ M1 S. j( d
  22.                 {
    ; q$ h9 \6 A% s2 b6 o* f
  23.                     Modbus_03_Slave();" `; t" Q! T% A
  24.                     break;0 T7 P( J8 b% t% S0 X* O
  25.                 }. [# M) Z5 p: D& z4 c6 c7 M' X: y4 l
  26.                 case 6:                                                                                            //写单个寄存器
    - O: s, p- U6 J# L
  27.                 {9 ~: L) N2 M# \$ q3 {
  28.                     Modbus_06_Slave();
    4 N3 r, Y* Y; ]. [3 n7 R! F5 a, j
  29.                     break;
    6 h: \1 u& U" Q; J0 \+ W
  30.                 }" Q; l& @9 W' ]. ^! @
  31.                 case 16:                                                                                           //写多个寄存器7 ?# R! @, a! O8 K6 x
  32.                 {8 O  L" E7 k$ e
  33.                     Modbus_16_Slave();
    8 n- S# w. V# A9 A; j
  34.                     break;' J6 X: c, Q7 M2 y
  35.                 }* u/ W' }! u+ l& B" o2 I0 @: V) I
  36.                 default:/ p+ _& L6 f9 f: @
  37.                 {
    . s6 U  g; Z- ?, p  y
  38.                     err = 1;                                                                                       //不支持该功能码+ c3 j& B+ O3 U+ w9 g  o& K
  39.                     break;) u9 ~9 j  ]( Y9 K$ Y: G- }$ R
  40.                 }5 `" Q9 J" }. V
  41.             }
    $ Y7 S- n0 c$ h  y( E" r( \3 }
  42.         }- z* ]/ Z/ l0 g/ W1 T0 O) R
  43.         if( err > 0 )
    . ?( C  J) W0 N- }
  44.         {
    # I  y1 a4 b) m! I, v% h
  45.             SendBuf[0] = data_backup[0];
    2 h9 U( f3 b6 L
  46.             SendBuf[1] = data_backup[1] | 0x80;& _$ p8 ?3 @, W3 T
  47.             SendBuf[2] = err;                                                                                      //发送错误代码
    ( d, c/ T6 t8 q# c) K
  48.             CRC16Temp = App_Tab_Get_CRC16( SendBuf, 3 );                                                           //计算CRC校验值
    ' i4 o+ ]+ p& \6 Q; `, W
  49.             SendBuf[3] = CRC16Temp & 0xFF;                                                                         //CRC低位
    , q0 {. X& m. f4 S2 ~" z* T
  50.             SendBuf[4] = ( CRC16Temp >> 8 );                                                                       //CRC高位
    ) N0 U9 V% p$ ]4 A# H
  51.             uart2_Send( SendBuf, 5 );                                        4 O! A4 p4 \7 s0 a. t  k' b4 H
  52.             err = 0;                                                                                               //发送完数据后清除错误标志* T- m$ ]7 H5 Q' f2 k
  53.         }0 K! h0 `2 O) `& a/ k
  54.     }
    2 I3 J2 ^( L  [* n3 ?  N
  55. }" w# x4 M' e: `  Z

  56. 9 j& {8 [7 }) j" k( t5 x. W' V) D
  57. /*4 q! g; d1 x6 K  T$ K4 p4 Y4 ]) p) k
  58. 函数功能:读保持寄存器  03+ s, M! m1 a) r; [
  59. 主站请求报文:      0x01 0x03   0x0000  0x0001  0x840A     读从0开始的1个保持寄存器. P; m2 O4 O7 ?+ |. v' f% ?
  60. 从站正常响应报文:  0x01 0x03   0x02    0x09C4  0xBF87     读到的2字节数据为 0x09C4
    ) b$ z' S& i/ N; {
  61. */" h8 w: C3 \9 Q, B: m0 |" o
  62. void Modbus_03_Slave( void )1 p- M6 W+ E# ~+ l7 w7 k. Y
  63. {0 h8 w6 l+ c) c2 n6 r1 Y/ \. v$ r
  64.     u16 RegNum = 0;
    4 I. U9 L/ P/ h2 d: l. z
  65.     u16 CRC16Temp = 0;
    ! M; C3 B: n7 S- L
  66.     u8 i = 0;1 `! j- U+ _& A" _) }
  67.     RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量  _: S8 u5 w5 Q$ }4 P  k
  68.     if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )      //寄存器地址+寄存器数量 在规定范围内 <=8& Z/ B$ O0 [& L  N. E6 d) Z
  69.     {
    ) ^; K6 ]+ J4 I, Q
  70.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址        
    # N! E8 d2 e* j- \1 X& C
  71.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
    ( t( T- b- @2 s  L0 C- u
  72.         SendBuf[2] = RegNum * 2;                                                                                                                                                                                        //返回字节数量
    + Q; v8 R, g+ l; b0 Y9 f. u
  73.         for( i = 0; i < RegNum; i++ )                                                                             //循环读取保持寄存器内的值
    9 J1 ~+ ]0 c( r7 m
  74.         {
    6 C8 P" X3 R" t- t9 ~/ D
  75.             SendBuf[3 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2];
    ! n/ G# h7 E8 w) A3 c& m! a# I+ P
  76.             SendBuf[4 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2 + 1];
    : W8 ~8 p& d6 Y. s6 \
  77.         }. ?3 s( u* [" U5 i9 R
  78.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, RegNum * 2 + 3 );                                                  //获取CRC校验值- P, Y& q5 V2 q8 {
  79.         SendBuf[RegNum * 2 + 3] = CRC16Temp & 0xFF;                                                                //CRC低位
    4 n3 S6 C/ h  g  ^
  80.         SendBuf[RegNum * 2 + 4] = ( CRC16Temp >> 8 );                                                              //CRC高位+ }. v0 O  I! F# }& O
  81.         uart2_Send( SendBuf, RegNum * 2 + 5 );9 ?% P% Y# w8 [2 Y( U" A; [3 [% T" l
  82.     }
    5 t: p  i4 Z: x  {3 ^. R
  83.     else
    ; j8 o2 r# l- {) X) i
  84.     {" m9 Z( g* c. e. b
  85.         err = 3;                                                                                                   //寄存器数量不在规定范围内9 p+ p% Z6 L; {$ ^5 r/ b
  86.     }% n5 g# d# [. q- e
  87. }
    * T8 K, X( M; t/ e
  88. /*
    / j7 Z( R" s6 u* D: T
  89. 函数功能:写单个保持寄存器 06
    & p/ K% ^9 i4 X# Y+ [
  90. 主站请求报文:      0x01 0x06    0x0000  0x1388    0x849C   写0号寄存器的值为0x1388& M5 m. |2 A: c+ a6 Y: C
  91. 从站正常响应报文:  0x01 0x06    0x0000  0x1388    0x849C    0号寄存器的值为0x13881 i- ^; A' y. }$ `2 B+ H! Q. P
  92. */
    ) `2 _2 R8 a* Q
  93. void Modbus_06_Slave( void )
    ( l! B4 m2 m+ s
  94. {# I, {  j0 j/ b9 B8 e- o' V
  95.     u16  RegValue = 0;7 A. u' U% [7 Q! W% z$ [3 c; g, y
  96.     u16 CRC16Temp = 0;
    : ?" m; v! K* G
  97.     RegValue = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                              //获取寄存器值
    5 V4 d# I, _0 R% H
  98.     if( RegValue <= HoldMaxValue )                                                  //寄存器值不超过1000% [  p& o! w+ s% y  F6 o: h# w
  99.     {
    2 m& l) m3 ?) c& P, U& U# m8 j
  100.         HoldReg[StartRegAddr * 2] = data_backup[4];                                                         //存储寄存器值
    + K/ u/ V' Q; t0 W
  101.         HoldReg[StartRegAddr * 2 + 1] = data_backup[5];
    / g2 t" z! N$ o
  102.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址
    # w4 u6 Q  A# J6 a( t
  103.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
    ! A" Y: ~4 M+ H$ ?
  104.         SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
    - x, H( n! y# e" Y- ~3 S
  105.         SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位$ L5 J. U" q& w8 Z) K6 t, v
  106.         SendBuf[4] = data_backup[4];                                                                                                                                                                        //值高位
    3 t: h: D% G9 U) M' P' w
  107.         SendBuf[5] = data_backup[5];                                                                                                                                                                        //值低位
    * B! u" R/ G; f
  108.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                              //获取CRC校验值% i$ |- [+ K. h
  109.         SendBuf[6] = CRC16Temp & 0xFF;                                                                            //CRC低位' b. e" F" z2 z3 f; ^
  110.         SendBuf[7] = ( CRC16Temp >> 8 );                                                                          //CRC高位% {# a3 I# p* z9 _; P/ v
  111.         uart2_Send( SendBuf, 8 );
    & }, }5 O8 z  k
  112.     }1 \# B5 Y, H: f% m$ E1 F6 K0 A( r
  113.     else4 _: e; x3 L& I8 A
  114.     {; G8 e" F& z! Y0 d* _! P* @
  115.         err =  3;                                                                                                  //寄存器数值不在规定范围内, J3 ^  @3 c. H. K
  116.     }' s; ~& S+ [/ Q% R1 ^3 g8 n  S
  117. }
    - Y5 q; N, \5 y' V$ B3 w
  118. /*  `2 Q0 ^; J" y
  119. 函数功能:写多个连续保持寄存器值 16! ~: L3 J$ {. Y( S! X  f
  120. 主站请求报文:       0x01 0x10    0x7540  0x0002  0x04  0x0000 0x2710    0xB731  写从0x7540地址开始的2个保持寄存器值 共4字节1 s% a7 T: _, L2 z' r1 L5 Z% [. n
  121. 从站正常响应报文:   0x01 0x10    0x7540  0x0002  0x5A10                         写从0x7540地址开始的2个保持寄存器值
    5 |% x  y2 n* s9 m
  122. */
    * a% t1 o8 \8 ]! |
  123. void Modbus_16_Slave( void )% ^) T3 k( Y6 A+ D( w$ `9 S* q
  124. {
    9 Y. P4 Y9 b3 O/ ?  K: f- C
  125.     u16 RegNum = 0;: C& X! U7 w2 c8 @$ a1 d' ]/ l
  126.     u16 CRC16Temp = 0;
    4 s( y& w& W' q; l, r) `
  127.     u8 i = 0;/ y: U( m, r) z1 n* e0 X
  128.     RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量
    9 R" ^& V, j8 o( j9 r: @
  129.     if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )             //寄存器地址+寄存器数量 在规定范围内 <=8
    * y1 |  o. d* ?
  130.     {
    - ~  ]4 p2 r) [# Q; z% u! G$ c
  131.         for( i = 0; i < RegNum; i++ )                                                                              //存储寄存器设置值
    ) p" o% s; |7 ~0 ~  L
  132.         {
    / t* V  M: s7 N& D5 M1 w9 u
  133.             HoldReg[StartRegAddr * 2 + i * 2] = data_backup[i * 2 + 7];( I) y: V" O( L3 i' R" |
  134.             HoldReg[StartRegAddr * 2 + 1 + i * 2] = data_backup[i * 2 + 8];
    1 v! R- `( }! N# P, E" [" L
  135.         }
    # j8 ~0 {) F4 K/ J% K
  136.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //起始地址* h1 ?- @% E7 x4 o
  137.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
    . G9 j/ w( u. t
  138.         SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
    - S+ v! P8 X8 K
  139.         SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
    ' I* I% a* w7 d- I: b8 m
  140.         SendBuf[4] = data_backup[4];                                                                                                                                                                        //寄存器数量高位
    8 t" Q( {1 A$ g+ _+ c  `
  141.         SendBuf[5] = data_backup[5];                                                                                                                                                                        //寄存器数量低位6 E1 `! M& L& h
  142.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                        //获取CRC校验值2 N7 e, C$ r" i/ @; @, c" G
  143.         SendBuf[6] = CRC16Temp & 0xFF;                                                                      //CRC低位
    2 k6 k- F5 ?2 ~3 }, ~
  144.         SendBuf[7] = ( CRC16Temp >> 8 );                                                                    //CRC高位' {8 P( }' M& N
  145.         uart2_Send( SendBuf, 8 );% R5 i% J! m$ @7 V8 b' P5 o
  146.     }
    4 v; V7 M3 c, M
  147.     else. d1 }; R) g) m& j% f' {
  148.     {
    . A/ r# l. P! u* j: L" \
  149.         err = 3;                                                                                                   //寄存器数量不在规定范围内/ J% ]/ p( T1 d3 u: V: c; g
  150.     }; F9 P! E# E( C1 a
  151. }
复制代码

. e; J& K# `4 p8 }- LModbus的执行在这里只实现了最常用的三个。
! o( y9 I6 {/ A# I0 t+ N  G: Y, ~5 i3 u3 O
HYI%R~(IXBRGFE3Y}7K{U$U.png
) G/ A5 j; D( `( s  L2 A
4 I$ K, }3 ?* x$ W7 k各个功能码的实现原理在这里就不说了,直接通过代码中和注释就可以看明白了。
$ j3 Z' X' g1 n$ Z# I4 v; _% P; O* _0 x% h
下面贴出串口和modbus的完整代码
9 [/ ]: j6 Q8 T- r
/ R& H. r, n8 B) i0 ]0 X3 p  Suart2.h3 S) `+ S& o4 ]$ O2 h5 Y, P

; q3 A! ?- E* t/ |
  1. #ifndef __UART2_H( s1 m# i8 Q5 N2 c
  2. #define __UART2_H0 h, Z# F5 h# V7 i# R
  3. #include "sys.h"
    * [* Q) {7 }( Q
  4. ) t- Y" O1 \5 R' `) u* K. C
  5. #define DMA_REC_LEN 50                                                                                                                                                        //DMA数据接收缓冲区) h/ t+ t) `  H

  6. : t' X/ l; {0 _) l: F4 V$ H, X
  7. void  uart2_init(u16 baud);1 g% U  m( [# o- T% E
  8. void uartDMA_Init( void );
    / y5 V' E4 B( _+ H
  9. void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx );& z4 q3 J5 R% a8 K$ Y* A( |
  10. void uart2_Send( u8 *buf, u16 len );# e8 l5 v# @# Y/ ?; H
  11. void copy_data( u8 *buf, u16 len );/ ?- J: d& N- k" n+ y$ F
  12. #endif* i; {$ V% B% i
复制代码

$ U0 A) G( U: M# l+ z' p# M7 C' Juart2.c$ v1 U9 |# b6 k3 i: Y' @1 k
1 y  b8 q# u+ x& l( L; L$ O
  1. //串口2空闲中断 + DMA数据传输: q2 R. _  S6 c# N% X# v: ]1 O
  2. #include "uart2.h"
    4 k+ L) S; |1 n
  3. #define UART2_DMA  1                                                                                                                                                                //使用串口2  DMA传输
    6 E  U, _8 k, u7 p9 O# Z
  4. #define  CHECK_NONE_ONE_STOP    1 //无校验位  1个停止位  1有效  0 无效 1 P  q' y& A* z5 K6 ^0 h$ ?
  5. #define  CHECK_NONE_TWO_STOP    0 //无校验位  2个停止位  1有效  0 无效 ! z2 l+ k3 e8 y# [* ~
  6. #define  CHECK_EVEN    0          //偶数校验   1有效  0 无效
    % v& {; [/ R2 ^  |
  7. #define  CHECK_ODD     0          //奇数校验   1有效  0 无效
    0 H( P9 ?5 i# b0 L! F& Q
  8. + `8 S& v" p1 J
  9. u8 dma_rec_buff[DMA_REC_LEN] = {0};
    3 M) `) J! |3 q/ l  o! x
  10. u16 uart2_rec_cnt = 0;                                                                                                                                                        //串口接收数据长度! B, m# ?. |5 v% i
  11. 1 z3 x* s' ~/ z/ a/ ]
  12. u8 data_backup[DMA_REC_LEN] = {0};                                                                                                         //数据备份. S7 g. v& u! L4 ]. Z8 a9 X
  13. u16 dataLen_backup = 0;                                                                                                                                                        //长度备份
    / Z* H: p  ^" `) w4 |/ p, a
  14. _Bool receiveOK_flag = 0;                                                                                                                                                //接收完成标志位
    6 z! [. m3 V" M4 P
  15. " l- N) ^: U, J6 U( p
  16. /** A* r. v( F; p# U  R
  17. 空闲中断是什么意思呢?, e0 M8 h. I! z4 d! k0 T# D
  18. 指的是当总线接收数据时,一旦数据流断了,此时总线没有接收传输,处于空闲状态,IDLE就会置1,产生空闲中断;又有数据发送时,IDLE位就会置0;  E( o# D. l- ^5 [9 W
  19. 注意:置1之后它不会自动清0,也不会因为状态位是1而一直产生中断,它只有0跳变到1时才会产生,也可以理解为上升沿触发。
    - R9 m6 K3 v. I" r0 H
  20. 所以,为确保下次空闲中断正常进行,需要在中断服务函数发送任意数据来清除标志位。
    " z( e  P, g6 N2 n
  21. */- R# H- n6 E) i% \' J; u( Y* K
  22. * d7 u6 Z: z; _2 A. |2 t
  23. void  uart2_init( u16 baud )
    ' y+ F: ~2 e6 b, W" x9 Q
  24. {
    8 X# S5 k. t* A' l3 c
  25.     GPIO_InitTypeDef GPIO_InitStructure;
    " q5 F, l" e6 f7 d
  26.     USART_InitTypeDef USART_InitStructure;9 W3 Q9 d! x1 v  d
  27.     NVIC_InitTypeDef  NVIC_InitStructure;
    / F+ O* g+ B1 ?" A
  28. 6 x/ c% p2 V; L% Q' r8 r
  29.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    3 g6 P+ K5 w! f' _4 n- C
  30.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
    4 Z2 p# ^2 ]  ]) v2 [8 y5 S) |
  31. $ [" y* D7 A) ~6 d6 Y
  32.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;2 l- @+ O. ]1 U/ t2 D
  33.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                                                                                                 //推挽复用模式; r7 ?3 s9 J! W* n, Z2 V7 H' ?( W+ T
  34.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 i  `: r' N- q" y) T3 |4 R8 f9 ]" d
  35.     GPIO_Init( GPIOA, &GPIO_InitStructure );
      j/ y; I+ E+ B; o
  36. , }# z4 {3 X* e3 V! }  r/ Y
  37.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;! y) Y3 [/ e3 ]% l2 {$ W
  38.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                                                                         //浮空输入模式
    % Z) E* S) B7 V/ W
  39.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ; ^9 f# {. r/ X. P; t
  40.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    3 e+ F6 r* N; r" D
  41. - o% X6 I, q) K
  42.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;- C' s; m8 h# E
  43.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    4 T( C$ U$ e) @, V5 r7 m% C
  44.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    % f3 n" \+ E. y6 t: p
  45.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;- S0 B! {; ^: J4 @
  46. 8 K6 A% w6 ]) }( o- g
  47.     NVIC_Init( &NVIC_InitStructure );
    ' [% W; g  h+ {( s& X

  48. 1 a- k# Y/ i: }
  49.     USART_InitStructure.USART_BaudRate = baud;
    % v- w$ q# k3 @1 |
  50.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    / A; w. C( ]5 P6 O6 d" v3 i
  51.     USART_InitStructure.USART_StopBits = USART_StopBits_1;6 M* ]4 k# U8 ^1 X8 l' R/ ^+ {; J! L
  52.     USART_InitStructure.USART_Parity = USART_Parity_No;4 ^2 V0 o: H' u5 E2 s, i* D
  53.                 4 ?1 G( X% Q5 a* R/ @
  54. #if(CHECK_EVEN == 1)                                                                                                                                                                                                                          //如果定义了偶校验  数据位长度要改为9位
    * |. l* Q, G4 Y* W
  55.     USART_InitStructure.USART_WordLength = USART_WordLength_9b;, k5 {8 `" `; u
  56.     USART_InitStructure.USART_Parity = USART_Parity_Even;
    * ?4 j. X" G/ S$ H- m5 @
  57. #endif
    * d4 J* u$ X7 S5 s
  58. * Z! _. U4 G" ~* ~4 o' v
  59. #if(CHECK_ODD == 1)                                                                                                                                                                                                                          //如果定义了奇校验  数据位长度要改为9位4 z: O- w) g, u  |2 s
  60.     USART_InitStructure.USART_WordLength = USART_WordLength_9b;# h4 ^3 w# n/ r* |# ?9 W4 S2 _8 ^
  61.     USART_InitStructure.USART_Parity = USART_Parity_Odd;
    ' Z8 I: ]9 V/ s/ [) g7 E+ @2 O# S
  62. #endif7 \4 i1 |6 P/ I* F# _

  63. ) C6 w) q: j  \! C# U
  64. #if(CHECK_NONE_ONE_STOP==1)                                                                                                                                                                                           //停止位为 一位% w" G- [2 o. I* f' H/ ]: z
  65.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
    ) l; \/ N  f+ b' b: ]3 [3 K) N
  66. #endif2 S& B/ C( M" C, p5 G, j! D
  67. 9 N/ x1 t: V! X7 v2 B# j5 n. ~3 X  D
  68. #if(CHECK_NONE_TWO_STOP==1)                                                                                                                                                                                                 //停止位为 两位                                                                                                                                        * H+ _0 k9 m3 Z$ p
  69.     USART_InitStructure.USART_StopBits = USART_StopBits_2;+ ]) x7 M9 l7 n2 s) j% ]
  70. #endif                " P: i$ {) W0 P- a  n
  71.                
    5 Z) G, y. g2 i  o) o1 I+ K
  72.                
    . x9 ]5 n$ i8 [) t
  73.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    ; B% L4 [9 O' c6 _
  74.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    " c6 W3 H: s# B4 z
  75. 0 i8 j1 l" c$ X2 z
  76.     USART_Init( USART2, &USART_InitStructure );$ @8 G$ _5 z% K
  77.                 6 h. L, m" D7 D: B! X$ R9 n8 A- q6 O
  78. #if (UART2_DMA == 1)
    7 @5 u# @! U! ]8 r0 m3 l4 c' Z
  79.     USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );                                                                                         //使能串口空闲中断
    9 [* A" a( R, l
  80.     USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );                                                                                         //使能串口2 DMA接收+ r6 N1 n# D+ e, T4 H
  81.     uartDMA_Init();                                                                                                                                                                                                                                 //初始化 DMA
    - c3 m1 I4 K" \- H9 ]: @/ J. O
  82. #else
    5 p! ?1 u- b! q% j0 a" e) T0 @9 C
  83.     USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );                                                                                   //使能串口RXNE接收中断
    ' m; J7 ^8 d0 A- N8 Z# o8 A5 Q% V8 w
  84. #endif) X( j# B# M4 ]" B$ m
  85.     USART_Cmd( USART2, ENABLE );                                                                                                                                                                         //使能串口24 c! t; V/ J0 ~7 s1 x% x
  86.                 5 m5 l5 B! t1 m! J0 ?
  87. //RXNE中断和IDLE中断的区别?  @3 ?+ Y- ]8 L) v; U
  88. //当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。( l) e% ^& A+ ~+ F
  89. }
    8 [$ V. n& W) i5 i* j" t# I* x. d
  90. 9 Z5 t) o: k: ?. _; Y; i
  91. void uartDMA_Init( void )  i( `0 \* c/ q/ l( \% S/ d
  92. {
    ) s. G) n0 R* x
  93.     DMA_InitTypeDef  DMA_IniStructure;% @, X8 Z& }* r2 l/ Z4 x2 }% T: I

  94. * J; S3 d* K) N0 `' }4 t6 `% g+ u
  95.     RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );                                                                          //使能DMA时钟" i$ V% g( E& W" u1 E, i1 q4 r
  96.   t' R7 F0 H/ Y
  97.     DMA_DeInit( DMA1_Channel6 );                                                                                                                                                             //DMA1通道6对应 USART2_RX
    + ^+ ^, k/ p- ~" @7 N
  98.     DMA_IniStructure.DMA_PeripheralBaseAddr = ( u32 )&USART2->DR;                                         //DMA外设usart基地址
    5 j" x( c: N8 J
  99.     DMA_IniStructure.DMA_MemoryBaseAddr = ( u32 )dma_rec_buff;                                                  //DMA内存基地址1 G5 t/ H# D4 P) o8 i% L
  100.     DMA_IniStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                                                                         //数据传输方向,从外设读取发送到内存
    8 n' l% H1 P9 [' F! X! N
  101.     DMA_IniStructure.DMA_BufferSize = DMA_REC_LEN;                                                                                                 //DMA通道的DMA缓存的大小  也就是 DMA一次传输的字节数, @3 q5 A  H( y- P
  102.     DMA_IniStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                 //外设地址寄存器不变( g' K. l/ A* C" ?- |0 x
  103.     DMA_IniStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                                 //数据缓冲区地址递增) T0 M' |. ^2 X
  104.     DMA_IniStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设以字节为单位搬运8 `. \' @* E* u8 M
  105.     DMA_IniStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                                  //数据缓冲区以字节为单位搬入
      c9 x. ~" x' R$ H, E2 `4 l. @. {
  106.     DMA_IniStructure.DMA_Mode = DMA_Mode_Normal;                                                                                                         //工作在正常缓存模式6 s9 U" B! m( \0 X
  107.     DMA_IniStructure.DMA_Priority = DMA_Priority_Medium;                                                                         //DMA通道 x拥有中优先级
      D- m% [4 v! h; [; b
  108.     DMA_IniStructure.DMA_M2M = DMA_M2M_Disable;                                                                                                                 //DMA通道x没有设置为内存到内存传输* U- E& P; G- [* c6 [7 w( G5 d% ~

  109. - d$ G$ N: P  q, d2 a! _; [
  110.     DMA_Init( DMA1_Channel6, &DMA_IniStructure );
    ; e* c0 F* o! d. c! j0 Y( g5 v
  111.     DMA_Cmd( DMA1_Channel6, ENABLE );& b& i! \5 A2 g" _0 ^
  112. }
    0 Y% y4 o/ d. y' A6 D9 N. q
  113. & j" |7 t. A7 Y- |: C$ H4 V
  114. //重新恢复DMA指针& x# J) D/ L5 m3 B# |- T
  115. void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx )+ [' l, x/ g  i5 \+ |. `
  116. {
    # z/ P/ v4 m% H3 L/ @
  117.     DMA_Cmd( DMA_CHx, DISABLE );                                                                                                                                                                   //关闭DMA1所指示的通道6 Z  R5 N, b2 d( M. W7 s
  118.     DMA_SetCurrDataCounter( DMA_CHx, DMA_REC_LEN );                                                                                                 //DMA通道的DMA缓存的大小
    ( U' @. l. v0 J# ~) Q. P+ \+ g$ U3 m
  119.     DMA_Cmd( DMA_CHx, ENABLE );                                                                                                                                                                           //DMA1所指示的通道' c: o5 S9 C7 y$ e
  120. }. Y" h& k0 g$ b. Q& F$ f
  121. //发送len个字节
    & X$ L" O$ P9 W6 C; y
  122. //buf:发送区首地址5 B+ ~( `+ S: o( X
  123. //len:发送的字节数, C0 J. D6 k7 l0 m7 Y3 ?8 y
  124. void uart2_Send( u8 *buf, u16 len )" `6 g5 R* |4 b/ u, j3 K1 A1 Y' q
  125. {
    " t8 K& w4 A9 T/ ^. T' S
  126.     u16 t;
    6 m6 G; n5 ]7 ?6 ~1 {/ O8 }
  127.     for( t = 0; t < len; t++ )                                                                                                                                                                                 //循环发送数据$ X% G9 K2 }7 c) n5 ^
  128.     {2 r& T+ x8 J, H: h9 ?: [
  129.         while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );
    : M' a, A9 J9 r7 p% Q5 s$ y  A
  130.         USART_SendData( USART2, buf[t] );. M! t$ I: I/ z1 }/ m
  131.     }& y, z6 ?- L( B6 W8 C% A
  132.     while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );+ X/ u% P0 X0 _" }3 ]1 Z2 h) y
  133. }) L3 h+ ~, O  }: i% h+ Y2 n
  134. 8 t% L3 X& F' j
  135. //备份接收到的数据0 r2 |# x6 m% F& r
  136. void copy_data( u8 *buf, u16 len )
    0 {% n( X' Q1 I
  137. {/ P; l/ j4 O! s9 z: ]2 {
  138.     u16 t;
    % O+ l' E$ @) _! w4 c0 r
  139.     dataLen_backup = len;                                                                                                                                                                                                         //保存数据长度
    * q3 F. f, j/ G8 ]# ^
  140.     for( t = 0; t < len; t++ ), v7 k$ u9 t& o- f; ?
  141.     {# {) k0 c% I; `! R) P# ]( W- Y
  142.         data_backup[t] = buf[t];                                                                                                                                                                         //备份接收到的数据,防止在处理数据过程中接收到新数据,将旧数据覆盖掉。" t( @& p$ K( B* S3 E
  143.     }
    2 V8 E; u$ O2 ?$ \
  144. }
    # t, F/ \; {; Y6 l, f! t9 J' n
  145. // Modbus RTU 通信协议5 Q/ D$ v& m4 s7 ?2 W
  146. //地址码 功能码 寄存器高位 寄存器低位  数据高位  数据低位 CRC高位 CRC低位3 e3 n8 W" ~4 j7 ?0 I  [& v
  147. //01     03      00          00         03         e8      xx        xx
    7 @& T5 }2 N; B- J( F
  148. //利用空闲中断接收串口不定长数据& Q( C$ ~; j5 R# O# e' n3 S2 h
  149. //串口接收一组数据结束后才会进入中断,在数据发送过程中,已经接收到的数据将会被存入DMA的缓冲区中: T$ ?/ f- Q: I5 F4 G% T
  150. void USART2_IRQHandler( void )5 o, P2 A  D& [
  151. {$ {/ q: F4 {, s- }: v
  152.     u8 tem = 0;
    , a; r5 w. O" m! {: b
  153. #if (UART2_DMA == 1)                        //如果使能了 UART2_DMA 则使用串口空闲中断和 DMA功能接收数据
    % D! l# A) a* b. a* k. `
  154.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )                                                                                   //空闲中断  一帧数据发送完成
    * S6 H$ ?, \$ `) j+ {& t: ~
  155.     {
    ! K, n( v7 y8 M9 x
  156.         USART_ReceiveData( USART2 );                                                                                                                                                                                 //读取数据注意:这句必须要,否则不能够清除空闲中断标志位。
    6 M3 o/ [5 k2 V4 j
  157.         DMA_Cmd( DMA1_Channel6, DISABLE );                                                                                                                                                         //关闭 DMA 防止后续数据干扰( ]2 j( P# [; r2 J; z( @
  158.         uart2_rec_cnt = DMA_REC_LEN - DMA_GetCurrDataCounter( DMA1_Channel6 );  //DMA接收缓冲区数据长度减去当前 DMA传输通道中剩余单元数量就是已经接收到的数据数量4 Y' k7 E. L$ Z3 V
  159.         //uart2_Send( dma_rec_buff, uart2_rec_cnt );                                                                                                                //发送接收到的数据6 G2 f, D( _* g4 r
  160.                           copy_data( dma_rec_buff, uart2_rec_cnt );                                                                                                                    //备份数据  \$ h' v3 f8 }6 u( G. d
  161.         receiveOK_flag = 1;                                                                                                                                                                                                                        //置位数据接收完成标志位) ^( |/ V3 U- u8 s' f3 G8 k8 v
  162.         USART_ClearITPendingBit( USART2, USART_IT_IDLE );                                                                                                 //清除空闲中断标志位
    ) Q, g$ z% i6 b9 N: {
  163.         myDMA_Enable( DMA1_Channel6 );                                                                                                                                                                         //重新恢复 DMA等待下一次接收% g# ?: ?: w% y) A6 L1 z/ i; l9 E
  164.     }7 `) m; F6 {; H& }
  165. #else                                                                                //如果未使能 UART2_DMA 则通过常规方式接收数据  每接收到一个字节就会进入一次中断1 _' H$ `7 c; g+ n# ]. B6 V
  166.     if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )                                                 //接收中断
    ' `1 z; V" }0 O% l* w
  167.     {
    ( i3 r7 M% s) G7 }0 }- O' {1 m
  168.         tem = USART_ReceiveData( USART2 );# ?9 b0 E" R( |0 n# m+ ]' }! X
  169.         USART_SendData( USART2, tem );
    " P$ Q6 V/ ]) ?8 D
  170.     }* b* _8 Z/ V7 y0 W. ?* C
  171. #endif
    & k: I" J8 R) Y; O0 r- `+ h9 |6 ^
  172. }
复制代码
& J& b0 p7 W% |% l" v1 J8 J
modbus.h
" W( b+ w$ o4 |, g* o0 ?; h# v, N6 Q) i. F0 l' W, w7 M2 g
  1. #ifndef __MODBUS_H
    : {3 F- _# y! W- R. ^6 C
  2. #define __MODBUS_H% M9 d+ e5 w3 P
  3. #include "sys.h"; U# Z' \7 ?5 j
  4. 4 o, j6 A" l" w
  5. - x2 b( M8 ]& N' L; [+ i* d
  6. #define SlaveID  0x01     //从机地址: ?- y) A7 R* h, s! R  p

  7. + v0 k) [( d* t$ y9 O$ X. _# s
  8. void DisposeReceive( void );
    % l4 ]: T& W9 ~3 v  `8 g
  9. void Modbus_03_Slave(void);0 e9 x3 A# x) s( |" D: I
  10. void Modbus_06_Slave(void);
    . m( |1 @; x7 p% w/ _, S
  11. void Modbus_16_Slave(void);
    5 Y& B- h" ^  y% ~
  12. #endif
复制代码

) L) s% @% T4 B' @8 h, T  k; w5 D- Amodbus.c' L( n& I7 s+ x' K+ V# e
' @& ]4 U" y- M  B
  1. #include "modbus.h"" u, r) b/ f- ], }$ g* t
  2. #include "crc16.h"' b" x9 H8 E# i  h+ J$ Z6 r/ R
  3. #include "uart2.h"$ X4 C$ F  {; O% I$ R* _$ K

  4. 3 T' {8 ?* \' W7 E3 H% i# G
  5. #define HoldRegStartAddr    0x0000                                                                                                                                                                                //保持寄存器起始地址
    , @  B4 y' K) u# T1 a9 h, @" _7 y
  6. #define HoldRegCount        8                                                                                                                                                                                                        //保持寄存器数量8 }+ ^: S3 q2 N. ^% N% E8 A! ~
  7. #define HoldMaxValue        1000                                                                                                                                                    //寄存器最大值# w* z( B+ B5 a8 I: B7 g
  8. ; O/ g/ @: ?, W
  9. extern u8 data_backup[DMA_REC_LEN];                                                                                                                                                                   //modbus接收到数据备份/ O- D8 q% ]2 b- e( a8 J$ A
  10. extern u16 dataLen_backup;                                                                                                                                                                                                                //modbus接收数据长度备份: H0 A0 P+ y) @  c2 O9 o
  11. . z- Z( A! j5 p" Q+ W' H; y
  12. 0 Q+ G  |3 U7 O, G8 i- s8 z& f
  13. u8 SendBuf[DMA_REC_LEN] = {0};/ g( e% ^- y- T$ E. G0 n
  14. u16 StartRegAddr = HoldRegStartAddr;
    . j; \/ X3 [$ [6 J
  15. u8 err = 0;                                                                                                        //错误代码
    # B# U" g4 P$ e9 `* h4 a) o

  16. * {8 O9 T( _) _! V
  17. u8 HoldReg[HoldRegCount*2] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8}; //存储8路数据值
    4 B( [: I( h" `

  18. 9 v7 [2 Y) R( M0 X( h
  19. 7 a  [/ V2 ]' S
  20. /*
    5 D9 [! I4 E6 G8 g# F
  21. 错误代码说明:
    5 h# s- H$ y+ n
  22. 0x01  不是支持的功能码% ]) \$ p* q! S3 m
  23. 0x02  起始地址不在规定范围内- O; m: G. E, t4 Y5 [! n3 q+ `  R
  24. 0x03  寄存器数量不在规定范围内2 s1 t6 ?9 o. R5 p# _7 N3 H; |
  25. 0x04  数据校验错误4 f7 B3 f0 }: e
  26. */6 X. V% E, X4 D$ `
  27. //处理接收到的数据
    + B$ k4 f2 |! T, }4 o( M
  28. // 接收: [地址][功能码][起始地址高][起始地址低][总寄存器数高][总寄存器数低][CRC低][CRC高]  |: J1 Q5 G6 z4 B% q7 O. i0 R
  29. void DisposeReceive( void )/ H8 n+ _6 O8 A- x
  30. {: r1 ^0 W/ @9 i6 Y
  31.     u16 CRC16 = 0, CRC16Temp = 0;, [4 s' P" K- s0 z3 a
  32.     if( data_backup[0] == SlaveID )                                                                         //地址等于本机地址 地址范围:1 - 32
    . d! }5 {+ f8 Z
  33.     {
    " V% G5 t; D4 |
  34.         CRC16 = App_Tab_Get_CRC16( data_backup, dataLen_backup - 2 );                                  //CRC校验 低字节在前 高字节在后 高字节为报文最后一个字节
    $ f# i, i( C" c
  35.         CRC16Temp = ( ( u16 )( data_backup[dataLen_backup - 1] << 8 ) | data_backup[dataLen_backup - 2] );5 e( q, p' c0 P5 c" k
  36.         if( CRC16 != CRC16Temp )
    - Y" h' s0 `' r2 k
  37.         {
    & T4 [+ a% X: H0 R; K" R- F7 r
  38.             err = 4;                                                                                               //CRC校验错误
    ! g) A0 ?4 A; U/ I( d6 H* l
  39.         }: q# i* _! m' ~; E6 e8 |& q7 a
  40.         StartRegAddr = ( u16 )( data_backup[2] << 8 ) | data_backup[3];
    6 u# Z* B& c: T
  41.         if( StartRegAddr > (HoldRegStartAddr + HoldRegCount - 1) )
    2 \! U4 G; _, L3 h0 Q% O$ z# A
  42.         {, z0 a  v7 E  n, T. C, s
  43.             err = 2;                                                                                               //起始地址不在规定范围内 00 - 07    1 - 8号通道8 o4 Q1 I' O. ^0 {! A& ]3 F
  44.         }
    0 ]7 {. D3 {4 S9 Y2 J/ b+ A0 B! q
  45.         if( err == 0 )! Y- c( a, b, F8 X8 X
  46.         {. k% m& R' L, S6 ~% X& @+ K( l
  47.             switch( data_backup[1] )                                                                        //功能码  s; y, Z2 g& s2 n) c% Z
  48.             {
    0 u8 Y( a% I' X8 I9 z
  49.                 case 3:                                                                                            //读多个寄存器+ x  ~0 N. Y8 e) v( E" ~: c
  50.                 {
    6 @# B7 C8 D" J4 h; x! u5 ^
  51.                     Modbus_03_Slave();( Z5 I2 y# D' @; e6 n6 L, f8 N- z
  52.                     break;
    % N0 v/ \; @( H  J
  53.                 }
    # W5 ~: c( F" F. X$ W- y& \, b8 H
  54.                 case 6:                                                                                            //写单个寄存器2 S; C- w5 \. v8 x: R
  55.                 {' {. P0 d0 c6 k0 Q/ O1 `* v) B, H+ ^
  56.                     Modbus_06_Slave();0 Z( \2 S* h+ e# d
  57.                     break;: C3 v# r4 a- }! [% c
  58.                 }
    + w6 C4 }% F9 G0 C& P
  59.                 case 16:                                                                                           //写多个寄存器
    , Q/ w  g( N; s- C
  60.                 {
      ?+ b2 C! s% W! P
  61.                     Modbus_16_Slave();
    ' r0 m$ [7 C, z  ^0 C1 a
  62.                     break;( k: d& ^0 ^' v# c4 `  s' y8 i
  63.                 }
    3 W5 m8 B: S1 O' \* {
  64.                 default:7 N9 {: P% ]- i& D5 D- U+ Z/ }
  65.                 {  t+ t; [8 U  ]" w6 A
  66.                     err = 1;                                                                                       //不支持该功能码9 L6 v4 a5 U7 j/ t% R
  67.                     break;
    ' T8 Z. O" b4 s1 @0 r
  68.                 }" F, O! {/ K- W& b# {" X* L6 d
  69.             }
    % Y1 X0 S% u: @  }
  70.         }, c' ~$ F1 x6 n
  71.         if( err > 0 )* X9 M" l. x& }8 ]' t2 L$ W6 C
  72.         {: E* `% I' p. E* E
  73.             SendBuf[0] = data_backup[0];
    ) ^5 E" _% S% s2 {9 Q: S
  74.             SendBuf[1] = data_backup[1] | 0x80;' Q/ _0 o6 H4 D7 ?$ A$ B
  75.             SendBuf[2] = err;                                                                                      //发送错误代码- r7 z+ N* ~& P! i8 K6 b, f
  76.             CRC16Temp = App_Tab_Get_CRC16( SendBuf, 3 );                                                           //计算CRC校验值
      y0 ?* J2 S* |5 e/ o
  77.             SendBuf[3] = CRC16Temp & 0xFF;                                                                         //CRC低位
    : O( o- a3 s: q' ]# j2 U% k
  78.             SendBuf[4] = ( CRC16Temp >> 8 );                                                                       //CRC高位+ R4 y+ t+ j" U% ^
  79.             uart2_Send( SendBuf, 5 );                                       
    : P6 h8 }3 P$ Z- y% h
  80.             err = 0;                                                                                               //发送完数据后清除错误标志- H. }# P  _7 W2 i5 A; d( z
  81.         }! F( f0 Z. ]+ E# o" d& Z! E8 R. ~5 M
  82.     }7 y" n9 Y1 [" I: J2 \# D
  83. }/ @8 `% T9 D) N

  84. 2 c8 G6 J- G: U, t  m* E0 a; ?7 k
  85. /*
    2 ~! z' R1 K! a8 V2 M$ M. Y
  86. 函数功能:读保持寄存器  03
    % f. ?- x. n' v$ n' j
  87. 主站请求报文:      0x01 0x03   0x0000  0x0001  0x840A     读从0开始的1个保持寄存器
    7 [- w- M# w! `$ Q" k2 s
  88. 从站正常响应报文:  0x01 0x03   0x02    0x09C4  0xBF87     读到的2字节数据为 0x09C42 k) l* X. X( w+ G
  89. */
    ( R3 z2 e6 ^) w# i" d9 g; g
  90. void Modbus_03_Slave( void )! }- Q3 n! h3 u. G2 V0 z
  91. {
    0 A/ m* O, V: f0 a  j
  92.     u16 RegNum = 0;
    " F2 p; b) {: _- [
  93.     u16 CRC16Temp = 0;
    ! }" [6 s4 x+ F5 z8 I0 V! G
  94.     u8 i = 0;
    # I5 o  g8 K& c+ W( d
  95.     RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量  ?- e# b( V3 I' X
  96.     if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )      //寄存器地址+寄存器数量 在规定范围内 <=8/ c5 \/ |2 G  T  P
  97.     {% y2 x# f% W" |0 y) ]
  98.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址        9 I9 n; {$ G$ h3 G* B
  99.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
    1 M' h! h$ L2 Q9 K6 H6 A( a
  100.         SendBuf[2] = RegNum * 2;                                                                                                                                                                                        //返回字节数量
      h% T8 h9 `) [' o# H7 b8 W0 i
  101.         for( i = 0; i < RegNum; i++ )                                                                             //循环读取保持寄存器内的值
    + n: ?! r3 q# S- K4 O- L4 |
  102.         {% m5 K: _; J* ^, \3 U
  103.             SendBuf[3 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2];
    . [* U" b( h$ P4 M7 D0 T  `0 B
  104.             SendBuf[4 + i * 2] = HoldReg[StartRegAddr * 2 + i * 2 + 1];
    8 J; [3 [' @8 F
  105.         }8 D0 G. E5 Y2 U3 ^8 t
  106.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, RegNum * 2 + 3 );                                                  //获取CRC校验值) c/ X* n0 O+ m
  107.         SendBuf[RegNum * 2 + 3] = CRC16Temp & 0xFF;                                                                //CRC低位
      m: i0 G8 A7 C- x+ F/ k. O/ K" H
  108.         SendBuf[RegNum * 2 + 4] = ( CRC16Temp >> 8 );                                                              //CRC高位; Q4 c5 W3 k+ ]1 r* o
  109.         uart2_Send( SendBuf, RegNum * 2 + 5 );
    ) m: B- l9 M, N; X# s$ ~
  110.     }1 b; ?" u" k4 q! S1 f3 Y
  111.     else
    , \6 |- Q9 h8 X0 X& t! u1 g
  112.     {
    . n: e- B( h# {/ }3 P% O; s" m2 y) B
  113.         err = 3;                                                                                                   //寄存器数量不在规定范围内
    , ?! J7 P- {/ ^+ [2 g
  114.     }5 q! @3 b5 @# c" e
  115. }: m; e  m/ F5 X5 \8 K: m8 O
  116. /*3 t" ~2 P9 P* g
  117. 函数功能:写单个保持寄存器 06
    # b' a0 `1 p( \5 F* O# {. o! x
  118. 主站请求报文:      0x01 0x06    0x0000  0x1388    0x849C   写0号寄存器的值为0x1388# f) l7 s& i( u9 c* f
  119. 从站正常响应报文:  0x01 0x06    0x0000  0x1388    0x849C    0号寄存器的值为0x13883 [5 S6 Q: F. g2 l
  120. */
    , n3 c6 G8 }' e& u- W1 d
  121. void Modbus_06_Slave( void )
    * I* ]# ?0 q, ]: S+ j
  122. {
    * }0 f( v4 {$ B/ w) B
  123.     u16  RegValue = 0;, H, \& ~5 W* \4 _
  124.     u16 CRC16Temp = 0;6 ~5 q/ F* D) h5 U, Z& ]0 d
  125.     RegValue = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                              //获取寄存器值
    9 d6 t( Z: q- l& l* ^
  126.     if( RegValue <= HoldMaxValue )                                                  //寄存器值不超过1000+ O+ F' g. P! B9 x7 [' Y
  127.     {
    7 z8 l# d; P1 S2 A; {8 a4 m
  128.         HoldReg[StartRegAddr * 2] = data_backup[4];                                                         //存储寄存器值
    8 }2 [5 ]( h/ \. H7 n! G9 ?
  129.         HoldReg[StartRegAddr * 2 + 1] = data_backup[5];
    ( r2 u9 O' ]  g0 @8 ]6 }$ B: m
  130.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //地址& u! p$ ^! m) q3 M
  131.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码/ E' J8 T# w7 N
  132.         SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位6 T2 v- |- r, v6 P
  133.         SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
    ' r# h+ n! h1 G6 `1 k
  134.         SendBuf[4] = data_backup[4];                                                                                                                                                                        //值高位4 o4 H5 J( x1 H7 |7 S
  135.         SendBuf[5] = data_backup[5];                                                                                                                                                                        //值低位$ H" r5 }6 ?9 E% W. V6 d1 Q
  136.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                              //获取CRC校验值" s) N% A/ ^1 {4 T4 x
  137.         SendBuf[6] = CRC16Temp & 0xFF;                                                                            //CRC低位5 r, D3 r& m, R, }) K7 Q. V7 w4 C
  138.         SendBuf[7] = ( CRC16Temp >> 8 );                                                                          //CRC高位1 j, C) j. C$ R* J  Y- N' c8 T
  139.         uart2_Send( SendBuf, 8 );  q# a- ]0 w; B0 \
  140.     }9 Z) h! K6 V! [/ u9 m- H
  141.     else5 h8 j+ R5 z6 A, q+ v2 T8 ?, U6 R
  142.     {
    ! \( X; I# L- i; m
  143.         err =  3;                                                                                                  //寄存器数值不在规定范围内! T7 Q$ i( a" |5 Q- D7 y$ @
  144.     }
    ; q- K) }8 B* h3 c* \
  145. }
    3 M. J$ E  z# j. B/ T7 k
  146. /*" u9 l8 Y1 `$ I! J* H
  147. 函数功能:写多个连续保持寄存器值 16; p. Z/ J; N5 m3 i. z
  148. 主站请求报文:       0x01 0x10    0x7540  0x0002  0x04  0x0000 0x2710    0xB731  写从0x7540地址开始的2个保持寄存器值 共4字节
    0 G4 {9 l7 ~+ H  x
  149. 从站正常响应报文:   0x01 0x10    0x7540  0x0002  0x5A10                         写从0x7540地址开始的2个保持寄存器值
    6 p. P$ A- r( Q7 Q8 s1 S
  150. */
    5 A5 j+ J- w2 {' d1 F
  151. void Modbus_16_Slave( void )
    : ?- M2 x. O, T! h4 N4 X# A/ j
  152. {& w1 L2 ~$ n, o* Z7 G2 F" D8 L3 b) ?
  153.     u16 RegNum = 0;
    0 N5 y& R: [& Y- A0 h: _1 m
  154.     u16 CRC16Temp = 0;
    / |$ ~- D6 `- f0 N
  155.     u8 i = 0;
    ( g( m9 H( e, ?9 P5 ]
  156.     RegNum = ( u16 )( data_backup[4] << 8 ) | data_backup[5];                                                //获取寄存器数量
    ! t$ ~1 |& N! ]0 V" ~# B
  157.     if( ( StartRegAddr + RegNum ) <= (HoldRegStartAddr + HoldRegCount) )             //寄存器地址+寄存器数量 在规定范围内 <=8
    ) G  ^2 ^% q' r  C& q8 Z% V
  158.     {
    3 q! ~. w( [* `% S4 v9 J
  159.         for( i = 0; i < RegNum; i++ )                                                                              //存储寄存器设置值/ h8 V) y9 V  Y7 ~2 L1 C
  160.         {
    / G: G* l8 }- g$ s" z; E$ @
  161.             HoldReg[StartRegAddr * 2 + i * 2] = data_backup[i * 2 + 7];
    7 r" R: N+ u! N2 l& U! b
  162.             HoldReg[StartRegAddr * 2 + 1 + i * 2] = data_backup[i * 2 + 8];
    4 y( B3 A0 f* B6 y; ^4 ^
  163.         }
    % ^& t  g7 J' B9 a8 z
  164.         SendBuf[0] = data_backup[0];                                                                                                                                                                        //起始地址, Z6 p  k. K6 A$ Z  b4 E
  165.         SendBuf[1] = data_backup[1];                                                                                                                                                                        //功能码
    3 i  t# L0 a8 e; w
  166.         SendBuf[2] = data_backup[2];                                                                                                                                                                        //地址高位
    2 V4 ?6 j. r7 l; g0 T
  167.         SendBuf[3] = data_backup[3];                                                                                                                                                                        //地址低位
    . M' v( I5 u- s0 ?
  168.         SendBuf[4] = data_backup[4];                                                                                                                                                                        //寄存器数量高位! m, F5 B* s3 U+ h9 X, s
  169.         SendBuf[5] = data_backup[5];                                                                                                                                                                        //寄存器数量低位; d: B/ k$ Y  M0 [. N
  170.         CRC16Temp = App_Tab_Get_CRC16( SendBuf, 6 );                                                        //获取CRC校验值3 n1 k: q! h6 e+ N3 x* z" E( i  l
  171.         SendBuf[6] = CRC16Temp & 0xFF;                                                                      //CRC低位
    ) `/ ~" z* d0 x; P- o; c
  172.         SendBuf[7] = ( CRC16Temp >> 8 );                                                                    //CRC高位
    ) b+ Q; K! H% I1 N- ~0 {, |; u
  173.         uart2_Send( SendBuf, 8 );: F3 q1 P3 W: p) ]4 ], B
  174.     }; w0 c5 v# d' J8 c' k
  175.     else8 `1 l  K% }/ K
  176.     {
    / D( W! \  |4 ?( x( G! z, t+ D; Z( Y
  177.         err = 3;                                                                                                   //寄存器数量不在规定范围内/ O9 }) z" B2 F& a  Q
  178.     }) \  q6 n7 w6 v" F/ h% d6 A! E
  179. }
复制代码
. B4 J2 O5 l) j
测试效果. U) a; ]3 u9 d% x5 G1 m! k: |
5 U3 _% e( c3 m1 V
测试功能码 0x03  读一个或者多个保持寄存器值) D" K' Q8 x' V% N6 [5 g) r

- w- E. W- t: P7 z. k# \( J! Q! Y G[DIAGIW8)$K9KXN[LUJ8W8.png ; n4 d  M. r2 \2 {

' I! B; Y8 g5 P& [$ y, H- b" G测试功能码 0x06 写单个保持寄存值. n, Q9 {+ p: G" }1 Q: z) J2 M

+ |6 {% }% N: g- z 5R6${[O14XV}RWG2%ELMRN2.png
3 |* B3 U" I- A. v, l0 m& |) E1 z
测试功能码 0x10 写多个保持寄存器值- I( l& H. y2 A4 h  I6 T: r% ?3 d
& Q+ w6 a) F) k! P9 y* X* r
WJLI9948FBP1D%XE**{%QP.png , r, n& B! j+ |( v/ |
7 c$ c) c; |- w$ P
再次读取所有寄存器值
8 W! J" w" j: G# A3 d9 y1 w! I+ A, D9 @3 u" O
$%K_$S@0%F]LU5H~YQ$T6$N.png
( M4 d, v8 G" P3 T) K& Z" @( Q& P& M9 Z: f3 d2 f# l2 C
说明寄存器值修改成功。$ q% t: B1 w4 y, ~+ Q. p
% A  m& ?3 j! {" }5 D# f

; B8 t5 ?4 {% v4 F' M9 b- n3 S; P- Y
赞 收藏 1 评论0 发布时间:2022-3-20 21:44

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版