手搓循环队列----模拟MODBUS主机与从机通信(丐版)

项目场景:

提示:功能较为简单现在只可用于简单的数据处理,可在基础上自由发挥

提示:例如做MODBUS RTU主机与从机通信(丐版)


主要代码:

提示:头文件.H

#ifndef __QUEUE_H_
#define __QUEUE_H_

#include "stdio.h"	
#include <string.h>

#define DATA_SIZE 	86   //存放数据的最大缓存数
#define BUF_SIZE 	5	 //静态循环队列的个数

typedef struct{
	uint8_t DATA_count;					//记录对应帧存放的数据的个数
	uint8_t DATA[DATA_SIZE];			//存放数据
}struct_data;

//循环队列
typedef struct{
	uint8_t BUF_count;							//记录存放了几帧数据
	uint8_t BUF_RED_pointer;					//记录读指针
	uint8_t BUF_WRITE_pointer;					//记录写指针
	uint8_t BUF_LOSE_count;						//记录的丢失的数据
	struct_data DATA_BUF[BUF_SIZE];											
}struct_buf;

void init_queue(struct_buf *queue);
void write_data(struct_buf *queue, void *data, uint8_t len);
void read_data(struct_buf *queue, void *data, uint8_t *len);
uint8_t GET_data_count(struct_buf *queue);

#endif

提示:程序文件.c

/*
 * queue.c
 *
 *  Created on: 2022年7月1日
 *      Author: Hero_Lian
 */

#include "sys.h"
#include "queue.h"

/*
 * 初始化指定数据接收队列
 */
void init_queue(struct_buf *queue)
{
	queue->BUF_count = 0;
	queue->BUF_LOSE_count = 0;
	queue->BUF_RED_pointer = 0;
	queue->BUF_WRITE_pointer = 0;
}
uint8_t GET_data_count(struct_buf *queue)
{
	return queue->BUF_count;
}
/*
 * 将数据指针的数据存入队列中
 *
 * *queue :队列指针
 * *data  :数据指针
 * *len	  :存放是数据个数
 * *返回值:
 * *      
 */
void write_data(struct_buf *queue, void *data, uint8_t len)
{	
//	//检测数据合法性
//	if(len >= DATA_SIZE)//如果存储的数据超过缓存
//		return 1;//返回1
	
	//队列总帧数+1
	if(queue->BUF_count < BUF_SIZE)//记录整个循环队列中存储的数据个数 写一个就加1 读一个就减1
		queue->BUF_count++;
	// 写入数据
	memcpy(queue->DATA_BUF[queue->BUF_WRITE_pointer].DATA,data,len);//传输一帧数据
	//写入数据长度
	queue->DATA_BUF[queue->BUF_WRITE_pointer].DATA_count = len;							//传输这一帧数据的长度
	//写入写指针
	queue->BUF_WRITE_pointer++;											//写指针+1 将地址指向下一个数据		
	//判断写指针是否合法 不能溢出
	if(queue->BUF_WRITE_pointer >= BUF_SIZE)				
		queue->BUF_WRITE_pointer = 0;
	//检测溢出
	if((queue->BUF_count != 0)&&(queue->BUF_WRITE_pointer == queue->BUF_RED_pointer))
		queue->BUF_LOSE_count++;
}
/*
 * 将队列指针的数据读取到数据指针中
 *
 * *queue :队列指针
 * *data  :数据指针
 * *len	  :比较的数据个数
 * *返回值 :
 * *       
 */
void read_data(struct_buf *queue, void *data, uint8_t *len)
{
	//队列总帧数-1
	if(queue->BUF_count > 0)//记录整个循环队列中存储的数据个数 写一个就加1 读一个就减1
	{
		queue->BUF_count--;
		//读取数据
		memcpy(data,queue->DATA_BUF[queue->BUF_RED_pointer].DATA,queue->DATA_BUF[queue->BUF_RED_pointer].DATA_count);//读取一帧数据
		//读取数据长度
		*len = queue->DATA_BUF[queue->BUF_RED_pointer].DATA_count;	
		//写入读指针
		queue->BUF_RED_pointer++;
		//判断读指针是否合法 不能溢出
		if(queue->BUF_RED_pointer >= BUF_SIZE)				
			queue->BUF_RED_pointer = 0;
	}
}

准备:

定义:

struct_buf uart1_queue;//用于校验MODBUS RTU写指令   是否被从机正确接收
struct_buf DJ_queue;//用于接收给从机发送读指令之后  从机返回的数据

初始化:

init_queue(&uart1_queue);	//初始化循环队列			
init_queue(&DJ_queue);		//初始化循环队列																		

用串口给从机发送数据:

/*
==代码功能:给电机驱动器发送数据
==形参:
			Driver_ADR_DJ 驱动器地址  01 A电机  02B电机
			function_code 功能码 0x03读 0x06写
			REC_addr_H  :
									读状态:起始寄存器高字节

									写状态:所写寄存器高字节
			REC_addr_L  :
									读状态:起始寄存器低字节

									写状态:所写寄存器低字节
			DATA_H  :
									读状态:要读的寄存器数量高字节

									写状态:要写的寄存器数据高字节
			DATA_L  :
									读状态:要读的寄存器数量低字节

									写状态:要写的寄存器数据高字节
*/
void Driver1_data_TX_5_17(uint8_t Driver_ADR_DJ,uint8_t function_code , uint8_t REC_addr_H , uint8_t REC_addr_L , uint8_t DATA_H , uint8_t DATA_L)
{
	uint8_t i;
	uint16_t CrC_shuju;
	USART1_TX_BUF[0] = Driver_ADR_DJ;	
	USART1_TX_BUF[1] = function_code;		
	USART1_TX_BUF[2] = REC_addr_H;		
	USART1_TX_BUF[3] = REC_addr_L;		
	USART1_TX_BUF[4] = DATA_H;
	USART1_TX_BUF[5] = DATA_L;			
	CrC_shuju = usMBCRC16(USART1_TX_BUF,6);						//CRC计算
	USART1_TX_BUF[6] = 	(CrC_shuju&0xFF);						//CRC低八位
	USART1_TX_BUF[7] = ((CrC_shuju >>8)&0xFF);					//CRC高八位
	for(i=0;i<8;i++)
	{
		Usart_SendByte(USART1, USART1_TX_BUF[i]);         		//向串口1发送数据
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);	//等待发送结束
	}
}																	

将从机返回来的串口数据写入我们的循环队列:
提示:本文使用的是"串口+定时器"实现接收不等长数据:

void USART1_IRQHandler(void)//串口中断
{
	uint8_t recv_data;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收到一个字节
	{
		recv_data = USART_ReceiveData(USART1); 
		//开始接收
		if(recv_data==0x01 && recv_begin == 0)//从机地址是1 按照MODBUS RTU的数据格式 数据第一位就是0x01
		{
			uart1_rx_buf[0] = recv_data;
			recv_begin = 1;
			uart1_rx_count++;
			
			TIM_Cmd(BASIC_TIM, ENABLE);//开启定时器 使用前必须初始化好定时器的配置
			TIM_SetCounter(BASIC_TIM,0);//清除定时器计时
		}
		else if(recv_begin && uart1_rx_count < DATA_SIZE)
		{
			uart1_rx_buf[uart1_rx_count++] = recv_data;
			TIM_SetCounter(BASIC_TIM,0);//每次接收到串口数据就清除定时器计时
		}
		else
		{ 
			uart1_rx_count = 0;
		}
	}
}
void  BASIC_TIM_IRQHandler (void)//定时器中断
{//进入这里证明已经完整的接收到了一帧串口数据
	uint16_t CrC_shuju;
	if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET ) 
	{
		TIM_ClearITPendingBit(BASIC_TIM , TIM_IT_Update);  	
		
		CrC_shuju = usMBCRC16(uart1_rx_buf,uart1_rx_count-2);//CRC计算
		
		if(uart1_rx_buf[uart1_rx_count - 2] == 	(CrC_shuju&0xFF) && uart1_rx_buf[uart1_rx_count - 1] == ((CrC_shuju >>8)&0xFF))//如果这一帧数据准确
		{
			uart1_rx_ok = 1; //数据校验成功 等数据填入到队列 这里直接处理数据 所以没用这个寄存器
			
			if(uart1_rx_buf[1] == 0x06)//这个区分一下是写的数据还是读的数据放在不同的循环队列里
			{
				write_data(&uart1_queue,uart1_rx_buf,uart1_rx_count);
			}
			else if(uart1_rx_buf[1] == 0x03)
			{
				write_data(&DJ_queue,uart1_rx_buf,uart1_rx_count);
			}
		}
		//清理 数据开始接收标志位 和 累计计数 马上就准备接收下一帧数据
		uart1_rx_count = 0;
		recv_begin = 0;
		TIM_Cmd(BASIC_TIM, DISABLE);
	} 	
}

应用:

写MODBUS从机 + 校验:

char Driver1_write_data_verify(void);//驱动器数据校验函数
int main()
{
	while(1)
	{
			Driver1_data_TX_5_17(0x01,0x06,0x00,0x44,0x00,0x01);
			delay_ms(30);
			//根据MODBUS RTU特性进行数据校验(判断从机返回来的写指令和我们发过去的写指令是否一致)
			if(Driver1_write_data_verify() == 0)
			{
			//进到这里证明 写 从机成功
			}
	}
}
/*
==代码功能:驱动器写函数校验
==形参:
			function_code 功能码 0x03读 0x06写
			REC_addr_H  :
									读状态:起始寄存器高字节

									写状态:所写寄存器高字节
			REC_addr_L  :
									读状态:起始寄存器低字节

									写状态:所写寄存器低字节
			DATA_H  :	
									读状态:要读的寄存器数量高字节

									写状态:要写的寄存器数据高字节
			DATA_L  :
									读状态:要读的寄存器数量低字节

									写状态:要写的寄存器数据高字节
*/
char Driver1_write_data_verify(void)//驱动器数据校验函数
{
	uint8_t USART1_RX_COUNT;			 				//接收数据数量
	uint8_t USART1_RX_BUF[32]={0};     				//接收校验数据.
	
	uint8_t queue_count;
	queue_count = GET_data_count(&uart1_queue);
	if(queue_count)//如果循环队列中有数据   ???
	{
//		USART1_RX_COUNT = 8;
		__disable_irq();   // 关闭总中断
		read_data(&uart1_queue,USART1_RX_BUF,&USART1_RX_COUNT);
		__enable_irq();    // 开启总中断
		return strncmp((void*)USART1_RX_BUF,(void*)USART1_TX_BUF,USART1_RX_COUNT); 
	}
	else
		return 2;//
//(strncmp函数为字符串比较函数,其函数声明为int strncmp ( const char * str1, const char * str2, size_t n );
//功能是把 str1 和 str2 进行比较,最多比较前 n 个字节,若str1与str2的前n个字符相同,则返回0
//若s1大于s2,则返回大于0的值;若s1 小于s2,则返回小于0的值)


}

读MODBUS从机 + 数据处理:

void Get_Motor_status(void);//电机状态获取函数
int main()
{
	while(1)
	{
			Driver1_data_TX_5_17(0x01,0x03,0x00,0x10,0x00,0x1E);
			delay_ms(30);
			Get_Motor_status(void)
	}
}
void Get_Motor_status(void)//电机状态获取函数
{
	uint8_t DATA_count = 0;
	DATA_count = GET_data_count(&DJ_queue);
	if(DATA_count)//如果循环队列中有数值
	{
		__disable_irq();   // 关闭总中断
		read_data(&DJ_queue,Motor_status_Data,&Motor_status_Data_count);
		__enable_irq();    // 开启总中断
	}
	if(Motor_status_Data[0] == 0x01)
	{
		//数据处理
	}
}

结语:

欢迎评论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值