liming3267 发表于 2025-7-3 19:22:24

ADC_DMA采集影响Modbus RTU通讯

大家好,最近在做Modbus RTU通讯的时候遇到一个奇怪的问题,就是串口数据会受到ADC_DMA处理的影响。

Modbus.h
//MODBUS全局头文件
#ifndef _MODBUS_H
#define _MODBUS_H      
#include "config.h"
//====================================================GLOBAL=全局定义===以下所有都是公用定义============================================================================================
//--------------------------MODBUS全局标志宏定义-----------------------------------------------------------------------------------------------------------------------------------------
#define Baudrate4   9600                                                                                                                                                                                //uart4波特率定义
//------------------------定义上位机需要的各种数据类型地址范围---------------------------------------------------------------------------------------------------------------------------
/*************************************************************************************************************************************************************************************
线圈寄存器:00001-09999       1字节
输入寄存器:10001-19999       1字节
保持寄存器:40001-49999
u16: 40001-41000            2字节      共1000个数据
u32: 41001-42000            4字节      共500个数据
f32: 42001-43000            4字节      共1249个数据
****************************************************************************************************************************************************************************/
//--------------定义上位机需要的各种数据类型范围-对应标准Modbus地址保持型寄存器4区(40001-49999)-----------------------------------------------------------------------------
#define limit_int_lower      0                                                                                                                                                                   //定义整型变量地址下限
#define limit_int_upper      999                                                                                                                                                                   //定义整型变量地址上限
#define limit_long_lower    1000                                                                                                                                                                   //定义长整型变量地址下限
#define limit_long_upper    1998                                                                                                                                                                   //定义长整型变量地址上限
#define limit_float_lower   2000                                                                                                                                                                   //定义浮点型变量地址下限
#define limit_float_upper   2998                                                                                                                                                                   //定义浮点型变量地址上限
#define limit_DB_lower      3000                                                                                                                                                                   //定义混合区变量地址下限
#define limit_DB_upper      3998                                                                                                                                                                   //定义混合区变量地址上限

//修改以下参数值要注意发送缓冲区send[]的大小,例如整型数据num_int=200,则缓冲区send[]至少要定义为400
#define num_int 250                                                                                                                                                          //整型变量数量上限 1249      250字
#define num_long 50                                                                                                                                                              //长整型变量数量上限 624       双字   注意如果数组定义数量少于使用的数量会造成未知错误
#define num_float 150                                                                                                                                                              //浮点型变量上限 1249          浮点
//定义1区开关量输入信号对应的avr中字节数组的大小(1个字节对应8个开关量输入状态)
#define num_DI 20                                                                                                                                                         //1区变量数量上限 20                字节
//定义0区开关量输入信号对应的avr中字节数组的大小(1个字节对应8个开关量线圈状态)
#define num_DO 20                                                                                                                                                                 //0区变量数量上限 20                字节
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#define num_0x 20                                                                                                                                                           //定义0区数据库上限
#define num_1x 20                                                                                                                                                       //定义1区数据库上限                        
#define num_4x 40                                                                                                                                                       //定义4区数据库上限
#define num_Saddr 10                                                                                                                                                    //定义地址寄存器中有8个元素   
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//#define code_key_off 11
//#define code_key_on22
//-------------------------MODBUS对应地址在AVR中定义的数组---------------------------------------------------------------------------------------------------------------------------------------
extern u8 xdata DI_input;                                                                                                                                                       //读离散输入状态--寄存器地址10001-19999--位操作--单个位或多个位
extern u8 xdata DO_output;                                                                                                                                                                 //读/写线圈状态--寄存器地址00001-09999--位操作--单个位或多个位
//通过读功能码“03”读单个或多个寄存器,通过写功能码"06“写单个,”10”写多个,对应地址40001-49999进行操作
extern u16 xdata data_int;                                                                                                                                          //整型int:40001-40999         2字节      共1000个数据
extern u32 xdata data_long;                                                                                                                                          //长整型long: 41000-41999         4字节      共500个数据
extern f32 xdata data_float;                                                                                                                                       //浮点型float:42000-42999         4字节      共500个数据
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------MODBUS对应地址在AVR中定义的数组------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------存放CRC输出的高低值---------------------------------------------------------------------------------------------------------------
extern u8 xdata MODBUS_CRC_data_Lo;                                                                                                                                     //crc16 校验,CRC高8位和低8位定义
extern u8 xdata MODBUS_CRC_data_Hi;                                                                                                                                     //crc16 校验,CRC高8位和低8位定义
//-------------------------------------------------------为了数据解析存放联合体变量数据-----------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------定义modbus结构体---------------------------------------------------------------------------------------------------------------                      --
typedef struct
{
    u16   Myadd;                                                                                                                    //本设备的地址
        u16   Baudrate;                                                                                                                                                                                                //波特率               
        u16          Timer_Reload;                                                                                                                                                                                        //定时器重装值(用于生成波特率)
       
        u16   Timeout;                                                                                                                //MODbus的数据断续时间                      
    u8    Recount;                                                                                                                //MODbus端口已经收到的数据个数
    u8    Timerun;                                                                                                                //MODbus定时器是否计时的标志
    u8    Reflag;                                                                                                                   //收到一帧数据的标志
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------      
    u16Saddr;                                                                                                                   //用于存放地址寄存器首地址;
    u16   num_data;                                                                                                                     //读写数据个数 注意:这里由于要调用指针所以要把数据类型设置统一改为u16
    u16   num_byte;                                                                                                                     //用于存放寄存器的字节总数 注意:这里由于要调用指针所以要把数据类型设置统一改为u16
    u8   addr_data;                                                                                                                     //用来记录发送的数据地址变量
   
        u8   mindex;                                                                                                                           //modbus通讯,用来存放寄存器数组序号(索引读写数组的序号)
    u8   mbit;                                                                                                                      //modbus通讯,从来存放寄存器位标号   (索引读写字节的某一位)
    u8   mvalue;                                                                                                                       //modbus通讯,用来存放某一位变量值    (1或0)
    s16index_STR;                                                                                                                 //开始地址索引
    s16index_END;                                                                                                                 //结束地址索引      
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        u8        flag_DIR;                                                                                                                                                                                  //串口B 485方向寄存器设置为接收
        u16inc_usart;                                                                                                                                                                                 //串口B延时计时
        u8   modbus_1ms;                                                                                                                                                                                       //1ms标志
        u8   TxSendFlag;                                                                                                                                                                                        //串口4写完成标志
        u8       DmaTxFlag;                                                                                                                                                                                                //DMA串口写标志
        u8       DmaRxFlag;                                                                                                                                                                                                //DMA串口读标志
        u16RX_TimeOut;                                                                                                                                                                                        //串口读取超时标志
        u16RX_Cnt;                                                                                                                                                                                            //接收计数
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
        u16 flag_remote;                                                                                                                                                                                        //远程通讯标志
        u32 inc_remote;                                                                                                                                                                                                //记录远程通讯计时
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    u8Sendbuf;                                                                                                            //MODbus发送缓冲区
        u8Rcbuf;                                                                                                                       //MODBUS接收缓冲区
}MODBUS_RTU;

extern MODBUS_RTU xdata MODBUS_STR_modbus4;                                                                                                                     //声明一个MODBUS实例 4                                                                     
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//extern void CRC16(u8 dat[],u8 lenth);                                                                                                                                       //计算法CRC16校验函数 dat[]为接收校验数组的首地址,lenth进行校验数组的长度
extern void MODBUS_CRC16(u8 *Pushdata, u8 length);                                                                                                                  //查表法CRC16校验函数 *Pushdata 为接收校验数据/数组的首地址,lenth进行校验数据/数组的长度

//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
extern void MODBUS_Set_Array_Bit(u8 Array[],u8 Index,u8 Bit,u8 Value);                                                                                                                   //写入数组某元素的第几位的位状态
extern u8 MODBUS_Get_Array_Bit(u8 Array[],u8 Index,u8 Bit);                                                                                                                     //获得数组某元素的第几位的位状态
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------数据解析的转换函数------------------------------------------------------------------------------------------------------------------------------------                                                                                                                                               
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
extern void Modbus_fun3(MODBUS_RTU *MODBUS_STR_modbus,void (*UART_DMA_Transmit)(u8*,u16));                                                                                //Modbus进行通讯的03功能码

#endif
这里定义了一个结构体来存放Modbus所有通讯数据,这样做是为了以后方便移植。

Modbus.c

//MODBUS全局变量
#include "..\..\comm\STC32G.h"
#include "stdio.h"
#include "intrins.h"
#include <string.h>
#include "MODBUS.H"                                                          //串口通讯全局头文件                                                       //
#include "config.h"
#include "method.h"
#include "IO.h"
//================================================MODBUS全局变量====================================================================================================================================
//-----------------------------------------------为了数据解析存放联合体变量数据----------------------------------------------------------------------------------------------------------------------
u8 xdata MODBUS_CRC_data_Lo;                                                                      //存储CRC16校验校验值的低8位数据
u8 xdata MODBUS_CRC_data_Hi;                                                                      //存储CRC16校验校验值的高8位数据
u16 flag_modbus=0;       
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
u8 xdata DI_input={0};                                             //存储离散输入状态--寄存器地址10001-19999--位操作--单个位或多个位
u8 xdata DO_output={0};                                               //存储读/写线圈状态--寄存器地址00001-09999--位操作--单个位或多个位
//------------------------------通过读功能码“03”读单个或多个寄存器,通过写功能码"06“写单个,”10”写多个,对应地址40001-49999进行操作-------------------------------------------------------------------
u16 xdata data_int;                                               //存储整型 int:40001-40999             2字节      共1000个数据
u32 xdata data_long;                                             //存储长整型 long: 41000-41999         4字节      共500个数据
f32 xdata data_float;                                              //存储浮点型 float:42000-42999         4字节      共500个数据
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//u8 MODBUS_DB_0X={0};                                                        //定义0区数据库
//u8 MODBUS_DB_1X={0};                                           //定义1区数据库
//u8 xdata MODBUS_DB_4X={0};                                   //定义4区数据库
//DB xdata MODBUS_DB1;                                          //定义数据区

MODBUS_RTU xdata MODBUS_STR_modbus4;                                 //声明一个MODBUS实例 4
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
u16 xdata MODBUS_Saddr={0,2,4,8,12,14,18,22};                //定义地址寄存器
//==================================================================================================================================================================================================
//---------------------------查表法计算CRC16的数据表高字节--------------------------------------------------------------------------------------------------------------------------------------------
const u8auchCRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
//------------------------------查表法计算CRC16的数据表低字节---------------------------------------------------------------------------------------------------------------------------------------
const u8auchCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
    0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
    0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
    0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
    0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
    0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
    0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
    0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
    0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
    0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
    0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
    0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
    0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
/***************************************************************************************************************************************************************************************************
函数名称: void MODBUS_CRC16(u8 *Pushdata, u8 length)
功能描述:计算中*Pushdata数据/数组所有元素的CRC16结果 (采用查表法)
参数说明: *Pushdata参与计算CRC的数组,length数组的长度
返回说明: u8 CRC_data_Lo CRC数据的低位,u8 CRC_data_Hi CRC数据的高位位
特殊说明:
备    注:
修改时间:2020-12-14
***************************************************************************************************************************************************************************************************/
void MODBUS_CRC16(u8 *Pushdata, u8 length)
{
    u8 uchCRCHi = 0xFF;
    u8 uchCRCLo = 0xFF;
    u8 uIndex;
    //unsigned int CRC=0xFFFF;

    while (length--)
    {
uIndex = uchCRCHi^*Pushdata++;
      uchCRCHi = uchCRCLo^auchCRCHi;
      uchCRCLo = auchCRCLo;
    }
    //CRC=(uchCRCHi << 8 | uchCRCLo);
    MODBUS_CRC_data_Lo=uchCRCLo;
    MODBUS_CRC_data_Hi=uchCRCHi;
}
/***************************************************************************************************************************************************************************************************
函数名称: vMODBUS_RTU *MODBUS_STR_modbusoid Modbus_fun3(MODBUS_RTU *MODBUS_STR_modbus,void (*UART_DMA_Transmit)(u8*,u16),u8 *pData, u16 Size)
功能描述:Modbus进行通讯的03功能码
参数说明: MODBUS_RTU *MODBUS_STR_modbus结构体,UART_DMA_Transmit串口发送函数
返回说明:
特殊说明:
备    注:
修改时间:2024-07-23
***************************************************************************************************************************************************************************************************/
void Modbus_fun3(MODBUS_RTU *MODBUS_STR_modbus,void (*UART_DMA_Transmit)(u8*,u16))                                                                                                          //读继电器输出的当前状 态功能码:“03”;
{
    u8 i=0,j=0;   
       
    MODBUS_STR_modbus->Saddr=MODBUS_STR_modbus->Rcbuf*256+MODBUS_STR_modbus->Rcbuf;                                                                                                  //计算出要读取的4X数据寄存器首地址(4X寄存器的地址是Modbus地址的2倍关系);
    MODBUS_STR_modbus->Sendbuf=MODBUS_STR_modbus->Myadd;                                                                                                                                                     //站号
       
        MODBUS_STR_modbus->Sendbuf=3;                                                                                                                                                                                         //功能码
    MODBUS_STR_modbus->addr_data=3;                                                                                                                                                                                              //返回的数据从第三个数组位开始放数据
//----------------------------------------读取整型变量数据-40001-40999------------------------------------------------------------------------------------------------------------------------------
    if(MODBUS_STR_modbus->Saddr>=limit_int_lower&&MODBUS_STR_modbus->Saddr<=limit_int_upper)                                                                                          //判断读取的数据是否在整型地址范围内在MODBUSB.H 下限:40001上限:42499
    {
      MODBUS_STR_modbus->num_data=MODBUS_STR_modbus->Rcbuf*256+MODBUS_STR_modbus->Rcbuf;                                                                                 //读取寄存数个数(数据存储在接收数组usart_rx_bufB中)
      MODBUS_STR_modbus->num_byte=2*MODBUS_STR_modbus->num_data;                                                                                                              //返回寄存器字节数数量(1个整型数据有2个字节)
      MODBUS_STR_modbus->Sendbuf=MODBUS_STR_modbus->num_byte;                                                                                                              //返回字节总数
      for(i=0;i<MODBUS_STR_modbus->num_data;i++)                                                                                                     //读取指定的整型数据到发送寄存器sendB[]中
      {
            trans_int_byte(data_int);                                                                                   //读取整型寄存中的数据转换成2字节数组(读取的首地址-下限+偏移量)
                                                                                                                                                                                                                                                                                //trans_int_byte()是把整型变量数组data_int[]转换成二字节数组ui2_byte
            for(j=0;j<2;j++)
            {
                MODBUS_STR_modbus->Sendbuf=ui2_byte;                                                                                              //把整型数据转换过来的字节数组赋给发送寄存器
                MODBUS_STR_modbus->addr_data++;                                                                                                   //数据存储偏移量+1
            }
      }
    }
//------------------------------读取长整型变量数据--41000-41999--------------------------------------------------------------------------------------------------------------------------         
    else if(MODBUS_STR_modbus->Saddr>=limit_long_lower&&MODBUS_STR_modbus->Saddr<=limit_long_upper)                                                                 //判断读取的数据是否在长整型地址范围内在MODBUSB.H 下限:41000上限:41999
    {
      MODBUS_STR_modbus->num_data=(MODBUS_STR_modbus->Rcbuf*256+MODBUS_STR_modbus->Rcbuf)/2;                                                                         //读取寄存数个数 (“/2”modbus发送长整型数据占2个字 )
      MODBUS_STR_modbus->num_byte=4*MODBUS_STR_modbus->num_data;                                                                                                                      //返回寄存字节数数量(1个长整型数据有4个字节),
      MODBUS_STR_modbus->Sendbuf=MODBUS_STR_modbus->num_byte;                                                                                                                         //返回字节总数
      for(i=0;i<MODBUS_STR_modbus->num_data;i++)                                                                                                               //读取长整型数据到发送寄存器sendB[]中
      {
            trans_long_byte(data_long[(MODBUS_STR_modbus->Saddr-limit_long_lower)/2+i]);                                                                                              //读取长整型寄存中的数据转换成4字节数组       长整型数组索引=(当前地址-起始地址)/2    上位机每两个字构成一个长整型变量的地址
                                                                                                                                                                                                                                                                                //((读取的首地址-下限)/2+偏移量)
            for(j=0;j<4;j++)
            {
                MODBUS_STR_modbus->Sendbuf=ui4_byte;                                                                                                    //把长整型数据转换过来的字节数组赋给发送寄存器
                MODBUS_STR_modbus->addr_data++;                                                                                                   //数据存储偏移量+1
            }
      }
    }
//-------------------------------读取浮点型变量数据--42000-42999-------------------------------------------------------------------------------------------------------------------------
    else if(MODBUS_STR_modbus->Saddr>=limit_float_lower&&MODBUS_STR_modbus->Saddr<=limit_float_upper)                                                                        //判断读取的数据是否在 浮点整型地址范围内在MODBUSB.H 下限:42000上限:42999
    {
      MODBUS_STR_modbus->num_data=(MODBUS_STR_modbus->Rcbuf*256+MODBUS_STR_modbus->Rcbuf)/2;                                                                         //读取寄存数个数 (“/2”modbus发送浮点型数据占2个字 )
      MODBUS_STR_modbus->num_byte=4*MODBUS_STR_modbus->num_data;                                                                                                                          //返回寄存字节数数量(1个浮点型数据有4个字节)
      MODBUS_STR_modbus->Sendbuf=MODBUS_STR_modbus->num_byte;                                                                                                                             //返回字节总数
      for(i=0;i<MODBUS_STR_modbus->num_data;i++)                                                                                                                 //读取浮点数据到发送寄存器
      {
            trans_float_byte(data_float[(MODBUS_STR_modbus->Saddr-limit_float_lower)/2+i]);                                                                                                //读取浮点数寄存中的数据转换成4字节数组         浮点数组索引=(当前地址-起始地址)/2   上位机每两个字构成一个浮点变量的地址
                       
                        for(j=0;j<4;j++)
            {
                MODBUS_STR_modbus->Sendbuf=ui4_byte;                                                                                                    //把长整型数据转换过来的字节数组赋给发送寄存器                               
                                MODBUS_STR_modbus->addr_data++;                                                                                                   //数据存储偏移量+1
            }
      }
    }
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    if(MODBUS_STR_modbus->Saddr>=limit_int_lower&&MODBUS_STR_modbus->Saddr<=limit_float_upper)                                                         //如果在地址范围内   40001-42999
    {
      MODBUS_CRC16(MODBUS_STR_modbus->Sendbuf,MODBUS_STR_modbus->addr_data);                                                                      //CRC校验函数;(sendB要计算的数据,addr_data要计算的个数)
      MODBUS_STR_modbus->Sendbuf=MODBUS_CRC_data_Hi;                                                                //装载CRC高位
      MODBUS_STR_modbus->addr_data++;                                                                                                                                                            //地址便宜量+1
      MODBUS_STR_modbus->Sendbuf=MODBUS_CRC_data_Lo;                                                                //装载CRC低位
      MODBUS_STR_modbus->addr_data++;                                                                                                                                                             //地址便宜量+1
               
                UART_DMA_Transmit(MODBUS_STR_modbus->Sendbuf,MODBUS_STR_modbus->addr_data);                                                                                                                //这里发送的字节数为当前函数中的结构变量(不能使用参数传递过来的结构体变量值)
        }

}
这里把Modbus 03代码做成了函数,通过把对应串口的结构体数据传递进来可以获取解析的数据。这里为了精简显示只贴了03功能码。

Modbus4.c这里是用串口4进通讯的,串口4接收通过DMA进行了收取数据到MODBUS_STR_modbus4.Rcbuf中。然后调用“Modbus.c”中的03功能吗,来进行数据的解析,并通过DMA把发送缓冲“MODBUS_STR_modbus.Sendbuf”中的数据发送出去。
ADC_MEASURE.h

#ifndef _ADC_MEASURE_H
#define _ADC_MEASURE_H
//#define ADC_Num 32                                                                                               //ADC采集的次数上限
#define ADC_DAQ_Num 5                                                                                               //DAQ采集的个数
#define ADC_REF 2500                                                                                                                //ADC的电压基准   电源电压基准VCC=3300 外部基准Vref=2500(默认)       
#define ADC_on1                                                                                                                        //ADC结果使能                               
#define ADC_off 0                                                                                                                        //ADC结果失效
#define ADC_Channel_Mask   0x8F                                                                                                //ADC通道屏蔽                              
#define ADC_Channal_Offset 0x01                                                                                                //ADC通道端口偏移,从通道14开始作为通道0 (~0x01)=0xFE (1111 1110)
#define input_pressure        (DI_input&(1<<5))                                                                //管道压力超压标志 DI05
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#define        ADC_CH                2                        /* 1~16, ADC转换通道数, 需同步修改 DMA_ADC_CHSW 转换通道 */
//#define        ADC_DATA        8                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=2 */
//#define        ADC_DATA        12                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=4 */
//#define        ADC_DATA        20                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=8 */
//#define        ADC_DATA        36                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=16 */

//#define        ADC_DATA        68                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=32 */
//#define        ADC_DATA        132                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=64 */
//#define        ADC_DATA        260                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=128 */
#define        ADC_DATA                516                        /* 6~n, 每个通道ADC转换数据总数, 2*转换次数+4, 需同步修改 DMA_ADC_CFG2 转换次数=256 */

//#define        DMA_ADDR        0x800                /* DMA数据存放地址 */
//#define        DMA_ADDR        0x1400                /* DMA数据存放地址 */
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
typedef struct                                                                                                                                          
{   
    u8 channel;                                                                                       //AD当前通道   
    u8 time_channel;                                                                                       //AD采集通道(上限)
    u8 flag_meas;                                                                                    //单次测量完成(0-正在采集,1-采集完成)         
    u16 inc_meas;                                                                                //AD采集次数的累加变量
    u16 time_meas;                                                                                     //AD采集总次数(求平均值)
    u16 ADC_data;                                                                     //存放AD单次采集的值
        u16        ADC_1ms;                                                                                                                        //ADC 1ms时间到达       
        u16 inc_ADC_ms;                                                                                                                        //ADC ms计时       
        u16 inc_pressure;                                                                                                                //ADC超压计时                       
    u16 time_pressure;                                                                                                                //ACC超压时间设定               
        u16 flag_pressure;                                                                                                                //管道超压连锁 0-超压检测禁用,1-超压检测使能
        u16 flag_Low_Pressure;                                                                                                        //管道欠压缺药连锁 0-欠压检测禁用,1-欠压检测使能
        u16 state_pressure;                                                                                                                //管道压力状态 0-管道压力正常,1-管道压力超压,2-管道压力欠压                                       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        f32 value_Low_Pressure;                                                                                                        //管道欠压值kPa
        u32 time_Low_Pressure;                                                                                                        //管道欠压检测设定时间
        u32 inc_Low_Pressure;                                                                                                        //管道欠压检测计时时间                                       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
        f32 voldata;                                                                         //把每个通道单次采集的值装入数组中
    f32 sum_voldata;                                                                        //把每个通道采集多次的值累加后放入数组中(为了求平局值)
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    f32 avg_voldata;                                                                        //用来存放每个通道实际电压的平均值
    f32 value;                                                                           //用来存放实际电压转换的工程量
    f32 vol_upper;                                                                          //用来存放电压对应的上限值
    f32 vol_lower;                                                                          //用来存放电压对应的下限值
    f32 value_upper;                                                                        //用来存放工程量的上限
    f32 value_lower;                                                                        //用来存放工程量的下限
    f32 value_comp_upper;                                                                           //用来存放比较值的高值 (大于此值,输出一个状态)
    f32 value_comp_lower;                                                                           //用来存放比较值的低值 (小于此值,输出一个状态)
}ADC_VAR;                                               
extern ADC_VAR xdata ADCA_STR_VAR;                                                                                   //声明一个ADC模块结构体实例变量
extern u8 xdata DmaBuffer;                                                                //定义DMA缓冲
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
extern void ADC_Init(void);                                                                                                        //ADC初始化                               
extern void ADC_Meas(void);                                                                                                        //ADC采集处理
extern void ADC_check_pressure(void);                                                                                //ADC压力检测               

#endif这里同样定义了一个结构体来存放ADC相关的数据。

ADC_MEASURE.c

/*
说明:        ADC采集通通道为0-通道14(P06),1-通道13(P05)
                ADC数据格式为右对齐
                ADC每次采集速度为1ms
                ADC采集10次时,电压、电流波动大概±5mV左右,电流(通道2)长时间测试可能会超时,偶尔会超出这个值。
                ADC采集为100次时,电压、电流波动都在±1mV左右。
*/
#include "..\..\comm\STC32G.h"
#include "config.h"
#include "intrins.h"
#include "stdio.h"
#include "ADC_MEASURE.h"
#include "method.h"
#include "MODBUS.H"
#include "IO.h"
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------变量定义-----------------------------------------------------------------------------------------------
ADC_VAR xdata ADCA_STR_VAR;                                                                                                                                          //输出结构体变量   
//u8 xdata DmaBuffer _at_ DMA_ADDR;
u8 xdata DmaBuffer;                                                                                                                                        //定义DMA缓冲               
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void ADC_DMA_GetValue(void);                                                                                                                                                        //从DMA获取ADC平均值       
void ADC_Data_Porcess(void);                                                                                                                                                        //ADC 数据处理
void ADC_Quantity_Process(void);                                                                                                                                                //ADC 工程量处理
void ADC_Rstart(void);                                                                                                                                                                        //ADC 重新运行一次ADC转换
void DMA_Config(void);
//====================================================================================================================================================================================================
// 函数: void ADC_STR_Init(void)
// 描述: ADC结构体变量初始化
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-2-24
// 备注:
//====================================================================================================================================================================================================
void ADC_STR_Init(void)
{       
        u16 i=0;       
//---------------------------------------------------------------------------变量初始化-------------------------------------------------------------------------------------------------------------------------
    ADCA_STR_VAR.inc_meas=0;                                                                                                                                      //AD采集次数的累加变量
    ADCA_STR_VAR.time_meas=10000;                                                                                                                          //AD采集总次数(求平均值10)ADC_Num 32//ADC采集的次数上限
    ADCA_STR_VAR.channel=0;                                                                                                                                      //AD当前通道
    ADCA_STR_VAR.time_channel=2;                                                                                                                               //AD采集通道上限(当前为2个通道)
        ADCA_STR_VAR.flag_meas=0;                                                                                                                                  //单次测量完成(0-正在采集,1-采集完成)
    ADCA_STR_VAR.ADC_1ms=0;                                                                                                                                                                //ADC 1ms时间到达                       
        ADCA_STR_VAR.inc_ADC_ms=0;                                                                                                                                                        //ADC ms 计时       
        ADCA_STR_VAR.inc_pressure=0;                                                                                                                                                //ACC超压计时       
        ADCA_STR_VAR.time_pressure=500;                                                                                                                                                //ACC超压时间设定
        ADCA_STR_VAR.flag_pressure=0;                                                                                                                                                //管道超压连锁        0-禁用        1-使能
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        ADCA_STR_VAR.flag_Low_Pressure=0;                                                                                                                                        //管道欠压连锁        0-禁用        1-使能        
        ADCA_STR_VAR.value_Low_Pressure=100;                                                                                                                                //管道欠压值kPa
        ADCA_STR_VAR.time_Low_Pressure=60000;                                                                                                                                //管道欠压检测设定时间(60s)
        ADCA_STR_VAR.inc_Low_Pressure=0;                                                                                                                                        //管道欠压检测计时时间       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.ADC_data=0;                                                                                                //记录单词ADC准换值                       
        for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.voldata=0;                                                                                                //记录瞬时电压值
        for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.sum_voldata=0;                                                                                        //记录电压累计值
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
        for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.avg_voldata=0;                                                                       //用来存放每个通道实际电压的平均值
    for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.value=0;                                                                            //用来存放实际电压转换的工程量
    for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.vol_upper=2500;                                                                    //用来存放电压对应的上限值
    for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.vol_lower=0;                                                                              //用来存放电压对应的下限值
    for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.value_upper=1000;                                                                         //用来存放工程量的上限
    for(i=0;i<ADC_DAQ_Num;i++) ADCA_STR_VAR.value_lower=0;                                                                        //用来存放工程量的下限
    ADCA_STR_VAR.value_comp_upper=300;                                                                                                                           //用来存放比较值的高值 (大于此值,输出一个状态)
    ADCA_STR_VAR.value_comp_upper=400;                                                                                                                           //用来存放比较值的高值 (大于此值,输出一个状态)
    ADCA_STR_VAR.value_comp_upper=550;                                                                                                                           //用来存放比较值的高值 (大于此值,输出一个状态)
    ADCA_STR_VAR.value_comp_upper=400;                                                                                                                           //用来存放比较值的高值 (大于此值,输出一个状态)
    ADCA_STR_VAR.value_comp_upper=400;                                                                                                                           //用来存放比较值的高值 (大于此值,输出一个状态)                                                   
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------   
    ADCA_STR_VAR.value_comp_lower=200;                                                                                                                    //用来存放比较值下限值1      
    ADCA_STR_VAR.value_comp_lower=250;                                                                                                                    //用来存放比较值下限值2
    ADCA_STR_VAR.value_comp_lower=150;                                                                                                                    //用来存放比较值下限值3   
    ADCA_STR_VAR.value_comp_lower=150;                                                                                                                    //用来存放比较值下限值4
    ADCA_STR_VAR.value_comp_lower=150;                                                                                                                    //用来存放比较值下限值5
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//====================================================================================================================================================================================================
// 函数: void ADC_Init(void)
// 描述: ADC初始化设置
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-2-24
// 备注:
//====================================================================================================================================================================================================
void ADC_Init(void)                                                                       
{
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        ADC_STR_Init();                                                                                                                                                                                //ADC结构变量初始化
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       

        P0M0&=~(1<<6);                                                                                                                                                                                //P0.6作为电压输出口        高组输入
        P0M1|=(1<<6);                                                                                                                                                                                //P0.6
        P0M0&=~(1<<5);                                                                                                                                                                             //P0.5作为电压输出口        高组输入
        P0M1|=(1<<5);                                                                                                                                                                                //P0.5
       
        //P0M0=0x00;                                                                                                                                                                                //P0.6作为电压输出口        高组输入
        //P0M1=0x40;                                                                                                                                                                                //P0.6       
        //P0M0=0x00;                                                                                                                                                                                //P0.5作为电压输出口        高组输入
        //P0M1=0x20;                                                                                                                                                                                //P0.5       
       
        P0_PullUp_DIS(PIN_6);                                                                                                                                                                //关闭P06上拉
        P0_PullUp_DIS(PIN_5);                                                                                                                                                                //关闭P05上拉       

//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        //ADCTIM=0x3f;                                                                                                                                                                                //                                                               
        //ADC保持时间2,模拟采样时间32
        SetBits(ADCTIM,CSHOLD0|SMPDUTY4|SMPDUTY3|SMPDUTY2|SMPDUTY1|SMPDUTY0);                                                                //ADC保持时间2,模拟采样时间32
        ADC_RES=0;                                                                                                                                                                                          //ADC数据寄存器清零
        ADC_RESL=0;                                                                                                                                                                                        //ADC低位寄存器清零                       
        //设置ADC工作频率Sysclk/2/16
        ADCCFG=0x2f;                                                                                                                                                                                //设置ADC数据格式为右对齐                       
        ADC_CONTR=(~(ADCA_STR_VAR.channel+ADC_Channal_Offset));                                                                                                //选择P06为采集通道14
        //ADC_CONTR=0x0E;                                                                                                                                                                        //选择P06为采集通道14
        //ADC_CONTR=0x0D;                                                                                                                                                                        //选择P05为采集通道13   
        ADC_POWER=1;                                                                                                                                                                                //ADC电源使能
        delay_ms(2);                                                                                                                                                                                //延时2ms,等待ADC上电稳定       
        //EADC=1;                                                                                                                                                                                                //ADC中断使能
        //ADC_START=1;                                                                                                                                                                                //进行一次ADC转换
       
        DMA_Config();

}
//=====================================================================================================================================================================================================
// 函数: void DMA_Config(void)
// 描述: ADC DMA 功能配置.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2025-5-6
//=====================================================================================================================================================================================================
void DMA_Config(void)
{
        DMA_ADC_STA = 0x00;
        DMA_ADC_CFG = 0x80;                //bit7 1:Enable Interrupt
        DMA_ADC_RXAH = (u8)((u16)&DmaBuffer>> 8);        //ADC转换数据存储地址
        DMA_ADC_RXAL = (u8)((u16)&DmaBuffer);
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
        //DMA_ADC_CFG2 = 0x08;        //每个通道ADC转换次数:2
        //DMA_ADC_CFG2 = 0x09;        //每个通道ADC转换次数:4
        //DMA_ADC_CFG2 = 0x0A;        //每个通道ADC转换次数:8
        //DMA_ADC_CFG2 = 0x0B;        //每个通道ADC转换次数:16
       
        //DMA_ADC_CFG2 = 0x0C;        //每个通道ADC转换次数:32
        //DMA_ADC_CFG2 = 0x0D;        //每个通道ADC转换次数:64
        //DMA_ADC_CFG2 = 0x0E;        //每个通道ADC转换次数:128
        DMA_ADC_CFG2 = 0x0F;        //每个通道ADC转换次数:256
       
        DMA_ADC_CHSW0 = 0x00;        //ADC通道使能寄存器 ADC7~ADC0
        DMA_ADC_CHSW1 = 0x60;        //ADC通道使能寄存器 ADC15~ADC8 使能14,13通道
        //DMA_ADC_CHSW1 = 0x40;        //ADC通道使能寄存器 ADC15~ADC8 使能14通道
        DMA_ADC_CR = 0xc0;                //bit7 1:Enable ADC_DMA, bit6 1:Start ADC_DMA
}

//=========================================================================================================================================================================================================
// 函数: void ADC_DMA_Interrupt (void) interrupt 48
// 描述: ADC DMA中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-5-6
// 备注:
//=======================================================================================================================================================================================================
void ADC_DMA_Interrupt(void) interrupt DMA_ADC_VECTOR
{
        DMA_ADC_STA = 0;                                                                                        //DMA_ADC 中断标志清零
        ADCA_STR_VAR.flag_meas=1;                                                                        //ADC数据处理标志=1
}
//====================================================================================================================================================================================================
// 函数: void ADC_Isr() interrupt 5
// 描述: ADC中断处理
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-2-24
// 备注:
//====================================================================================================================================================================================================
/*
void ADC_Isr() interrupt 5
{
        ADC_FLAG=0;                                                                                                                                                                                        //清除中断标志
        ADCA_STR_VAR.ADC_data=(ADC_RES<<8)+ADC_RESL;                                                                                                                //读取ADC采集数据(数据右对齐)
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------               
        ADCA_STR_VAR.flag_meas=1;                                                                                                                                      //电压采集完成标志=1准备进行电压处理   
        //ADC_Meas();                                                                                                                                                                                //ADC数据处理
}       
*/
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//====================================================================================================================================================================================================
// 函数: void ADC_Meas(void)       
// 描述: ADC测量处理
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-2-24
// 备注:
//====================================================================================================================================================================================================
void ADC_Meas(void)                                                                                                                                                                                        //ADC测量处理
{
        if(ADCA_STR_VAR.ADC_1ms)                                                                                                                                                                //判断如果1ms时间到达       
        {
                ADCA_STR_VAR.ADC_1ms=0;                                                                                                                                                                //1ms清零        10                                                                                                                                                                                               
                if(++ADCA_STR_VAR.inc_ADC_ms>=10)
                {               
                        ADCA_STR_VAR.inc_ADC_ms=0;
                        if(ADCA_STR_VAR.flag_meas)                                                                                                                                                       
                        {       
                                ADCA_STR_VAR.flag_meas=0;                                                                                                                                            //复位电压采集完成标志=0       
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                ADC_DMA_GetValue();                                                                                                                                                                //从DMA获取ADC平局值
                                ADC_Data_Porcess();                                                                                                                                                                //数据处理
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                DMA_ADC_CR=0xc0;                                                                                                                                                                //重新启动一次ADC_DMA采集 bit7 1:Enable ADC_DMA, bit6 1:Start ADC_DMA       
                        }       
                }
        }
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------                     
}
//====================================================================================================================================================================================================
// 函数: void ADC_DMA_GetValue(void)
// 描述: 从ADC_DMA寄存器中获取数据
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-5-6
// 备注:
//====================================================================================================================================================================================================
void ADC_DMA_GetValue(void)                                                                                                                                                                                //ADC测量处理
{       
        //ADCA_STR_VAR.ADC_data=((u16)DmaBuffer<<8)+DmaBuffer;                                                //读取ADC采集平均数据(数据右对齐)管道压力通道14
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------       
        ADCA_STR_VAR.ADC_data=((u16)DmaBuffer<<8)+DmaBuffer;                                                //读取ADC采集平均数据(数据右对齐)管道压力通道14       
        ADCA_STR_VAR.ADC_data=((u16)DmaBuffer<<8)+DmaBuffer;                                                //读取ADC采集平均数据(数据右对齐)模拟量控制通道13                       
}
//====================================================================================================================================================================================================
// 函数: void ADC_Data_Porcess(void)       
// 描述: ADC数据处理
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-4-16
// 备注:
//====================================================================================================================================================================================================
void ADC_Data_Porcess(void)
{
        ADCA_STR_VAR.avg_voldata=(unsigned long)ADCA_STR_VAR.ADC_data*ADC_REF/4096;           //计算单次电压值mV(外部2.5V基准) 管道压力通道14
        ADCA_STR_VAR.avg_voldata=(unsigned long)ADCA_STR_VAR.ADC_data*ADC_REF/4096;           //计算单次电压值mV(外部2.5V基准) 模拟量控制通道13       
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        ADCA_STR_VAR.value=(ADCA_STR_VAR.avg_voldata-ADCA_STR_VAR.vol_lower)*((ADCA_STR_VAR.value_upper-ADCA_STR_VAR.value_lower)/(ADCA_STR_VAR.vol_upper-ADCA_STR_VAR.vol_lower))+ADCA_STR_VAR.value_lower;    //计算物理量值
        ADCA_STR_VAR.value=(ADCA_STR_VAR.avg_voldata-ADCA_STR_VAR.vol_lower)*((ADCA_STR_VAR.value_upper-ADCA_STR_VAR.value_lower)/(ADCA_STR_VAR.vol_upper-ADCA_STR_VAR.vol_lower))+ADCA_STR_VAR.value_lower;    //计算物理量值
}       

//====================================================================================================================================================================================================
// 函数: void ADC_Rstart(void)       
// 描述: 重新运行一次ADC转换
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-4-16
// 备注:
//====================================================================================================================================================================================================
//void ADC_Rstart(void)                                                                                                                                                                                                                                  //重新运行一次ADC转换
//{
//        ADC_CONTR=ADC_Channel_Mask&(~(ADCA_STR_VAR.channel+ADC_Channal_Offset));                                                                                                                  //设置当前采集通道                                                                                                                       
//        _nop_(); _nop_();                                                                                                                                                                                                                                        //插入空周期稳定一会儿
//        ADC_START=1;                                                                                                                                                                                                                                                //进行一次ADC转换
//}       
        这里使用了DMA的方式来接收ADC的数据,但是不知道什么原因会造成Modbus通讯数据的故障。不适用DMA的方式进行ADC采集不会对Modbus通讯造成故障。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
下面是使用串口数据监控到的数据故障的结果。发送的都是 “01 03 08 0C 00 0C 87 AC” 查询代码,间隔时间为100ms,出现错误的时间不固定,有时候几分钟就出,有时候20-30分钟会出,有时候可能会更长的时间。
            

下面是测试代码:

attach://107136.rar



国芯人工智能 发表于 2025-7-3 19:23:00

大家好,关于您在使用Modbus RTU通信时遇到的串口数据受ADCDMA处理影响的问题,我们非常理解您的困扰。以下是我们对这一问题的专业分析及建议。

首先,从系统架构的角度来看,ADC(模数转换)与UART(通用异步收发传输器)作为嵌入式系统中常见的外设模块,通常由不同的DMA(直接内存访问)通道进行数据传输。当ADC通过DMA采集数据时,如果未合理配置DMA优先级或资源分配,确实可能对其他外设(如UART)的数据传输造成干扰,尤其是在高负载或实时性要求较高的场景下。

一、问题原因分析

1. DMA通道冲突
   在许多嵌入式平台中,多个外设共享相同的DMA控制器,而每个DMA通道具有不同的优先级。若ADC和UART使用了同一DMA通道或相近的优先级设置,可能会导致UART在接收或发送数据时被中断,从而引发数据丢失或误码。

2. CPU资源竞争
   当ADC通过DMA进行高速数据采集时,虽然DMA本身不占用CPU资源,但如果ADC的DMA传输频繁触发中断(如每帧数据结束时),可能会间接影响到UART的中断响应时间,进而影响Modbus RTU的通信稳定性。

3. 波特率与时序匹配问题
   Modbus RTU协议对通信时序有严格要求,特别是在帧间隔(Frame Interval)和字符间隔(Character Interval)方面。如果ADC DMA操作导致UART的接收缓冲区未能及时处理数据,可能导致帧解析错误,进而引发通信失败。

4. 硬件设计限制
   某些MCU的UART和ADC可能共用部分硬件资源(如时钟源、引脚等)。若ADC采集频率过高,可能对UART的时钟稳定性产生一定影响,尤其在低功耗模式下更为明显。

二、解决方案建议

1. 优化DMA配置
为ADC和UART分别分配独立的DMA通道,并设置合理的优先级。通常建议将UART的DMA请求优先级设为“高”或“中”,以确保其通信不受ADC采集的干扰。
若无法分配独立通道,可尝试调整DMA的仲裁策略,确保UART的DMA请求在ADC传输期间仍能获得足够的响应机会。

2. 减少ADC DMA中断频率
如果ADC采集频率较高且不需要实时处理,可考虑降低采样率或采用双缓冲机制,避免频繁触发DMA中断,从而减少对UART的干扰。
使用DMA完成中断而非每帧中断,可以降低系统开销,提升整体效率。

3. 增加UART接收缓冲区大小
增加UART接收缓冲区的长度,可以在ADC DMA操作期间暂存更多数据,防止因数据积压而导致丢包或解析错误。
同时,可结合软件定时器检测帧间隔,确保Modbus RTU的通信时序符合规范。

4. 检查时钟源稳定性
确保ADC和UART所使用的时钟源稳定可靠,避免因时钟抖动或漂移导致通信异常。
如有必要,可启用外部晶振或使用更高精度的时钟源,以提升系统整体的可靠性。

5. 软件层面的容错处理
在Modbus RTU协议实现中,增加错误校验机制(如CRC校验),并在接收到数据后进行完整性判断,防止因通信异常导致的逻辑错误。
对于关键数据,可设置重传机制,提高系统的鲁棒性。

三、总结

综上所述,ADCDMA采集对Modbus RTU通信的影响主要源于DMA资源竞争、CPU调度延迟及通信时序匹配等问题。通过合理配置DMA通道、优化ADC采集策略、增强UART缓冲能力以及完善软件容错机制,可以有效缓解甚至消除此类干扰,保障系统的稳定运行。

如果您在实际调试过程中遇到具体现象或日志信息,欢迎进一步提供详细内容,我们将为您提供更具针对性的技术支持。

国芯人工智能团队 敬上

[本文内容由国芯人工智能辅助生成,仅供参考]

liming3267 发表于 2025-7-3 19:26:17


这里画红框的是出错的反回码,未画红框的是正确的返回码。

liming3267 发表于 2025-7-15 16:49:42

有没有大神帮忙看下,这个问题找了好久了!

芯启元 发表于 2025-7-16 16:55:07

liming3267 发表于 2025-7-15 16:49
有没有大神帮忙看下,这个问题找了好久了!

已经转发给对应工程师了,看到后会给您回复的。

神农鼎 发表于 2025-7-17 07:59:26

看头文件是 STC32G.H,
STC32G12K128, STC32G8K64 都可以仿真,
建议用仿真来快速分析错在哪



STC32G12K128自带的硬件USB直接仿真-仿真演示-STC单片机教学视频 国芯人工智能技术交流网站 - AI32位8051交流社区

liming3267 发表于 2025-7-17 14:30:41

感谢!
页: [1]
查看完整版本: ADC_DMA采集影响Modbus RTU通讯