Browse Source

Testing softuart library

softuart
Xose Pérez 6 years ago
parent
commit
061d4c5577
4 changed files with 475 additions and 29 deletions
  1. +3
    -3
      README.md
  2. +395
    -0
      code/espurna/libs/softuart.c
  3. +53
    -0
      code/espurna/libs/softuart.h
  4. +24
    -26
      code/espurna/sensors/V9261FSensor.h

+ 3
- 3
README.md View File

@ -4,9 +4,9 @@ ESPurna ("spark" in Catalan) is a custom firmware for ESP8266 based smart switch
It uses the Arduino Core for ESP8266 framework and a number of 3rd party libraries.
[![version](https://img.shields.io/badge/version-1.12.4a-brightgreen.svg)](CHANGELOG.md)
![branch](https://img.shields.io/badge/branch-dev-orange.svg)
[![travis](https://travis-ci.org/xoseperez/espurna.svg?branch=dev)](https://travis-ci.org/xoseperez/espurna)
[![codacy](https://img.shields.io/codacy/grade/c9496e25cf07434cba786b462cb15f49/dev.svg)](https://www.codacy.com/app/xoseperez/espurna/dashboard)
![branch](https://img.shields.io/badge/branch-softuart-orange.svg)
[![travis](https://travis-ci.org/xoseperez/espurna.svg?branch=softuart)](https://travis-ci.org/xoseperez/espurna)
[![codacy](https://img.shields.io/codacy/grade/c9496e25cf07434cba786b462cb15f49/softuart.svg)](https://www.codacy.com/app/xoseperez/espurna/dashboard)
[![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=xose%2eperez%40gmail%2ecom&lc=US&no_note=0&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHostedGuest)
[![twitter](https://img.shields.io/twitter/follow/xoseperez.svg?style=social)](https://twitter.com/intent/follow?screen_name=xoseperez)


+ 395
- 0
code/espurna/libs/softuart.c View File

@ -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;
}

+ 53
- 0
code/espurna/libs/softuart.h View File

@ -0,0 +1,53 @@
#ifndef SOFTUART_H_
#define SOFTUART_H_
#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;
} 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;
bool inverse;
//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);
void Softuart_Intr_Handler(Softuart *s);
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, bool inverse);
void Softuart_Putchar(Softuart *s, char data);
void Softuart_Puts(Softuart *s, const char *c );
uint8_t Softuart_Readline(Softuart *s, char* Buffer, uint8_t MaxLen );
uint8_t Softuart_Read(Softuart *s);
//define mapping from pin to functio mode
typedef struct {
uint32_t gpio_mux_name;
uint8_t gpio_func;
} softuart_reg_t;
#endif /* SOFTUART_H_ */

+ 24
- 26
code/espurna/sensors/V9261FSensor.h View File

@ -10,7 +10,9 @@
#include "Arduino.h"
#include "BaseSensor.h"
#include <SoftwareSerial.h>
extern "C" {
#include "libs/softuart.h"
}
class V9261FSensor : public BaseSensor {
@ -25,10 +27,6 @@ class V9261FSensor : public BaseSensor {
_sensor_id = SENSOR_V9261F_ID;
}
~V9261FSensor() {
if (_serial) delete _serial;
}
// ---------------------------------------------------------------------
void setRX(unsigned char pin_rx) {
@ -63,10 +61,8 @@ class V9261FSensor : public BaseSensor {
if (!_dirty) return;
_dirty = false;
if (_serial) delete _serial;
_serial = new SoftwareSerial(_pin_rx, SW_SERIAL_UNUSED_PIN, _inverted, 32);
_serial->begin(V9261F_BAUDRATE);
Softuart_SetPinRx(&_serial, _pin_rx);
Softuart_Init(&_serial, V9261F_BAUDRATE, _inverted);
}
@ -120,6 +116,12 @@ class V9261FSensor : public BaseSensor {
// Protected
// ---------------------------------------------------------------------
void _flush() {
while (Softuart_Available(&_serial)) {
Softuart_Read(&_serial);
}
}
void _read() {
static unsigned char state = 0;
@ -129,24 +131,22 @@ class V9261FSensor : public BaseSensor {
if (state == 0) {
while (_serial->available()) {
_serial->flush();
found = true;
last = millis();
}
_flush();
found = true;
last = millis();
if (found && (millis() - last > V9261F_SYNC_INTERVAL)) {
_serial->flush();
_flush();
index = 0;
state = 1;
}
} else if (state == 1) {
while (_serial->available()) {
_serial->read();
while (Softuart_Available(&_serial)) {
Softuart_Read(&_serial);
if (index++ >= 7) {
_serial->flush();
_flush();
index = 0;
state = 2;
}
@ -154,10 +154,10 @@ class V9261FSensor : public BaseSensor {
} else if (state == 2) {
while (_serial->available()) {
_data[index] = _serial->read();
while (Softuart_Available(&_serial)) {
_data[index] = Softuart_Read(&_serial);
if (index++ >= 19) {
_serial->flush();
_flush();
last = millis();
state = 3;
}
@ -210,10 +210,8 @@ class V9261FSensor : public BaseSensor {
} else if (state == 4) {
while (_serial->available()) {
_serial->flush();
last = millis();
}
_flush();
last = millis();
if (millis() - last > V9261F_SYNC_INTERVAL) {
state = 1;
@ -236,7 +234,7 @@ class V9261FSensor : public BaseSensor {
unsigned int _pin_rx = V9261F_PIN;
bool _inverted = V9261F_PIN_INVERSE;
SoftwareSerial * _serial = NULL;
Softuart _serial;
double _active = 0;
double _reactive = 0;


Loading…
Cancel
Save