移植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

发布了9 篇原创文章 · 获赞 4 · 访问量 867

猜你喜欢

转载自blog.csdn.net/Davidysw/article/details/103417373