移植modbus rtu从机到STM32

·
近期看了硬石电子的关于modbus rtu代码,写的很亲民,便于理解,在这里做一下笔记,方便以后查看。

一、移植步骤

一共需要移植
两个.c文件:bsp_usartx_fifo.c、modbus_slave.c
两个.h文件:bsp_usartx_fifo.h、modbus_slave.h

放置两个函数:Usart_FIFO_Init(); MODS_Poll();

开启三个宏:#define USART1_FIFO_EN 1//使能初始化串口1
__________ #define USART_SELECT_NUM 1//将modbus用在串口1
__________ #define HBAUD485 USART1_BAUD//设置串口波特率

1、串口初始化、定时器初始化

在单片机上使用modbus rtu,肯定是通过串口UART进行通讯(485的话就是多控制一个引脚的高低电平变化),那第一步便是进行串口的初始化,modbus rtu协议帧是对时间严格把控的,那怎么能少的了定时器,紧跟着的便是定时器初始化。

(1)将Usart_FIFO_Init()放在主函数的初始化位置,

#include "stm32f10x.h"
#include "bsp_usartx_fifo.h"
#include "modbus_slave.h"
/*主函数*/
int main(void)
{
  	Usart_FIFO_Init();  
	while ( 1 )
	{	 		
	 	MODS_Poll();	
	}
}

(2)进入Usart_FIFO_Init(),看看有哪些需要初始化:

void Usart_FIFO_Init(void)
{ 
  /* 初始化串口相关的变量 */
  UsartVarInit();
  /* 初始化串口相关的变量 */
  RS485_InitTXEN();
  /* 配置NVIC,设定USART接收中断优先级 */
  NVIC_Configuration_USART();
  /* 初始化USART对应GPIO和USART外设 */
  InitHardUsart();  
	
 /* 定时器初始化 */
  bsp_InitHardTimer();	
}

a.首先UsartVarInit()的作用是对串口相关的结构体变量初始化,其中需要注意的是宏定义USART1_FIFO_EN的开关,这里我使用UART1做的测试,将该宏置1来开启串口1,然后#define USART_SELECT_NUM 1//将modbus用在串口1上;这里分为两部分,为的是方便对每个串口的单独配置,可以对其他串口添加需要的应用代码。

static void UsartVarInit(void)
{
#if USART1_FIFO_EN == 1
	g_tUsart1.usart = USART1;						/* STM32 串口设备 */
	g_tUsart1.pTxBuf = g_TxBuf1;					/* 发送缓冲区指针 */
	g_tUsart1.pRxBuf = g_RxBuf1;					/* 接收缓冲区指针 */
	g_tUsart1.usTxBufSize = USART1_TX_BUF_SIZE;	    /* 发送缓冲区大小 */
	g_tUsart1.usRxBufSize = USART1_RX_BUF_SIZE;	    /* 接收缓冲区大小 */
	g_tUsart1.usTxWrite = 0;			          	/* 发送FIFO写索引 */
	g_tUsart1.usTxRead = 0;						    /* 发送FIFO读索引 */
	g_tUsart1.usRxWrite = 0;						/* 接收FIFO写索引 */
	g_tUsart1.usRxRead = 0;						    /* 接收FIFO读索引 */
	g_tUsart1.usRxCount = 0;						/* 接收到的新数据个数 */
	g_tUsart1.usTxCount = 0;						/* 待发送的数据个数 */
	g_tUsart1.SendBefor = RS485_SendBefor;			/* 发送数据前的回调函数 */
	g_tUsart1.SendOver = RS485_SendOver;			/* 发送完毕后的回调函数 */
	g_tUsart1.ReciveNew = RS485_ReciveNew;			/* 接收到新数据后的回调函数 */
#endif
...
}

b.其次是对串口驱动的配置,初始化了485的引脚,配置中断优先级,配置串口引脚波特率等。
RS485_InitTXEN();
NVIC_Configuration_USART();
InitHardUsart();
c.为简化文件的个数,没有建立tim.c,直接将定时器初始化放在了串口初始化的末尾处bsp_InitHardTimer()。分频系数72-1,时钟周期1us。

2、轮循MODS_Poll()

将MODS_Poll()放在主函数的while(1)中;

/*主函数*/
int main(void)
{
  	Usart_FIFO_Init();  
	while ( 1 )
	{	 		
	 	MODS_Poll();	
	}
}

3、添加寄存器

使用modbus从机的最终目的是响应来自主机的命令,主机命令都是按照功能码区别的,因此只需要编写编写对应的应用函数,添加所需要的寄存器地址及数值便可,这里以03 06 10功能码为例,分别编写读寄存器,写寄存器函数,添加寄存器的数量只需要添加case SLAVE_REG_P01…的宏定义,同时添加对应的结构体变量 uint16_t P01…,结构体变量只需要在需要的地方赋值即可。

/*
*********************************************************************************************************
*	函 数 名: MODS_ReadRegValue
*	功能说明: 读取保持寄存器的值
*	形    参: reg_addr 寄存器地址
*			  reg_value 存放寄存器结果
*	返 回 值: 1表示OK 0表示错误
*********************************************************************************************************
*/
static uint8_t MODS_ReadRegValue(uint16_t reg_addr, uint8_t *reg_value)
{
	uint16_t value;
	
	switch (reg_addr)									/* 判断寄存器地址 */
	{
		case SLAVE_REG_P01:
			value =	g_tVar.P01;	
			break;
		case SLAVE_REG_P02:
			value =	g_tVar.P02;							/* 将寄存器值读出 */
			break;
	  case SLAVE_REG_P03:
			value =	g_tVar.P03;
		break;
		default:
			return 0;									/* 读取寄存器超界,参数异常,返回 0 */
	}

	reg_value[0] = value >> 8;
	reg_value[1] = value;

	return 1;											/* 读取成功 */
}

/*
*********************************************************************************************************
*	函 数 名: MODS_WriteRegValue
*	功能说明: 读取保持寄存器的值
*	形    参: reg_addr 寄存器地址
*			  reg_value 寄存器值
*	返 回 值: 1表示OK 0表示错误
*********************************************************************************************************
*/
static uint8_t MODS_WriteRegValue(uint16_t reg_addr, uint16_t reg_value)
{
	switch (reg_addr)							/* 判断寄存器地址 */
	{	
		case SLAVE_REG_P01:
			g_tVar.P01 = reg_value;				/* 将值写入保存寄存器 */
			break;
		
		case SLAVE_REG_P02:
			g_tVar.P02 = reg_value;				/* 将值写入保存寄存器 */
			break;
		case SLAVE_REG_P03:
			g_tVar.P03 = reg_value;
		break;
		default:
			return 0;		/* 参数异常,返回 0 */
	}

	return 1;		/* 读取成功 */
}

/* 03H 读保持寄存器(内部寄存器) */
/* 06H 写保持寄存器(内部寄存器) */
/* 10H 写多个保存寄存器(内部寄存器) */
#define SLAVE_REG_P01		0x0301
#define SLAVE_REG_P02		0x0302
#define SLAVE_REG_P03		0x0303

typedef struct
{
	/* 03H 06H 读写保持寄存器 */
	uint16_t P01;
	uint16_t P02;
	uint16_t P03;
	/* 04H 读取模拟量寄存器 */
	uint16_t A01;

	/* 01H 05H 读写单个强制线圈 */
	uint16_t D01;
	uint16_t D02;
	uint16_t D03;
	uint16_t D04;

}VAR_T;

二、代码

链接:https://pan.baidu.com/s/1XTeeJrtT3iE9qAbe7pIrlQ
提取码:yja1

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32是一款功能强大的微控制器系列,它的硬件资源和丰富的开发工具使得移植Modbus RTU相对简单。Modbus RTU是一种常用的串行通信协议,用于在工业自动化系统中传输数据。下面就是移植Modbus RTUSTM32的步骤: 1. 确定硬件资源:在开始移植之前,需要确定STM32芯片上的串口资源。Modbus RTU通常使用RS485或RS232串口进行通信。选择合适的串口资源,并确保它们的引脚分配和外设配置正确。 2. 导入Modbus库文件:在STM32项目中,导入适用于Modbus RTU协议的库文件。例如可以使用开源的modbus库,这个库提供了Modbus RTU的通信协议和相关函数,方便我们在STM32上实现Modbus通信。 3. 配置串口:在初始化过程中,配置串口的参数,例如波特率、数据位、停止位和校验位等。这些参数需要与Modbus RTU设备的通信参数相匹配。 4. 实现Modbus协议:根据Modbus RTU协议的规范,实现相关的通信函数。这些函数通常包括发送和接收数据的操作。在STM32上,可以利用串口中断来处理Modbus RTU通信。 5. 实现Modbus功能码:根据自己的需求,实现Modbus RTU的功能码,例如读取和写入保持寄存器、读取和写入输入寄存器等。这些功能码需要根据Modbus RTU设备的协议进行实现。 6. 编写应用程序:根据具体的应用场景,编写应用程序来操作Modbus设备。例如,读取传感器数据或者控制执行器等。 移植Modbus RTUSTM32的过程中,需要充分理解Modbus RTU协议的规范和要求,并根据具体情况进行相应的配置和程序开发。合理利用STM32的硬件资源和开发工具,可以快速实现Modbus RTU通信功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值