mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
drivers/soft_uart: add software based UART implementation
This commit is contained in:
parent
60def88929
commit
96c67b0fa5
@ -760,6 +760,11 @@ ifneq (,$(filter soft_spi,$(USEMODULE)))
|
|||||||
USEMODULE += xtimer
|
USEMODULE += xtimer
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter soft_uart,$(USEMODULE)))
|
||||||
|
FEATURES_REQUIRED += periph_gpio_irq
|
||||||
|
FEATURES_REQUIRED += periph_timer_periodic
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter sps30,$(USEMODULE)))
|
ifneq (,$(filter sps30,$(USEMODULE)))
|
||||||
FEATURES_REQUIRED += periph_i2c
|
FEATURES_REQUIRED += periph_i2c
|
||||||
USEMODULE += checksum
|
USEMODULE += checksum
|
||||||
|
@ -364,6 +364,10 @@ ifneq (,$(filter soft_spi,$(USEMODULE)))
|
|||||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/soft_spi/include
|
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/soft_spi/include
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter soft_uart,$(USEMODULE)))
|
||||||
|
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/soft_uart/include
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter sps30,$(USEMODULE)))
|
ifneq (,$(filter sps30,$(USEMODULE)))
|
||||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sps30/include
|
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sps30/include
|
||||||
endif
|
endif
|
||||||
|
130
drivers/include/soft_uart.h
Normal file
130
drivers/include/soft_uart.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 ML!PA Consulting GmbH
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup drivers_soft_uart Software UART
|
||||||
|
* @ingroup drivers_soft_periph
|
||||||
|
* @brief Software implemented UART
|
||||||
|
*
|
||||||
|
* This module provides a software implemented Universal Asynchronous Receiver Transmitter.
|
||||||
|
* It is intended to be used in situation where hardware UART is not available.
|
||||||
|
* The signatures of the functions are similar to the functions declared in uart.h
|
||||||
|
*
|
||||||
|
* Currently sending and receiving is not possible at the same time, so loopback operation
|
||||||
|
* is not possible.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Software UART port descriptor definition
|
||||||
|
*
|
||||||
|
* @author Benjamin Valentin <benjjamin.valentin@ml-pa.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOFT_UART_H
|
||||||
|
#define SOFT_UART_H
|
||||||
|
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
#include "periph/uart.h"
|
||||||
|
#include "periph/timer.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Software UART port descriptor
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
gpio_t rx_pin; /**< RX pin */
|
||||||
|
gpio_t tx_pin; /**< TX pin */
|
||||||
|
tim_t rx_timer; /**< Hardware timer used for RX */
|
||||||
|
tim_t tx_timer; /**< Hardware timer used for TX */
|
||||||
|
uint32_t timer_freq; /**< Operating frequency of the timer.
|
||||||
|
Should be a multiple of baudrate */
|
||||||
|
} soft_uart_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Software UART type definition
|
||||||
|
*/
|
||||||
|
typedef unsigned soft_uart_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a given UART device
|
||||||
|
*
|
||||||
|
* The UART device will be initialized with the following configuration:
|
||||||
|
* - 8 data bits
|
||||||
|
* - no parity
|
||||||
|
* - 1 stop bit
|
||||||
|
* - baudrate as given
|
||||||
|
*
|
||||||
|
* If no callback parameter is given (rx_cb := NULL), the UART will be
|
||||||
|
* initialized in TX only mode.
|
||||||
|
*
|
||||||
|
* @param[in] uart UART device to initialize
|
||||||
|
* @param[in] baudrate desired symbol rate in baud
|
||||||
|
* @param[in] rx_cb receive callback, executed in interrupt context once
|
||||||
|
* for every byte that is received (RX buffer filled),
|
||||||
|
* set to NULL for TX only mode
|
||||||
|
* @param[in] arg optional context passed to the callback functions
|
||||||
|
*
|
||||||
|
* @return UART_OK on success
|
||||||
|
* @return UART_NODEV on invalid UART device
|
||||||
|
* @return UART_NOBAUD on inapplicable baudrate
|
||||||
|
* @return UART_INTERR on other errors
|
||||||
|
*/
|
||||||
|
int soft_uart_init(soft_uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup parity, data and stop bits for a given UART device
|
||||||
|
*
|
||||||
|
* @param[in] uart UART device to configure
|
||||||
|
* @param[in] data_bits number of data bits in a UART frame
|
||||||
|
* @param[in] parity parity mode
|
||||||
|
* @param[in] stop_bits number of stop bits in a UART frame
|
||||||
|
*
|
||||||
|
* @return UART_OK on success
|
||||||
|
* @return UART_NOMODE on other errors
|
||||||
|
*/
|
||||||
|
int soft_uart_mode(soft_uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity,
|
||||||
|
uart_stop_bits_t stop_bits);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write data from the given buffer to the specified UART device
|
||||||
|
*
|
||||||
|
* This function is blocking, as it will only return after @p len bytes from the
|
||||||
|
* given buffer have been send. The way this data is send is up to the
|
||||||
|
* implementation: active waiting, interrupt driven, DMA, etc.
|
||||||
|
*
|
||||||
|
* @param[in] uart UART device to use for transmission
|
||||||
|
* @param[in] data data buffer to send
|
||||||
|
* @param[in] len number of bytes to send
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void soft_uart_write(soft_uart_t uart, const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Power on the given UART device
|
||||||
|
*
|
||||||
|
* @param[in] uart the UART device to power on
|
||||||
|
*/
|
||||||
|
void soft_uart_poweron(soft_uart_t uart);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Power off the given UART device
|
||||||
|
*
|
||||||
|
* @param[in] uart the UART device to power off
|
||||||
|
*/
|
||||||
|
void soft_uart_poweroff(soft_uart_t uart);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SOFT_UART_H */
|
||||||
|
/** @} */
|
1
drivers/soft_uart/Makefile
Normal file
1
drivers/soft_uart/Makefile
Normal file
@ -0,0 +1 @@
|
|||||||
|
include $(RIOTBASE)/Makefile.base
|
67
drivers/soft_uart/include/soft_uart_params.h
Normal file
67
drivers/soft_uart/include/soft_uart_params.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 ML!PA Consulting GmbH
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup drivers_soft_uart
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Software UART configuration
|
||||||
|
*
|
||||||
|
* @author Benjamin Valentin <benjjamin.valentin@ml-pa.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOFT_UART_PARAMS_H
|
||||||
|
#define SOFT_UART_PARAMS_H
|
||||||
|
|
||||||
|
#include "soft_uart.h"
|
||||||
|
#include "macros/units.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOFT_UART_PARAM_RX
|
||||||
|
#define SOFT_UART_PARAM_RX GPIO_UNDEF
|
||||||
|
#endif
|
||||||
|
#ifndef SOFT_UART_PARAM_TX
|
||||||
|
#define SOFT_UART_PARAM_TX GPIO_UNDEF
|
||||||
|
#endif
|
||||||
|
#ifndef SOFT_UART_PARAM_TIMER_RX
|
||||||
|
#define SOFT_UART_PARAM_TIMER_RX (0)
|
||||||
|
#endif
|
||||||
|
#ifndef SOFT_UART_PARAM_TIMER_TX
|
||||||
|
#define SOFT_UART_PARAM_TIMER_TX (1)
|
||||||
|
#endif
|
||||||
|
#ifndef SOFT_UART_PARAM_FREQ
|
||||||
|
#define SOFT_UART_PARAM_FREQ MHZ(1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOFT_UART_PARAMS
|
||||||
|
#define SOFT_UART_PARAMS { .rx_pin = SOFT_UART_PARAM_RX, \
|
||||||
|
.tx_pin = SOFT_UART_PARAM_TX, \
|
||||||
|
.rx_timer = SOFT_UART_PARAM_TIMER_RX, \
|
||||||
|
.tx_timer = SOFT_UART_PARAM_TIMER_TX, \
|
||||||
|
.timer_freq = SOFT_UART_PARAM_FREQ }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sotware UART port descriptor array
|
||||||
|
*/
|
||||||
|
static const soft_uart_conf_t soft_uart_config[] = {
|
||||||
|
SOFT_UART_PARAMS,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SOFT_UART_NUMOF ARRAY_SIZE(soft_uart_config)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SOFT_UART_PARAMS_H */
|
||||||
|
/** @} */
|
329
drivers/soft_uart/soft_uart.c
Normal file
329
drivers/soft_uart/soft_uart.c
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 ML!PA Consulting GmbH
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup drivers_soft_uart
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Software UART implementation
|
||||||
|
*
|
||||||
|
* @author Benjamin Valentin <benjjamin.valentin@ml-pa.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "soft_uart.h"
|
||||||
|
#include "soft_uart_params.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STATE_RX_IDLE,
|
||||||
|
STATE_RX_HIGH,
|
||||||
|
STATE_RX_LOW
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PARITY_NONE,
|
||||||
|
PARITY_EVEN,
|
||||||
|
PARITY_ODD,
|
||||||
|
PARITY_MARK,
|
||||||
|
PARITY_SPACE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct uart_ctx {
|
||||||
|
mutex_t lock; /**< UART mutex */
|
||||||
|
mutex_t sync; /**< TX byte done signal */
|
||||||
|
uart_rx_cb_t rx_cb; /**< RX callback */
|
||||||
|
void* rx_cb_arg; /**< RX callback arg */
|
||||||
|
uint32_t bit_time; /**< timer ticks per bit */
|
||||||
|
uint16_t byte_tx; /**< current TX byte */
|
||||||
|
uint16_t byte_rx; /**< curretn RX byte */
|
||||||
|
uint8_t bits_tx; /**< TX bit pos */
|
||||||
|
uint8_t state_rx; /**< RX state */
|
||||||
|
#ifdef MODULE_SOFT_UART_MODECFG
|
||||||
|
uint8_t data_bits; /**< number of data bits */
|
||||||
|
uint8_t stop_bits; /**< number of stop bits */
|
||||||
|
uint8_t parity; /**< parity mode */
|
||||||
|
#endif
|
||||||
|
} soft_uart_ctx[SOFT_UART_NUMOF];
|
||||||
|
|
||||||
|
#ifdef MODULE_SOFT_UART_MODECFG
|
||||||
|
#define BITS_DATA(ctx) (ctx)->data_bits
|
||||||
|
#define BITS_STOP(ctx) (ctx)->stop_bits
|
||||||
|
#define BITS_PARITY(ctx) ((ctx)->parity != PARITY_NONE)
|
||||||
|
#else
|
||||||
|
#define BITS_DATA(ctx) 8
|
||||||
|
#define BITS_STOP(ctx) 1
|
||||||
|
#define BITS_PARITY(ctx) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void _tx_timer_cb(void *arg, int chan)
|
||||||
|
{
|
||||||
|
soft_uart_t uart = (soft_uart_t)arg;
|
||||||
|
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
gpio_write(cfg->tx_pin, ctx->byte_tx & 1);
|
||||||
|
ctx->byte_tx >>= 1;
|
||||||
|
|
||||||
|
if (--ctx->bits_tx == 0) {
|
||||||
|
timer_clear(cfg->tx_timer, chan);
|
||||||
|
mutex_unlock(&ctx->sync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _rx_timer_cb(void *arg, int chan)
|
||||||
|
{
|
||||||
|
soft_uart_t uart = (soft_uart_t)arg;
|
||||||
|
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
(void)chan;
|
||||||
|
|
||||||
|
timer_stop(cfg->rx_timer);
|
||||||
|
|
||||||
|
/* ignore spurious interrupts */
|
||||||
|
if (ctx->state_rx == STATE_RX_IDLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->state_rx = STATE_RX_IDLE;
|
||||||
|
ctx->rx_cb(ctx->rx_cb_arg, ctx->byte_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _rx_gpio_cb(void *arg)
|
||||||
|
{
|
||||||
|
soft_uart_t uart = (soft_uart_t)arg;
|
||||||
|
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
/* TODO: use Timer Capture feature */
|
||||||
|
const uint32_t now = timer_read(cfg->rx_timer);
|
||||||
|
|
||||||
|
if (ctx->state_rx == STATE_RX_IDLE) {
|
||||||
|
timer_start(cfg->rx_timer);
|
||||||
|
ctx->state_rx = STATE_RX_LOW;
|
||||||
|
ctx->byte_rx = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we only get interrupts on flanks, so all bits
|
||||||
|
* till the next interrupt will have the same level. */
|
||||||
|
uint8_t bit = now / ctx->bit_time;
|
||||||
|
uint8_t mask = 0xff << bit;
|
||||||
|
|
||||||
|
if (ctx->state_rx == STATE_RX_HIGH) {
|
||||||
|
ctx->byte_rx &= ~mask;
|
||||||
|
ctx->state_rx = STATE_RX_LOW;
|
||||||
|
} else {
|
||||||
|
ctx->byte_rx |= mask;
|
||||||
|
ctx->state_rx = STATE_RX_HIGH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int soft_uart_init(soft_uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
|
||||||
|
{
|
||||||
|
if (uart >= SOFT_UART_NUMOF) {
|
||||||
|
return UART_NODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
mutex_init(&ctx->lock);
|
||||||
|
static const mutex_t init_locked = MUTEX_INIT_LOCKED;
|
||||||
|
ctx->sync = init_locked;
|
||||||
|
|
||||||
|
ctx->bit_time = (cfg->timer_freq + baudrate / 2) / baudrate;
|
||||||
|
|
||||||
|
unsigned accuracy = (100 * cfg->timer_freq / ctx->bit_time) / baudrate;
|
||||||
|
if (accuracy > 110 || accuracy < 90) {
|
||||||
|
return UART_NOBAUD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->rx_pin == GPIO_UNDEF) {
|
||||||
|
rx_cb = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MODULE_SOFT_UART_MODECFG
|
||||||
|
ctx->data_bits = 8;
|
||||||
|
ctx->stop_bits = 1;
|
||||||
|
ctx->parity = PARITY_NONE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ctx->rx_cb = rx_cb;
|
||||||
|
ctx->rx_cb_arg = arg;
|
||||||
|
|
||||||
|
ctx->state_rx = STATE_RX_IDLE;
|
||||||
|
|
||||||
|
if (cfg->tx_pin != GPIO_UNDEF) {
|
||||||
|
timer_init(cfg->tx_timer, cfg->timer_freq, _tx_timer_cb, (void *)uart);
|
||||||
|
gpio_write(cfg->tx_pin, !(cfg->flags & SOFT_UART_FLAG_INVERT_TX));
|
||||||
|
gpio_init(cfg->tx_pin, GPIO_OUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx_cb) {
|
||||||
|
timer_init(cfg->rx_timer, cfg->timer_freq, _rx_timer_cb, (void *)uart);
|
||||||
|
timer_stop(cfg->rx_timer);
|
||||||
|
/* timer should fire at the end of the byte */
|
||||||
|
timer_set_periodic(cfg->rx_timer, 0, ctx->bit_time * (BITS_DATA(ctx) + BITS_PARITY(ctx) + 1),
|
||||||
|
TIM_FLAG_RESET_ON_MATCH | TIM_FLAG_RESET_ON_SET);
|
||||||
|
gpio_init_int(cfg->rx_pin, GPIO_IN, GPIO_BOTH, _rx_gpio_cb, (void*) uart);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MODULE_SOFT_UART_MODECFG
|
||||||
|
int soft_uart_mode(soft_uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity,
|
||||||
|
uart_stop_bits_t stop_bits)
|
||||||
|
{
|
||||||
|
if (uart >= SOFT_UART_NUMOF) {
|
||||||
|
return UART_NODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
switch (data_bits) {
|
||||||
|
case UART_DATA_BITS_5:
|
||||||
|
ctx->data_bits = 5;
|
||||||
|
break;
|
||||||
|
case UART_DATA_BITS_6:
|
||||||
|
ctx->data_bits = 6;
|
||||||
|
break;
|
||||||
|
case UART_DATA_BITS_7:
|
||||||
|
ctx->data_bits = 7;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case UART_DATA_BITS_8:
|
||||||
|
ctx->data_bits = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (parity) {
|
||||||
|
case UART_PARITY_EVEN:
|
||||||
|
ctx->parity = PARITY_EVEN;
|
||||||
|
break;
|
||||||
|
case UART_PARITY_ODD:
|
||||||
|
ctx->parity = PARITY_ODD;
|
||||||
|
break;
|
||||||
|
case UART_PARITY_MARK:
|
||||||
|
ctx->parity = PARITY_MARK;
|
||||||
|
break;
|
||||||
|
case UART_PARITY_SPACE:
|
||||||
|
ctx->parity = PARITY_SPACE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case UART_PARITY_NONE:
|
||||||
|
ctx->parity = PARITY_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (stop_bits) {
|
||||||
|
case UART_STOP_BITS_2:
|
||||||
|
ctx->stop_bits = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case UART_STOP_BITS_1:
|
||||||
|
ctx->stop_bits = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* MODULE_SOFT_UART_MODECF */
|
||||||
|
|
||||||
|
static void soft_uart_write_byte(soft_uart_t uart, uint8_t data)
|
||||||
|
{
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
/* start bit (LOW) + data bits */
|
||||||
|
ctx->bits_tx = 1 + BITS_DATA(ctx);
|
||||||
|
ctx->byte_tx = data << 1;
|
||||||
|
|
||||||
|
#ifdef MODULE_SOFT_UART_MODECFG
|
||||||
|
if (ctx->parity != PARITY_NONE) {
|
||||||
|
uint8_t parity = 0;
|
||||||
|
|
||||||
|
switch (ctx->parity) {
|
||||||
|
case PARITY_EVEN:
|
||||||
|
parity = __builtin_parity(data);
|
||||||
|
break;
|
||||||
|
case PARITY_ODD:
|
||||||
|
parity = !__builtin_parity(data);
|
||||||
|
break;
|
||||||
|
case PARITY_MARK:
|
||||||
|
parity = 1;
|
||||||
|
break;
|
||||||
|
case PARITY_SPACE:
|
||||||
|
parity = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->byte_tx |= parity << ctx->bits_tx++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < BITS_STOP(ctx); ++i) {
|
||||||
|
ctx->byte_tx |= 1 << ctx->bits_tx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->flags & SOFT_UART_FLAG_INVERT_TX) {
|
||||||
|
ctx->byte_tx = ~ctx->byte_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_set_periodic(cfg->tx_timer, 0, ctx->bit_time,
|
||||||
|
TIM_FLAG_RESET_ON_MATCH | TIM_FLAG_RESET_ON_SET);
|
||||||
|
mutex_lock(&ctx->sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
void soft_uart_write(uart_t uart, const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
const uint8_t *end = data + len;
|
||||||
|
|
||||||
|
mutex_lock(&ctx->lock);
|
||||||
|
timer_start(cfg->tx_timer);
|
||||||
|
|
||||||
|
while (data != end) {
|
||||||
|
soft_uart_write_byte(uart, *data++);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_stop(cfg->tx_timer);
|
||||||
|
mutex_unlock(&soft_uart_ctx[uart].lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void soft_uart_poweron(soft_uart_t uart)
|
||||||
|
{
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
if (ctx->rx_cb) {
|
||||||
|
gpio_irq_enable(cfg->rx_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void soft_uart_poweroff(soft_uart_t uart)
|
||||||
|
{
|
||||||
|
const soft_uart_conf_t *cfg = &soft_uart_config[uart];
|
||||||
|
struct uart_ctx *ctx = &soft_uart_ctx[uart];
|
||||||
|
|
||||||
|
if (ctx->rx_cb) {
|
||||||
|
gpio_irq_disable(cfg->rx_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* timers are already stopped after RX/TX */
|
||||||
|
}
|
@ -105,6 +105,7 @@ PSEUDOMODULES += sock_dtls
|
|||||||
PSEUDOMODULES += sock_ip
|
PSEUDOMODULES += sock_ip
|
||||||
PSEUDOMODULES += sock_tcp
|
PSEUDOMODULES += sock_tcp
|
||||||
PSEUDOMODULES += sock_udp
|
PSEUDOMODULES += sock_udp
|
||||||
|
PSEUDOMODULES += soft_uart_modecfg
|
||||||
PSEUDOMODULES += stdin
|
PSEUDOMODULES += stdin
|
||||||
PSEUDOMODULES += stdio_ethos
|
PSEUDOMODULES += stdio_ethos
|
||||||
PSEUDOMODULES += stdio_cdc_acm
|
PSEUDOMODULES += stdio_cdc_acm
|
||||||
|
Loading…
Reference in New Issue
Block a user