From: Bernard Blackham Date: Fri, 15 Aug 2003 07:18:13 +0000 (+0000) Subject: UART code for a 16550. X-Git-Tag: ROMW~69 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=2e7a8beb908896930042bc0564fbd16f3e0b0b62;p=uccvend-snackrom.git UART code for a 16550. Lots of other fixes. Beginnings of main() --- diff --git a/ROM2/Makefile b/ROM2/Makefile index 58e78bd..a7fd90d 100644 --- a/ROM2/Makefile +++ b/ROM2/Makefile @@ -2,7 +2,7 @@ OBJS = \ motors.o keypad.o display_basic.o coinmech.o chime.o \ - helpers.o server.o 16550.o main_basic.o \ + helpers.o server.o main_basic.o comm.o \ vectors.o INCLUDES = vend.h keypad.h chime.h asm.h display_basic.h ports.h types.h diff --git a/ROM2/asm.h b/ROM2/asm.h index 2c07ac8..41e9fe2 100644 --- a/ROM2/asm.h +++ b/ROM2/asm.h @@ -34,4 +34,12 @@ extern inline void bclr(const void* addr, const u8 mask) { // ); } +extern inline void lock() { + asm volatile ("cli"); +} + +extern inline void unlock() { + asm volatile ("sei"); +} + #endif /* _ASM_H_ */ diff --git a/ROM2/comm.c b/ROM2/comm.c new file mode 100644 index 0000000..09ad2a1 --- /dev/null +++ b/ROM2/comm.c @@ -0,0 +1,186 @@ +#include "comm.h" +#include "vend.h" + +#define DLAB_SET() bset((void*)&_uart_regs[UART_LINE_CTL], LC_DLAB); +#define DLAB_CLR() bclr((void*)&_uart_regs[UART_LINE_CTL], LC_DLAB); + +char tx_buffer[TX_BUFFER_LEN+2]; +volatile char rx_buffer[RX_BUFFER_LEN+1]; +volatile u8 rx_buf_pos; /* -ve denotes not sending/receiving */ +volatile char msg_buf[RX_BUFFER_LEN+1]; /* rx_buffer copied here without \n */ +volatile u8 rx_queue_state, tx_queue_state; +volatile bool packet_is_bad; + +/* If we were controlling a modem we'd probably do a lot more such as toggling + * the control lines (DTR,RTS etc). But given this is a very stripped down + * version talking over merely TX/RX, we don't care about these control lines. + * + * This code speak 8 data bits, No parity, 1 Stop bit, at the baud rate specified + * in comm.h (should be 9600 bps) + */ + +u8 uart_init() { + /* + * Because in our daughterboard circuit we are not connecting the reset line + * to the CPU's, we can not make any assumptions about the state of the UART. + * Hence we initialize all registers and flush the FIFOs + */ + + u16 divisor; + lock(); /* disable interrupts for this */ + + /* set baud rate */ + divisor = UART_SPEED / BAUD_RATE; + DLAB_SET(); + _uart_regs[UART_DLAB_MSB] = (u8)(divisor >> 8); + _uart_regs[UART_DLAB_LSB] = (u8)(divisor & 0xff); + DLAB_CLR(); + + /* set RX, TX & LS interrupts */ + _uart_regs[UART_INT_ENABLE] = + IE_RECEIVER_READY | IE_TRANSMITTER_READY | IE_LINE_STATUS_CHANGE; + + /* Use the FIFO and empty them */ + _uart_regs[UART_FIFO_CTL] = + FIFO_ENABLE | FIFO_RX_CLEAR | FIFO_TX_CLEAR | FIFO_LVL_1; + + /* 8 data bits, 1 stop bit, no parity. */ + _uart_regs[UART_LINE_CTL] = LC_8BITS; + + /* modem controls: clear them all */ + _uart_regs[UART_MODEM_CTL] = 0; + + rx_queue_state = 0; + tx_queue_state = 0; + rx_buf_pos = 0; + + unlock(); + return 1; +} + +/******************************************************************************* + * Interrupt handler for UART. + * + * This actually consists of several functions, rolled into one. + */ + +extern inline void rx_int() { + char c = _uart_regs[UART_RX_BUFFER]; + if (rx_queue_state == 2) return; /* buffers full. too bad */ + + if (rx_buf_pos < RX_BUFFER_LEN) { + rx_buffer[rx_buf_pos] = c; + + if (c == '\n') { + rx_buf_pos = 0; + if (packet_is_bad) { + packet_is_bad = 0; + /* just forget this packet ever happened */ + } else { + rx_buffer[rx_buf_pos] = 0; + switch (rx_queue_state) { + case 0: + my_strncpy((char*)msg_buf, (char*)rx_buffer, RX_BUFFER_LEN); + rx_queue_state++; + case 1: + /* we can't copy it in yet. will be done from msg_clr() */ + rx_queue_state++; + break; + case 2: /* another weird case? */ + default: + break; + } + } + } else + rx_buf_pos++; + } /* else drop it (buffer full) - probably a corrupt packet */ +} + +extern inline void tx_int() { + /* this interrupt is called with the precondition that the TX fifo of the UART + * is completely empty and will hold a complete message (of less than 14 chars) + */ + if (tx_queue_state & 0x01) { /* msg to be sent pending */ + /* load it in */ + int i; + for (i=0; tx_buffer[i]; i++) + _uart_regs[UART_TX_BUFFER] = tx_buffer[i]; + bclr((void*)&tx_queue_state, 0x01); + } else + bclr((void*)&tx_queue_state, 0x02); /* mark the FIFO as free */ +} + +void uart_interrupt() { + u8 status; + while (1) { + status = _uart_regs[UART_INT_IDENT]; + switch (status) { + case IS_FIFO_TIMEOUT: + case IS_RECEIVER_READY: + rx_int(); + break; + case IS_TRANSMITTER_READY: + tx_int(); + break; + case IS_MODEM_STATUS_CHANGE: + /* read the modem status register to clear the interrupt */ + (void)_uart_regs[UART_MODEM_STATUS]; + break; + case IS_LINE_STATUS_CHANGE: + status = _uart_regs[UART_LINE_STATUS]; + if (status & LS_OVERRUN_ERR) packet_is_bad = 1; + if (status & LS_PARITY_ERR) packet_is_bad = 1; + if (status & LS_FRAMING_ERR) packet_is_bad = 1; + /* LS_BREAK_INTERRUPT ignored for now. Do we want it? */ + break; + default: + return; + } + } +} + +/******************************************************************************* + * End of interrupt handler + */ + +/* sends the packet in tx_buffer and doesn't return until it's been sent */ +void send_packet() { + bset((void*)&tx_queue_state, 0x01); + lock(); + tx_int(); + unlock(); + while (tx_queue_state & 0x01); /* wait for completion */ +} + +void msg_clr() { + /* called when a msg in msg_buf has been read */ + switch (rx_queue_state) { + case 0: /* this shouldn't happen */ + break; + case 1: + lock(); + rx_queue_state = 0; + unlock(); + break; + case 2: + /* copy the rx_buffer into msg_buf */ + my_strncpy((char*)msg_buf, (char*)rx_buffer, RX_BUFFER_LEN); + lock(); + rx_queue_state = 1; + unlock(); + break; + default: + lock(); + rx_queue_state = 0; + unlock(); + } +} + + +void comm_init() { + uart_init(); + lock(); + tx_buffer[0] = rx_buffer[0] = msg_buf[0] = 0; + packet_is_bad = 0; + unlock(); +} diff --git a/ROM2/comm.h b/ROM2/comm.h new file mode 100644 index 0000000..69f8d00 --- /dev/null +++ b/ROM2/comm.h @@ -0,0 +1,122 @@ +#ifndef _COMM_H_ +#define _COMM_H_ + +#include "vend.h" + +/* + * The communications protocol: + * + * \n used for separating packets. Caps is important. + * + * Messages Sent: + * KX - keypad press where X is (ascii 0..9 or R) + * CXXXXX - coin balance, XXXXX is number of cents. + * MXYY - dispense ack/nack. X is what happened (0..MOTOR_*_FAIL), YY is the motor + * + * Messages Received: + * VXX - vend a slot XX + * DXXXXXXXXXXX - write the string XXXXXXXXXX to screen (10 chars) + * B - beep + * U - query current coin balance. - replies with CXXXXX + * GXXXXX - give change, XXXXX is the amount of the cost of the item hence change + * is the current value in the coin mech - XXXXX + * + */ + +#define TX_BUFFER_LEN 6 /* maximum 13 due to the way tx_int works with the FIFO */ +extern char tx_buffer[TX_BUFFER_LEN+2]; /* \n + null terminated */ +#define RX_BUFFER_LEN 11 +extern volatile char rx_buffer[RX_BUFFER_LEN+1]; /* null terminated */ +extern volatile char msg_buf[RX_BUFFER_LEN+1]; /* rx_buffer copied here w/o \n */ + +/* rx_queue_state denotes the state of rx_buffer & msg_buf. Is one of: + * 0: both rx_buffer & msg_buf are empty and there are no msgs + * 1: msg_buf has a pending msg, but rx_buffer is not yet full. + * 2: msg_buf has a pending msg, as does rx_buffer - further msgs will be lost + */ +extern volatile u8 rx_queue_state; + +/* tx_queue_state bits are: + * bit 0: if the tx_buffer has a message pending to be sent. + * bit 1: if the TX fifo is in use. + */ +extern volatile u8 tx_queue_state; + +void comm_init(); +void msg_clr(); + +/*************************************/ +/*** 16550 UART specific #defines ***/ +/*************************************/ + +#define UART_SPEED 4915200 /* FIXME divide this by some magic number */ +#define BAUD_RATE 9600 + +/* Register offsets for _uart_regs */ + /* Must be accessed with DLAB low */ +#define UART_RX_BUFFER 0x00 /* read */ +#define UART_TX_BUFFER 0x00 /* write */ +#define UART_INT_ENABLE 0x01 +#define UART_INT_IDENT 0x02 /* read */ +#define UART_FIFO_CTL 0x02 /* write */ +#define UART_LINE_CTL 0x03 +#define UART_MODEM_CTL 0x04 +#define UART_LINE_STATUS 0x05 +#define UART_MODEM_STATUS 0x06 +#define UART_SCRATCH 0x07 + + /* Same addresses as above, but accessed with DLAB high */ +#define UART_DLAB_LSB 0x00 +#define UART_DLAB_MSB 0x01 + +extern volatile u8 _uart_regs[]; /* UART registers - fixed at link time */ + +/* The following #define's adapted from Minix 2.0.0's rs232.c */ + +/* Interrupt enable bits. */ +#define IE_RECEIVER_READY 1 +#define IE_TRANSMITTER_READY 2 +#define IE_LINE_STATUS_CHANGE 4 +#define IE_MODEM_STATUS_CHANGE 8 + +/* Interrupt status bits. */ +#define IS_MODEM_STATUS_CHANGE 0x00 +#define IS_TRANSMITTER_READY 0x02 +#define IS_RECEIVER_READY 0x04 +#define IS_LINE_STATUS_CHANGE 0x06 +#define IS_FIFO_TIMEOUT 0x0C + +/* FIFO control bits. */ +#define FIFO_ENABLE 0x01 +#define FIFO_RX_CLEAR 0x02 +#define FIFO_TX_CLEAR 0x04 +#define FIFO_DMA_ENABLE 0x08 +#define FIFO_LVL_1 (0x00<<6) +#define FIFO_LVL_4 (0x01<<6) +#define FIFO_LVL_8 (0x10<<6) +#define FIFO_LVL_14 (0x11<<6) + +/* Line control bits. */ +#define LC_5BITS 0x00 +#define LC_6BITS 0x01 +#define LC_7BITS 0x10 +#define LC_8BITS 0x11 +#define LC_2STOP_BITS 0x04 +#define LC_PARITY 0x08 +#define LC_PAREVEN 0x10 +#define LC_BREAK 0x40 +#define LC_DLAB 0x80 + +/* Line status bits. */ +#define LS_DATA_READY 1 +#define LS_OVERRUN_ERR 2 +#define LS_PARITY_ERR 4 +#define LS_FRAMING_ERR 8 +#define LS_BREAK_INTERRUPT 0x10 +#define LS_TRANSMITTER_READY 0x20 + +/* Modem control bits. */ +#define MC_OUT1 4 +#define MC_OUT2 8 + +#endif /* _COMM_H_ */ diff --git a/ROM2/helpers.c b/ROM2/helpers.c index 0f9f4db..a35073c 100644 --- a/ROM2/helpers.c +++ b/ROM2/helpers.c @@ -19,3 +19,14 @@ void print_amount(u16 amt) { } set_msg(str); } + +void my_strncpy(char* dst, char* src, u8 max_size) { + u8 i; + for (i = 0; src[i] && i < max_size; i++) dst[i] = src[i]; + if (src[i] == 0 && i < max_size) dst[i] = 0; /* null terminator */ +} + +void my_memcpy(char* dst, char* src, u8 size) { + u8 i = 0; + for (i = 0; i < size; i++) dst[i] = src[i]; +} diff --git a/ROM2/main_basic.c b/ROM2/main_basic.c index 8d54ee9..ff8687b 100644 --- a/ROM2/main_basic.c +++ b/ROM2/main_basic.c @@ -9,39 +9,61 @@ #include "chime.h" #include "server.h" #include "coinmech.h" +#include "comm.h" #include "vend.h" +extern inline void enable_rti() { +} + void _start() { set_bus_expanded(); - display_init(); - /* enable RTI & set rate */ - /* init coin mech */ - /* scan for motors */ + /* enable RTI */ + _io_ports[M6811_TMSK2] = 0x40; + /* powerup ADC, set IRQ' for edge-sensitive operation */ + _io_ports[M6811_OPTION] = 0xA0; + /* set the stack pointer */ + //asm volatile ("lds %1":: "i"(_stack):"sp"); + main(); + +loop_forever: + goto loop_forever; /* wait for an interrupt to wake us up again */ } int main() { + u16 last_coin_value = coin_value; + + /* init coin mech */ + comm_init(); + display_init(); while(1) { - /* - * have serial packet? - * - * decode msg & process: - * - dispense motor - * - display string - * - give change - * - beep - */ - - /* - * have keypress? - * - beep - * - send via serial - */ - - /* - * have coin balance change? - * - send via serial - */ + if (rx_queue_state) { + switch (msg_buf[0]) { + case 'V': /* dispense something */ + break; + case 'D': /* write string to display */ + break; + case 'B': /* beep */ + break; + case 'U': /* query current coin balance */ + break; + case 'G': /* give change */ + break; + default: + /* shrug */ + break; + } + msg_clr(); + } + keypad_read(); + if (keypad_pressed()) { + /* send packet about it */ + /* beep? */ + } + + if (coin_value != last_coin_value) { + /* send a packet about it */ + } } } diff --git a/ROM2/vectors.s b/ROM2/vectors.s index abcfc97..76e6614 100644 --- a/ROM2/vectors.s +++ b/ROM2/vectors.s @@ -27,6 +27,8 @@ Boston, MA 02111-1307, USA. */ .sect .text .globl _start + .globl sci_interrupt + .globl uart_interrupt ;; Default interrupt handler. .sect .text @@ -65,8 +67,8 @@ vectors: .word def ; ffde (TOI) ;; Timer Output Compare - .word def ; ffe0 - .word def ; ffe2 + .word def ; ffe0 + .word def ; ffe2 .word def ; ffe4 .word def ; ffe6 .word def ; ffe8 @@ -78,11 +80,11 @@ vectors: ;; Misc .word rti ; fff0 (RTII) - .word def ; fff2 (IRQ) + .word uart_interrupt ; fff2 (IRQ) .word def ; fff4 (XIRQ) .word def ; fff6 (SWI) .word def ; fff8 (ILL) - .word def ; fffa (COP Failure) - .word def ; fffc (COP Clock monitor) + .word _start ; fffa (COP Failure) + .word _start ; fffc (COP Clock monitor) .word _start ; fffe (reset) diff --git a/ROM2/vend.h b/ROM2/vend.h index c9cc6d6..9c3fe65 100644 --- a/ROM2/vend.h +++ b/ROM2/vend.h @@ -26,9 +26,13 @@ extern volatile u8* _misc_input; extern volatile u8* _home_sensors; #define home_sensors (*_home_sensors) +extern u16 _stack; + /******* from helpers.c *******/ void delay(u16 ms); void print_amount(u16 amt); +void my_strncpy(char* dst, char* src, u8 max_size); /* for null-term strings */ +void my_memcpy(char* dst, char* src, u8 size); /******** Some meaningful bits ******/ #define PORTA_CHIME 0x10 /* chime is on when set */ @@ -54,8 +58,11 @@ void print_amount(u16 amt); #define A3800_MOTOR_COL9_ENABLE 0x40 /******* from main.c *******/ +void __attribute__((noreturn)) _start (void); int __attribute__((noreturn)) main (void); void __attribute__((interrupt)) rti (void); +void __attribute__((interrupt)) sci_interrupt (void); +void __attribute__((interrupt)) uart_interrupt (void); /* other one liners */ extern inline bool door_open() { return switch_input & A1800_DOOR_OPEN; }