diff --git a/components/bt/include/esp32c3/include/esp_bt.h b/components/bt/include/esp32c3/include/esp_bt.h index 7a991d121e..f2ee5d8dbc 100644 --- a/components/bt/include/esp32c3/include/esp_bt.h +++ b/components/bt/include/esp32c3/include/esp_bt.h @@ -93,6 +93,13 @@ enum { ESP_BT_COEX_PHY_CODED_TX_RX_TIME_LIMIT_FORCE_ENABLE, /*!< Always Enable the limit */ }; +#define ESP_BT_HCI_TL_STATUS_OK (0) /*!< HCI_TL Tx/Rx operation status OK */ + +/** + * @brief callback function for HCI Transport Layer send/receive operations + */ +typedef void (* esp_bt_hci_tl_callback_t) (void *arg, uint8_t status); + #ifdef CONFIG_BT_ENABLED #define BT_CTRL_BLE_MAX_ACT_LIMIT 10 //Maximum BLE activity limitation @@ -197,8 +204,8 @@ typedef struct { int (* _open)(void); /* hci tl open */ void (* _close)(void); /* hci tl close */ void (* _finish_transfers)(void); /* hci tl finish trasnfers */ - void (* _recv)(uint8_t *buf, uint32_t len, void (*callback) (void*, uint8_t), void* dummy); /* hci tl recv */ - void (* _send)(uint8_t *buf, uint32_t len, void (*callback) (void*, uint8_t), void* dummy); /* hci tl send */ + void (* _recv)(uint8_t *buf, uint32_t len, esp_bt_hci_tl_callback_t callback, void* arg); /* hci tl recv */ + void (* _send)(uint8_t *buf, uint32_t len, esp_bt_hci_tl_callback_t callback, void* arg); /* hci tl send */ bool (* _flow_off)(void); /* hci tl flow off */ void (* _flow_on)(void); /* hci tl flow on */ } esp_bt_hci_tl_t; diff --git a/components/hal/esp32c3/include/hal/uhci_ll.h b/components/hal/esp32c3/include/hal/uhci_ll.h index 1566d0b091..235d28ab3a 100644 --- a/components/hal/esp32c3/include/hal/uhci_ll.h +++ b/components/hal/esp32c3/include/hal/uhci_ll.h @@ -20,12 +20,16 @@ #include #include "uhci_types.h" #include "soc/uhci_struct.h" -#include "soc/gdma_struct.h" - -#define UHCI_DMA_INDEX 0 #define UHCI_LL_GET_HW(num) (((num) == 0) ? (&UHCI0) : (NULL)) +typedef enum { + UHCI_RX_BREAK_CHR_EOF = 0x1, + UHCI_RX_IDLE_EOF = 0x2, + UHCI_RX_LEN_EOF = 0x4, + UHCI_RX_EOF_MAX = 0x7, +} uhci_rxeof_cfg_t; + static inline void uhci_ll_init(uhci_dev_t *hw) { typeof(hw->conf0) conf0_reg; @@ -38,7 +42,8 @@ static inline void uhci_ll_init(uhci_dev_t *hw) static inline void uhci_ll_attach_uart_port(uhci_dev_t *hw, int uart_num) { - abort(); // TODO ESP32-C3 IDF-2117 + hw->conf0.uart0_ce = (uart_num == 0)? 1: 0; + hw->conf0.uart1_ce = (uart_num == 1)? 1: 0; } static inline void uhci_ll_set_seper_chr(uhci_dev_t *hw, uhci_seper_chr_t *seper_char) @@ -90,20 +95,6 @@ static inline void uhci_ll_set_swflow_ctrl_sub_chr(uhci_dev_t *hw, uhci_swflow_c hw->escape_conf.val = escape_conf_reg.val; } -static inline void uhci_ll_dma_in_reset(uhci_dev_t *hw) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].in.in_conf0.in_rst = 1; - GDMA.channel[UHCI_DMA_INDEX].in.in_conf0.in_rst = 0; -} - -static inline void uhci_ll_dma_out_reset(uhci_dev_t *hw) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].out.out_conf0.out_rst = 1; - GDMA.channel[UHCI_DMA_INDEX].out.out_conf0.out_rst = 0; -} - static inline void uhci_ll_enable_intr(uhci_dev_t *hw, uint32_t intr_mask) { hw->int_ena.val |= intr_mask; @@ -124,41 +115,6 @@ static inline uint32_t uhci_ll_get_intr(uhci_dev_t *hw) return hw->int_st.val; } -static inline void uhci_ll_set_rx_dma(uhci_dev_t *hw, uint32_t addr) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].in.in_link.addr = addr; -} - -static inline void uhci_ll_set_tx_dma(uhci_dev_t *hw, uint32_t addr) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].out.out_link.addr = addr; -} - -static inline void uhci_ll_rx_dma_start(uhci_dev_t *hw) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].in.in_link.start = 1; -} - -static inline void uhci_ll_tx_dma_start(uhci_dev_t *hw) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].out.out_link.start = 1; -} - -static inline void uhci_ll_rx_dma_stop(uhci_dev_t *hw) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].in.in_link.stop = 1; -} - -static inline void uhci_ll_tx_dma_stop(uhci_dev_t *hw) -{ - (void)hw; - GDMA.channel[UHCI_DMA_INDEX].out.out_link.stop = 1; -} static inline void uhci_ll_set_eof_mode(uhci_dev_t *hw, uint32_t eof_mode) { diff --git a/components/hal/esp32c3/include/hal/uhci_types.h b/components/hal/esp32c3/include/hal/uhci_types.h new file mode 100644 index 0000000000..7b7f41d0f9 --- /dev/null +++ b/components/hal/esp32c3/include/hal/uhci_types.h @@ -0,0 +1,54 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +// Though the UHCI driver hasn't been published, some types are defined here +// for users to develop over the HAL. See example: controller_hci_uart_esp32c3 + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @brief UHCI escape sequence + */ +typedef struct { + uint8_t seper_chr; /*!< escape sequence character */ + uint8_t sub_chr1; /*!< escape sequence sub-character 1 */ + uint8_t sub_chr2; /*!< escape sequence sub-character 2 */ + bool sub_chr_en; /*!< enable use of sub-chaacter of escape sequence */ +} uhci_seper_chr_t; + +/** + * @brief UHCI software flow control + */ +typedef struct { + uint8_t xon_chr; /*!< character for XON */ + uint8_t xon_sub1; /*!< sub-character 1 for XON */ + uint8_t xon_sub2; /*!< sub-character 2 for XON */ + uint8_t xoff_chr; /*!< character 2 for XOFF */ + uint8_t xoff_sub1; /*!< sub-character 1 for XOFF */ + uint8_t xoff_sub2; /*!< sub-character 2 for XOFF */ + uint8_t flow_en; /*!< enable use of software flow control */ +} uhci_swflow_ctrl_sub_chr_t; + +#ifdef __cplusplus +} +#endif diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3/CMakeLists.txt b/examples/bluetooth/hci/controller_hci_uart_esp32c3/CMakeLists.txt new file mode 100644 index 0000000000..c816f96634 --- /dev/null +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(controller_hci_uart_demo) diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3/README.md b/examples/bluetooth/hci/controller_hci_uart_esp32c3/README.md new file mode 100644 index 0000000000..94e01fb8e4 --- /dev/null +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3/README.md @@ -0,0 +1,83 @@ +ESP-IDF UART HCI Controller +================================= +| Supported Targets | ESP32-C3 | +| ----------------- | -------- | + +This example demonstrates how to configure the Bluetooth Low Energy Controller's HCI (Host Controller Interface) to communicate over UART. + +Using this example, BLE radio capabilities of ESP32-C3 chip, can be: + +1. tested via standard HCI messages in Direct Test Mode + +2. used with external Bluetooth host stack installed on PC, or other MCU. + +This example uses UHCI, GDMA together with UART to implement the HCI UART transport. + +This example uses LL/register access directly, because the UHCI driver hasn't been implemented yet. + +## How to use example + +### Hardware Required + +This example should be able to run on any commonly available ESP32-C3 development board. To connect UART to PC, another board such as ESP_Test Board or FT232 USB UART board is usually needed. + +In this example, two UARTs are used: + +- UART0 is used as normal output or by IDF monitor + +- UART1 is used to convey HCI messages + + +RTS and CTS lines of UART1 are required. GPIO4, GPIO5, GPIO6, GPIO7 are used as TxD, RxD, RTS, CTS PINs of UART1, respectively. + +In a frequently-used scenario, if ESP_Test Board is used, connect the TX0, RX0, RTS0, CTS0 and GND of ESP_Test Board to ESP32-C3 UART1 PINs, and Attach ESP_Test board to the host PC. + +### Configure the project + +``` +idf.py menuconfig +``` + +* Baudrate of UART1 can be configured in `Example Configuration > UART Baudrate for HCI` + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +The example sets up the HCI UART transport and enable Bluetooth Controller, after started. UART1 PIN and baudrate settings is printed at serial output: + +``` +I (296) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (296) gpio: GPIO[6]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (306) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (316) gpio: GPIO[6]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (326) BTDM_INIT: BT controller compile version [6ab3130] +I (336) coexist: coexist rom version 8459080 +I (336) phy_init: phy_version 500,985899c,Apr 19 2021,16:05:08 +I (466) system_api: Base MAC address is not set +I (466) system_api: read default base MAC address from EFUSE +I (476) BTDM_INIT: Bluetooth MAC: 7c:df:a1:40:3f:16 + +I (476) UHCI: HCI messages can be communicated over UART1: +--PINs: TxD 4, RxD 5, RTS 6, CTS 7 +--Baudrate: 921600 +``` + +After these output occurs, HCI messages can be commnunicated over UART1. + +## Troubleshooting + +## Example Breakdown + diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/CMakeLists.txt b/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/CMakeLists.txt new file mode 100644 index 0000000000..47b859fbf9 --- /dev/null +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "uhci_uart_demo.c" + INCLUDE_DIRS "") diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/Kconfig.projbuild b/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/Kconfig.projbuild new file mode 100644 index 0000000000..395e8bef94 --- /dev/null +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/Kconfig.projbuild @@ -0,0 +1,10 @@ +menu "Example Configuration" + + config EXAMPLE_HCI_UART_BAUDRATE + int "UART Baudrate for HCI" + range 115200 921600 + default 115200 + help + UART Baudrate for HCI. Please use standard baudrate. + +endmenu diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/uhci_uart_demo.c b/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/uhci_uart_demo.c new file mode 100644 index 0000000000..3bb3c41dab --- /dev/null +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3/main/uhci_uart_demo.c @@ -0,0 +1,280 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include "driver/periph_ctrl.h" +#include "driver/gpio.h" +#include "driver/uart.h" +#include "soc/lldesc.h" +#include "esp_private/gdma.h" +#include "hal/uhci_ll.h" +#include "esp_bt.h" +#include "esp_log.h" + +static const char *tag = "UHCI"; + +#define UART_HCI_NUM (1) + +#define UART_RX_THRS (120) + +#define GPIO_UART_TXD_OUT (4) +#define GPIO_UART_RXD_IN (5) +#define GPIO_UART_RTS_OUT (6) +#define GPIO_UART_CTS_IN (7) + +#define GPIO_OUTPUT_PIN_SEL ((1ULL<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) +{ + assert(buf != NULL); + assert(size != 0); + assert(callback != NULL); + + uart_env.tx.callback = callback; + uart_env.tx.arg = arg; + + memset(&uart_env.tx.link, 0, sizeof(lldesc_t)); + uart_env.tx.link.length = size; + 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 bool hci_uart_tl_flow_off(void) +{ + return true; +} + +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) +{ + 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); + esp_bt_hci_tl_callback_t callback = uart_env.tx.callback; + void *arg = uart_env.tx.arg; + + // clear callback pointer + uart_env.tx.callback = NULL; + uart_env.tx.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 void uart_gpio_set(void) +{ + gpio_config_t io_output_conf = { + .intr_type = GPIO_PIN_INTR_DISABLE, //disable interrupt + .mode = GPIO_MODE_OUTPUT, // output mode + .pin_bit_mask = GPIO_OUTPUT_PIN_SEL, // bit mask of the output pins + .pull_down_en = 0, // disable pull-down mode + .pull_up_en = 0, // disable pull-up mode + }; + gpio_config(&io_output_conf); + + gpio_config_t io_input_conf = { + .intr_type = GPIO_PIN_INTR_DISABLE, //disable interrupt + .mode = GPIO_MODE_INPUT, // input mode + .pin_bit_mask = GPIO_OUTPUT_PIN_SEL, // bit mask of the input pins + .pull_down_en = 0, // disable pull-down mode + .pull_up_en = 0, // 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); +} + +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 + uart_config_t uart_config = { + .baud_rate = CONFIG_EXAMPLE_HCI_UART_BAUDRATE, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + .rx_flow_ctrl_thresh = UART_RX_THRS, + .source_clk = UART_SCLK_APB, + }; + ESP_ERROR_CHECK(uart_param_config(UART_HCI_NUM, &uart_config)); + + // install DMA driver + gdma_channel_alloc_config_t tx_channel_config = { + .flags.reserve_sibling = 1, + .direction = GDMA_CHANNEL_DIRECTION_TX, + }; + ESP_ERROR_CHECK(gdma_new_channel(&tx_channel_config, &s_tx_channel)); + gdma_channel_alloc_config_t rx_channel_config = { + .direction = GDMA_CHANNEL_DIRECTION_RX, + .sibling_chan = s_tx_channel, + }; + ESP_ERROR_CHECK(gdma_new_channel(&rx_channel_config, &s_rx_channel)); + + gdma_connect(s_tx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); + gdma_connect(s_rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART, 0)); + + gdma_strategy_config_t strategy_config = { + .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_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); +} + + +void app_main(void) +{ + esp_err_t ret; + + uhci_uart_install(); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + bt_cfg.hci_tl_funcs = &s_hci_uart_tl_funcs; + + ret = esp_bt_controller_init(&bt_cfg); + if (ret != ESP_OK) { + ESP_LOGE(tag, "Bluetooth Controller initialize failed: %s", esp_err_to_name(ret)); + return; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret != ESP_OK) { + ESP_LOGE(tag, "Bluetooth Controller initialize failed: %s", esp_err_to_name(ret)); + return; + } + + ESP_LOGI(tag, "HCI messages can be communicated over UART%d: \n" + "--PINs: TxD %d, RxD %d, RTS %d, CTS %d\n" + "--Baudrate: %d", UART_HCI_NUM, + GPIO_UART_TXD_OUT, GPIO_UART_RXD_IN, GPIO_UART_RTS_OUT, GPIO_UART_CTS_IN, + CONFIG_EXAMPLE_HCI_UART_BAUDRATE); +} diff --git a/examples/bluetooth/hci/controller_hci_uart_esp32c3/sdkconfig.defaults b/examples/bluetooth/hci/controller_hci_uart_esp32c3/sdkconfig.defaults new file mode 100644 index 0000000000..b786985ee1 --- /dev/null +++ b/examples/bluetooth/hci/controller_hci_uart_esp32c3/sdkconfig.defaults @@ -0,0 +1,12 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# + +CONFIG_BT_ENABLED=y +CONFIG_BT_CTRL_ESP32C3=y +CONFIG_BT_CTRL_HCI_MODE_UART_H4=y +CONFIG_BT_CTRL_HCI_TL=0 +CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=n +CONFIG_BT_CTRL_BLE_SCAN_DUPL=n +# End of deprecated options