Created
March 8, 2015 13:59
-
-
Save tai/378c954ca8edfebf4ae1 to your computer and use it in GitHub Desktop.
Software UART implementation for mbed (testing)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // 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