参考代码
This commit is contained in:
79
app/app_main.c
Normal file
79
app/app_main.c
Normal 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
7
app/app_main.h
Normal 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
395
app/button/button_event.c
Normal 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
47
app/button/button_event.h
Normal 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
27
app/config/board_config.c
Normal 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
40
app/config/board_config.h
Normal 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; // 配置数据
|
||||
40
app/config/board_config/bec_led_strip_esp32c3.h
Normal file
40
app/config/board_config/bec_led_strip_esp32c3.h
Normal 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
|
||||
40
app/config/board_config/devkit_esp32c2.h
Normal file
40
app/config/board_config/devkit_esp32c2.h
Normal 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
|
||||
40
app/config/board_config/devkit_esp32c3.h
Normal file
40
app/config/board_config/devkit_esp32c3.h
Normal 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
|
||||
40
app/config/board_config/fixedwing_new.h
Normal file
40
app/config/board_config/fixedwing_new.h
Normal 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
|
||||
40
app/config/board_config/fixedwing_old.h
Normal file
40
app/config/board_config/fixedwing_old.h
Normal 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
158
app/console.c
Normal 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
16
app/console.h
Normal 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
|
||||
249
app/drivers/data_port/sb_data_port.c
Executable file
249
app/drivers/data_port/sb_data_port.c
Executable 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;
|
||||
}
|
||||
}
|
||||
42
app/drivers/data_port/sb_data_port.h
Executable file
42
app/drivers/data_port/sb_data_port.h
Executable 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);
|
||||
486
app/drivers/data_port/uart/uart_port.c
Executable file
486
app/drivers/data_port/uart/uart_port.c
Executable 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;
|
||||
}
|
||||
28
app/drivers/data_port/uart/uart_port.h
Executable file
28
app/drivers/data_port/uart/uart_port.h
Executable 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);
|
||||
Reference in New Issue
Block a user