//****************************************************************************** // D437_3.c - Multi-funtion demo with Clock, Temperature and Milli-Voltmeter // // // Murugavel Raju // Texas Instruments, Inc // November, 2002 // Built with IAR Embedded Workbench Version: 1.26A //****************************************************************************** #include // Standard Definition header file for MSP430F437 #define PB_TIME (1 << 1) // Push button 1: Time [and Time set]. #define PB_TEMP (1 << 2) // Push button 2: Temperature [and Seconds set when PB1 is Time set]. #define PB_VOLT (1 << 3) // Push button 3: Voltage [and Minutes set when PB1 is Time set]. #define PB_IN P2IN // The switches read as zero when pressed (and one when released). #define TIME_IN (!(PB_IN & PB_TIME)) #define TEMP_IN (!(PB_IN & PB_TEMP)) #define VOLT_IN (!(PB_IN & PB_VOLT)) #define HOUR_IN (!(PB_IN & PB_TEMP)) #define MIN_IN (!(PB_IN & PB_VOLT)) #define PB_IE P2IE #define PB_IFG P2IFG enum { FALSE, // 0 TRUE // 1 }; enum { TIME, // 0 TEMPERATURE, // 1 VOLTAGE // 2 }; typedef unsigned int word; // Type definition for 'word' // Function prototypes. int __low_level_init(void); // Low level initialization. void init_sys(void); // System initialization. void time(void); // Real-time clock mode and display time. void temperature(unsigned char mode); // Temperature F or C mode. void voltage(void); // Millivoltmeter mode. void display_temp(word temp, unsigned char mode); // Display temperature (Fahrenheit: xxF, Celsius: xxC). void display_volts(word millivolts); // Display volts (x.xxx). void display_value(word value); // Display value (xxxx). int itobcd(word i); // Hex word to bcd. void check_cal(void); // Check to initiallize cal constant. void Temp_cal(void); // Temp sensor calibration function. void Ref_cal(void); // ADC12 Ref calibration function. void flash_write(word* address, word data); // Write the (integer) data to the addressed flash. void flash_erase(word* address); // Erase the addressed flash segment. // Variables in RAM. static unsigned char secs = 0, mins = 0, hrs = 0x12; // Real time clock (in BCD). static unsigned char mode = TIME; // Initially display time. static unsigned char temp_mode = TRUE; // Initially display temperature in Fahrenheit. FALSE: Celcius. TRUE: Fahrenheit. static unsigned char calmode = 0; static word Refcal_ram; // ADC12 1.5V reference calibration variable. static word Temp_slope_ram;// Temperature slope calibration variable. static word Temp_offset_ram;// Temperature offset calibration variable. // Global "variables" (in flash-INFO). // The following variables are allocated in the flash Information memory; they can be read directly, but the flash // controller must be used to erase and write them. These variables are non-volatile. #pragma memory=dataseg(INFO) static word Refcal_flash ; // ADC12 reference calibration in Flash INFO memory. static word Temp_slope;// Temperature sensor slope calibration in Flash INFO memory. static word Temp_offset;//Temperature sensor offset calibration in Flash INFO memory. #pragma memory=default // LCD segment definitions. #define a 0x01 #define b 0x10 #define c 0x02 #define d 0x20 #define e 0x04 #define f 0x40 #define g 0x08 #define h 0x80 // LCD Character Generator (stored in Flash). const char char_gen[] = { a+b+c+d+e+f, // Displays "0" b+c, // Displays "1" a+b+d+e+g, // Displays "2" a+b+c+d+g, // Displays "3" b+c+f+g, // Displays "4" a+c+d+f+g, // Displays "5" a+c+d+e+f+g, // Displays "6" a+b+c, // Displays "7" a+b+c+d+e+f+g, // Displays "8" a+b+c+d+f+g, // Displays "9" a+b+c+e+f+g, // Displays "A" 0x00, // Displays Blank a+d+e+f, // Displays "C" a+b+f+g, // Displays "degrees" o a+d+e+f+g, // Displays "E" a+e+f+g // Displays "F" }; #undef a #undef b #undef c #undef d #undef e #undef f #undef g #undef h void main(void) { init_sys();// System Initialization check_cal();// Check if INFO has calibration constants Refcal_ram = Refcal_flash;// Register RAM copy of Vref from Flash Temp_slope_ram = Temp_slope;// Register RAM copy of Temp sensor slope Temp_offset_ram = Temp_offset;// Register RAM copy of Temp sensor offset calmode = P2IN & PB_VOLT; // Register Calibration mode(dual boot) while (1) { // THE FOLLOWING LINES SHOULD BE UNCOMMENTED FOR HIGH POWER DEMO. IE2 = 0x00; // Disable Basic Timer interrupt. while (!(IFG2 & BTIFG)); // Poll for Basic Timer interrupt. IE2 |= BTIFG; // Enable Basic Timer interrupt. // THE FOLLOWING LINE SHOULD BE UNCOMMENTED FOR ULTRA LOW POWER DEMO. // LPM3; // The main loop is synchronous to the 1 second interrupt. switch (mode) { case TIME: time(); break; // Time mode case TEMPERATURE: temperature(temp_mode); break; // Temperature mode case VOLTAGE: voltage(); break; // Milli-volt mode default: break; } } // while (1) } // main() int __low_level_init(void) { unsigned int i; FLL_CTL0 |= XCAP18PF; // Set load capacitance for xtal. WDTCTL = WDTPW | WDTHOLD; // Disable the Watchdog. for (i = 0; i < 20000; i++); // Delay - to allow watch crystal to stabilize. return (1); /* Flag to initialize the data segment. */ } void init_sys(void) { // Configure all unused port pins as outputs. P1OUT = 0; P1DIR = 0xff; P2OUT = 0; P2DIR = ~(PB_TIME | PB_TEMP | PB_VOLT); P2IES = PB_TIME | PB_TEMP | PB_VOLT; P2IFG = 0; P2IE = PB_TIME | PB_TEMP | PB_VOLT; P3OUT = 0; P3DIR = 0xff; P4OUT = 0; P4DIR = 0xff; P5OUT = 0; P5DIR = 0xff; P6OUT = 0; P6DIR = 0x7f; // P6.7 = analog input. P6SEL = BIT7; // P6.7 = analog input. LCDCTL = LCDSG0_6 | LCDSTATIC | LCDON; // Static LCD, segments = 0 - 23. { int i; for (i = 0; i < 20; LCDMEM[i++] = 0); // Clear LCD memory. } BTCTL = BTFRFQ1 | BTFRFQ0 | BTDIV | BTIP2 | BTIP1; // 1 second interrupt, and LCD frequency. IE2 |= BTIE; // Enable Basic Timer interrupt. ADC12CTL1 = SHP; ADC12IE = BIT0; _EINT(); // Enable interrupts. } // sys_init() void time(void) { int display_hrsmins = TRUE; // Display hours:minutes when setting the time. ADC12CTL0 = 0; // Turn-off ADC12. if (TIME_IN) // Set time button. { PB_IE &= ~(PB_TIME | PB_TEMP | PB_VOLT); // Disable PB interrupts while setting time. if (MIN_IN) // Increment minutes button. { if ((++mins & 0x0f) == 0x0a) { mins = (mins + 0x10) & 0xf0; } if (mins == 0x60) { mins = 0; } } else { if (HOUR_IN) // Increment hours button. { if ((++hrs & 0x0f) == 0x0a) { hrs = (hrs + 0x10) &0xf0; } if (hrs == 0x13) { hrs = 1; } } else { display_hrsmins = FALSE; // Display minutes:seconds. } } PB_IFG = 0; PB_IE |= PB_TIME | PB_TEMP | PB_VOLT; } LCDM8 ^= 0x10; // Toggle ":" // Display hours:minutes when incrementing the time, else display minutes:seconds. display_hrsmins ? display_value(((hrs << 8) + mins)) : display_value((((mins << 8) + secs) & 0xfff)); } // time() void temperature(unsigned char mode) { ADC12CTL0 &= ~ENC; // Clear ENC first. ADC12CTL0 = SHT0_15 | REFON | ADC12ON; ADC12MCTL0 = INCH_10 | SREF_1; // Sample channel 10 using internal reference. TACTL = TASSEL0 | TACLR | MC1; // TACLK = ACLK, 16-bit up-mode. CCR1 = 1500; // Delay to allow Ref to settle. CCTL1 = CCIE; // Compare-mode interrupt. LPM3; // Wait for delay. ADC12CTL0 |= ENC | ADC12SC; // Start conversion. LPM3; // Wait for conversion completion. ADC12CTL0 &= ~ENC; // Clear ENC first. ADC12CTL0 = 0; // Turn-off ADC12. display_temp(ADC12MEM0, mode); } // temperature() void voltage(void) { ADC12CTL0 &= ~ENC; // Clear ENC first. ADC12CTL0 = SHT0_8 | REFON | ADC12ON; ADC12MCTL0 = INCH_7 | SREF_1; // Sample channel 7 using internal 1.5V reference. TACTL = TASSEL0 | TACLR | MC1; // TACLK = ACLK, 16-bit up-mode. CCR1 = 1500; // Delay to allow Ref to settle. CCTL1 = CCIE; // Compare-mode interrupt. LPM3; // Wait for delay. ADC12CTL0 |= ENC | ADC12SC; // Start conversion. LPM3; // Wait for conversion completion. ADC12CTL0 &= ~ENC; // Clear ENC first. if (calmode == 0) { Ref_cal(); } else { Refcal_ram = Refcal_flash; } display_volts((int) (((long) ADC12MEM0 * Refcal_ram) / 4096)); // Display the (milli) voltage. } // voltage() void display_temp(word temp, unsigned char mode) // Display temperature Fahrenheit { word sign; signed int tempF, tempC; // Variables for temperature in F and C. if (calmode == 0)// Check if Cal mode { /* Temp_cal();// Run calibration function mode = FALSE; // Force display to deg F mode */ } else { Temp_slope_ram = Temp_slope; // Make RAM copy from Flash (calibrated value) Temp_offset_ram = Temp_offset;// Make RAM copy from Flash (calibrated value) } tempF = (((long) temp * Temp_slope_ram) / 4096 - Temp_offset_ram); if (mode) { tempC = (((long) tempF - 32) * 5/9); if (tempC > 26) { P1OUT |= 0x01; // Turn-on LED. } else { P1OUT &= ~0x01; // Turn-off LED. } if (tempC < 0 ) { sign = 1; tempC= ~tempC + 1; // Make tempC positive (using simple 2's complement operation). } else { sign = 0; } display_value(((itobcd(tempC)) << 4) + 0x000c); // Display temperature degrees C. } else { if (tempF > 79) { P1OUT |= 0x01; // Turn-on LED. } else { P1OUT &= ~0x01; // Turn-off LED. } if (tempF < 0) { sign = 1; tempF = ~tempF + 1; // Make tempF positive (using simple 2's complement operation). } else { sign = 0; } display_value(((itobcd(tempF))<< 4)+ 0x000f); // Display temperature degrees F. } if (sign) { LCDM10 |= 0x01; // display "-". } else { LCDM10 &= 0xfe; // clear "-". } } // display_temp(temp, mode) void display_volts(word millivolts) // Display volts (v.vvv). { display_value((itobcd(millivolts))); LCDM15 |= 0x10; // Set "." (v.vvv). } // display_volts(millivolts) void display_value(word value) { word digits, colon, segments; digits = value; // Preserve value. // First digit. value &= 0x0f; segments = char_gen[value]; // Get LCD segment data for the current digit from the character generator. LCDM1 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM2 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM3 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM4 = segments ; // Segments to LCD. colon = LCDM8 & 0x10 ; // Save current colon info temporarily. value = digits >> 4; // Second digit. value &= 0x0f; segments = char_gen[value] ; // Get LCD segment data for the current digit from the character generator. LCDM5 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM6 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM7 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM8 = segments|colon ; // Segments to LCD. digits = digits >> 8; // Third digit. value = 0x0f & digits; segments = char_gen[value]; // Get LCD segment data for the current digit from the character generator. LCDM11 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM12 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM13 = segments ; // Segments to LCD. segments >>= 1; // Shift to the next pair of segments. LCDM14 = segments; // Segments to LCD. if (digits & 0x10) // Display the "3 1/2th" digit. { LCDM15 |= 0x01; // Enable "1" for the "3 1/2th" digit } else { LCDM15 &= 0xfe; } } // display_value(value) int itobcd(word i) // Convert hex word to BCD. { int bcd = 0; int j = 0; while (i > 9) { bcd |= ((i % 10) << j); i /= 10; j += 4; } // itobcd(i) return (bcd | (i << j)); } void check_cal(void) // Check if INFO memory blank or contains calibration constants { if (Refcal_flash == 0xffff) // Check if Flash INFO Refcal erased? { flash_write(&Refcal_flash, 1500); // Intialize with ideal Vref value 1500mV. flash_write(&Temp_slope, 761); // Intialize ideal temperature diode slope for deg F . flash_write(&Temp_offset, 469); // Intialize ideal temperature diode offset for deg F . } } // check_cal() void Temp_cal(void) // Temperature sensor calibration { /*PB_IE = 0; // Disable push button interrupts. if (TIME_IN) // If Time button pressed decrement Temp offset { Temp_offset_ram--; } if (TEMP_IN) // If Temp button pressed increment Temp offset { Temp_offset_ram++; } Refcal_ram = Refcal_flash;// Make RAM copy of Vref cal value Temp_slope_ram = Temp_slope;// Make RAM copy of Temp slope value if (VOLT_IN) // If Volt button pressed store cal in Flash { flash_erase(&Refcal_flash); // Erase Flash INFO segment A. flash_write(&Refcal_flash, Refcal_ram); // Write cal data to Refcal. flash_write(&Temp_offset, Temp_offset_ram); // flash_write(&Temp_slope, Temp_slope_ram); // calmode |= PB_VOLT; // Clear cal mode. PB_IFG = 0; // Clear any pending push button int flags. PB_IE = PB_TIME | PB_TEMP | PB_VOLT; // Enable push button interrupts. } */ }// void Temp_cal() void Ref_cal(void) // ADC12 Vref calibration { PB_IE = 0; // Disable push button interrupts. if (TIME_IN) // If Time button pressed decrement cal { Refcal_ram--; } if (TEMP_IN) // If Temp button pressed increment cal { Refcal_ram++; } if (VOLT_IN) // If Volt button pressed store cal in Flash { flash_erase(&Refcal_flash); // Erase Flash INFO segment of Refcal. flash_write(&Refcal_flash, Refcal_ram); // Write cal data to Refcal. flash_write(&Temp_offset, Temp_offset_ram); // Write Temp cal data. flash_write(&Temp_slope, Temp_slope_ram); // calmode |= PB_VOLT; // Clear cal mode. PB_IFG = 0; // Clear any pending push button int flags. PB_IE = PB_TIME | PB_TEMP | PB_VOLT; // Enable push button interrupts. } } // Ref_cal() void flash_write(word* address, word data) // Write the (integer) data to the addressed flash. { word gie = _BIC_SR(GIE) & GIE; // Disable interrupts. FCTL3 = FWKEY; // Unlock the flash. FCTL1 = FWKEY | WRT; // Enable flash write. *address = data; // Write the data to the flash. FCTL1 = FWKEY; // Disable flash write. FCTL3 = FWKEY | LOCK; // Lock the flash. _BIS_SR(gie); // Restore interrupts (to previous state). }// flash_write(word* address, data) void flash_erase(word* address) // Erase the addressed flash segment. { word gie = _BIC_SR(GIE) & GIE; // Disable interrupts. FCTL3 = FWKEY; // Unlock the flash. FCTL1 = FWKEY | ERASE; // Enable flash segment erase. *address = 0; // Erase the flash segment. FCTL1 = FWKEY; // Disable flash segment erase. FCTL3 = FWKEY | LOCK; // Lock the flash. _BIS_SR(gie); // Restore interrupts (to previous state). }// flash_erase(word* address, data) // Interrupt Service Routines. interrupt [PORT2_VECTOR] void p2_isr(void) { static int toggle_temp_mode = FALSE; P1OUT &= ~0x01; // Turn off LED (if on). LCDM15 = 0xef; // Clear "." (if any). LCDM15 &= 0xee; // Clear "1.". LCDM8 &= 0xef; // Clear ":". if (TIME_IN) { mode = TIME; toggle_temp_mode = FALSE; } else { if (TEMP_IN) { mode = TEMPERATURE; if (toggle_temp_mode) { temp_mode = temp_mode ? FALSE : TRUE; // Toggle between Fahrenheit and Celcius after second button push. } else { toggle_temp_mode = TRUE; } } else { if (VOLT_IN) { mode = VOLTAGE; toggle_temp_mode = FALSE; } } } PB_IFG = 0; // Clear all button interrupts. // Note that LPM3 is *not* cleared. Basically, the switching of modes is synchronous to the 1 second interrupt. }// p2_isr() interrupt [BASICTIMER_VECTOR] void bt_isr(void) { if ((++secs & 0x0f) == 0x0a) { secs = (secs + 0x10) & 0xf0; // Increment Seconds in BCD. if (secs == 0x60) { secs = 0; if ((++mins & 0x0f) == 0x0a) // Increment Minutes in BCD. { mins = (mins + 0x10) & 0xf0; if (mins == 0x60) { mins = 0; if (++hrs == 0x13) hrs = 0x01;// If hrs transition is 12 to 13, hrs = 1 if ((hrs & 0x0f) == 0x0a) // Check hrs and do BCD correction if required { hrs = 0x10; } } } } } LPM3_EXIT; // Exit LPM3 mode on return }// bt_isr() interrupt [ADC_VECTOR] void adc_isr(void) { ADC12IFG &= ~BIT0; // Clear MEM0 interrupt flag. LPM3_EXIT; // The ADC value is available in ADC12MEM0. }// adc_isr() enum { NO_INT = 0, CC1_INT = 2, CC2_INT = 4, TA_INT = 10 }; interrupt [TIMERA1_VECTOR] void ta1_isr(void) { switch (TAIV) { case NO_INT: break; case CC1_INT: TACTL = 0; break; // Disable TimerA. case CC2_INT: break; case TA_INT: break; default: break; } LPM3_EXIT; // Exit LPM3 on return }// ta1_isr()