单片机数据接收缓冲流的设计与实现

发布时间:2024-11-12

单片机串口数据接收系统设计

由一个串口接收数据引发的问题与字节缓冲流系统的设计

在一个wifi数据收发项目调试时发现,数据在高速连续发送和接收时,经常出现数据出现了丢失和系查询指定标志位:这种方式通常在main函数的大循环中不断的检测标志位或者等待该标志位来判断1:在大循环中 if(标志位成立)表明有数据接收 然后进行数据的处理。

优点: 不会引起整个main函数 线程的阻塞 ;在简单的数据接收项目中可以使用

缺点: 单片机一般都为单线程,复杂的控制中采用操作系统,例如UC/OS;所以,将所以都函数放在main函数大循环中进行轮番处理。整个循环周期时间不确定,其他任务函数可能发生阻塞,不能够保证数据到来时,正好在执行检测指定标志位,从而出现了数据丢失。

显然:以上两种发送在复杂的控制系统中是不能采用的 ,因此:在没有多任务操作系统时,数据的接收采用中断接收的法式是最佳的。使用中断,可以不用查询和等待的方式接收数据,解决了许多问题。,此时,单片机可以说是多线程执行程序。main函数是一个线程,中断服务子程序是一个线程。中断是前台,main函数是后台。由于是多线程(一般而言),不得不考虑数据的安全性。中断可能随时到来。Main函数会随时被打断,程序计数器寄存器PC指针指向中断函数入口地址,指向中断函数。Main函数在处理数据时被打断,可能会引发数据的丢失。共同访问全局变量时,使用互斥信号量等一些手段保障数据不被修改。设计可能被中断打断的函数时,要注意函数的重入问题,像static等关键字。

字节接收缓冲系统设计的核心思想:

消息队列核心算法实现:

1:消息队列核心数据结构: typedef struct Queue

{

unsigned char front; //队列头索引 unsigned char rear; //队列尾索引

unsigned char *pArray;//简易的队列 指向数组

1:前台(即中断)负责接收数据,并不进行处理,将数据放入消息队列中。 2:后台(main函数)负责从消息队列中取出消息,并处理。

3:整个接收系统核心为 队列,可以当做缓冲区;遵循先进先出原则 FIFO

2:在大循环中 while(标志位);通过while来等待数据的到来。

优点:数据不会出现丢失,稳定。

缺点:整个main函数主线程出现堵塞,其他函数无法执行,上述所示。

统的死机。单片机在接收串口数据时,传统采用中断方法或者查询指定标志位方法接收数据。 是否有数据接收。通常有两种方式:

采用队列方式接收数据比较简单,并且实现了缓冲,不会出现数据的丢失。

}QueueTypeDef;

单片机串口数据接收系统设计

2: 判断队列是否为满伪算法

3: 判断队列是否为空伪算法

4: 将数据加入队列伪算法

5: 将数据从队列中取出伪算法 if( 队列不为空) { }

以上是接收最简单的一个字节的队列;ASCII C 编译通过 不依赖于单片机 ;将其加入中断服务子程序中,把接收的数据加入队列中;以stm32 单片机串口中断为例:

void USART2_IRQHandler(void) {

Main函数从消息队列中取出数据

unsigned char val; while(1) {

if(out_queue(&Queue, &val)) //从队列中取出数据 if(USART_GetITStatus(USART2,USART_IT_RXNE)==SET) { } }

if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) { }

USART_ClearFlag(USART2,USART_FLAG_ORE); USART_ReceiveData(USART2);

USART_ClearITPendingBit(USART2,USART_IT_RXNE);

en_queue(&Queue,(uint8_t)USART_ReceiveData(USART2)); //将数据加入消息队列中

Val = pArray[front];

front = (front + 1 ) % 数组长度 if( 队列不为满 ) { }

pArray[rear] = 数据; rear = (rear + 1) % 数组的长度 if(rear == front)

if( (rear + 1) % 数组的元素个数) == front)

单片机串口数据接收系统设计

}

}

…. …..

//其他任务 ……. if(i == 16) i++;

i=0;

LCD_print(1,i,val); //显示取出的数据

以上算法思路是以接收最简单的一个字节为例:当然可以接收更复杂的数据,数据结构如下

typedef struct Message //消息数据结构 {

typedef struct Queue //消息队列 数据结构 {

MessageTypeDef;这个数据结构中 构造了接收数据的格式 并不是前面最简单的一个字节,根据实际接收数据的需要来构造数据结构,当然在中断函数中要进行数据的处理,也可以放在主函数中处理数据,中断中依然是将字节放入消息队列中。主函数处理完数据后在放入另一个消息队列中,由其他函数处理数据,多级消息队列。以下是带特定格式的消息数据处理: buff[buff_index] = USART_ReceiveData(USART2); // // // // // // //

{ } else {

buff_index++;

if(buff[0] == 0x2B) //校验数据头

//将接收的数据(1个字节)放入缓冲区

u8 front; //队列头 u8 rear;

MessageTypeDef message[MessQueueSize]; //消息

BOOL (*postMessage)(MessageTypeDef dat ); //消息进列 BOOL (*getMessage)(MessageTypeDef * datAddr);//消息出列

//队列尾 + 1

u8 clientID; //客户端名 u8 messgeLength; //消息长度

u8 message_str[MessageSize]; //存放消息的数组

}MessageTypeDef;

}MessQueue;

单片机串口数据接收系统设计

// // // // // // // // // // // // // //

}

if(buff_index == 8) //获取数据尾 { }

if(buff_index == length) {

length = 200; }

buff_index = 0;

length = (buff[7] - 0x30) + 1 + buff_index; // 计算数据尾索引

receive = TRUE; //数据接收完成 //DISABLE_WIFI_RX_IRQ(); //

附: 字节缓冲流系统源码:

文件 : queue.h

#ifndef __QUEUE_H__ #define __QUEUE_H__

#ifndef bool

#define bool unsigned char #define true 1 #define false 0 #endif

#define QueueArraySize

typedef struct Queue {

unsigned char front; //队列头 unsigned char rear; //队列尾+1

unsigned char *pArray; //指向字节数组

32 //队列长度 (字节)

}QueueTypeDef;

单片机串口数据接收系统设计

extern QueueTypeDef Queue;

extern unsigned char queueArray[QueueArraySize];

void queue_Init(QueueTypeDef *pQ, unsigned char *array); //初始化 bool full_queue(QueueTypeDef *pQ); //满 bool emput_queue(QueueTypeDef *pQ); //空

bool en_queue(QueueTypeDef *pQ, unsigned char val); //入队列 bool out_queue(QueueTypeDef *pQ, unsigned char *dat); //出队列 #endif

文件:queue.c

#include "queue.h"

QueueTypeDef Queue;

unsigned char queueArray[QueueArraySize];

void queue_Init(QueueTypeDef *pQ, unsigned char *array) { }

bool full_queue(QueueTypeDef *pQ) { }

bool emput_queue(QueueTypeDef *pQ) { }

if(pQ->front == pQ->rear) else

return false; return true;

if((pQ->rear + 1) % QueueArraySize == pQ->front) else

return false; return true; Queue.front; Queue.rear;

pQ->pArray = array; pQ->front = 0; pQ->rear = 0;

单片机串口数据接收系统设计

bool en_queue(QueueTypeDef *pQ, unsigned char val) { }

bool out_queue(QueueTypeDef *pQ, unsigned char *dat) { }

附:复杂数据接收缓冲流实现

1:文件:queue.h

#ifndef __QUEUE_H__ #define __QUEUE_H__

#ifndef BOOL

#define BOOL unsigned char #define TRUE 1 #define FALSE 0 #endif

#define MessageSize #define MessQueueSize

10 //消息长度 (字节)

20 //队列长度 sizeof( MessageTypeDef)

if(emput_queue(pQ)) { } else { }

*dat = pQ->pArray[pQ->front];

pQ->front = (pQ->front + 1) % QueueArraySize; return true; return false; if(full_queue(pQ)) { } else { }

*((pQ->pArray)+(pQ->rear)) = val; //pQ->pArray[pQ->rear] = val;

pQ->rear = (pQ->rear + 1) % QueueArraySize; return true; return false;

单片机串口数据接收系统设计

typedef unsigned char u8; typedef unsigned int u16;

typedef struct Message //消息数据结构 {

typedef struct Queue //消息队列 数据结构 {

extern MessQueue mess_queue;

void MessageQueueInit(void); //初始化 BOOL full_queue(void) ;//判断是否为满 BOOL emput_queue(void);//判断是否为空 BOOL en_queue(MessageTypeDef message) ;//入列 BOOL out_queue(MessageTypeDef *message); //出列 #endif

2:文件:queue.c

#include "queue1.h"

MessQueue mess_queue; //定义消息队列

void MessageQueueInit(void) //初始化

u8 front; //队列头 u8 rear;

MessageTypeDef message[MessQueueSize]; //消息

BOOL (*postMessage)(MessageTypeDef dat ); //消息进列 BOOL (*getMessage)(MessageTypeDef * datAddr);//消息出列

//队列尾 + 1

u8 clientID; //客户端名 u8 messgeLength; //消息长度

u8 message_str[MessageSize]; //存放消息的数组

}MessageTypeDef;

}MessQueue;

单片机串口数据接收系统设计

}

BOOL full_queue(void) //判断队列是否为满 { }

BOOL emput_queue(void) //判断队列是否为空 { }

BOOL en_queue(MessageTypeDef message) //入列 {

if(full_queue()) //判断队列是否为满 { } else {

u8 i = 0;

(&(mess_queue.message[mess_queue.rear]))->clientID = message.clientID; return FALSE;

if(mess_queue.front = mess_queue.rear) //front = rear { } else { }

return FALSE; return TRUE;

if( (mess_queue.rear + 1) % MessQueueSize == mess_queue.front ) // rear + 1 = front { } else { }

return FALSE; return TRUE; mess_queue.front = 0; mess_queue.rear = 0;

mess_queue.postMessage = en_queue; mess_queue.getMessage = out_queue;

单片机串口数据接收系统设计

}

}

for(i=0; i<(message.messgeLength); i++) { }

mess_queue.rear = (mess_queue.rear + 1) % MessQueueSize; return TRUE;

(&(mess_queue.message[mess_queue.rear]))->message_str[i] = message.message_str[i];

BOOL out_queue(MessageTypeDef *message) //出列 { } 完!

以上为整个数据接收的核心算法 在ASCII C编译器上编译通过,不依赖于底层。移植时

修改相应的数据类型。

}

mess_queue.front = (mess_queue.front + 1) % MessQueueSize; return TRUE;

for(i=0; i<(message->messgeLength); i++) { }

message->message_str[i] = (&(mess_queue.message[mess_queue.front]))->message_str[i];

if(emput_queue()) //判断是否为空 { } else {

u8 i;

message->clientID = (&(mess_queue.message[mess_queue.front]))->clientID;

message->messgeLength = (&(mess_queue.message[mess_queue.front]))->messgeLength; return FALSE;

单片机数据接收缓冲流的设计与实现.doc 将本文的Word文档下载到电脑

    精彩图片

    热门精选

    大家正在看

    × 游客快捷下载通道(下载后可以自由复制和排版)

    限时特价:7 元/份 原价:20元

    支付方式:

    开通VIP包月会员 特价:29元/月

    注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
    微信:fanwen365 QQ:370150219