【ESP8266】app_IR_TxRx_demo中的RingBuf代码透析

在裸机单片机开发中,有时候需要用到先入先出队列(FIFO),可是一般的裸机开发环境是没有一个库函数给开发者使用队列的,这个时候需要自己写队列函数。后来,我在开发ESP8266和基于cc2530的contiki系统时,都发现这些系统下面有一个RingBuf文件,说明这个RingBuf就是为了解决裸机单片机开发中队列的问题。下面我来分析一下ESP8266中的RingBuf代码。

阅读代码时第一步都是先看数据结构,那么来看看RINGBUF数据结构。

typedef struct{
    uint8_t* p_o;               /**< Original pointer */
    uint8_t* volatile p_r;      /**< Read pointer */
    uint8_t* volatile p_w;      /**< Write pointer */
    volatile int32_t fill_cnt;  /**< Number of filled slots */
    int32_t size;               /**< Buffer size */
}RINGBUF;

该结构体很简单,p_o是用来保存初始地址的指针,p_r是读取指针,p_w是写入指针,fill_cnt用来计数的,每写入一个数据就加1,size是保存该数组队列的大小。

初始化函数RINGBUF_Init:

/*
* \brief init a RINGBUF object
* \param r pointer to a RINGBUF object
* \param buf pointer to a byte array
* \param size size of buf
* \return 0 if successfull, otherwise failed
*/
int16_t RINGBUF_Init(RINGBUF *r, uint8_t* buf, int32_t size)
{
    if (r == NULL || buf == NULL || size < 2)
        return -1;                  //如果r和buf传入的参数为空、size数组大小小于2,则初始化失败

    r->p_o = r->p_r = r->p_w = buf; //初始化p_o,p_r,p_w
    r->fill_cnt = 0;                //初始化fill_cnt
    r->size = size;                 //初始化size

    return 0;
}

写入一个数据RINGBUF_Put:

/**
* \brief put a character into ring buffer
* \param r pointer to a ringbuf object
* \param c character to be put
* \return 0 if successfull, otherwise failed
*/
int16_t RINGBUF_Put(RINGBUF *r, uint8_t c)
{
    if (r->fill_cnt >= r->size){
        printf("BUF FULL\n");
        return -1;                  // 如果缓冲区满了,则返回-1错误
    }   

    r->fill_cnt++;                  // 每写入一个字节,则加1计数

    *r->p_w++ = c;                  // 把数据放入缓冲区,并指向下一个地址

    if (r->p_w >= r->p_o + r->size) // 如果写入指针超过了缓冲区末尾,则
        r->p_w = r->p_o;            // 把指针指向原点,这就是RING的含义

    return 0;
}

读取数据RINGBUF_Get:

/**
* \brief get a character from ring buffer
* \param r pointer to a ringbuf object
* \param c read character
* \return 0 if successfull, otherwise failed
*/
int16_t RINGBUF_Get(RINGBUF *r, uint8_t* c, int32_t length)
{
    int32_t cnt = 0;
    if (r->fill_cnt <= 0)
        return -1;             // 如果缓冲区为空,则返回-1错误

    if (length>r->fill_cnt){
        length = r->fill_cnt;  // 最大只能读取缓冲区拥有数据的长度
    }

    int i;
    cnt = r->fill_cnt;
    for (i = 0; i<length; i++)
    {
        r->fill_cnt--;                  // 每读取一个字节,计数减1
        *c = *r->p_r++;                 // 返回数据给*c
        *c++;
        if (r->p_r >= r->p_o + r->size) // 如果读取指针超过了缓冲区末尾,则
            r->p_r = r->p_o;            // 把指针指向原点
    }
    return 0;
}

主函数及其他,这里使用VS2013开发环境测试该RingBuf:

#define RX_RCV_LEN 128
RINGBUF IR_RX_BUFF;
uint8_t ir_rx_buf[RX_RCV_LEN];
#define READ10CHAR_TEST 0

int _tmain(int argc, _TCHAR* argv[])
{
    //RINGBUF初始化
    //把ir_rx_buf初始化为一个RingBuf,使用IR_RX_BUFF结构体保存信息,长度为sizeof(ir_rx_buf)
    RINGBUF_Init(&IR_RX_BUFF, ir_rx_buf, sizeof(ir_rx_buf));

    int i = 10;
    while (i--){
#if READ10CHAR_TEST
        RINGBUF_Put(&IR_RX_BUFF, '1'+i);
#else
        RINGBUF_Put(&IR_RX_BUFF, i);    //放入10个字节到IR_RX_BUFF中
#endif
    }

#if READ10CHAR_TEST
    uint8_t data[11] = {0};
    RINGBUF_Get(&IR_RX_BUFF, data, 10); //从IR_RX_BUFF读取1个字节
    printf("RingBuf pop : %s \r\n", data);  //打印该字节
#else
    uint8_t data;
    while (IR_RX_BUFF.fill_cnt){
        i = 10;
        while (i--){
            RINGBUF_Get(&IR_RX_BUFF, &data, 1); //从IR_RX_BUFF读取1个字节
            printf("RingBuf pop : %d \r\n", data);  //打印该字节
        }
    }
#endif

    return 0;
}

当READ10CHAR_TEST置0时,每次从RINGBUF读取一字节,打印出的信息为:

RingBuf pop : 9
RingBuf pop : 8
RingBuf pop : 7
RingBuf pop : 6
RingBuf pop : 5
RingBuf pop : 4
RingBuf pop : 3
RingBuf pop : 2
RingBuf pop : 1
RingBuf pop : 0
请按任意键继续…

当READ10CHAR_TEST置1时,一次性从RINGBUF读取10字节,打印出的信息为:

RingBuf pop : :987654321
请按任意键继续…

扩展

后面扩展了一下这个RingBuf的功能,增加了两个函数。

//清空缓冲区
int16_t RINGBUF_Clr(RINGBUF *r)
{
    memset(r->p_o, 0, r->size);
    r->p_r = r->p_w = r->p_o;
    r->fill_cnt = 0;

    return 0;
}


//读取第index个数据
uint8_t RINGBUF_Read(RINGBUF *r, int32_t index)
{
    uint8_t data = 0;

    if (r->fill_cnt <= 0)
        return 0;             // 如果缓冲区为空,则返回0

    if (index > r->fill_cnt){
        index = r->fill_cnt;  // 最大只能读取缓冲区拥有数据的长度
    }

        if (r->p_r + index >= r->p_o + r->size){ // 如果读取的index超过了缓冲区末尾,则
                //r->p_r = r->p_o;                      // 从头开始计算index
            index = index - (r->p_o + r->size - r->p_r);
        }

        data = r->p_r[index];   //读取第index个数据

    return data;
}
#include "esp8266.h" #include "delay.h" #include "string.h" #include "stdarg.h" #include "stdio.h" uint8_t esp8266_rx_buf[ESP8266_RX_SIZE]; uint8_t esp8266_tx_buf[ESP8266_TX_SIZE]; uint8_t esp8266_rx_len = 0,esp8266_rx_len_prev = -1; //esp8266_init UART_HandleTypeDef esp8266_handle = {0}; void esp8266_uart_init(uint32_t baudrate) { esp8266_handle.Instance = USART2; esp8266_handle.Init.BaudRate = baudrate; esp8266_handle.Init.Parity = UART_PARITY_NONE; esp8266_handle.Init.Mode = UART_MODE_TX_RX; esp8266_handle.Init.StopBits = UART_STOPBITS_1; esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B; esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; HAL_UART_Init(&esp8266_handle); HAL_NVIC_SetPriority(USART2_IRQn,2,2); HAL_NVIC_EnableIRQ(USART2_IRQn); } void USART2_IRQHandler(void) { uint8_t receive_data = 0; if(__HAL_UART_GET_FLAG(&esp8266_handle,UART_FLAG_RXNE) != RESET) { if(esp8266_rx_len >= ESP8266_RX_SIZE) esp8266_rx_len = 0; HAL_UART_Receive(&esp8266_handle,&receive_data,1,100); esp8266_rx_buf[esp8266_rx_len++] = receive_data; } } void esp8266_send_data(char* format,...) { va_list args; uint8_t len; va_start(args,format); vsprintf((char*)esp8266_tx_buf,format,args); len = strlen((char*)esp8266_tx_buf); HAL_UART_Transmit(&esp8266_handle,esp8266_tx_buf,len,100); } void clear_rx_buf(void) { for(uint8_t i = 0;i < ESP8266_RX_SIZE;i++) { esp8266_rx_buf[i] = 0; } esp8266_rx_len = esp8266_rx_len_prev = 0; } //esp8266_wait_recive_data uint8_t esp8266_wait_recive_data(void) { if(esp8266_rx_len == 0) return ESP8266_REEOR; if(esp8266_rx_len_prev == esp8266_rx_len) { esp8266_rx_len = 0; return ESP8266_ROK; } esp8266_rx_len_prev = esp8266_rx_len; return ESP8266_REEOR; } void esp8266_receive_data(void) { if(esp8266_wait_recive_data() == ESP8266_ROK) { printf("esp8266 recv:%s",esp8266_rx_buf); clear_rx_buf(); } } //esp8266_send_cmd uint8_t esp8266_send_cmd(char* cmd,char* res) { uint8_t time_out = 250; clear_rx_buf(); HAL_UART_Transmit(&esp8266_handle, (uint8_t *)cmd, strlen(cmd), 100); while(time_out--) { if(esp8266_wait_recive_data() == ESP8266_ROK) { printf("[SEND] %s\r\n",cmd); printf("[RECV] %s\r\n",(char*)esp8266_rx_buf); delay_ms(500); if(strstr((const char*)esp8266_rx_buf, res) != NULL) return ESP8266_ROK; else return ESP8266_REEOR; } delay_ms(10); } return ESP8266_REEOR; } uint8_t esp8266_at_test(void) { return esp8266_send_cmd("AT\r\n","OK"); } uint8_t esp8266_set_mode(uint8_t mode) { char cmd[64]; sprintf(cmd,"AT+CWMODE=%d\r\n",mode); return esp8266_send_cmd(cmd,"OK"); } uint8_t esp8266_connection_mode(uint8_t mode) { char cmd[64]; sprintf(cmd,"AT+CIPMUX=%d\r\n",mode); return esp8266_send_cmd(cmd,"OK"); } uint8_t esp8266_join_ap(char *ssid, char *pwd) { char cmd[64]; sprintf(cmd,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pwd); return esp8266_send_cmd(cmd,"WIFI GOT IP"); } uint8_t esp8266_connect_tcp_server(char *server_ip, char *server_port) { char cmd[64]; sprintf(cmd,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",TCP_SERVER_IP,TCP_SERVER_PORT); return esp8266_send_cmd(cmd,"CONNECT"); } uint8_t esp8266_enter_unvarnished(void) { uint8_t ret1 = esp8266_send_cmd("AT+CIPMODE=1\r\n", "OK"); uint8_t ret2 = esp8266_send_cmd("AT+CIPSEND\r\n", ">"); return (ret1 == ESP8266_ROK && ret2 == ESP8266_ROK) ? ESP8266_ROK : ESP8266_REEOR; } void esp8266_init(uint32_t baudrate) { esp8266_uart_init(baudrate); if(esp8266_at_test() == ESP8266_ROK) printf("OK\r\n"); printf("准备配置STA中...\r\n"); while(esp8266_set_mode(ESP8266_STA_MODE)); printf("成功配置STA模式\r\n"); printf("准备配置多联通中...\r\n"); while(esp8266_connection_mode(ESP8266_MULTI_CONNECTION)); printf("成功配置多联通模式\r\n"); printf("准备连接网络...\r\n"); while(esp8266_join_ap(WIFI_SSID,WIFI_PWD)); printf("成功连接网络->%s\r\n",WIFI_SSID); printf("准备连接TCP中...\r\n"); while(esp8266_connect_tcp_server(TCP_SERVER_IP,TCP_SERVER_PORT)); printf("成功连接TCP服务器\r\n"); printf("准备设置穿透模式中...\r\n"); while(esp8266_enter_unvarnished()); printf("成功设置穿透模式\r\n"); } void esp8266_test(void) { esp8266_send_data("this seucc to ai!"); esp8266_receive_data(); } 逻辑有问题吗
07-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值