UART code for a 16550.
authorBernard Blackham <[email protected]>
Fri, 15 Aug 2003 07:18:13 +0000 (07:18 +0000)
committerBernard Blackham <[email protected]>
Fri, 15 Aug 2003 07:18:13 +0000 (07:18 +0000)
Lots of other fixes.
Beginnings of main()

ROM2/Makefile
ROM2/asm.h
ROM2/comm.c [new file with mode: 0644]
ROM2/comm.h [new file with mode: 0644]
ROM2/helpers.c
ROM2/main_basic.c
ROM2/vectors.s
ROM2/vend.h

index 58e78bd..a7fd90d 100644 (file)
@@ -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
 
index 2c07ac8..41e9fe2 100644 (file)
@@ -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 (file)
index 0000000..09ad2a1
--- /dev/null
@@ -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 (file)
index 0000000..69f8d00
--- /dev/null
@@ -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_ */
index 0f9f4db..a35073c 100644 (file)
@@ -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];
+}
index 8d54ee9..ff8687b 100644 (file)
@@ -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 */
+               }
        }
 }
 
index abcfc97..76e6614 100644 (file)
@@ -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)
 
index c9cc6d6..9c3fe65 100644 (file)
@@ -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; }

UCC git Repository :: git.ucc.asn.au