Lots of other fixes.
Beginnings of main()
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
// );
}
+extern inline void lock() {
+ asm volatile ("cli");
+}
+
+extern inline void unlock() {
+ asm volatile ("sei");
+}
+
#endif /* _ASM_H_ */
--- /dev/null
+#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();
+}
--- /dev/null
+#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_ */
}
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];
+}
#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 */
+ }
}
}
.sect .text
.globl _start
+ .globl sci_interrupt
+ .globl uart_interrupt
;; Default interrupt handler.
.sect .text
.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
;; 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)
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 */
#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; }