#include "ets_sys.h" #include "osapi.h" #include "gpio.h" #include "os_type.h" #include "user_interface.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, bool inverse) { //disable rs485 s->is_rs485 = 0; s->inverse = inverse; 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 in direct logic) if (!s->inverse) 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); os_delay_us(100000); 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 in direct logic) if (!s->inverse) 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(Softuart *s) { uint8_t level, gpio_id; // 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); //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 == s->inverse) { //pin is in trigger edge //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); } } // 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 if (s->inverse) d = ~d; s->buffer.receive_buffer_head = (s->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF; return d; } // 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; } 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; unsigned start_time = 0x7FFFFFFF & system_get_time(); // inverse logic if (s->inverse) data = ~data; //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++); } } 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; }