Skip to content

Instantly share code, notes, and snippets.

@tai
Created March 8, 2015 13:59
Show Gist options
  • Select an option

  • Save tai/378c954ca8edfebf4ae1 to your computer and use it in GitHub Desktop.

Select an option

Save tai/378c954ca8edfebf4ae1 to your computer and use it in GitHub Desktop.
Software UART implementation for mbed (testing)
//
// LPC1114FN28+MBED software UART test
//
// This is a pure-mbed software UART implementation.
// I tested this with 9600-8n1 configuration on LPC1114FN28/102.
//
#include "mbed.h"
//
// Timing control parameters
//
// As it is hard to be clock-precise on mbed, timing control parameter
// for bit sampling (for RX) and bit toggling (for TX) is obtained
// experimentally. So it probably needs tweaking with other CPU.
//
// With 9600baud, bitclock is ~104[us/bit].
// However, due to mbed API overhead (overhead by attach_us and interrupt
// routing), I ended up attaching RX/TX function with ~85us delay.
//
// Here, TX bitspace != RX bitspace just to make my logic analyzer happy.
// It samples bits with different timing, and this change was needed to make
// every device (RX/TX on both sides + LA) recognize data as same value. YMMV.
//
#define TX_BITSPACE 86 /* make this + overhead == ~104us */
#define TX_BITSPACE_ONSTART 10 /* big enough to finish attach_us */
#define RX_BITSPACE 82 /* make this + overhead == ~104us */
#define RX_BITSPACE_ONRESET 1000 /* big enough to skip whole transfer */
#define RX_BITSPACE_ONSTART 110 /* big enough to skip START bit */
enum { LED_ON = 0, LED_OFF };
enum { IDLE = 1, STOP = 1, START = 0 };
DigitalOut led(P1_5);
// TX/RX bitclock must be separate as call to attach_us may compete.
Timeout rxclock, txclock;
InterruptIn rxint(P1_6);
DigitalOut tx(P1_7);
DigitalIn rx(P1_6);
static struct uart_buffer {
uint8_t buffer[16];
uint8_t ri, wi;
uint8_t cc;
struct {
bool in_process:1;
unsigned int data_len:4;
unsigned int stop_len:2;
unsigned int parity_len:1;
bool parity_even:1;
unsigned int parity:1;
bool frame_error:1;
bool parity_error:1;
bool overflow_error:1;
} state;
} rb, sb;
void send_bit() {
int bit;
if (sb.state.data_len) {
sb.state.data_len--;
bit = sb.cc & 0x01;
sb.cc >>= 1;
if (sb.state.parity_len) {
sb.state.parity ^= bit;
}
}
else if (sb.state.parity_len) {
sb.state.parity_len--;
bit = sb.state.parity ^ sb.state.parity_even;
}
else if (sb.state.stop_len) {
sb.state.stop_len--;
bit = STOP;
}
else {
if (sb.ri == sb.wi) {
sb.state.in_process = 0;
return;
}
sb.cc = sb.buffer[sb.ri++];
sb.ri = sb.ri < sizeof(sb.buffer) ? sb.ri : 0;
sb.state.data_len = 8;
sb.state.parity_len = 0;
sb.state.stop_len = 1;
bit = START;
}
tx = bit;
txclock.attach_us(&send_bit, TX_BITSPACE);
}
int uart_putc(uint8_t c) {
uint8_t wi = sb.wi++;
sb.wi = sb.wi < sizeof(sb.buffer) ? sb.wi : 0;
if (sb.wi == sb.ri) {
sb.wi = wi;
goto buffer_overflow;
}
sb.buffer[wi] = c;
if (sb.state.in_process == 0) {
sb.state.in_process = 1;
txclock.attach_us(&send_bit, TX_BITSPACE_ONSTART);
}
return 0;
buffer_overflow:
return -1;
}
int uart_getc() {
if (rb.ri == rb.wi) {
return -1;
}
int c = rb.buffer[rb.ri++];
rb.ri = rb.ri < sizeof(rb.buffer) ? rb.ri : 0;
return c;
}
void recv_reset() {
rb.state.in_process = 0;
//rxint.enable_irq();
}
void recv_bit() {
led = LED_ON;
int bit = rx;
led = LED_OFF;
if (rb.state.data_len) {
rb.state.data_len--;
rb.cc >>= 1;
rb.cc |= bit ? 0x80 : 0x00;
if (rb.state.parity_len) {
rb.state.parity ^= bit;
}
}
else if (rb.state.parity_len) {
rb.state.parity_len--;
if (bit != (rb.state.parity ^ rb.state.parity_even)) {
goto parity_error;
}
}
else if (rb.state.stop_len) {
rb.state.stop_len--;
if (bit != STOP) {
goto frame_error;
}
// receive completed
if (rb.state.stop_len == 0) {
uint8_t wi = rb.wi++;
// save to buffer
rb.wi = rb.wi < sizeof(rb.buffer) ? rb.wi : 0;
if (rb.wi == rb.ri) {
rb.wi = wi;
goto buffer_overflow;
}
rb.buffer[wi] = rb.cc;
// prepare for next event
recv_reset();
return;
}
}
rxclock.attach_us(&recv_bit, RX_BITSPACE);
return;
buffer_overflow:
rb.state.overflow_error = 1;
rxclock.attach_us(&recv_reset, RX_BITSPACE_ONRESET);
return;
parity_error:
rb.state.parity_error = 1;
frame_error:
rb.state.frame_error = 1;
rxclock.attach_us(&recv_reset, RX_BITSPACE_ONRESET);
}
void recv_start() {
//rxint.disable_irq();
if (rb.state.in_process == 0) {
rb.state.in_process = 1;
rb.state.data_len = 8;
rb.state.parity_len = 0;
rb.state.stop_len = 1;
rxclock.attach_us(&recv_bit, RX_BITSPACE_ONSTART);
}
}
void uart_init() {
led = LED_OFF;
tx = IDLE;
rxint.fall(&recv_start);
}
int main(void) {
uart_init();
for (;;) {
int c = uart_getc();
if (c > 0) {
while (uart_putc(c) < 0);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment