//****************************************************************************** // Copyright (c) 2017 Invivo Corporation all rights reserved // Project: Power Control Board for the UroNav Connect project // Author: David Harding // Date: July 2017 - Initial code //****************************************************************************** // MSP430FR2111 PwrCtrlBrd - Initialize microcontroller, execute main loop // // Description: The microcontroller monitors the front panel switch (FPS) for // two events. If the FPS is press for more than 200 ms and less than 6 s, // then the microcontroller determines if it should power up or power down the // main power supply (MPS); i.e. the UroNav system. If it needs to power down, // it issues a serial command that request a shutdown event to the single board // computer (SBC). The SCB displays a dialog box to the user. If the user // cancels the shutdown, the state is unchanged (i.e. powered up). If the // user confirms the shutdown, the SCB is shutdown by the UroNav program. The // PwrCtrlBrd detects the 5 V supply on the SCB is down and completes the // shutdown by turning the MPS off. // // The SBC may also initiate a shutdown. If the SBC completes its shutdown, the // PwrCtrlBrd detects the 5 V supply on the SCB is down and then shutdowns down // the MPS. // // If the FPS is pressed for 6 s or more, then the microcontroller will disable // the main power supply (MPS) without requesting a shutdown event from the SBC. // ********************** Microcontroller configuration ********************* // P1.0 - Set up as an input to the comparator Channel C0 (FPS sense) // P1.1 - Set up as DIO input to sense +5V from SBC (generates interrupt on low-to-high) // P1.2 - Set up as UART Rx // P1.3 - Set up as UART Tx // P1.4 - Set up as DIO output to drive UART DTR // P1.5 - Set up as DIO input to sense UART DSR // P1.6 - Set up as DIO output to drive low current on-board LED // P1.7 - Set up as DIO output to drive the MPS inhibit signal // P2.0 - Set up as DIO output to test point (comparator output - Cout) // P2.1 - Set up as DIO output to drive LED on FPS // P2.6 - Set up as DIO output (unconnected) // P2.7 - Set up as DIO output (unconnected) // // ********************** Development configuration ********************* // Built with desktop Code Composer Studio version 7.2.0.00013 //****************************************************************************** #include #include // Forward function declarations void Init_CS(void); void Init_IO(void); void Init_UART(void); void Init_eCOMP(void); void Software_Trim(void); void powerdown(void); void powerup(void); void put_schar(unsigned char* schar, unsigned int len); unsigned int utoa(unsigned int value, unsigned char *sp); // Project defines #define MCLK_FREQ_MHZ 1 // Set MCLK here (1 MHz) #define MIN_PULSE_WIDTH 1638 // INT(0.2 * 8192) count for 200 ms #define DELAY_6S 49152 // INT(6 * 8192) count for 6 s #define DELAY_250ms 2048 // INT(0.25 * 8192) count for 250 ms #define NUMofSAMPLES 6 // Number of samples taken to determine the SBC 5V supply is down #define STX 2 // Start of text ASCII character #define ETX 3 // End of text ASCII character // State flags #define POWERED_UP (0x01) //1 = unit powered up, 0 = unit powered down; #define FAILSAFE (0x02) //1 = FAILSAFE mode, 0 = normal mode; #define TxFlag (0x04) //1 = Serial Tx message pending, 0 = message complete (idle) #define RxFlag (0x08) //1 = Serial Rx message pending, 0 = message complete (idle) #define BadMessage (0x10) //1 = Bad serial message received, 0 = good serial message #define Reset_FS_cnt (0x20) //1 = Request to reset FAILSAFE counter, 0 = disallow reset #define SBC_5V_down (0x40) //1 = SBC 5V supply is down, 0 = SBC 5V supply is up #define PULSE_END (0x80) // 1 = fall edge of FPS detected, 0 = no release of FPS //***************************************************************************** //************************ FRAM persistent variables ************************ //***************************************************************************** // FRAM persistent variables do not get initialized by the C startup routine, // but rather the debug tool chain initializes them for the first time as the // application code is loaded onto the target. These variables are persistent // across power cycles. #pragma PERSISTENT(FAILSAFE_count) unsigned int FAILSAFE_count = 0; //***************************************************************************** //**************************** Global variables **************************** //***************************************************************************** unsigned int state = 0; // See state flags defined above unsigned int SBC_5V_down_counter = 0; // Counts the number of samples taken on P1.1 //***************** Front panel switch pulse width variable ****************** unsigned int start_pulse; unsigned int stop_pulse; unsigned int pulse_width; //********************* Serial communication variables ********************** int TxMessageLength = 0; unsigned char* TxBuff_pointer; unsigned char RxBuff; //****************** Serial communication characters and message ************* unsigned const char version[] = {STX, 'P', 'w', 'r', 'C', 't', 'r', 'l', 'B', 'r', 'd', '-', 'V', '1', '.', '0', '.', '1', '7', '4', '4', ETX}; unsigned const char prompt[] = {STX, 'A', 'r', 'e', ' ', 'y', 'o', 'u', ' ', 's', 'u', 'r', 'e', ' ', 'y', 'o', 'u', ' ', 'w', 'a', 'n', 't', ' ', 't', 'o', ' ', 'r', 'e', 's', 'e', 't', ' ', 't', 'h', 'e', ' ', 'c', 'o', 'u', 'n', 't', 'e', 'r', '?',' ', '(', 'y', '/', 'n', ')', ETX}; unsigned const char FSC[] = {STX, 'F', 'A', 'I', 'L', 'S', 'A', 'F', 'E', ' ', 'C', 'o', 'u', 'n', 't', '=', 0}; unsigned const char ENQ = 5; unsigned const char ACK = 6; unsigned const char SO = 14; unsigned const char DC2 = 18; unsigned const char NAK = 21; unsigned const char CAN = 24; //******************* Previous message variables ***************************** unsigned char* Previous_message_pointer = (unsigned char*) &NAK; int Previous_message_length = 1; //****************************************************************************** // main - Initialize configuration and run main loop //****************************************************************************** void main(void) { size_t FSC_length; unsigned int index; unsigned int cnt_length; unsigned int Rx_NAK_count = 0; unsigned int Tx_NAK_count = 0; unsigned char FSC_message[22]; unsigned char cnt[6] = {0}; WDTCTL = WDTPW | WDTHOLD; // Disable watchdog timer Init_CS(); // Set up clock systems Init_IO(); // Set up IO ports Init_UART(); // Set up serial communication port Init_eCOMP(); // Set eComp module with timer B0 __bis_SR_register(GIE); // Interrupts enabled //********************************** main loop ******************************** while(1) { // Check for switch activation if(state & PULSE_END) { state &= ~PULSE_END; pulse_width = stop_pulse - start_pulse; if(pulse_width > MIN_PULSE_WIDTH) // Pulse must be longer than 200 ms { switch (state & POWERED_UP) // Check the current powered state { case 0: powerup(); break; case 1: put_schar((unsigned char*) &DC2, 1); // Request shutdown break; } } } // If FAILSAFE detected, shutdown if((state & FAILSAFE) && (state & POWERED_UP)) powerdown(); // If 5V on the SBC goes down while state is powered up, shutdown main power supply if((state & SBC_5V_down) && (state & POWERED_UP)) powerdown(); // Check for received serial characters if(state & RxFlag) { state &= ~RxFlag; // Clear Rx flag state |= BadMessage; // Set the Bad message flag if(RxBuff == 'F') // Return the FAILSAFE count { state &= ~ BadMessage; // Message is good Tx_NAK_count = 0; // Allow previous message to be Tx Rx_NAK_count = 0; // Allow NAKs to be Transmitted cnt_length = utoa(FAILSAFE_count, cnt); FSC_length = strlen((const char*) &FSC); for(index = 0; index < FSC_length; index++) { FSC_message[index] = FSC[index]; } for(;index < FSC_length + cnt_length; index++) { FSC_message[index] = cnt[index - FSC_length]; } FSC_message[index++] = ETX; put_schar((unsigned char*) &FSC_message, index); } if(RxBuff == '0') // Return prompt { state &= ~ BadMessage; // Message is good Tx_NAK_count = 0; // Allow previous message to be Tx Rx_NAK_count = 0; // Allow NAKs to be Transmitted state |= Reset_FS_cnt; // Set request to reset FS counter flag put_schar((unsigned char*) &prompt, 51); } if(RxBuff == 'y' || RxBuff == 'Y') // Okay to clear FAILSAFE count { state &= ~ BadMessage; // Message is good Tx_NAK_count = 0; // Allow previous message to be Tx Rx_NAK_count = 0; // Allow NAKs to be Transmitted if(state & Reset_FS_cnt) { SYSCFG0 = FRWPPW; // Program FRAM write enable FAILSAFE_count = 0; // Reset FAILSAFE counter to zero SYSCFG0 = FRWPPW | PFWP; // Program FRAM write protected (not writable) cnt_length = utoa(FAILSAFE_count, cnt); FSC_length = strlen((const char*) &FSC); for(index = 0; index < FSC_length; index++) { FSC_message[index] = FSC[index]; } for(;index < FSC_length + cnt_length; index++) { FSC_message[index] = cnt[index - FSC_length]; } FSC_message[index++] = ETX; put_schar((unsigned char*) &FSC_message, index); } state &= ~ Reset_FS_cnt; // Request to reset FS counter complete, clear flag } if(RxBuff == 'n' || RxBuff == 'N') // Abort clearing FAILSAFE count { state &= ~ BadMessage; // Message is good Tx_NAK_count = 0; // Allow previous message to be Tx Rx_NAK_count = 0; // Allow NAKs to be Transmitted state &= ~ Reset_FS_cnt; // Abort request to reset FS counter } if(RxBuff == ENQ) // Request for version of PwrCtrlBrd software { state &= ~ BadMessage; // Message is good Tx_NAK_count = 0; // Allow previous message to be Tx Rx_NAK_count = 0; // Allow NAKs to be Transmitted put_schar((unsigned char*) &version, 17); } if(RxBuff == NAK) // Request to repeat previous command { state &= ~ BadMessage; // Message is good Rx_NAK_count = 0; // Allow NAKs to be Transmitted if(Tx_NAK_count < 5) // Stop previous message TX after 5 NAKs { put_schar(0, 0); // Repeat previous command Tx_NAK_count++; } } if(state & BadMessage) // Received unknown command { Tx_NAK_count = 0; // Allow previous message to be Tx if(Rx_NAK_count < 5) { put_schar((unsigned char*) &NAK, 1); Rx_NAK_count++; } } } } } //****************************************************************************** // Init_CS - Initialize configuration of clock systems //****************************************************************************** // Configure MCLK for 1MHz. // FLL reference clock is REFO. // ACLK = default REFO ~32768Hz. // SMCLK = MCLK = 1MHz. // Use DCO software trim to lock FLL. If FLL is locked and // DCO tap is closest to 256, the DCOFTRIM value is the best one. //****************************************************************************** void Init_CS(void) { __bis_SR_register(SCG0); // Disable FLL CSCTL3 = SELREF__REFOCLK; // Set REFO as FLL reference source CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_0; // DCOFTRIM=3, //DCO Range=1MHz CSCTL2 = FLLD_0 + 30; // DCODIV = 1MHz __delay_cycles(3); __bic_SR_register(SCG0); // Enable FLL Software_Trim(); // Software Trim to get the best DCOFTRIM value CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // set default REFO(~32768Hz) // as ACLK source, ACLK = 32768Hz // default DCODIV as MCLK and SMCLK source } //****************************************************************************** // Init_IO - Initialize configuration of DIO //****************************************************************************** // P1.0 - Set up as an input to the comparator Channel C0 (FPS sense) // P1.1 - Set up as DIO input to sense +5V from SBC (generates interrupt on low-to-high) // P1.2 - Set up as UART Rx // P1.3 - Set up as UART Tx // P1.4 - Set up as DIO output to drive UART DTR // P1.5 - Set up as DIO input to sense UART DSR // P1.6 - Set up as DIO output to drive low current on-board LED // P1.7 - Set up as DIO output to drive the MPS inhibit signal // P2.0 - Set up as DIO output to test point (comparator output - Cout) // P2.1 - Set up as DIO output to drive LED on FPS // P2.6 - Set up as DIO output (unconnected) // P2.7 - Set up as DIO output (unconnected) //****************************************************************************** void Init_IO(void) { P1DIR = 0xD8; // Set direction of pins P2DIR = 0xFF; P1OUT = 0xD0; // On-board LED on, DTR on, high = MPS off P2OUT = 0x02; // FPS LED high = off P1IES &= ~BIT1; // P1.1 interrupt on low-to-high edge P1IE |= BIT1; // P1.1 interrupt enabled // Configure UART pins SYSCFG3 |= USCIARMP; // Enable remapping P1SEL0 |= BIT2 | BIT3; // Set pins as second function (UART) // Configure Comparator input & output P1SEL0 |= BIT0; // Select eCOMP input function on P1.0/C0 P1SEL1 |= BIT0; P2DIR |= BIT0; P2SEL1 |= BIT0; // Select CPOUT function on P2.0/COUT PM5CTL0 &= ~LOCKLPM5; // Disable the IO power-on default // high-impedance mode to activate // previously configured port settings } //****************************************************************************** // Init_UART - Initialize configuration of UART //****************************************************************************** // Baud rate clock = SMCLK (1 MHz) // Divider N = baud rate clock / baud rate => 10^6 / 9600 = 104.1667 // Since N is greater than 16, enable the 16 times over sample (UCOS16 = 1) // Now, N = 10^6 / (9600 *16) = 6.51042 => UCA0BRW = INT(6.51042) = 6 // First modulation reg (UCBRF0) = INT(16 * REMAIN(6.51042)) = INT(8.167) = 8 // Second modulation reg (UCBRS0) = 0x11 - see lookup table 21-4 in User Guide // // To change the parameters for the character (Parity, character length, etc.) // Un-comment the appropriate line(s) below //****************************************************************************** void Init_UART(void) { // Configure UART UCA0CTLW0 |= UCSWRST; // Put eUSCI in reset to do configuration UCA0CTLW0 |= UCSSEL__SMCLK; // BRCLK source SMCLK //*********************** Reset defaults ************************************* // Parity - none, // Parity even/odd - NA, // LSB transmitted first, // 8-bit character length // One stop bit // ************** Un-comment below to set different character format ********* // UCA0CTLW0 |= UCPEN // Enable Parity // UCA0CTLW0 |= UCPAR__EVEN // Even Parity // UCA0CTLW0 |= UCPAR__ODD // Odd Parity // UCA0CTLW0 |= UMSB // MSB transmitted first // UCA0CTLW0 |= UC7BIT__7BIT // Enable 7-bit character length // UCA0CTLW0 |= UCSPB // Enable two stop bits //***************************************************************************** // Baud Rate calculation // Baud Rate = 9600, SMCLK = 1 MHz, over sample is 16 UCA0BRW = 6; // Baud rate divider UCA0MCTLW = 0x1181; // UCOS16 = 1 (over sampling enabled) // UCBRF0 = 8; UCBRS0 = 0x11 UCA0CTLW0 &= ~UCSWRST; // Initialize eUSCI UCA0IE |= UCRXIE; // Enable USCI_A0 Rx interrupt } //****************************************************************************** // Init_eCOMP - Initialize configuration of comparator //****************************************************************************** // The eCom module is set up so that the Front panel switch on port pin P1.0 is // connected to the positive input of the comparator. The negative input of the // comparator is connected to the DAC output. The DAC output is controlled by // two buffers. Buffer 1 is set for 3/4 of VCC so that the output of the // comparator goes positive only after the positive input is above 3/4 of VCC. // When the comparator output goes high, the DAC output switches to buffer 2. // Buffer 2 is setup for 1/4 of VCC. Now the positive input must go below 1/4 // of VCC for the comparator output to switch back to a low level. This provides // hysteresis for the switch input. // // The output of the comparator is then filtered to further prevent // oscillations in the output due slow slew rate. // // The output of the comparator is then connected to both the port pin P2.0 and // the capture/compare block 1. The capture 1 register is set up to capture both // the rising and falling edges of the comparator output. The difference // between the rising and falling edges determines how long the switch is pressed. // // When the rising edge is detected, it starts the compare block 0. The // compare block 0 is set to trigger an interrupt 6 seconds after the rising // edge. If the falling edge occurs before the 6 second compare interrupt // occurs, then the button press is processed normally. If the 6 second compare // interrupt occurs before the falling edge, then the state changes to the // FAILSAFE mode. //****************************************************************************** void Init_eCOMP(void) { // Configure reference PMMCTL0_H = PMMPW_H; // Unlock the PMM registers PMMCTL2 |= INTREFEN; // Enable internal reference __delay_cycles(400); // Delay for reference settling // Setup comparator CPCTL0 |= CPPSEL_0; // Select C0 as input for V+ terminal CPCTL0 |= CPNSEL_6; // Select DAC as input for V- terminal CPCTL0 |= CPPEN | CPNEN; // Enable both eCOMP inputs CPDACCTL = CPDACEN; // Select VCC as reference and enable DAC CPDACDATA = 0x1030; // CPDACBUF1=VCC *3/4 CPDACBUF2=VCC *1/4 CPCTL1 |= CPFLT | CPFLTDLY_3 | CPMSEL | CPEN; // | | | |--- Turn on eCOMP // | | |------------ Low power low speed mode // | |----------------------- Analog filter delay 3600 ns // |--------------------------------- Enable analog filter // Setup capture/compare block 0 for compare mode TB0CCTL0 |= CCIS_2; // Defaults to compare mode and load on write // |------------- Input set to GND // Setup capture/compare block 1 for capture mode TB0CCTL1 |= CM_3 | CCIS_1 | CCIE | CAP | SCS; // | | | | |---- Synchronous capture // | | | |---------- Capture mode, not compare mode // | | |---------------- Enable capture interrupt // | |------------------------- Capture input CCI0B = eCOMP output // |-------------------------------- Capture both edges // Timer0_B3 Setup TB0CTL |= TBSSEL_1 | ID_2 | MC_2 | TBCLR; // | | | |---- Clear TB0R and divider logic // | | |------------- Timer in continuous mode // | |-------------------- Input clock divided by 4 (8192 Hz) // |------------------------------ Clock source = ACLK (32768 Hz) } //****************************************************************************** // Software_Trim - To get the best DCOFTRIM value //****************************************************************************** // Code provided from TI examples //****************************************************************************** void Software_Trim() { unsigned int oldDcoTap = 0xffff; unsigned int newDcoTap = 0xffff; unsigned int newDcoDelta = 0xffff; unsigned int bestDcoDelta = 0xffff; unsigned int csCtl0Copy = 0; unsigned int csCtl1Copy = 0; unsigned int csCtl0Read = 0; unsigned int csCtl1Read = 0; unsigned int dcoFreqTrim = 3; unsigned char endLoop = 0; do { CSCTL0 = 0x100; // DCO Tap = 256 do { CSCTL7 &= ~DCOFFG; // Clear DCO fault flag }while (CSCTL7 & DCOFFG); // Test DCO fault flag __delay_cycles((unsigned int)3000 * MCLK_FREQ_MHZ);// Wait FLL lock status (FLLUNLOCK) to be stable // Suggest to wait 24 cycles of divided FLL reference clock while((CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)) && ((CSCTL7 & DCOFFG) == 0)); csCtl0Read = CSCTL0; // Read CSCTL0 csCtl1Read = CSCTL1; // Read CSCTL1 oldDcoTap = newDcoTap; // Record DCOTAP value of last time newDcoTap = csCtl0Read & 0x01ff; // Get DCOTAP value of this time dcoFreqTrim = (csCtl1Read & 0x0070)>>4;// Get DCOFTRIM value if(newDcoTap < 256) // DCOTAP < 256 { newDcoDelta = 256 - newDcoTap; // Delta value between DCPTAP and 256 if((oldDcoTap != 0xffff) && (oldDcoTap >= 256)) // DCOTAP cross 256 endLoop = 1; // Stop while loop else { dcoFreqTrim--; CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4); } } else // DCOTAP >= 256 { newDcoDelta = newDcoTap - 256; // Delta value between DCPTAP and 256 if(oldDcoTap < 256) // DCOTAP cross 256 endLoop = 1; // Stop while loop else { dcoFreqTrim++; CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4); } } if(newDcoDelta < bestDcoDelta) // Record DCOTAP closest to 256 { csCtl0Copy = csCtl0Read; csCtl1Copy = csCtl1Read; bestDcoDelta = newDcoDelta; } }while(endLoop == 0); // Poll until endLoop == 1 CSCTL0 = csCtl0Copy; // Reload locked DCOTAP CSCTL1 = csCtl1Copy; // Reload locked DCOFTRIM while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // Poll until FLL is locked } //****************************************************************************** // powerdown - Controls hardware pins and changes program state to powered down //****************************************************************************** // Ensure that the operating system on the SBC is not running by monitoring // the 5 volt supply on the SBC. When the 5 volt supply is confirmed to be // down, then do the following: // Turn off the main power supply. // Turn off the front panel switch LED // Change program state to powered down // // The FAILSAFE mode is also monitored. If the FAILSAFE is detected, also // shutdown the system. //****************************************************************************** void powerdown(void) { // Wait for 5V supply on SBC to go down or the FAILSAFE mode while(!((state & SBC_5V_down) || (state & FAILSAFE))); state &= ~(POWERED_UP |SBC_5V_down); // Clear Flags P1OUT |= BIT7; // Turn off the power supply P2OUT |= BIT1; // FPS LED off } //****************************************************************************** // powerup - Controls hardware pins and changes program state to powered up //****************************************************************************** // Turn on the main power supply. // Turn on the front panel switch LED. // Change program state to powered up. // An infinite loop is entered. The loop exits if the FAILSAFE mode is detected. // // Normally the loop will exit by receiving an character from the SBC, // after it is completely booted. When the ENQ is received, the reply string is // sent by over the serial link and the loop is exited. // // If a character other than ENQ is received, a NAK is sent back to have the // command from the SBC repeated. After five consecutive NAK are sent, the loop // is exited on the next unknown character received. //****************************************************************************** void powerup(void) { int Tx_NAK_count = 0; int delay = 760; P1OUT &= ~BIT7; // Turn on the power supply P2OUT &= ~BIT1; // FPS LED on state |= POWERED_UP; // Now in the on state state &= ~ SBC_5V_down; // Clear Flag from previous down state TB0CCTL1 &= ~CCIE; // Disable FPS interrupts while waiting // Delay approximately 760 ms to allow the 5V on the SBC to come up. // Measured delay between enabling the MPS and the active signal for // the 5 V supply on the SBC is 380 ms. Providing 100% margin gives // 760 ms. while (delay--) { __delay_cycles(1000); } // There is a special case where the FPS can be pressed repeatedly in // rapid succession which causes the SBC to fail to boot correctly. // Even though the MPS is up and running. So this section of code // sets up the ISR to check if the SBC 5V is still down. If so, // the SBC_5V_down flag will be set and the MPS is turn off to // synchronize the state of the power control board with the SBC. if(P1IN & BIT1) { TB0CCR2 = TB0R + DELAY_250ms; // Read timer and add delay to compare 2 TB0CCTL2 &= ~CCIFG; // Ensure no compare 2 interrupt is pending TB0CCTL2 |= CCIE; // Enable compare 2 interrupt } TB0CCTL0 &= ~CCIFG; // Ensure no interrupt is pending TB0CCTL1 |= CCIE; // Enable FPS interrupts while waiting while(1) { if((state & SBC_5V_down) || (state & FAILSAFE)) // Exit loop if FAILSAFE or SBC is off { state &= ~(POWERED_UP | SBC_5V_down); // Clear Flags P1OUT |= BIT7; // Turn off the power supply P2OUT |= BIT1; // FPS LED off break; } if(state & RxFlag) // Wait for ENQ from SBC { state &= ~RxFlag; if(RxBuff == ENQ) { put_schar((unsigned char*) &version, 17); break; } else // Unknown command, request repeat command { if(Tx_NAK_count < 5) //Transmit a NAK up to five times { put_schar((unsigned char*) &NAK, 1); Tx_NAK_count++; } else { break; } } } } state &= ~PULSE_END; //Clear any FPS activation } //****************************************************************************** // put_schar - Transmit a character to the serial communication port //****************************************************************************** // If arguments are zero, retransmit previous message. // Set the length of message to be sent. // Put character into Tx buffer used by the ISR. // Set the Tx flag to indicate that a transmission is in progress. // Enable interrupt. Since the UART Tx register is empty, an interrupt occurs // immediately. Loop until message is completely sent. //****************************************************************************** void put_schar(unsigned char* schar, unsigned int len) { if(schar == 0 && len == 0) { TxMessageLength = Previous_message_length; TxBuff_pointer = Previous_message_pointer; } else { if(*schar != NAK) { Previous_message_length = len; Previous_message_pointer = schar; } TxMessageLength = len; TxBuff_pointer = schar; } state |= TxFlag; UCA0IE |= UCTXIE; // Enable USCI_A0 Tx interrupt while(state & TxFlag); // TxFlag cleared by the ISR } //****************************************************************************** // utoa - Convert an unsigned integer into a decimal ASCII string //****************************************************************************** // value is the unsigned integer to be converted // sp is a pointer to the string buffer where the characters are placed // returns the length of the string //****************************************************************************** unsigned int utoa(unsigned int value, unsigned char *sp) { char tmp[16]; char *tp = tmp; unsigned int i, len; while (value || tp == tmp) { i = value % 10; value /= 10; if (i < 10) *tp++ = i + '0'; } len = tp - tmp; while (tp > tmp) *sp++ = *--tp; return len; } //****************************************************************************** //******************************* ISRs ************************************** //****************************************************************************** // UART ISR //****************************************************************************** #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)) { case USCI_NONE: break; case USCI_UART_UCRXIFG: RxBuff = UCA0RXBUF; state |= RxFlag; // Set flag that a char has been received break; case USCI_UART_UCTXIFG: UCA0TXBUF = *TxBuff_pointer; TxMessageLength--; if(TxMessageLength == 0) { state &= ~TxFlag; // Clear flag to indicate completion of Tx UCA0IE &= ~UCTXIE; // Disable USCI_A0 TX interrupt } else { TxBuff_pointer++; } break; case USCI_UART_UCSTTIFG: break; case USCI_UART_UCTXCPTIFG: break; } } //****************************************************************************** // Port 1 interrupt service routine //****************************************************************************** // A high-to-low transition on port pin 1.6 (the SBC 5V sensing signal) creates // an interrupt. Once this transition is detected, a timer is set up to // sample the 5V signal every 250 ms. When the 250 ms delay expires, the // compare 2 ISR is called. The compare 2 ISR is set up to take multiple // sample of the 5V signal. //****************************************************************************** #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(PORT1_VECTOR))) Port_1 (void) #else #error Compiler not supported! #endif { P1IFG &= ~BIT1; // Clear P1.1 IFG TB0CCR2 = TB0R + DELAY_250ms; // Read timer and add delay to compare 2 TB0CCTL2 &= ~CCIFG; // Ensure no compare 2 interrupt is pending TB0CCTL2 |= CCIE; // Enable compare 2 interrupt } //****************************************************************************** // Timer B0 CCR0, interrupt service routine //****************************************************************************** // This is the highest priority timer interrupt. It is used for the FAILSAFE // mode. The number of times this ISR is called is counted for off line // service analysis. //****************************************************************************** #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 { state |= FAILSAFE; // Set FAILSAFE flag SYSCFG0 = FRWPPW; // Program FRAM write enable FAILSAFE_count++; // Count the number of times FAILSAFE occurs SYSCFG0 = FRWPPW | PFWP; // Program FRAM write protected (not writable) TB0CCTL0 &= ~CCIE; // Disable compare interrupt } //****************************************************************************** // Timer0_B1 CCR1 and CCR2, interrupt service routine //****************************************************************************** // CCR1 is used to capture the FPS activation. The rising edge is captured // and starts the CCR0 to interrupt in 6 seconds. If the fall edge is captured // before the CCR0 interrupt occurs, then the fall edge timer value is captured // and the CCR0 interrupt is disabled. This is a normal FPS activation. If // the CCR0 interrupt occurs before the fall edge, then the FAILSAFE mode is // set. On the detection of the fall edge after FAILSAFE mode is handled in // the background tasks, the FAILSAFE flag is cleared. // // CCR2 is used to sample the SBC_5V signal multiple (6) times at a rate of // 250 ms. If the SBC_5V signal is low each time, the state flag // SBC_5V_down is set. This flag is monitored by the background tasks. This // flag is cleared by the background tasks: powerdown and powerup. //****************************************************************************** #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector = TIMER0_B1_VECTOR __interrupt void TIMER0_B1_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(TIMER0_B1_VECTOR))) TIMER0_B1_ISR (void) #else #error Compiler not supported! #endif { switch(__even_in_range(TB0IV,TB0IV_TBIFG)) { case TB0IV_NONE: break; // No interrupt case TB0IV_TBCCR1: if(!(TB0CCTL1 & COV)) // No overflow { if((TB0CCTL1 & CCI) == CCI) // Rising edge interrupt { start_pulse = TB0CCR1; TB0CCR0 = start_pulse + DELAY_6S; // Compare interrupt in 6 seconds TB0CCTL0 &= ~CCIFG; // Ensure no interrupt is pending TB0CCTL0 |= CCIE; // Enable compare interrupt } else // Falling edge interrupt { if(state & FAILSAFE) { state &= ~(FAILSAFE | PULSE_END); // Clear Flags } else { state |= PULSE_END; stop_pulse = TB0CCR1; TB0CCTL0 &= ~CCIFG; // Ensure no interrupt is pending TB0CCTL0 &= ~CCIE; // Disable compare interrupt } } } else // Clear overflow { TB0CCTL1 &= ~COV; } break; case TB0IV_TBCCR2: // CCR2 check to ensure SBC 5V supply is still down if(SBC_5V_down_counter < NUMofSAMPLES) { if(P1IN & BIT1) { SBC_5V_down_counter++; TB0CCR2 = TB0R + DELAY_250ms; // Read timer and add delay to compare 2 TB0CCTL2 &= ~CCIFG; // Ensure no compare 2 interrupt is pending } else { SBC_5V_down_counter = 0; // Reset counter TB0CCTL2 &= ~CCIE; // Disable compare 2 interrupt } } else { state |= SBC_5V_down; // Set flag SBC_5V_down_counter = 0; // Reset counter TB0CCTL2 &= ~CCIE; // Disable compare 2 interrupt } break; case TB0IV_TBIFG: break; // overflow default: break; } }