Softuart (#307)
* extras/softuart: support for multiple UARTs, dynamic RX/TX pins
This commit is contained in:
parent
fda5d0b942
commit
6b0547b963
7 changed files with 463 additions and 0 deletions
23
examples/softuart/LICENSE
Normal file
23
examples/softuart/LICENSE
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (C) 2016 Bernhard Guillon <Bernhard.Guillon@web.de>
|
||||||
|
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.
|
||||||
|
|
4
examples/softuart/Makefile
Normal file
4
examples/softuart/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
PROGRAM = softuart
|
||||||
|
EXTRA_COMPONENTS = extras/softuart
|
||||||
|
ESPBAUD = 460800
|
||||||
|
include ../../common.mk
|
40
examples/softuart/main.c
Normal file
40
examples/softuart/main.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Softuart example
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Ruslan V. Uss <unclerus@gmail.com>
|
||||||
|
* Copyright (C) 2016 Bernhard Guillon <Bernhard.Guillon@web.de>
|
||||||
|
* Copyright (c) 2015 plieningerweb
|
||||||
|
*
|
||||||
|
* MIT Licensed as described in the file LICENSE
|
||||||
|
*/
|
||||||
|
#include <esp/gpio.h>
|
||||||
|
#include <esp/uart.h>
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
//#include <FreeRTOS.h>
|
||||||
|
//#include <task.h>
|
||||||
|
|
||||||
|
#include <softuart/softuart.h>
|
||||||
|
|
||||||
|
#define RX_PIN 5
|
||||||
|
#define TX_PIN 4
|
||||||
|
|
||||||
|
void user_init(void)
|
||||||
|
{
|
||||||
|
// setup real UART for now
|
||||||
|
uart_set_baud(0, 115200);
|
||||||
|
printf("SDK version:%s\n\n", sdk_system_get_sdk_version());
|
||||||
|
|
||||||
|
// setup software uart to 9600 8n1
|
||||||
|
softuart_open(0, 9600, RX_PIN, TX_PIN);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (!softuart_available(0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char c = softuart_read(0);
|
||||||
|
printf("input: %c, 0x%02x\n", c, c);
|
||||||
|
softuart_puts(0, "start\r\n");
|
||||||
|
}
|
||||||
|
}
|
23
extras/softuart/LICENSE
Normal file
23
extras/softuart/LICENSE
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (C) 2016 Bernhard Guillon <Bernhard.Guillon@web.de>
|
||||||
|
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.
|
||||||
|
|
10
extras/softuart/component.mk
Normal file
10
extras/softuart/component.mk
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Component makefile for extras/softuart
|
||||||
|
|
||||||
|
# expected anyone using this driver includes it as 'softuart/softuart.h'
|
||||||
|
INC_DIRS += $(softuart_ROOT)..
|
||||||
|
|
||||||
|
# args for passing into compile rule generation
|
||||||
|
softuart_SRC_DIR = $(softuart_ROOT)
|
||||||
|
|
||||||
|
$(eval $(call component_compile_rules,softuart))
|
||||||
|
|
270
extras/softuart/softuart.c
Normal file
270
extras/softuart/softuart.c
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
* Softuart
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Ruslan V. Uss <unclerus@gmail.com>
|
||||||
|
* Copyright (C) 2016 Bernhard Guillon <Bernhard.Guillon@web.de>
|
||||||
|
*
|
||||||
|
* This code is based on Softuart from here [1] and reworked to
|
||||||
|
* fit into esp-open-rtos.
|
||||||
|
*
|
||||||
|
* it fits my needs to read the GY-GPS6MV2 module with 9600 8n1
|
||||||
|
*
|
||||||
|
* Original Copyright:
|
||||||
|
* Copyright (c) 2015 plieningerweb
|
||||||
|
*
|
||||||
|
* MIT Licensed as described in the file LICENSE
|
||||||
|
*
|
||||||
|
* 1 https://github.com/plieningerweb/esp8266-software-uart
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "softuart.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp/gpio.h>
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
//#define SOFTUART_DEBUG
|
||||||
|
|
||||||
|
#ifdef SOFTUART_DEBUG
|
||||||
|
#define debug(fmt, ...) printf("%s: " fmt "\n", "SOFTUART", ## __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define debug(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char receive_buffer[SOFTUART_MAX_RX_BUFF];
|
||||||
|
uint8_t receive_buffer_tail;
|
||||||
|
uint8_t receive_buffer_head;
|
||||||
|
uint8_t buffer_overflow;
|
||||||
|
} softuart_buffer_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t rx_pin, tx_pin;
|
||||||
|
uint32_t baudrate;
|
||||||
|
volatile softuart_buffer_t buffer;
|
||||||
|
uint16_t bit_time;
|
||||||
|
} softuart_t;
|
||||||
|
|
||||||
|
static softuart_t uarts[SOFTUART_MAX_UARTS] = { { 0 } };
|
||||||
|
|
||||||
|
inline static int8_t find_uart_by_rx(uint8_t rx_pin)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < SOFTUART_MAX_UARTS; i++)
|
||||||
|
if (uarts[i].baudrate && uarts[i].rx_pin == rx_pin) return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPIO interrupt handler
|
||||||
|
static void handle_rx(uint8_t gpio_num)
|
||||||
|
{
|
||||||
|
// find uart
|
||||||
|
int8_t uart_no = find_uart_by_rx(gpio_num);
|
||||||
|
if (uart_no < 0) return;
|
||||||
|
|
||||||
|
softuart_t *uart = uarts + uart_no;
|
||||||
|
|
||||||
|
// Disable interrupt
|
||||||
|
gpio_set_interrupt(gpio_num, GPIO_INTTYPE_NONE, handle_rx);
|
||||||
|
|
||||||
|
// Wait till start bit is half over so we can sample the next one in the center
|
||||||
|
sdk_os_delay_us(uart->bit_time / 2);
|
||||||
|
|
||||||
|
// Now sample bits
|
||||||
|
uint8_t d = 0;
|
||||||
|
uint32_t start_time = 0x7FFFFFFF & sdk_system_get_time();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
while ((0x7FFFFFFF & sdk_system_get_time()) < (start_time + (uart->bit_time * (i + 1))))
|
||||||
|
{
|
||||||
|
// If system timer overflow, escape from while loop
|
||||||
|
if ((0x7FFFFFFF & sdk_system_get_time()) < start_time)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Shift d to the right
|
||||||
|
d >>= 1;
|
||||||
|
|
||||||
|
// Read bit
|
||||||
|
if (gpio_read(uart->rx_pin))
|
||||||
|
{
|
||||||
|
// If high, set msb of 8bit to 1
|
||||||
|
d |= 0x80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store byte in buffer
|
||||||
|
// If buffer full, set the overflow flag and return
|
||||||
|
uint8_t next = (uart->buffer.receive_buffer_tail + 1) % SOFTUART_MAX_RX_BUFF;
|
||||||
|
if (next != uart->buffer.receive_buffer_head)
|
||||||
|
{
|
||||||
|
// save new data in buffer: tail points to where byte goes
|
||||||
|
uart->buffer.receive_buffer[uart->buffer.receive_buffer_tail] = d; // save new byte
|
||||||
|
uart->buffer.receive_buffer_tail = next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uart->buffer.buffer_overflow = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for stop bit
|
||||||
|
sdk_os_delay_us(uart->bit_time);
|
||||||
|
|
||||||
|
// Done, reenable interrupt
|
||||||
|
gpio_set_interrupt(uart->rx_pin, GPIO_INTTYPE_EDGE_NEG, handle_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_uart_no(uint8_t uart_no)
|
||||||
|
{
|
||||||
|
if (uart_no >= SOFTUART_MAX_UARTS)
|
||||||
|
{
|
||||||
|
debug("Invalid uart number %d, %d max", uart_no, SOFTUART_MAX_UARTS);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_uart_enabled(uint8_t uart_no)
|
||||||
|
{
|
||||||
|
if (!uarts[uart_no].baudrate)
|
||||||
|
{
|
||||||
|
debug("Uart %d is disabled", uart_no);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Public
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool softuart_open(uint8_t uart_no, uint32_t baudrate, uint8_t rx_pin, uint8_t tx_pin)
|
||||||
|
{
|
||||||
|
// do some checks
|
||||||
|
if (!check_uart_no(uart_no)) return false;
|
||||||
|
if (baudrate == 0)
|
||||||
|
{
|
||||||
|
debug("Invalid baudrate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < SOFTUART_MAX_UARTS; i++)
|
||||||
|
if (uarts[i].baudrate && i != uart_no
|
||||||
|
&& (uarts[i].rx_pin == rx_pin || uarts[i].tx_pin == tx_pin || uarts[i].rx_pin == tx_pin || uarts[i].tx_pin == rx_pin))
|
||||||
|
{
|
||||||
|
debug("Cannot share pins between uarts");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
softuart_close(uart_no);
|
||||||
|
|
||||||
|
softuart_t *uart = uarts + uart_no;
|
||||||
|
|
||||||
|
uart->baudrate = baudrate;
|
||||||
|
uart->rx_pin = rx_pin;
|
||||||
|
uart->tx_pin = tx_pin;
|
||||||
|
|
||||||
|
// Calculate bit_time
|
||||||
|
uart->bit_time = (1000000 / baudrate);
|
||||||
|
if (((100000000 / baudrate) - (100 * uart->bit_time)) > 50) uart->bit_time++;
|
||||||
|
|
||||||
|
// Setup Rx
|
||||||
|
gpio_enable(rx_pin, GPIO_INPUT);
|
||||||
|
gpio_set_pullup(rx_pin, true, false);
|
||||||
|
|
||||||
|
// Setup Tx
|
||||||
|
gpio_enable(tx_pin, GPIO_OUTPUT);
|
||||||
|
gpio_set_pullup(tx_pin, true, false);
|
||||||
|
gpio_write(tx_pin, 1);
|
||||||
|
|
||||||
|
// Setup the interrupt handler to get the start bit
|
||||||
|
gpio_set_interrupt(rx_pin, GPIO_INTTYPE_EDGE_NEG, handle_rx);
|
||||||
|
|
||||||
|
sdk_os_delay_us(1000); // TODO: not sure if it really needed
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool softuart_close(uint8_t uart_no)
|
||||||
|
{
|
||||||
|
if (!check_uart_no(uart_no)) return false;
|
||||||
|
softuart_t *uart = uarts + uart_no;
|
||||||
|
|
||||||
|
if (!uart->baudrate) return true;
|
||||||
|
|
||||||
|
// Remove interrupt
|
||||||
|
gpio_set_interrupt(uart->rx_pin, GPIO_INTTYPE_NONE, NULL);
|
||||||
|
// Mark as unused
|
||||||
|
uart->baudrate = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool softuart_put(uint8_t uart_no, char c)
|
||||||
|
{
|
||||||
|
if (!check_uart_no(uart_no)) return false;
|
||||||
|
if (!check_uart_enabled(uart_no)) return false;
|
||||||
|
softuart_t *uart = uarts + uart_no;
|
||||||
|
|
||||||
|
uint32_t start_time = 0x7FFFFFFF & sdk_system_get_time();
|
||||||
|
gpio_write(uart->tx_pin, 0);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i <= 8; i++)
|
||||||
|
{
|
||||||
|
while ((0x7FFFFFFF & sdk_system_get_time()) < (start_time + (uart->bit_time * (i + 1))))
|
||||||
|
{
|
||||||
|
if ((0x7FFFFFFF & sdk_system_get_time()) < start_time)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gpio_write(uart->tx_pin, c & (1 << i));
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((0x7FFFFFFF & sdk_system_get_time()) < (start_time + (uart->bit_time * 9)))
|
||||||
|
{
|
||||||
|
if ((0x7FFFFFFF & sdk_system_get_time()) < start_time)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gpio_write(uart->tx_pin, 1);
|
||||||
|
sdk_os_delay_us(uart->bit_time * 6);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool softuart_puts(uint8_t uart_no, const char *s)
|
||||||
|
{
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (!softuart_put(uart_no, *s++))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool softuart_available(uint8_t uart_no)
|
||||||
|
{
|
||||||
|
if (!check_uart_no(uart_no)) return false;
|
||||||
|
if (!check_uart_enabled(uart_no)) return false;
|
||||||
|
softuart_t *uart = uarts + uart_no;
|
||||||
|
|
||||||
|
return (uart->buffer.receive_buffer_tail + SOFTUART_MAX_RX_BUFF - uart->buffer.receive_buffer_head) % SOFTUART_MAX_RX_BUFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t softuart_read(uint8_t uart_no)
|
||||||
|
{
|
||||||
|
if (!check_uart_no(uart_no)) return 0;
|
||||||
|
if (!check_uart_enabled(uart_no)) return 0;
|
||||||
|
softuart_t *uart = uarts + uart_no;
|
||||||
|
|
||||||
|
// Empty buffer?
|
||||||
|
if (uart->buffer.receive_buffer_head == uart->buffer.receive_buffer_tail) return 0;
|
||||||
|
|
||||||
|
// Read from "head"
|
||||||
|
uint8_t d = uart->buffer.receive_buffer[uart->buffer.receive_buffer_head]; // grab next byte
|
||||||
|
uart->buffer.receive_buffer_head = (uart->buffer.receive_buffer_head + 1) % SOFTUART_MAX_RX_BUFF;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
93
extras/softuart/softuart.h
Normal file
93
extras/softuart/softuart.h
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Softuart for esp-open-rtos
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Ruslan V. Uss <unclerus@gmail.com>
|
||||||
|
* Copyright (C) 2016 Bernhard Guillon <Bernhard.Guillon@web.de>
|
||||||
|
*
|
||||||
|
* This code is based on Softuart from here [1] and reworked to
|
||||||
|
* fit into esp-open-rtos. For now only the RX part is ported.
|
||||||
|
* Also the configuration of the pin is for now hardcoded.
|
||||||
|
*
|
||||||
|
* it fits my needs to read the GY-GPS6MV2 module with 9600 8n1
|
||||||
|
*
|
||||||
|
* Original Copyright:
|
||||||
|
* Copyright (c) 2015 plieningerweb
|
||||||
|
*
|
||||||
|
* MIT Licensed as described in the file LICENSE
|
||||||
|
*
|
||||||
|
* 1 https://github.com/plieningerweb/esp8266-software-uart
|
||||||
|
*/
|
||||||
|
#ifndef SOFTUART_H_
|
||||||
|
#define SOFTUART_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOFTUART_MAX_UARTS
|
||||||
|
#define SOFTUART_MAX_UARTS 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOFTUART_MAX_RX_BUFF
|
||||||
|
#define SOFTUART_MAX_RX_BUFF 64 //!< Must be power of two: 2, 4, 8, 16 etc.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize software uart and setup interrupt handler
|
||||||
|
* @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS
|
||||||
|
* @param baudrate Baudrate, e.g. 9600, 19200, etc
|
||||||
|
* @param rx_pin GPIO pin number for RX
|
||||||
|
* @param tx_pin GPIO pin number for TX
|
||||||
|
* @return true if no errors occured otherwise false
|
||||||
|
*/
|
||||||
|
bool softuart_open(uint8_t uart_no, uint32_t baudrate, uint8_t rx_pin, uint8_t tx_pin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitialize software uart
|
||||||
|
* @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS
|
||||||
|
* @return true if no errors occured otherwise false
|
||||||
|
*/
|
||||||
|
bool softuart_close(uint8_t uart_no);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put char to software uart
|
||||||
|
* @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS
|
||||||
|
* @param c Char
|
||||||
|
* @return true if no errors occured otherwise false
|
||||||
|
*/
|
||||||
|
bool softuart_put(uint8_t uart_no, char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put string to software uart
|
||||||
|
* @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS
|
||||||
|
* @param s Null-terminated string
|
||||||
|
* @return true if no errors occured otherwise false
|
||||||
|
*/
|
||||||
|
bool softuart_puts(uint8_t uart_no, const char *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if data is available
|
||||||
|
* @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS
|
||||||
|
* @return true if data is available otherwise false
|
||||||
|
*/
|
||||||
|
bool softuart_available(uint8_t uart_no);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read current byte from internal buffer if available.
|
||||||
|
*
|
||||||
|
* NOTE: This call is non blocking.
|
||||||
|
* NOTE: You have to check softuart_available() first.
|
||||||
|
* @param uart_no Software uart index, 0..SOFTUART_MAX_UARTS
|
||||||
|
* @return current byte if available otherwise 0
|
||||||
|
*/
|
||||||
|
uint8_t softuart_read(uint8_t uart_no);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SOFTUART_H_ */
|
Loading…
Reference in a new issue