FreeRTOS - 基于ESP32 串口数据收发

这篇是基于FreeRTOS 针对数据收发做了个集成化处理 这样保证引入该文件后 外界只需要调用收发接口 就能做到对数据的收发 。


)

目前是在ESP32的开发环境下做了个简单的串口数据收发

1.核心思想

既然是数据收发 避免不了超时、堵塞、流控等这些机制。
既然是基于FreeRTOS 就要好好利用它的一些特性 帮助我们更好的维护数据。

在开始说FreeRTOS的前 不妨先发散下思维 想一想 假如你也不熟悉RTOS特性 你要怎么设计 才能保证数据收发过程中 数据不会丢失呢。
数据收发 不外乎就2种场景 好理解点 以下把收到数据叫做生产者 因为生产数据嘛
把发送数据叫做消费者。
场景1.生产者生产的数量 > 消费者消费的数量 这种可以叫做 “供大于求”
场景2.生产者生产的数量 <= 消费者消费的数量 这种可以叫做 “供不应求”

那场景1呢 我们就想着 要在缓存满了的情况下 让生产者不生产了 这样就不会出现生产者一直生产 但消费者来不及消费 导致那些生产的数据丢失了

场景2呢 就是完全可以接受 在缓冲区足够的情况下 进行收发。
所以我们要针对场景1做处理。

FreeRTOS有锁 信号量 堵塞等机制 我们利用好这俩机制就能解决场景1的问题。

//基于以上 我们可以创建这样一个结构体 用来存放我们的数据
//其中buf用来存放数据
//read write分别是读写指针 由于buf空间有限 我们可以通过读写指针做一个环形缓冲区
//count是当前写入的数据量
//queue 是数据队列
//mutex 是锁
//ready_semaphore 是信号量

typedef struct {
    uint8_t *buf;
    size_t read;
    size_t write;
    size_t count;

    QueueHandle_t queue;
    SemaphoreHandle_t mutex;
    SemaphoreHandle_t ready_semaphore;
} DATA_T;

目前用EPS32的串口数据收发做举例

#include "driver/uart.h"
#include "driver/gpio.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_log.h>
#include "freertos/semphr.h"
#include "freertos/queue.h"


#define UART_NUM UART_NUM_1
#define UART_TX_PIN 18
#define UART_RX_PIN 17
#define UART_BUF_SIZE 1024

typedef struct {
    uint8_t *buf;
    size_t length;
    uint32_t timestamp;
} DATA_PACKET_T;

typedef struct {
    uint8_t *buf;
    size_t read;
    size_t write;
    size_t count;

    QueueHandle_t queue;
    SemaphoreHandle_t mutex;
    SemaphoreHandle_t ready_semaphore;
} DATA_T;

static DATA_T data_t;
static const char *UART_TAG = "example:uart";

int buffer_get_used_space(void)
{
    return data_t.count;
}

int buffer_get_remain_space(void)
{
    return UART_BUF_SIZE - buffer_get_used_space();
}

bool buffer_is_empty(void)
{
    return data_t.count == 0;
}

bool buffer_is_full(void)
{
    return data_t.count >= UART_BUF_SIZE;
}

void uart_init(void)
{
    memset(&data_t, 0, sizeof(data_t));

    data_t.ready_semaphore = xSemaphoreCreateBinary();
    data_t.mutex = xSemaphoreCreateMutex();

    data_t.buf = malloc(UART_BUF_SIZE);
    data_t.read = data_t.write = data_t.count = 0;

    if (data_t.buf == NULL) {
        ESP_LOGI(UART_TAG, "缓冲区分配失败\n");
        return;
    }

    uart_config_t uart_config = {
        .baud_rate = 230400,                        // 设置波特率
        .data_bits = UART_DATA_8_BITS,              // 设置数据位为 8 位
        .parity    = UART_PARITY_DISABLE,           // 不使用校验位
        .stop_bits = UART_STOP_BITS_1,              // 使用一个停止位
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE       // 禁用硬件流控
    };

    ESP_ERROR_CHECK(uart_driver_install(UART_NUM, UART_BUF_SIZE, 0, 20, &data_t.queue, 0)); // 串口驱动安装
    ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config)); // 串口参数配置
    ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); // 串口引脚配置
    ESP_ERROR_CHECK(uart_enable_rx_intr(UART_NUM)); //使能串口中断
}


void buffer_write_data(uint8_t *data, size_t length)
{
    if (length == 0) return;
    
    if (xSemaphoreTake(data_t.mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        for (size_t i = 0; i < length; i++) {
            data_t.buf[data_t.write] = data[i];
            data_t.write = (data_t.write + 1) % UART_BUF_SIZE;
            data_t.count++;
            xSemaphoreGive(data_t.ready_semaphore);
        }
        xSemaphoreGive(data_t.mutex);
        //ESP_LOGI(UART_TAG, "缓冲区写入 %d 字节,当前数据量: %d\n", length, data_t.count);
    }
}

// 非阻塞读取,立即返回可用数据
size_t buffer_read_nonblocking(uint8_t *output, size_t max_length)
{
    size_t total_read = 0;
    if (xSemaphoreTake(data_t.mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
        while (!buffer_is_empty() && total_read < max_length) {
            output[total_read] = data_t.buf[data_t.read];
            data_t.read = (data_t.read + 1) % UART_BUF_SIZE;
            data_t.count--;
            total_read++;
        }
        //ESP_LOGI(UART_TAG, "RX noblock %d  %d/%d\n", total_read, data_t.read, data_t.write);
        xSemaphoreGive(data_t.mutex);
    }
    
    return total_read;
}

// 从缓冲区读取数据(阻塞等待)
size_t buffer_read_blocking(uint8_t *output, size_t required_length, TickType_t timeout)
{
    size_t total_read = 0;
    uint32_t start_time = xTaskGetTickCount();
    
    while (total_read < required_length) {
        // 检查超时
        if ((xTaskGetTickCount() - start_time) > timeout) {
            ESP_LOGI(UART_TAG, "读取超时,已读取 %d/%d 字节\n", total_read, required_length);
            break;
        }

        // 一次性读取尽可能多的数据
        size_t available = buffer_get_used_space();
        if (available > 0) {
            size_t to_read = (available < (required_length - total_read)) ? available : (required_length - total_read);

            // 等待数据信号量
            if (xSemaphoreTake(data_t.ready_semaphore, pdMS_TO_TICKS(100)) == pdTRUE) {
                if (xSemaphoreTake(data_t.mutex, pdMS_TO_TICKS(50)) == pdTRUE) {
                    // 计算连续可读的数据长度
                    size_t contiguous = (data_t.read <= data_t.write) ?  (data_t.write - data_t.read) : (UART_BUF_SIZE - data_t.read);
                    size_t actual_read = (to_read < contiguous) ? to_read : contiguous;
                    memcpy(output + total_read, data_t.buf + data_t.read, actual_read);
                    //ESP_LOGI(UART_TAG, "RX block  %d/%d\n",  data_t.read, data_t.write);
                    data_t.read = (data_t.read + actual_read) % UART_BUF_SIZE;
                    data_t.count -= actual_read;
                    total_read += actual_read;
                    xSemaphoreGive(data_t.mutex);
                }
            }
        } else {
            vTaskDelay(1 / portTICK_PERIOD_MS);
        }
    }
    
    return total_read;
}


void data_recv_proc(void *param)
{
    int len;
    int temp;
    uint8_t buf[256];
    uart_event_t event;

    uart_init();
    // 检查队列是否创建成功
    if (data_t.queue == NULL) {
        ESP_LOGE(UART_TAG, "UART事件队列创建失败,任务退出");
        vTaskDelete(NULL);
        return;
    }

    while (1) {
        if (xQueueReceive(data_t.queue, &event, portMAX_DELAY)) {
            switch (event.type) {
                case UART_DATA:
                    // 有数据到达
                    if (!event.size) {
                        break;
                    }
                    temp = buffer_get_remain_space();
                    temp = event.size > temp ? temp : event.size;
                    temp = sizeof(buf) > temp ? temp : sizeof(buf);
                    // 读取数据到缓冲区
                    len = uart_read_bytes(UART_NUM, buf, temp, pdMS_TO_TICKS(100));
                    if (len > 0) {
                        buffer_write_data(buf, len);
                    }
                    
                    break;
                    
                case UART_FIFO_OVF:
                case UART_BUFFER_FULL:
                    ESP_LOGI(UART_TAG, "UART %s\n", (event.type == UART_FIFO_OVF) ? "FIFO 溢出" : "缓冲区满");
                    uart_flush_input(UART_NUM);
                    xQueueReset(data_t.queue);
                    break;
                default:
                    ESP_LOGI(UART_TAG, "UART 事件类型: %d\n", event.type);
                    break;
            }
        }
    }
}

void uart_send_data(uint8_t *data, size_t length)
{
    if (length == 0) return;
    
    int sent = uart_write_bytes(UART_NUM, data, length);
    if (sent == length) {
        ESP_LOGI("UART", "成功发送 %d 字节", sent);
    } else {
        ESP_LOGE("UART", "发送失败,期望: %d, 实际: %d", length, sent);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值