From c276fc5a51854403b987ba82a080aabe00b65b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Izzo?= Date: Tue, 29 Aug 2023 22:16:06 +0200 Subject: [PATCH] ttwrplus: moved all the SA8x8 code in a dedicated translation unit. --- platform/drivers/baseband/AT1846S_SA8x8.cpp | 39 +-- platform/drivers/baseband/SA8x8.c | 282 +++++++++++++++++++ platform/drivers/baseband/SA8x8.h | 78 +++++ platform/drivers/baseband/radio_ttwrplus.cpp | 240 +--------------- platform/targets/ttwrplus/CMakeLists.txt | 1 + 5 files changed, 373 insertions(+), 267 deletions(-) create mode 100644 platform/drivers/baseband/SA8x8.c create mode 100644 platform/drivers/baseband/SA8x8.h diff --git a/platform/drivers/baseband/AT1846S_SA8x8.cpp b/platform/drivers/baseband/AT1846S_SA8x8.cpp index edbacac1..ad317381 100644 --- a/platform/drivers/baseband/AT1846S_SA8x8.cpp +++ b/platform/drivers/baseband/AT1846S_SA8x8.cpp @@ -18,17 +18,9 @@ * along with this program; if not, see * **************************************************************************/ -#include - -#include -#include - #include #include "AT1846S.h" - -#define SA8X8_MSG_SIZE 32 - -char rx_buf[SA8X8_MSG_SIZE] = { 0 }; +#include "SA8x8.h" void AT1846S::init() { @@ -198,41 +190,16 @@ void AT1846S::setOpMode(const AT1846S_OpMode mode) /* * Implementation of AT1846S I2C interface through SA8x8 */ - -static constexpr uint8_t devAddr = 0xE2; - void AT1846S::i2c_init() { - // I2C already init'd by platform support package } -/* - * These callbacks needs to be implemented by the platform, providing functions - * to read and write from the serial port - */ -extern void radio_uartPrint(const char *fmt, ...); -extern void radio_uartScan(char *buf); - void AT1846S::i2c_writeReg16(uint8_t reg, uint16_t value) { - /* - * SA8x8 with sa8x8_fw uses PEEK and POKE AT commands to write to AT1846S - */ - radio_uartPrint("AT+POKE=%d,%d\r\n", reg, value); - radio_uartScan(rx_buf); - // Check that response is "OK\r" - if (strncmp(rx_buf, "OK\r", 3)) - printk("SA8x8 Error: %d <- %d\n", reg, value); + sa8x8_writeAT1846Sreg(reg, value); } uint16_t AT1846S::i2c_readReg16(uint8_t reg) { - /* - * SA8x8 with sa8x8_fw uses PEEK and POKE AT commands to write to AT1846S - */ - int32_t value = 0; - radio_uartPrint("AT+PEEK=%d\r\n", reg); - radio_uartScan(rx_buf); - sscanf(rx_buf, "%d\r", &value); - return value; + return sa8x8_readAT1846Sreg(reg); } diff --git a/platform/drivers/baseband/SA8x8.c b/platform/drivers/baseband/SA8x8.c new file mode 100644 index 00000000..b07cb9cd --- /dev/null +++ b/platform/drivers/baseband/SA8x8.c @@ -0,0 +1,282 @@ +/*************************************************************************** + * Copyright (C) 2023 by Silvano Seva IU2KWO * + * and Niccolò Izzo IU2KIN * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "SA8x8.h" + +/* + * Minimum required version of sa868-fw + */ +#define SA868FW_MAJOR 1 +#define SA868FW_MINOR 1 +#define SA868FW_PATCH 0 +#define SA868FW_RELEASE 20 + + +#if DT_NODE_HAS_STATUS(DT_ALIAS(radio), okay) +#define UART_RADIO_DEV_NODE DT_ALIAS(radio) +#else +#error "Please select the correct radio UART device" +#endif + +#define RADIO_PDN_NODE DT_ALIAS(radio_pdn) + +#define SA8X8_MSG_SIZE 32U + +static const struct device *const uart_dev = DEVICE_DT_GET(UART_RADIO_DEV_NODE); +static const struct gpio_dt_spec radio_pdn = GPIO_DT_SPEC_GET(RADIO_PDN_NODE, gpios); + +K_MSGQ_DEFINE(uart_msgq, SA8X8_MSG_SIZE, 10, 4); + +static uint16_t rx_buf_pos; +static char rx_buf[SA8X8_MSG_SIZE]; + + +static void uartRxCallback(const struct device *dev, void *user_data) +{ + uint8_t c; + + if (uart_irq_update(uart_dev) == false) + return; + + if (uart_irq_rx_ready(uart_dev) == false) + return; + + // read until FIFO empty + while (uart_fifo_read(uart_dev, &c, 1) == 1) + { + if ((c == '\n') && (rx_buf_pos > 0)) + { + rx_buf[rx_buf_pos] = '\0'; + + // if queue is full, message is silently dropped + k_msgq_put(&uart_msgq, &rx_buf, K_NO_WAIT); + rx_buf_pos = 0; + } + else if (rx_buf_pos < (sizeof(rx_buf) - 1)) + { + rx_buf[rx_buf_pos++] = c; + } + } +} + +static void uartPrint(const char *fmt, ...) +{ + char buf[SA8X8_MSG_SIZE]; + + va_list args; + va_start(args, fmt); + int len = vsnprintk(buf, SA8X8_MSG_SIZE, fmt, args); + va_end(args); + + for(int i = 0; i < len; i++) + uart_poll_out(uart_dev, buf[i]); +} + +static inline void waitUntilReady() +{ + char buf[SA8X8_MSG_SIZE] = { 0 }; + + while(true) + { + uartPrint("AT\r\n"); + int ret = k_msgq_get(&uart_msgq, buf, K_MSEC(250)); + if(ret != 0) + printk("SA8x8: baseband is not ready!\n"); + + if(strncmp(buf, "OK\r", SA8X8_MSG_SIZE) == 0) + break; + } +} + +static inline bool checkFwVersion() +{ + uint8_t major; + uint8_t minor; + uint8_t patch; + uint8_t release; + + char *fwVersionStr = sa8x8_getFwVersion(); + sscanf(fwVersionStr, "sa8x8-fw/v%hhu.%hhu.%hhu.r%hhu", &major, &minor, + &patch, &release); + + if((major >= SA868FW_MAJOR) && (minor >= SA868FW_MINOR) && + (patch >= SA868FW_PATCH) && (release >= SA868FW_RELEASE)) + { + return true; + } + + // Major, minor, patch or release not matching. + printk("SA8x8: error, unsupported baseband firmware, please update!\n"); + return false; +} + + +int sa8x8_init() +{ + // Initialize GPIO for SA868S power down + if(gpio_is_ready_dt(&radio_pdn) == false) + { + printk("SA8x8: error, radio device %s is not ready\n", radio_pdn.port->name); + return -1; + } + + int ret = gpio_pin_configure_dt(&radio_pdn, GPIO_OUTPUT); + if (ret != 0) + { + printk("SA8x8: error %d, failed to configure %s pin %d\n", ret, + radio_pdn.port->name, radio_pdn.pin); + return ret; + } + + // Reset the SA868S baseband + gpio_pin_set_dt(&radio_pdn, 1); + delayMs(100); + gpio_pin_set_dt(&radio_pdn, 0); + + // Setup UART for communication + if (device_is_ready(uart_dev) == false) + { + printk("SA8x8: error, UART device not found!\n"); + return -1; + } + + ret = uart_irq_callback_user_data_set(uart_dev, uartRxCallback, NULL); + if (ret < 0) + { + switch(ret) + { + case -ENOTSUP: + printk("SA8x8: error, interrupt-driven UART support not enabled\n"); + break; + + case -ENOSYS: + printk("SA8x8: error, UART device does not support interrupt-driven API\n"); + break; + + default: + printk("SA8x8: error, cannot set UART callback: %d\n", ret); + break; + } + + return ret; + } + + uart_irq_rx_enable(uart_dev); + + waitUntilReady(); + bool ok = checkFwVersion(); + if(ok) + return 0; + + return -1; +} + +const char *sa8x8_getModel() +{ + static char model[SA8X8_MSG_SIZE] = { 0 }; + + if(model[0] == 0) + { + uartPrint("AT+MODEL\r\n"); + int ret = k_msgq_get(&uart_msgq, model, K_MSEC(100)); + if(ret != 0) + printk("SA8x8: error while reading radio model\n"); + } + + return model; +} + +const char *sa8x8_getFwVersion() +{ + static char fw_version[SA8X8_MSG_SIZE] = { 0 }; + + if(fw_version[0] == 0) + { + uartPrint("AT+VERSION\r\n"); + int ret = k_msgq_get(&uart_msgq, fw_version, K_MSEC(100)); + if(ret != 0) + printk("SA8x8: error while reading FW version\n"); + } + + return fw_version; +} + +int sa8x8_enableHSMode() +{ + char buf[SA8X8_MSG_SIZE] = { 0 }; + struct uart_config uart_config; + + int ret = uart_config_get(uart_dev, &uart_config); + if(ret != 0) + { + printk("SA8x8: error while retrieving UART configuration!\n"); + return ret; + } + + uartPrint("AT+TURBO\r\n"); + ret = k_msgq_get(&uart_msgq, buf, K_MSEC(100)); + if(ret != 0) + { + printk("SA8x8: error while retrieving turbo response!\n"); + return ret; + } + + uart_config.baudrate = 115200; + + ret = uart_configure(uart_dev, &uart_config); + if(ret != 0) + { + printk("c error while setting UART configuration!\n"); + return ret; + } + + return 0; +} + +void sa8x8_writeAT1846Sreg(uint8_t reg, uint16_t value) +{ + char buf[SA8X8_MSG_SIZE]; + + uartPrint("AT+POKE=%d,%d\r\n", reg, value); + k_msgq_get(&uart_msgq, buf, K_MSEC(100)); + + // Check that response is "OK\r" + if(strncmp(buf, "OK\r", 3U) != 0) + printk("SA8x8 Error: %d <- %d\n", reg, value); +} + +uint16_t sa8x8_readAT1846Sreg(uint8_t reg) +{ + char buf[SA8X8_MSG_SIZE]; + uint16_t value = 0; + + uartPrint("AT+PEEK=%d\r\n", reg); + k_msgq_get(&uart_msgq, buf, K_MSEC(100)); + + int ret = sscanf(buf, "%hd\r", &value); + if(ret != 1) + printk("SA8x8 Error: %d ->\n", reg); + + return value; +} diff --git a/platform/drivers/baseband/SA8x8.h b/platform/drivers/baseband/SA8x8.h new file mode 100644 index 00000000..18e70b68 --- /dev/null +++ b/platform/drivers/baseband/SA8x8.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2023 by Silvano Seva IU2KWO * + * and Niccolò Izzo IU2KIN * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#ifndef SA8x8_H +#define SA8x8_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Setup the GPIOs and UART to control the SA8x8 module and initalize it. + * + * @return 0 on success, an error code otherwise. + */ +int sa8x8_init(); + +/** + * Get the SA8x8 module type. + * + * @return a string describing the module type. + */ +const char *sa8x8_getModel(); + +/** + * Get the SA8x8 firmware version. + * + * @return firmware version string. + */ +const char *sa8x8_getFwVersion(); + +/** + * Enable high-speed mode on the SA8x8 serial port, changing the baud rate to + * 115200 bit per second. + * + * @return 0 on success, an error code otherwise. + */ +int sa8x8_enableHSMode(); + +/** + * Write a register of the AT1846S radio IC contained in the SA8x8 module. + * + * @param reg: register number. + * @param value: value to be written. + */ +void sa8x8_writeAT1846Sreg(uint8_t reg, uint16_t value); + +/** + * Read a register of the AT1846S radio IC contained in the SA8x8 module. + * + * @param reg: register number. + * @return register value. + */ +uint16_t sa8x8_readAT1846Sreg(uint8_t reg); + +#ifdef __cplusplus +} +#endif + +#endif /* SA8x8_H */ diff --git a/platform/drivers/baseband/radio_ttwrplus.cpp b/platform/drivers/baseband/radio_ttwrplus.cpp index 7ee58bcf..06f6d196 100644 --- a/platform/drivers/baseband/radio_ttwrplus.cpp +++ b/platform/drivers/baseband/radio_ttwrplus.cpp @@ -18,254 +18,32 @@ * along with this program; if not, see * ***************************************************************************/ -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include -#include "AT1846S.h" #include "radioUtils.h" +#include "AT1846S.h" +#include "SA8x8.h" -/* - * Minimum required version of sa868-fw - */ -#define SA868FW_MAJOR 1 -#define SA868FW_MINOR 1 -#define SA868FW_PATCH 0 -#define SA868FW_RELEASE 20 +const rtxStatus_t *config; // Pointer to data structure with radio configuration -/* - * Define radio node to control the SA868 - */ -#if DT_NODE_HAS_STATUS(DT_ALIAS(radio), okay) -#define UART_RADIO_DEV_NODE DT_ALIAS(radio) -#else -#error "Please select the correct radio UART device" -#endif - -#define SA8X8_MSG_SIZE 32 - -K_MSGQ_DEFINE(uart_msgq, SA8X8_MSG_SIZE, 10, 4); - -/* receive buffer used in UART ISR callback */ -static char rx_buf[SA8X8_MSG_SIZE]; -static uint16_t rx_buf_pos; - -static const struct device *const radio_dev = DEVICE_DT_GET(UART_RADIO_DEV_NODE); - -#define RADIO_PDN_NODE DT_ALIAS(radio_pdn) - -static const struct gpio_dt_spec radio_pdn = GPIO_DT_SPEC_GET(RADIO_PDN_NODE, gpios); - - -const rtxStatus_t *config; // Pointer to data structure with radio configuration - -Band currRxBand = BND_NONE; // Current band for RX -Band currTxBand = BND_NONE; // Current band for TX - -enum opstatus radioStatus; // Current operating status +Band currRxBand = BND_NONE; // Current band for RX +Band currTxBand = BND_NONE; // Current band for TX +enum opstatus radioStatus; // Current operating status AT1846S& at1846s = AT1846S::instance(); // AT1846S driver -void radio_serialCb(const struct device *dev, void *user_data) -{ - uint8_t c; - - if (!uart_irq_update(radio_dev)) { - return; - } - - if (!uart_irq_rx_ready(radio_dev)) { - return; - } - - /* read until FIFO empty */ - while (uart_fifo_read(radio_dev, &c, 1) == 1) { - if (c == '\n' && rx_buf_pos > 0) { - /* terminate string */ - rx_buf[rx_buf_pos] = '\0'; - - /* if queue is full, message is silently dropped */ - k_msgq_put(&uart_msgq, &rx_buf, K_NO_WAIT); - - /* reset the buffer (it was copied to the msgq) */ - rx_buf_pos = 0; - } else if (rx_buf_pos < (sizeof(rx_buf) - 1)) { - rx_buf[rx_buf_pos++] = c; - } - /* else: characters beyond buffer size are dropped */ - } -} - -void radio_uartPrint(const char *fmt, ...) -{ - char buf[SA8X8_MSG_SIZE] = { 0 }; - va_list args; - va_start(args, fmt); - vsnprintk(buf, SA8X8_MSG_SIZE, fmt, args); - int msg_len = strnlen(buf, SA8X8_MSG_SIZE); - for (uint16_t i = 0; i < msg_len; i++) { - uart_poll_out(radio_dev, buf[i]); - } - va_end(args); -} - -void radio_uartScan(char *buf) -{ - k_msgq_get(&uart_msgq, buf, K_MSEC(100)); -} - -char *radio_getFwVersion() -{ - char *tx_buf = (char *) malloc(sizeof(char) * SA8X8_MSG_SIZE); - radio_uartPrint("AT+VERSION\r\n"); - k_msgq_get(&uart_msgq, tx_buf, K_MSEC(100)); - return tx_buf; -} - -char *radio_getModel() -{ - char *tx_buf = (char *) malloc(sizeof(char) * SA8X8_MSG_SIZE); - radio_uartPrint("AT+MODEL\r\n"); - k_msgq_get(&uart_msgq, tx_buf, K_MSEC(100)); - return tx_buf; -} - -enum Band radio_getBand() -{ - enum Band band = BND_NONE; - char *tx_buf = radio_getModel(); - if (!strncmp(tx_buf, "SA868S-VHF\r", SA8X8_MSG_SIZE)) - band = BND_VHF; - else if (!strncmp(tx_buf, "SA868S-UHF\r", SA8X8_MSG_SIZE)) - band = BND_UHF; - free(tx_buf); - return band; -} - -enum Band radio_waitUntilReady() -{ - bool ready = false; - enum Band band = BND_NONE; - do - { - band = radio_getBand(); - if (band != BND_NONE) - ready = true; - } while(!ready); - return band; -} - -void radio_enableTurbo() -{ - int ret = 0; - struct uart_config uart_config; - - ret = uart_config_get(radio_dev, &uart_config); - if (ret) { - printk("Error: in retrieving UART configuration!\n"); - return; - } - - radio_uartPrint("AT+TURBO\r\n"); - - char *tx_buf = (char *) malloc(sizeof(char) * SA8X8_MSG_SIZE); - ret = k_msgq_get(&uart_msgq, tx_buf, K_MSEC(100)); - if (ret) { - printk("Error: in retrieving turbo response!\n"); - return; - } - - uart_config.baudrate = 115200; - - ret = uart_configure(radio_dev, &uart_config); - if (ret) { - printk("Error: in setting UART configuration!\n"); - return; - } -} void radio_init(const rtxStatus_t *rtxState) { config = rtxState; radioStatus = OFF; - int ret; // Turn on baseband pmu_setBasebandPower(true); - // Initialize GPIO for SA868S power down - if (!gpio_is_ready_dt(&radio_pdn)) { - printk("Error: radio device %s is not ready\n", - radio_pdn.port->name); - } - - ret = gpio_pin_configure_dt(&radio_pdn, GPIO_OUTPUT); - if (ret != 0) { - printk("Error %d: failed to configure %s pin %d\n", ret, radio_pdn.port->name, radio_pdn.pin); - } - - if (!device_is_ready(radio_dev)) { - printk("UART device not found!\n"); - return; - } - - ret = uart_irq_callback_user_data_set(radio_dev, radio_serialCb, NULL); - - if (ret < 0) { - if (ret == -ENOTSUP) { - printk("Interrupt-driven UART support not enabled\n"); - } else if (ret == -ENOSYS) { - printk("UART device does not support interrupt-driven API\n"); - } else { - printk("Error setting UART callback: %d\n", ret); - } - - return; - } - - uart_irq_rx_enable(radio_dev); - - // Reset the SA868S baseband - ret = gpio_pin_set_dt(&radio_pdn, 1); - if (ret != 0) { - printk("Failed to reset baseband"); - return; - } - delayMs(10); - ret = gpio_pin_set_dt(&radio_pdn, 0); - if (ret != 0) { - printk("Failed to reset baseband"); - return; - } - - // Wait until SA868 is responsive - radio_waitUntilReady(); - - // Check for minimum supported firmware version. - char *fwVersionStr = radio_getFwVersion(); - uint8_t major = 0, minor = 0, patch = 0, release = 0; - sscanf(fwVersionStr, "sa8x8-fw/v%hhu.%hhu.%hhu.r%hhu", &major, &minor, &patch, &release); - if (major < SA868FW_MAJOR || - (major == SA868FW_MAJOR && minor < SA868FW_MINOR) || - (major == SA868FW_MAJOR && minor == SA868FW_MINOR && patch == SA868FW_PATCH && release < SA868FW_RELEASE)) - { - printk("Error: unsupported baseband firmware, please update!\n"); - return; - } - free(fwVersionStr); - - // Enable TURBO mode (115200 baud rate serial) - radio_enableTurbo(); - - // TODO: Implement audio paths configuration + // Init the SA8x8 mode, set serial to 115200 baud + sa8x8_init(); + sa8x8_enableHSMode(); /* * Configure AT1846S, keep AF output disabled at power on. diff --git a/platform/targets/ttwrplus/CMakeLists.txt b/platform/targets/ttwrplus/CMakeLists.txt index 063bb2ab..93b5c814 100644 --- a/platform/targets/ttwrplus/CMakeLists.txt +++ b/platform/targets/ttwrplus/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources(app ${OPENRTX_ROOT}/platform/drivers/keyboard/keyboard_ttwrplus.c ${OPENRTX_ROOT}/platform/drivers/baseband/radio_ttwrplus.cpp ${OPENRTX_ROOT}/platform/drivers/baseband/AT1846S_SA8x8.cpp + ${OPENRTX_ROOT}/platform/drivers/baseband/SA8x8.c ${OPENRTX_ROOT}/platform/drivers/GPS/GPS_ttwrplus.c ${OPENRTX_ROOT}/platform/drivers/stubs/audio_stub.c