功能简介:
实现一个数据流(笔者用于串口)的接收缓冲,然后在cpu空闲的时候处理这些数据流,一般数据流有特定的格式,以便于程序分割命令和参数。
本文使用的是类似linxu shell的可见字符串。实际使用中也可以更改为16进制数据。
使用环境示例:
串口接收函数调用write_queue();插入上位机输入的数据。程序通过调用read_queue()获取数据。
/*
queue.c
--实现功能:定义一个环形消息队列,实现1.读一个字节 2.插入一个字节 3.取出一条命令。
---------- 命令格式定义:cmd prama\r 获取命令依照" "区分命令和参数,依照"\r"区分结尾。
author:nome
date :2014/4/18
*/
#include "string.h"
#include "queue.h"
#define Q_LEN 100 //队列长度
#define TIMEOUT 900000
char strqueue[Q_LEN]; //队列数组
int write = 0; //写偏移
int read = 0; //读偏移
/*
* 功能:从队列中读取一个字节
* 失败:返回-1
* 成功:返回0
*/
char read_queue(char *pdata)
{
if(write==read)
return -1;
*pdata = strqueue[read];
read = (read+1)%Q_LEN;
return 0;
}
/*
* 功能:往队列中写入一个字节
* 失败:返回-1
* 成功:返回0
*/
char write_queue(char data)
{
if((write+1)%Q_LEN==read)
return -1;
strqueue[write] = data;
write = (write+1)%Q_LEN;
return 0;
}
/*
* 功能:从队列中取出一个完整的字符串命令。
* 失败:返回-1
* 成功:返回0
*cmd 存放命令的指针,param 存放参数的指针。
*/
int get_q_string(char *cmd,char *param)
{
int i = 0;
int timeout = 0;
char data;
if(read_queue(&data)) //查看队列中是否有数据,没有数据返回,有数据等待接收完整命令。
return -1;
cmd[i++] = data;
for(;;){
if(0==read_queue(&cmd[i]))
i++;
if(cmd[i-1]==' ')
break;
if(timeout>TIMEOUT)
return -1;
timeout++;
}
cmd[i] = 0; //提供一个cmd结束符
timeout = 0;
i = 0;
for(;;){
if(0==read_queue(¶m[i]))
i++;
if(param[i-1]=='\r')
break;
if(timeout>TIMEOUT)
return -1;
timeout++;
}
param[i-1] = 0; //去掉\r 换成结束符
return 0;
}
下面一个.c和一个.h用于处理上面.c取出的字符串。
#ifndef _USART_CMD_H
#define _USART_CMD_H
struct _cmd_list{
char *cmd;
void (*func)(char *param);
};
#define CMD_CALLBACK_LIST_BEGIN struct _cmd_list cmd_list[] = {NULL,null,
#define CMD_CALLBACK_LIST_END NULL,NULL};
#define CMD_CALLBACK(cmd_string,callback) cmd_string,callback,
#endif
#include "queue.h"
#include "string.h"
#include "math.h"
#include "stdio.h"
#include "usart_cmd.h"
extern void usart_sendstring(char *str); //定义一个串口发送字符串的函数
//字符串转int
int str_to_int(char *str)
{
int i = 0,j = 0;
int ret = 0;
for(;;){
if(str[i++]==0||i>20)
break;
}
j = i = i-2;
for(;i>=0;i--)
{
ret += (str[i]-'0')*(pow(10,(j-i)));
}
return ret;
}
/********************一些回调函数 不必在意*****************************/
void setfreq(char *param)
{
SX1276LoRaSetRFFrequency(str_to_int(param));
memset(param,0,32);
sprintf(param,"setfreq ok! freq=%d\r\n",SX1276LoRaGetRFFrequency());
usart_sendstring(param);
}
extern int send_receive_flag ;
extern int packet_num ;
void send(char *param)
{
send_receive_flag = 0;
packet_num = str_to_int(param);
}
void receive(char *param)
{
send_receive_flag = 1;
}
void null(char *param)
{}
/********************一些回调函数 不必在意*****************************/
//在此处添加你的命令字符串和回调函数
CMD_CALLBACK_LIST_BEGIN
CMD_CALLBACK("setfreq",setfreq)
CMD_CALLBACK("send",send)
CMD_CALLBACK("receive",receive)
CMD_CALLBACK_LIST_END
char cmd[10];
char param[32];
int get_cmd(void)
{
int i = 0;
if(get_q_string(cmd,param))
return 0;
for(;;){
if(strcmp(cmd,cmd_list[i].cmd)==0)
return i;
if(cmd_list[++i].cmd==NULL)
return 0;
}
}
//这个函数需要在main函数中轮询调用
void dispose_cmd(void)
{
cmd_list[get_cmd()].func(param);
}