STC32G12K128单片机的 moubus-rtu 主机测试工程

news2024/11/24 23:09:05

简介

STC32G12K128 是STC 推出的一款32位的 C251 的单片机。最近拿到一块官方申请的 屠龙刀-STC32G开发板,就用它的提供的库函数,查考安富莱提供的 modbus 例程移植了一个 modbus-rtu 主站的工程。

modbus-rtu host 移植注意点

  1. modbus-rtu 功能配置
  • 配置 modbus-rtu 使能主机还是从机,亦或是全部使能
  • 配置主机或者从机使用的串口、波特率、打印调试信息
    在这里插入图片描述
  • 主机要读取的从机地址通过全局变量 g_SlaveAddr 设置。
    在这里插入图片描述
  1. 初始化 modbus-rtu 主站使用到的串口和定时器
  • modbus-rtu 没有开始和结束符,通过3.5个字符的时间间隔来断帧,所以此处初始化一个定时器4来计算3.5个字符的时间用于断帧。注意:此处使用定时器不要和对应串口波特率产生的定时器冲突
  • 初始化对应的串口4
void MODH_PeripheralInit(void)
{
    TIM_InitTypeDef		TIM_InitStructure = {0};
    GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
    COMx_InitDefine		COMx_InitStructure;					//结构定义

    /* 硬件定时器初始化 */
    /*
    	3.5个字符的时间间隔,只是用在RTU模式下面,因为RTU模式没有开始符和结束符,
    	两个数据包之间只能靠时间间隔来区分,Modbus定义在不同的波特率下,间隔时间是不一样的,
    	所以就是3.5个字符的时间,波特率高,这个时间间隔就小,波特率低,这个时间间隔相应就大

    	4800  = 7.297ms
    	9600  = 3.646ms
    	19200  = 1.771ms
    	38400  = 0.885ms
    */
    uint32_t timeout = 0;

    //timeout = 35000000 / MODBUS_SLAVE_BAUD;	/* 计算超时时间,单位us 35000000*/
    /* 此处直接将定时器的初始值赋值,具体计算的公式参考注释,此处默认使用9600的波特率/主频22.1184MHZ,且定时器使用12T模式 */
    uiTimerAutoLoadVal = 63558;//(65536UL - ((12000000*3646) / MAIN_Fosc));//3646=35000000 / 9600

    /* 使用硬件定时器4  如果有冲突,请改修此处定时器 */
    TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_12T;	//指定时钟源,     TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
    TIM_InitStructure.TIM_ClkOut    = DISABLE;					//是否输出高速脉冲, ENABLE或DISABLE
    TIM_InitStructure.TIM_Value     = uiTimerAutoLoadVal;		//初值 因为上面12分频了  所以是 12*1000000
    TIM_InitStructure.TIM_Run       = DISABLE;					//是否初始化后启动定时器, ENABLE或DISABLE
    Timer_Inilize(Timer4, &TIM_InitStructure);					//初始化Timer4	  Timer0,Timer1,Timer2,Timer3,Timer4
    NVIC_Timer4_Init(ENABLE, NULL);		//中断使能, ENABLE/DISABLE; 无优先级


    /* 初始化串口 */
    GPIO_InitStructure.Pin  = GPIO_Pin_2 | GPIO_Pin_3;		//指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7
    GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);	//初始化

    COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;		//模式,   UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
    COMx_InitStructure.UART_BRT_Use   = BRT_Timer2;			//使用波特率,   BRT_Timer2, BRT_Timer4 (注意: 串口2固定使用BRT_Timer2)
    COMx_InitStructure.UART_BaudRate  = 9600ul;			//波特率,     110 ~ 115200
    COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLE
    UART_Configuration(UART4, &COMx_InitStructure);		//初始化串口4 UART1,UART2,UART3,UART4
    NVIC_UART4_Init(ENABLE, Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3

    UART4_SW(UART4_SW_P02_P03);		//UART4_SW_P02_P03,UART4_SW_P52_P53

}
  1. 在定时器的中断函数中添加 modbus-rtu 主机 3.5 个字符超时处理函数
//========================================================================
// 函数: Timer4_ISR_Handler
// 描述: Timer4中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-23
//========================================================================
void Timer4_ISR_Handler (void) interrupt TMR4_VECTOR		//进中断时已经清除标志
{
#if ( MODBUS_CFG_SLAVE_EN == 1 )
    MODS_RxTimeOut();    /* Modbus从站超时处理 */
#endif
#if ( MODBUS_CFG_HOST_EN == 1 )
    MODH_RxTimeOut();    /* Modbus主站超时处理 */
#endif
	// TODO: 在此处添加用户代码
	//P63 = ~P63;
}
  1. 在串口的接口中断函数中添加 modbus-rtu 主机接收字节的处理函数
//========================================================================
// 函数: UART4_ISR_Handler
// 描述: UART4中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2020-09-23
//========================================================================
#ifdef UART4
void UART4_ISR_Handler(void) interrupt UART4_VECTOR
{
    if(S4RI)
    {
        CLR_RI4();
#if ( MODBUS_CFG_SLAVE_EN == 1 )
        MODS_ReciveNew(S4BUF);
#elif ( MODBUS_CFG_HOST_EN == 1 )
        MODH_ReciveNew(S4BUF);
#else
        if(COM4.RX_Cnt >= COM_RX4_Lenth)	COM4.RX_Cnt = 0;
        RX4_Buffer[COM4.RX_Cnt++] = S4BUF;
        COM4.RX_TimeOut = TimeOutSet4;
#endif
    }

    if(S4TI)
    {
        CLR_TI4();

#if(UART_QUEUE_MODE == 1)   //判断是否使用队列模式
        if(COM4.TX_send != COM4.TX_write)
        {
            S4BUF = TX4_Buffer[COM4.TX_send];
            if(++COM4.TX_send >= COM_TX4_Lenth)		COM4.TX_send = 0;
        }
        else	COM4.B_TX_busy = 0;
#else
        COM4.B_TX_busy = 0;     //使用阻塞方式发送直接清除繁忙标志
#endif
    }
}
  1. 在 main() 函数的大循环之前调用 MODS_PeripheralInit() 以初始化使用到的相关硬件;然后在死循环里一直调用 MODS_Poll() 解析 modbus-rtu 从机协议,并且可以定时通过API接口读取或者写入从机某个寄存器地址的值。
    例程例程里,每隔 2s 往 0x301 寄存器写入一个自增 1 的值,然后下一秒从该寄存器中再读取,结果通过串口助手打印可以查看。
/* modbus主机测试任务,每1s执行一次该函数 */
void app_ModbusHostTask(void)
{
    static uint8_t wrflag = 0;
    static uint16_t regVal = 0;
    
    if(wrflag == 0)
    {
        MODH_WriteParam_06H(REG_P01, regVal);	//往寄存器 ERG_P01 寄存器中写入 regVal
        printf("modbus host write reg[%hu] value:%hu\n",REG_P01,regVal);
        regVal++; //写完一次后自增
    }
    else
    {
        MODH_ReadParam_03H(REG_P01,1);	//从寄存器 ERG_P01 中读取
        printf("modbus host read reg[%hu] value:%hu\n",REG_P01,g_tHostVar.P01);
    }
    wrflag = !wrflag;
}
  1. 可以通过 modbus_host.h 文件中的宏定义对 modbus-rtu 的功能进行裁剪,可以禁用不需要使用的功能,以解决空间。
//-----------------------------------------------------------------------------------------//
#define MODBUS_HOST_RTU_01H_FUNCTION DISABLE
#define MODBUS_HOST_RTU_02H_FUNCTION DISABLE
#define MODBUS_HOST_RTU_03H_FUNCTION ENABLE
#define MODBUS_HOST_RTU_04H_FUNCTION DISABLE
#define MODBUS_HOST_RTU_05H_FUNCTION DISABLE
#define MODBUS_HOST_RTU_06H_FUNCTION ENABLE
#define MODBUS_HOST_RTU_10H_FUNCTION ENABLE

测试

  1. 先将工程源码编译生成后的hex烧录到开发板中
  2. 再使用 USB转TTL 连接 STC32G12K28 开发板的串口4(P02-RXD P03-TXD)
  3. 电脑端使用 modbus 从机的模拟软件(此处使用 modbusslave )
  • 根据工程源码里设置的保存寄存器的参数,设置 modbusslave 软件里,从机的地址,被读取的寄存器地址和寄存器数量
    在这里插入图片描述
  • 设置串口和波特率
    在这里插入图片描述
  • 通过 modbusslave 窗口和单片机的串口打印日志观察现象
    在这里插入图片描述

工程源码

工程源码可以在此处下载(无需积分);如果觉得文章对你有帮忙,请关注点赞!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MapReduce序列化【用户流量使用统计】

目录 什么是序列化和反序列化? 序列化 反序列化 为什么要序列化? 序列化的主要应用场景 MapReduce实现序列化 自定义bean对象实现Writable接口 1.实现Writable接口 2.无参构造 3.重写序列化方法 4.重写反序列化方法 5.顺序一致 6.重写toStri…

您应该查看的5个ChatGPT WordPress插件

要创建免费网站?从易服客建站平台免费开始 500M免费空间,可升级为20GB电子商务网站 您应该查看的5个ChatGPT WordPress插件 发布于 2023年4月1日 ChatGPT 席卷了数字世界。作为内容创建者或营销者,您可能希望通过在您的网站上使用ChatGPT…

JimuReport - 积木报表(一款免费Web报表工具)

一款免费的数据可视化报表,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完成报表设计。…

扫雷【C语言】

用C语言实现一个9X9的扫雷 test.c 测试部分 game.c 游戏实现部分 game.h 游戏声明部分 菜单部分 游戏部分 游戏部分包括创建一个扫雷的区域,在其中埋雷,玩家进行扫雷,判断扫雷是否成功 这里我i们定义行列,便于以后将其…

2023.05.12-使用Transformers Agents来一键调用千万个AI模型

1. 简介 简单来说,就是以前想要实现某一个AI功能,需要自己去网上搜索对应的模型、下载对应的权重才能使用。现在可以把中间的这些个环节都砍了,我们只需要告诉模型我们想要对某段文字或者某张图片进行什么操作,transformer就会自…

【经验贴】项目风险管理的有效方法

你遇见过“最奇葩”的项目风险是什么? 中级项目经理小李:我比较幸运? 项目一开始发现客户的需求不太明确,就识别出可能会有范围无限蔓延的风险,制定了一系列的应对措施,不出所料出了问题,最终还是将风险遏…

springboot整合redis,MongoDB,Elasticsearch(ES)

目录 springboot整合redis 连接Redis 字符串操作 哈希表操作 列表操作 集合操作 有序集合操作 lettcus与jedis的区别 springboot整合MongoDB 新增数据 查询数据 更新数据 删除数据 springboot整合Elasticsearch(ES) 创建ElasticsearchRepo…

区间预测 | MATLAB实现QRCNN-BiLSTM卷积双向长短期记忆神经网络分位数回归时间序列区间预测

区间预测 | MATLAB实现QRCNN-BiLSTM卷积双向长短期记忆神经网络分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRCNN-BiLSTM卷积双向长短期记忆神经网络分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于QRCNN…

Redis数据热迁移

1、redis数据热迁移,先通过redis-shake工具实现。 2、再通过redis-full-check检查迁移后的数据是否想同。 3、redis-shake版本下载: https://github.com/tair-opensource/RedisShake/releases 4、redis-full-check版本下载: https://github.c…

更简单的存和取bean

注解(更简单存bean和取bean)更简单的存bean(使用类注解)方法注解更简单的获取bean 注解(更简单存bean和取bean) 注解:声明和表示这个类或方法有某个能力。servlet的Web注解;实现路由…

2000多套微信小程序源码-史上最全的不同行业的源码集合

前言 提示:这里可以添加本文要记录的大概内容: 很多伙伴学习小程序不知怎么开始,我准备了2000多套小程序源码,基本覆盖各个行业,大家有需要的可以借鉴学习~ 提示:以下是本篇文章正文内容,下面…

“数字人交互,与虚拟的自己互动”——用PaddleAvatar打造数字分身,探索人机交互的未来

“数字人交互,与虚拟的自己互动”——用PaddleAvatar打造数字分身,探索人机交互的未来 你是否曾经幻想过与自己的虚拟人交互?现在,使用PaddleAvatar,您可以将自己的图像、音频和视频转化为一个逼真的数字人视频&#…

Spring Cloud Alibaba--Nacos集群配置

文章目录 一、Nacos持久化配置二、Nacos集群配置三、避坑指南集群端口号冲突问题内存不足,无法启动的问题 一、Nacos持久化配置 Nacos默认自带的是嵌入式数据库derby,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。 …

Goby 漏洞更新 |secnet-智能路由系统 actpt_5g.data 信息泄露

漏洞名称:secnet-智能路由系统 actpt_5g.data 信息泄露 English Name:secnet Intelligent Router actpt_5g.data Infoleakage CVSS core: 7.5 影响资产数:71768 漏洞描述: secnet安网智能AC管理系统是广州安网通信技术有限公…

mysql错误:2059 - Authentication plugin ‘caching_ sha2_password‘ cannot be loaded:

这个错误是因为MySQL数据库使用了 caching_sha2_password 插件进行身份验证,而该插件需要 MySQL 8.0.4 及以上版本的 MySQL 客户端才能够使用。 如果你使用的是旧版本的 MySQL 客户端,可以考虑升级到 MySQL 8.0.4 或更高版本。如果升级不是一个可行的选…

Windows重启mysql的方法(快速简单)

目录 一、背景 二、操作步骤 错误做法 正确做法 一、背景 有时候修改了数据库,但是MySQL数据库内容有延迟缓存,那么就需要重启一下数据库去解决问题 二、操作步骤 错误做法 直接去cmd命令里面输入net stop mysql这样停止,这样很可能…

【C++初阶】C++模版(进阶)

文章目录 前言一、非类型模板参数二、模板的特化概念函数模板特化类模板特化1.全特化3.偏特化 三、模板分离编译什么是分离编译模板的分离编译解决方法 模板总结 前言 前边我们讲解了模版初阶的内容,对泛型编程,函数模版,类模板有了一定的认…

上海亚商投顾:沪指失守3300点关口 AI应用方向大幅调整

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 市场情绪 三大指数今日集体调整,尾盘均跌超1%,沪指失守3300点关口,日线录得4连阴走势。游…

IS210AEPSG1AFC磁场由串联励磁绕组和并联励磁的组合提供

​ IS210AEPSG1AFC磁场由串联励磁绕组和并联励磁的组合提供 复合发生器 在这种类型中,磁场由串联励磁绕组和并联励磁的组合提供,并联磁场有许多圈数的导线,但它只承载小电流,而串联励磁绕组有几圈粗线并承载负载电流 串联励磁绕组…

【JAVAEE】线程池基础知识⭐

目录 1.什么是线程池 2.为什么要使用线程池 3.怎么使用线程池 4.自定义一个线程池 5.为什么不推荐使用系统自带的线程池 5.1线程池构造方法的参数和含义 5.1.1拒绝策略 5.2线程池的工作原理 5.3为什么不适用系统自带的线程池 补充:工厂模式 1.什么是线程池…