mirror of
https://github.com/travisgoodspeed/goodwatch
synced 2024-11-23 01:08:35 +00:00
189 lines
4.4 KiB
C
189 lines
4.4 KiB
C
/*! \file uart.c
|
|
\brief UART Driver for the GoodWatch
|
|
|
|
|
|
The goal here is to use P1.5 and P1.6 as a UART, just as the BSL
|
|
does.
|
|
|
|
Because we *also* use these pins for the MODE and SET buttons, we
|
|
need to be a bit tricky about their initialization, and the UART may
|
|
not be used at the same time as those watch buttons. The general
|
|
idea is that uartactive will be set to 1 when the UART receives its
|
|
first commands.
|
|
|
|
Similarly, we can't handle the UART at our normal rate of 32kHz, so
|
|
the first command must be sent as individual bytes to raise the CPU
|
|
speed.
|
|
|
|
*/
|
|
|
|
#include<stdint.h>
|
|
#include<stdio.h>
|
|
#include<msp430.h>
|
|
|
|
#include "uart.h"
|
|
#include "monitor.h"
|
|
|
|
//! Set to 1 if the UART is active.
|
|
int uartactive=0;
|
|
|
|
//! Initializes the UART if the host is listening.
|
|
void uart_init(){
|
|
printf("Initializing UART ");
|
|
|
|
PMAPPWD = 0x02D52;
|
|
P1MAP5 = PM_UCA0RXD; // Map UCA0RXD output to P1.5
|
|
P1MAP6 = PM_UCA0TXD; // Map UCA0TXD output to P1.6
|
|
PMAPPWD = 0;
|
|
|
|
P1DIR |= BIT6; // Set P1.6 as TX output
|
|
P1SEL |= BIT5 + BIT6; // Select P1.5 & P1.6 to UART function
|
|
|
|
UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
|
|
UCA0CTL1 |= UCSSEL_1; // ACLK
|
|
UCA0BR0 = 3; // 32kHz 9600 (see User's Guide)
|
|
UCA0BR1 = 0; // 32kHz 9600
|
|
UCA0MCTL = UCBRS_3 + UCBRF_0; // Modln UCBRSx=0, UCBRFx=0,
|
|
|
|
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
|
|
UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt
|
|
//UCA0IE |= UCTXIE; // Enable USCI_A0 TX interrupt
|
|
|
|
printf("Done.\n");
|
|
}
|
|
|
|
//! Transmit a byte to the UART.
|
|
void uart_tx(uint8_t byte){
|
|
/* Wait for the buffer to free up, then send the byte. It'd be
|
|
better to populate a ring buffer and dump then on TXIFG as
|
|
appropriate.
|
|
*/
|
|
while (!(UCA0IFG&UCTXIFG));
|
|
UCA0TXBUF = byte;
|
|
}
|
|
|
|
#define UARTBUFLEN 32
|
|
uint8_t uart_buffer[UARTBUFLEN];
|
|
|
|
|
|
|
|
static uint16_t outlength, outindex, outcrc;
|
|
|
|
//! Handle sending a UART byte.
|
|
static void handle_txbyte(){
|
|
/* For convenience, we use the same format and checksums as the BSL
|
|
format.
|
|
|
|
"\x00"+ll+lh+msg+crc
|
|
*/
|
|
|
|
//State machine.
|
|
static enum {IDLE,LL,LH,MSG,CRCL,CRCH} state=IDLE;
|
|
|
|
printf("Sending packet.\n");
|
|
do{
|
|
switch(state){
|
|
case IDLE:
|
|
/* Send 0x00 0x80 as the first bytes of the message. */
|
|
uart_tx(0x00);
|
|
uart_tx(0x80);
|
|
state=LL;
|
|
break;
|
|
case LL:
|
|
uart_tx(outlength&0xFF);
|
|
state=LH;
|
|
break;
|
|
case LH:
|
|
uart_tx((outlength>>8));
|
|
outindex=0;
|
|
state=MSG;
|
|
break;
|
|
case MSG:
|
|
uart_tx(uart_buffer[outindex++]);
|
|
if(outindex==outlength)
|
|
state=CRCL;
|
|
break;
|
|
case CRCL:
|
|
uart_tx(outcrc&0xFF);
|
|
state=CRCH;
|
|
break;
|
|
case CRCH:
|
|
uart_tx((outcrc>>8));
|
|
state=IDLE;
|
|
|
|
printf("Sent %d byte packet.\n",outlength);
|
|
break;
|
|
}
|
|
}while(state!=IDLE);
|
|
printf("Done, state=%d.\n",state);
|
|
}
|
|
|
|
//! Handle a UART byte.
|
|
static void handle_rxbyte(uint8_t byte){
|
|
/* For convenience, we use the same format and checksums as the BSL
|
|
format.
|
|
|
|
"\x80"+ll+lh+msg+crc
|
|
*/
|
|
|
|
//State machine.
|
|
static enum {IDLE,LL,LH,MSG,CRCL,CRCH} state=IDLE;
|
|
//Length and index.
|
|
static uint16_t length, index, crc;
|
|
|
|
|
|
switch(state){
|
|
case IDLE:
|
|
/* Wait for 0x80 as the first byte of the message. */
|
|
if(byte==0x80)
|
|
state=LL;
|
|
break;
|
|
case LL:
|
|
length=byte;
|
|
state=LH;
|
|
break;
|
|
case LH:
|
|
length|= ((uint16_t)byte)<<8;
|
|
index=0;
|
|
state=MSG;
|
|
break;
|
|
case MSG:
|
|
uart_buffer[index++]=byte;
|
|
if(index==length)
|
|
state=CRCL;
|
|
break;
|
|
case CRCL:
|
|
crc=byte;
|
|
state=CRCH;
|
|
break;
|
|
case CRCH:
|
|
crc|= ((uint16_t)byte)<<8;
|
|
state=IDLE;
|
|
|
|
printf("Got %d byte packet.\n",length);
|
|
outlength=monitor_handle(uart_buffer, length);
|
|
outindex=0;
|
|
outcrc=crc;
|
|
handle_txbyte();
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Echo back RXed character, confirm TX buffer is ready first
|
|
void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void){
|
|
switch(UCA0IV&~1){
|
|
case 0:break; // Vector 0 - no interrupt
|
|
case 2: // Vector 2 - RXIFG
|
|
handle_rxbyte(UCA0RXBUF);
|
|
break;
|
|
case 4:
|
|
//printf("TXIFG was triggered.\n");
|
|
//handle_txbyte();
|
|
break; // Vector 4 - TXIFG
|
|
default: break;
|
|
}
|
|
}
|
|
|