From dbf643fc52164e1167661130defcf5806e201400 Mon Sep 17 00:00:00 2001 From: Damien Mascord Date: Mon, 15 Jul 2019 23:45:58 +1000 Subject: [PATCH 01/14] ports/esp8266: Added SoftUART support. Code copied from https://github.com/p-v-o-s/micropython. --- lib/lwip | 2 +- ports/esp8266/Makefile | 2 + ports/esp8266/boards/esp8266_common.ld | 2 + ports/esp8266/espmissingincludes.h | 75 ++++ ports/esp8266/machine_softuart.c | 246 +++++++++++++ ports/esp8266/modmachine.c | 1 + ports/esp8266/modmachine.h | 1 + ports/esp8266/softuart.c | 456 +++++++++++++++++++++++++ ports/esp8266/softuart.h | 76 +++++ 9 files changed, 860 insertions(+), 1 deletion(-) create mode 100644 ports/esp8266/espmissingincludes.h create mode 100644 ports/esp8266/machine_softuart.c create mode 100644 ports/esp8266/softuart.c create mode 100644 ports/esp8266/softuart.h diff --git a/lib/lwip b/lib/lwip index 159e31b689577..92f23d6ca0971 160000 --- a/lib/lwip +++ b/lib/lwip @@ -1 +1 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 +Subproject commit 92f23d6ca0971a32f2085b9480e738d34174417b diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 02ea05f76e064..e5c831563f91a 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -82,6 +82,7 @@ endif MPY_CROSS_FLAGS += -march=xtensa SRC_C = \ + softuart.c \ strtoll.c \ main.c \ help.c \ @@ -100,6 +101,7 @@ SRC_C = \ machine_rtc.c \ machine_adc.c \ machine_uart.c \ + machine_softuart.c \ machine_wdt.c \ machine_hspi.c \ modesp.c \ diff --git a/ports/esp8266/boards/esp8266_common.ld b/ports/esp8266/boards/esp8266_common.ld index c6e37d942c6bf..505b47fa36965 100644 --- a/ports/esp8266/boards/esp8266_common.ld +++ b/ports/esp8266/boards/esp8266_common.ld @@ -157,6 +157,8 @@ SECTIONS *machine_rtc.o(.literal*, .text*) *machine_adc.o(.literal*, .text*) *machine_uart.o(.literal*, .text*) + *softuart.o(.literal*, .text*) + *machine_softuart.o(.literal*, .text*) *modpybi2c.o(.literal*, .text*) *modmachine.o(.literal*, .text*) *machine_wdt.o(.literal*, .text*) diff --git a/ports/esp8266/espmissingincludes.h b/ports/esp8266/espmissingincludes.h new file mode 100644 index 0000000000000..518f138d827fc --- /dev/null +++ b/ports/esp8266/espmissingincludes.h @@ -0,0 +1,75 @@ +#ifndef ESPMISSINGINCLUDES_H +#define ESPMISSINGINCLUDES_H + +#include +#include + + +int strcasecmp(const char *a, const char *b); +#ifndef FREERTOS +#include +#include +//Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. +//MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler. +typedef struct espconn espconn; + +int atoi(const char *nptr); +//void ets_install_putc1(void *routine); +void ets_isr_mask(unsigned intr); +void ets_isr_unmask(unsigned intr); +int ets_memcmp(const void *s1, const void *s2, size_t n); +void *ets_memcpy(void *dest, const void *src, size_t n); +void *ets_memset(void *s, int c, size_t n); +int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +int ets_str2macaddr(void *, void *); +int ets_strcmp(const char *s1, const char *s2); +char *ets_strcpy(char *dest, const char *src); +//size_t ets_strlen(const char *s); +//int ets_strncmp(const char *s1, const char *s2, int len); +char *ets_strncpy(char *dest, const char *src, size_t n); +char *ets_strstr(const char *haystack, const char *needle); +void ets_timer_disarm(os_timer_t *a); +void ets_timer_setfn(os_timer_t *t, ETSTimerFunc *fn, void *parg); +void ets_update_cpu_frequency(int freqmhz); +void *os_memmove(void *dest, const void *src, size_t n); +int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4))); +int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +//void uart_div_modify(int no, unsigned int freq); +uint8 wifi_get_opmode(void); +uint32 system_get_time(); +int rand(void); +void ets_bzero(void *s, size_t n); +//void ets_delay_us(int ms); + +//Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself +//has no meaning here. +#ifndef RC_LIMIT_P2P_11N +//Defs for SDK <1.4.0 +void *pvPortMalloc(size_t xWantedSize); +void *pvPortZalloc(size_t); +void vPortFree(void *ptr); +void *vPortMalloc(size_t xWantedSize); +void pvPortFree(void *ptr); +#else +//void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +//void *pvPortZalloc(size_t, const char *file, int line); +//void vPortFree(void *ptr, const char *file, int line); +void *vPortMalloc(size_t xWantedSize, const char *file, int line); +void pvPortFree(void *ptr, const char *file, int line); +#endif + +//Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one. +#ifdef PIN_FUNC_SELECT +#undef PIN_FUNC_SELECT +#define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ + WRITE_PERI_REG(PIN_NAME, \ + (READ_PERI_REG(PIN_NAME) \ + & (~(PERIPHS_IO_MUX_FUNC< +#include +#include + +#include "ets_sys.h" +#include "mem.h" +#include "softuart.h" + + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/mphal.h" +//#include "py/malloc.h" +#include "modmachine.h" + +// UartDev is defined and initialized in rom code. +//FXIME //extern UartDevice UartDev; + +Softuart softuartDevice; + +typedef struct _pyb_softuart_obj_t { + mp_obj_base_t base; + //uint8_t uart_id; + Softuart *softuart_ptr; //point to instance of driver object + pyb_pin_obj_t *tx; + pyb_pin_obj_t *rx; + uint8_t bits; + uint8_t parity; + uint8_t stop; + uint32_t baudrate; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) +} pyb_softuart_obj_t; + +STATIC const char *_parity_name[] = {"None", "1", "0"}; + +/******************************************************************************/ +// MicroPython bindings for softUART + +STATIC void pyb_softuart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SoftUART(tx=%u, rx=%u, baudrate=%u, bits=%u, parity=%s, stop=%u, timeout=%u, timeout_char=%u)", + mp_obj_get_pin(self->tx), mp_obj_get_pin(self->rx), + self->baudrate, self->bits, _parity_name[self->parity], + self->stop, self->timeout, self->timeout_char); +} + +STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_timeout, ARG_timeout_char}; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, + //{ MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, + //{ MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + //{ MP_QSTR_stop, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 10} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + //assign the pins + self->tx = mp_obj_get_pin_obj(args[ARG_tx].u_obj); + Softuart_SetPinTx(&softuartDevice, mp_obj_get_pin(self->tx)); + self->rx = mp_obj_get_pin_obj(args[ARG_rx].u_obj); + Softuart_SetPinRx(&softuartDevice, mp_obj_get_pin(self->rx)); + + // set baudrate + if (args[ARG_baudrate].u_int > 0) { + self->baudrate = args[ARG_baudrate].u_int; + Softuart_Init(&softuartDevice, self->baudrate); + //UartDev.baut_rate = self->baudrate; // Sic! + } + + // set data bits + self->bits = 8; //no other options are supported + + + // set parity + self->parity = 0; //"NONE" no other options are supported + + // set stop bits + self->stop = 1; //"NONE" no other options are supported + + // set timeout + self->timeout = args[ARG_timeout].u_int; + + // set timeout_char + // make sure it is at least as long as a whole character (13 bits to be safe) + self->timeout_char = args[ARG_timeout_char].u_int; + uint32_t min_timeout_char = 13000 / self->baudrate + 1; + if (self->timeout_char < min_timeout_char) { + self->timeout_char = min_timeout_char; +} + + // setup + //FIXME //uart_setup(self->uart_id); +} + +STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // create instance + pyb_softuart_obj_t *self = m_new_obj(pyb_softuart_obj_t); + self->base.type = &pyb_softuart_type; + //FIXME removed //self->uart_id = uart_id; + //self->softuart_ptr = os_malloc(sizeof(Softuart)); + self->baudrate = 9600; + self->bits = 8; + self->parity = 0; + self->stop = 1; + self->timeout = 0; + self->timeout_char = 0; + + // init the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_softuart_init_helper(self, n_args, args, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t pyb_softuart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + pyb_softuart_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(pyb_softuart_init_obj, 1, pyb_softuart_init); + +STATIC mp_obj_t pyb_softuart_flush(mp_obj_t self_in) { + //pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + Softuart_Flush(&softuartDevice); //reset the rx buffer to empty + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(pyb_softuart_flush_obj, pyb_softuart_flush); + + +STATIC const mp_rom_map_elem_t pyb_softuart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_softuart_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&pyb_softuart_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(pyb_softuart_locals_dict, pyb_softuart_locals_dict_table); + +STATIC mp_uint_t pyb_softuart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + // wait for first char to become available + if (!Softuart_rxWait(&softuartDevice, self->timeout * 1000)) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + // read the data + uint8_t *buf = buf_in; + while (Softuart_Available(&softuartDevice)) { + *buf++ = Softuart_Read(&softuartDevice); + if (--size == 0 || !Softuart_rxWait(&softuartDevice, self->timeout_char * 1000)) { + // return number of bytes read + return buf - (uint8_t*)buf_in; + } + } + return 0; //FIXME +} + +STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + //pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + const byte *buf = buf_in; + + /* TODO implement non-blocking + // wait to be able to write the first character + if (!uart_tx_wait(self, timeout)) { + *errcode = EAGAIN; + return MP_STREAM_ERROR; + } + */ + + // write the data + for (size_t i = 0; i < size; ++i) { + Softuart_Putchar(&softuartDevice, *buf++); + //FIXME //uart_tx_one_char(self->uart_id, *buf++); + } + + // return number of bytes written + return size; +} + +STATIC mp_uint_t pyb_softuart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; +} + +STATIC const mp_stream_p_t softuart_stream_p = { + .read = pyb_softuart_read, + .write = pyb_softuart_write, + .ioctl = pyb_softuart_ioctl, + .is_text = false, +}; + +const mp_obj_type_t pyb_softuart_type = { + { &mp_type_type }, + .name = MP_QSTR_SoftUART, + .print = pyb_softuart_print, + .make_new = pyb_softuart_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &softuart_stream_p, + .locals_dict = (mp_obj_dict_t*)&pyb_softuart_locals_dict, +}; diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 86c1d728d5369..d6ae7f7884e22 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -421,6 +421,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pyb_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftUART), MP_ROM_PTR(&pyb_softuart_type) }, #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, diff --git a/ports/esp8266/modmachine.h b/ports/esp8266/modmachine.h index be4debd335a64..6c980f77dfeb6 100644 --- a/ports/esp8266/modmachine.h +++ b/ports/esp8266/modmachine.h @@ -8,6 +8,7 @@ extern const mp_obj_type_t pyb_pwm_type; extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t pyb_rtc_type; extern const mp_obj_type_t pyb_uart_type; +extern const mp_obj_type_t pyb_softuart_type; extern const mp_obj_type_t pyb_i2c_type; extern const mp_obj_type_t machine_hspi_type; diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c new file mode 100644 index 0000000000000..ea9cddafa31ab --- /dev/null +++ b/ports/esp8266/softuart.c @@ -0,0 +1,456 @@ +/* +The MIT License (MIT) + +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. +*/ + +#include "ets_sys.h" +#include "etshal.h" +#include "osapi.h" +#include "gpio.h" +#include "os_type.h" +#include "esp_mphal.h" +#include "user_interface.h" +#include "espmissingincludes.h" + +#include "softuart.h" + +//array of pointers to instances +Softuart *_Softuart_GPIO_Instances[SOFTUART_GPIO_COUNT]; +uint8_t _Softuart_Instances_Count = 0; + +//intialize list of gpio names and functions +softuart_reg_t softuart_reg[] = +{ + { PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0 }, //gpio0 + { PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1 }, //gpio1 (uart) + { PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2 }, //gpio2 + { PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3 }, //gpio3 (uart) + { PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 }, //gpio4 + { PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 }, //gpio5 + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12 }, //gpio12 + { PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13 }, //gpio13 + { PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14 }, //gpio14 + { PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15 }, //gpio15 + //@TODO TODO gpio16 is missing (?include) +}; + +uint8_t Softuart_Bitcount(uint32_t x) +{ + uint8_t count; + + for (count=0; x != 0; x>>=1) { + if ( x & 0x01) { + return count; + } + count++; + } + //error: no 1 found! + return 0xFF; +} + +uint8_t Softuart_IsGpioValid(uint8_t gpio_id) +{ + if ((gpio_id > 5 && gpio_id < 12) || gpio_id > 15) + { + return 0; + } + return 1; +} + +void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id) +{ + if(! Softuart_IsGpioValid(gpio_id)) { + //os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); + } else { + s->pin_rx.gpio_id = gpio_id; + s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; + } +} + +void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id) +{ + if(! Softuart_IsGpioValid(gpio_id)) { + //os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); + } else { + s->pin_tx.gpio_id = gpio_id; + s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; + } +} + +void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id) +{ + //os_printf("SOFTUART RS485 init\r\n"); + + //enable rs485 + s->is_rs485 = 1; + + //set pin in instance + s->pin_rs485_tx_enable = gpio_id; + + //enable pin as gpio + PIN_FUNC_SELECT(softuart_reg[gpio_id].gpio_mux_name,softuart_reg[gpio_id].gpio_func); + + PIN_PULLUP_DIS(softuart_reg[gpio_id].gpio_mux_name); + + //set low for tx idle (so other bus participants can send) + GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_id), 0); + + //os_printf("SOFTUART RS485 init done\r\n"); +} + +void Softuart_Init(Softuart *s, uint32_t baudrate) +{ + //disable rs485 + s->is_rs485 = 0; + + if(! _Softuart_Instances_Count) { + //os_printf("SOFTUART initialize gpio\r\n"); + //Initilaize gpio subsystem + gpio_init(); + } + + //set bit time + if(baudrate <= 0) { + //os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); + } else { + s->bit_time = (1000000 / baudrate); + if ( ((100000000 / baudrate) - (100*s->bit_time)) > 50 ) s->bit_time++; + //os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); + } + + + //init tx pin + if(!s->pin_tx.gpio_mux_name) { + //os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); + } else { + //enable pin as gpio + PIN_FUNC_SELECT(s->pin_tx.gpio_mux_name, s->pin_tx.gpio_func); + + //set pullup (UART idle is VDD) + PIN_PULLUP_EN(s->pin_tx.gpio_mux_name); + + //set high for tx idle + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); + unsigned long long_delay = 100000UL; + os_delay_us(long_delay); + + //os_printf("SOFTUART TX INIT DONE\r\n"); + } + + //init rx pin + if(!s->pin_rx.gpio_mux_name) { + //os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); + } else { + //enable pin as gpio + PIN_FUNC_SELECT(s->pin_rx.gpio_mux_name, s->pin_rx.gpio_func); + + //set pullup (UART idle is VDD) + PIN_PULLUP_EN(s->pin_rx.gpio_mux_name); + + //set to input -> disable output + GPIO_DIS_OUTPUT(GPIO_ID_PIN(s->pin_rx.gpio_id)); + + //set interrupt related things + + //disable interrupts by GPIO + ETS_GPIO_INTR_DISABLE(); + + //attach interrupt handler and a pointer that will be passed around each time + ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, s); + + //not sure what this does... (quote from example): + // void gpio_register_set(uint32 reg_id, uint32 value); + // + // From include file + // Set the specified GPIO register to the specified value. + // This is a very general and powerful interface that is not + // expected to be used during normal operation. It is intended + // mainly for debug, or for unusual requirements. + // + // All people repeat this mantra but I don't know what it means + // + gpio_register_set(GPIO_PIN_ADDR(s->pin_rx.gpio_id), + GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) | + GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) | + GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); + + //clear interrupt handler status, basically writing a low to the output + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(s->pin_rx.gpio_id)); + + //enable interrupt for pin on any edge (rise and fall) + //@TODO: should work with ANYEDGE (=3), but complie error + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); + + //globally enable GPIO interrupts + ETS_GPIO_INTR_ENABLE(); + + //os_printf("SOFTUART RX INIT DONE\r\n"); + } + + //add instance to array of instances + _Softuart_GPIO_Instances[s->pin_rx.gpio_id] = s; + _Softuart_Instances_Count++; + + //os_printf("SOFTUART INIT DONE\r\n"); +} + +void Softuart_Intr_Handler(void *p) +{ + uint8_t level, gpio_id; + // clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler + + //ignore void* pointer and make a new pointer + Softuart *s; + + + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + gpio_id = Softuart_Bitcount(gpio_status); + + //if interrupt was by an attached rx pin + if (gpio_id != 0xFF) + { + //load instance which has rx pin on interrupt pin attached + s = _Softuart_GPIO_Instances[gpio_id]; + + // disable interrupt for GPIO0 + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), GPIO_PIN_INTR_DISABLE); + + // Do something, for example, increment whatyouwant indirectly + //check level + level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); + if(!level) + { + //pin is low + //therefore we have a start bit + + //wait till start bit is half over so we can sample the next one in the center + os_delay_us(s->bit_time/2); + + //now sample bits + unsigned i; + unsigned d = 0; + //mark the start of the data bit pattern + uint32_t start_time = system_get_time(); + for(i = 0; i < 8; i ++ ) + { + //delay for bit timing + // NOTE signed arithmetic handles system_get_time() wrap-around + // NOTE using repeated calls to `os_delay_us` can accumulate overhead errors + // see https://forum.micropython.org/viewtopic.php?f=16&t=2204&start=10#p16935 + int32_t end_time = (int32_t) (start_time + (i + 1)*s->bit_time); + while ( end_time - (int32_t) system_get_time() > 0){}; + + //shift d to the right + d >>= 1; + + //read bit + if(GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id))) { + //if high, set msb of 8bit to 1 + d |= 0x80; + } + } + + //store byte in buffer + // if buffer full, set the overflow flag and return + uint8 next = (s->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; + if (next != s->buffer.receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; // save new byte + s->buffer.receive_buffer_tail = next; + } + else + { + s->buffer.buffer_overflow = 1; + } + + //wait for stop bit + os_delay_us(s->bit_time); + //done + } + //clear interrupt + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); + // Reactivate interrupts for GPIO0 + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); + } else + { + //clear interrupt, no matter from which pin + //otherwise, this interrupt will be called again forever + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); + } +} + + + +// Read data from buffer +uint8_t Softuart_Read(Softuart *s) +{ + // Empty buffer? + if (s->buffer.receive_buffer_head == s->buffer.receive_buffer_tail) + return 0; + + // Read from "head" + uint8_t d = s->buffer.receive_buffer[s->buffer.receive_buffer_head]; // grab next byte + s->buffer.receive_buffer_head = (s->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; + return d; +} + +// Flush data from buffer +uint32_t Softuart_Flush(Softuart *s) +{ + uint32_t num_chars = s->buffer.receive_buffer_tail - s->buffer.receive_buffer_head; + // Empty buffer + s->buffer.receive_buffer_head = 0; + s->buffer.receive_buffer_tail = 0; + return num_chars; +} + + +// Is data in buffer available? +BOOL Softuart_Available(Softuart *s) +{ + return (s->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - s->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; +} + +// cversek: +// based on micropython/esp8266/uart.c bool uart_rx_wait(uint32_t timeout_us) +// Waits at most timeout microseconds for at least 1 char to become ready for reading. +// Returns true if something available, false if not. +BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us) +{ + //handles system_get_time() wrap-around + int32_t when_timedout = (int32_t) system_get_time() + timeout_us; + for (;;) { + if (Softuart_Available(s)) { + return true; // have at least 1 char ready for reading + } + //handles system_get_time()-wrap around + if (when_timedout - (int32_t) system_get_time() <= 0) { + return false; // timeout + } + ets_event_poll(); + } +} + +static inline u8 chbit(u8 data, u8 bit) +{ + if ((data & bit) != 0) + { + return 1; + } + else + { + return 0; + } +} + +// Function for printing individual characters +void Softuart_Putchar(Softuart *s, char data) +{ + unsigned i; + + //if rs485 set tx enable + if(s->is_rs485 == 1) + { + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 1); + } + + //mark the start of the data bit pattern + uint32_t start_time = system_get_time(); + int32_t end_time; //calculated in loop + + //Start Bit + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 0); + for(i = 0; i <= 8; i ++ ) + { + //delay for bit timing + // NOTE signed arithmetic handles system_get_time() wrap-around + // NOTE using repeated calls to `os_delay_us` can accumulate overhead errors + // see https://forum.micropython.org/viewtopic.php?f=16&t=2204&start=10#p16935 + end_time = (int32_t) (start_time + (i + 1)*s->bit_time); + while ( end_time - (int32_t) system_get_time() > 0){}; + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), chbit(data,1<bit_time); + while ( end_time - (int32_t) system_get_time() > 0){}; + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); + + // Delay after byte, for new sync + os_delay_us(s->bit_time*6); + + //if rs485 set tx disable + if(s->is_rs485 == 1) + { + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 0); + } +} + +void Softuart_Puts(Softuart *s, const char *c ) +{ + while ( *c ) { + Softuart_Putchar(s,( u8 )*c++); + } +} + +uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ) +{ + uint8_t NextChar; + uint8_t len = 0; + + while( Softuart_Available(s) ) + { + NextChar = Softuart_Read(s); + + if(NextChar == '\r') + { + continue; + } else if(NextChar == '\n') + { + //break only if we already found a character + //if it was .e.g. only \r, we wait for the first useful character + if(len > 0) { + break; + } + } else if(len < MaxLen - 1 ) + { + *Buffer++ = NextChar; + len++; + } else { + break; + } + } + //add string terminator + *Buffer++ = '\0'; + + return len; +} + diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h new file mode 100644 index 0000000000000..70003062a3a47 --- /dev/null +++ b/ports/esp8266/softuart.h @@ -0,0 +1,76 @@ +/* +The MIT License (MIT) + +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. +*/ + +#ifndef SOFTUART_H_ +#define SOFTUART_H_ + +#define SOFTUART_MAX_RX_BUFF 64 + +#define SOFTUART_GPIO_COUNT 16 + +typedef struct softuart_pin_t { + uint8_t gpio_id; + uint32_t gpio_mux_name; + uint8_t gpio_func; +} softuart_pin_t; + +typedef struct softuart_buffer_t { + 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 { + softuart_pin_t pin_rx; + softuart_pin_t pin_tx; + //optional rs485 tx enable pin (high -> tx enabled) + uint8_t pin_rs485_tx_enable; + //wether or not this softuart is rs485 and controlls rs485 tx enable pin + uint8_t is_rs485; + volatile softuart_buffer_t buffer; + uint16_t bit_time; +} Softuart; + + +BOOL Softuart_Available(Softuart *s); +uint32_t Softuart_Flush(Softuart *s); +BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us); +void Softuart_Intr_Handler(void *p); //void* for type compatibility with etshal.h: void ets_isr_attach(int irq_no, void (*handler)(void *), void *arg); +void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id); +void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id); +void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id); +void Softuart_Init(Softuart *s, uint32_t baudrate); +void Softuart_Putchar(Softuart *s, char data); +void Softuart_Puts(Softuart *s, const char *c ); +uint8_t Softuart_Read(Softuart *s); +uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ); + +//define mapping from pin to functio mode +typedef struct { + uint32_t gpio_mux_name; + uint8_t gpio_func; +} softuart_reg_t; + +#endif /* SOFTUART_H_ */ From d90a4974392dad32b070d487f106df0465d48957 Mon Sep 17 00:00:00 2001 From: Damien Mascord Date: Wed, 17 Jul 2019 14:37:59 +1000 Subject: [PATCH 02/14] esp8266/softuart.c: Integrated the latest code from plieningerweb. Link: https://github.com/plieningerweb/esp8266-software-uart. --- ports/esp8266/softuart.c | 618 +++++++++++++++++++-------------------- ports/esp8266/softuart.h | 62 ++-- 2 files changed, 315 insertions(+), 365 deletions(-) diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index ea9cddafa31ab..35a3492a910a1 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -1,27 +1,3 @@ -/* -The MIT License (MIT) - -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. -*/ - #include "ets_sys.h" #include "etshal.h" #include "osapi.h" @@ -30,7 +6,6 @@ SOFTWARE. #include "esp_mphal.h" #include "user_interface.h" #include "espmissingincludes.h" - #include "softuart.h" //array of pointers to instances @@ -40,271 +15,270 @@ uint8_t _Softuart_Instances_Count = 0; //intialize list of gpio names and functions softuart_reg_t softuart_reg[] = { - { PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0 }, //gpio0 - { PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1 }, //gpio1 (uart) - { PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2 }, //gpio2 - { PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3 }, //gpio3 (uart) - { PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 }, //gpio4 - { PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 }, //gpio5 - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12 }, //gpio12 - { PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13 }, //gpio13 - { PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14 }, //gpio14 - { PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15 }, //gpio15 - //@TODO TODO gpio16 is missing (?include) + { PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0 }, //gpio0 + { PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1 }, //gpio1 (uart) + { PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2 }, //gpio2 + { PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3 }, //gpio3 (uart) + { PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 }, //gpio4 + { PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 }, //gpio5 + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12 }, //gpio12 + { PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13 }, //gpio13 + { PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14 }, //gpio14 + { PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15 }, //gpio15 + //@TODO TODO gpio16 is missing (?include) }; uint8_t Softuart_Bitcount(uint32_t x) { - uint8_t count; + uint8_t count; - for (count=0; x != 0; x>>=1) { - if ( x & 0x01) { - return count; - } - count++; - } - //error: no 1 found! - return 0xFF; + for (count=0; x != 0; x>>=1) { + if ( x & 0x01) { + return count; + } + count++; + } + //error: no 1 found! + return 0xFF; } uint8_t Softuart_IsGpioValid(uint8_t gpio_id) { - if ((gpio_id > 5 && gpio_id < 12) || gpio_id > 15) - { - return 0; - } - return 1; + if ((gpio_id > 5 && gpio_id < 12) || gpio_id > 15) + { + return 0; + } + return 1; } void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id) { - if(! Softuart_IsGpioValid(gpio_id)) { - //os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); - } else { - s->pin_rx.gpio_id = gpio_id; - s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; - s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; - } + if(! Softuart_IsGpioValid(gpio_id)) { + os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); + } else { + s->pin_rx.gpio_id = gpio_id; + s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; + } } void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id) { - if(! Softuart_IsGpioValid(gpio_id)) { - //os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); - } else { - s->pin_tx.gpio_id = gpio_id; - s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; - s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; - } + if(! Softuart_IsGpioValid(gpio_id)) { + os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); + } else { + s->pin_tx.gpio_id = gpio_id; + s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; + } } void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id) { - //os_printf("SOFTUART RS485 init\r\n"); + os_printf("SOFTUART RS485 init\r\n"); - //enable rs485 - s->is_rs485 = 1; + //enable rs485 + s->is_rs485 = 1; - //set pin in instance - s->pin_rs485_tx_enable = gpio_id; + //set pin in instance + s->pin_rs485_tx_enable = gpio_id; - //enable pin as gpio - PIN_FUNC_SELECT(softuart_reg[gpio_id].gpio_mux_name,softuart_reg[gpio_id].gpio_func); + //enable pin as gpio + PIN_FUNC_SELECT(softuart_reg[gpio_id].gpio_mux_name,softuart_reg[gpio_id].gpio_func); - PIN_PULLUP_DIS(softuart_reg[gpio_id].gpio_mux_name); - - //set low for tx idle (so other bus participants can send) - GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_id), 0); - - //os_printf("SOFTUART RS485 init done\r\n"); + PIN_PULLUP_DIS(softuart_reg[gpio_id].gpio_mux_name); + + //set low for tx idle (so other bus participants can send) + GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_id), 0); + + os_printf("SOFTUART RS485 init done\r\n"); } void Softuart_Init(Softuart *s, uint32_t baudrate) { - //disable rs485 - s->is_rs485 = 0; - - if(! _Softuart_Instances_Count) { - //os_printf("SOFTUART initialize gpio\r\n"); - //Initilaize gpio subsystem - gpio_init(); - } - - //set bit time - if(baudrate <= 0) { - //os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); + //disable rs485 + s->is_rs485 = 0; + + if(! _Softuart_Instances_Count) { + os_printf("SOFTUART initialize gpio\r\n"); + //Initilaize gpio subsystem + gpio_init(); + } + + //set bit time + if(baudrate <= 0) { + os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); } else { s->bit_time = (1000000 / baudrate); if ( ((100000000 / baudrate) - (100*s->bit_time)) > 50 ) s->bit_time++; - //os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); + os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); } - //init tx pin - if(!s->pin_tx.gpio_mux_name) { - //os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); - } else { - //enable pin as gpio - PIN_FUNC_SELECT(s->pin_tx.gpio_mux_name, s->pin_tx.gpio_func); - - //set pullup (UART idle is VDD) - PIN_PULLUP_EN(s->pin_tx.gpio_mux_name); - - //set high for tx idle - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); - unsigned long long_delay = 100000UL; - os_delay_us(long_delay); - - //os_printf("SOFTUART TX INIT DONE\r\n"); - } - - //init rx pin - if(!s->pin_rx.gpio_mux_name) { - //os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); - } else { - //enable pin as gpio - PIN_FUNC_SELECT(s->pin_rx.gpio_mux_name, s->pin_rx.gpio_func); - - //set pullup (UART idle is VDD) - PIN_PULLUP_EN(s->pin_rx.gpio_mux_name); - - //set to input -> disable output - GPIO_DIS_OUTPUT(GPIO_ID_PIN(s->pin_rx.gpio_id)); - - //set interrupt related things - - //disable interrupts by GPIO - ETS_GPIO_INTR_DISABLE(); - - //attach interrupt handler and a pointer that will be passed around each time - ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, s); - - //not sure what this does... (quote from example): - // void gpio_register_set(uint32 reg_id, uint32 value); - // - // From include file - // Set the specified GPIO register to the specified value. - // This is a very general and powerful interface that is not - // expected to be used during normal operation. It is intended - // mainly for debug, or for unusual requirements. - // - // All people repeat this mantra but I don't know what it means - // - gpio_register_set(GPIO_PIN_ADDR(s->pin_rx.gpio_id), - GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) | - GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) | - GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); - - //clear interrupt handler status, basically writing a low to the output - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(s->pin_rx.gpio_id)); - - //enable interrupt for pin on any edge (rise and fall) - //@TODO: should work with ANYEDGE (=3), but complie error - gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); - - //globally enable GPIO interrupts - ETS_GPIO_INTR_ENABLE(); - - //os_printf("SOFTUART RX INIT DONE\r\n"); - } - - //add instance to array of instances - _Softuart_GPIO_Instances[s->pin_rx.gpio_id] = s; - _Softuart_Instances_Count++; - - //os_printf("SOFTUART INIT DONE\r\n"); + //init tx pin + if(!s->pin_tx.gpio_mux_name) { + os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); + } else { + //enable pin as gpio + PIN_FUNC_SELECT(s->pin_tx.gpio_mux_name, s->pin_tx.gpio_func); + + //set pullup (UART idle is VDD) + PIN_PULLUP_EN(s->pin_tx.gpio_mux_name); + + //set high for tx idle + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); + unsigned int delay = 100000; + os_delay_us(delay); + + os_printf("SOFTUART TX INIT DONE\r\n"); + } + + //init rx pin + if(!s->pin_rx.gpio_mux_name) { + os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); + } else { + //enable pin as gpio + PIN_FUNC_SELECT(s->pin_rx.gpio_mux_name, s->pin_rx.gpio_func); + + //set pullup (UART idle is VDD) + PIN_PULLUP_EN(s->pin_rx.gpio_mux_name); + + //set to input -> disable output + GPIO_DIS_OUTPUT(GPIO_ID_PIN(s->pin_rx.gpio_id)); + + //set interrupt related things + + //disable interrupts by GPIO + ETS_GPIO_INTR_DISABLE(); + + //attach interrupt handler and a pointer that will be passed around each time + ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, s); + + //not sure what this does... (quote from example): + // void gpio_register_set(uint32 reg_id, uint32 value); + // + // From include file + // Set the specified GPIO register to the specified value. + // This is a very general and powerful interface that is not + // expected to be used during normal operation. It is intended + // mainly for debug, or for unusual requirements. + // + // All people repeat this mantra but I don't know what it means + // + gpio_register_set(GPIO_PIN_ADDR(s->pin_rx.gpio_id), + GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) | + GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) | + GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); + + //clear interrupt handler status, basically writing a low to the output + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(s->pin_rx.gpio_id)); + + //enable interrupt for pin on any edge (rise and fall) + //@TODO: should work with ANYEDGE (=3), but complie error + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); + + //globally enable GPIO interrupts + ETS_GPIO_INTR_ENABLE(); + + os_printf("SOFTUART RX INIT DONE\r\n"); + } + + //add instance to array of instances + _Softuart_GPIO_Instances[s->pin_rx.gpio_id] = s; + _Softuart_Instances_Count++; + + os_printf("SOFTUART INIT DONE\r\n"); } void Softuart_Intr_Handler(void *p) { - uint8_t level, gpio_id; - // clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler - - //ignore void* pointer and make a new pointer - Softuart *s; + uint8_t level, gpio_id; +// clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler + //ignore void* pointer and make a new pointer + Softuart *s; - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); - gpio_id = Softuart_Bitcount(gpio_status); + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + gpio_id = Softuart_Bitcount(gpio_status); - //if interrupt was by an attached rx pin - if (gpio_id != 0xFF) - { - //load instance which has rx pin on interrupt pin attached - s = _Softuart_GPIO_Instances[gpio_id]; - - // disable interrupt for GPIO0 - gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), GPIO_PIN_INTR_DISABLE); - - // Do something, for example, increment whatyouwant indirectly - //check level - level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); - if(!level) + //if interrupt was by an attached rx pin + if (gpio_id != 0xFF) { - //pin is low - //therefore we have a start bit - - //wait till start bit is half over so we can sample the next one in the center - os_delay_us(s->bit_time/2); - - //now sample bits - unsigned i; - unsigned d = 0; - //mark the start of the data bit pattern - uint32_t start_time = system_get_time(); - for(i = 0; i < 8; i ++ ) - { - //delay for bit timing - // NOTE signed arithmetic handles system_get_time() wrap-around - // NOTE using repeated calls to `os_delay_us` can accumulate overhead errors - // see https://forum.micropython.org/viewtopic.php?f=16&t=2204&start=10#p16935 - int32_t end_time = (int32_t) (start_time + (i + 1)*s->bit_time); - while ( end_time - (int32_t) system_get_time() > 0){}; - - //shift d to the right - d >>= 1; - - //read bit - if(GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id))) { - //if high, set msb of 8bit to 1 - d |= 0x80; - } - } - - //store byte in buffer - // if buffer full, set the overflow flag and return - uint8 next = (s->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; - if (next != s->buffer.receive_buffer_head) - { - // save new data in buffer: tail points to where byte goes - s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; // save new byte - s->buffer.receive_buffer_tail = next; - } - else - { - s->buffer.buffer_overflow = 1; - } - - //wait for stop bit - os_delay_us(s->bit_time); - //done - } - //clear interrupt - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); - // Reactivate interrupts for GPIO0 - gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); - } else - { - //clear interrupt, no matter from which pin - //otherwise, this interrupt will be called again forever - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); - } + //load instance which has rx pin on interrupt pin attached + s = _Softuart_GPIO_Instances[gpio_id]; + +// disable interrupt for GPIO0 + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), GPIO_PIN_INTR_DISABLE); + +// Do something, for example, increment whatyouwant indirectly + //check level + level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); + if(!level) { + //pin is low + //therefore we have a start bit + + //wait till start bit is half over so we can sample the next one in the center + os_delay_us(s->bit_time/2); + + //now sample bits + unsigned i; + unsigned d = 0; + unsigned start_time = 0x7FFFFFFF & system_get_time(); + + for(i = 0; i < 8; i ++ ) + { + while ((0x7FFFFFFF & system_get_time()) < (start_time + (s->bit_time*(i+1)))) + { + //If system timer overflow, escape from while loop + if ((0x7FFFFFFF & system_get_time()) < start_time){break;} + } + //shift d to the right + d >>= 1; + + //read bit + if(GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id))) { + //if high, set msb of 8bit to 1 + d |= 0x80; + } + } + + //store byte in buffer + + // if buffer full, set the overflow flag and return + uint8 next = (s->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; + if (next != s->buffer.receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; // save new byte + s->buffer.receive_buffer_tail = next; + } + else + { + s->buffer.buffer_overflow = 1; + } + + //wait for stop bit + os_delay_us(s->bit_time); + + //done + } + + //clear interrupt + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); + +// Reactivate interrupts for GPIO0 + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); + } else { + //clear interrupt, no matter from which pin + //otherwise, this interrupt will be called again forever + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); + } } @@ -318,7 +292,7 @@ uint8_t Softuart_Read(Softuart *s) // Read from "head" uint8_t d = s->buffer.receive_buffer[s->buffer.receive_buffer_head]; // grab next byte - s->buffer.receive_buffer_head = (s->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; + s->buffer.receive_buffer_head = (s->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; return d; } @@ -332,11 +306,10 @@ uint32_t Softuart_Flush(Softuart *s) return num_chars; } - // Is data in buffer available? BOOL Softuart_Available(Softuart *s) { - return (s->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - s->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; + return (s->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - s->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; } // cversek: @@ -359,98 +332,97 @@ BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us) } } + static inline u8 chbit(u8 data, u8 bit) { if ((data & bit) != 0) { - return 1; + return 1; } else { - return 0; + return 0; } } // Function for printing individual characters void Softuart_Putchar(Softuart *s, char data) { - unsigned i; - - //if rs485 set tx enable - if(s->is_rs485 == 1) - { - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 1); - } - - //mark the start of the data bit pattern - uint32_t start_time = system_get_time(); - int32_t end_time; //calculated in loop - - //Start Bit - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 0); - for(i = 0; i <= 8; i ++ ) - { - //delay for bit timing - // NOTE signed arithmetic handles system_get_time() wrap-around - // NOTE using repeated calls to `os_delay_us` can accumulate overhead errors - // see https://forum.micropython.org/viewtopic.php?f=16&t=2204&start=10#p16935 - end_time = (int32_t) (start_time + (i + 1)*s->bit_time); - while ( end_time - (int32_t) system_get_time() > 0){}; - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), chbit(data,1<bit_time); - while ( end_time - (int32_t) system_get_time() > 0){}; - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); - - // Delay after byte, for new sync - os_delay_us(s->bit_time*6); - - //if rs485 set tx disable - if(s->is_rs485 == 1) - { - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 0); - } + unsigned i; + unsigned start_time = 0x7FFFFFFF & system_get_time(); + + //if rs485 set tx enable + if(s->is_rs485 == 1) + { + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 1); + } + + //Start Bit + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 0); + for(i = 0; i < 8; i ++ ) + { + while ((0x7FFFFFFF & system_get_time()) < (start_time + (s->bit_time*(i+1)))) + { + //If system timer overflow, escape from while loop + if ((0x7FFFFFFF & system_get_time()) < start_time){break;} + } + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), chbit(data,1<bit_time*9))) + { + //If system timer overflow, escape from while loop + if ((0x7FFFFFFF & system_get_time()) < start_time){break;} + } + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); + + // Delay after byte, for new sync + os_delay_us(s->bit_time*6); + + //if rs485 set tx disable + if(s->is_rs485 == 1) + { + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 0); + } } void Softuart_Puts(Softuart *s, const char *c ) { - while ( *c ) { - Softuart_Putchar(s,( u8 )*c++); - } + while ( *c ) { + Softuart_Putchar(s,( u8 )*c++); + } } uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ) { - uint8_t NextChar; - uint8_t len = 0; - - while( Softuart_Available(s) ) - { - NextChar = Softuart_Read(s); - - if(NextChar == '\r') - { - continue; - } else if(NextChar == '\n') - { - //break only if we already found a character - //if it was .e.g. only \r, we wait for the first useful character - if(len > 0) { - break; - } - } else if(len < MaxLen - 1 ) - { - *Buffer++ = NextChar; - len++; - } else { - break; - } - } - //add string terminator - *Buffer++ = '\0'; - - return len; + uint8_t NextChar; + uint8_t len = 0; + + while( Softuart_Available(s) ) + { + NextChar = Softuart_Read(s); + + if(NextChar == '\r') + { + continue; + } else if(NextChar == '\n') + { + //break only if we already found a character + //if it was .e.g. only \r, we wait for the first useful character + if(len > 0) { + break; + } + } else if(len < MaxLen - 1 ) + { + *Buffer++ = NextChar; + len++; + } else { + break; + } + } + //add string terminator + *Buffer++ = '\0'; + + return len; } - diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index 70003062a3a47..197d2188edee7 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -1,56 +1,34 @@ -/* -The MIT License (MIT) - -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. -*/ - #ifndef SOFTUART_H_ #define SOFTUART_H_ -#define SOFTUART_MAX_RX_BUFF 64 +#include "user_interface.h" + +#define SOFTUART_MAX_RX_BUFF 64 #define SOFTUART_GPIO_COUNT 16 typedef struct softuart_pin_t { - uint8_t gpio_id; - uint32_t gpio_mux_name; - uint8_t gpio_func; + uint8_t gpio_id; + uint32_t gpio_mux_name; + uint8_t gpio_func; } softuart_pin_t; typedef struct softuart_buffer_t { - char receive_buffer[SOFTUART_MAX_RX_BUFF]; - uint8_t receive_buffer_tail; - uint8_t receive_buffer_head; - uint8_t buffer_overflow; + 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 { - softuart_pin_t pin_rx; - softuart_pin_t pin_tx; - //optional rs485 tx enable pin (high -> tx enabled) - uint8_t pin_rs485_tx_enable; - //wether or not this softuart is rs485 and controlls rs485 tx enable pin - uint8_t is_rs485; - volatile softuart_buffer_t buffer; - uint16_t bit_time; + softuart_pin_t pin_rx; + softuart_pin_t pin_tx; + //optional rs485 tx enable pin (high -> tx enabled) + uint8_t pin_rs485_tx_enable; + //wether or not this softuart is rs485 and controlls rs485 tx enable pin + uint8_t is_rs485; + volatile softuart_buffer_t buffer; + uint16_t bit_time; } Softuart; @@ -69,8 +47,8 @@ uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ); //define mapping from pin to functio mode typedef struct { - uint32_t gpio_mux_name; - uint8_t gpio_func; + uint32_t gpio_mux_name; + uint8_t gpio_func; } softuart_reg_t; #endif /* SOFTUART_H_ */ From 2fdf6d946e9e773910b47beb419b4e1f72ea2e58 Mon Sep 17 00:00:00 2001 From: Damien Mascord Date: Wed, 17 Jul 2019 15:06:19 +1000 Subject: [PATCH 03/14] esp8266/softuart.c: Adapted performance improvements from juancgalvez. Link: https://github.com/juancgalvez/Arduino-esp8266-Software-UART. --- ports/esp8266/softuart.c | 94 +++++++++++++++++++++++++++------------- ports/esp8266/softuart.h | 1 + 2 files changed, 64 insertions(+), 31 deletions(-) diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index 35a3492a910a1..d5654d985e36b 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -115,8 +115,7 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) if(baudrate <= 0) { os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); } else { - s->bit_time = (1000000 / baudrate); - if ( ((100000000 / baudrate) - (100*s->bit_time)) > 50 ) s->bit_time++; + s->bit_time = system_get_cpu_freq() * 1000000 / baudrate; os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); } @@ -133,7 +132,7 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) //set high for tx idle GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); - unsigned int delay = 100000; + unsigned int delay = 100000; os_delay_us(delay); os_printf("SOFTUART TX INIT DONE\r\n"); @@ -196,14 +195,31 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) os_printf("SOFTUART INIT DONE\r\n"); } -void Softuart_Intr_Handler(void *p) +//*********************************** + +#define RSR_CCOUNT(r) __asm__ __volatile__("rsr %0,ccount":"=a" (r)) +static inline uint32_t get_ccount(void) { - uint8_t level, gpio_id; -// clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler + uint32_t ccount; + RSR_CCOUNT(ccount); + return ccount; +} +//*********************************** + +void Softuart_Intr_Handler(void *p) +{ + uint8_t level, gpio_id; + unsigned start_time = get_ccount(); + //ignore void* pointer and make a new pointer Softuart *s; + // disable all interrupts + ets_intr_lock(); + + // clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); gpio_id = Softuart_Bitcount(gpio_status); @@ -223,21 +239,23 @@ void Softuart_Intr_Handler(void *p) //pin is low //therefore we have a start bit - //wait till start bit is half over so we can sample the next one in the center - os_delay_us(s->bit_time/2); - //now sample bits unsigned i; unsigned d = 0; - unsigned start_time = 0x7FFFFFFF & system_get_time(); + unsigned elapsed_time = get_ccount() - start_time; + s->elapsed = elapsed_time; + + //wait till start bit is half over so we can sample the next one in the center + //os_delay_us(s->bit_time/2); + if (elapsed_time < s->bit_time / 2) { + unsigned wait_time = s->bit_time / 2 - elapsed_time; + while ((get_ccount() - start_time) < wait_time); + start_time += wait_time; + } for(i = 0; i < 8; i ++ ) { - while ((0x7FFFFFFF & system_get_time()) < (start_time + (s->bit_time*(i+1)))) - { - //If system timer overflow, escape from while loop - if ((0x7FFFFFFF & system_get_time()) < start_time){break;} - } + while ((get_ccount() - start_time) < s->bit_time); //shift d to the right d >>= 1; @@ -246,8 +264,14 @@ void Softuart_Intr_Handler(void *p) //if high, set msb of 8bit to 1 d |= 0x80; } + // recalculate start time for next bit + start_time += s->bit_time; } + //wait for stop bit + //os_delay_us(s->bit_time); + while ((get_ccount() - start_time) < s->bit_time); + //store byte in buffer // if buffer full, set the overflow flag and return @@ -261,10 +285,7 @@ void Softuart_Intr_Handler(void *p) else { s->buffer.buffer_overflow = 1; - } - - //wait for stop bit - os_delay_us(s->bit_time); + } //done } @@ -279,6 +300,10 @@ void Softuart_Intr_Handler(void *p) //otherwise, this interrupt will be called again forever GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); } + // re-enable all interrupts + + ets_intr_unlock(); + } @@ -349,8 +374,16 @@ static inline u8 chbit(u8 data, u8 bit) void Softuart_Putchar(Softuart *s, char data) { unsigned i; - unsigned start_time = 0x7FFFFFFF & system_get_time(); + unsigned start_time; + // is this needed? delay(0); + + start_time = get_ccount(); + + // disable all interrupts + + ets_intr_lock(); + //if rs485 set tx enable if(s->is_rs485 == 1) { @@ -361,30 +394,29 @@ void Softuart_Putchar(Softuart *s, char data) GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 0); for(i = 0; i < 8; i ++ ) { - while ((0x7FFFFFFF & system_get_time()) < (start_time + (s->bit_time*(i+1)))) - { - //If system timer overflow, escape from while loop - if ((0x7FFFFFFF & system_get_time()) < start_time){break;} - } + while ((get_ccount() - start_time) < s->bit_time); GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), chbit(data,1<bit_time; } // Stop bit - while ((0x7FFFFFFF & system_get_time()) < (start_time + (s->bit_time*9))) - { - //If system timer overflow, escape from while loop - if ((0x7FFFFFFF & system_get_time()) < start_time){break;} - } + while ((get_ccount() - start_time) < s->bit_time); GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); // Delay after byte, for new sync - os_delay_us(s->bit_time*6); + os_delay_us(s->bit_time*6/system_get_cpu_freq()); //if rs485 set tx disable if(s->is_rs485 == 1) { GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 0); } + + // re-enable all interrupts + + ets_intr_unlock(); } void Softuart_Puts(Softuart *s, const char *c ) diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index 197d2188edee7..469293640a513 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -29,6 +29,7 @@ typedef struct { uint8_t is_rs485; volatile softuart_buffer_t buffer; uint16_t bit_time; + uint32_t elapsed; } Softuart; From 98eb666d3526617c7782702ad7a038c9c0f3fb9d Mon Sep 17 00:00:00 2001 From: Damien Mascord Date: Thu, 15 Aug 2019 14:07:28 +1000 Subject: [PATCH 04/14] ports/esp8266: Added implementation for ioctl. select.select now works. --- ports/esp8266/machine_softuart.c | 17 +++++++++++++++-- ports/esp8266/softuart.c | 12 +++++++++++- ports/esp8266/softuart.h | 3 +++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/ports/esp8266/machine_softuart.c b/ports/esp8266/machine_softuart.c index 457c2d8982e78..65f93734a2999 100644 --- a/ports/esp8266/machine_softuart.c +++ b/ports/esp8266/machine_softuart.c @@ -223,8 +223,21 @@ STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uin } STATIC mp_uint_t pyb_softuart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { - *errcode = MP_EINVAL; - return MP_STREAM_ERROR; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_STREAM_POLL_RD) && Softuart_rx_any(&softuartDevice)) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && Softuart_tx_any_room(&softuartDevice)) { + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; } STATIC const mp_stream_p_t softuart_stream_p = { diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index d5654d985e36b..b0574a2f60a9d 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -334,9 +334,19 @@ uint32_t Softuart_Flush(Softuart *s) // Is data in buffer available? BOOL Softuart_Available(Softuart *s) { - return (s->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - s->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; + return (s->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - s->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; } +int Softuart_rx_any(Softuart *s) { + return (Softuart_Available(s)); +} + +int Softuart_tx_any_room(Softuart *s) { + // TODO always assume we have room... there is no send buffer + return true; +} + + // cversek: // based on micropython/esp8266/uart.c bool uart_rx_wait(uint32_t timeout_us) // Waits at most timeout microseconds for at least 1 char to become ready for reading. diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index 469293640a513..bd0ebdc545b68 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -45,6 +45,9 @@ void Softuart_Putchar(Softuart *s, char data); void Softuart_Puts(Softuart *s, const char *c ); uint8_t Softuart_Read(Softuart *s); uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ); +// check status of rx/tx +int Softuart_rx_any(Softuart *s); +int Softuart_tx_any_room(Softuart *s); //define mapping from pin to functio mode typedef struct { From 6961d220f16a820071e7e5c706aca6d00044f6fb Mon Sep 17 00:00:00 2001 From: Damien Mascord Date: Fri, 16 Aug 2019 13:15:06 +1000 Subject: [PATCH 05/14] ports/esp8266: Added MP_FASTCODE macro. Ensuring that timing is correct (to allow for 115200 baud). --- ports/esp8266/machine_softuart.c | 2 +- ports/esp8266/softuart.c | 6 +++--- ports/esp8266/softuart.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/esp8266/machine_softuart.c b/ports/esp8266/machine_softuart.c index 65f93734a2999..4e7abc070a1fb 100644 --- a/ports/esp8266/machine_softuart.c +++ b/ports/esp8266/machine_softuart.c @@ -174,7 +174,7 @@ STATIC const mp_rom_map_elem_t pyb_softuart_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(pyb_softuart_locals_dict, pyb_softuart_locals_dict_table); -STATIC mp_uint_t pyb_softuart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); // make sure we want at least 1 char diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index b0574a2f60a9d..289fbb7671e0c 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -198,7 +198,7 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) //*********************************** #define RSR_CCOUNT(r) __asm__ __volatile__("rsr %0,ccount":"=a" (r)) -static inline uint32_t get_ccount(void) +static inline uint32_t MP_FASTCODE(get_ccount)(void) { uint32_t ccount; RSR_CCOUNT(ccount); @@ -207,7 +207,7 @@ static inline uint32_t get_ccount(void) //*********************************** -void Softuart_Intr_Handler(void *p) +void MP_FASTCODE(Softuart_Intr_Handler)(void *p) { uint8_t level, gpio_id; unsigned start_time = get_ccount(); @@ -381,7 +381,7 @@ static inline u8 chbit(u8 data, u8 bit) } // Function for printing individual characters -void Softuart_Putchar(Softuart *s, char data) +void MP_FASTCODE(Softuart_Putchar)(Softuart *s, char data) { unsigned i; unsigned start_time; diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index bd0ebdc545b68..51d4392cd792b 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -3,7 +3,7 @@ #include "user_interface.h" -#define SOFTUART_MAX_RX_BUFF 64 +#define SOFTUART_MAX_RX_BUFF 256 #define SOFTUART_GPIO_COUNT 16 From 4be45ad5d38ca43fc902ed89fc9e5c3931fb0b7c Mon Sep 17 00:00:00 2001 From: Damien Mascord Date: Tue, 20 Aug 2019 17:49:34 +1000 Subject: [PATCH 06/14] esp8266/softuart.h: Changed bit rate to be a 32bit number. Allows to work with 1200 baud or lower. --- ports/esp8266/softuart.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index 51d4392cd792b..90cfd836a600f 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -28,8 +28,8 @@ typedef struct { //wether or not this softuart is rs485 and controlls rs485 tx enable pin uint8_t is_rs485; volatile softuart_buffer_t buffer; - uint16_t bit_time; - uint32_t elapsed; + uint32_t bit_time; + uint32_t elapsed; } Softuart; From 924c60dedea27e1bdefa88af870c904762b49577 Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Mon, 13 Sep 2021 15:54:41 +0200 Subject: [PATCH 07/14] ports/esp8266: Fixed code formatting. Used provided tools/codeformat.py. --- ports/esp8266/espmissingincludes.h | 46 +- ports/esp8266/machine_softuart.c | 54 +-- ports/esp8266/softuart.c | 694 ++++++++++++++--------------- ports/esp8266/softuart.h | 46 +- 4 files changed, 412 insertions(+), 428 deletions(-) diff --git a/ports/esp8266/espmissingincludes.h b/ports/esp8266/espmissingincludes.h index 518f138d827fc..6cadace90fc3b 100644 --- a/ports/esp8266/espmissingincludes.h +++ b/ports/esp8266/espmissingincludes.h @@ -9,65 +9,65 @@ int strcasecmp(const char *a, const char *b); #ifndef FREERTOS #include #include -//Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. -//MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler. +// Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. +// MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler. typedef struct espconn espconn; int atoi(const char *nptr); -//void ets_install_putc1(void *routine); +// void ets_install_putc1(void *routine); void ets_isr_mask(unsigned intr); void ets_isr_unmask(unsigned intr); int ets_memcmp(const void *s1, const void *s2, size_t n); void *ets_memcpy(void *dest, const void *src, size_t n); void *ets_memset(void *s, int c, size_t n); -int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format(printf, 2, 3))); int ets_str2macaddr(void *, void *); int ets_strcmp(const char *s1, const char *s2); char *ets_strcpy(char *dest, const char *src); -//size_t ets_strlen(const char *s); -//int ets_strncmp(const char *s1, const char *s2, int len); +// size_t ets_strlen(const char *s); +// int ets_strncmp(const char *s1, const char *s2, int len); char *ets_strncpy(char *dest, const char *src, size_t n); char *ets_strstr(const char *haystack, const char *needle); void ets_timer_disarm(os_timer_t *a); void ets_timer_setfn(os_timer_t *t, ETSTimerFunc *fn, void *parg); void ets_update_cpu_frequency(int freqmhz); void *os_memmove(void *dest, const void *src, size_t n); -int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4))); -int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -//void uart_div_modify(int no, unsigned int freq); +int os_printf(const char *format, ...) __attribute__ ((format(printf, 1, 2))); +int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format(printf, 3, 4))); +int os_printf_plus(const char *format, ...) __attribute__ ((format(printf, 1, 2))); +// void uart_div_modify(int no, unsigned int freq); uint8 wifi_get_opmode(void); uint32 system_get_time(); int rand(void); void ets_bzero(void *s, size_t n); -//void ets_delay_us(int ms); +// void ets_delay_us(int ms); -//Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself -//has no meaning here. +// Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself +// has no meaning here. #ifndef RC_LIMIT_P2P_11N -//Defs for SDK <1.4.0 +// Defs for SDK <1.4.0 void *pvPortMalloc(size_t xWantedSize); void *pvPortZalloc(size_t); void vPortFree(void *ptr); void *vPortMalloc(size_t xWantedSize); void pvPortFree(void *ptr); #else -//void *pvPortMalloc(size_t xWantedSize, const char *file, int line); -//void *pvPortZalloc(size_t, const char *file, int line); -//void vPortFree(void *ptr, const char *file, int line); +// void *pvPortMalloc(size_t xWantedSize, const char *file, int line); +// void *pvPortZalloc(size_t, const char *file, int line); +// void vPortFree(void *ptr, const char *file, int line); void *vPortMalloc(size_t xWantedSize, const char *file, int line); void pvPortFree(void *ptr, const char *file, int line); #endif -//Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one. +// Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one. #ifdef PIN_FUNC_SELECT #undef PIN_FUNC_SELECT #define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ - WRITE_PERI_REG(PIN_NAME, \ - (READ_PERI_REG(PIN_NAME) \ - & (~(PERIPHS_IO_MUX_FUNC<tx = mp_obj_get_pin_obj(args[ARG_tx].u_obj); Softuart_SetPinTx(&softuartDevice, mp_obj_get_pin(self->tx)); self->rx = mp_obj_get_pin_obj(args[ARG_rx].u_obj); @@ -97,19 +97,19 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co if (args[ARG_baudrate].u_int > 0) { self->baudrate = args[ARG_baudrate].u_int; Softuart_Init(&softuartDevice, self->baudrate); - //UartDev.baut_rate = self->baudrate; // Sic! + // UartDev.baut_rate = self->baudrate; // Sic! } // set data bits - self->bits = 8; //no other options are supported - + self->bits = 8; // no other options are supported + // set parity - self->parity = 0; //"NONE" no other options are supported + self->parity = 0; // "NONE" no other options are supported // set stop bits - self->stop = 1; //"NONE" no other options are supported - + self->stop = 1; // "NONE" no other options are supported + // set timeout self->timeout = args[ARG_timeout].u_int; @@ -119,10 +119,10 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co uint32_t min_timeout_char = 13000 / self->baudrate + 1; if (self->timeout_char < min_timeout_char) { self->timeout_char = min_timeout_char; -} + } // setup - //FIXME //uart_setup(self->uart_id); + // FIXME //uart_setup(self->uart_id); } STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -131,8 +131,8 @@ STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, // create instance pyb_softuart_obj_t *self = m_new_obj(pyb_softuart_obj_t); self->base.type = &pyb_softuart_type; - //FIXME removed //self->uart_id = uart_id; - //self->softuart_ptr = os_malloc(sizeof(Softuart)); + // FIXME removed //self->uart_id = uart_id; + // self->softuart_ptr = os_malloc(sizeof(Softuart)); self->baudrate = 9600; self->bits = 8; self->parity = 0; @@ -155,8 +155,8 @@ STATIC mp_obj_t pyb_softuart_init(size_t n_args, const mp_obj_t *args, mp_map_t MP_DEFINE_CONST_FUN_OBJ_KW(pyb_softuart_init_obj, 1, pyb_softuart_init); STATIC mp_obj_t pyb_softuart_flush(mp_obj_t self_in) { - //pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); - Softuart_Flush(&softuartDevice); //reset the rx buffer to empty + // pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + Softuart_Flush(&softuartDevice); // reset the rx buffer to empty return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(pyb_softuart_flush_obj, pyb_softuart_flush); @@ -169,7 +169,7 @@ STATIC const mp_rom_map_elem_t pyb_softuart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, - + }; STATIC MP_DEFINE_CONST_DICT(pyb_softuart_locals_dict, pyb_softuart_locals_dict_table); @@ -194,14 +194,14 @@ STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, *buf++ = Softuart_Read(&softuartDevice); if (--size == 0 || !Softuart_rxWait(&softuartDevice, self->timeout_char * 1000)) { // return number of bytes read - return buf - (uint8_t*)buf_in; + return buf - (uint8_t *)buf_in; } } - return 0; //FIXME + return 0; // FIXME } STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { - //pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + // pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); const byte *buf = buf_in; /* TODO implement non-blocking @@ -215,7 +215,7 @@ STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uin // write the data for (size_t i = 0; i < size; ++i) { Softuart_Putchar(&softuartDevice, *buf++); - //FIXME //uart_tx_one_char(self->uart_id, *buf++); + // FIXME //uart_tx_one_char(self->uart_id, *buf++); } // return number of bytes written @@ -255,5 +255,5 @@ const mp_obj_type_t pyb_softuart_type = { .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, .protocol = &softuart_stream_p, - .locals_dict = (mp_obj_dict_t*)&pyb_softuart_locals_dict, + .locals_dict = (mp_obj_dict_t *)&pyb_softuart_locals_dict, }; diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index 289fbb7671e0c..ac9ed27a6a7d6 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -8,196 +8,189 @@ #include "espmissingincludes.h" #include "softuart.h" -//array of pointers to instances +// array of pointers to instances Softuart *_Softuart_GPIO_Instances[SOFTUART_GPIO_COUNT]; uint8_t _Softuart_Instances_Count = 0; -//intialize list of gpio names and functions +// intialize list of gpio names and functions softuart_reg_t softuart_reg[] = { - { PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0 }, //gpio0 - { PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1 }, //gpio1 (uart) - { PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2 }, //gpio2 - { PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3 }, //gpio3 (uart) - { PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 }, //gpio4 - { PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 }, //gpio5 - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12 }, //gpio12 - { PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13 }, //gpio13 - { PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14 }, //gpio14 - { PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15 }, //gpio15 - //@TODO TODO gpio16 is missing (?include) + { PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0 }, // gpio0 + { PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1 }, // gpio1 (uart) + { PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2 }, // gpio2 + { PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3 }, // gpio3 (uart) + { PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4 }, // gpio4 + { PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5 }, // gpio5 + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12 }, // gpio12 + { PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13 }, // gpio13 + { PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14 }, // gpio14 + { PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15 }, // gpio15 + // @TODO TODO gpio16 is missing (?include) }; -uint8_t Softuart_Bitcount(uint32_t x) -{ - uint8_t count; - - for (count=0; x != 0; x>>=1) { - if ( x & 0x01) { - return count; - } - count++; - } - //error: no 1 found! - return 0xFF; +uint8_t Softuart_Bitcount(uint32_t x) { + uint8_t count; + + for (count = 0; x != 0; x >>= 1) { + if (x & 0x01) { + return count; + } + count++; + } + // error: no 1 found! + return 0xFF; } -uint8_t Softuart_IsGpioValid(uint8_t gpio_id) -{ - if ((gpio_id > 5 && gpio_id < 12) || gpio_id > 15) - { - return 0; - } - return 1; +uint8_t Softuart_IsGpioValid(uint8_t gpio_id) { + if ((gpio_id > 5 && gpio_id < 12) || gpio_id > 15) { + return 0; + } + return 1; } -void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id) -{ - if(! Softuart_IsGpioValid(gpio_id)) { - os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); - } else { - s->pin_rx.gpio_id = gpio_id; - s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; - s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; - } +void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id) { + if (!Softuart_IsGpioValid(gpio_id)) { + os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); + } else { + s->pin_rx.gpio_id = gpio_id; + s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; + } } -void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id) -{ - if(! Softuart_IsGpioValid(gpio_id)) { - os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); - } else { - s->pin_tx.gpio_id = gpio_id; - s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; - s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; - } +void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id) { + if (!Softuart_IsGpioValid(gpio_id)) { + os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); + } else { + s->pin_tx.gpio_id = gpio_id; + s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; + } } -void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id) -{ - os_printf("SOFTUART RS485 init\r\n"); +void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id) { + os_printf("SOFTUART RS485 init\r\n"); + + // enable rs485 + s->is_rs485 = 1; - //enable rs485 - s->is_rs485 = 1; + // set pin in instance + s->pin_rs485_tx_enable = gpio_id; - //set pin in instance - s->pin_rs485_tx_enable = gpio_id; + // enable pin as gpio + PIN_FUNC_SELECT(softuart_reg[gpio_id].gpio_mux_name,softuart_reg[gpio_id].gpio_func); - //enable pin as gpio - PIN_FUNC_SELECT(softuart_reg[gpio_id].gpio_mux_name,softuart_reg[gpio_id].gpio_func); + PIN_PULLUP_DIS(softuart_reg[gpio_id].gpio_mux_name); - PIN_PULLUP_DIS(softuart_reg[gpio_id].gpio_mux_name); - - //set low for tx idle (so other bus participants can send) - GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_id), 0); - - os_printf("SOFTUART RS485 init done\r\n"); + // set low for tx idle (so other bus participants can send) + GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_id), 0); + + os_printf("SOFTUART RS485 init done\r\n"); } -void Softuart_Init(Softuart *s, uint32_t baudrate) -{ - //disable rs485 - s->is_rs485 = 0; - - if(! _Softuart_Instances_Count) { - os_printf("SOFTUART initialize gpio\r\n"); - //Initilaize gpio subsystem - gpio_init(); - } - - //set bit time - if(baudrate <= 0) { - os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); - } else { - s->bit_time = system_get_cpu_freq() * 1000000 / baudrate; - os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); - } +void Softuart_Init(Softuart *s, uint32_t baudrate) { + // disable rs485 + s->is_rs485 = 0; + + if (!_Softuart_Instances_Count) { + os_printf("SOFTUART initialize gpio\r\n"); + // Initilaize gpio subsystem + gpio_init(); + } + + // set bit time + if (baudrate <= 0) { + os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); + } else { + s->bit_time = system_get_cpu_freq() * 1000000 / baudrate; + os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); + } + + + // init tx pin + if (!s->pin_tx.gpio_mux_name) { + os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); + } else { + // enable pin as gpio + PIN_FUNC_SELECT(s->pin_tx.gpio_mux_name, s->pin_tx.gpio_func); + // set pullup (UART idle is VDD) + PIN_PULLUP_EN(s->pin_tx.gpio_mux_name); - //init tx pin - if(!s->pin_tx.gpio_mux_name) { - os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); - } else { - //enable pin as gpio - PIN_FUNC_SELECT(s->pin_tx.gpio_mux_name, s->pin_tx.gpio_func); - - //set pullup (UART idle is VDD) - PIN_PULLUP_EN(s->pin_tx.gpio_mux_name); - - //set high for tx idle - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); - unsigned int delay = 100000; - os_delay_us(delay); - - os_printf("SOFTUART TX INIT DONE\r\n"); - } - - //init rx pin - if(!s->pin_rx.gpio_mux_name) { - os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); - } else { - //enable pin as gpio - PIN_FUNC_SELECT(s->pin_rx.gpio_mux_name, s->pin_rx.gpio_func); - - //set pullup (UART idle is VDD) - PIN_PULLUP_EN(s->pin_rx.gpio_mux_name); - - //set to input -> disable output - GPIO_DIS_OUTPUT(GPIO_ID_PIN(s->pin_rx.gpio_id)); - - //set interrupt related things - - //disable interrupts by GPIO - ETS_GPIO_INTR_DISABLE(); - - //attach interrupt handler and a pointer that will be passed around each time - ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, s); - - //not sure what this does... (quote from example): - // void gpio_register_set(uint32 reg_id, uint32 value); - // - // From include file - // Set the specified GPIO register to the specified value. - // This is a very general and powerful interface that is not - // expected to be used during normal operation. It is intended - // mainly for debug, or for unusual requirements. - // - // All people repeat this mantra but I don't know what it means - // - gpio_register_set(GPIO_PIN_ADDR(s->pin_rx.gpio_id), - GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) | - GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) | - GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); - - //clear interrupt handler status, basically writing a low to the output - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(s->pin_rx.gpio_id)); - - //enable interrupt for pin on any edge (rise and fall) - //@TODO: should work with ANYEDGE (=3), but complie error - gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); - - //globally enable GPIO interrupts - ETS_GPIO_INTR_ENABLE(); - - os_printf("SOFTUART RX INIT DONE\r\n"); - } - - //add instance to array of instances - _Softuart_GPIO_Instances[s->pin_rx.gpio_id] = s; - _Softuart_Instances_Count++; - - os_printf("SOFTUART INIT DONE\r\n"); + // set high for tx idle + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); + unsigned int delay = 100000; + os_delay_us(delay); + + os_printf("SOFTUART TX INIT DONE\r\n"); + } + + // init rx pin + if (!s->pin_rx.gpio_mux_name) { + os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); + } else { + // enable pin as gpio + PIN_FUNC_SELECT(s->pin_rx.gpio_mux_name, s->pin_rx.gpio_func); + + // set pullup (UART idle is VDD) + PIN_PULLUP_EN(s->pin_rx.gpio_mux_name); + + // set to input -> disable output + GPIO_DIS_OUTPUT(GPIO_ID_PIN(s->pin_rx.gpio_id)); + + // set interrupt related things + + // disable interrupts by GPIO + ETS_GPIO_INTR_DISABLE(); + + // attach interrupt handler and a pointer that will be passed around each time + ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, s); + + // not sure what this does... (quote from example): + // void gpio_register_set(uint32 reg_id, uint32 value); + // + // From include file + // Set the specified GPIO register to the specified value. + // This is a very general and powerful interface that is not + // expected to be used during normal operation. It is intended + // mainly for debug, or for unusual requirements. + // + // All people repeat this mantra but I don't know what it means + // + gpio_register_set(GPIO_PIN_ADDR(s->pin_rx.gpio_id), + GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE) | + GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE) | + GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE)); + + // clear interrupt handler status, basically writing a low to the output + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(s->pin_rx.gpio_id)); + + // enable interrupt for pin on any edge (rise and fall) + // @TODO: should work with ANYEDGE (=3), but complie error + gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); + + // globally enable GPIO interrupts + ETS_GPIO_INTR_ENABLE(); + + os_printf("SOFTUART RX INIT DONE\r\n"); + } + + // add instance to array of instances + _Softuart_GPIO_Instances[s->pin_rx.gpio_id] = s; + _Softuart_Instances_Count++; + + os_printf("SOFTUART INIT DONE\r\n"); } -//*********************************** +// *********************************** -#define RSR_CCOUNT(r) __asm__ __volatile__("rsr %0,ccount":"=a" (r)) +#define RSR_CCOUNT(r) __asm__ __volatile__ ("rsr %0,ccount" : "=a" (r)) static inline uint32_t MP_FASTCODE(get_ccount)(void) { uint32_t ccount; @@ -205,14 +198,14 @@ static inline uint32_t MP_FASTCODE(get_ccount)(void) return ccount; } -//*********************************** +// *********************************** void MP_FASTCODE(Softuart_Intr_Handler)(void *p) { uint8_t level, gpio_id; unsigned start_time = get_ccount(); - - //ignore void* pointer and make a new pointer + + // ignore void* pointer and make a new pointer Softuart *s; // disable all interrupts @@ -221,124 +214,124 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) // clear gpio status. Say ESP8266EX SDK Programming Guide in 5.1.6. GPIO interrupt handler uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); - gpio_id = Softuart_Bitcount(gpio_status); + gpio_id = Softuart_Bitcount(gpio_status); - //if interrupt was by an attached rx pin - if (gpio_id != 0xFF) - { - //load instance which has rx pin on interrupt pin attached - s = _Softuart_GPIO_Instances[gpio_id]; + // if interrupt was by an attached rx pin + if (gpio_id != 0xFF) { + // load instance which has rx pin on interrupt pin attached + s = _Softuart_GPIO_Instances[gpio_id]; // disable interrupt for GPIO0 gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), GPIO_PIN_INTR_DISABLE); // Do something, for example, increment whatyouwant indirectly - //check level - level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); - if(!level) { - //pin is low - //therefore we have a start bit - - //now sample bits - unsigned i; - unsigned d = 0; - unsigned elapsed_time = get_ccount() - start_time; - s->elapsed = elapsed_time; - - //wait till start bit is half over so we can sample the next one in the center - //os_delay_us(s->bit_time/2); - if (elapsed_time < s->bit_time / 2) { - unsigned wait_time = s->bit_time / 2 - elapsed_time; - while ((get_ccount() - start_time) < wait_time); - start_time += wait_time; - } - - for(i = 0; i < 8; i ++ ) - { - while ((get_ccount() - start_time) < s->bit_time); - //shift d to the right - d >>= 1; - - //read bit - if(GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id))) { - //if high, set msb of 8bit to 1 - d |= 0x80; - } - // recalculate start time for next bit - start_time += s->bit_time; - } - - //wait for stop bit - //os_delay_us(s->bit_time); - while ((get_ccount() - start_time) < s->bit_time); - - //store byte in buffer - - // if buffer full, set the overflow flag and return - uint8 next = (s->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; - if (next != s->buffer.receive_buffer_head) - { - // save new data in buffer: tail points to where byte goes - s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; // save new byte - s->buffer.receive_buffer_tail = next; - } - else - { - s->buffer.buffer_overflow = 1; - } - - //done - } - - //clear interrupt + // check level + level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); + if (!level) { + // pin is low + // therefore we have a start bit + + // now sample bits + unsigned i; + unsigned d = 0; + unsigned elapsed_time = get_ccount() - start_time; + s->elapsed = elapsed_time; + + // wait till start bit is half over so we can sample the next one in the center + // os_delay_us(s->bit_time/2); + if (elapsed_time < s->bit_time / 2) { + unsigned wait_time = s->bit_time / 2 - elapsed_time; + while ((get_ccount() - start_time) < wait_time) { + ; + } + start_time += wait_time; + } + + for (i = 0; i < 8; i++) + { + while ((get_ccount() - start_time) < s->bit_time) { + ; + } + // shift d to the right + d >>= 1; + + // read bit + if (GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id))) { + // if high, set msb of 8bit to 1 + d |= 0x80; + } + // recalculate start time for next bit + start_time += s->bit_time; + } + + // wait for stop bit + // os_delay_us(s->bit_time); + while ((get_ccount() - start_time) < s->bit_time) { + ; + } + + // store byte in buffer + + // if buffer full, set the overflow flag and return + uint8 next = (s->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; + if (next != s->buffer.receive_buffer_head) { + // save new data in buffer: tail points to where byte goes + s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; // save new byte + s->buffer.receive_buffer_tail = next; + } else { + s->buffer.buffer_overflow = 1; + } + + // done + } + + // clear interrupt GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); // Reactivate interrupts for GPIO0 gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); - } else { - //clear interrupt, no matter from which pin - //otherwise, this interrupt will be called again forever + } else { + // clear interrupt, no matter from which pin + // otherwise, this interrupt will be called again forever GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); - } - // re-enable all interrupts - - ets_intr_unlock(); - + } + // re-enable all interrupts + + ets_intr_unlock(); + } // Read data from buffer -uint8_t Softuart_Read(Softuart *s) -{ - // Empty buffer? - if (s->buffer.receive_buffer_head == s->buffer.receive_buffer_tail) - return 0; - - // Read from "head" - uint8_t d = s->buffer.receive_buffer[s->buffer.receive_buffer_head]; // grab next byte - s->buffer.receive_buffer_head = (s->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; - return d; +uint8_t Softuart_Read(Softuart *s) { + // Empty buffer? + if (s->buffer.receive_buffer_head == s->buffer.receive_buffer_tail) { + return 0; + } + + // Read from "head" + uint8_t d = s->buffer.receive_buffer[s->buffer.receive_buffer_head]; // grab next byte + s->buffer.receive_buffer_head = (s->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; + return d; } // Flush data from buffer -uint32_t Softuart_Flush(Softuart *s) -{ - uint32_t num_chars = s->buffer.receive_buffer_tail - s->buffer.receive_buffer_head; - // Empty buffer - s->buffer.receive_buffer_head = 0; - s->buffer.receive_buffer_tail = 0; - return num_chars; +uint32_t Softuart_Flush(Softuart *s) { + uint32_t num_chars = s->buffer.receive_buffer_tail - s->buffer.receive_buffer_head; + // Empty buffer + s->buffer.receive_buffer_head = 0; + s->buffer.receive_buffer_tail = 0; + return num_chars; } // Is data in buffer available? -BOOL Softuart_Available(Softuart *s) -{ +BOOL Softuart_Available(Softuart *s) { return (s->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - s->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF; } int Softuart_rx_any(Softuart *s) { - return (Softuart_Available(s)); + return Softuart_Available(s); } int Softuart_tx_any_room(Softuart *s) { @@ -351,16 +344,15 @@ int Softuart_tx_any_room(Softuart *s) { // based on micropython/esp8266/uart.c bool uart_rx_wait(uint32_t timeout_us) // Waits at most timeout microseconds for at least 1 char to become ready for reading. // Returns true if something available, false if not. -BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us) -{ - //handles system_get_time() wrap-around - int32_t when_timedout = (int32_t) system_get_time() + timeout_us; +BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us) { + // handles system_get_time() wrap-around + int32_t when_timedout = (int32_t)system_get_time() + timeout_us; for (;;) { if (Softuart_Available(s)) { return true; // have at least 1 char ready for reading } - //handles system_get_time()-wrap around - if (when_timedout - (int32_t) system_get_time() <= 0) { + // handles system_get_time()-wrap around + if (when_timedout - (int32_t)system_get_time() <= 0) { return false; // timeout } ets_event_poll(); @@ -368,103 +360,95 @@ BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us) } -static inline u8 chbit(u8 data, u8 bit) -{ - if ((data & bit) != 0) - { - return 1; - } - else - { - return 0; +static inline u8 chbit(u8 data, u8 bit) { + if ((data & bit) != 0) { + return 1; + } else { + return 0; } } // Function for printing individual characters -void MP_FASTCODE(Softuart_Putchar)(Softuart *s, char data) +void MP_FASTCODE(Softuart_Putchar)(Softuart * s, char data) { - unsigned i; - unsigned start_time; - - // is this needed? delay(0); - - start_time = get_ccount(); - - // disable all interrupts - - ets_intr_lock(); - - //if rs485 set tx enable - if(s->is_rs485 == 1) - { - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 1); - } - - //Start Bit - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 0); - for(i = 0; i < 8; i ++ ) - { - while ((get_ccount() - start_time) < s->bit_time); - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), chbit(data,1<bit_time; - } - - // Stop bit - while ((get_ccount() - start_time) < s->bit_time); - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); - - // Delay after byte, for new sync - os_delay_us(s->bit_time*6/system_get_cpu_freq()); - - //if rs485 set tx disable - if(s->is_rs485 == 1) - { - GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 0); - } - - // re-enable all interrupts - - ets_intr_unlock(); + unsigned i; + unsigned start_time; + + // is this needed? delay(0); + + start_time = get_ccount(); + + // disable all interrupts + + ets_intr_lock(); + + // if rs485 set tx enable + if (s->is_rs485 == 1) { + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 1); + } + + // Start Bit + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 0); + for (i = 0; i < 8; i++) + { + while ((get_ccount() - start_time) < s->bit_time) { + ; + } + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), chbit(data,1 << i)); + + // recalculate start time for next bit + start_time += s->bit_time; + } + + // Stop bit + while ((get_ccount() - start_time) < s->bit_time) { + ; + } + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); + + // Delay after byte, for new sync + os_delay_us(s->bit_time * 6 / system_get_cpu_freq()); + + // if rs485 set tx disable + if (s->is_rs485 == 1) { + GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_rs485_tx_enable), 0); + } + + // re-enable all interrupts + + ets_intr_unlock(); } -void Softuart_Puts(Softuart *s, const char *c ) -{ - while ( *c ) { - Softuart_Putchar(s,( u8 )*c++); - } +void Softuart_Puts(Softuart *s, const char *c) { + while (*c) { + Softuart_Putchar(s,(u8) * c++); + } } -uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ) -{ - uint8_t NextChar; - uint8_t len = 0; - - while( Softuart_Available(s) ) - { - NextChar = Softuart_Read(s); - - if(NextChar == '\r') - { - continue; - } else if(NextChar == '\n') - { - //break only if we already found a character - //if it was .e.g. only \r, we wait for the first useful character - if(len > 0) { - break; - } - } else if(len < MaxLen - 1 ) - { - *Buffer++ = NextChar; - len++; - } else { - break; - } - } - //add string terminator - *Buffer++ = '\0'; - - return len; +uint8_t Softuart_Readline(Softuart *s, char *Buffer, uint8_t MaxLen) { + uint8_t NextChar; + uint8_t len = 0; + + while (Softuart_Available(s)) { + NextChar = Softuart_Read(s); + + if (NextChar == '\r') { + continue; + } else if (NextChar == '\n') { + // break only if we already found a character + // if it was .e.g. only \r, we wait for the first useful character + if (len > 0) { + break; + } + } else if (len < MaxLen - 1) { + *Buffer++ = NextChar; + len++; + } else { + break; + } + } + // add string terminator + *Buffer++ = '\0'; + + return len; } diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index 90cfd836a600f..ca691b96ff140 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -3,56 +3,56 @@ #include "user_interface.h" -#define SOFTUART_MAX_RX_BUFF 256 +#define SOFTUART_MAX_RX_BUFF 256 #define SOFTUART_GPIO_COUNT 16 typedef struct softuart_pin_t { - uint8_t gpio_id; - uint32_t gpio_mux_name; - uint8_t gpio_func; + uint8_t gpio_id; + uint32_t gpio_mux_name; + uint8_t gpio_func; } softuart_pin_t; typedef struct softuart_buffer_t { - char receive_buffer[SOFTUART_MAX_RX_BUFF]; - uint8_t receive_buffer_tail; - uint8_t receive_buffer_head; - uint8_t buffer_overflow; + 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 { - softuart_pin_t pin_rx; - softuart_pin_t pin_tx; - //optional rs485 tx enable pin (high -> tx enabled) - uint8_t pin_rs485_tx_enable; - //wether or not this softuart is rs485 and controlls rs485 tx enable pin - uint8_t is_rs485; - volatile softuart_buffer_t buffer; - uint32_t bit_time; - uint32_t elapsed; + softuart_pin_t pin_rx; + softuart_pin_t pin_tx; + // optional rs485 tx enable pin (high -> tx enabled) + uint8_t pin_rs485_tx_enable; + // wether or not this softuart is rs485 and controlls rs485 tx enable pin + uint8_t is_rs485; + volatile softuart_buffer_t buffer; + uint32_t bit_time; + uint32_t elapsed; } Softuart; BOOL Softuart_Available(Softuart *s); uint32_t Softuart_Flush(Softuart *s); BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us); -void Softuart_Intr_Handler(void *p); //void* for type compatibility with etshal.h: void ets_isr_attach(int irq_no, void (*handler)(void *), void *arg); +void Softuart_Intr_Handler(void *p); // void* for type compatibility with etshal.h: void ets_isr_attach(int irq_no, void (*handler)(void *), void *arg); void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id); void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id); void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id); void Softuart_Init(Softuart *s, uint32_t baudrate); void Softuart_Putchar(Softuart *s, char data); -void Softuart_Puts(Softuart *s, const char *c ); +void Softuart_Puts(Softuart *s, const char *c); uint8_t Softuart_Read(Softuart *s); -uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen ); +uint8_t Softuart_Readline(Softuart *s, char *Buffer, uint8_t MaxLen); // check status of rx/tx int Softuart_rx_any(Softuart *s); int Softuart_tx_any_room(Softuart *s); -//define mapping from pin to functio mode +// define mapping from pin to functio mode typedef struct { - uint32_t gpio_mux_name; - uint8_t gpio_func; + uint32_t gpio_mux_name; + uint8_t gpio_func; } softuart_reg_t; #endif /* SOFTUART_H_ */ From 66cf0b058ea96ce6a5a0a53640ae325563f63f68 Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Mon, 13 Sep 2021 16:32:10 +0200 Subject: [PATCH 08/14] lib/lwip: Updated to match upstream. --- lib/lwip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lwip b/lib/lwip index 92f23d6ca0971..159e31b689577 160000 --- a/lib/lwip +++ b/lib/lwip @@ -1 +1 @@ -Subproject commit 92f23d6ca0971a32f2085b9480e738d34174417b +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 From 67c2dc43fb75173c9f4144bb4e7e1121828e9b95 Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Mon, 13 Sep 2021 16:59:31 +0200 Subject: [PATCH 09/14] ports/esp8266: Deleted espmissingincludes.h. --- ports/esp8266/espmissingincludes.h | 75 ------------------------------ ports/esp8266/softuart.c | 1 - 2 files changed, 76 deletions(-) delete mode 100644 ports/esp8266/espmissingincludes.h diff --git a/ports/esp8266/espmissingincludes.h b/ports/esp8266/espmissingincludes.h deleted file mode 100644 index 6cadace90fc3b..0000000000000 --- a/ports/esp8266/espmissingincludes.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef ESPMISSINGINCLUDES_H -#define ESPMISSINGINCLUDES_H - -#include -#include - - -int strcasecmp(const char *a, const char *b); -#ifndef FREERTOS -#include -#include -// Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. -// MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler. -typedef struct espconn espconn; - -int atoi(const char *nptr); -// void ets_install_putc1(void *routine); -void ets_isr_mask(unsigned intr); -void ets_isr_unmask(unsigned intr); -int ets_memcmp(const void *s1, const void *s2, size_t n); -void *ets_memcpy(void *dest, const void *src, size_t n); -void *ets_memset(void *s, int c, size_t n); -int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format(printf, 2, 3))); -int ets_str2macaddr(void *, void *); -int ets_strcmp(const char *s1, const char *s2); -char *ets_strcpy(char *dest, const char *src); -// size_t ets_strlen(const char *s); -// int ets_strncmp(const char *s1, const char *s2, int len); -char *ets_strncpy(char *dest, const char *src, size_t n); -char *ets_strstr(const char *haystack, const char *needle); -void ets_timer_disarm(os_timer_t *a); -void ets_timer_setfn(os_timer_t *t, ETSTimerFunc *fn, void *parg); -void ets_update_cpu_frequency(int freqmhz); -void *os_memmove(void *dest, const void *src, size_t n); -int os_printf(const char *format, ...) __attribute__ ((format(printf, 1, 2))); -int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format(printf, 3, 4))); -int os_printf_plus(const char *format, ...) __attribute__ ((format(printf, 1, 2))); -// void uart_div_modify(int no, unsigned int freq); -uint8 wifi_get_opmode(void); -uint32 system_get_time(); -int rand(void); -void ets_bzero(void *s, size_t n); -// void ets_delay_us(int ms); - -// Hack: this is defined in SDK 1.4.0 and undefined in 1.3.0. It's only used for this, the symbol itself -// has no meaning here. -#ifndef RC_LIMIT_P2P_11N -// Defs for SDK <1.4.0 -void *pvPortMalloc(size_t xWantedSize); -void *pvPortZalloc(size_t); -void vPortFree(void *ptr); -void *vPortMalloc(size_t xWantedSize); -void pvPortFree(void *ptr); -#else -// void *pvPortMalloc(size_t xWantedSize, const char *file, int line); -// void *pvPortZalloc(size_t, const char *file, int line); -// void vPortFree(void *ptr, const char *file, int line); -void *vPortMalloc(size_t xWantedSize, const char *file, int line); -void pvPortFree(void *ptr, const char *file, int line); -#endif - -// Standard PIN_FUNC_SELECT gives a warning. Replace by a non-warning one. -#ifdef PIN_FUNC_SELECT -#undef PIN_FUNC_SELECT -#define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ - WRITE_PERI_REG(PIN_NAME, \ - (READ_PERI_REG(PIN_NAME) \ - & (~(PERIPHS_IO_MUX_FUNC << PERIPHS_IO_MUX_FUNC_S))) \ - | ((((FUNC & BIT2) << 2) | (FUNC & 0x3)) << PERIPHS_IO_MUX_FUNC_S)); \ -} while (0) -#endif - -#endif - -#endif diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index ac9ed27a6a7d6..e8feaf981ab85 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -5,7 +5,6 @@ #include "os_type.h" #include "esp_mphal.h" #include "user_interface.h" -#include "espmissingincludes.h" #include "softuart.h" // array of pointers to instances From fc60f499bb3c4d480f2833f4e7a898b819fc8470 Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Mon, 13 Sep 2021 19:10:57 +0200 Subject: [PATCH 10/14] ports/esp8266: Softuart code cleanup. --- ports/esp8266/machine_softuart.c | 1 - ports/esp8266/softuart.c | 13 +++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/ports/esp8266/machine_softuart.c b/ports/esp8266/machine_softuart.c index 660c7900d1aff..3610e6f0f917f 100644 --- a/ports/esp8266/machine_softuart.c +++ b/ports/esp8266/machine_softuart.c @@ -103,7 +103,6 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co // set data bits self->bits = 8; // no other options are supported - // set parity self->parity = 0; // "NONE" no other options are supported diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index e8feaf981ab85..ee3cdf2d83ca3 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -220,10 +220,10 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) // load instance which has rx pin on interrupt pin attached s = _Softuart_GPIO_Instances[gpio_id]; -// disable interrupt for GPIO0 + // disable interrupt for GPIO0 gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), GPIO_PIN_INTR_DISABLE); -// Do something, for example, increment whatyouwant indirectly + // Do something, for example, increment whatyouwant indirectly // check level level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); if (!level) { @@ -246,8 +246,7 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) start_time += wait_time; } - for (i = 0; i < 8; i++) - { + for (i = 0; i < 8; i++) { while ((get_ccount() - start_time) < s->bit_time) { ; } @@ -287,7 +286,7 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) // clear interrupt GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); -// Reactivate interrupts for GPIO0 + // Reactivate interrupts for GPIO0 gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), 3); } else { // clear interrupt, no matter from which pin @@ -300,7 +299,7 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) } - +// *********************************** // Read data from buffer uint8_t Softuart_Read(Softuart *s) { @@ -338,7 +337,6 @@ int Softuart_tx_any_room(Softuart *s) { return true; } - // cversek: // based on micropython/esp8266/uart.c bool uart_rx_wait(uint32_t timeout_us) // Waits at most timeout microseconds for at least 1 char to become ready for reading. @@ -358,7 +356,6 @@ BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us) { } } - static inline u8 chbit(u8 data, u8 bit) { if ((data & bit) != 0) { return 1; From 10c8ec3a7a36880116bea6d1863ab2cc65474dcc Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Tue, 14 Sep 2021 00:03:51 +0200 Subject: [PATCH 11/14] ports/esp8266: Refactoring, multiple instances supported. Now multiple instances of SoftUART possible. softuart.c: Removed os_printf calls (they reset my ESP). softuart.c: Moved validation to machine_softuart.c (as in regular UART). machine_softuart.c: Tx/Rx can now only be defined via constructor (as uart_id regular UART). machine_softuart.c: Unsupported parameters now raise ValueErrors. --- ports/esp8266/machine_softuart.c | 152 +++++++++++++++++++++---------- ports/esp8266/softuart.c | 79 ++++++---------- ports/esp8266/softuart.h | 5 +- 3 files changed, 138 insertions(+), 98 deletions(-) diff --git a/ports/esp8266/machine_softuart.c b/ports/esp8266/machine_softuart.c index 3610e6f0f917f..d77b3f0e72a36 100644 --- a/ports/esp8266/machine_softuart.c +++ b/ports/esp8266/machine_softuart.c @@ -32,20 +32,14 @@ #include "mem.h" #include "softuart.h" - #include "py/runtime.h" #include "py/stream.h" #include "py/mperrno.h" +#include "py/misc.h" #include "py/mphal.h" -// #include "py/malloc.h" #include "modmachine.h" -// UartDev is defined and initialized in rom code. -// FXIME //extern UartDevice UartDev; - -Softuart softuartDevice; - -typedef struct _pyb_softuart_obj_t { +typedef struct { mp_obj_base_t base; // uint8_t uart_id; Softuart *softuart_ptr; // point to instance of driver object @@ -59,7 +53,7 @@ typedef struct _pyb_softuart_obj_t { uint16_t timeout_char; // timeout waiting between chars (in ms) } pyb_softuart_obj_t; -STATIC const char *_parity_name[] = {"None", "1", "0"}; +STATIC const char *_parity_name[] = {"None", "ODD", "EVEN"}; /******************************************************************************/ // MicroPython bindings for softUART @@ -73,41 +67,77 @@ STATIC void pyb_softuart_print(const mp_print_t *print, mp_obj_t self_in, mp_pri } STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_timeout, ARG_timeout_char}; + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_timeout, ARG_timeout_char}; static const mp_arg_t allowed_args[] = { - { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_rx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + // Only supported via constructor (analogous to UART id) + // { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + // { MP_QSTR_rx, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, - // { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, - // { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - // { MP_QSTR_stop, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 10} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // assign the pins - self->tx = mp_obj_get_pin_obj(args[ARG_tx].u_obj); - Softuart_SetPinTx(&softuartDevice, mp_obj_get_pin(self->tx)); - self->rx = mp_obj_get_pin_obj(args[ARG_rx].u_obj); - Softuart_SetPinRx(&softuartDevice, mp_obj_get_pin(self->rx)); - - // set baudrate - if (args[ARG_baudrate].u_int > 0) { + // set baudrate (0 = default) + if (args[ARG_baudrate].u_int < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("negative baudrate")); + } else if (args[ARG_baudrate].u_int > 0) { self->baudrate = args[ARG_baudrate].u_int; - Softuart_Init(&softuartDevice, self->baudrate); - // UartDev.baut_rate = self->baudrate; // Sic! } // set data bits - self->bits = 8; // no other options are supported + switch (args[ARG_bits].u_int) { + case 0: + break; // keep default + case 8: + // TODO set self->softuart_ptr bits if supported + self->bits = 8; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("unsupported or invalid data bits")); + break; + } // set parity - self->parity = 0; // "NONE" no other options are supported + if (args[ARG_parity].u_obj != MP_OBJ_NULL) { + if (args[ARG_parity].u_obj == mp_const_none) { + // TODO disable self->softuart_ptr parity if supported + self->parity = 0; + } else { + mp_int_t parity = mp_obj_get_int(args[ARG_parity].u_obj); + // TODO enable self->softuart_ptr parity if supported + if (parity & 1) { + // TODO enable ODD parity + // self->parity = 1; + mp_raise_ValueError(MP_ERROR_TEXT("unsupported parity")); + } else { + // TODO enable EVEN parity + // self->parity = 2; + mp_raise_ValueError(MP_ERROR_TEXT("unsupported parity")); + } + } + } // set stop bits - self->stop = 1; // "NONE" no other options are supported + switch (args[ARG_stop].u_int) { + case 0: + break; // keep default + case 1: + // TODO set self->softuart_ptr stop bits if supported + self->stop = 1; + break; + case 2: + mp_raise_ValueError(MP_ERROR_TEXT("unsupported stop bits")); + // self->stop = 2; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("invalid stop bits")); + break; + } // set timeout self->timeout = args[ARG_timeout].u_int; @@ -121,46 +151,73 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co } // setup - // FIXME //uart_setup(self->uart_id); + Softuart_Init(self->softuart_ptr, self->baudrate); } STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + enum { ARG_tx, ARG_rx }; + + // check for 2 required arguments (tx, rx pins) + // keeping the convention of machine.UART, later occuring + // kwargs will be compatible with init() + mp_arg_check_num(n_args, n_kw, 2, MP_OBJ_FUN_ARGS_MAX, true); // create instance pyb_softuart_obj_t *self = m_new_obj(pyb_softuart_obj_t); self->base.type = &pyb_softuart_type; - // FIXME removed //self->uart_id = uart_id; - // self->softuart_ptr = os_malloc(sizeof(Softuart)); + + // assign pointer to driver structure + self->softuart_ptr = m_new_obj(Softuart); + + // assign the pins + // TODO make tx-only, rx-only UART possible (by passing None) + self->tx = mp_obj_get_pin_obj(args[ARG_tx]); + uint tx_pin = mp_obj_get_pin(self->tx); + if (Softuart_IsGpioValid(tx_pin)) { + Softuart_SetPinTx(self->softuart_ptr, tx_pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid tx pin")); + } + + self->rx = mp_obj_get_pin_obj(args[ARG_rx]); + uint rx_pin = mp_obj_get_pin(self->rx); + if (Softuart_IsGpioValid(rx_pin)) { + Softuart_SetPinRx(self->softuart_ptr, rx_pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid rx pin")); + } + + // set defaults self->baudrate = 9600; self->bits = 8; self->parity = 0; self->stop = 1; - self->timeout = 0; - self->timeout_char = 0; + self->timeout = 1000; + self->timeout_char = 10; // init the peripheral mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - pyb_softuart_init_helper(self, n_args, args, &kw_args); + pyb_softuart_init_helper(self, n_args - 2, args + 2, &kw_args); // skip 2 arguments return MP_OBJ_FROM_PTR(self); } STATIC mp_obj_t pyb_softuart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + // omit self (args[0]) and call init helper pyb_softuart_init_helper(args[0], n_args - 1, args + 1, kw_args); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(pyb_softuart_init_obj, 1, pyb_softuart_init); +MP_DEFINE_CONST_FUN_OBJ_KW(pyb_softuart_init_obj, 0, pyb_softuart_init); STATIC mp_obj_t pyb_softuart_flush(mp_obj_t self_in) { - // pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); - Softuart_Flush(&softuartDevice); // reset the rx buffer to empty + pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + Softuart_Flush(self->softuart_ptr); // reset the rx buffer to empty return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(pyb_softuart_flush_obj, pyb_softuart_flush); - STATIC const mp_rom_map_elem_t pyb_softuart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_softuart_init_obj) }, { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&pyb_softuart_flush_obj) }, @@ -170,7 +227,6 @@ STATIC const mp_rom_map_elem_t pyb_softuart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, }; - STATIC MP_DEFINE_CONST_DICT(pyb_softuart_locals_dict, pyb_softuart_locals_dict_table); STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { @@ -182,7 +238,7 @@ STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, } // wait for first char to become available - if (!Softuart_rxWait(&softuartDevice, self->timeout * 1000)) { + if (!Softuart_rxWait(self->softuart_ptr, self->timeout * 1000)) { *errcode = MP_EAGAIN; return MP_STREAM_ERROR; } @@ -190,8 +246,8 @@ STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, // read the data uint8_t *buf = buf_in; while (Softuart_Available(&softuartDevice)) { - *buf++ = Softuart_Read(&softuartDevice); - if (--size == 0 || !Softuart_rxWait(&softuartDevice, self->timeout_char * 1000)) { + *buf++ = Softuart_Read(self->softuart_ptr); + if (--size == 0 || !Softuart_rxWait(self->softuart_ptr, self->timeout_char * 1000)) { // return number of bytes read return buf - (uint8_t *)buf_in; } @@ -200,7 +256,7 @@ STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, } STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { - // pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); const byte *buf = buf_in; /* TODO implement non-blocking @@ -213,7 +269,7 @@ STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uin // write the data for (size_t i = 0; i < size; ++i) { - Softuart_Putchar(&softuartDevice, *buf++); + Softuart_Putchar(self->softuart_ptr, *buf++); // FIXME //uart_tx_one_char(self->uart_id, *buf++); } @@ -222,14 +278,16 @@ STATIC mp_uint_t pyb_softuart_write(mp_obj_t self_in, const void *buf_in, mp_uin } STATIC mp_uint_t pyb_softuart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_STREAM_POLL_RD) && Softuart_rx_any(&softuartDevice)) { + if ((flags & MP_STREAM_POLL_RD) && Softuart_rx_any(self->softuart_ptr)) { ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_STREAM_POLL_WR) && Softuart_tx_any_room(&softuartDevice)) { + if ((flags & MP_STREAM_POLL_WR) && Softuart_tx_any_room(self->softuart_ptr)) { ret |= MP_STREAM_POLL_WR; } } else { diff --git a/ports/esp8266/softuart.c b/ports/esp8266/softuart.c index ee3cdf2d83ca3..2d053a957207a 100644 --- a/ports/esp8266/softuart.c +++ b/ports/esp8266/softuart.c @@ -7,12 +7,15 @@ #include "user_interface.h" #include "softuart.h" +#include "py/misc.h" + // array of pointers to instances +// ugly, but using *p in interrupt handler doesn't work... Softuart *_Softuart_GPIO_Instances[SOFTUART_GPIO_COUNT]; uint8_t _Softuart_Instances_Count = 0; // intialize list of gpio names and functions -softuart_reg_t softuart_reg[] = +const softuart_reg_t softuart_reg[] = { { PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0 }, // gpio0 { PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1 }, // gpio1 (uart) @@ -33,6 +36,8 @@ softuart_reg_t softuart_reg[] = // @TODO TODO gpio16 is missing (?include) }; +// returns position of first 1 in a number +// used to determine which gpio caused the interrupt uint8_t Softuart_Bitcount(uint32_t x) { uint8_t count; @@ -53,29 +58,21 @@ uint8_t Softuart_IsGpioValid(uint8_t gpio_id) { return 1; } +// gpio_id should be validated in machine_softuart void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id) { - if (!Softuart_IsGpioValid(gpio_id)) { - os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); - } else { - s->pin_rx.gpio_id = gpio_id; - s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; - s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; - } + s->pin_rx.gpio_id = gpio_id; + s->pin_rx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_rx.gpio_func = softuart_reg[gpio_id].gpio_func; } +// gpio_id should be validated in machine_softuart void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id) { - if (!Softuart_IsGpioValid(gpio_id)) { - os_printf("SOFTUART GPIO not valid %d\r\n",gpio_id); - } else { - s->pin_tx.gpio_id = gpio_id; - s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; - s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; - } + s->pin_tx.gpio_id = gpio_id; + s->pin_tx.gpio_mux_name = softuart_reg[gpio_id].gpio_mux_name; + s->pin_tx.gpio_func = softuart_reg[gpio_id].gpio_func; } void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id) { - os_printf("SOFTUART RS485 init\r\n"); - // enable rs485 s->is_rs485 = 1; @@ -89,8 +86,6 @@ void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id) { // set low for tx idle (so other bus participants can send) GPIO_OUTPUT_SET(GPIO_ID_PIN(gpio_id), 0); - - os_printf("SOFTUART RS485 init done\r\n"); } void Softuart_Init(Softuart *s, uint32_t baudrate) { @@ -98,23 +93,19 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) { s->is_rs485 = 0; if (!_Softuart_Instances_Count) { - os_printf("SOFTUART initialize gpio\r\n"); // Initilaize gpio subsystem gpio_init(); } // set bit time - if (baudrate <= 0) { - os_printf("SOFTUART ERROR: Set baud rate (%d)\r\n",baudrate); - } else { - s->bit_time = system_get_cpu_freq() * 1000000 / baudrate; - os_printf("SOFTUART bit_time is %d\r\n",s->bit_time); - } - + // baudrate should be validated in machine_softuart + s->bit_time = system_get_cpu_freq() * 1000000 / baudrate; // init tx pin if (!s->pin_tx.gpio_mux_name) { - os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); + // calls to os_* sometimes reset my ESP + // os_printf("SOFTUART ERROR: Set tx pin (%d)\r\n",s->pin_tx.gpio_mux_name); + // TODO Support Rx only uart } else { // enable pin as gpio PIN_FUNC_SELECT(s->pin_tx.gpio_mux_name, s->pin_tx.gpio_func); @@ -126,13 +117,13 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) { GPIO_OUTPUT_SET(GPIO_ID_PIN(s->pin_tx.gpio_id), 1); unsigned int delay = 100000; os_delay_us(delay); - - os_printf("SOFTUART TX INIT DONE\r\n"); } // init rx pin if (!s->pin_rx.gpio_mux_name) { - os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); + // calls to os_* sometimes reset my ESP + // os_printf("SOFTUART ERROR: Set rx pin (%d)\r\n",s->pin_rx.gpio_mux_name); + // TODO Support Tx only uart } else { // enable pin as gpio PIN_FUNC_SELECT(s->pin_rx.gpio_mux_name, s->pin_rx.gpio_func); @@ -148,8 +139,8 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) { // disable interrupts by GPIO ETS_GPIO_INTR_DISABLE(); - // attach interrupt handler and a pointer that will be passed around each time - ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, s); + // attach interrupt handler + ETS_GPIO_INTR_ATTACH(Softuart_Intr_Handler, NULL); // not sure what this does... (quote from example): // void gpio_register_set(uint32 reg_id, uint32 value); @@ -176,22 +167,17 @@ void Softuart_Init(Softuart *s, uint32_t baudrate) { // globally enable GPIO interrupts ETS_GPIO_INTR_ENABLE(); - - os_printf("SOFTUART RX INIT DONE\r\n"); } // add instance to array of instances _Softuart_GPIO_Instances[s->pin_rx.gpio_id] = s; _Softuart_Instances_Count++; - - os_printf("SOFTUART INIT DONE\r\n"); } // *********************************** #define RSR_CCOUNT(r) __asm__ __volatile__ ("rsr %0,ccount" : "=a" (r)) -static inline uint32_t MP_FASTCODE(get_ccount)(void) -{ +static inline uint32_t MP_FASTCODE(get_ccount)(void) { uint32_t ccount; RSR_CCOUNT(ccount); return ccount; @@ -199,14 +185,10 @@ static inline uint32_t MP_FASTCODE(get_ccount)(void) // *********************************** -void MP_FASTCODE(Softuart_Intr_Handler)(void *p) -{ +void MP_FASTCODE(Softuart_Intr_Handler)(void *p) { uint8_t level, gpio_id; unsigned start_time = get_ccount(); - // ignore void* pointer and make a new pointer - Softuart *s; - // disable all interrupts ets_intr_lock(); @@ -218,12 +200,11 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) // if interrupt was by an attached rx pin if (gpio_id != 0xFF) { // load instance which has rx pin on interrupt pin attached - s = _Softuart_GPIO_Instances[gpio_id]; + Softuart *s = _Softuart_GPIO_Instances[gpio_id]; // disable interrupt for GPIO0 gpio_pin_intr_state_set(GPIO_ID_PIN(s->pin_rx.gpio_id), GPIO_PIN_INTR_DISABLE); - // Do something, for example, increment whatyouwant indirectly // check level level = GPIO_INPUT_GET(GPIO_ID_PIN(s->pin_rx.gpio_id)); if (!level) { @@ -274,7 +255,7 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) uint8 next = (s->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF; if (next != s->buffer.receive_buffer_head) { // save new data in buffer: tail points to where byte goes - s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; // save new byte + s->buffer.receive_buffer[s->buffer.receive_buffer_tail] = d; s->buffer.receive_buffer_tail = next; } else { s->buffer.buffer_overflow = 1; @@ -293,8 +274,8 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) // otherwise, this interrupt will be called again forever GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); } - // re-enable all interrupts + // re-enable all interrupts ets_intr_unlock(); } @@ -305,7 +286,7 @@ void MP_FASTCODE(Softuart_Intr_Handler)(void *p) uint8_t Softuart_Read(Softuart *s) { // Empty buffer? if (s->buffer.receive_buffer_head == s->buffer.receive_buffer_tail) { - return 0; + return 0; // TODO move return to argument } // Read from "head" diff --git a/ports/esp8266/softuart.h b/ports/esp8266/softuart.h index ca691b96ff140..61031886c28b1 100644 --- a/ports/esp8266/softuart.h +++ b/ports/esp8266/softuart.h @@ -7,13 +7,13 @@ #define SOFTUART_GPIO_COUNT 16 -typedef struct softuart_pin_t { +typedef struct { uint8_t gpio_id; uint32_t gpio_mux_name; uint8_t gpio_func; } softuart_pin_t; -typedef struct softuart_buffer_t { +typedef struct { char receive_buffer[SOFTUART_MAX_RX_BUFF]; uint8_t receive_buffer_tail; uint8_t receive_buffer_head; @@ -37,6 +37,7 @@ BOOL Softuart_Available(Softuart *s); uint32_t Softuart_Flush(Softuart *s); BOOL Softuart_rxWait(Softuart *s, uint32_t timeout_us); void Softuart_Intr_Handler(void *p); // void* for type compatibility with etshal.h: void ets_isr_attach(int irq_no, void (*handler)(void *), void *arg); +uint8_t Softuart_IsGpioValid(uint8_t gpio_id); void Softuart_SetPinRx(Softuart *s, uint8_t gpio_id); void Softuart_SetPinTx(Softuart *s, uint8_t gpio_id); void Softuart_EnableRs485(Softuart *s, uint8_t gpio_id); From 8f8abcc0c3d2e29a208e0633550b0eeb64151fd3 Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Tue, 14 Sep 2021 00:12:55 +0200 Subject: [PATCH 12/14] esp8266/machine_softuart.c: Fixed wrong pointer. esp8266/examples: Added 2 examples for SoftUART (read, write). --- ports/esp8266/examples/softuart_read.py | 24 ++++++++++++++++++++++++ ports/esp8266/examples/softuart_write.py | 15 +++++++++++++++ ports/esp8266/machine_softuart.c | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 ports/esp8266/examples/softuart_read.py create mode 100644 ports/esp8266/examples/softuart_write.py diff --git a/ports/esp8266/examples/softuart_read.py b/ports/esp8266/examples/softuart_read.py new file mode 100644 index 0000000000000..d8445ac32f78e --- /dev/null +++ b/ports/esp8266/examples/softuart_read.py @@ -0,0 +1,24 @@ +# test suite for SoftUART + +from machine import SoftUART, Pin +import time +import select + +poll = select.poll() + +s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 +s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 +poll.register(s1) +poll.register(s2) + +print("polling...") + +while True: + read = poll.ipoll() + + for r, ev in read: + if (ev & select.POLLIN) != 0: + i=None + if r is s1: i="s1" + else: i="s2" + print(i, r.read()) diff --git a/ports/esp8266/examples/softuart_write.py b/ports/esp8266/examples/softuart_write.py new file mode 100644 index 0000000000000..871cfe9859759 --- /dev/null +++ b/ports/esp8266/examples/softuart_write.py @@ -0,0 +1,15 @@ +# test suite for SoftUART + +from machine import SoftUART, Pin +import time + +s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 +s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 + +while True: + print("sending... ",end="") + s1.write("Hello world s1\r\n") + print("1, ", end="") + s2.write("Hello world s2\r\n") + print("2, done.") + time.sleep(1) diff --git a/ports/esp8266/machine_softuart.c b/ports/esp8266/machine_softuart.c index d77b3f0e72a36..c5962b15def1c 100644 --- a/ports/esp8266/machine_softuart.c +++ b/ports/esp8266/machine_softuart.c @@ -245,7 +245,7 @@ STATIC mp_uint_t MP_FASTCODE(pyb_softuart_read)(mp_obj_t self_in, void *buf_in, // read the data uint8_t *buf = buf_in; - while (Softuart_Available(&softuartDevice)) { + while (Softuart_Available(self->softuart_ptr)) { *buf++ = Softuart_Read(self->softuart_ptr); if (--size == 0 || !Softuart_rxWait(self->softuart_ptr, self->timeout_char * 1000)) { // return number of bytes read From b883820869601b732709284f783fed132ead3713 Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Tue, 14 Sep 2021 00:20:58 +0200 Subject: [PATCH 13/14] esp8266/examples: Fixed code formatting. --- ports/esp8266/examples/softuart_read.py | 20 +++++++++++--------- ports/esp8266/examples/softuart_write.py | 16 ++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/ports/esp8266/examples/softuart_read.py b/ports/esp8266/examples/softuart_read.py index d8445ac32f78e..84403bd031a37 100644 --- a/ports/esp8266/examples/softuart_read.py +++ b/ports/esp8266/examples/softuart_read.py @@ -6,19 +6,21 @@ poll = select.poll() -s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 -s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 +s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 +s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 poll.register(s1) poll.register(s2) print("polling...") while True: - read = poll.ipoll() + read = poll.ipoll() - for r, ev in read: - if (ev & select.POLLIN) != 0: - i=None - if r is s1: i="s1" - else: i="s2" - print(i, r.read()) + for r, ev in read: + if (ev & select.POLLIN) != 0: + i = None + if r is s1: + i = "s1" + else: + i = "s2" + print(i, r.read()) diff --git a/ports/esp8266/examples/softuart_write.py b/ports/esp8266/examples/softuart_write.py index 871cfe9859759..fcf6de10076af 100644 --- a/ports/esp8266/examples/softuart_write.py +++ b/ports/esp8266/examples/softuart_write.py @@ -3,13 +3,13 @@ from machine import SoftUART, Pin import time -s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 -s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 +s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 +s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 while True: - print("sending... ",end="") - s1.write("Hello world s1\r\n") - print("1, ", end="") - s2.write("Hello world s2\r\n") - print("2, done.") - time.sleep(1) + print("sending... ", end="") + s1.write("Hello world s1\r\n") + print("1, ", end="") + s2.write("Hello world s2\r\n") + print("2, done.") + time.sleep(1) From e536ad3434ccf0df7bc7c920ed968be26f2d920c Mon Sep 17 00:00:00 2001 From: MrJake222 Date: Tue, 14 Sep 2021 12:44:48 +0200 Subject: [PATCH 14/14] esp8266/machine_softuart.c: More verbose API, moved tx/rx to kwargs. --- ports/esp8266/examples/softuart_write.py | 15 +++- ports/esp8266/machine_softuart.c | 104 +++++++++++++---------- 2 files changed, 72 insertions(+), 47 deletions(-) diff --git a/ports/esp8266/examples/softuart_write.py b/ports/esp8266/examples/softuart_write.py index fcf6de10076af..aaae716ba7049 100644 --- a/ports/esp8266/examples/softuart_write.py +++ b/ports/esp8266/examples/softuart_write.py @@ -3,13 +3,22 @@ from machine import SoftUART, Pin import time -s1 = SoftUART(Pin(2), Pin(4), baudrate=9600, timeout=0) # tx=2 rx=4 -s2 = SoftUART(Pin(0), Pin(5), baudrate=9600, timeout=0) # tx=0 rx=5 +s1 = SoftUART(tx=Pin(12), rx=Pin(4), baudrate=9600, timeout=0) +s2 = SoftUART(tx=Pin(0), rx=Pin(5), baudrate=9600, timeout=0) -while True: +# Change tx pin and baudrate +s1.init(tx=Pin(2), rx=Pin(4), baudrate=115200) + +c = 10 +while c > 0: print("sending... ", end="") s1.write("Hello world s1\r\n") print("1, ", end="") s2.write("Hello world s2\r\n") print("2, done.") time.sleep(1) + c -= 1 + +s1.deinit() +s2.deinit() +print("done") diff --git a/ports/esp8266/machine_softuart.c b/ports/esp8266/machine_softuart.c index c5962b15def1c..083d55167fec8 100644 --- a/ports/esp8266/machine_softuart.c +++ b/ports/esp8266/machine_softuart.c @@ -55,6 +55,20 @@ typedef struct { STATIC const char *_parity_name[] = {"None", "ODD", "EVEN"}; +STATIC uint8 verify_gpio_pin(mp_obj_t obj_pin) { + // currently not possible to be MP_OBJ_NULL (because required) + if (obj_pin == MP_OBJ_NULL || obj_pin == mp_const_none) { + return 0xFF; + } else { + uint pin = mp_obj_get_pin(mp_obj_get_pin_obj(obj_pin)); + if (Softuart_IsGpioValid(pin)) { + return pin; + } else { + return 0xFF; + } + } +} + /******************************************************************************/ // MicroPython bindings for softUART @@ -67,11 +81,11 @@ STATIC void pyb_softuart_print(const mp_print_t *print, mp_obj_t self_in, mp_pri } STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_timeout, ARG_timeout_char}; + enum { ARG_tx, ARG_rx, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_timeout, ARG_timeout_char}; static const mp_arg_t allowed_args[] = { - // Only supported via constructor (analogous to UART id) - // { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_OBJ }, - // { MP_QSTR_rx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + // TODO make tx-only/rx-only UART possible (by passing None) + { MP_QSTR_tx, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rx, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, @@ -82,6 +96,23 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + // assign the pins + // TODO make tx-only/rx-only UART possible (by passing None) + uint8 tx_pin = verify_gpio_pin(args[ARG_tx].u_obj); + if (tx_pin != 0xFF) { + // valid tx pin + Softuart_SetPinTx(self->softuart_ptr, tx_pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid or no tx pin")); + } + + uint8 rx_pin = verify_gpio_pin(args[ARG_rx].u_obj); + if (rx_pin != 0xFF) { + Softuart_SetPinRx(self->softuart_ptr, rx_pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid or no rx pin")); + } + // set baudrate (0 = default) if (args[ARG_baudrate].u_int < 0) { mp_raise_ValueError(MP_ERROR_TEXT("negative baudrate")); @@ -103,22 +134,20 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co } // set parity - if (args[ARG_parity].u_obj != MP_OBJ_NULL) { - if (args[ARG_parity].u_obj == mp_const_none) { - // TODO disable self->softuart_ptr parity if supported - self->parity = 0; + if (args[ARG_parity].u_obj == MP_OBJ_NULL || args[ARG_parity].u_obj == mp_const_none) { + // TODO disable self->softuart_ptr parity if supported + self->parity = 0; + } else { + mp_int_t parity = mp_obj_get_int(args[ARG_parity].u_obj); + // TODO enable self->softuart_ptr parity if supported + if (parity & 1) { + // TODO enable ODD parity + // self->parity = 1; + mp_raise_ValueError(MP_ERROR_TEXT("unsupported parity")); } else { - mp_int_t parity = mp_obj_get_int(args[ARG_parity].u_obj); - // TODO enable self->softuart_ptr parity if supported - if (parity & 1) { - // TODO enable ODD parity - // self->parity = 1; - mp_raise_ValueError(MP_ERROR_TEXT("unsupported parity")); - } else { - // TODO enable EVEN parity - // self->parity = 2; - mp_raise_ValueError(MP_ERROR_TEXT("unsupported parity")); - } + // TODO enable EVEN parity + // self->parity = 2; + mp_raise_ValueError(MP_ERROR_TEXT("unsupported parity")); } } @@ -155,12 +184,8 @@ STATIC void pyb_softuart_init_helper(pyb_softuart_obj_t *self, size_t n_args, co } STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - enum { ARG_tx, ARG_rx }; - - // check for 2 required arguments (tx, rx pins) - // keeping the convention of machine.UART, later occuring - // kwargs will be compatible with init() - mp_arg_check_num(n_args, n_kw, 2, MP_OBJ_FUN_ARGS_MAX, true); + // tx, rx pins moved to kwargs + // mp_arg_check_num(n_args, n_kw, 2, MP_OBJ_FUN_ARGS_MAX, true); // create instance pyb_softuart_obj_t *self = m_new_obj(pyb_softuart_obj_t); @@ -169,24 +194,6 @@ STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, // assign pointer to driver structure self->softuart_ptr = m_new_obj(Softuart); - // assign the pins - // TODO make tx-only, rx-only UART possible (by passing None) - self->tx = mp_obj_get_pin_obj(args[ARG_tx]); - uint tx_pin = mp_obj_get_pin(self->tx); - if (Softuart_IsGpioValid(tx_pin)) { - Softuart_SetPinTx(self->softuart_ptr, tx_pin); - } else { - mp_raise_ValueError(MP_ERROR_TEXT("invalid tx pin")); - } - - self->rx = mp_obj_get_pin_obj(args[ARG_rx]); - uint rx_pin = mp_obj_get_pin(self->rx); - if (Softuart_IsGpioValid(rx_pin)) { - Softuart_SetPinRx(self->softuart_ptr, rx_pin); - } else { - mp_raise_ValueError(MP_ERROR_TEXT("invalid rx pin")); - } - // set defaults self->baudrate = 9600; self->bits = 8; @@ -198,7 +205,7 @@ STATIC mp_obj_t pyb_softuart_make_new(const mp_obj_type_t *type, size_t n_args, // init the peripheral mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - pyb_softuart_init_helper(self, n_args - 2, args + 2, &kw_args); // skip 2 arguments + pyb_softuart_init_helper(self, n_args, args, &kw_args); return MP_OBJ_FROM_PTR(self); } @@ -210,6 +217,14 @@ STATIC mp_obj_t pyb_softuart_init(size_t n_args, const mp_obj_t *args, mp_map_t } MP_DEFINE_CONST_FUN_OBJ_KW(pyb_softuart_init_obj, 0, pyb_softuart_init); +STATIC mp_obj_t pyb_softuart_deinit(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(args[0]); + // free memory + m_del_obj(Softuart, self->softuart_ptr); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(pyb_softuart_deinit_obj, 0, pyb_softuart_deinit); + STATIC mp_obj_t pyb_softuart_flush(mp_obj_t self_in) { pyb_softuart_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -220,6 +235,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(pyb_softuart_flush_obj, pyb_softuart_flush); STATIC const mp_rom_map_elem_t pyb_softuart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_softuart_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pyb_softuart_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&pyb_softuart_flush_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy