diff --git a/swuart/Makefile b/swuart/Makefile index bd56ff6..4eec199 100644 --- a/swuart/Makefile +++ b/swuart/Makefile @@ -1,5 +1,5 @@ CC = mipsel-openwrt-linux-gcc -CFLAGS = -g -Wall -I../libubb/include +CFLAGS = -g -Wall -O9 -I../libubb/include LDFLAGS = -static LDLIBS = -L../libubb -lubb diff --git a/swuart/swuart.c b/swuart/swuart.c index 4310be1..b5f380c 100644 --- a/swuart/swuart.c +++ b/swuart/swuart.c @@ -1,5 +1,5 @@ /* - * swuart/swuart.c - Software-implemented half-duplex UART for UBB + * swuart/swuart.c - Software-implemented UART for UBB * * Written 2012 by Werner Almesberger * Copyright 2012 Werner Almesberger @@ -11,38 +11,221 @@ */ +#include #include +#include #include +#include #include #include -#define TX UBB_DAT2 +#define RX UBB_DAT0 +#define TX UBB_DAT1 +#define TIMER 7 static uint32_t tx_mask, rx_mask; -static int loops; +static uint32_t ticks; + + +/* ----- Hardware timer ---------------------------------------------------- */ /* - * TX bit timing: - * - * bps loops nominal estimated measured error - * 1152200 202 8.696 us 8.673 us 8.67 us -0.3% - * 38400 619 26.042 us 26.062 us 26.04 us n/a - * 9600 2494 104.167 us 104.250 us 104.16 us n/a - * - * Estimated loop timing: 0.25+0.0417*loops + * We run the hardware timer at PCLK/16 = 7 MHz. This gives us 60 cycles at + * 115200 bps and sets the lowest data rate to 106.8 bps. */ +#define PCLK (112*1000*1000) /* 112 MHz */ +#define TCLK (PCLK/16) /* 7 MHz */ + + +static void get_timer(uint32_t cycle) +{ + TSCR = 1 << TIMER; /* enable clock to timer */ + TDFR(TIMER) = cycle-1; /* cycle = one bit time */ + TCSR(TIMER) = (2 << 3) | 1; /* count at PCLK/16 */ +} + + +static void reset_timer(void) +{ + TECR = 1 << TIMER; /* disable timer */ + TCNT(TIMER) = 0; /* reset timer */ + TFCR = 0x10001 << TIMER; /* clear flags */ + TESR = 1 << TIMER; /* enable timer */ +} + + +static void set_half_cycle(uint32_t cycle) +{ + uint32_t t; + + t = (uint16_t) TCNT(TIMER)+(cycle >> 1); + /* TCNT+cycle/2 mod cycle-1 */ + if (t >= cycle-1) + t -= cycle-1; + TDHR(TIMER) = t; + TFCR = 0x10000 << TIMER; /* clear half flags */ +} + + +static void release_timer(void) +{ + TECR = 1 << TIMER; /* disable timer */ + TFCR = 0x10001 << TIMER; /* clear flags */ + TSSR = 1 << TIMER; /* disable clock to timer */ +} + + +static inline bool timer_full(void) +{ + if (!(TFR & (1 << TIMER))) + return 0; + TFCR = 1 << TIMER; + return 1; +} + + +static inline bool timer_half(void) +{ + if (!(TFR & (0x10000 << TIMER))) + return 0; + TFCR = 0x10000 << TIMER; + return 1; +} + + +/* ----- Enable/disable interrupts ----------------------------------------- */ + + +static uint32_t old_icmr; + + +static void disable_interrupts(void) +{ + old_icmr = ICMR; + ICMSR = 0xffffffff; +} + + +static void enable_interrupts(void) +{ + ICMCR = ~old_icmr; +} + + +/* ----- Transmit a block -------------------------------------------------- */ + + +struct rx_err { + unsigned glitch; /* unstable start bit */ + unsigned framing; /* stop bit wasn't "1" */ + unsigned overflow; /* buffer overflow */ +} rx_err; + + +static int trx(uint8_t *out, int out_size, uint8_t *in, int in_size, + unsigned wait_bits, unsigned idle_bits) +{ + unsigned tx, rx = 0, idle = 0; + int tx_bits = 0, rx_bits = 0; + int in_len = 0; + + reset_timer(); + while (1) { +full: + /* timer has just crossed FULL */ + + if (tx_bits) { + if (tx & 1) + SET(tx_mask); + else + CLR(tx_mask); + tx >>= 1; + tx_bits--; + } else { + if (out_size) { + CLR(tx_mask); + tx = *out++ | 0x100; + out_size--; + tx_bits = 9; + } else { + if (idle++ == wait_bits) + return in_len; + } + } + + /* wait for reception (if any) */ + + if (rx_bits) { + while (!timer_half()); + } else { + while (!timer_full()) + if (!PIN(rx_mask)) { + set_half_cycle(ticks); + rx_bits = 10; + break; + } + if (!rx_bits) + continue; + while (!timer_half()) + if (timer_full()) + goto full; + } + + /* timer has just crossed HALF */ + + rx = (rx >> 1) | (PIN(rx_mask) ? 0x200 : 0); + if (!--rx_bits) { + if (rx & 1) { + rx_err.glitch++; + } else if (!(rx & 0x200)) { + rx_err.framing++; + } else if (in_len == in_size) { + rx_err.overflow++; + } else { + *in++ = rx >> 1; + in_len++; + idle = 0; + wait_bits = idle_bits; + } + } + + while (!timer_full()); + } +} + + +int swuart_trx(void *out, int out_size, void *in, int in_size, + int wait_bits, int idle_bits) +{ + int got; + + disable_interrupts(); + get_timer(ticks); + + got = trx(out, out_size, in, in_size, wait_bits, idle_bits); + + release_timer(); + enable_interrupts(); + + return got; +} + + +/* ----- Open/close the software UART -------------------------------------- */ + + int swuart_open(uint32_t tx, uint32_t rx, int bps) { if (ubb_open(0)) return -1; - loops = 24000000/bps-6; + ticks = TCLK/bps-1; tx_mask = tx; rx_mask = rx; @@ -55,47 +238,39 @@ int swuart_open(uint32_t tx, uint32_t rx, int bps) } -static void send(uint8_t *out, int out_size) +void swuart_close(void) { - const uint8_t *end = out+out_size; - unsigned pattern; - int i, j; - - while (out != end) { - pattern = 0x200 | (*out++ << 1); - for (i = 0; i != 10; i++) { - if (pattern & 1) - SET(tx_mask); - else - CLR(tx_mask); - pattern >>= 1; - for (j = 0; j != loops; j++); - } - } + ubb_close(0); } -int swuart_trx(void *out, int out_size, void *in, int in_size, int bit_wait) +/* ----- Main -------------------------------------------------------------- */ + + +static void at_exit(void) { - uint32_t icmr; - - icmr = ICMR; - ICMSR = 0xffffffff; - - send(out, out_size); - - ICMCR = ~icmr; - - return 0; + swuart_close(); } int main(int argc, char **argv) { - swuart_open(TX, 0, atoi(argv[1])); + uint8_t buf[40]; + int got, i; + + swuart_open(TX, RX, atoi(argv[1])); + atexit(at_exit); while (1) { - swuart_trx("U ", 2, NULL, 0, 0); + got = swuart_trx(argv[2], strlen(argv[2]), buf, sizeof(buf), + 10000, 100); + printf("%d (%d %d %d): ", got, + rx_err.glitch, rx_err.framing, rx_err.overflow); + for (i = 0; i != got; i++) + if (buf[i] >= ' ' && buf[i] <= '~') + printf("%c", buf[i]); + else + printf("\\%02o", buf[i]); + printf("\n"); usleep(100); } - ubb_close(0); }