mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
refactor(example/bt): Refactor controller_hci_uart example for ESP32-C3/S3 to use UHCI driver
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
idf_component_register(SRCS "main.c"
|
idf_component_register(SRCS "main.c"
|
||||||
PRIV_REQUIRES bt nvs_flash esp_driver_uart esp_driver_gpio
|
PRIV_REQUIRES bt nvs_flash esp_driver_uart esp_ringbuf
|
||||||
INCLUDE_DIRS "")
|
INCLUDE_DIRS "")
|
||||||
|
|||||||
@@ -5,12 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp_private/periph_ctrl.h" // for enabling UHCI module, remove it after UHCI driver is released
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "driver/gpio.h"
|
#include "freertos/ringbuf.h"
|
||||||
#include "driver/uart.h"
|
#include "driver/uart.h"
|
||||||
#include "soc/lldesc.h"
|
#include "driver/uhci.h"
|
||||||
#include "esp_private/gdma.h"
|
|
||||||
#include "hal/uhci_ll.h"
|
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "esp_bt.h"
|
#include "esp_bt.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -21,6 +19,9 @@ static const char *tag = "UHCI";
|
|||||||
|
|
||||||
#define UART_RX_THRS (120)
|
#define UART_RX_THRS (120)
|
||||||
|
|
||||||
|
#define UHCI_MAX_RX_BUFFER_SIZE (512)
|
||||||
|
#define UHCI_MAX_TX_BUFFER_SIZE (512)
|
||||||
|
|
||||||
#define GPIO_UART_TXD_OUT (4)
|
#define GPIO_UART_TXD_OUT (4)
|
||||||
#define GPIO_UART_RXD_IN (5)
|
#define GPIO_UART_RXD_IN (5)
|
||||||
#define GPIO_UART_RTS_OUT (6)
|
#define GPIO_UART_RTS_OUT (6)
|
||||||
@@ -41,19 +42,21 @@ static void hci_uart_tl_finish_transfers(void);
|
|||||||
struct uart_txrxchannel {
|
struct uart_txrxchannel {
|
||||||
esp_bt_hci_tl_callback_t callback;
|
esp_bt_hci_tl_callback_t callback;
|
||||||
void *arg;
|
void *arg;
|
||||||
lldesc_t link;
|
uint8_t *buf;
|
||||||
|
uint32_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uart_env_tag {
|
struct uart_env_tag {
|
||||||
struct uart_txrxchannel tx;
|
struct uart_txrxchannel tx;
|
||||||
struct uart_txrxchannel rx;
|
struct uart_txrxchannel rx;
|
||||||
|
uhci_controller_handle_t uhci_handle;
|
||||||
|
SemaphoreHandle_t rx_process_sema;
|
||||||
|
RingbufHandle_t ringbufhandle;
|
||||||
|
volatile bool rx_eof;
|
||||||
|
uint8_t rx_data_buffer[UHCI_MAX_RX_BUFFER_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uart_env_tag uart_env;
|
static struct uart_env_tag uart_env = {0};
|
||||||
|
|
||||||
static uhci_dev_t *s_uhci_hw = &UHCI0;
|
|
||||||
static gdma_channel_handle_t s_rx_channel;
|
|
||||||
static gdma_channel_handle_t s_tx_channel;
|
|
||||||
|
|
||||||
static esp_bt_hci_tl_t s_hci_uart_tl_funcs = {
|
static esp_bt_hci_tl_t s_hci_uart_tl_funcs = {
|
||||||
._magic = ESP_BT_HCI_TL_MAGIC_VALUE,
|
._magic = ESP_BT_HCI_TL_MAGIC_VALUE,
|
||||||
@@ -82,16 +85,13 @@ static IRAM_ATTR void hci_uart_tl_recv_async(uint8_t *buf, uint32_t size, esp_bt
|
|||||||
assert(buf != NULL);
|
assert(buf != NULL);
|
||||||
assert(size != 0);
|
assert(size != 0);
|
||||||
assert(callback != NULL);
|
assert(callback != NULL);
|
||||||
|
|
||||||
uart_env.rx.callback = callback;
|
uart_env.rx.callback = callback;
|
||||||
uart_env.rx.arg = arg;
|
uart_env.rx.arg = arg;
|
||||||
|
uart_env.rx.size = size;
|
||||||
|
uart_env.rx.buf = buf;
|
||||||
|
|
||||||
memset(&uart_env.rx.link, 0, sizeof(lldesc_t));
|
xSemaphoreGive(uart_env.rx_process_sema);
|
||||||
uart_env.rx.link.buf = buf;
|
|
||||||
uart_env.rx.link.size = size;
|
|
||||||
|
|
||||||
s_uhci_hw->pkt_thres.thrs = size;
|
|
||||||
|
|
||||||
gdma_start(s_rx_channel, (intptr_t)(&uart_env.rx.link));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static IRAM_ATTR void hci_uart_tl_send_async(uint8_t *buf, uint32_t size, esp_bt_hci_tl_callback_t callback, void *arg)
|
static IRAM_ATTR void hci_uart_tl_send_async(uint8_t *buf, uint32_t size, esp_bt_hci_tl_callback_t callback, void *arg)
|
||||||
@@ -102,13 +102,11 @@ static IRAM_ATTR void hci_uart_tl_send_async(uint8_t *buf, uint32_t size, esp_bt
|
|||||||
|
|
||||||
uart_env.tx.callback = callback;
|
uart_env.tx.callback = callback;
|
||||||
uart_env.tx.arg = arg;
|
uart_env.tx.arg = arg;
|
||||||
|
uart_env.tx.size = size;
|
||||||
|
|
||||||
memset(&uart_env.tx.link, 0, sizeof(lldesc_t));
|
if (uhci_transmit(uart_env.uhci_handle, buf, size) != ESP_OK) {
|
||||||
uart_env.tx.link.length = size;
|
assert(0);
|
||||||
uart_env.tx.link.buf = buf;
|
}
|
||||||
uart_env.tx.link.eof = 1;
|
|
||||||
|
|
||||||
gdma_start(s_tx_channel, (intptr_t)(&uart_env.tx.link));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_uart_tl_flow_on(void)
|
static void hci_uart_tl_flow_on(void)
|
||||||
@@ -124,36 +122,18 @@ static void hci_uart_tl_finish_transfers(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static IRAM_ATTR bool hci_uart_tl_rx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
static IRAM_ATTR bool uhci_tx_done_cb(uhci_controller_handle_t uhci_ctrl,
|
||||||
|
const uhci_tx_done_event_data_t *edata, void *user_ctx)
|
||||||
{
|
{
|
||||||
assert(dma_chan == s_rx_channel);
|
|
||||||
assert(uart_env.rx.callback != NULL);
|
|
||||||
esp_bt_hci_tl_callback_t callback = uart_env.rx.callback;
|
|
||||||
void *arg = uart_env.rx.arg;
|
|
||||||
|
|
||||||
// clear callback pointer
|
|
||||||
uart_env.rx.callback = NULL;
|
|
||||||
uart_env.rx.arg = NULL;
|
|
||||||
|
|
||||||
// call handler
|
|
||||||
callback(arg, ESP_BT_HCI_TL_STATUS_OK);
|
|
||||||
|
|
||||||
// send notification to Bluetooth Controller task
|
|
||||||
esp_bt_h4tl_eif_io_event_notify(1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static IRAM_ATTR bool hci_uart_tl_tx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
|
||||||
{
|
|
||||||
assert(dma_chan == s_tx_channel);
|
|
||||||
assert(uart_env.tx.callback != NULL);
|
assert(uart_env.tx.callback != NULL);
|
||||||
|
assert(uart_env.tx.size == edata->sent_size);
|
||||||
esp_bt_hci_tl_callback_t callback = uart_env.tx.callback;
|
esp_bt_hci_tl_callback_t callback = uart_env.tx.callback;
|
||||||
void *arg = uart_env.tx.arg;
|
void *arg = uart_env.tx.arg;
|
||||||
|
|
||||||
// clear callback pointer
|
// clear callback pointer
|
||||||
uart_env.tx.callback = NULL;
|
uart_env.tx.callback = NULL;
|
||||||
uart_env.tx.arg = NULL;
|
uart_env.tx.arg = NULL;
|
||||||
|
uart_env.tx.size = 0;
|
||||||
|
|
||||||
// call handler
|
// call handler
|
||||||
callback(arg, ESP_BT_HCI_TL_STATUS_OK);
|
callback(arg, ESP_BT_HCI_TL_STATUS_OK);
|
||||||
@@ -164,39 +144,71 @@ static IRAM_ATTR bool hci_uart_tl_tx_eof_callback(gdma_channel_handle_t dma_chan
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uart_gpio_set(void)
|
static IRAM_ATTR bool uhci_rx_event_cb(uhci_controller_handle_t uhci_ctrl, const uhci_rx_event_data_t *edata, void *user_ctx)
|
||||||
{
|
{
|
||||||
gpio_config_t io_output_conf = {
|
BaseType_t done = xRingbufferSendFromISR(uart_env.ringbufhandle, edata->data, edata->recv_size, 0);
|
||||||
.intr_type = GPIO_INTR_DISABLE, //disable interrupt
|
if (!done) {
|
||||||
.mode = GPIO_MODE_OUTPUT, // output mode
|
assert(0);
|
||||||
.pin_bit_mask = GPIO_OUTPUT_PIN_SEL, // bit mask of the output pins
|
}
|
||||||
.pull_down_en = GPIO_PULLDOWN_DISABLE, // disable pull-down mode
|
if (edata->flags.totally_received) {
|
||||||
.pull_up_en = GPIO_PULLUP_DISABLE, // disable pull-up mode
|
uart_env.rx_eof = true;
|
||||||
};
|
}
|
||||||
gpio_config(&io_output_conf);
|
|
||||||
|
|
||||||
gpio_config_t io_input_conf = {
|
return true;
|
||||||
.intr_type = GPIO_INTR_DISABLE, //disable interrupt
|
|
||||||
.mode = GPIO_MODE_INPUT, // input mode
|
|
||||||
.pin_bit_mask = GPIO_INPUT_PIN_SEL, // bit mask of the input pins
|
|
||||||
.pull_down_en = GPIO_PULLDOWN_DISABLE, // disable pull-down mode
|
|
||||||
.pull_up_en = GPIO_PULLUP_DISABLE, // disable pull-down mode
|
|
||||||
};
|
|
||||||
gpio_config(&io_input_conf);
|
|
||||||
|
|
||||||
uart_set_pin(UART_HCI_NUM, GPIO_UART_TXD_OUT, GPIO_UART_RXD_IN, GPIO_UART_RTS_OUT, GPIO_UART_CTS_IN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void uhci_rx_done_process_task(void *arg)
|
||||||
|
{
|
||||||
|
size_t item_size = 0;
|
||||||
|
uint8_t *data = NULL;
|
||||||
|
while (1) {
|
||||||
|
xSemaphoreTake(uart_env.rx_process_sema, portMAX_DELAY);
|
||||||
|
data = xRingbufferReceiveUpTo(uart_env.ringbufhandle, &item_size, portMAX_DELAY, uart_env.rx.size);
|
||||||
|
if (item_size != uart_env.rx.size) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
memcpy(uart_env.rx.buf, data, item_size);
|
||||||
|
vRingbufferReturnItem(uart_env.ringbufhandle, data);
|
||||||
|
|
||||||
|
if (uart_env.rx_eof) {
|
||||||
|
uhci_receive(uart_env.uhci_handle, uart_env.rx_data_buffer, UHCI_MAX_RX_BUFFER_SIZE);
|
||||||
|
uart_env.rx_eof = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(uart_env.rx.callback != NULL);
|
||||||
|
esp_bt_hci_tl_callback_t callback = uart_env.rx.callback;
|
||||||
|
void *arg = uart_env.rx.arg;
|
||||||
|
|
||||||
|
// clear callback pointer
|
||||||
|
uart_env.rx.callback = NULL;
|
||||||
|
uart_env.rx.arg = NULL;
|
||||||
|
uart_env.rx.size = 0;
|
||||||
|
|
||||||
|
// call handler
|
||||||
|
callback(arg, ESP_BT_HCI_TL_STATUS_OK);
|
||||||
|
|
||||||
|
// send notification to Bluetooth Controller task
|
||||||
|
esp_bt_h4tl_eif_io_event_notify(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uhci_rx_prepare(void)
|
||||||
|
{
|
||||||
|
uhci_receive(uart_env.uhci_handle, uart_env.rx_data_buffer, UHCI_MAX_RX_BUFFER_SIZE);
|
||||||
|
|
||||||
|
uart_env.ringbufhandle = xRingbufferCreate(UHCI_MAX_RX_BUFFER_SIZE * 4, RINGBUF_TYPE_BYTEBUF);
|
||||||
|
if (uart_env.ringbufhandle == NULL) {
|
||||||
|
ESP_LOGE(tag, "Ringbuffer create failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uart_env.rx_process_sema = xSemaphoreCreateBinary();
|
||||||
|
xTaskCreate(uhci_rx_done_process_task, "trigger controller task", 4096, NULL, 5, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void uhci_uart_install(void)
|
void uhci_uart_install(void)
|
||||||
{
|
{
|
||||||
periph_module_enable(PERIPH_UHCI0_MODULE);
|
|
||||||
periph_module_reset(PERIPH_UHCI0_MODULE);
|
|
||||||
|
|
||||||
periph_module_enable(PERIPH_UART1_MODULE);
|
|
||||||
periph_module_reset(PERIPH_UART1_MODULE);
|
|
||||||
|
|
||||||
uart_gpio_set();
|
|
||||||
|
|
||||||
// configure UART1
|
// configure UART1
|
||||||
uart_config_t uart_config = {
|
uart_config_t uart_config = {
|
||||||
.baud_rate = CONFIG_EXAMPLE_HCI_UART_BAUDRATE,
|
.baud_rate = CONFIG_EXAMPLE_HCI_UART_BAUDRATE,
|
||||||
@@ -212,45 +224,27 @@ void uhci_uart_install(void)
|
|||||||
.source_clk = UART_SCLK_DEFAULT,
|
.source_clk = UART_SCLK_DEFAULT,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(uart_param_config(UART_HCI_NUM, &uart_config));
|
ESP_ERROR_CHECK(uart_param_config(UART_HCI_NUM, &uart_config));
|
||||||
|
ESP_ERROR_CHECK(uart_set_pin(UART_HCI_NUM, GPIO_UART_TXD_OUT, GPIO_UART_RXD_IN, GPIO_UART_RTS_OUT, GPIO_UART_CTS_IN));
|
||||||
|
|
||||||
// install DMA driver
|
uhci_controller_config_t uhci_cfg = {
|
||||||
gdma_channel_alloc_config_t tx_channel_config = {
|
.uart_port = UART_HCI_NUM,
|
||||||
.flags.reserve_sibling = 1,
|
.tx_trans_queue_depth = 1,
|
||||||
.direction = GDMA_CHANNEL_DIRECTION_TX,
|
.max_receive_internal_mem = UHCI_MAX_RX_BUFFER_SIZE,
|
||||||
|
.max_transmit_size = UHCI_MAX_TX_BUFFER_SIZE,
|
||||||
|
.dma_burst_size = 32,
|
||||||
|
.rx_eof_flags.idle_eof = 1,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(gdma_new_ahb_channel(&tx_channel_config, &s_tx_channel));
|
|
||||||
gdma_channel_alloc_config_t rx_channel_config = {
|
ESP_ERROR_CHECK(uhci_new_controller(&uhci_cfg, &uart_env.uhci_handle));
|
||||||
.direction = GDMA_CHANNEL_DIRECTION_RX,
|
|
||||||
.sibling_chan = s_tx_channel,
|
uhci_event_callbacks_t uhci_cbs = {
|
||||||
|
.on_rx_trans_event = uhci_rx_event_cb,
|
||||||
|
.on_tx_trans_done = uhci_tx_done_cb,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(gdma_new_ahb_channel(&rx_channel_config, &s_rx_channel));
|
|
||||||
|
|
||||||
gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
|
ESP_ERROR_CHECK(uhci_register_event_callbacks(uart_env.uhci_handle, &uhci_cbs, NULL));
|
||||||
gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0));
|
|
||||||
|
|
||||||
gdma_strategy_config_t strategy_config = {
|
uhci_rx_prepare();
|
||||||
.auto_update_desc = false,
|
|
||||||
.owner_check = false
|
|
||||||
};
|
|
||||||
gdma_apply_strategy(s_tx_channel, &strategy_config);
|
|
||||||
gdma_apply_strategy(s_rx_channel, &strategy_config);
|
|
||||||
|
|
||||||
gdma_rx_event_callbacks_t rx_cbs = {
|
|
||||||
.on_recv_eof = hci_uart_tl_rx_eof_callback
|
|
||||||
};
|
|
||||||
gdma_register_rx_event_callbacks(s_rx_channel, &rx_cbs, NULL);
|
|
||||||
|
|
||||||
gdma_tx_event_callbacks_t tx_cbs = {
|
|
||||||
.on_trans_eof = hci_uart_tl_tx_eof_callback
|
|
||||||
};
|
|
||||||
gdma_register_tx_event_callbacks(s_tx_channel, &tx_cbs, NULL);
|
|
||||||
|
|
||||||
// configure UHCI
|
|
||||||
uhci_ll_init(s_uhci_hw);
|
|
||||||
uhci_ll_rx_set_eof_mode(s_uhci_hw, UHCI_RX_LEN_EOF);
|
|
||||||
// disable software flow control
|
|
||||||
s_uhci_hw->escape_conf.val = 0;
|
|
||||||
uhci_ll_attach_uart_port(s_uhci_hw, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user