侧边栏壁纸
博主头像
李振洋的博客博主等级

歌颂动荡的青春

  • 累计撰写 38 篇文章
  • 累计创建 6 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

基于STM32实现MODBUS-RTU从机协议

基于STM32实现MODBUS-RTU从机协议

准备材料

STM32CubeMX软件(Version 6.10.0

Keil µVision5 IDE(MDK-Arm

合宙DAP下载器 DAPLink使用手册 - LuatOS 文档

Modbus Poll Modbus Master Simulator (modbustools.com)

STM32F103

任务

实现Modbus-RTU从机协议

配置工程

使用STM32CubeMX软件配置工程

串口1

Stop Bits 设置为 2

STM32-MODBUS-INIT.png

定时器 TIM2

STM32-MODBUS-TIM2.png

代码


/**
  * @brief  解析数据包. 轮流调用
  * @param  None
  * @retval None
  */

void MODS_Poll(void)
{
	uint16_t addr;
	uint16_t crc1;
	
	// 超过3.5个字符时间后执行MODH_RxTimeOut()函数。
	// 全局变量 g_rtu_timeout = 1; 通知主程序开始解码 
	if (g_mods_timeout == 0)	
	{
		/* 没有超时,继续接收。 */
		return;								
	}
	/* 清标志 */
	g_mods_timeout = 0;	 					
	/* 接收到的数据小于4个字节就认为错误,地址(8bit)+指令(8bit)+操作寄存器(16bit) */
	if (g_tModS.RxCount < 4)				
	{
		goto err_ret;
	}
	/* 计算CRC校验 */
	crc1 = CRC16_Modbus(g_tModS.RxBuf, g_tModS.RxCount);
	if (crc1 != 0)
	{
		printf("%c", 'c');
		goto err_ret;
	}
	/* 从站地址 (1字节) */
	/* 第1字节 站号 */
	addr = g_tModS.RxBuf[0];
	/* 判断主机发送的命令地址是否符合 */				
	if (addr != SADDR485)		 			
	{
		goto err_ret;
	}
	/* 分析应用层协议 */
	MODS_AnalyzeApp();						
	
err_ret:
	{
		g_tModS.RxCount = 0;		
	}
}

/**
  * @brief  中断接收数据
  * @param  _byte 接收的数据
  * @retval None
  */

void MODS_ReciveNew(uint8_t _byte)
{
	uint8_t i;
	
	/* 根据波特率,获取需要延迟的时间 */
	for(i = 0; i < (sizeof(ModbusBaudRate)/sizeof(ModbusBaudRate[0])); i++)
	{
		if(SBAUD485 == ModbusBaudRate[i].Bps)
		{
			break;
		}	
	}

	g_mods_timeout = 0;
	/* 硬件定时中断,3.5个字符的时间间隔*/
	__HAL_TIM_SET_AUTORELOAD(&htim2, ModbusBaudRate[i].usTimeOut-1);
	__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
	HAL_TIM_Base_Start_IT(&htim2);
	
	if (g_tModS.RxCount < S_RX_BUF_SIZE)
	{
		g_tModS.RxBuf[g_tModS.RxCount++] = _byte;
	}
}


/**
  * @brief  接收数据帧超时
  * @param  _byte 接收的数据
  * @retval None
  */
void MODS_RxTimeOut()
{
	g_mods_timeout = 1;
}

/**
  * @brief  发送数据, 自动追加2字节CRC
  * @param  _pBuf 待发送的数据包
  * @param  _ucLen待发送的数据包长度
  * @retval None
  */
static void MODS_SendWithCRC(uint8_t *_pBuf, uint8_t _ucLen)
{
	uint16_t crc;
	uint8_t buf[S_TX_BUF_SIZE];

	memcpy(buf, _pBuf, _ucLen);
	crc = CRC16_Modbus(_pBuf, _ucLen);
	buf[_ucLen++] = crc >> 8;
	buf[_ucLen++] = crc;

	HAL_UART_Transmit(&huart1, buf, _ucLen, 100);
}

/**
  * @brief  发送错误应答
  * @param  _ucErrCode 错误码
  * @retval None
  */
static void MODS_SendAckErr(uint8_t _ucErrCode)
{
	uint8_t txbuf[3];

	txbuf[0] = g_tModS.RxBuf[0];					/* 485地址 */
	txbuf[1] = g_tModS.RxBuf[1] | 0x80;				/* 异常的功能码 */
	txbuf[2] = _ucErrCode;							/* 错误代码(01,02,03,04) */

	MODS_SendWithCRC(txbuf, 3);
}

/**
  * @brief  发送正确的应答
  * @param  None
  * @retval None
  */
static void MODS_SendAckOk(void)
{
	uint8_t txbuf[6];
	uint8_t i;

	for (i = 0; i < 6; i++)
	{
		txbuf[i] = g_tModS.RxBuf[i];
	}
	MODS_SendWithCRC(txbuf, 6);
}

/**
  * @brief  分析应用层协议
  * @param  None
  * @retval None
  */
static void MODS_AnalyzeApp(void)
{
	switch (g_tModS.RxBuf[1])				/* 第2个字节 功能码 */
	{
		case 0x01:							/* 读取线圈状态 */
			MODS_01H();
			break;

		case 0x02:							/* 读取输入状态 */
			MODS_02H();
			break;
		
		case 0x03:							/* 读取保持寄存器 */
			MODS_03H();
			break;
		
		case 0x04:							/* 读取输入寄存器 */
			MODS_04H();
			break;
		
		case 0x05:							/* 强制单线圈 */
			MODS_05H();
			break;
		
		case 0x06:							/* 写单个保存寄存器 */
			MODS_06H();	
			break;
			
		case 0x10:							/* 写多个保存寄存器 */
			MODS_10H();
			break;
		
		default:
			g_tModS.RspCode = RSP_ERR_CMD;
			MODS_SendAckErr(g_tModS.RspCode);	/* 告诉主机命令错误 */
			break;
	}
}

一些代码不想展示😏

验证

MODBUS-POLL.png

0

评论区