STM32-modbus rtu 之主机程序

2023-11-11

一、STM32串口的发送与接收

考虑到modbus的使用场合大多为半双工而非全双工,所以,串口接收采用DMA+空闲中断,发送则直接发送。

 
#include "serial.h"
#include "string.h"
 
_serialbuf_st serialRXbuf_st;
_serialbuf_st serialTXbuf_st;
 
/*DMA接收数据缓存*/
u8 g_uart1DmaRXBuf[UART_DMARX_SIZE];
    
/*
说明:3个串口直接发送函数
编写:林
*/
void myUSART_Sendbyte(USART_TypeDef* USARTx, uint16_t Data)
{
    while((USARTx->SR&0X40)==0); 
    USARTx->DR = (Data & (uint16_t)0x01FF);
}
void myUSART_Sendstr(USART_TypeDef* USARTx, const char  *s)
{
    while(*s != '\0')
    {      
        myUSART_Sendbyte( USARTx, *s) ;
        s++;
    }
}
void myUSART_Sendarr(USART_TypeDef* USARTx, u8 a[] ,uint8_t len)
{
    uint8_t i=0;
    while(i <  len )
    {      
        myUSART_Sendbyte( USARTx, a[i]) ;
        i++;
    }
}
 
/*
说明:
  串口1初始化
  串口1使用DMA 接收 
编写:林
*/
void Usart1_init(u32 baud)
{    
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef  USART_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    USART_InitStructure.USART_BaudRate =baud;//一秒发送BaudRate个bit
    USART_InitStructure.USART_WordLength =USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure); 
 
    DMA_DeInit(DMA1_Channel5);  
 
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(USART1->DR));  
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)g_uart1DmaRXBuf;  
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  
    DMA_InitStructure.DMA_BufferSize =  UART_DMARX_SIZE;  
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;  
 
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  
    DMA_Init(DMA1_Channel5,&DMA_InitStructure);  
 
    USART_ITConfig(USART1,USART_IT_TC,DISABLE);  
    USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);  
    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); 
         
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;               //   
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;       //    
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;              //    
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                 //     
    NVIC_Init(&NVIC_InitStructure);     
                                            
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);  
    USART_Cmd(USART1, ENABLE);     
    DMA_Cmd(DMA1_Channel5,ENABLE);  
 
    memset(   & serialRXbuf_st ,0, sizeof (   serialRXbuf_st ) ) ;
}
 

 

//等待发送完成
void WaitForTransmitComplete(USART_TypeDef* USARTx)
{
    while((USARTx->SR&0X40)==0){}; 
}
 
/*
说明:串口中断,DMA与空闲中断处理,用于串口接收
编写:林
*/
void USART1_IRQHandler(void)
{ 
    _serialbuf_st *p= &serialRXbuf_st;
    __IO u8 temp = 0;
    u8 i=0;
 
    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        temp = USART1->SR;
        temp = USART1->DR; 
        DMA_Cmd(DMA1_Channel5,DISABLE);
        temp = UART_DMARX_SIZE - ((uint16_t)(DMA1_Channel5->CNDTR));
        
        for (i = 0;i < temp;i++)
        {
              p->buf[i] =g_uart1DmaRXBuf[i];
        }
        p->len = temp ;
        
        DMA_SetCurrDataCounter(DMA1_Channel5,UART_DMARX_SIZE);
        DMA_Cmd(DMA1_Channel5,ENABLE);
    } 
    __nop(); 
}

 

/*serial.h*/
 
#ifndef __SERIALx_H
#define __SERIALx_H    
 
#include "stm32f10x.h"
/*DMA接收数据缓存大小*/
#define UART_DMARX_SIZE 0xff
 
typedef struct  
{
    u8 buf[UART_DMARX_SIZE];
    __IO u8 len;
} _serialbuf_st ;  //串口数据结构
 
typedef struct  
{
    u8 addr;//从机地址
    u8 start;//寄存器起始
    u8 len;  //接收到或待发送的寄存器数
    u16 buf[UART_DMARX_SIZE/2];//寄存器数据
} _mbdata_st; //用户数据
 
extern _serialbuf_st serialRXbuf_st;
extern _serialbuf_st serialTXbuf_st;
 
void Usart1_init(u32 baud) ;
void WaitForTransmitComplete(USART_TypeDef* USARTx) ;
void myUSART_Sendbyte(USART_TypeDef* USARTx, uint16_t Data) ;
void myUSART_Sendstr(USART_TypeDef* USARTx, const char  *s) ;
void myUSART_Sendarr(USART_TypeDef* USARTx, u8 a[] ,uint8_t len);
 
#endif

 

二、实现读保持寄存器功能:F=0x03

首先实现发送函数

/*
说明:
    接收“读保持寄存器”的结果
    命令0X03
返回:
    res_OK 正确
    res_ERR1 其他错误
    res_ERR2 地址不符
    res_ERR3 无反馈
*/
u8 mb_recv_readHoldingReg( _mbdata_st *mbp)
{ 
    u8 i;
    if( serialRXbuf_st.len == 0 ) return res_ERR3;
 
    serialRXbuf_st.len=0;
    if( mbp->addr == serialRXbuf_st.buf[0] )
    {
         if( 0x03 == serialRXbuf_st.buf[1] )
         { 
             for(i=0;i<serialRXbuf_st.buf[2]/2;i++)
             {
                    mbp->buf[mbp->start +i]= (u16)(serialRXbuf_st.buf[i*2+3]>>8) + serialRXbuf_st.buf[i*2+4];
             }
             mbp->len =  serialRXbuf_st.buf [2]/2;//寄存器数
             return res_OK;
         }
         else
         {
             return res_ERR1;
         }
    }
    return res_ERR2; 
}

三、实现写保持寄存器功能:F=0X10

发送与接收代码如下

 
/*
发送"写保持寄存器",命令0X10
*/
void mb_sent_writeHoldingReg( const _mbdata_st  mbp)
{ 
    u8 i=0;
    u16 temp;
    u8 len = mbp.len; 
    if(len>0x7d)len=0x7d;
    serialTXbuf_st.buf[0] = mbp.addr;
    serialTXbuf_st.buf[1] = 0x10;
    serialTXbuf_st.buf[2] = mbp.start>>8;
    serialTXbuf_st.buf[3] = mbp.start;
    serialTXbuf_st.buf[4] = 0;
    serialTXbuf_st.buf[5] =  len;
    serialTXbuf_st.buf[6] =  len*2;
    for(;i<mbp.len;i++)
    {
      serialTXbuf_st.buf[i*2+7] = mbp.buf[i]>>8;
      serialTXbuf_st.buf[i*2+8] = mbp.buf[i];
    }   
    temp=usMBCRC16(  serialTXbuf_st.buf,   len*2+7 );
    serialTXbuf_st.buf[ len*2+7] = temp;    //低
    serialTXbuf_st.buf[ len*2+8] = temp>>8;
 
    myUSART_Sendarr(  USART1,   serialTXbuf_st.buf ,   len*2+9) ;
    WaitForTransmitComplete(USART1) ; //发送完成
 }
/*
说明:
    接收“写保持寄存器”的从机反馈
返回:
    res_OK 正确
    res_ERR1 校验错误
    res_ERR2 返回格式错误
    res_ERR3 无反馈
*/
u8 mb_recv_writeHoldingReg( _mbdata_st  *mbp )
{
    u8 i=0;
    if( serialRXbuf_st.len == 0 ) return res_ERR3;
    serialRXbuf_st.len=0;
 
    for(i=0;i<6;i++)
    {
        if ( serialTXbuf_st.buf[i] != serialRXbuf_st.buf[i] ) return res_ERR2;
    }
    if( serialRXbuf_st.buf[6] + (u16)(serialRXbuf_st.buf[7]<<8) != usMBCRC16(  serialRXbuf_st.buf,  6 )) return res_ERR1;
    return res_OK;
}

四、程序调用

为了方便,将上面函数统一起来

u8 gmod = 0 ;//测试用,gmod=0,测试写保持寄存器功能,gmod=1读保持寄存器功能。
_mbdata_st HoldingReg_st = {1,0,5,{1,2,3,4,5,6,7,8,9}};
u8 gsync = 1 ;
 
void mb_setMODRXorTX(bool RxorTx)
{
    //此处需修改硬件,用于使用外部器件(比如485器件)的接收或发送。
}
 
//统一发送
void smb_sentHoldingReg(const _mbdata_st  mbp   )
{    mb_setMODRXorTX(1);//改为发送模式
     if( gmod == 1 )
         mb_sent_writeHoldingReg( HoldingReg_st);
     else 
         mb_sent_readHoldingReg( HoldingReg_st );
     mb_setMODRXorTX(0);//改为接收模式
     gsync=1;
}
//统一接收
 u8 smb_recvHoldingReg( _mbdata_st  *mbp   )
{
    u8 rel=0xff;
    if( 1 == gsync)//发送完成
    {
         gsync=0;
         while( serialRXbuf_st.len == 0 ) 
         {
            if(TIM3->CNT >4900)  break ;//从机无响应
         };//接收完成
         
         if( gmod == 1)
         {
            rel=mb_recv_writeHoldingReg( &HoldingReg_st ) ;
         }else 
         {
            rel=mb_recv_readHoldingReg(   &HoldingReg_st ) ;
         }
    }  
    return rel ;
}

在定时器服务里调用发送函数

void TIM3_IRQHandler(void)   //TIM3中断
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
        {
             if(gmod == 1) HoldingReg_st.buf [1]++ ;   //累增某个保持寄存器,用于测试观察
             smb_sentHoldingReg( HoldingReg_st ) ;
        }
    }
}

在主循环里调用接收函数

    while(1)
    {
          rel = smb_recvHoldingReg( &HoldingReg_st ) ;
          if( res_OK == rel && HoldingReg_st.buf[0] == 1 ) LED0=!LED0; //观察结果
    }

五、验证

程序烧录到STM32,,串口连接电脑,使用PC端从机软件 Modbus Slave 观察

F=0X03

 

F=0X10,(虽然显示03)

 

这个从机软件不能显示F=0X10也就是16,但功能是可以使用的。

不过这毕竟令人心里不爽,所以我使用nmodbus类库编写了C#上位机软件进行验证,如下图所示,可见F=16也就是0X10

https://blog.csdn.net/wangzibigan/article/details/77722939

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32-modbus rtu 之主机程序 的相关文章

  • RS485通讯---Modbus数据链路层与应用层(二)

    前言 RS485通讯 Modbus物理层 xff1a https blog csdn net qq 36883460 article details 105630712 Modbus RTU通讯协议中OSI模型 xff0c 数据链路层和应用
  • 通讯协议(modbus协议和serial读取数据)

    通讯协议 1 调试助手1 1 crc计算助手1 2 串口调试助手 2 使用serial节点读取数据3 modbus协议3 1 报文3 2 功能号3 2 1主机对从机写数据操作3 2 2主机对从机读数据操作 3 3 MODBUS报文模型 4
  • Modbus串口设备模拟器-DevSimulate(Slave+自定义逻辑)

    串口Modbus设备模拟器 DevSimulate DevSimulate是一款可以在电脑上模拟一个或多个从设备的串口工具软件 xff0c 并且可以通过设计数学公式对任意点位进行逻辑关系的自定义 即实现Slave 43 用户自定义逻辑功能
  • 【STM32】RS485 Modbus协议 采集传感器数据

    源码链接 xff1a Modbus传感器采集协议 一 硬件 1 传感器 xff1a 为液压传感器 xff0c 12vDC xff0c RS485数据输出 xff0c 采用Modbus协议通信 2 电路 xff1a 根据传感器属性 xff0c
  • Modbus-RTU通讯协议中CRC校验码的计算步骤

    在CRC计算时只用8个数据位 xff0c 起始位及停止位 xff0c 如有奇偶校验位也包括奇偶校验位 xff0c 都不参与CRC计算 CRC计算方法是 xff1a 1 预置1个16位的寄存器为十六进制FFFF xff08 全1 xff09
  • C# Modbus CRC校验

    Modbus CRC校验 直接输入byte 输出bool public static bool CRC Check byte byteData bool Flag false byte CRC new byte 2 UInt16 wCrc
  • STM32读取RS485传感器数据,但接收到的数据开头总是多一个字节或少一个一字节,有人能告诉我哪里出问题了吗

    STM32读取RS485传感器数据 但接收到的数据开头总是多一个字节或少一个一字节 有人能告诉我哪里出问题了吗
  • 值得收藏 Modbus RTU 协议详解

    值得收藏 Modbus RTU 协议详解 目录 值得收藏 Modbus RTU 协议详解 Modbus是什么 Modbus分类 Modbus通讯过程 Modbus RTU协议数据帧结构 功能码01 读线圈状态 功能码02 读离散量输入 功能
  • QtModbus Serial 简单示例

    来自QQ群 Linux 技术分享 311078264 打开链接加入QQ群 https jq qq com wv 1027 k 5Gr3bAx 此文档由elikang整理 为了文章简单直接 许多细节未能在文章中体现 如有疑问请进群讨论 Qt
  • ModbusTcp和ModbusRtu

    ModbusTcp和ModbusRtu 自己实现的modbusTCP主从站和RTU主从站 纯C语言实现 未调用第三方库 工程下载链接 modbusTcp主从站和RTU主从站 1 何为Modbus通信协议 1 1 Modbus基本介绍 Mod
  • Modbus常用功能码协议详解

    Modbus常用功能码协议详解 01H 读线圈状态 1 描述 读从机线圈寄存器 位操作 可读单个或者多个 2 发送指令 假设从机地址位0x01 寄存器开始地址0x0023 寄存器结束抵制0x0038 总共读取21个线圈 协议图如下 3 响应
  • node-red限速分流节点开发

    node red限速分流节点开发 功能简述 节点设计 界面设计 功能设计 功能实现 功能简述 此节点有一个输入 两个输出 此节点可完成信息的限速分流功能 当信息高速输入时 节点进行限速处理 例 两秒一次 到指定时间后信息由一口输出 若未到指
  • C# Modbus Rtu实现

    Modbus Rtu的实现与Modbus Tcp的实现类似 C ModBus Tcp的学习及Master的实现 我们还是需要借用一个开源库NModbus4 在vs中 打开NuGet管理器 安装NModbus4 具体实现 具体实现与之前的Mo
  • STM32菜鸟成长记录——移植FreeModbusRTU

    本文章用于记录学习RS485及配置Modbus协议 参考博文 FreeModbus移植文章1 FreeModbus移植文章2
  • Modbus网关的 四种类型

    概述 Modbus网关是一种能够将Modubs TCP协议转化为Modbus RTU协议的设备 Modbus广泛应用于仪表和传感器领域 可以获得仪表和传感器的数据 但是传统的基于RS485的Modbus RTU 或ASCII 速度和扩展性较
  • modbus读取保持寄存器实例

    读取108 110的实例 一共读取3个寄存器 请求 03 00 6B 00 03 03 功能码 表示读取保存寄存器 006B 十六进制表示107 从107开始往后读取 0003 十六进制表示读取3个寄存器 响应 03 06 02 2B 00
  • MFC 中的 libmodbus

    我正在尝试将 libmodbus 提供的 modbus 库集成到 Visual Studio 2010 中的自定义 MFC 项目中 到目前为止我所遵循的步骤如下 1 我在 Visual Studio 2010 中创建了项目 如下面的快照所示
  • 如何使用 pymodbus 写入 PLC 输入寄存器

    我想使用 pymodbus 写入 PLC 输入寄存器 我能够阅读它们 from pymodbus client sync import ModbusTcpClient client ModbusTcpClient 10 10 10 32 c
  • Qt modbus串口流控处理

    我正在通过串行端口使用 QModbusDevice 编写一个小程序 使用QModbusRtuSerialMaster类 并有一些问题 问题之一似乎是串口的流量控制不正确 检查串行端口嗅探器时 我发现工作客户端在发送请求时打开 RTS 然后关
  • 无法访问超过 255 个的 Modbus 寄存器 SimpleModbus

    我是法国学生 我已经使用 SimpleModbus 1 周了 它工作得很好 直到我尝试实现一个具有大量寄存器 1000needed 使用从站 1 无法访问超过 255 个的寄存器 无论我做什么 一旦寄存器读数超过 255 我就会收到超时错误

随机推荐

  • 前端使用代理跨域后后端无法接收Session

    将一个 MVC 项目重构为一个前后端分离项目 前端使用了 react axios vite 在前后端分离项目中 通常都会使用代理来解决跨域问题 vite 需要在 vite config js 文件中配置代理 export default d
  • hive的分组和组内排序

    背景 hive的分组和组内排序 语法 语法 row number over partition by 字段a order by 计算项b desc rank rank是排序的别名 partition by 用于给结果集分组 如果没有指定那么
  • ES内存持续增长问题分析

    环境介绍 es版本 5 6 4 Xms31g Xmx31g XX MaxDirectMemorySize 10g 问题说明 用top命令观察ES使用的物理内存不断增加到54 6G 已知堆内存31G 堆外内存MaxDirectMemorySi
  • one-class(单分类) kNN(K-Nearest Neighbor)算法Matlab实现

    one class 单分类 kNN K Nearest Neighbor 算法Matlab实现 本文的核心是给出了一个基于kNN的单分类 one class 分类器实现代码 并给出了数据以及运行实例 让读者能更好地理解并使用 代码基于MAT
  • C++解决TCP粘包

    目录 TCP粘包问题 TCP客户端 TCP服务端 源码测试 TCP粘包问题 TCP是面向连接的 面向流的可靠性传输 TCP会将多个间隔较小且数据量小的数据 合并成一个大的数据块 然后进行封包发送 这样一个数据包里就可能含有多个消息的数据 面
  • Android车载开发必备知识

    Bing Android车载开发必备知识 你好 根据搜索结果 Android车载开发必备知识是指一些关于Android系统在车载环境中的应用 测试 分发和优化的技术和方法 可以帮助开发者在车载领域开发出高效 稳定 安全和用户友好的应用 以下
  • Gradle基本知识点与常用配置

    http blog csdn net u010818425 article details 52268126 查看原文 http blog csdn net u010818425 article details 52268126 本文篇幅较
  • 【C】PTA两个有序链表序列的合并

    课程 数据结构 陈越 何钦铭 本题要求实现一个函数 将两个链表表示的递增整数序列合并为一个非递减的整数序列 函数接口定义 List Merge List L1 List L2 其中List结构定义如下 typedef struct Node
  • 基于门控循环单元(GRU)的多输入多输出预测,门控循环单元(GRU)的数据回归预测。

    清空环境变量 warning off 关闭报警信息 close all 关闭开启的图窗 clear 清空变量 clc 清空命令行 导入数据 res xlsread 数据 xlsx 数据分析 num size 0 8 训练集占数据集比例 ou
  • 用CMAKE编译OpenCV 3.4.2+Opencv Contrib 3.4生成可执行包

    1 github下载OpenCV https github com opencv opencv tree 3 4 2 github下载高级扩展包 https github com opencv opencv contrib tree 3 4
  • 关于 IAR 环境的一些使用 琐记

    虽然对于IAR这个环境很不喜欢 但为了搞CC2530 不得不用 把碰到的一些点滴记录下来 1 Options gt C C Compiler gt Preprocessor gt Defined symbols one per line 的
  • 群晖NAS的公网、NAT、DDNS、证书等配置二

    一 公网IP DNSPOD域名解析 光猫桥接 路由器端口转发 DDNS解析 WebDAV实践 1 申请域名 去腾讯云申请一个域名 买个便宜的 好像是3年一百多块 腾讯云 产业智变 云启未来 2 申请公网IP 打电话给电信10000 转人工服
  • Libevent3——bufferevent上创建socket通信的服务器端、客户端的流程、及服务器端、客户端的代码实现分析⛽

    承接上文 服务器监听流程简要如下 创建监听器 有客户端连上时就是满足条件的时候 监听器的回调函数被调用 回调函数会创建新的用于通信的文件描述符fd 那么fd是需要被封装到一个bufferevent对象中 所以再创建一个bev对象对fd进行封
  • 如何修复ssh漏洞进行版本升级

    目录 一 ssh低版本漏洞信息 OpenSSH GSSAPI 处理远端代码执行漏洞 OpenSSH GSSAPI认证终止信息泄露漏洞 OpenSSH X连接会话劫持漏洞 二 升级ssh版本进行修复漏洞 第一步 安装Telnet服务 第二步
  • 轨迹数据挖掘(trajectory data mining)

    位置采集和移动计算技术的进步已经产生了大量的空间轨迹数据 这些数据代表了移动物体 如人 车辆和动物 的移动性 在过去十年中 已经提出了许多技术来处理 管理和挖掘轨迹数据 促进了广泛的应用 在本文中 我们对轨迹数据挖掘的主要研究进行了系统的调
  • 计算机键盘上删除,电脑键盘删除键是哪一个

    以win10 华为MateBook X为例 计算机键盘上 有两个具有删除功能的按键 分别是BackSpace键 退格键 和Delete键 删除键 BackSpace键只有1个 在主键盘区车键上方 有些键盘标注为 符号 在文本编辑状态 按下该
  • vue使用Teleport组件封装弹窗

    先看效果吧 使用弹窗组件文件 代码如下
  • CSDN问答

    近期AI成为热点话题 ChatGPT GPT4 new bing Bard AI 绘画 AI 编程工具引发大量讨论 请结合自身学习经历 一起来聊聊你对 AI 技术以及其今后发展的看法吧 请在下面的问题中选择一些来回答 1 你人生中第一次接触
  • Python编程:计算一元二次方程(用定义类的方法实现)

    Python编程 计算一元二次方程 题目 为一元二次方程ax 2 bx c 0设计一个类名为Equation的类 这个类包括 代表3个系数的成员变量a b c 一个名为getDiscriminant 的方法返回判别式的值 一个名为getRo
  • STM32-modbus rtu 之主机程序

    一 STM32串口的发送与接收 考虑到modbus的使用场合大多为半双工而非全双工 所以 串口接收采用DMA 空闲中断 发送则直接发送 include serial h include string h serialbuf st seria