[STC32F12K54入门第三步]USART1+Modbus RTU从机

这篇具有很好参考价值的文章主要介绍了[STC32F12K54入门第三步]USART1+Modbus RTU从机。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


提示:以下是本篇文章正文内容,下面案例可供参考

一、Modbus RTU是什么?

想要了解的去看看我STM32高级篇的Modbus RTU的文章或者自己去网上看看。其实很简单。就是一些报文格式,然后解析格式。本文主要是驱动程序。注意博主是默认你会Modbus RTU协议的哈,注释里面会讲一些Modbus RTU的报文格式。

二、Modbus RTU程序展示

这是程序的流程图
[STC32F12K54入门第三步]USART1+Modbus RTU从机,STC32F12K54,单片机,嵌入式硬件

1.串口配置

这里我们使用USART1串口来配置
USART.h

#ifndef __USART_H
#define __USART_H
#include <STC32G.H>
#include <String.h>
#include "System.h"
//#define MAIN_Fosc     56000000UL  //定义主时钟
#define Baudrate      115200L
#define TM            (65536 -(MAIN_Fosc/Baudrate+2)/4)

extern u16 Modbus_timeOut;
extern unsigned char buff[1024];
extern char rx_len;

void Uart1_Init(void);
void Usart1_Send(unsigned char dat);
void Usart1_Send_Str(unsigned char* dat,unsigned short dat_len);

#endif

USART.c

#include "USART.h"
#include "stdio.h"
unsigned char buff[1024];
char rx_len;
u16 Modbus_timeOut;
int i=0;

/*函数名:Uart1_Init(void)
*功能:串口1初始化 波特率:115200
*形参:无
*返回值:无
*修改时间:2023/7/1
*作者:小夏
*/
void Uart1_Init(void){
	S1_S1=1;
	S1_S0=0;   //使用P1.6,P1.7
	SCON=0x40;
	REN=1;
	S1BRT =1;
	T2L  = TM;
	T2H  = TM>>8;
	AUXR |= 0x14;	//定时器2时钟1T模式,开始计时
	rx_len=0;
	//开启中断
	ES=1;   
	EA=1;
	
}
/*函数名:Usart1_Send(unsigned char dat)
*功能:串口一发送一个数据
*形参:dat char数据
*返回值:无
*修改时间:2023/7/1
*作者:小夏
*/
void Usart1_Send(unsigned char dat)
{
	SBUF=dat;
	while(TI==0);
	TI=0;
}
/*函数名:Usart1_Send_Str(unsigned char* dat,unsigned short dat_len)
*功能:串口一发送字符串数据
*形参:dat char数据    dat_len发送的数据长度
*返回值:无
*修改时间:2023/7/1
*作者:小夏
*/
void Usart1_Send_Str(unsigned char* dat,unsigned short dat_len)
{
	while(dat_len--){
		Usart1_Send(*dat++);
	}
}
/*函数名:USART_BackCall_IRQ(void) interrupt 4
*功能:串口一的中断处理函数
*形参:无
*返回值:无
*修改时间:2023/7/1
*作者:小夏
*/
void USART_BackCall_IRQ(void) interrupt 4
{
	
		if(RI){
			RI=0;
			if(rx_len<sizeof(buff)){
				buff[rx_len++]=SBUF;
				P21=!P21;
			}	
      Modbus_timeOut=20;			
		}
	
} 
/*函数名:putchar(char c)
*功能:串口一的串口重定向
*形参:无
*返回值:无
*修改时间:2023/7/1
*作者:小夏
*/
char putchar(char c)
{
	Usart1_Send(c);
	return c;
}

2.Timer定时器配置

Timer.h

#ifndef __Timer_H
#define __Timer_H
#include <STC32G.H>
#include <String.h>
#include "System.h"

void Timer_Init(void);
#endif

Timer.c

#include "Timer.h"
#include "USART.h"

u16 time;

/*函数名:TM0_Isr() interrupt 1
*功能:Timer0中断处理函数
*形参:无
*返回值:无
*修改时间:2023/7/1
*作者:小夏
*/
void TM0_Isr() interrupt 1
{
	time++;
	if(time>=1){
		if(Modbus_timeOut>1)Modbus_timeOut--;
		if(Modbus_timeOut>1)Modbus_timeOut--;
		if(Modbus_timeOut>1)Modbus_timeOut--;
		time=0;
	}
	
}

/*函数名:Timer_Init(u16 Per)
*功能:Timer0初始化 1ms让LED灯电平变换
*形参:无
*返回值:无
*修改时间:2023/6/56
*作者:小夏
*/
void Timer_Init(void){
		TMOD=0x00;
		TL0=0xCF; //1ms  由于我们使用的晶振是56mhz,所以1ms跳动5600次,这里是65535-5600. 
		TH0=0xFD;
		TR0=1;
		ET0=1;
		EA=1;	
}

3.配置CRC16校验位和Modbus RTU发送函数

CRC16.h

#ifndef _CRC16_H
#define _CRC16_H

#include <STC32G.H>
#include <String.h>
#include "System.h"

unsigned int GetCRC16(unsigned char *pPtr,unsigned char ucLen);	/* 获得CRC16校验值 */
unsigned char make_rs485_replay_str(unsigned short addr,unsigned short len,unsigned char* source,unsigned char* dest,unsigned char devicdID,unsigned char func);
#endif 

CRC16.c

/*************************************************************************************
文件名称:crc16.c
版    本:V1.0
日    期:2020-5-11
编    著:Eric Xie
说    明:CRC校验表
修改日志:



**************************************************************************************/
#include "crc16.h"

const unsigned char TabH[] = {  //CRC高位字节值表
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40  
    } ;  
const unsigned char TabL[] = {  //CRC低位字节值表
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,  
        0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,  
        0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,  
        0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,  
        0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,  
        0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,  
        0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,  
        0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,  
        0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,  
        0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,  
        0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,  
        0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,  
        0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,  
        0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,  
        0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,  
        0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,  
        0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,  
        0x43, 0x83, 0x41, 0x81, 0x80, 0x40  
    } ;

/*************************************************************************************
 * 函数说明: CRC16校验
 * 入口参数:u8 *ptr,u8 len
 * 出口参数:u16
 * 函数功能:根据入口参数数组的值计算crc16校验值 并返回
**************************************************************************************/
unsigned int GetCRC16(unsigned char *pPtr,unsigned char ucLen)
{ 
    unsigned int  uiIndex;
    unsigned char ucCrch = 0xFF;  		//高CRC字节
    unsigned char ucCrcl = 0xFF;  		//低CRC字节 
    while (ucLen --)  			//计算指定长度CRC
    {
        uiIndex = ucCrch ^ *pPtr++;
        ucCrch  = ucCrcl ^ TabH[uiIndex];
        ucCrcl  = TabL[uiIndex];
    }
    
    return ((ucCrch << 8) | ucCrcl);  
} 

/*函数名:unsigned char make_rs485_replay_str(unsigned short addr,unsigned short len,unsigned char* source,unsigned char* dest,unsigned char devicdID,unsigned char func)
*功能:串口1初始化 波特率:115200
*形参:unsigned short addr,  //读的起始地址
*unsigned short len,   //主机读的数据数量
*unsigned char* source, //读取到的主机的报文
*unsigned char* dest,   //上传到主机的buff包
*unsigned char devicdID,//从机地址
*unsigned char func     //功能码
*返回值:unsigned char 返回数据数量
*修改时间:2023/7/1
*作者:小夏
*/
unsigned char make_rs485_replay_str(unsigned short addr,unsigned short len,unsigned char* source,unsigned char* dest,unsigned char devicdID,unsigned char func)
{
	unsigned short crc;
	unsigned char i=0;
	unsigned char p=0;
	dest[0]=devicdID;
	dest[1]=func;
	//printf("dest[2]=0x%x\r\n",dest[1]);
	if(func==0x03)
	{
		dest[2]=len*2;
		p=3;
		for(i=0;i<len;i++)
		{
			if(addr+i>=0 && addr+i<64)
			{
				//printf("size=%d\r\n",sizeof(info.data)/2);
				dest[p]=0xFFAD>>8;
				dest[p+1]=0xFFAD;  
				//dest[p]=TEST>>8;	
				//dest[p+1]=TEST;				
			}
			else			
			{
				dest[p]=0;
				dest[p+1]=0;
			}
			p+=2;
		}
	}
	crc=GetCRC16(dest,p);
	dest[p]=crc>>8;
	dest[p+1]=crc;
	return p+2;
}	

4.主函数

main.c

#include <STC32G.H>
#include "Timer.h"
#include "System.h"
#include "USART.h"
#include "Timer.h"
#include "crc16.h"
#include "stdio.h"

u8 Modbus_begin[]="Modbus Begin\r\n";
unsigned char send[128];
unsigned short startAddr;
unsigned short len2;
unsigned short len;
unsigned short crc;
unsigned short crc2;
int main(void){
	GPIO_Init();
	Timer_Init();
	Uart1_Init();
	delay_ms(20);
	
	while(1){
		if(Modbus_timeOut==1)
			{
				Modbus_timeOut=0;
				
				if(rx_len>=5)
				{
					//printf("rx_len=%d\r\n",rx_len);
					crc =GetCRC16((unsigned char*)buff,rx_len-2);//计算接收数据的CRC16校验
					crc2=((buff[rx_len-2]<<8)+buff[rx_len-1]);//读取串口接收的数据的CRC校验位
					if(crc==crc2)
					{
						if(buff[0]==0x01) //判断modebus从机地址
						{
/*-------------------------------------------------------------------------------------
								Modbus RTU协议
协议报文     举例
(主机接收从
机数据)	    (PULL)Tx:01          03        00 00           00 03           05 CB
                   从机地址位  功能码     寄存器地址    提取的数据个数     CRC16校验位
			(Slave)Rx:01         03           06             00 01        00 02         00 03         FD 74
				  从机地址位    功能码   上报数据字节数     第一个数据    第二个数据    第三个数据    CRC16校验位
// 功能码0x03 保持读寄存器,就是主机会定时不断请求从机的命令					
-------------------------------------------------------------------------------------*/								
							if(buff[1]==0x03)//判读modebus功能地址 读功能码
							{
															 
								startAddr=(buff[2]<<8)+buff[3];//提取起始地址
								len2=(buff[4]<<8)+buff[5]; //提取结束地址
								len=make_rs485_replay_str(startAddr,len2,(unsigned char*)buff,send,buff[0],buff[1]);
								//dats[0]=usart_dat.szRx[2];
								if(len>0)
								{
								  Usart1_Send_Str(send,len);								
								}
							}
/*-------------------------------------------------------------------------------------
								Modbus RTU协议
协议报文     举例
(从机发送数 (PULL)Tx:01           06      00 00          00 02           08 0B
据到主机)        从机地址位    功能码   起始地址       结束地址        CRC16校验位
  			(Slave)Tx:01          06      00 00          00 02           08 0B
                  从机地址位     功能码   起始地址       结束地址       CRC16校验位
// 功能码0x06 写一个寄存器,就是主机写一个命令到从机						
-------------------------------------------------------------------------------------*/	
							
						else if(buff[1]==0x06)//写功能码 
						{
							unsigned short addr=(buff[2]<<8)+buff[3];
							unsigned short datas=(buff[4]<<8)+buff[5];
							//HAL_UART_Transmit_DMA(&huart1,(unsigned char*)buff,rx_len);
							 if(addr==0x00){
									if(datas==0x01){
											P23=!P23;
									}									
								}
					 }
					}
				}	
						rx_len=0;					
		 }	
		}
	}		
}	

	
		

5.效果展示

0x03:保持读线圈
[STC32F12K54入门第三步]USART1+Modbus RTU从机,STC32F12K54,单片机,嵌入式硬件
0x06:写一个线圈 写入 0x01改变P21的led电平
[STC32F12K54入门第三步]USART1+Modbus RTU从机,STC32F12K54,单片机,嵌入式硬件


总结

Modbus RTU就是这个样子。嘿嘿,下一篇会讲STC32F驱动ESP32获取时间等数据。文章来源地址https://www.toymoban.com/news/detail-526518.html

到了这里,关于[STC32F12K54入门第三步]USART1+Modbus RTU从机的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • stm32---串口接收与发送(针对USART1的Rx和Tx引脚)

    stm32---串口接收与发送(针对USART1的Rx和Tx引脚)

             

    2024年02月04日
    浏览(12)
  • STM32F103RCT6 -- 基于FreeRTOS 的USART1 串口通讯

    使用 FreeRTOS 提供的队列(Queue)机制来实现数据的接收和发送 TX - PA9 RX - PA10 波特率:9600 数据位:8bit 校验位:无 停止位:1bit 数据格式: RX: 55 AA 06 00 06 31 02 24 01 FC 80 TX: 55 AA 06 00 06 32 01 24 01 B8 70 55 AA – 帧头 06 - 数据字节数,不包括帧头,不包括校验位 00 06 – 模块 31 02 24

    2024年02月16日
    浏览(38)
  • 普中STM32-PZ6806L开发板(HAL库函数实现-USART1 printf+scanf/gets)

    普中STM32-PZ6806L开发板(HAL库函数实现-USART1 printf+scanf/gets)

    实现printf+scanf/gets通过USART1 的发送接收。 主芯片串口引脚图 我的板子板子自带串口坏掉了, 所以使用USB转TTL线, 连接如下 电路原理图 实物图 scanf是以空白符(空格、制表符、换行等等)为结束标志的,当遇到空白符是就会结束一次输入,如果你需要读取空格的话可以使用gets或者

    2024年02月04日
    浏览(8)
  • STM32开发之Modbus协议(主站RTU)

    在单片机方面,针对于通讯常用的协议之一modbus,这里将modbus协议和硬件之间的关系完全独立出来,硬件和协议之间的联系采用的是回调的方式进行一个关联。 1、此协议可直接移植,并不需要关心硬件相关的。 2、modbus相关协议概念自行查找,本文只做代码的实现。 宏定义(

    2024年02月12日
    浏览(11)
  • STM32开发之Modbus协议(RTU从站)

    说明 1、本文不做协议格式的讲解,只做实现,如需了解协议格式,自行搜索 2、本文不依赖于硬件相关的资源,建立在硬件通讯之上,通过回调的形式和对应的硬件进行关联 3、相关协议内容参照,上一篇RTU主站 宏定义(modbus_core_define) crc校验(modbus_core_crc) 头文件 源文件

    2024年02月11日
    浏览(8)
  • STM32CUBUMX配置RS485 modbus STM32(从机)亲测可用

    STM32CUBUMX配置RS485 modbus STM32(从机)亲测可用

    ———————————————————————————————————— ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子,使用STM32CUBEMX做了很多驱动,包括ADC、UART、RS485、EEPROM(IIC)、FLASH(SPI)、mod

    2024年02月14日
    浏览(11)
  • 基于STM32的MODBUS-RTU框架的实现

    基于STM32的MODBUS-RTU框架的实现

    ---------------------------------------------------------------------------------------手动分割线-------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------文章开始------------------------------------------------------------------------------

    2024年02月01日
    浏览(11)
  • APM32F072单片机进入STOP模式,并通过RTC Wakeup Timer和USART1串口接收事件唤醒

    串口初始化(注意USART1时钟源要选择HSI): 使用power_init函数初始化RTC,然后调用power_enter_stop_mode(n)函数进入STOP模式,n秒后自动唤醒,或由USART1接收唤醒:

    2024年02月13日
    浏览(11)
  • 关于STM32F407ZGT6的USB损坏后使用ST-Link和USART1实现串口功能

    关于STM32F407ZGT6的USB损坏后使用ST-Link和USART1实现串口功能

    开发板:STM32F407ZGT6; 目标:想使用软件“串口调试助手” 情况:开发板上的USB_UART口所在器件损坏或者直接没有;   解决办法:查看该开发板的原理图,可得:串口1的RX接TXD,串口1的TX接RXD,那么按如下步骤操作: 1、现在使用USB转TTL模块,将串口1的RX接USB转TTL模块的TXD,

    2024年02月08日
    浏览(9)
  • 关于STM32F407ZGT6的USB_UART端口损坏后使用ST-Link和USART1实现串口功能

    关于STM32F407ZGT6的USB_UART端口损坏后使用ST-Link和USART1实现串口功能

    开发板:STM32F407ZGT6; 目标:想使用软件“串口调试助手” 情况:开发板上的USB_UART口所在器件损坏或者直接没有;   解决办法:查看该开发板的原理图,可得:串口1的RX接TXD,串口1的TX接RXD,那么按如下步骤操作: 1、现在使用USB转TTL模块,将串口1的RX接USB转TTL模块的TXD,

    2024年02月07日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包