项目场景:
提示:功能较为简单现在只可用于简单的数据处理,可在基础上自由发挥
提示:例如做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)
{
//数据处理
}
}
结语:
欢迎评论!