blob: 0963c79ef5552c7ae4799f3ce7853235cd7c2b9a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "os/mynewt.h"
#include <hal/hal_gpio.h>
#include <hal/hal_uart.h>
#include <hal/hal_timer.h>
#include <uart/uart.h>
#include "uart_bitbang/uart_bitbang.h"
/*
* Async UART as a bitbanger.
* Cannot run very fast, as it relies on cputimer to time sampling and
* bit tx start times.
*/
struct uart_bitbang {
int ub_bittime; /* number of cputimer ticks per bit */
struct {
int pin; /* RX pin */
struct hal_timer timer;
uint32_t start; /* cputime when byte rx started */
uint8_t byte; /* receiving this byte */
uint8_t bits; /* how many bits we've seen */
int false_irq;
} ub_rx;
struct {
int pin; /* TX pin */
struct hal_timer timer;
uint32_t start; /* cputime when byte tx started */
uint8_t byte; /* byte being transmitted */
uint8_t bits; /* how many bits have been sent */
} ub_tx;
uint8_t ub_open:1;
uint8_t ub_rx_stall:1;
uint8_t ub_txing:1;
uint32_t ub_cputimer_freq;
hal_uart_rx_char ub_rx_func;
hal_uart_tx_char ub_tx_func;
hal_uart_tx_done ub_tx_done;
void *ub_func_arg;
};
/*
* Bytes start with START bit (0) followed by 8 data bits and then the
* STOP bit (1). STOP bit should be configurable. Data bits are sent LSB first.
*/
static void
uart_bitbang_tx_timer(void *arg)
{
struct uart_bitbang *ub = (struct uart_bitbang *)arg;
uint32_t next = 0;
int data;
if (!ub->ub_txing || ub->ub_tx.bits > 9) {
if (ub->ub_tx.bits > 9) {
if (ub->ub_tx_done) {
ub->ub_tx_done(ub->ub_func_arg);
}
}
data = ub->ub_tx_func(ub->ub_func_arg);
if (data < 0) {
ub->ub_txing = 0;
return;
} else {
ub->ub_tx.byte = data;
}
/*
* Start bit
*/
hal_gpio_write(ub->ub_tx.pin, 0);
ub->ub_tx.start = os_cputime_get32();
next = ub->ub_tx.start + ub->ub_bittime;
ub->ub_txing = 1;
ub->ub_tx.bits = 0;
} else {
if (ub->ub_tx.bits++ < 8) {
hal_gpio_write(ub->ub_tx.pin, ub->ub_tx.byte & 0x01);
ub->ub_tx.byte = ub->ub_tx.byte >> 1;
next = ub->ub_tx.start + (ub->ub_bittime * (ub->ub_tx.bits + 1));
} else {
/*
* STOP bit.
*/
hal_gpio_write(ub->ub_tx.pin, 1);
next = ub->ub_tx.start + (ub->ub_bittime * 10);
}
}
os_cputime_timer_start(&ub->ub_tx.timer, next);
}
static void
uart_bitbang_rx_timer(void *arg)
{
struct uart_bitbang *ub = (struct uart_bitbang *)arg;
int val;
val = hal_gpio_read(ub->ub_rx.pin);
if (val) {
ub->ub_rx.byte = 0x80 | (ub->ub_rx.byte >> 1);
} else {
ub->ub_rx.byte = (ub->ub_rx.byte >> 1);
}
if (ub->ub_rx.bits == 7) {
val = ub->ub_rx_func(ub->ub_func_arg, ub->ub_rx.byte);
if (val) {
ub->ub_rx_stall = 1;
} else {
/*
* Re-enable GPIO IRQ after we've sampled last bit. STOP bit
* is ignored.
*/
hal_gpio_irq_enable(ub->ub_rx.pin);
}
} else {
ub->ub_rx.bits++;
os_cputime_timer_start(&ub->ub_rx.timer,
ub->ub_rx.start + (ub->ub_bittime * (ub->ub_rx.bits + 1)) +
(ub->ub_bittime >> 1));
}
}
/*
* Byte RX starts when we get transition from high to low.
* We disable RX irq after we've seen start until end of RX of one byte.
*/
static void
uart_bitbang_isr(void *arg)
{
struct uart_bitbang *ub = (struct uart_bitbang *)arg;
uint32_t time;
time = os_cputime_get32();
if (ub->ub_rx.start - time < (9 * ub->ub_bittime)) {
++ub->ub_rx.false_irq;
return;
}
ub->ub_rx.start = time;
ub->ub_rx.byte = 0;
ub->ub_rx.bits = 0;
/*
* We try to sample in the middle of a bit. First sample is taken
* 1.5 bittimes after beginning of start bit.
*/
os_cputime_timer_start(&ub->ub_rx.timer, time +
ub->ub_bittime + (ub->ub_bittime >> 1));
hal_gpio_irq_disable(ub->ub_rx.pin);
}
static void
uart_bitbang_blocking_tx(struct uart_dev *dev, uint8_t data)
{
struct uart_bitbang *ub;
int i;
uint32_t start, next;
ub = (struct uart_bitbang *)dev->ud_priv;
if (!ub->ub_open) {
return;
}
hal_gpio_write(ub->ub_tx.pin, 0);
start = os_cputime_get32();
next = start + ub->ub_bittime;
while (os_cputime_get32() < next);
for (i = 0; i < 8; i++) {
hal_gpio_write(ub->ub_tx.pin, data & 0x01);
data = data >> 1;
next = start + (ub->ub_bittime * i + 1);
while (os_cputime_get32() < next);
}
next = start + (ub->ub_bittime * 10);
hal_gpio_write(ub->ub_tx.pin, 1);
while (os_cputime_get32() < next);
}
static void
uart_bitbang_start_tx(struct uart_dev *dev)
{
struct uart_bitbang *ub;
int sr;
ub = (struct uart_bitbang *)dev->ud_priv;
if (!ub->ub_open) {
return;
}
if (!ub->ub_txing) {
OS_ENTER_CRITICAL(sr);
uart_bitbang_tx_timer(ub);
OS_EXIT_CRITICAL(sr);
}
}
static void
uart_bitbang_start_rx(struct uart_dev *dev)
{
struct uart_bitbang *ub;
int sr;
int rc;
ub = (struct uart_bitbang *)dev->ud_priv;
if (ub->ub_rx_stall) {
rc = ub->ub_rx_func(ub->ub_func_arg, ub->ub_rx.byte);
if (rc == 0) {
OS_ENTER_CRITICAL(sr);
ub->ub_rx_stall = 0;
OS_EXIT_CRITICAL(sr);
/*
* Start looking for start bit again.
*/
hal_gpio_irq_enable(ub->ub_rx.pin);
}
}
}
static int
uart_bitbang_config(struct uart_bitbang *ub, int32_t baudrate, uint8_t databits,
uint8_t stopbits, enum hal_uart_parity parity,
enum hal_uart_flow_ctl flow_ctl)
{
if (databits != 8 || parity != HAL_UART_PARITY_NONE ||
flow_ctl != HAL_UART_FLOW_CTL_NONE) {
return -1;
}
assert(ub->ub_rx.pin != ub->ub_tx.pin); /* make sure it's initialized */
if (baudrate > 19200) {
return -1;
}
ub->ub_bittime = ub->ub_cputimer_freq / baudrate;
os_cputime_timer_init(&ub->ub_rx.timer, uart_bitbang_rx_timer, ub);
os_cputime_timer_init(&ub->ub_tx.timer, uart_bitbang_tx_timer, ub);
if (hal_gpio_init_out(ub->ub_tx.pin, 1)) {
return -1;
}
if (hal_gpio_irq_init(ub->ub_rx.pin, uart_bitbang_isr, ub,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP)) {
return -1;
}
hal_gpio_irq_enable(ub->ub_rx.pin);
ub->ub_open = 1;
return 0;
}
static int
uart_bitbang_open(struct os_dev *odev, uint32_t wait, void *arg)
{
struct uart_dev *dev = (struct uart_dev *)odev;
struct uart_bitbang *ub;
struct uart_conf *uc;
ub = (struct uart_bitbang *)dev->ud_priv;
uc = (struct uart_conf *)arg;
ub->ub_rx_func = uc->uc_rx_char;
ub->ub_tx_func = uc->uc_tx_char;
ub->ub_tx_done = uc->uc_tx_done;
ub->ub_func_arg = uc->uc_cb_arg;
if (uart_bitbang_config(ub, uc->uc_speed, uc->uc_databits,
uc->uc_stopbits, uc->uc_parity, uc->uc_flow_ctl)) {
return OS_EINVAL;
}
return OS_OK;
}
static int
uart_bitbang_close(struct os_dev *odev)
{
struct uart_dev *dev = (struct uart_dev *)odev;
struct uart_bitbang *ub;
int sr;
ub = (struct uart_bitbang *)dev->ud_priv;
OS_ENTER_CRITICAL(sr);
hal_gpio_irq_disable(ub->ub_rx.pin);
hal_gpio_irq_release(ub->ub_rx.pin);
ub->ub_open = 0;
ub->ub_txing = 0;
ub->ub_rx_stall = 0;
os_cputime_timer_stop(&ub->ub_tx.timer);
os_cputime_timer_stop(&ub->ub_rx.timer);
OS_EXIT_CRITICAL(sr);
return OS_OK;
}
int
uart_bitbang_init(struct os_dev *odev, void *arg)
{
struct uart_dev *dev = (struct uart_dev *)odev;
struct uart_bitbang *ub;
struct uart_bitbang_conf *ubc;
ub = (struct uart_bitbang *)os_malloc(sizeof(struct uart_bitbang));
if (!ub) {
return OS_ENOMEM;
}
memset(ub, 0, sizeof(*ub));
ubc = (struct uart_bitbang_conf *)arg;
ub->ub_rx.pin = ubc->ubc_rxpin;
ub->ub_tx.pin = ubc->ubc_txpin;
ub->ub_cputimer_freq = ubc->ubc_cputimer_freq;
OS_DEV_SETHANDLERS(odev, uart_bitbang_open, uart_bitbang_close);
dev->ud_funcs.uf_start_tx = uart_bitbang_start_tx;
dev->ud_funcs.uf_start_rx = uart_bitbang_start_rx;
dev->ud_funcs.uf_blocking_tx = uart_bitbang_blocking_tx;
dev->ud_priv = ub;
return OS_OK;
}