Project Objective: Develop an open source AIS Alarm that alerts sailors that a new marine vessel with AIS is within range
This post will add 38400 baud UART to the existing code and test it.
/* * dAISy_Alarm_V002 - adds UART at 38400 baud * dAISy_Alarm_V001 - implements millisecond timer and blinks LED * * MCLK, SCLK set to 8 MHz * ACLK set to 32768 Hz * Milliseconds counted on timer B0 * UART at 38400 baud * * Frank Milburn 30 December 2017 * Developed on MSP430FR2111 with CCS V7.2, compiler TIv17.9.0.STS */ #include "driverlib.h" // Miscellaneous #define DEBUG 2 // 0 = no debug, 1...3 is increasing debug // Clock and Timer related definitions #define TIMER_UP_COUNT 8000 // approximately 1 millisecond @ 8 MHz #define MCLK_FREQ_MHZ 8 // master clock frequency // GPIO related definitions #define LED_WARN GPIO_PORT_P1,GPIO_PIN0 // warning LED #define TX_PIN BIT7 // TX pin is P1.7 #define RX_PIN BIT6 // RX pin is P1.6 // Function prototypes void initClocks(void); // initialize clocks void initGPIO(void); // initialize GPIO void initTimerB0(void); // initialize timer B0 void initUART(void); // initialize UART unsigned long milliSecs(void); // returns milliseconds since counter started // Global variable declarations const unsigned long BEEP_LEN = 10; // beep length in milliseconds volatile unsigned long MILLIS = 0; // running count of milliseconds volatile unsigned char RXData = 0; // for testing only REMOVE volatile unsigned char TXData = 1; // for testing only REMOVE int main(void) { WDT_A_hold(WDT_A_BASE); // stop the watch dog PMM_unlockLPM5(); // activate port settings after power on initClocks(); initGPIO(); initTimerB0(); initUART(); __bis_SR_register(GIE); // globally enable interrupts for(;;){ TXData = 0; int i; for(i = 0; i < 255; i++){ TXData += 1; // Increment TX data and transmit EUSCI_A_UART_transmitData(EUSCI_A0_BASE, TXData); while(EUSCI_A_UART_queryStatusFlags(EUSCI_A0_BASE,EUSCI_A_UART_BUSY)){ // Wait until complete } } } } // ------------------------------------------------------------------------------------- void initClocks(void){ __bis_SR_register(SCG0); // disable FLL CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_3; // DCOFTRIM=3, DCO Range = 8MHz CSCTL2 = FLLD_0 + 243; // DCODIV = 8MHz __delay_cycles(3); __bic_SR_register(SCG0); // enable FLL CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // set default REFO(~32768Hz) as // ACLK source, ACLK = 32768Hz // default DCODIV as MCLK, SMCLK source } // ------------------------------------------------------------------------------------- void initGPIO(void){ GPIO_setAsOutputPin(LED_WARN); GPIO_setOutputLowOnPin(LED_WARN); P1SEL0 |= TX_PIN | RX_PIN; // UART per Table 6-34 of datasheet } // ------------------------------------------------------------------------------------- unsigned long milliSecs(void){ return MILLIS; // return milliseconds } // ------------------------------------------------------------------------------------- void initTimerB0(void){ TB0CCTL0 |= CCIE; // TBCCR0 interrupt enabled TB0CCR0 = TIMER_UP_COUNT; // approx 1 millisecond TB0CTL |= TBSSEL__SMCLK | MC__UP; // SMCLK, Up mode } // ------------------------------------------------------------------------------------- void initUART(void){ // Set baud rate to 38400 using parameters in table 21-5 of the Family User Gude // UCBRx = 13, UCBRFx = 0, UCBRSx = 0x84, UCOS16 = 1 // Expected error from the table is: TX neg TX pos RX neg RX pos // -0.32 0.32 -0.64 0.48 EUSCI_A_UART_initParam param = {0}; param.selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK; param.clockPrescalar = 13; // UCBRx param.firstModReg = 0; // UCBRFx param.secondModReg = 0x84; // UCBRSx param.parity = EUSCI_A_UART_NO_PARITY; param.msborLsbFirst = EUSCI_A_UART_LSB_FIRST; param.numberofStopBits = EUSCI_A_UART_ONE_STOP_BIT; param.uartMode = EUSCI_A_UART_MODE; param.overSampling = EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION; // UCOS16 = 1 if(STATUS_FAIL == EUSCI_A_UART_init(EUSCI_A0_BASE, ¶m)) { GPIO_setOutputHighOnPin(LED_WARN); return; } //Enable UART module for operation EUSCI_A_UART_enable(EUSCI_A0_BASE); //Enable Receive Interrupt EUSCI_A_UART_clearInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT); EUSCI_A_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT); } // ------------------------------------------------------------------------------------- // Timer B0 interrupt service routine #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector = TIMER0_B0_VECTOR __interrupt void Timer_B (void) #elif defined(__GNUC__) void __attribute__ ((interrupt(TIMER0_B0_VECTOR))) Timer_B (void) #else #error Compiler not supported! #endif { MILLIS++; } // ------------------------------------------------------------------------------------- #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_A0_VECTOR __interrupt void USCI_A0_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USCI_A0_VECTOR))) USCI_A0_ISR (void) #else #error Compiler not supported! #endif { switch(__even_in_range(UCA0IV,USCI_UART_UCTXCPTIFG)) { // Vector 2 - RXIFG - Flag error if RX does not equal TX case USCI_UART_UCRXIFG: RXData = EUSCI_A_UART_receiveData(EUSCI_A0_BASE); if(RXData != TXData) { GPIO_setOutputHighOnPin(LED_WARN); // turn on red LED while(1); // loop forever } break; default: break; } }
I have had problems implementing UART on the MSP430 before but I must be living right because it pretty much worked first time. The primary addition is the function initUART(). dAISy can operate at 4800, 9600, 38400 baud - set at 38400 baud here. The parameters for 38400 baud are taken from table 21-5 in the User Family Guide.
The TX pin on the microcontroller is jumpered to the RX pin. In main() there is a for loop that increments the variable TXData from 0 to 255 over and over again. TXData is sent over UART to the RX pin on the microcontroller and tested in the ISR that captures received characters. If the received character is not the same as the transmitted character then a LED is lit and execution is trapped.
This is the first test to see if things are working as expected. If the jumper is removed, the LED should light up. Since the microcontroller is using the same clock for TX and RX this may not seem a particularly rigorous test given that UART is asynchronous. The clock has been tested though and is reasonably accurate and provided the other devices it is communicating with are reasonably accurate it should be OK. The expected accuracy from the data sheet has been noted in the comments.
The second test is to set a breakpoint in debug at line 140 (where RXData is tested against TXData in the ISR). The two variables can then be examined as the program steps through as shown below.
The variables are seen to be equivalent in the upper right hand window with the Expressions tab open. Test passed.
The memory usage should also be noted in the Console at lower left. FRAM usage is 1278 bytes and RAM is 290 bytes. Remember that there is a bit less than 4 k of FRAM available and that approximately 2 k is desired for the AIS message ring buffer. I am still hopeful it will fit comfortably. If not, there is Plan B which could mean using a different member of the MSP430FRxx family for the microcontroller.
For a final test the logic analyzer was hauled out again to check if it is seeing the character sequence that is expected.
Third test passed.
The next post should be a bit more interesting. The dAISy receiver will be connected to the prototype and a test devised to assure that AIS sentences are received correctly.
Past Posts from this Project:
AIS Alarm - Prototype Hardware
AIS Alarm - Prototype Code Outline
References and Links:
WEGMATT LLC - dAISy AIS Receiver - low cost AIS receiver
Texas Instruments MSP430FR2xx FRAM Microcontrollers - Post No. 4