From 6b0547b963fd76a05a6e778ed5f4fe727478aadb Mon Sep 17 00:00:00 2001 From: mr-nice Date: Mon, 6 Mar 2017 18:28:20 +0100 Subject: [PATCH] Softuart (#307) * extras/softuart: support for multiple UARTs, dynamic RX/TX pins --- examples/softuart/LICENSE | 23 +++ examples/softuart/Makefile | 4 + examples/softuart/main.c | 40 ++++++ extras/softuart/LICENSE | 23 +++ extras/softuart/component.mk | 10 ++ extras/softuart/softuart.c | 270 +++++++++++++++++++++++++++++++++++ extras/softuart/softuart.h | 93 ++++++++++++ 7 files changed, 463 insertions(+) create mode 100644 examples/softuart/LICENSE create mode 100644 examples/softuart/Makefile create mode 100644 examples/softuart/main.c create mode 100644 extras/softuart/LICENSE create mode 100644 extras/softuart/component.mk create mode 100644 extras/softuart/softuart.c create mode 100644 extras/softuart/softuart.h diff --git a/examples/softuart/LICENSE b/examples/softuart/LICENSE new file mode 100644 index 0000000..104c838 --- /dev/null +++ b/examples/softuart/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (C) 2016 Bernhard Guillon +Copyright (c) 2015 plieningerweb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/examples/softuart/Makefile b/examples/softuart/Makefile new file mode 100644 index 0000000..5b3f951 --- /dev/null +++ b/examples/softuart/Makefile @@ -0,0 +1,4 @@ +PROGRAM = softuart +EXTRA_COMPONENTS = extras/softuart +ESPBAUD = 460800 +include ../../common.mk diff --git a/examples/softuart/main.c b/examples/softuart/main.c new file mode 100644 index 0000000..75b280e --- /dev/null +++ b/examples/softuart/main.c @@ -0,0 +1,40 @@ +/* + * Softuart example + * + * Copyright (C) 2017 Ruslan V. Uss + * Copyright (C) 2016 Bernhard Guillon + * Copyright (c) 2015 plieningerweb + * + * MIT Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +//#include +//#include + +#include + +#define RX_PIN 5 +#define TX_PIN 4 + +void user_init(void) +{ + // setup real UART for now + uart_set_baud(0, 115200); + printf("SDK version:%s\n\n", sdk_system_get_sdk_version()); + + // setup software uart to 9600 8n1 + softuart_open(0, 9600, RX_PIN, TX_PIN); + + while (true) + { + if (!softuart_available(0)) + continue; + + char c = softuart_read(0); + printf("input: %c, 0x%02x\n", c, c); + softuart_puts(0, "start\r\n"); + } +} diff --git a/extras/softuart/LICENSE b/extras/softuart/LICENSE new file mode 100644 index 0000000..104c838 --- /dev/null +++ b/extras/softuart/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (C) 2016 Bernhard Guillon +Copyright (c) 2015 plieningerweb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/extras/softuart/component.mk b/extras/softuart/component.mk new file mode 100644 index 0000000..9517cf2 --- /dev/null +++ b/extras/softuart/component.mk @@ -0,0 +1,10 @@ +# Component makefile for extras/softuart + +# expected anyone using this driver includes it as 'softuart/softuart.h' +INC_DIRS += $(softuart_ROOT).. + +# args for passing into compile rule generation +softuart_SRC_DIR = $(softuart_ROOT) + +$(eval $(call component_compile_rules,softuart)) + diff --git a/extras/softuart/softuart.c b/extras/softuart/softuart.c new file mode 100644 index 0000000..7cfc0fc --- /dev/null +++ b/extras/softuart/softuart.c @@ -0,0 +1,270 @@ +/* + * Softuart + * + * Copyright (C) 2017 Ruslan V. Uss + * Copyright (C) 2016 Bernhard Guillon + * + * This code is based on Softuart from here [1] and reworked to + * fit into esp-open-rtos. + * + * it fits my needs to read the GY-GPS6MV2 module with 9600 8n1 + * + * Original Copyright: + * Copyright (c) 2015 plieningerweb + * + * MIT Licensed as described in the file LICENSE + * + * 1 https://github.com/plieningerweb/esp8266-software-uart + */ + +#include "softuart.h" +#include +#include +#include +#include + +//#define SOFTUART_DEBUG + +#ifdef SOFTUART_DEBUG +#define debug(fmt, ...) printf("%s: " fmt "\n", "SOFTUART", ## __VA_ARGS__) +#else +#define debug(fmt, ...) +#endif + +typedef struct +{ + char receive_buffer[SOFTUART_MAX_RX_BUFF]; + uint8_t receive_buffer_tail; + uint8_t receive_buffer_head; + uint8_t buffer_overflow; +} softuart_buffer_t; + +typedef struct +{ + uint8_t rx_pin, tx_pin; + uint32_t baudrate; + volatile softuart_buffer_t buffer; + uint16_t bit_time; +} softuart_t; + +static softuart_t uarts[SOFTUART_MAX_UARTS] = { { 0 } }; + +inline static int8_t find_uart_by_rx(uint8_t rx_pin) +{ + for (uint8_t i = 0; i < SOFTUART_MAX_UARTS; i++) + if (uarts[i].baudrate && uarts[i].rx_pin == rx_pin) return i; + + return -1; +} + +// GPIO interrupt handler +static void handle_rx(uint8_t gpio_num) +{ + // find uart + int8_t uart_no = find_uart_by_rx(gpio_num); + if (uart_no < 0) return; + + softuart_t *uart = uarts + uart_no; + + // Disable interrupt + gpio_set_interrupt(gpio_num, GPIO_INTTYPE_NONE, handle_rx); + + // Wait till start bit is half over so we can sample the next one in the center + sdk_os_delay_us(uart->bit_time / 2); + + // Now sample bits + uint8_t d = 0; + uint32_t start_time = 0x7FFFFFFF & sdk_system_get_time(); + + for (uint8_t i = 0; i < 8; i++) + { + while ((0x7FFFFFFF & sdk_system_get_time()) < (start_time + (uart->bit_time * (i + 1)))) + { + // If system timer overflow, escape from while loop + if ((0x7FFFFFFF & sdk_system_get_time()) < start_time) + break; + } + // Shift d to the right + d >>= 1; + + // Read bit + if (gpio_read(uart->rx_pin)) + { + // If high, set msb of 8bit to 1 + d |= 0x80; + } + } + + // Store byte in buffer + // If buffer full, set the overflow flag and return + uint8_t next = (uart->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; + if (next != uart->buffer.receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + uart->buffer.receive_buffer[uart->buffer.receive_buffer_tail] = d; // save new byte + uart->buffer.receive_buffer_tail = next; + } + else + { + uart->buffer.buffer_overflow = 1; + } + + // Wait for stop bit + sdk_os_delay_us(uart->bit_time); + + // Done, reenable interrupt + gpio_set_interrupt(uart->rx_pin, GPIO_INTTYPE_EDGE_NEG, handle_rx); +} + +static bool check_uart_no(uint8_t uart_no) +{ + if (uart_no >= SOFTUART_MAX_UARTS) + { + debug("Invalid uart number %d, %d max", uart_no, SOFTUART_MAX_UARTS); + return false; + } + + return true; +} + +static bool check_uart_enabled(uint8_t uart_no) +{ + if (!uarts[uart_no].baudrate) + { + debug("Uart %d is disabled", uart_no); + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Public +/////////////////////////////////////////////////////////////////////////////// + +bool softuart_open(uint8_t uart_no, uint32_t baudrate, uint8_t rx_pin, uint8_t tx_pin) +{ + // do some checks + if (!check_uart_no(uart_no)) return false; + if (baudrate == 0) + { + debug("Invalid baudrate"); + return false; + } + for (uint8_t i = 0; i < SOFTUART_MAX_UARTS; i++) + if (uarts[i].baudrate && i != uart_no + && (uarts[i].rx_pin == rx_pin || uarts[i].tx_pin == tx_pin || uarts[i].rx_pin == tx_pin || uarts[i].tx_pin == rx_pin)) + { + debug("Cannot share pins between uarts"); + return false; + } + + softuart_close(uart_no); + + softuart_t *uart = uarts + uart_no; + + uart->baudrate = baudrate; + uart->rx_pin = rx_pin; + uart->tx_pin = tx_pin; + + // Calculate bit_time + uart->bit_time = (1000000 / baudrate); + if (((100000000 / baudrate) - (100 * uart->bit_time)) > 50) uart->bit_time++; + + // Setup Rx + gpio_enable(rx_pin, GPIO_INPUT); + gpio_set_pullup(rx_pin, true, false); + + // Setup Tx + gpio_enable(tx_pin, GPIO_OUTPUT); + gpio_set_pullup(tx_pin, true, false); + gpio_write(tx_pin, 1); + + // Setup the interrupt handler to get the start bit + gpio_set_interrupt(rx_pin, GPIO_INTTYPE_EDGE_NEG, handle_rx); + + sdk_os_delay_us(1000); // TODO: not sure if it really needed + + return true; +} + +bool softuart_close(uint8_t uart_no) +{ + if (!check_uart_no(uart_no)) return false; + softuart_t *uart = uarts + uart_no; + + if (!uart->baudrate) return true; + + // Remove interrupt + gpio_set_interrupt(uart->rx_pin, GPIO_INTTYPE_NONE, NULL); + // Mark as unused + uart->baudrate = 0; + + return true; +} + +bool softuart_put(uint8_t uart_no, char c) +{ + if (!check_uart_no(uart_no)) return false; + if (!check_uart_enabled(uart_no)) return false; + softuart_t *uart = uarts + uart_no; + + uint32_t start_time = 0x7FFFFFFF & sdk_system_get_time(); + gpio_write(uart->tx_pin, 0); + + for (uint8_t i = 0; i <= 8; i++) + { + while ((0x7FFFFFFF & sdk_system_get_time()) < (start_time + (uart->bit_time * (i + 1)))) + { + if ((0x7FFFFFFF & sdk_system_get_time()) < start_time) + break; + } + gpio_write(uart->tx_pin, c & (1 << i)); + } + + while ((0x7FFFFFFF & sdk_system_get_time()) < (start_time + (uart->bit_time * 9))) + { + if ((0x7FFFFFFF & sdk_system_get_time()) < start_time) + break; + } + gpio_write(uart->tx_pin, 1); + sdk_os_delay_us(uart->bit_time * 6); + + return true; +} + +bool softuart_puts(uint8_t uart_no, const char *s) +{ + while (*s) + { + if (!softuart_put(uart_no, *s++)) + return false; + } + + return true; +} + +bool softuart_available(uint8_t uart_no) +{ + if (!check_uart_no(uart_no)) return false; + if (!check_uart_enabled(uart_no)) return false; + softuart_t *uart = uarts + uart_no; + + return (uart->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - uart->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; +} + +uint8_t softuart_read(uint8_t uart_no) +{ + if (!check_uart_no(uart_no)) return 0; + if (!check_uart_enabled(uart_no)) return 0; + softuart_t *uart = uarts + uart_no; + + // Empty buffer? + if (uart->buffer.receive_buffer_head == uart->buffer.receive_buffer_tail) return 0; + + // Read from "head" + uint8_t d = uart->buffer.receive_buffer[uart->buffer.receive_buffer_head]; // grab next byte + uart->buffer.receive_buffer_head = (uart->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; + return d; +} + diff --git a/extras/softuart/softuart.h b/extras/softuart/softuart.h new file mode 100644 index 0000000..e5fa8f6 --- /dev/null +++ b/extras/softuart/softuart.h @@ -0,0 +1,93 @@ +/* + * Softuart for esp-open-rtos + * + * Copyright (C) 2017 Ruslan V. Uss + * Copyright (C) 2016 Bernhard Guillon + * + * This code is based on Softuart from here [1] and reworked to + * fit into esp-open-rtos. For now only the RX part is ported. + * Also the configuration of the pin is for now hardcoded. + * + * it fits my needs to read the GY-GPS6MV2 module with 9600 8n1 + * + * Original Copyright: + * Copyright (c) 2015 plieningerweb + * + * MIT Licensed as described in the file LICENSE + * + * 1 https://github.com/plieningerweb/esp8266-software-uart + */ +#ifndef SOFTUART_H_ +#define SOFTUART_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef SOFTUART_MAX_UARTS + #define SOFTUART_MAX_UARTS 2 +#endif + +#ifndef SOFTUART_MAX_RX_BUFF + #define SOFTUART_MAX_RX_BUFF 64 //!< Must be power of two: 2, 4, 8, 16 etc. +#endif + +/** + * Initialize software uart and setup interrupt handler + * @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS + * @param baudrate Baudrate, e.g. 9600, 19200, etc + * @param rx_pin GPIO pin number for RX + * @param tx_pin GPIO pin number for TX + * @return true if no errors occured otherwise false + */ +bool softuart_open(uint8_t uart_no, uint32_t baudrate, uint8_t rx_pin, uint8_t tx_pin); + +/** + * Deinitialize software uart + * @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS + * @return true if no errors occured otherwise false + */ +bool softuart_close(uint8_t uart_no); + +/** + * Put char to software uart + * @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS + * @param c Char + * @return true if no errors occured otherwise false + */ +bool softuart_put(uint8_t uart_no, char c); + +/** + * Put string to software uart + * @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS + * @param s Null-terminated string + * @return true if no errors occured otherwise false + */ +bool softuart_puts(uint8_t uart_no, const char *s); + +/** + * Check if data is available + * @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS + * @return true if data is available otherwise false + */ +bool softuart_available(uint8_t uart_no); + +/** + * Read current byte from internal buffer if available. + * + * NOTE: This call is non blocking. + * NOTE: You have to check softuart_available() first. + * @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS + * @return current byte if available otherwise 0 + */ +uint8_t softuart_read(uint8_t uart_no); + +#ifdef __cplusplus +} +#endif + +#endif /* SOFTUART_H_ */