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

news/2024/10/18 7:48:19/

简介

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

modbus-rtu slave 移植注意点

  1. modbus-rtu 功能配置
  • 配置 modbus-rtu 使能主机还是从机,亦或是全部使能
  • 配置主机或者从机使用的串口、波特率、从机地址、打印调试信息
    在这里插入图片描述
  1. 初始化 modbus-rtu 从站使用到的串口和定时器
  • modbus-rtu 没有开始和结束符,通过3.5个字符的时间间隔来断帧,所以此处初始化一个定时器4来计算3.5个字符的时间用于断帧。注意:此处使用定时器不要和对应串口波特率产生的定时器冲突
  • 初始化对应的串口4
void MODS_PeripheralInit(void)
{TIM_InitTypeDef		TIM_InitStructure = {0};GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义COMx_InitDefine		COMx_InitStructure;					//结构定义/* 硬件定时器初始化 *//*3.5个字符的时间间隔,只是用在RTU模式下面,因为RTU模式没有开始符和结束符,两个数据包之间只能靠时间间隔来区分,Modbus定义在不同的波特率下,间隔时间是不一样的,所以就是3.5个字符的时间,波特率高,这个时间间隔就小,波特率低,这个时间间隔相应就大4800  = 7.297ms9600  = 3.646ms19200  = 1.771ms38400  = 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  如果有冲突,请改修此处定时器 *///	//定时器4做16位自动重装, 中断频率为50HZ,中断函数从P6.3取反输出25HZ方波信号.TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_12T;	//指定时钟源,     TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_ExtTIM_InitStructure.TIM_ClkOut    = DISABLE;					//是否输出高速脉冲, ENABLE或DISABLETIM_InitStructure.TIM_Value     = uiTimerAutoLoadVal;		//初值 因为上面12分频了  所以是 12*1000000TIM_InitStructure.TIM_Run       = DISABLE;					//是否初始化后启动定时器, ENABLE或DISABLETimer_Inilize(Timer4, &TIM_InitStructure);					//初始化Timer4	  Timer0,Timer1,Timer2,Timer3,Timer4NVIC_Timer4_Init(ENABLE, NULL);		//中断使能, ENABLE/DISABLE; 无优先级/* 初始化 MODBUS 从机使用的串口 P02-RXD P03-TXD */GPIO_InitStructure.Pin  = GPIO_Pin_2 | GPIO_Pin_3;		//指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7GPIO_InitStructure.Mode = GPIO_PullUp;	                //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PPGPIO_Inilize(GPIO_P0, &GPIO_InitStructure);	            //初始化COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;		//模式,   UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use   = BRT_Timer2;			//使用波特率,   BRT_Timer2, BRT_Timer4 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate  = 9600ul;			    //波特率,     110 ~ 115200COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLEUART_Configuration(UART4, &COMx_InitStructure);		    //初始化串口4 UART1,UART2,UART3,UART4NVIC_UART4_Init(ENABLE, Priority_1);		            //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART4_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);
#elseif(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;
#elseCOM4.B_TX_busy = 0;     //使用阻塞方式发送直接清除繁忙标志
#endif}
}
  1. 在 main() 函数的大循环之前调用 MODS_PeripheralInit() 以初始化使用到的相关硬件;然后在死循环里一直调用 MODS_Poll() 解析 modbus-rtu 从机协议。
  2. 可以通过 modbus_slave.h 文件中的宏定义对 modbus-rtu 的功能进行裁剪,可以禁用不需要使用的功能,以解决空间。
//-----------------------------------------------------------------------------------------//
#define MODBUS_SLAVE_RTU_01H_FUNCTION DISABLE
#define MODBUS_SLAVE_RTU_02H_FUNCTION DISABLE
#define MODBUS_SLAVE_RTU_03H_FUNCTION ENABLE
#define MODBUS_SLAVE_RTU_04H_FUNCTION DISABLE
#define MODBUS_SLAVE_RTU_05H_FUNCTION DISABLE
#define MODBUS_SLAVE_RTU_06H_FUNCTION ENABLE
#define MODBUS_SLAVE_RTU_10H_FUNCTION ENABLE
/* 使能测试功能,实际使用可禁用 */
#define MODBUS_RTU_TEST ENABLE//DISABLE  ENABLE

工程源码

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

测试

  1. 先将工程源码编译生成后的hex烧录到开发板中
  2. 再使用 USB转TTL 连接 STC32G12K28 开发板的串口4(P02-RXD P03-TXD)
  3. 电脑端使用 modbus 主机的模拟软件(此处使用 modbuspoll )
  • 设置串口和波特率
    在这里插入图片描述

  • 根据工程源码里设置的保存寄存器的参数,设置 modbuspoll 软件里读取的地址

//-----------------------------------------------------------------------------------------//
#if (MODBUS_RTU_TEST == ENABLE)   //测试使用代码
/* 01H 读强制单线圈 */
/* 05H 写强制单线圈 */
#define REG_D01		0x0101
#define REG_D02		0x0102
#define REG_D03		0x0103
#define REG_D04		0x0104
#define REG_DXX 	REG_D04/* 02H 读取输入状态 */
#define REG_T01		0x0201
#define REG_T02		0x0202
#define REG_T03		0x0203
#define REG_TXX		REG_T03/* 03H 读保持寄存器 */
/* 06H 写保持寄存器 */
/* 10H 写多个保存寄存器 */
#define SLAVE_REG_P01		0x0301
#define SLAVE_REG_P02		0x0302/* 04H 读取输入寄存器(模拟信号) */
#define REG_A01		0x0401
#define REG_AXX		REG_A01
typedef struct
{/* 03H 06H 读写保持寄存器 */uint16_t P01;uint16_t P02;/* 04H 读取模拟量寄存器 */uint16_t A01;/* 01H 05H 读写单个强制线圈 */uint16_t D01;uint16_t D02;uint16_t D03;uint16_t D04;}SLAVE_VAR_T;
#endif

在这里插入图片描述

  • 单击对应保持寄存器的值,可以设置该寄存器的值
    在这里插入图片描述

http://www.ppmy.cn/news/59336.html

相关文章

【C++】 重载操作符

目录 概念 类内重载操作符 类外重载操作符 使用注意 对象类型转换 封装Iterator 概念 C提供的运算符,通常只支持对于基本数据类型和标准库中提供的类进行操作,对于自定义类型如果想通过操作符实现对应的操作,需要自定义重载的操作符并实…

Android中Paint字体的灵活使用

在Android开发中,Paint是一个非常重要的绘图工具,可以用于在控制台应用程序或Java GUI应用程序中绘制各种形状和图案。其中,Paint.setText()方法是用于设置Paint绘制的文本内容的。在Android开发中,如果你想要设置文本内容&#x…

红黑树理论详解与Java实现

文章目录 基本定义五大性质红黑树和2-3-4树的关系红黑树和2-3-4树各结点对应关系添加结点到红黑树注意事项添加的所有情况 添加导致不平衡叔父节点不是红色节点(祖父节点为红色)添加不平衡LL/RR添加不平衡LR/RL 叔父节点是红色节点(祖父节点为…

【HTML+CSS+JS】登录注册页面大合集

前言 学JS也学了一段时间,正巧碰上了人工智能要调用人脸识别接口进行真人人脸识别,于是便萌生了用人脸来进行注册和登录的想法,这样的话就需要开发一个登录注册页面,然后用JS绑定注册事件调用人脸识别接口进行登录注册 饭要一口一…

vue首屏白屏原因及解决办法

vue首屏白屏原因大概有以下几点: 一.路由模式错误(路由重复或者没有配置路由) (1)由于把路由模式mode设置成history了,默认是hash 解决方法:将模式改为hash模式,或者直接把模式配置删除,而且hi…

让GPT成为护理专家 - 护士的工作如此简单

引子    书接上文《GPT接入企微应用 - 让工作快乐起来》,我把GPT接入了企微应用,不少同事都开始尝试起来了。有的浅尝辄止,有的刨根问底,五花八门,无所不有。这里摘抄几份: “帮我写一份表白信&#xff…

{.....},正则表达式将{}和{}中的内容全部替换为1

解决办法:replaceAll("\\{.*?\\}", "1") 当在Java字符串中使用正则表达式时,需要注意转义字符的使用。因为在Java中某些字符本身就有特殊含义,例如 \、{、} 等等,如果直接使用这些字符来进行正则表达式匹配…

【水光互补优化调度】基于非支配排序遗传算法的多目标水光互补优化调度(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…