参考代码

This commit is contained in:
LokLiang
2024-03-28 12:19:52 +08:00
commit 7b86aa3362
96 changed files with 19986 additions and 0 deletions

79
app/app_main.c Normal file
View File

@@ -0,0 +1,79 @@
#include "app_main.h"
/* 标准库 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 配置 */
#include "config/board_config.h"
/* SDK */
#include "esp_err.h"
/* 通用模块 */
#include "button/button_event.h"
/* 应用模块 */
/* 自定义框架抽象层 */
#include "os/os.h"
#include "os/os_common.h"
#include "console.h"
#include "shell/sh_vset.h"
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG
#define SYS_LOG_DOMAIN "MAIN"
#include "sys_log.h"
nvs_handle g_nvs_hdl; // nvs 句柄
os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列
static void _init_nvs(void);
static int _change_mode_event_button(const button_event_t *event);
static void _vset_cb(sh_t *sh_hdl);
void work_app_main(void *arg)
{
/* 初始化 SDK 的 nvs 模块 */
_init_nvs();
/* 初始化按键检测和控制 */
button_init(PIN_BIT(g_cfg_board->key_boot.pin), g_cfg_board->key_boot.en_lev);
button_event_add_callback(g_cfg_board->key_boot.pin, _change_mode_event_button);
/* 启动 shell */
SYS_LOG_INF("app start");
vset_init(&g_uart_handle_vt100, _vset_cb);
os_thread_sleep(100);
shell_start();
}
static void _init_nvs(void)
{
#define _NVS_NAME "nvs-app"
if (g_nvs_hdl == 0)
{
ESP_ERROR_CHECK(nvs_open(_NVS_NAME, NVS_READWRITE, &g_nvs_hdl));
}
}
static int _change_mode_event_button(const button_event_t *event)
{
static const char *const stat_tab[] = {
[BUTTON_UP] = "up",
[BUTTON_DOWN] = "down",
[BUTTON_HELD] = "held",
};
if (event->event < __ARRAY_SIZE(stat_tab))
{
SYS_LOG_DBG("button stat: %s", stat_tab[event->event]);
}
return 0;
}
static void _vset_cb(sh_t *sh_hdl)
{
}

7
app/app_main.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include "nvs.h"
#include "os/os.h"
extern nvs_handle g_nvs_hdl; // nvs 句柄
extern os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列

395
app/button/button_event.c Normal file
View File

@@ -0,0 +1,395 @@
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "driver/gpio.h"
#include "os/os.h"
#include "button_event.h"
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN
#define SYS_LOG_DOMAIN "BUTTON"
#include "sys_log.h"
#define VOLTAGE_HIGH 1 // 高电平有效
#define VOLTAGE_LOW 0 // 高电平有效
#define DEBOUNCE_CHECK_VOLTAGE_CHANGED 1
#define DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED 0
#define DEBOUCE_CHECK_MAX_TIMES 5
typedef struct
{
uint8_t pin;
uint8_t is_inverted; // 是否反向,如果为真的话,则当按钮按下后,引脚为低电平
uint16_t history;
uint32_t down_time;
uint32_t next_long_time;
int current_voltage; // 当前引脚电平
int debounce_check_counter;
uint8_t allow_multiple_long_press_event;
uint8_t long_press_event_sent;
} debounce_t;
int pin_count = -1;
debounce_t *debounce;
static os_queue_t s_event_queue; // 当出现按键变化时被传送到的队列对象
static button_callback_t *g_callback_list_head = NULL;
static button_callback_t *g_callback_list_tail = NULL;
static os_work_t s_work_hdl;
static os_queue_t *_pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode);
static uint32_t millis()
{
return os_get_sys_time();
}
static int send_event(debounce_t *db, button_event_e ev)
{
static char *const ev_tbl[] = {
[BUTTON_UP] = "release",
[BUTTON_DOWN] = "press",
[BUTTON_HELD] = "held"};
SYS_ASSERT(ev < sizeof(ev_tbl), "ev: %d", ev);
SYS_LOG_DBG("event happend: pinnum: %d, ev: %s", db->pin, ev_tbl[ev]);
button_event_t event = {
.pin = db->pin,
.event = ev,
};
os_queue_send(&s_event_queue, &event, 0);
button_callback_t *tmp = g_callback_list_head;
button_callback_t *callback_list = NULL;
button_callback_t *callback_list_it = NULL;
while (tmp != NULL)
{
if (tmp->pin == db->pin)
{
button_callback_t *new_callback = NULL;
if (callback_list == NULL)
{
callback_list = os_malloc(sizeof(button_callback_t));
callback_list_it = callback_list;
new_callback = callback_list_it;
}
else
{
new_callback = os_malloc(sizeof(button_callback_t));
}
new_callback->pin = db->pin;
new_callback->event_callback = tmp->event_callback;
new_callback->next = NULL;
if (new_callback != callback_list_it)
{
callback_list_it->next = new_callback;
callback_list_it = new_callback;
}
}
tmp = tmp->next;
}
tmp = callback_list;
int stop_forward_event = 0;
while (tmp != NULL)
{
if (tmp->pin == db->pin)
{
SYS_LOG_DBG("do call back: pinnum: %d, ev: %s", db->pin, ev_tbl[ev]);
stop_forward_event = tmp->event_callback(&event);
if (stop_forward_event)
{
break;
}
}
tmp = tmp->next;
}
// 释放临时遍历对象
tmp = callback_list;
while (tmp)
{
button_callback_t *next_obj = tmp->next;
os_free(tmp);
tmp = next_obj;
}
return stop_forward_event;
}
static int update_debounce_counter(int pin_index)
{
debounce_t *button_event_info = &debounce[pin_index];
int level = gpio_get_level(button_event_info->pin);
if (button_event_info->current_voltage != level)
{
button_event_info->debounce_check_counter += 1;
}
else if (button_event_info->current_voltage == level && button_event_info->debounce_check_counter > 0)
{
button_event_info->debounce_check_counter -= 1;
}
// 防抖检测已经连续多次检测到相同的电平,则改变按钮当前的电平
if (button_event_info->debounce_check_counter == DEBOUCE_CHECK_MAX_TIMES)
{
button_event_info->current_voltage = level;
button_event_info->debounce_check_counter = 0;
SYS_LOG_DBG("button voltage changed, pin:%d, voltage:%d", button_event_info->pin, level);
return DEBOUNCE_CHECK_VOLTAGE_CHANGED;
}
return DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED;
}
static bool is_button_down(int pin_index)
{
debounce_t *button_event_info = &debounce[pin_index];
if (button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_LOW)
{
// 如果电平是反向的,并且当前是低电平,则认为按钮按下了
return true;
}
else if (!button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_HIGH)
{
// 如果电平不是反向的,并且当前是高电平,则认为按钮按下了
return true;
}
return false;
}
static bool is_button_up(int pin_index)
{
debounce_t *button_event_info = &debounce[pin_index];
if (button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_HIGH)
{
// 如果电平是反向的,并且当前是高电平,则认为按钮释放了
return true;
}
else if (!button_event_info->is_inverted && button_event_info->current_voltage == VOLTAGE_LOW)
{
// 如果电平不是反向的,并且当前是低电平,则认为按钮释放了
return true;
}
return false;
}
static void _work_handler_button(void *pvParameter)
{
static uint8_t cancel_laster_event = 1;
for (int idx = 0; idx < pin_count; idx++)
{
debounce_t *button_event_info = &debounce[idx];
int check_result = update_debounce_counter(idx); // 防抖处理
if (check_result == DEBOUNCE_CHECK_VOLTAGE_DID_NOT_CHANGED)
{
// 如果allow_multiple_long_press_event为false则一次长按无论长按多久只会触发一次长按事件
if (!button_event_info->allow_multiple_long_press_event && button_event_info->long_press_event_sent != 0)
{
continue;
}
// 电平没有变化,则检查按钮是否已经按下,以开始检查长按操作
if (button_event_info->down_time && millis() >= button_event_info->next_long_time)
{
button_event_info->next_long_time = millis() + LONG_PRESS_REPEAT;
button_event_info->long_press_event_sent = 1;
cancel_laster_event = send_event(&debounce[idx], BUTTON_HELD);
if (cancel_laster_event)
{
button_event_info->down_time = 0;
}
}
}
else
{
// 电平有变化,检查是按下还是松开
if (button_event_info->down_time == 0 && is_button_down(idx))
{
cancel_laster_event = 0;
SYS_LOG_DBG("update downtime for pin:%d", button_event_info->pin);
button_event_info->down_time = millis();
button_event_info->next_long_time = button_event_info->down_time + LONG_PRESS_DURATION;
cancel_laster_event = send_event(&debounce[idx], BUTTON_DOWN);
if (cancel_laster_event)
{
button_event_info->down_time = 0;
}
}
else if (button_event_info->down_time && is_button_up(idx))
{
button_event_info->down_time = 0;
button_event_info->long_press_event_sent = 0;
cancel_laster_event = send_event(&debounce[idx], BUTTON_UP);
if (cancel_laster_event)
{
button_event_info->down_time = 0;
}
}
}
}
os_work_later(10);
}
/**
* @brief 初始化按电平式的按键检测。
* 这将创建一个用于扫描按键的线程,当检测到按键变化时,将在线程中产生两种通知:
* 1. 发送到内部创建的队列中(提示:如果使用了 os_queue_regist() 注册工作项,该工作项将被自动唤醒)
* 2. 执行由 button_event_add_callback() 设置的回调函数(提示:同一引脚可设置多个回调)
*
* @param pin_select 按位表示 MCU 的引脚呈
* @param en_lev 触发电平
* @return os_queue_t* 由内部创建的消息队列
*/
os_queue_t *button_init(unsigned long long pin_select, uint8_t en_lev)
{
return _pulled_button_init(pin_select, en_lev == 0 ? GPIO_PULLUP_ONLY : GPIO_PULLDOWN_ONLY);
}
static os_queue_t *_pulled_button_init(unsigned long long pin_select, gpio_pull_mode_t pull_mode)
{
if (pin_count != -1)
{
SYS_LOG_WRN("Already initialized");
return NULL;
}
// Configure the pins
gpio_config_t io_conf;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = (pull_mode == GPIO_PULLUP_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN);
io_conf.pull_down_en = (pull_mode == GPIO_PULLDOWN_ONLY || pull_mode == GPIO_PULLUP_PULLDOWN);
io_conf.pin_bit_mask = pin_select;
io_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&io_conf);
// Scan the pin map to determine number of pins
pin_count = 0;
int pin = 0;
for (pin = 0; pin < sizeof(pin_select) * 8; pin++)
{
if ((1ULL << pin) & pin_select)
{
pin_count++;
}
}
// Initialize global state and s_event_queue
debounce = os_calloc(sizeof(debounce_t) * pin_count);
// s_event_queue = xQueueCreate(4, sizeof(button_event_t));
os_queue_create(&s_event_queue, 4, sizeof(button_event_t));
// Scan the pin map to determine each pin number, populate the state
uint32_t idx = 0;
for (pin = 0; pin < sizeof(pin_select) * 8; pin++)
{
if ((1ULL << pin) & pin_select)
{
SYS_LOG_DBG("Registering button input: %d", pin);
debounce_t *button_event_info = &debounce[idx];
button_event_info->pin = pin;
button_event_info->down_time = 0;
button_event_info->allow_multiple_long_press_event = 1;
button_event_info->is_inverted = (pull_mode == GPIO_PULLUP_ONLY);
idx++;
}
}
os_work_create(&s_work_hdl, "", _work_handler_button, NULL, OS_PRIORITY_HIGHEST);
os_work_submit(default_os_work_q_hdl, &s_work_hdl, 1000);
SYS_LOG_INF("Initialized");
return &s_event_queue;
}
/**
* @brief 添加回调函数:当由 button_init() 设置的对应的引脚号产生按键状态变化时,由内部线程创建执行的回调函数。
*
* @param pin 0..n
* @param callback int (*)(const button_event_t *event)
*/
void button_event_add_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback)
{
button_callback_t *callback_obj = os_malloc(sizeof(button_callback_t));
callback_obj->pin = pin;
callback_obj->event_callback = callback;
callback_obj->next = NULL;
if (g_callback_list_head == NULL)
{
g_callback_list_head = callback_obj;
g_callback_list_tail = g_callback_list_head;
}
else
{
g_callback_list_tail->next = callback_obj;
g_callback_list_tail = callback_obj;
}
}
void button_event_remove_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback)
{
button_callback_t *tmp = g_callback_list_head;
button_callback_t *previous_item = NULL;
SYS_LOG_DBG("remove pin:%d", pin);
while (tmp != NULL)
{
if (tmp->event_callback == callback && tmp->pin == pin)
{
if (previous_item)
{
previous_item->next = tmp->next; // 断开当前点节,并将前一个和后一个连接起来
os_free(tmp);
SYS_LOG_DBG("found item");
tmp = previous_item->next;
}
else
{
// 没有上一个节点,则将头部节点,设置为待移除节点的下一个节点
if (tmp->next)
{
g_callback_list_head = tmp->next;
}
else
{
g_callback_list_head = NULL;
SYS_LOG_DBG("####!!!!#####!!!!");
}
os_free(tmp);
tmp = g_callback_list_head;
SYS_LOG_DBG("case111");
}
SYS_LOG_DBG("2222");
}
else
{
previous_item = tmp;
tmp = tmp->next;
SYS_LOG_DBG("1111");
}
}
// 更新g_callback_list_tail指针
while (tmp != NULL)
{
g_callback_list_tail = tmp;
tmp = tmp->next;
}
SYS_LOG_DBG("doneeee1");
}

47
app/button/button_event.h Normal file
View File

@@ -0,0 +1,47 @@
/**
* @file button_event.h
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2023-09-04
*
* @copyright Copyright (c) 2023
*
*/
#pragma once
#include "os/os.h"
#include "driver/gpio.h"
#define PIN_BIT(x) (1ULL << x)
#define LONG_PRESS_DURATION (1000)
#define LONG_PRESS_REPEAT (200)
typedef enum
{
BUTTON_UP,
BUTTON_DOWN,
BUTTON_HELD
} button_event_e;
typedef struct
{
uint32_t pin;
button_event_e event;
} button_event_t;
typedef int (*BUTTON_EVENT_CALLBACK)(const button_event_t *event);
typedef struct button_callback_s
{
struct button_callback_s *next;
uint32_t pin;
BUTTON_EVENT_CALLBACK event_callback;
} button_callback_t;
os_queue_t *button_init(unsigned long long pin_select, uint8_t en_lev);
void button_event_add_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback);
void button_event_remove_callback(uint32_t pin, BUTTON_EVENT_CALLBACK callback);

27
app/config/board_config.c Normal file
View File

@@ -0,0 +1,27 @@
#include "board_config.h"
#include "driver/uart.h"
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG
#define SYS_LOG_DOMAIN "CFG"
#define CONS_ABORT()
#include "sys_log.h"
static cfg_board_t const s_cfg_board_default = {
/* 控制台串口 */
.uart_console = {
.pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_0,
.irq_prior = 0,
.br = 115200,
},
/* 启动按键 */
.key_boot = {
.pin = 9, // 用于切换灯效
.en_lev = 0, // 用于切换灯效
},
};
const cfg_board_t *g_cfg_board = &s_cfg_board_default;

40
app/config/board_config.h Normal file
View File

@@ -0,0 +1,40 @@
/**
* @file board_config.h
* @author LokLiang
* @brief 统一板载硬件描述数据结构
* @version 0.1
* @date 2023-11-24
*
* @copyright Copyright (c) 2023
*
* 目的与作用场景:
* 使同类型的固件适应不同的硬件版本。
* 例如同一类型产品,即使某些功能引脚改动,依然可以根据配置文件正确引导程序,而不需要新增特定的固件版本,旧的产品依然得到长期的支持。
*
* 数据设置:
* 只能在工厂中配置,除此之外不允许任何手段尝试修改。
* 数据地址固定为分区配置中
*
*/
#pragma once
#include "drivers/chip/_hal.h"
typedef struct // 按键配置
{
uint8_t pin; // 引脚号
uint8_t en_lev; // 触发电平
} cfg_board_key_t;
typedef struct // 数据结构一旦定下不可随意变更
{
/* 硬件描述类 */
hal_uart_hdl_t uart_console; // 控制台
cfg_board_key_t key_boot; // 启动按键
/* 产品功能描述类 */
} cfg_board_t;
extern const cfg_board_t *g_cfg_board; // 配置数据

View File

@@ -0,0 +1,40 @@
#if (CONFIG_BOARD_NAME_BEC_LED_STRIP_ESP32C3)
#define CONFIG_IDF_TARGET "esp32c3" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */
static cfg_board_t const s_cfg_board_default = {
.firmware_str = PRODUCT_TYPE,
.platform_str = CONFIG_IDF_TARGET,
.board_name = "bec_led_strip_esp32c3",
/* 控制台串口 */
.uart_console = {
.pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_0,
.irq_prior = 0,
.br = 115200,
},
/* 数据透传串口 */
.uart_fc = {
.pin_txd = {3, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {5, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_1,
.irq_prior = 24,
.br = 115200,
},
.led_spi = {
.spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI
.pin = 18, // 模拟 PWM 输出引脚
},
/* 启动按键 */
.key_boot = {
.pin = 7, // 用于切换灯效
.en_lev = 1, // 用于切换灯效
},
};
#endif

View File

@@ -0,0 +1,40 @@
#if (CONFIG_BOARD_NAME_DEVKIT_ESP32C2)
#define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */
static cfg_board_t const s_cfg_board_default = {
.firmware_str = PRODUCT_TYPE,
.platform_str = CONFIG_IDF_TARGET,
.board_name = "devkit_esp32c2",
/* 控制台串口 */
.uart_console = {
.pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_0,
.irq_prior = 0,
.br = 115200,
},
/* 数据透传串口 */
.uart_fc = {
.pin_txd = {7, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {6, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_1,
.irq_prior = 24,
.br = 115200,
},
.led_spi = {
.spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI
.pin = 1, // 模拟 PWM 输出引脚
},
/* 启动按键 */
.key_boot = {
.pin = 9, // 用于切换灯效
.en_lev = 0, // 用于切换灯效
},
};
#endif

View File

@@ -0,0 +1,40 @@
#if (CONFIG_BOARD_NAME_DEVKIT_ESP32C3)
#define CONFIG_IDF_TARGET "esp32c3" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */
static cfg_board_t const s_cfg_board_default = {
.firmware_str = PRODUCT_TYPE,
.platform_str = CONFIG_IDF_TARGET,
.board_name = "devkit_esp32c3",
/* 控制台串口 */
.uart_console = {
.pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_0,
.irq_prior = 0,
.br = 115200,
},
/* 数据透传串口 */
.uart_fc = {
.pin_txd = {7, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {6, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_1,
.irq_prior = 24,
.br = 115200,
},
.led_spi = {
.spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI
.pin = 10, // 模拟 PWM 输出引脚
},
/* 启动按键 */
.key_boot = {
.pin = 9, // 用于切换灯效
.en_lev = 0, // 用于切换灯效
},
};
#endif

View File

@@ -0,0 +1,40 @@
#if (CONFIG_BOARD_NAME_FIXEDWING_NEW)
#define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */
static cfg_board_t const s_cfg_board_default = {
.firmware_str = PRODUCT_TYPE,
.platform_str = CONFIG_IDF_TARGET,
.board_name = "fixedwing_new",
/* 控制台串口 */
.uart_console = {
.pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_0,
.irq_prior = 0,
.br = 115200,
},
/* 数据透传串口 */
.uart_fc = {
.pin_txd = {7, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {6, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_1,
.irq_prior = 24,
.br = 115200,
},
.led_spi = {
.spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI
.pin = 1, // 模拟 PWM 输出引脚
},
/* 启动按键 */
.key_boot = {
.pin = 3, // 用于切换灯效
.en_lev = 1, // 用于切换灯效
},
};
#endif

View File

@@ -0,0 +1,40 @@
#if (CONFIG_BOARD_NAME_FIXEDWING_OLD)
#define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */
static cfg_board_t const s_cfg_board_default = {
.firmware_str = PRODUCT_TYPE,
.platform_str = CONFIG_IDF_TARGET,
.board_name = "fixedwing_old",
/* 控制台串口 */
.uart_console = {
.pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {44, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_0,
.irq_prior = 0,
.br = 115200,
},
/* 数据透传串口 */
.uart_fc = {
.pin_txd = {8, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP},
.pin_rxd = {7, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP},
.id = UART_NUM_1,
.irq_prior = 24,
.br = 115200,
},
.led_spi = {
.spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI
.pin = 1, // 模拟 PWM 输出引脚
},
/* 启动按键 */
.key_boot = {
.pin = 3, // 用于切换灯效
.en_lev = 1, // 用于切换灯效
},
};
#endif

158
app/console.c Normal file
View File

@@ -0,0 +1,158 @@
#include "driver/uart.h"
#include "console.h"
#include "shell/sh.h"
#include "os/os.h"
#include "drivers/chip/uart.h"
#include "config/board_config.h"
#include "sys_log.h"
static shell_input_cb s_input_cb;
static void initialize_console(void)
{
/* Drain stdout before reconfiguring it */
fflush(stdout);
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = {
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
.source_clk = UART_SCLK_REF_TICK,
#else
.source_clk = UART_SCLK_XTAL,
#endif
};
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
}
static void _work_sh(void *arg)
{
char c;
int flag = 0;
shell_input_cb input_cb = s_input_cb;
if (input_cb)
{
static char last_key;
while (drv_uart_poll_read(g_cfg_board->uart_console.id, &c) > 0)
{
if (c != '\n' || last_key != '\r')
{
input_cb(&g_uart_handle_vt100, c);
flag = 1;
}
last_key = c;
}
}
else
{
while (drv_uart_poll_read(g_cfg_board->uart_console.id, &c) > 0)
{
sh_putc(&g_uart_handle_vt100, c);
flag = 1;
}
}
if (flag)
{
fflush(stdout);
}
os_work_later(10);
}
static int _port_sh_vprint_fn(const char *format, va_list va)
{
return vprintf(format, va);
}
/**
* @brief 启动 shell.
* 这将:
* 1. 执行对应的初始化
* 2. 自动默认工作队列中创建一个解析数据的后台
*/
void shell_start(void)
{
static uint8_t start_flag = 0;
if (start_flag == 0)
{
/* 初始化控制台串口 */
initialize_console();
/* 执行 shell 内部的初始化 */
sh_register_external(_port_sh_vprint_fn);
/* 注册系统命令 */
extern void soc_shell_register(void);
soc_shell_register();
/* 创建 shell 的接收任务 _work_sh() */
static os_work_t work_handler_shell;
os_work_create(&work_handler_shell, "work-shell", _work_sh, NULL, 1);
os_work_submit(default_os_work_q_hdl, &work_handler_shell, 0);
/* 设置 shell 的提示符 */
g_uart_handle_vt100.disable_echo = !CONFIG_SYS_LOG_CONS_ON; // 关闭回显
sh_putstr_quiet(&g_uart_handle_vt100, "sh version\r");
sh_set_prompt(&g_uart_handle_vt100, "esp32:/> ");
sh_putc(&g_uart_handle_vt100, '\r');
fflush(stdout);
start_flag = 1;
}
}
/**
* @brief 临时改变终端的输入接口。
* 使用 shell_input_restore() 可恢复预设的接口
*
* @param cb 输入接口
*/
void shell_input_set(shell_input_cb cb)
{
s_input_cb = cb;
}
/**
* @brief 恢复 shell_input_set() 被执行前的设置
*
*/
void shell_input_restore(void)
{
s_input_cb = NULL;
}
/**
* @brief 在执行命令的过程中使用。
* 若被执行的命令需要循环执行时,使用这个函数将读取控制台收到的数据,
* 当收到 ESC 按键时返回真值,此时可退出该命令的执行函数
*
* @retval true 按下了 ESC 键
* @retval false 未按下 ESC 键
*/
bool shell_is_aobrt(void)
{
char c;
if (drv_uart_poll_read(g_cfg_board->uart_console.id, &c) > 0)
{
if (c == '\x1b')
{
fflush(stdout);
return true;
}
}
return false;
}

16
app/console.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef __CONSOLE_H__
#define __CONSOLE_H__
#include <stdbool.h>
#include "shell/sh.h"
typedef void (*shell_input_cb)(sh_t *sh_hdl, char c);
void shell_start();
void shell_input_set(shell_input_cb cb);
void shell_input_restore(void);
bool shell_is_aobrt(void);
#endif

View File

@@ -0,0 +1,249 @@
#include "drivers/data_port/sb_data_port.h"
#include "os/os.h"
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF
#define SYS_LOG_DOMAIN "DATA"
#define CONS_ABORT()
#include "sys_log.h"
/**
* @brief 由具体驱动实现的,以达到节省资源为主要目的,启动数据接口
*
* @param port 由对应的驱动提供的绑定接口获得的句柄
* @retval 0 成功
*/
int sb_data_port_start(sb_data_port_t *port)
{
if (sb_data_port_is_started(port))
{
SYS_LOG_WRN("Data interface repeat started");
return 0;
}
if (port == NULL || port->vtable == NULL)
{
return false;
}
if (port->vtable->start)
{
return port->vtable->start(port);
}
else
{
SYS_LOG_ERR("The data interface has not been defined");
return -1;
}
}
/**
* @brief 由具体驱动实现的,以达到节省资源为主要目的,关闭数据接口
*
* @param port 由对应的驱动提供的绑定接口获得的句柄
* @retval 0 成功
*/
int sb_data_port_stop(sb_data_port_t *port)
{
if (!sb_data_port_is_started(port))
{
SYS_LOG_WRN("The data interface has not been started yet");
return 0;
}
if (port->vtable->stop)
{
return port->vtable->stop(port);
}
else
{
SYS_LOG_ERR("The data interface has not been defined");
return -1;
}
}
/**
* @brief 由具体驱动实现的,写数据到对应的接口。
* 建议这些数据将写入到驱动的缓存中,如果 wait_ms 为 0 立即返回,
* 如果 wait_ms > 0 则这个时间由本函数计时并处理,但期间可能出现堵塞。
* 注意如果有选择 wait_ms > 0 的情况,驱动中收取缓存的线程与应用不能是同一线程。
*
* @param port 由对应的驱动提供的绑定接口获得的句柄
* @param data 来源数据
* @param size 数据长度(字节)
* @param wait_ms 超时时间(毫秒)
* @retval < 0 操作失败或在指定的时间内未满足指定的操作长度
* @retval >= 0 实际已缓存的长度
*/
int sb_data_port_write(sb_data_port_t *port, const void *data, uint32_t size, uint32_t wait_ms)
{
if (!sb_data_port_is_started(port))
{
SYS_LOG_WRN("The data interface has not been started yet");
return -1;
}
if (port->vtable->write)
{
int ret = 0;
int cmp_time = os_get_sys_time();
while (size)
{
int curr_time = os_get_sys_time();
int wsize = port->vtable->write(port, data, size);
if (wsize > 0)
{
cmp_time = curr_time;
data = &((uint8_t *)data)[wsize];
size -= wsize;
ret += wsize;
}
else if (wsize == 0)
{
if (wait_ms == 0)
{
break;
}
else if (curr_time - cmp_time > wait_ms)
{
return -1;
}
else
{
os_thread_sleep(1);
continue;
}
}
else
{
return -1;
}
}
return ret;
}
else
{
SYS_LOG_ERR("The data interface has not been defined");
return -1;
}
}
/**
* @brief 由具体驱动实现的,从数据接口中读取已缓存的数据。
* 如果 wait_ms 为 0 将立即返回,
* 如果 wait_ms > 0 则这个时间由本函数计时并处理,期间可能出现堵塞。
* 注意如果有选择 wait_ms > 0 的情况,驱动中收取缓存的线程与应用不能是同一线程。
*
* @param port 由对应的驱动提供的绑定接口获得的句柄
* @param buffer[out] 保存到目标内存。值为 NULL 表示仅释放空间
* @param length 目标内存长度(字节)
* @param wait_ms 超时时间(毫秒)
* @retval < 0 操作失败或在指定的时间内未满足指定的操作长度
* @retval >= 0 实际已读取的长度
*/
int sb_data_port_read(sb_data_port_t *port, void *buffer, uint32_t length, uint32_t wait_ms)
{
if (!sb_data_port_is_started(port))
{
SYS_LOG_WRN("The data interface has not been started yet");
return -1;
}
if (port->vtable->read)
{
int ret = 0;
int cmp_time = os_get_sys_time();
while (length)
{
int curr_time = os_get_sys_time();
int rsize = port->vtable->read(port, buffer, length);
if (rsize > 0)
{
cmp_time = curr_time;
buffer = &((uint8_t *)buffer)[rsize];
length -= rsize;
ret += rsize;
}
else if (rsize == 0)
{
if (wait_ms == 0)
{
break;
}
else if (curr_time - cmp_time > wait_ms)
{
return -1;
}
else
{
os_thread_sleep(1);
continue;
}
}
else
{
return -1;
}
}
return ret;
}
else
{
SYS_LOG_ERR("The data interface has not been defined");
return -1;
}
}
/**
* @brief 由具体驱动实现的,获取当前数据接口是否可用(是否已启动)。
* 本套接口内部的所有其他接口内部都会优先经过这个接口确认状态,驱动内部的其他接口不需要二次确认。
*
* @param port 由对应的驱动提供的绑定接口获得的句柄
* @retval true 接口已启动并可用
* @retval false 接口未启动,不可用
*/
bool sb_data_port_is_started(sb_data_port_t *port)
{
SYS_ASSERT(port, "Data interface not bound");
SYS_ASSERT(port->vtable, "Data interface not bound");
if (port == NULL || port->vtable == NULL)
{
return false;
}
if (port->vtable->is_started)
{
return port->vtable->is_started(port);
}
else
{
SYS_LOG_ERR("The data interface has not been defined");
return false;
}
}
/**
* @brief 由具体驱动实现的,获取当前数据接口的本次可读长度
*
* @param port 由对应的驱动提供的绑定接口获得的句柄
* @retval uint32_t sb_data_port_read() 当前可读取的接收缓存长度
*/
uint32_t sb_data_port_get_rx_length(sb_data_port_t *port)
{
if (!sb_data_port_is_started(port))
{
return 0;
}
if (port->vtable->get_rx_length)
{
return port->vtable->get_rx_length(port);
}
else
{
SYS_LOG_ERR("The data interface has not been defined");
return -1;
}
}

View File

@@ -0,0 +1,42 @@
/**
* @file sb_data_port.h
* @author LokLiang
* @brief 统一的数据接口定义
* @version 0.1
* @date 2023-11-14
*
* @copyright Copyright (c) 2023
*
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef struct sb_data_port_s sb_data_port_t;
typedef struct
{
int (*start)(sb_data_port_t *port); // 由具体驱动实现的,以达到节省资源为主要目的,启动数据接口
int (*stop)(sb_data_port_t *port); // 由具体驱动实现的,以达到节省资源为主要目的,关闭数据接口
int (*write)(sb_data_port_t *port, const void *data, uint32_t size); // 由具体驱动实现的,写数据到对应的接口。
int (*read)(sb_data_port_t *port, void *buffer, uint32_t length); // 由具体驱动实现的,从数据接口中读取已缓存的数据。
bool (*is_started)(sb_data_port_t *port); // 由具体驱动实现的,获取当前数据接口是否可用(是否已启动)
uint32_t (*get_rx_length)(sb_data_port_t *port); // 由具体驱动实现的,获取当前数据接口的本次可读长度
} sb_data_port_vtable_t;
struct sb_data_port_s
{
const sb_data_port_vtable_t *vtable; // 接口
void *data; // 数据
};
int sb_data_port_start(sb_data_port_t *port);
int sb_data_port_stop(sb_data_port_t *port);
int sb_data_port_write(sb_data_port_t *port, const void *data, uint32_t size, uint32_t wait_ms);
int sb_data_port_read(sb_data_port_t *port, void *buffer, uint32_t length, uint32_t wait_ms);
bool sb_data_port_is_started(sb_data_port_t *port);
uint32_t sb_data_port_get_rx_length(sb_data_port_t *port);

View File

@@ -0,0 +1,486 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "os/os.h"
#include "uart_port.h"
#include "driver/uart.h"
#include "drivers/data_port/sb_data_port.h"
#define CONFIG_SYS_LOG_DUMP_ON 0
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN
#define SYS_LOG_DOMAIN "UART"
#include "sys_log.h"
#define _MAX_UART_NUM 3
#define UART_IN_TAST_STK_SIZE 2048
typedef struct
{
uint16_t size;
uint8_t data[0];
} __fifo_data_t;
typedef struct
{
int uart_num;
int baudrate;
int tx_pin;
int rx_pin;
int rx_task_priority;
uint16_t buffer_size;
uint8_t frame_ms;
volatile uint16_t buffer_used;
os_work_t *rx_resume_work;
QueueHandle_t event_queue;
os_thread_t rx_thread_hdl;
union
{
os_pipe_t rx_pipe;
os_fifo_t rx_fifo;
};
} __uart_data_t;
static void _uart_rx_thread(void *parmas)
{
uart_event_t event;
int ret = 0;
int len = 0;
int rx_len = 0;
__uart_data_t *uart_data = ((sb_data_port_t *)parmas)->data;
const uint32_t TMP_BUF_LEN = 1024;
uint8_t tmpbuffer[TMP_BUF_LEN];
TickType_t tick = portMAX_DELAY;
uint16_t recv_total = 0;
os_time_t rx_time = 0;
for (;;)
{
if (xQueueReceive(uart_data->event_queue, (void *)&event, tick))
{
switch (event.type)
{
case UART_DATA:
{
rx_len = event.size;
while (rx_len > 0)
{
if (uart_data->frame_ms == 0)
{
len = (rx_len > TMP_BUF_LEN) ? TMP_BUF_LEN : rx_len;
ret = uart_read_bytes(uart_data->uart_num, tmpbuffer, len, portMAX_DELAY);
if (ret <= 0)
{
break;
}
rx_len -= ret;
// 将数据写入ringbuffer由外部轮询ringbuffer
uint8_t *p = tmpbuffer;
for (int i = 0; i < 100 && ret != 0; i++)
{
int write_result = os_pipe_fifo_fill(&uart_data->rx_pipe, p, ret);
p = &((uint8_t *)p)[write_result];
ret -= write_result;
if (ret != 0)
{
os_thread_sleep(10);
}
}
if (ret != 0)
{
SYS_LOG_ERR("write data to rx_pipe_obj failed, remain: %d bytes", ret);
}
}
else
{
len = sizeof(tmpbuffer) - recv_total;
if (len > rx_len)
{
len = rx_len;
}
ret = uart_read_bytes(uart_data->uart_num, &tmpbuffer[recv_total], len, portMAX_DELAY);
if (ret <= 0)
{
break;
}
rx_len -= ret;
recv_total += ret;
if (recv_total >= sizeof(tmpbuffer))
{
rx_time = os_get_sys_time() - uart_data->frame_ms - 1; // 使退出 switch 后的超时条件必定成立而立即保存数据
break;
}
rx_time = os_get_sys_time();
}
}
}
break;
// Event of HW FIFO overflow detected
case UART_FIFO_OVF:
SYS_LOG_WRN("hw fifo overflow");
// If fifo overflow happened, you should consider adding flow control for your application.
// The ISR has already reset the rx FIFO,
uart_flush_input(uart_data->uart_num);
xQueueReset(uart_data->event_queue);
break;
// Event of UART ring buffer full
case UART_BUFFER_FULL:
SYS_LOG_WRN("ring buffer full");
// If buffer full happened, you should consider encreasing your buffer size
uart_flush_input(uart_data->uart_num);
xQueueReset(uart_data->event_queue);
break;
// Event of UART RX break detected
case UART_BREAK:
SYS_LOG_WRN("uart rx break");
break;
// Event of UART parity check error
case UART_PARITY_ERR:
SYS_LOG_WRN("uart parity error");
break;
// Event of UART frame error
case UART_FRAME_ERR:
SYS_LOG_WRN("uart frame error");
break;
// Others
default:
SYS_LOG_WRN("uart event type: %d", event.type);
break;
}
}
if (uart_data->frame_ms != 0)
{
if (os_get_sys_time() - rx_time > uart_data->frame_ms)
{
if (recv_total > 0)
{
while (uart_data->buffer_used >= uart_data->buffer_size)
{
os_thread_sleep(1);
}
__fifo_data_t *fifo_data = os_fifo_alloc(sizeof(__fifo_data_t) + recv_total);
if (fifo_data)
{
os_scheduler_suspend();
uart_data->buffer_used += recv_total + sizeof(((__fifo_data_t *)0)->size);
os_scheduler_resume();
fifo_data->size = recv_total;
memcpy(fifo_data->data, tmpbuffer, recv_total);
os_fifo_put(&uart_data->rx_fifo, fifo_data);
}
recv_total = 0;
tick = 1;
}
else
{
tick = portMAX_DELAY;
}
}
else
{
tick = 1;
}
}
}
}
static void uart_port_initialize(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
uart_config_t uart_config;
memset(&uart_config, 0, sizeof(uart_config));
uart_config.baud_rate = uart_data->baudrate;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
esp_err_t ret = uart_set_pin(uart_data->uart_num, uart_data->tx_pin, uart_data->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
if (ret != ESP_OK)
{
SYS_LOG_ERR("failed to set uart pin:%d, %d", uart_data->uart_num, ret);
return;
}
ret = uart_param_config(uart_data->uart_num, &uart_config);
if (ret != ESP_OK)
{
SYS_LOG_ERR("failed to set uart param:%d, %d", uart_data->uart_num, ret);
return;
}
ret = uart_driver_install(uart_data->uart_num, 0x400, 0x400, 3, &(uart_data->event_queue), 0);
if (ret != ESP_OK)
{
SYS_LOG_ERR("failed to uart_driver_install:%d, %d", uart_data->uart_num, ret);
return;
}
SYS_LOG_INF("uart_driver_install ok. (%d, %d, %d, %d, %d, %d)", uart_data->uart_num, 0x400, 0x400, 3, (uint32_t)&uart_data->event_queue, 0);
}
static int _uart_port_start(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
SYS_LOG_INF("uart start, uartnum:%d", uart_data->uart_num);
if (uart_data->frame_ms == 0)
{
if (!os_pipe_is_valid(&uart_data->rx_pipe))
{
if (os_pipe_create(&uart_data->rx_pipe, uart_data->buffer_size) != OS_OK)
{
port->vtable->stop(port);
return -1;
}
os_pipe_regist(&uart_data->rx_pipe, uart_data->rx_resume_work, 0);
}
}
else
{
if (!os_fifo_q_is_valid(&uart_data->rx_fifo))
{
if (os_fifo_q_create(&uart_data->rx_fifo) != OS_OK)
{
port->vtable->stop(port);
return -1;
}
os_fifo_q_regist(&uart_data->rx_fifo, uart_data->rx_resume_work, 0);
}
uart_data->buffer_used = 0;
}
if (!uart_is_driver_installed(uart_data->uart_num))
{
uart_port_initialize(port);
}
if (!os_thread_is_valid(&uart_data->rx_thread_hdl))
{
if (os_thread_create(&uart_data->rx_thread_hdl,
"uart_rx",
_uart_rx_thread,
port,
UART_IN_TAST_STK_SIZE,
uart_data->rx_task_priority) != OS_OK)
{
SYS_LOG_WRN("task create fail");
port->vtable->stop(port);
return -1;
}
}
return 0;
}
static int _uart_port_stop(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
if (os_thread_is_valid(&uart_data->rx_thread_hdl))
{
os_thread_delete(&uart_data->rx_thread_hdl);
}
if (uart_is_driver_installed(uart_data->uart_num))
{
uart_driver_delete(uart_data->uart_num);
}
if (uart_data->frame_ms == 0)
{
if (os_pipe_is_valid(&uart_data->rx_pipe))
{
os_pipe_delete(&uart_data->rx_pipe);
}
}
else
{
if (os_fifo_q_is_valid(&uart_data->rx_fifo))
{
os_fifo_q_delete(&uart_data->rx_fifo);
}
}
return 0;
}
static int _uart_port_write(sb_data_port_t *port, const void *data, uint32_t size)
{
__uart_data_t *uart_data = port->data;
int ret = uart_write_bytes(uart_data->uart_num, data, size);
if (ret > 0)
{
SYS_LOG_DUMP(data, ret, 0, 0);
}
return ret;
}
static int _uart_port_read(sb_data_port_t *port, void *data, uint32_t size)
{
__uart_data_t *uart_data = port->data;
int ret;
if (uart_data->frame_ms == 0)
{
ret = os_pipe_fifo_read(&uart_data->rx_pipe, data, size);
}
else
{
__fifo_data_t *fifo_data = os_fifo_take(&uart_data->rx_fifo, 0);
if (fifo_data)
{
ret = size < fifo_data->size ? size : fifo_data->size;
if (data)
{
memcpy(data, fifo_data->data, ret);
}
os_fifo_free(fifo_data);
os_scheduler_suspend();
uart_data->buffer_used -= ret + sizeof(((__fifo_data_t *)0)->size);
os_scheduler_resume();
}
else
{
ret = 0;
}
}
if (ret > 0)
{
SYS_LOG_DUMP(data, ret, 0, 0);
}
return ret;
}
static bool _uart_port_is_tart(sb_data_port_t *port)
{
if (port == NULL)
{
return false;
}
__uart_data_t *uart_data = port->data;
if (uart_data == NULL)
{
return false;
}
if (uart_data->frame_ms == 0)
{
if (!os_pipe_is_valid(&uart_data->rx_pipe))
{
return false;
}
}
else
{
if (!os_fifo_q_is_valid(&uart_data->rx_fifo))
{
return false;
}
}
return true;
}
static uint32_t _uart_port_get_rx_length(sb_data_port_t *port)
{
__uart_data_t *uart_data = port->data;
if (uart_data->frame_ms == 0)
{
return os_pipe_get_valid_size(&uart_data->rx_pipe);
}
else
{
__fifo_data_t *fifo_data = os_fifo_peek_head(&uart_data->rx_fifo);
if (fifo_data)
{
return fifo_data->size;
}
else
{
return 0;
}
}
}
static sb_data_port_vtable_t const s_uart_vtable = {
.start = _uart_port_start,
.stop = _uart_port_stop,
.write = _uart_port_write,
.read = _uart_port_read,
.is_started = _uart_port_is_tart,
.get_rx_length = _uart_port_get_rx_length,
};
int sb_uart_port_init(void)
{
return 0;
}
/**
* @brief
*
* @param uart_num 串口号 0..n
* @param baudrate 波特率
* @param tx_pin TXD 引脚
* @param rx_pin RXD 引脚
* @param rx_task_priority 内部接收任务的优先级
* @param buffer_size 接收缓存大小
* @param frame_ms 0: 数据流, > 0: 最少连续有 n 毫秒没有收到数据时成为一帧
* @param rx_resume_work 当收到新数据时唤醒的工作项,可设置为 NULL
* @return sb_data_port_t*
*/
sb_data_port_t *sb_uart_port_bind(int uart_num,
int baudrate,
int tx_pin,
int rx_pin,
int rx_task_priority,
uint16_t buffer_size,
uint8_t frame_ms,
os_work_t *rx_resume_work)
{
static sb_data_port_t s_uart_port[_MAX_UART_NUM];
static __uart_data_t s_uart_data[_MAX_UART_NUM];
sb_data_port_t *port = &s_uart_port[uart_num];
__uart_data_t *uart_data = &s_uart_data[uart_num];
SYS_ASSERT(uart_num < _MAX_UART_NUM, "");
SYS_ASSERT(port->data == NULL, "The interface has already been bound");
uart_data->uart_num = uart_num;
uart_data->baudrate = baudrate;
uart_data->tx_pin = tx_pin;
uart_data->rx_pin = rx_pin;
uart_data->rx_task_priority = rx_task_priority;
uart_data->buffer_size = buffer_size;
uart_data->frame_ms = frame_ms;
uart_data->rx_resume_work = rx_resume_work;
port->vtable = &s_uart_vtable;
port->data = uart_data;
return port;
}
void sb_uart_port_unbind(sb_data_port_t *port)
{
if (_uart_port_is_tart(port))
{
_uart_port_stop(port);
}
port->data = NULL;
}

View File

@@ -0,0 +1,28 @@
/**
* @file uart_port.h
* @author LokLiang
* @brief
* @version 0.1
* @date 2023-09-04
*
* @copyright Copyright (c) 2023
*
*/
#pragma once
#include "../sb_data_port.h"
#include "os/os.h"
int sb_uart_port_init(void);
sb_data_port_t *sb_uart_port_bind(int uartNum,
int baudrate,
int tx_pin,
int rx_pin,
int rx_task_priority,
uint16_t buffer_size,
uint8_t frame_ms,
os_work_t *rx_resume_work);
void sb_uart_port_unbind(sb_data_port_t *port);