这篇是基于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);
}
}
3718

被折叠的 条评论
为什么被折叠?



