|
|
@ -0,0 +1,395 @@ |
|
|
|
#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<<i)); |
|
|
|
} |
|
|
|
|
|
|
|
// 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;} |
|
|
|
} |
|
|
|
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; |
|
|
|
} |