Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 10/16/2010 in all areas

  1. 28 points
    oPossum

    Using the internal temperature sensor

    TI has some sample code for the internal temperature sensor, but it does not explain how to scale the ADC reading to useful units of degrees. Here is a step-by-step explanation of how to do the scaling with integer math for degrees C, K and F. There is also sample code to display the temperature on a Nokia 5110 LCD. The data sheet (SLAU144) has this formula for converting temperature in degrees Celsius to voltage. V = 0.00355 * C + 0.986 What we need is a formula for converting voltage to temperature. Rewrite the data sheet fomula with temperature on the left 0.00355 * C + 0.986 = V Divide both sides by 0.00355 C + 277.75 = V / 0.00355 Subtract 277.75 from both sides C = V / 0.00355 - 277.75 Now we have a formula for converting voltage to temperature. The data sheet has this formula for converting voltage to ADC value, once again the opposite of what we neeed. For Vref- == 0 A = 1023 * V / Vref Swap sides 1023 * V / Vref = A Multiply by Vref 1023 * V = A * Vref Divide by 1023 V = A * Vref / 1023 For a 1.5V reference V = A * 1.5 / 1023 Simplify V = A * 0.0014663 Substitute ADC conversion forumula for voltage in the temperature conversion formula. C = A * 0.0014663 / 0.00355 - 277.75 Simplify C = A * 0.413 - 277.75 Now we have a formula to convert ADC reading to temperature. It uses real numbers, so floating point math is required for good precision. Floating point is slow and requires more flash, so let's use integer math instead. Multiply by 65536 (2^16) and then divide by the same. C = (A * 27069 - 18202393) / 65536 Use a right shift instead of divide. This will become a move of high word to low word. C = (A * 27069 - 18202393) >> 16 Add 0.5 * 65536 to impove rounding. C = (A * 27069 - 18202393 + 32768) >> 16 Simplify. C = (A * 27069 - 18169625) >> 16 So that is how to go from ADC to degrees Celsius. To convert degrees C to degees K. K = C + 273.15 Applied to ADC to degrees C conversion formula. K = (A * 27069 - 18169625) >> 16 - 273.15 Implement with integer math by multiplying by 65536 K = (A * 27069 - 18169625 - 17,901,158) >> 16 Simplify. K = (A * 27069 - 268467) >> 16 To convert degrees C to degrees F. F = C * 9 / 5 + 32 Applied to voltage to degrees C conversion forumula F = (V / 0.00355 - 277.75) * 9 / 5 + 32 Multiply by 9 F = (V / 0.0003944 - 2499.75) / 5 + 32 Divide by 5 F = (V / 0.0019722 - 499.95) + 32 Add 32 F = V / 0.0019722 - 467.95 Substitute ADC to voltage forumula F = A * 0.0014663 / 0.0019722 - 467.95 Simplifiy F = A * 0.7435 - 467.95 Convert to integer F = (A * 48724 - 30667156) >> 16 Improve rounding F = (A * 48724 - 30667156 + 32768) >> 16 Simplify F = (A * 48724 - 30634388) >> 16 So now we have three formulas to convert ADC reading to degrees C, K and F using fast and compact integer math. C = (A * 27069 - 18169625) >> 16 K = (A * 27069 - 268467) >> 16 F = (A * 48724 - 30634388) >> 16 Using the ADC value, rather than a different temperature scale, will ensure greatest precision for each temperature scale. main.c #include #include #include "lcd.h" #define ADC_SLEEP // Sleep during ADC conversion //#define SHOW_ADC // Show ADC raw and ADC millivolts // Print integer from -999 to 9999 using 12 x 16 font void print_int(int i, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; unsigned x = 48; do { d = div(d.quot, 10); pd12(d.rem, x -= 12, y); } while(d.quot); if(neg) pd12(14, x -= 12, y); while(x) pd12(10, x -= 12, y); } // Print integer from 0 to 9999 vertically using 6 x 8 font void print_v(int i, unsigned x) { unsigned y = 4; unsigned c; if(i < 0 || i > 9999) return; div_t d; d.quot = i; do { d = div(d.quot, 10); c = d.rem + '0'; lcd_print((char *)&c, x, --y); } while(d.quot); c = ' '; while(y) lcd_print((char *)&c, x, --y); } void main(void) { unsigned adc; // ADC value int c, k, f; // Temperature in degrees C, K, and F unsigned mv; // ADC reading in millivolts // WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset // lcd_init(); // Initialize LCD lcd_clear(0); // pd12(15, 48, 0); // Degrees pd12(17, 59, 0); // F pd12(15, 48, 2); // Degrees pd12(16, 58, 2); // C pd12(15, 48, 4); // Degrees pd12(18, 59, 4); // K #ifdef SHOW_ADC // lcd_print("Am", 72, 4); // AD / mV lcd_print("DV", 72, 5); // #endif // // ADC10CTL0 = 0; // Configure ADC ADC10CTL1 = INCH_10 | ADC10DIV_3; // ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE; //ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE | REF2_5V; #ifdef ADC_SLEEP // ADC10CTL0 |= ADC10IE; // Enable ADC conversion complete interrupt #endif // // for(; { // for-ever #ifdef ADC_SLEEP // ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion __bis_SR_register(LPM0_bits + GIE); // Sleep until conversion complete #else // ADC10CTL0 &= ~ADC10IFG; // Clear conversion complete flag ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete #endif // // adc = ADC10MEM; // Read ADC // // Convert to temperature c = ((27069L * adc) - 18169625L) >> 16; // Vref = 1.5V //c = ((45115L * adc) - 18169625L) >> 16; // Vref = 2.5V // k = ((27069L * adc) - 268467L) >> 16; // Vref = 1.5V //k = ((45115L * adc) - 268467L) >> 16; // Vref = 2.5V // f = ((48724L * adc) - 30634388L) >> 16; // Vref = 1.5V //f = ((81206L * adc) - 30634388L) >> 16; // Vref = 2.5V // // Convert to millivolts mv = (96094L * adc + 32768) >> 16; // Vref = 1.5V //mv = (160156L * adc + 32768) >> 16; // Vref = 2.5V // // Display on LCD print_int(f, 0); // Degrees F print_int(c, 2); // Degrees C print_int(k, 4); // Degrees K // #ifdef SHOW_ADC // print_v(adc, 72); // ADC print_v(mv, 78); // ADC millivolts #endif // // //__delay_cycles(100000); // } // } #pragma vector = ADC10_VECTOR // ADC conversion complete interrupt __interrupt void ADC10_ISR(void) // { // __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // lcd.h typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type); void lcd_home(void); void lcd_pos(unsigned char x, unsigned char y); void lcd_clear(unsigned char x); void lcd_init(void); void lcd_print(char *s, unsigned x, unsigned y); void pd12(unsigned n, unsigned x, unsigned y); lcd.c #include #include "lcd.h" //static const unsigned TXD = BIT1; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; static const unsigned LCD_CLK = BIT5; static const unsigned LCD_BACKLIGHT = BIT6; static const unsigned LCD_DATA = BIT7; static const unsigned LCD_DC = BIT0; // PORT2 static const unsigned LCD_CE = BIT1; // PORT2 void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; P2OUT &= ~LCD_CE; do { mask = 0x0080; do { if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; mask >>= 1; } while(!(mask & 1)); if(!type) P2OUT &= ~LCD_DC; if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; P2OUT |= LCD_DC; if(!(type & 2)) ++cmd; } while(--len); P2OUT |= LCD_CE; } static const unsigned char home[] = { 0x40, 0x80 }; void lcd_home(void) { lcd_send(home, sizeof(home), lcd_command); } void lcd_pos(unsigned char x, unsigned char y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); } void lcd_clear(unsigned char x) { lcd_home(); lcd_send(&x, 504, lcd_data_repeat); lcd_home(); } void lcd_init(void) { static const unsigned char init[] = { 0x20 + 0x01, // function set - extended instructions enabled //0x80 + 64, // set vop (contrast) 0 - 127 0x80 + 66, // set vop (contrast) 0 - 127 0x04 + 0x02, // temperature control 0x10 + 0x03, // set bias system 0x20 + 0x00, // function set - chip active, horizontal addressing, basic instructions 0x08 + 0x04 // display control - normal mode }; P1REN = RXD | SWITCH; P1DIR = LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P1OUT = LCD_CLK | RXD | SWITCH | LCD_BACKLIGHT; P2REN = 0; P2DIR = LCD_DC | LCD_CE; P2OUT = LCD_CE; __delay_cycles(20000); P2OUT |= LCD_DC; __delay_cycles(20000); lcd_send(init, sizeof(init), lcd_command); } static const unsigned char font6x8[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 20 32 0x00, 0x00, 0x5F, 0x00, 0x00, // 21 33 ! 0x00, 0x07, 0x00, 0x07, 0x00, // 22 34 " 0x14, 0x7F, 0x14, 0x7F, 0x14, // 23 35 # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24 36 $ 0x23, 0x13, 0x08, 0x64, 0x62, // 25 37 % 0x36, 0x49, 0x56, 0x20, 0x50, // 26 38 & 0x00, 0x08, 0x07, 0x03, 0x00, // 27 39 ' 0x00, 0x1C, 0x22, 0x41, 0x00, // 28 40 ( 0x00, 0x41, 0x22, 0x1C, 0x00, // 29 41 ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A 42 * 0x08, 0x08, 0x3E, 0x08, 0x08, // 2B 43 + 0x00, 0x40, 0x38, 0x18, 0x00, // 2C 44 , 0x08, 0x08, 0x08, 0x08, 0x08, // 2D 45 - 0x00, 0x00, 0x60, 0x60, 0x00, // 2E 46 . 0x20, 0x10, 0x08, 0x04, 0x02, // 2F 47 / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 30 48 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 31 49 1 0x42, 0x61, 0x51, 0x49, 0x46, // 32 50 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 33 51 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 34 52 4 0x27, 0x45, 0x45, 0x45, 0x39, // 35 53 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 36 54 6 0x41, 0x21, 0x11, 0x09, 0x07, // 37 55 7 0x36, 0x49, 0x49, 0x49, 0x36, // 38 56 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 39 57 9 0x00, 0x00, 0x14, 0x00, 0x00, // 3A 58 : 0x00, 0x00, 0x40, 0x34, 0x00, // 3B 59 ; 0x00, 0x08, 0x14, 0x22, 0x41, // 3C 60 < 0x14, 0x14, 0x14, 0x14, 0x14, // 3D 61 = 0x00, 0x41, 0x22, 0x14, 0x08, // 3E 62 > 0x02, 0x01, 0x51, 0x09, 0x06, // 3F 63 ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // 40 64 @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // 41 65 A 0x7F, 0x49, 0x49, 0x49, 0x36, // 42 66 B 0x3E, 0x41, 0x41, 0x41, 0x22, // 43 67 C 0x7F, 0x41, 0x41, 0x41, 0x3E, // 44 68 D 0x7F, 0x49, 0x49, 0x49, 0x41, // 45 69 E 0x7F, 0x09, 0x09, 0x09, 0x01, // 46 70 F 0x3E, 0x41, 0x49, 0x49, 0x7A, // 47 71 G 0x7F, 0x08, 0x08, 0x08, 0x7F, // 48 72 H 0x00, 0x41, 0x7F, 0x41, 0x00, // 49 73 I 0x20, 0x40, 0x41, 0x3F, 0x01, // 4A 74 J 0x7F, 0x08, 0x14, 0x22, 0x41, // 4B 75 K 0x7F, 0x40, 0x40, 0x40, 0x40, // 4C 76 L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // 4D 77 M 0x7F, 0x04, 0x08, 0x10, 0x7F, // 4E 78 N 0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F 79 O 0x7F, 0x09, 0x09, 0x09, 0x06, // 50 80 P 0x3E, 0x41, 0x51, 0x21, 0x5E, // 51 81 Q 0x7F, 0x09, 0x19, 0x29, 0x46, // 52 82 R 0x26, 0x49, 0x49, 0x49, 0x32, // 53 83 S 0x01, 0x01, 0x7F, 0x01, 0x01, // 54 84 T 0x3F, 0x40, 0x40, 0x40, 0x3F, // 55 85 U 0x1F, 0x20, 0x40, 0x20, 0x1F, // 56 86 V 0x3F, 0x40, 0x38, 0x40, 0x3F, // 57 87 W 0x63, 0x14, 0x08, 0x14, 0x63, // 58 88 X 0x03, 0x04, 0x78, 0x04, 0x03, // 59 89 Y 0x61, 0x51, 0x49, 0x45, 0x43, // 5A 90 Z 0x00, 0x7F, 0x41, 0x41, 0x41, // 5B 91 [ 0x02, 0x04, 0x08, 0x10, 0x20, // 5C 92 '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // 5D 93 ] 0x04, 0x02, 0x01, 0x02, 0x04, // 5E 94 ^ 0x80, 0x80, 0x80, 0x80, 0x80, // 5F 95 _ 0x00, 0x03, 0x07, 0x08, 0x00, // 60 96 ' 0x20, 0x54, 0x54, 0x54, 0x78, // 61 97 a 0x7F, 0x28, 0x44, 0x44, 0x38, // 62 98 b 0x38, 0x44, 0x44, 0x44, 0x28, // 63 99 c 0x38, 0x44, 0x44, 0x28, 0x7F, // 64 100 d 0x38, 0x54, 0x54, 0x54, 0x18, // 65 101 e 0x00, 0x08, 0x7E, 0x09, 0x02, // 66 102 f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // 67 103 g 0x7F, 0x08, 0x04, 0x04, 0x78, // 68 104 h 0x00, 0x44, 0x7D, 0x40, 0x00, // 69 105 i 0x00, 0x20, 0x40, 0x40, 0x3D, // 6A 106 j 0x00, 0x7F, 0x10, 0x28, 0x44, // 6B 107 k 0x00, 0x41, 0x7F, 0x40, 0x00, // 6C 108 l 0x7C, 0x04, 0x78, 0x04, 0x78, // 6D 109 m 0x7C, 0x08, 0x04, 0x04, 0x78, // 6E 110 n 0x38, 0x44, 0x44, 0x44, 0x38, // 6F 111 o 0xFC, 0x18, 0x24, 0x24, 0x18, // 70 112 p 0x18, 0x24, 0x24, 0x18, 0xFC, // 71 113 q 0x7C, 0x08, 0x04, 0x04, 0x08, // 72 114 r 0x48, 0x54, 0x54, 0x54, 0x24, // 73 115 s 0x04, 0x04, 0x3F, 0x44, 0x24, // 74 116 t 0x3C, 0x40, 0x40, 0x20, 0x7C, // 75 117 u 0x1C, 0x20, 0x40, 0x20, 0x1C, // 76 118 v 0x3C, 0x40, 0x30, 0x40, 0x3C, // 77 119 w 0x44, 0x28, 0x10, 0x28, 0x44, // 78 120 x 0x4C, 0x90, 0x90, 0x90, 0x7C, // 79 121 y 0x44, 0x64, 0x54, 0x4C, 0x44, // 7A 122 z 0x00, 0x08, 0x36, 0x41, 0x00, // 7B 123 { 0x00, 0x00, 0x77, 0x00, 0x00, // 7C 124 | 0x00, 0x41, 0x36, 0x08, 0x00, // 7D 125 } 0x02, 0x01, 0x02, 0x04, 0x02, // 7E 126 ~ 0x00, 0x06, 0x09, 0x09, 0x06, // 7F 127 degrees }; void lcd_print(char *s, unsigned x, unsigned y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); while(*s) { lcd_send(&font6x8[*s - 32][0], 5, lcd_data); lcd_send(&font6x8[0][0], 1, lcd_data); ++s; } } static const unsigned char num11x16[19][11 * 2] = { 0x00,0xF0,0xFC,0xFE,0x06,0x02,0x06,0xFE,0xFC,0xF0,0x00, // 0 0x00,0x07,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x07,0x00, 0x00,0x00,0x08,0x0C,0xFC,0xFE,0xFE,0x00,0x00,0x00,0x00, // 1 0x00,0x20,0x20,0x20,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x00, 0x00,0x0C,0x0E,0x06,0x02,0x02,0x86,0xFE,0x7C,0x38,0x00, // 2 0x00,0x30,0x38,0x3C,0x36,0x33,0x31,0x30,0x30,0x38,0x00, 0x00,0x0C,0x0E,0x86,0x82,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 3 0x00,0x18,0x38,0x30,0x20,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x00,0xC0,0x20,0x18,0x04,0xFE,0xFE,0xFE,0x00,0x00, // 4 0x00,0x03,0x02,0x02,0x02,0x22,0x3F,0x3F,0x3F,0x22,0x02, 0x00,0x00,0x7E,0x7E,0x46,0x46,0xC6,0xC6,0x86,0x00,0x00, // 5 0x00,0x18,0x38,0x30,0x20,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0xC0,0xF0,0xF8,0xFC,0x4C,0xC6,0xC2,0x82,0x00,0x00, // 6 0x00,0x0F,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0x06,0x06,0x06,0x06,0x06,0xC6,0xF6,0x3E,0x0E,0x00, // 7 0x00,0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,0x00, 0x00,0x38,0x7C,0xFE,0xC6,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 8 0x00,0x0E,0x1F,0x3F,0x31,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x78,0xFC,0xFE,0x86,0x02,0x86,0xFE,0xFC,0xF8,0x00, // 9 0x00,0x00,0x00,0x21,0x21,0x31,0x1D,0x1F,0x0F,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // : 0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // . 0x00,0x38,0x38,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x00,0x00, // / 0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // - 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x00,0x18,0x3C,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xF0,0xF8,0x0C,0x06,0x02,0x02,0x02,0x02,0x0E,0x0C,0x00, // C 0x03,0x07,0x0C,0x18,0x10,0x10,0x10,0x10,0x1C,0x0C,0x00, 0xFE,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, // F 0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xFE,0xFE,0x40,0xE0,0xB0,0x18,0x0C,0x06,0x02,0x00,0x00, // K 0x1F,0x1F,0x00,0x00,0x01,0x03,0x06,0x0C,0x18,0x10,0x00 }; void pd12(unsigned n, unsigned x, unsigned y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 + y; lcd_send(c, 2, lcd_command); lcd_send(num11x16[n], 11, lcd_data); c[1] = 0x41 + y; lcd_send(c, 2, lcd_command); lcd_send(num11x16[n] + 11, 11, lcd_data); }
  2. 14 points
    This uses code from Software RTC, Using the internal temperature sensor and Nokia 5110 template class. The code has been modified and refined a bit for this project. main.cpp #include #include #include #include #include "nokia7110tl.h" using namespace nokia7110; static const unsigned char ti[] = { 48, 48 / 8, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00,0x00,0xF0, 0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFC,0xFC,0x3C,0x80,0xFC,0xFD, 0xFD,0xFD,0x3D,0x81,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00, 0x00,0x1C,0x7C,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF7,0xF0,0xF0,0x00,0xF0,0xFF,0xFF,0xFF, 0xFF,0x0F,0xF0,0xF0,0xF0,0xF0,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xF0,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x3F,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,0x3F,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0x1F,0x00,0x1E,0x1F,0x1F,0x1F,0x1F, 0x01,0x1E,0x1F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x7F,0x7F,0x3F,0x3F,0x3F,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x03, 0x0F,0x3F,0xFF,0xFE,0xFE,0xFE,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0x7F,0x1F,0x07,0x03,0x03,0x01,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x07,0x0F,0x0F,0x1F,0x1F,0x3F, 0x3F,0x3F,0x3F,0x1C,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; /* P1.0 Reset P1.3 Temp Sensor (Not used) P2.0 Serial Data P2.1 Backlight P2.2 Chip Select P2.3 Data/Command P2.4 Serial Clock P2.5 Pushbutton */ // P1 static const unsigned LCD_RESET = BIT0; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; // P2 static const unsigned LCD_DATA = BIT0; static const unsigned LCD_BACKLIGHT = BIT1; static const unsigned LCD_CE = BIT2; static const unsigned LCD_DC = BIT3; static const unsigned LCD_CLK = BIT4; static const unsigned LCD_BTN = BIT5; Nokia7110 lcd; void show_time(const struct tm *t) // Show time on LCD { static const char *dow[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; int x, w; const char *s; div_t d; if(t->tm_hour < 10) { x = -2; lcd.fill(0, 0, 9, 2, 0); d.rem = t->tm_hour; } else { x = 3; lcd.fill(0, 0, 3, 2, 0); d = div(t->tm_hour, 10); lcd.pd12(d.quot, x, 0); } lcd.pd12(d.rem, x + 11, 0); lcd.pd12(11, x + 22, 0); d = div(t->tm_min, 10); lcd.pd12(d.quot, x + 27, 0); lcd.pd12(d.rem, x + 38, 0); lcd.pd12(11, x + 49, 0); d = div(t->tm_sec, 10); lcd.pd12(d.quot, x + 54, 0); lcd.pd12(d.rem, x + 65, 0); lcd.fill(x + 76, 0, 7 - x, 2, 0); if(t->tm_mon < 9) { x = -5; lcd.fill(0, 2, 6, 2, 0); d.rem = t->tm_mon + 1; } else { x = 0; d = div(t->tm_mon + 1, 10); lcd.pd12(d.quot, x, 2); } lcd.pd12(d.rem, x + 11, 2); lcd.pd12(13, x + 22, 2); d = div(t->tm_mday, 10); lcd.pd12(d.quot, x + 30, 2); lcd.pd12(d.rem, x + 41, 2); lcd.pd12(13, x + 52, 2); d = div(t->tm_year % 100, 10); lcd.pd12(d.quot, x + 60, 2); lcd.pd12(d.rem, x + 71, 2); if(x) lcd.fill(x + 82, 2, 1 - x, 2, 0); s = dow[t->tm_wday]; w = strlen(s) * 6; x = (83 - w) >> 1; lcd.fill(0, 4, x, 1, 0); lcd.print((unsigned char)x, (unsigned char)4, s); x += w; lcd.fill(x, 4, 83 - x, 1, 0); } // Print integer from -999 to 9999 using 12 x 16 font void print_int(int i, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; unsigned x = 48; do { d = div(d.quot, 10); lcd.pd12(d.rem, x -= 12, y); } while(d.quot); if(neg) lcd.pd12(14, x -= 12, y); while(x) lcd.pd12(10, x -= 12, y); } // Print integer from -999 to 9999 using 6 x 8 font void print_int(int i, unsigned x, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned e = x; x += 24; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; do { d = div(d.quot, 10); lcd.print(x -= 6, y, '0' + d.rem); } while(d.quot); if(neg) lcd.print(x -= 6, y, '-'); while(x > e) lcd.print(x -= 6, y, ' '); } void draw_bargraph(int f) { int x, y, bg; char c; unsigned char bgc[9] = { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0 }; char *bgl[8] = { "90", "80", "70", "60", "50", "40", "30", "20" }; x = 20; // bg = (f - (x - 5)) * 4 / 5; // for(y = 7; y >= 0; --y) { // lcd.pos(83, y); // if(bg < 0) { c = bgc[8]; } else if (bg > 7) { c = bgc[7]; } else { c = bgc[bg]; } lcd.write((unsigned char *)&c, 1, lcd_data); lcd.print(bgl[y], c); bg -= 8; } // } time_t tt; // Time in seconds since epoch unsigned adc; // ADC value #pragma vector = WDT_VECTOR // - Watchdog timer interrupt vector __interrupt void wdt_isr(void) // This interrupt will occur once per second { // ++tt; // Increment time_t __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // // #pragma vector = ADC10_VECTOR // ADC conversion complete interrupt __interrupt void ADC10_ISR(void) // { // adc = ADC10MEM; // Read ADC __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // int main(void) { int x, y; char c; struct tm ts; int dc, dk, df; // Temperature in degrees C, K, and F WDTCTL = WDTPW | WDTHOLD; P1REN = RXD | SWITCH; P1DIR = LCD_RESET; P1OUT = RXD | SWITCH | LCD_RESET; P2DIR = LCD_DC | LCD_CE | LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P2REN = LCD_BTN; P2OUT = LCD_CLK | LCD_DC | LCD_CE | LCD_BACKLIGHT | LCD_BTN; ADC10CTL0 = 0; // Configure ADC ADC10CTL1 = INCH_10 | ADC10DIV_3; // ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE; ADC10CTL0 |= ADC10IE; // Enable ADC conversion complete interrupt // // 32 kHz xtal loading //BCSCTL3 = XCAP_1; // 6 pF (default) BCSCTL3 = XCAP_2; // 10 pF //BCSCTL3 = XCAP_3; // 12.5 pF // WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL; // Use WDT as interval timer IE1 |= WDTIE; // Enable WDT interrupt _EINT(); // Enable interrupts // // Set initial time - there is no UI for this ts.tm_hour = 13; // Hour ts.tm_min = 37; // Minute ts.tm_sec = 42; // Second ts.tm_mon = 3; // Month (0 based!) ts.tm_mday = 20; // Day of Month ts.tm_year = 2012 - 1900; // Year ts.tm_wday = 5; // Day of Week - Not used by mktime() ts.tm_yday = 0; // Not used by mktime() ts.tm_isdst = 0; // DST flag - Not used by rtc_tick() // tt = mktime(&ts); // Convert tm struct to time_t // lcd.reset(); lcd.init(); lcd.clear(); lcd.print(30, 6, "MSP430"); lcd.print(18, 7, "Nokia 7110"); for(x = -47; x < 24; ++x) { lcd.bitmap(ti, x, 0); __delay_cycles(20000); } __delay_cycles(2000000); for(; x < 96; ++x) { lcd.bitmap(ti, x, 0); __delay_cycles(20000); } lcd.clear(); lcd.print(9, 7, "Character Set"); x = y = 0; lcd.pos(x, y); for(c = 32; c < 128; ++c) { lcd.print(c); if(++x >= 16) x = 0, lcd.pos(x, ++y); } __delay_cycles(3000000); lcd.clear(); ts.tm_hour = ts.tm_min = ts.tm_sec = 88; ts.tm_mon = 87; ts.tm_mday = ts.tm_year = 88; ts.tm_wday = 5; show_time(&ts); lcd.pd12(15, 48, 5); lcd.pd12(17, 59, 5); lcd.print(24, 7, "\x7F""C"); lcd.print(66, 7, "\x7F""K"); print_int(8888, 5); print_int(8888, 0, 7); print_int(8888, 42, 7); for(x = 15; x < 96; ++x) draw_bargraph(x); __delay_cycles(3000000); lcd.clear(); // lcd.pd12(15, 48, 5); // Degrees lcd.pd12(17, 59, 5); // F lcd.print(24, 7, "\x7F""C"); // C lcd.print(66, 7, "\x7F""K"); // K // for(; { // for-ever show_time(localtime(&tt)); // Convert time_t to tm struct and show on LCD __bis_SR_register(LPM0_bits + GIE); // Sleep until WDT interrupt ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion __bis_SR_register(LPM0_bits + GIE); // Sleep until conversion complete // // Convert to temperature dc = ((27069L * adc) - 18169625L) >> 16; // Vref = 1.5V dk = ((27069L * adc) - 268467L) >> 16; // Vref = 1.5V df = ((48724L * adc) - 30634388L) >> 16; // Vref = 1.5V // // Display on LCD print_int(df, 5); // Degrees F print_int(dc, 0, 7); // Degrees C print_int(dk, 42, 7); // Degrees K draw_bargraph(df); // Deg F Bargraph } // return 0; } nokia7110tl.h - C++ template class for Nokia 7110 (SED1565) namespace nokia7110 { unsigned char PNONE; typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> struct Nokia7110 { void write(const unsigned char *cmd, unsigned len, const lcd_cmd_type type = lcd_data); void reset(void); void init(void); void home(void); void pos(unsigned char x, unsigned char y); void clear(unsigned char x = 0); void fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z); void bitmap(const unsigned char *bmp, signed char x, signed char y, unsigned char w, unsigned char h); inline void bitmap(const unsigned char *bmp, signed char x, signed char y) { bitmap(bmp + 2, x, y, bmp[0], bmp[1]); }; void print(char c); inline void print(unsigned char x, unsigned char y, char c) { pos(x, y); print(c); }; void print(const char *s); inline void print(unsigned char x, unsigned char y, const char *s) { pos(x, y); print(s); }; void print(const char *s, unsigned char m); void printv(unsigned char x, unsigned char y, char *s); void pd12(unsigned n, unsigned x, unsigned y); }; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::write(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; if(&_EP != &PNONE) _EP &= ~_CE; do { mask = 0x0080; do { if(*cmd & mask) { _SP |= _DATA; _SP &= ~_CLK; } else { _SP &= ~(_CLK | _DATA); } _SP |= _CLK; mask >>= 1; } while(!(mask & 1)); if(&_CP == &PNONE) { __delay_cycles(_DC); } else { if(!type) _CP &= ~_DC; } if(*cmd & mask) { _SP |= _DATA; _SP &= ~_CLK; } else { _SP &= ~(_CLK | _DATA); } _SP |= _CLK; if(&_CP != &PNONE) _CP |= _DC; if(!(type & 2)) ++cmd; } while(--len); if(&_EP != &PNONE) _EP |= _CE; } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::reset(void) { if(&_RP == &PNONE) { // --- Set initial state of CLK, DC and CE as needed // * = output used for reset if(&_CP == &PNONE) { if(&_EP != &PNONE) { // CLK*, DATA, CE _EP |= _CE; } // else // CLK*, DATA } else { if(&_EP != &PNONE) { // CLK, DATA, DC*, CE if(&_SP == &_EP) { _SP |= (_CLK | _CE); } else { _SP |= _CLK; _EP |= _CE; } } else { // CLK, DATA, DC* _SP |= _CLK; } } // --- Reset pulse on CLK or DC as needed if(&_CP == &PNONE) { // No DC port, use CLK to reset _SP &= ~_CLK; __delay_cycles(_RD); _SP |= _CLK; } else { // Use DC to reset _CP &= ~_DC; __delay_cycles(_RD); _CP |= _DC; } } else { _RP &= ~_RST; if(&_EP != &PNONE) { if(&_SP == &_EP) { _SP |= (_CLK | _CE); } else { _SP |= _CLK; _EP |= _CE; } } else { _SP |= _CLK; } __delay_cycles(_RD); _RP |= _RST; } __delay_cycles(_RD); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::init(void) { static const unsigned char init[] = { 0xA6, //Display: Normal 0xA3, //LCD Bias Settings: 1/7 0xA1, //ADC Selection: Reverse 0xC0, //Common Output: Normal Direction //0xC8, //Common Output: Upside Down 0x22, //Set the V5 output Voltage 0x81, //Set Electronic Volume Register 0x2E, //Power Controller Set // Booster circuit: ON // Voltage regulator circuit: ON // Voltage follower circuit: OFF 0x2F, //Power Controller Set // Voltage follower circuit: ON 0xE3, //Non-OPeration Command 0x40, //Set the start line 0xAF, //LCD On //0xA5, //Display All Points: ON 0xA4, //Display All Points: NORMAL }; write(init, sizeof(init), lcd_command); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::home(void) { static const unsigned char home[] = { 0xB0, 0x11, 0x02 }; write(home, sizeof(home), lcd_command); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::pos(unsigned char x, unsigned char y) { unsigned char c[3]; x += 18; c[0] = 0xB0 | y; c[1] = 0x10 | (x >> 4); c[2] = x & 0x0F; write(c, sizeof(c), lcd_command); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::clear(unsigned char x) { for(unsigned y = 0; y < 9; ++y) { pos(0, y); write(&x, 96, lcd_data_repeat); } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z) { unsigned yy = y + h; unsigned char c[3]; x += 18; c[1] = 0x10 | (x >> 4); c[2] = (x & 0x0F); for(;y < yy; ++y) { c[0] = 0xB0 | y; write(c, sizeof(c), lcd_command); write(&z, w, lcd_data_repeat); } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::bitmap(const unsigned char *bmp, signed char x, signed char y, unsigned char w, unsigned char h) { unsigned char c[3]; unsigned char ww; if(x < 0) { ww = w + x; bmp -= x; x = 0; } else if(x + w >= 96) { ww = (96 - x); } else { ww = w; } x += 18; c[0] = 0xB0 | y; c[1] = 0x10 | (x >> 4); c[2] = x & 0x0F; while(h--) { write(c, sizeof(c), lcd_command); write(bmp, ww); bmp += w; ++c[0]; } } static const unsigned char font[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x5F, 0x00, 0x00, // ! 0x00, 0x07, 0x00, 0x07, 0x00, // " 0x14, 0x7F, 0x14, 0x7F, 0x14, // # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // $ 0x23, 0x13, 0x08, 0x64, 0x62, // % 0x36, 0x49, 0x56, 0x20, 0x50, // & 0x00, 0x08, 0x07, 0x03, 0x00, // ' 0x00, 0x1C, 0x22, 0x41, 0x00, // ( 0x00, 0x41, 0x22, 0x1C, 0x00, // ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // * 0x08, 0x08, 0x3E, 0x08, 0x08, // + 0x00, 0x40, 0x38, 0x18, 0x00, // , 0x08, 0x08, 0x08, 0x08, 0x08, // - 0x00, 0x00, 0x60, 0x60, 0x00, // . 0x20, 0x10, 0x08, 0x04, 0x02, // / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 0x42, 0x61, 0x51, 0x49, 0x46, // 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 0x27, 0x45, 0x45, 0x45, 0x39, // 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 0x41, 0x21, 0x11, 0x09, 0x07, // 7 0x36, 0x49, 0x49, 0x49, 0x36, // 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 0x00, 0x00, 0x14, 0x00, 0x00, // : 0x00, 0x00, 0x40, 0x34, 0x00, // ; 0x00, 0x08, 0x14, 0x22, 0x41, // < 0x14, 0x14, 0x14, 0x14, 0x14, // = 0x00, 0x41, 0x22, 0x14, 0x08, // > 0x02, 0x01, 0x51, 0x09, 0x06, // ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // A 0x7F, 0x49, 0x49, 0x49, 0x36, // B 0x3E, 0x41, 0x41, 0x41, 0x22, // C 0x7F, 0x41, 0x41, 0x41, 0x3E, // D 0x7F, 0x49, 0x49, 0x49, 0x41, // E 0x7F, 0x09, 0x09, 0x09, 0x01, // F 0x3E, 0x41, 0x49, 0x49, 0x7A, // G 0x7F, 0x08, 0x08, 0x08, 0x7F, // H 0x00, 0x41, 0x7F, 0x41, 0x00, // I 0x20, 0x40, 0x41, 0x3F, 0x01, // J 0x7F, 0x08, 0x14, 0x22, 0x41, // K 0x7F, 0x40, 0x40, 0x40, 0x40, // L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // M 0x7F, 0x04, 0x08, 0x10, 0x7F, // N 0x3E, 0x41, 0x41, 0x41, 0x3E, // O 0x7F, 0x09, 0x09, 0x09, 0x06, // P 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q 0x7F, 0x09, 0x19, 0x29, 0x46, // R 0x26, 0x49, 0x49, 0x49, 0x32, // S 0x01, 0x01, 0x7F, 0x01, 0x01, // T 0x3F, 0x40, 0x40, 0x40, 0x3F, // U 0x1F, 0x20, 0x40, 0x20, 0x1F, // V 0x3F, 0x40, 0x38, 0x40, 0x3F, // W 0x63, 0x14, 0x08, 0x14, 0x63, // X 0x03, 0x04, 0x78, 0x04, 0x03, // Y 0x61, 0x51, 0x49, 0x45, 0x43, // Z 0x00, 0x7F, 0x41, 0x41, 0x41, // [ 0x02, 0x04, 0x08, 0x10, 0x20, // '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // ] 0x04, 0x02, 0x01, 0x02, 0x04, // ^ 0x80, 0x80, 0x80, 0x80, 0x80, // _ 0x00, 0x03, 0x07, 0x08, 0x00, // ' 0x20, 0x54, 0x54, 0x54, 0x78, // a 0x7F, 0x28, 0x44, 0x44, 0x38, // b 0x38, 0x44, 0x44, 0x44, 0x28, // c 0x38, 0x44, 0x44, 0x28, 0x7F, // d 0x38, 0x54, 0x54, 0x54, 0x18, // e 0x00, 0x08, 0x7E, 0x09, 0x02, // f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g 0x7F, 0x08, 0x04, 0x04, 0x78, // h 0x00, 0x44, 0x7D, 0x40, 0x00, // i 0x00, 0x20, 0x40, 0x40, 0x3D, // j 0x00, 0x7F, 0x10, 0x28, 0x44, // k 0x00, 0x41, 0x7F, 0x40, 0x00, // l 0x7C, 0x04, 0x78, 0x04, 0x78, // m 0x7C, 0x08, 0x04, 0x04, 0x78, // n 0x38, 0x44, 0x44, 0x44, 0x38, // o 0xFC, 0x18, 0x24, 0x24, 0x18, // p 0x18, 0x24, 0x24, 0x18, 0xFC, // q 0x7C, 0x08, 0x04, 0x04, 0x08, // r 0x48, 0x54, 0x54, 0x54, 0x24, // s 0x04, 0x04, 0x3F, 0x44, 0x24, // t 0x3C, 0x40, 0x40, 0x20, 0x7C, // u 0x1C, 0x20, 0x40, 0x20, 0x1C, // v 0x3C, 0x40, 0x30, 0x40, 0x3C, // w 0x44, 0x28, 0x10, 0x28, 0x44, // x 0x4C, 0x90, 0x90, 0x90, 0x7C, // y 0x44, 0x64, 0x54, 0x4C, 0x44, // z 0x00, 0x08, 0x36, 0x41, 0x00, // { 0x00, 0x00, 0x77, 0x00, 0x00, // | 0x00, 0x41, 0x36, 0x08, 0x00, // } 0x02, 0x01, 0x02, 0x04, 0x02, // ~ 0x00, 0x06, 0x09, 0x09, 0x06, // degrees }; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::print(char c) { write(&font[c - 32][0], 5); write(&font[0][0], 1); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::print(const char *s) { while(*s) { write(&font[*s - 32][0], 5); write(&font[0][0], 1); ++s; } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::print(const char *s, unsigned char m) { unsigned char c; while(*s) { c = font[*s - 32][0] ^ m; write(&c, 1); c = font[*s - 32][1] ^ m; write(&c, 1); c = font[*s - 32][2] ^ m; write(&c, 1); c = font[*s - 32][3] ^ m; write(&c, 1); c = font[*s - 32][4] ^ m; write(&c, 1); write(&m, 1); ++s; } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::printv(unsigned char x, unsigned char y, char *s) { while(*s) { pos(x, y); ++y; write(&font[*s - 32][0], 5); write(&font[0][0], 1); ++s; } } static const unsigned char num11x16[19][11 * 2] = { 0x00,0xF0,0xFC,0xFE,0x06,0x02,0x06,0xFE,0xFC,0xF0,0x00, // 0 0x00,0x07,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x07,0x00, 0x00,0x00,0x08,0x0C,0xFC,0xFE,0xFE,0x00,0x00,0x00,0x00, // 1 0x00,0x20,0x20,0x20,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x00, 0x00,0x0C,0x0E,0x06,0x02,0x02,0x86,0xFE,0x7C,0x38,0x00, // 2 0x00,0x30,0x38,0x3C,0x36,0x33,0x31,0x30,0x30,0x38,0x00, 0x00,0x0C,0x0E,0x86,0x82,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 3 0x00,0x18,0x38,0x30,0x20,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x00,0xC0,0x20,0x18,0x04,0xFE,0xFE,0xFE,0x00,0x00, // 4 0x00,0x03,0x02,0x02,0x02,0x22,0x3F,0x3F,0x3F,0x22,0x02, 0x00,0x00,0x7E,0x7E,0x46,0x46,0xC6,0xC6,0x86,0x00,0x00, // 5 0x00,0x18,0x38,0x30,0x20,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0xC0,0xF0,0xF8,0xFC,0x4C,0xC6,0xC2,0x82,0x00,0x00, // 6 0x00,0x0F,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0x06,0x06,0x06,0x06,0x06,0xC6,0xF6,0x3E,0x0E,0x00, // 7 0x00,0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,0x00, 0x00,0x38,0x7C,0xFE,0xC6,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 8 0x00,0x0E,0x1F,0x3F,0x31,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x78,0xFC,0xFE,0x86,0x02,0x86,0xFE,0xFC,0xF8,0x00, // 9 0x00,0x00,0x00,0x21,0x21,0x31,0x1D,0x1F,0x0F,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // : 0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // . 0x00,0x38,0x38,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x00,0x00, // / 0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // - 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x00,0x18,0x3C,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xF0,0xF8,0x0C,0x06,0x02,0x02,0x02,0x02,0x0E,0x0C,0x00, // C 0x03,0x07,0x0C,0x18,0x10,0x10,0x10,0x10,0x1C,0x0C,0x00, 0xFE,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, // F 0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xFE,0xFE,0x40,0xE0,0xB0,0x18,0x0C,0x06,0x02,0x00,0x00, // K 0x1F,0x1F,0x00,0x00,0x01,0x03,0x06,0x0C,0x18,0x10,0x00 }; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::pd12(unsigned n, unsigned x, unsigned y) { pos(x, y); write(num11x16[n], 11, lcd_data); pos(x, ++y); write(num11x16[n] + 11, 11, lcd_data); } } // namespace
  3. 14 points
    Hello everyone, There has been alot of good questions going on in the past few weeks - accompanied with very good discussions. One way you can show your appreciation is by using the "Thanks" or "Thumbs up " button on the top right. It may be hard to notice but a screenshot always helps: Use it if you have received something useful from a member. It takes time to write those posts and code. Consider it as karma, that's as good as it can get. :mrgreen: Thanks everyone..and wish you a happy new year.
  4. 13 points
    The Launchpad comes with a 32.768 kHz crystal that can be used when timing more precise than the DCO is needed. The crystal frequency is useful for simple timing tasks, such as a RTC, because it is a power of 2. That allows a simple cascade of 15 flip-flops to create a 1 pulse per second time base. It is common for microcontroller timers to have prescalers, postscalers and/or preset divisors that are simply a tap on the flip-flip chain. The watchdog timer in the MSP430 is an example of a timer with power of 2 presets (64, 512, 8192, 32768). TimerA units have a prescaler with power of 2 presets (1, 2, 4, 8). The sample code in this post uses the watchdog timer to create an interrupt that occurs once per second. TimerA can also be used to do this. One of the advantages of using TimerA is the ability to change the preset +/- 1 every N cycles to allow for calibration and temperature compensation of the crystal. I chose to use the watchdog timer so that TimerA was free to be used for alarm tones or PWM backlight control. Using the C standard library time functions for a RTC The C standard library provides several functions for working with real time. The function prototypes are in time.h. The type time_t is typically an integer type that is in units of 1 second. It doesn't have to be that, but usually is. The epoch is usually January 1, 1970 for "unix time." Be aware that CCS uses an epoch of January 1, 1900 and time_t is unsigned rather than signed. I assume GCC uses the more common unix time epoch and time_t is signed. Since time_t is in units of 1 second, the ISR for timekeeping is trivial... #pragma vector = WDT_VECTOR // - Watchdog timer interrupt vector __interrupt void wdt_isr(void) // This interrupt will occur once per second { // ++tt; // Increment time_t } // To display the time it can be converted to a tm structure that has elements for hours, minutes, day, month, ect. There are two functions to do this: gmtime() and localtime(). gmtime() converts to UTC, and localtime converts to the time zone for the set locale. Both functions take a time_t and return a pointer to a static tm struct. To set the time the mktime() function will convert a tm struct to a time_t value. It will also determine day of week and day or year for you. Benefits Tivial ISR. Simple and efficient. Portable code - the C standard library time functions work on almost any device with a C compiler. Easy to calulate time deltas - use ordinary integer math on time_t values. Easy to compare times using <, >, ==, etc. Compact representation of time (32 bit time_t typically) is good for timestamps of data logs. Some file systems (not FAT unfortunately) use time_t as for timestamps. Concerns Converting from time_t to tm struct takes quite a few clock cycles, so not good for low power (battery powered) systems. Time functions take up quite a bit of flash - will not fit in G2211 for example. Software model of a hardware RTC Hardware real time clocks typically have counters for seconds, minutes, hours, ect. Each counter will reset at the appropriate limit - 60 seconds, 24 hours, ect. This approch can be use for a software RTC. The ISR has more code than the previous version... #pragma vector = WDT_VECTOR // - Watchdog timer interrupt vector __interrupt void wdt_isr(void) // This interrupt will occur once per second { // if(++t.tm_sec > 59) { // Increment seconds, check for overflow t.tm_sec = 0; // Reset seconds if(++t.tm_min > 59) { // Increment minutes, check for overflow t.tm_min = 0; // Reset minutes if(++t.tm_hour > 23) { // Increment hours, check for overflow t.tm_hour = 0; // Reset hours ++t.tm_yday; // Increment day of year if(++t.tm_wday > 6) // Increment day of week, check for overflow t.tm_wday = 0; // Reset day of week // Increment day of month, check for overflow if(++t.tm_mday > dim[t.tm_mon][is_leap_year(t.tm_year + 1900)]) { t.tm_mday = 1; // Reset day of month if(++t.tm_mon > 11) { // Increment month, check for overflow t.tm_mon = 0; // Reset month t.tm_yday = 0; // Reset day of year ++t.tm_year; // Increment year } // - year } // - month } // - day } // - hour } // - minute } // Most of the time the ISR will just increment the seconds member and return. The worst case is at the end of the year when the year is incremented and all other counters are reset to inital values. There is no need for any conversion for display of the time or setting the time. Be aware that day of week and day of year must be explicity set and can be out-of-sync if not properly set. Benefits No conversion function needed for display of time or setting of time. Small code size. Fits in G2211. High efficiency is good for battery powered applications. Use of tm struct provides some familiarity to those who have used the C standard library functions. Concerns No easy to way calulate time delta. No support for time zones / localization. Time comparison (for alarms) requires more MCU cycles because several structure members require comparason rather than just a long integer. This sample code can be configured to use either of the two methods descibed by (un)commenting the "#define USE_STDLIB_TIME" in main.c main.c #include #include #include #include "rtc.h" #include "lcd.h" //#define USE_STDLIB_TIME // Use functions in time.h void show_time(const struct tm *t) // Show time on LCD { static const char *dow[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; int x, w; const char *d; if(t->tm_hour < 10) { x = -1; lcd_fill(0, 1, 10, 2, 0); } else { x = 4; lcd_fill(0, 1, 4, 2, 0); lcd_pd12(t->tm_hour / 10, x, 1); } lcd_pd12(t->tm_hour % 10, x + 11, 1); lcd_pd12(11, x + 22, 1); lcd_pd12(t->tm_min / 10, x + 27, 1); lcd_pd12(t->tm_min % 10, x + 38, 1); lcd_pd12(11, x + 49, 1); lcd_pd12(t->tm_sec / 10, x + 54, 1); lcd_pd12(t->tm_sec % 10, x + 65, 1); lcd_fill(x + 76, 1, 8 - x, 2, 0); if(t->tm_mon < 9) { x = -4; lcd_fill(0, 3, 7, 2, 0); } else { x = 1; lcd_fill(0, 3, 1, 2, 0); lcd_pd12((t->tm_mon + 1) / 10, x, 3); } lcd_pd12((t->tm_mon + 1) % 10, x + 11, 3); lcd_pd12(13, x + 22, 3); lcd_pd12(t->tm_mday / 10, x + 30, 3); lcd_pd12(t->tm_mday % 10, x + 41, 3); lcd_pd12(13, x + 52, 3); lcd_pd12(t->tm_year %100 / 10, x + 60, 3); lcd_pd12(t->tm_year % 10, x + 71, 3); lcd_fill(x + 82, 3, 2 - x, 2, 0); d = dow[t->tm_wday]; w = strlen(d) * 6; x = (84 - w) >> 1; lcd_fill(0, 5, x, 1, 0); lcd_print(d, x, 5); x += w; lcd_fill(x, 5, 84 - x, 1, 0); } // struct tm ts; // Time structure time_t tt; // Time in seconds since epoch // // #pragma vector = WDT_VECTOR // - Watchdog timer interrupt vector __interrupt void wdt_isr(void) // This interrupt will occur once per second { // #ifdef USE_STDLIB_TIME // ++tt; // Increment time_t #else // rtc_tick(&ts); // Increment tm struct #endif // __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // // void main(void) // { // WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset // lcd_init(); // Init LCD lcd_clear(0); // lcd_print("MSP430 RTC", 12, 0); // // // 32 kHz xtal loading //BCSCTL3 = XCAP_1; // 6 pF (default) BCSCTL3 = XCAP_2; // 10 pF //BCSCTL3 = XCAP_3; // 12.5 pF // WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL; // Use WDT as interval timer IE1 |= WDTIE; // Enable WDT interrupt _EINT(); // Enable interrupts // // Set initial time - there is no UI for this ts.tm_hour = 13; // Hour ts.tm_min = 37; // Minute ts.tm_sec = 42; // Second ts.tm_mon = 3; // Month (0 based!) ts.tm_mday = 20; // Day of Month ts.tm_year = 2012 - 1900; // Year ts.tm_wday = 5; // Day of Week - Not used by mktime() ts.tm_yday = 0; // Not used by mktime() ts.tm_isdst = 0; // DST flag - Not used by rtc_tick() // #ifdef USE_STDLIB_TIME // Convert tm struct to time_t tt = mktime(&ts); // #endif // // for(; { // for-ever #ifdef USE_STDLIB_TIME // show_time(localtime(&tt)); // Convert time_t to tm struct and show on LCD #else // show_time(&ts); // Show time on LCD #endif // __bis_SR_register(LPM0_bits + GIE); // Sleep until WDT interrupt } // } // rtc.h void rtc_tick(struct tm *t); rtc.c - Software model of hardware RTC #include static int is_leap_year(const int y) { if(y & 3) return 0; // Not divisible by 4 switch(y % 400) { // Divisible by 100, but not by 400 (1900, 2100, 2200, 2300, 2500, 2600) case 100: case 200: case 300: return 0; } return 1; // Divisible by 4 and !(100 and !400) } void rtc_tick(struct tm *t) { static const signed char dim[12][2] = { // Number of days in month for non-leap year and leap year 31, 31, // January 28, 29, // February 31, 31, // March 30, 30, // April 31, 31, // May 30, 30, // June 31, 31, // July 31, 31, // August 30, 30, // September 31, 31, // October 30, 30, // November 31, 31 // December }; // // if(++t->tm_sec > 59) { // Increment seconds, check for overflow t->tm_sec = 0; // Reset seconds if(++t->tm_min > 59) { // Increment minutes, check for overflow t->tm_min = 0; // Reset minutes if(++t->tm_hour > 23) { // Increment hours, check for overflow t->tm_hour = 0; // Reset hours ++t->tm_yday; // Increment day of year if(++t->tm_wday > 6) // Increment day of week, check for overflow t->tm_wday = 0; // Reset day of week // Increment day of month, check for overflow if(++t->tm_mday > dim[t->tm_mon][is_leap_year(t->tm_year + 1900)]) { t->tm_mday = 1; // Reset day of month if(++t->tm_mon > 11) { // Increment month, check for overflow t->tm_mon = 0; // Reset month t->tm_yday = 0; // Reset day of year ++t->tm_year; // Increment year } // - year } // - month } // - day } // - hour } // - minute } // lcd.h typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type); void lcd_home(void); void lcd_pos(unsigned char x, unsigned char y); void lcd_clear(unsigned char x); void lcd_init(void); void lcd_fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z); void lcd_print(const char *s, unsigned x, unsigned y); void lcd_pd12(unsigned n, unsigned x, unsigned y); lcd.c #include #include "lcd.h" //static const unsigned TXD = BIT1; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; static const unsigned LCD_CLK = BIT5; static const unsigned LCD_BACKLIGHT = BIT6; static const unsigned LCD_DATA = BIT7; static const unsigned LCD_DC = BIT0; // PORT2 static const unsigned LCD_CE = BIT1; // PORT2 void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; P2OUT &= ~LCD_CE; do { mask = 0x0080; do { if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; mask >>= 1; } while(!(mask & 1)); if(!type) P2OUT &= ~LCD_DC; if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; P2OUT |= LCD_DC; if(!(type & 2)) ++cmd; } while(--len); P2OUT |= LCD_CE; } static const unsigned char home[] = { 0x40, 0x80 }; void lcd_home(void) { lcd_send(home, sizeof(home), lcd_command); } void lcd_pos(unsigned char x, unsigned char y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); } void lcd_clear(unsigned char x) { lcd_home(); lcd_send(&x, 504, lcd_data_repeat); lcd_home(); } void lcd_init(void) { static const unsigned char init[] = { 0x20 + 0x01, // function set - extended instructions enabled 0x80 + 64, // set vop (contrast) 0 - 127 0x04 + 0x02, // temperature control 0x10 + 0x03, // set bias system 0x20 + 0x00, // function set - chip active, horizontal addressing, basic instructions 0x08 + 0x04 // display control - normal mode }; P1REN = RXD | SWITCH; P1DIR = LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P1OUT = LCD_CLK | RXD | SWITCH | LCD_BACKLIGHT; P2REN = 0; P2DIR = LCD_DC | LCD_CE; P2OUT = LCD_CE; __delay_cycles(20000); P2OUT |= LCD_DC; __delay_cycles(20000); lcd_send(init, sizeof(init), lcd_command); } void lcd_fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z) { unsigned yy = y + h; unsigned char c[2]; c[0] = 0x80 | x; for(;y < yy; ++y) { c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); lcd_send(&z, w, lcd_data_repeat); } } static const unsigned char font6x8[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 20 32 0x00, 0x00, 0x5F, 0x00, 0x00, // 21 33 ! 0x00, 0x07, 0x00, 0x07, 0x00, // 22 34 " 0x14, 0x7F, 0x14, 0x7F, 0x14, // 23 35 # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24 36 $ 0x23, 0x13, 0x08, 0x64, 0x62, // 25 37 % 0x36, 0x49, 0x56, 0x20, 0x50, // 26 38 & 0x00, 0x08, 0x07, 0x03, 0x00, // 27 39 ' 0x00, 0x1C, 0x22, 0x41, 0x00, // 28 40 ( 0x00, 0x41, 0x22, 0x1C, 0x00, // 29 41 ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A 42 * 0x08, 0x08, 0x3E, 0x08, 0x08, // 2B 43 + 0x00, 0x40, 0x38, 0x18, 0x00, // 2C 44 , 0x08, 0x08, 0x08, 0x08, 0x08, // 2D 45 - 0x00, 0x00, 0x60, 0x60, 0x00, // 2E 46 . 0x20, 0x10, 0x08, 0x04, 0x02, // 2F 47 / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 30 48 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 31 49 1 0x42, 0x61, 0x51, 0x49, 0x46, // 32 50 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 33 51 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 34 52 4 0x27, 0x45, 0x45, 0x45, 0x39, // 35 53 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 36 54 6 0x41, 0x21, 0x11, 0x09, 0x07, // 37 55 7 0x36, 0x49, 0x49, 0x49, 0x36, // 38 56 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 39 57 9 0x00, 0x00, 0x14, 0x00, 0x00, // 3A 58 : 0x00, 0x00, 0x40, 0x34, 0x00, // 3B 59 ; 0x00, 0x08, 0x14, 0x22, 0x41, // 3C 60 < 0x14, 0x14, 0x14, 0x14, 0x14, // 3D 61 = 0x00, 0x41, 0x22, 0x14, 0x08, // 3E 62 > 0x02, 0x01, 0x51, 0x09, 0x06, // 3F 63 ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // 40 64 @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // 41 65 A 0x7F, 0x49, 0x49, 0x49, 0x36, // 42 66 B 0x3E, 0x41, 0x41, 0x41, 0x22, // 43 67 C 0x7F, 0x41, 0x41, 0x41, 0x3E, // 44 68 D 0x7F, 0x49, 0x49, 0x49, 0x41, // 45 69 E 0x7F, 0x09, 0x09, 0x09, 0x01, // 46 70 F 0x3E, 0x41, 0x49, 0x49, 0x7A, // 47 71 G 0x7F, 0x08, 0x08, 0x08, 0x7F, // 48 72 H 0x00, 0x41, 0x7F, 0x41, 0x00, // 49 73 I 0x20, 0x40, 0x41, 0x3F, 0x01, // 4A 74 J 0x7F, 0x08, 0x14, 0x22, 0x41, // 4B 75 K 0x7F, 0x40, 0x40, 0x40, 0x40, // 4C 76 L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // 4D 77 M 0x7F, 0x04, 0x08, 0x10, 0x7F, // 4E 78 N 0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F 79 O 0x7F, 0x09, 0x09, 0x09, 0x06, // 50 80 P 0x3E, 0x41, 0x51, 0x21, 0x5E, // 51 81 Q 0x7F, 0x09, 0x19, 0x29, 0x46, // 52 82 R 0x26, 0x49, 0x49, 0x49, 0x32, // 53 83 S 0x01, 0x01, 0x7F, 0x01, 0x01, // 54 84 T 0x3F, 0x40, 0x40, 0x40, 0x3F, // 55 85 U 0x1F, 0x20, 0x40, 0x20, 0x1F, // 56 86 V 0x3F, 0x40, 0x38, 0x40, 0x3F, // 57 87 W 0x63, 0x14, 0x08, 0x14, 0x63, // 58 88 X 0x03, 0x04, 0x78, 0x04, 0x03, // 59 89 Y 0x61, 0x51, 0x49, 0x45, 0x43, // 5A 90 Z 0x00, 0x7F, 0x41, 0x41, 0x41, // 5B 91 [ 0x02, 0x04, 0x08, 0x10, 0x20, // 5C 92 '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // 5D 93 ] 0x04, 0x02, 0x01, 0x02, 0x04, // 5E 94 ^ 0x80, 0x80, 0x80, 0x80, 0x80, // 5F 95 _ 0x00, 0x03, 0x07, 0x08, 0x00, // 60 96 ' 0x20, 0x54, 0x54, 0x54, 0x78, // 61 97 a 0x7F, 0x28, 0x44, 0x44, 0x38, // 62 98 b 0x38, 0x44, 0x44, 0x44, 0x28, // 63 99 c 0x38, 0x44, 0x44, 0x28, 0x7F, // 64 100 d 0x38, 0x54, 0x54, 0x54, 0x18, // 65 101 e 0x00, 0x08, 0x7E, 0x09, 0x02, // 66 102 f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // 67 103 g 0x7F, 0x08, 0x04, 0x04, 0x78, // 68 104 h 0x00, 0x44, 0x7D, 0x40, 0x00, // 69 105 i 0x00, 0x20, 0x40, 0x40, 0x3D, // 6A 106 j 0x00, 0x7F, 0x10, 0x28, 0x44, // 6B 107 k 0x00, 0x41, 0x7F, 0x40, 0x00, // 6C 108 l 0x7C, 0x04, 0x78, 0x04, 0x78, // 6D 109 m 0x7C, 0x08, 0x04, 0x04, 0x78, // 6E 110 n 0x38, 0x44, 0x44, 0x44, 0x38, // 6F 111 o 0xFC, 0x18, 0x24, 0x24, 0x18, // 70 112 p 0x18, 0x24, 0x24, 0x18, 0xFC, // 71 113 q 0x7C, 0x08, 0x04, 0x04, 0x08, // 72 114 r 0x48, 0x54, 0x54, 0x54, 0x24, // 73 115 s 0x04, 0x04, 0x3F, 0x44, 0x24, // 74 116 t 0x3C, 0x40, 0x40, 0x20, 0x7C, // 75 117 u 0x1C, 0x20, 0x40, 0x20, 0x1C, // 76 118 v 0x3C, 0x40, 0x30, 0x40, 0x3C, // 77 119 w 0x44, 0x28, 0x10, 0x28, 0x44, // 78 120 x 0x4C, 0x90, 0x90, 0x90, 0x7C, // 79 121 y 0x44, 0x64, 0x54, 0x4C, 0x44, // 7A 122 z 0x00, 0x08, 0x36, 0x41, 0x00, // 7B 123 { 0x00, 0x00, 0x77, 0x00, 0x00, // 7C 124 | 0x00, 0x41, 0x36, 0x08, 0x00, // 7D 125 } 0x02, 0x01, 0x02, 0x04, 0x02, // 7E 126 ~ 0x00, 0x06, 0x09, 0x09, 0x06, // 7F 127 degrees }; void lcd_print(const char *s, unsigned x, unsigned y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); while(*s) { lcd_send(&font6x8[*s - 32][0], 5, lcd_data); lcd_send(&font6x8[0][0], 1, lcd_data); ++s; } } static const unsigned char num11x16[19][11 * 2] = { 0x00,0xF0,0xFC,0xFE,0x06,0x02,0x06,0xFE,0xFC,0xF0,0x00, // 0 0x00,0x07,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x07,0x00, 0x00,0x00,0x08,0x0C,0xFC,0xFE,0xFE,0x00,0x00,0x00,0x00, // 1 0x00,0x20,0x20,0x20,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x00, 0x00,0x0C,0x0E,0x06,0x02,0x02,0x86,0xFE,0x7C,0x38,0x00, // 2 0x00,0x30,0x38,0x3C,0x36,0x33,0x31,0x30,0x30,0x38,0x00, 0x00,0x0C,0x0E,0x86,0x82,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 3 0x00,0x18,0x38,0x30,0x20,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x00,0xC0,0x20,0x18,0x04,0xFE,0xFE,0xFE,0x00,0x00, // 4 0x00,0x03,0x02,0x02,0x02,0x22,0x3F,0x3F,0x3F,0x22,0x02, 0x00,0x00,0x7E,0x7E,0x46,0x46,0xC6,0xC6,0x86,0x00,0x00, // 5 0x00,0x18,0x38,0x30,0x20,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0xC0,0xF0,0xF8,0xFC,0x4C,0xC6,0xC2,0x82,0x00,0x00, // 6 0x00,0x0F,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0x06,0x06,0x06,0x06,0x06,0xC6,0xF6,0x3E,0x0E,0x00, // 7 0x00,0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,0x00, 0x00,0x38,0x7C,0xFE,0xC6,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 8 0x00,0x0E,0x1F,0x3F,0x31,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x78,0xFC,0xFE,0x86,0x02,0x86,0xFE,0xFC,0xF8,0x00, // 9 0x00,0x00,0x00,0x21,0x21,0x31,0x1D,0x1F,0x0F,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // : 0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // . 0x00,0x38,0x38,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x00,0x00, // / 0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // - 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x00,0x18,0x3C,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xF0,0xF8,0x0C,0x06,0x02,0x02,0x02,0x02,0x0E,0x0C,0x00, // C 0x03,0x07,0x0C,0x18,0x10,0x10,0x10,0x10,0x1C,0x0C,0x00, 0xFE,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, // F 0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xFE,0xFE,0x40,0xE0,0xB0,0x18,0x0C,0x06,0x02,0x00,0x00, // K 0x1F,0x1F,0x00,0x00,0x01,0x03,0x06,0x0C,0x18,0x10,0x00 }; void lcd_pd12(unsigned n, unsigned x, unsigned y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 + y; lcd_send(c, 2, lcd_command); lcd_send(num11x16[n], 11, lcd_data); c[1] = 0x41 + y; lcd_send(c, 2, lcd_command); lcd_send(num11x16[n] + 11, 11, lcd_data); }
  5. 13 points
    This is a simple frequency counter with a range of 1 Hz to 16 MHz. It uses the 32 kHz watch crystal so the accuracy is good enough for many applications. Gate time is automatically set to 250 ms or 1 second. There is no signal conditioning (front end), so it is limited to 3V logic levels signals only! It is intended to be used with digital circuits like another MSP430 or 74HCxx circuits. It is not a substitute for a proper commercial frequency counter. How a frequency counter works: Fundamentals of Electronic Counters by Agilent Wiring main.c #include "msp430g2553.h" #include "lcd.h" static const unsigned long dv[] = { // Base 10 digit weights 10000000, // 8 digit maximum count 1000000, // 100000, // 10000, // 1000, // 100, // 10, // 1, // 0 // }; static void print_freq(unsigned long n) { const unsigned long *dp = dv; unsigned x = 0; unsigned c; unsigned long d; while(n < *dp) { // Skip leading zeros if(*dp == 100000 || *dp == 100) x += 2; // Space between 3 digit groups ++dp; // lcd_pd10(10, x, 2); // Print space x += 10; // } // if(n) { // Non-zero do { // d = *dp++; // Get digit value c = 0; // while(n >= d) ++c, n -= d; // Divide if(d == 100000 || d == 100) x += 2; // Space between 3 digit groups lcd_pd10(c, x, 2); // Print digit x += 10; // } while(!(d & 1)); // Until all digits done } else // lcd_pd10(0, x - 10, 2); // Print zero } static unsigned clock_input = 0; void set_input(void) { const unsigned char z = 0; lcd_pos(0, 4); lcd_send(&z, 84, lcd_data_repeat); switch(clock_input) { default: clock_input = 0; case 0: TACTL = TASSEL_2; lcd_print("Internal 16MHz", 0, 4); break; case 1: TACTL = TASSEL_0; lcd_print("Clock In P1.0", 3, 4); break; #if 0 case 2: // This should always show 32768 TACTL = TASSEL_1; // Something is very wrong if it doesn't lcd_print("Internal 32kHz", 0, 4); break; #endif } } void set_gate(unsigned long f) { if(WDTCTL & WDTIS0) { // 250 ms gate currently in use if(f < 800000) { // Switch to 1 s gate if frequncy is below 800 kHz lcd_print("1 Second Gate", 3, 5); WDTCTL = WDTPW | WDTTMSEL | WDTSSEL; } } else { // 1 s gate currently in use if(f > 900000) { // Switch to 250 ms gate if frequency above 900 kHz lcd_print(" 250 ms Gate ", 3, 5); WDTCTL = WDTPW | WDTTMSEL | WDTSSEL | WDTIS0; } } } void main(void) { unsigned long freq = 12345678L; // Measured frequency // WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset // DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // //BCSCTL3 = XCAP_1; // 6 pF (default) BCSCTL3 = XCAP_2; // 10 pF //BCSCTL3 = XCAP_3; // 12.5 pF // lcd_init(); // Initialize LCD // P1SEL |= BIT0; // Use P1.0 as TimerA input P1SEL2 &= ~BIT0; // P1DIR &= ~BIT0; // P1OUT &= ~BIT0; // Enable pull down resistor to reduce stray counts P1REN |= BIT0; // // WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL | WDTIS0; // Use WDT as interval timer // Default to 250 ms gate so that initial call to set_gate() // will switch to 1 s gate and update the LCD // lcd_clear(0); // Clear LCD lcd_print("Frequency", 16, 0); // What it is lcd_print("Counter", 22, 1); // print_freq(freq); // 8 digit frequency set_input(); // Set input and show on LCD set_gate(0); // Set gate time and show on LCD // for(; { // for-ever freq = 0; // Clear frequency TACTL |= TACLR; // Clear TimerA // IFG1 &= ~WDTIFG; // Wait for WDT period to begin while(!(IFG1 & WDTIFG)); // // TACTL |= MC_2; // Start counting - TimerA continuous mode // IFG1 &= ~WDTIFG; // while(!(IFG1 & WDTIFG)) { // While WDT period.. if(TACTL & TAIFG) { // Check for TimerA overflow freq += 0x10000L; // Add 1 to msw of frequency TACTL &= ~TAIFG; // Clear overflow flag } // } // // TACTL &= ~MC_2; // Stop counting - TimerA stop mode if(TACTL & TAIFG) freq += 0x10000L; // Handle TimerA overflow that may have occured between // last check of overflow and stopping TimerA freq |= TAR; // Merge TimerA count with overflow if(WDTCTL & WDTIS0) freq <<= 2; // Multiply by 4 if using 250 ms gate print_freq(freq); // Show on LCD // set_gate(freq); // Adjust gate time if necessary // if(!(P1IN & BIT3)) { // Check if pushbutton down ++clock_input; // Switch clock input set_input(); // } // } // } lcd.h typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type); void lcd_home(void); void lcd_pos(unsigned char x, unsigned char y); void lcd_clear(unsigned char x); void lcd_init(void); void lcd_print(char *s, unsigned x, unsigned y); void lcd_pd10(unsigned n, unsigned x, unsigned y); lcd.c #include "msp430g2553.h" #include "lcd.h" //static const unsigned TXD = BIT1; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; static const unsigned LCD_CLK = BIT5; static const unsigned LCD_BACKLIGHT = BIT6; static const unsigned LCD_DATA = BIT7; static const unsigned LCD_DC = BIT0; // PORT2 static const unsigned LCD_CE = BIT1; // PORT2 void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; P2OUT &= ~LCD_CE; do { mask = 0x0080; do { if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; mask >>= 1; } while(!(mask & 1)); if(!type) P2OUT &= ~LCD_DC; if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; P2OUT |= LCD_DC; if(!(type & 2)) ++cmd; } while(--len); P2OUT |= LCD_CE; } static const unsigned char home[] = { 0x40, 0x80 }; void lcd_home(void) { lcd_send(home, sizeof(home), lcd_command); } void lcd_pos(unsigned char x, unsigned char y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); } void lcd_clear(unsigned char x) { lcd_home(); lcd_send(&x, 504, lcd_data_repeat); lcd_home(); } void lcd_init(void) { static const unsigned char init[] = { 0x20 + 0x01, // Function set - extended instructions enabled //0x80 + 64, // Set vop (contrast) 0 - 127 0x80 + 66, // This is better for fast LCD update 0x04 + 0x02, // Temperature control 0x10 + 0x03, // Set bias system 0x20 + 0x00, // Function set - chip active, horizontal addressing, basic instructions 0x08 + 0x04 // Display control - normal mode }; P1REN = RXD | SWITCH; P1DIR = LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P1OUT = LCD_CLK | RXD | SWITCH | LCD_BACKLIGHT; P2REN = 0; P2DIR = LCD_DC | LCD_CE; P2OUT = LCD_CE; __delay_cycles(20000); P2OUT |= LCD_DC; __delay_cycles(20000); lcd_send(init, sizeof(init), lcd_command); } static const unsigned char font6x8[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 20 32 <space> 0x00, 0x00, 0x5F, 0x00, 0x00, // 21 33 ! 0x00, 0x07, 0x00, 0x07, 0x00, // 22 34 " 0x14, 0x7F, 0x14, 0x7F, 0x14, // 23 35 # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24 36 $ 0x23, 0x13, 0x08, 0x64, 0x62, // 25 37 % 0x36, 0x49, 0x56, 0x20, 0x50, // 26 38 & 0x00, 0x08, 0x07, 0x03, 0x00, // 27 39 ' 0x00, 0x1C, 0x22, 0x41, 0x00, // 28 40 ( 0x00, 0x41, 0x22, 0x1C, 0x00, // 29 41 ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A 42 * 0x08, 0x08, 0x3E, 0x08, 0x08, // 2B 43 + 0x00, 0x40, 0x38, 0x18, 0x00, // 2C 44 , 0x08, 0x08, 0x08, 0x08, 0x08, // 2D 45 - 0x00, 0x00, 0x60, 0x60, 0x00, // 2E 46 . 0x20, 0x10, 0x08, 0x04, 0x02, // 2F 47 / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 30 48 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 31 49 1 0x42, 0x61, 0x51, 0x49, 0x46, // 32 50 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 33 51 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 34 52 4 0x27, 0x45, 0x45, 0x45, 0x39, // 35 53 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 36 54 6 0x41, 0x21, 0x11, 0x09, 0x07, // 37 55 7 0x36, 0x49, 0x49, 0x49, 0x36, // 38 56 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 39 57 9 0x00, 0x00, 0x14, 0x00, 0x00, // 3A 58 : 0x00, 0x00, 0x40, 0x34, 0x00, // 3B 59 ; 0x00, 0x08, 0x14, 0x22, 0x41, // 3C 60 < 0x14, 0x14, 0x14, 0x14, 0x14, // 3D 61 = 0x00, 0x41, 0x22, 0x14, 0x08, // 3E 62 > 0x02, 0x01, 0x51, 0x09, 0x06, // 3F 63 ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // 40 64 @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // 41 65 A 0x7F, 0x49, 0x49, 0x49, 0x36, // 42 66 B 0x3E, 0x41, 0x41, 0x41, 0x22, // 43 67 C 0x7F, 0x41, 0x41, 0x41, 0x3E, // 44 68 D 0x7F, 0x49, 0x49, 0x49, 0x41, // 45 69 E 0x7F, 0x09, 0x09, 0x09, 0x01, // 46 70 F 0x3E, 0x41, 0x49, 0x49, 0x7A, // 47 71 G 0x7F, 0x08, 0x08, 0x08, 0x7F, // 48 72 H 0x00, 0x41, 0x7F, 0x41, 0x00, // 49 73 I 0x20, 0x40, 0x41, 0x3F, 0x01, // 4A 74 J 0x7F, 0x08, 0x14, 0x22, 0x41, // 4B 75 K 0x7F, 0x40, 0x40, 0x40, 0x40, // 4C 76 L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // 4D 77 M 0x7F, 0x04, 0x08, 0x10, 0x7F, // 4E 78 N 0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F 79 O 0x7F, 0x09, 0x09, 0x09, 0x06, // 50 80 P 0x3E, 0x41, 0x51, 0x21, 0x5E, // 51 81 Q 0x7F, 0x09, 0x19, 0x29, 0x46, // 52 82 R 0x26, 0x49, 0x49, 0x49, 0x32, // 53 83 S 0x01, 0x01, 0x7F, 0x01, 0x01, // 54 84 T 0x3F, 0x40, 0x40, 0x40, 0x3F, // 55 85 U 0x1F, 0x20, 0x40, 0x20, 0x1F, // 56 86 V 0x3F, 0x40, 0x38, 0x40, 0x3F, // 57 87 W 0x63, 0x14, 0x08, 0x14, 0x63, // 58 88 X 0x03, 0x04, 0x78, 0x04, 0x03, // 59 89 Y 0x61, 0x51, 0x49, 0x45, 0x43, // 5A 90 Z 0x00, 0x7F, 0x41, 0x41, 0x41, // 5B 91 [ 0x02, 0x04, 0x08, 0x10, 0x20, // 5C 92 '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // 5D 93 ] 0x04, 0x02, 0x01, 0x02, 0x04, // 5E 94 ^ 0x80, 0x80, 0x80, 0x80, 0x80, // 5F 95 _ 0x00, 0x03, 0x07, 0x08, 0x00, // 60 96 ' 0x20, 0x54, 0x54, 0x54, 0x78, // 61 97 a 0x7F, 0x28, 0x44, 0x44, 0x38, // 62 98 b 0x38, 0x44, 0x44, 0x44, 0x28, // 63 99 c 0x38, 0x44, 0x44, 0x28, 0x7F, // 64 100 d 0x38, 0x54, 0x54, 0x54, 0x18, // 65 101 e 0x00, 0x08, 0x7E, 0x09, 0x02, // 66 102 f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // 67 103 g 0x7F, 0x08, 0x04, 0x04, 0x78, // 68 104 h 0x00, 0x44, 0x7D, 0x40, 0x00, // 69 105 i 0x00, 0x20, 0x40, 0x40, 0x3D, // 6A 106 j 0x00, 0x7F, 0x10, 0x28, 0x44, // 6B 107 k 0x00, 0x41, 0x7F, 0x40, 0x00, // 6C 108 l 0x7C, 0x04, 0x78, 0x04, 0x78, // 6D 109 m 0x7C, 0x08, 0x04, 0x04, 0x78, // 6E 110 n 0x38, 0x44, 0x44, 0x44, 0x38, // 6F 111 o 0xFC, 0x18, 0x24, 0x24, 0x18, // 70 112 p 0x18, 0x24, 0x24, 0x18, 0xFC, // 71 113 q 0x7C, 0x08, 0x04, 0x04, 0x08, // 72 114 r 0x48, 0x54, 0x54, 0x54, 0x24, // 73 115 s 0x04, 0x04, 0x3F, 0x44, 0x24, // 74 116 t 0x3C, 0x40, 0x40, 0x20, 0x7C, // 75 117 u 0x1C, 0x20, 0x40, 0x20, 0x1C, // 76 118 v 0x3C, 0x40, 0x30, 0x40, 0x3C, // 77 119 w 0x44, 0x28, 0x10, 0x28, 0x44, // 78 120 x 0x4C, 0x90, 0x90, 0x90, 0x7C, // 79 121 y 0x44, 0x64, 0x54, 0x4C, 0x44, // 7A 122 z 0x00, 0x08, 0x36, 0x41, 0x00, // 7B 123 { 0x00, 0x00, 0x77, 0x00, 0x00, // 7C 124 | 0x00, 0x41, 0x36, 0x08, 0x00, // 7D 125 } 0x02, 0x01, 0x02, 0x04, 0x02, // 7E 126 ~ 0x00, 0x06, 0x09, 0x09, 0x06, // 7F 127 degrees }; void lcd_print(char *s, unsigned x, unsigned y) { lcd_pos(x, y); while(*s) { lcd_send(&font6x8[*s - 32][0], 5, lcd_data); lcd_send(&font6x8[0][0], 1, lcd_data); ++s; } } static const unsigned char num10x16[11][9 * 2] = { // Numbers for 10x16 cell 0xF0,0xF8,0x0C,0x04,0x04,0x04,0x0C,0xF8,0xF0, // 0 0x0F,0x1F,0x30,0x20,0x20,0x20,0x30,0x1F,0x0F, 0x00,0x00,0x10,0x10,0xFC,0xFC,0x00,0x00,0x00, // 1 0x00,0x00,0x20,0x20,0x3F,0x3F,0x20,0x20,0x00, 0x18,0x1C,0x04,0x04,0x04,0x04,0x8C,0xF8,0x70, // 2 0x20,0x30,0x38,0x2C,0x26,0x23,0x21,0x20,0x20, 0x18,0x1C,0x04,0x84,0x84,0x84,0xCC,0x78,0x30, // 3 0x18,0x38,0x20,0x20,0x20,0x20,0x31,0x1F,0x0E, 0x00,0x80,0x40,0x20,0x10,0x08,0xFC,0xFC,0x00, // 4 0x03,0x02,0x02,0x02,0x02,0x02,0x3F,0x3F,0x02, 0x00,0x7C,0x7C,0x44,0x44,0x44,0xC4,0x84,0x04, // 5 0x18,0x38,0x20,0x20,0x20,0x20,0x30,0x1F,0x0F, 0xE0,0xF0,0x58,0x4C,0x44,0x44,0xC4,0x84,0x00, // 6 0x0F,0x1F,0x30,0x20,0x20,0x20,0x30,0x1F,0x0F, 0x04,0x04,0x04,0x04,0x04,0xC4,0xF4,0x3C,0x0C, // 7 0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00, 0x30,0x78,0xCC,0x84,0x84,0x84,0xCC,0x78,0x30, // 8 0x0E,0x1F,0x31,0x20,0x20,0x20,0x31,0x1F,0x0E, 0xF0,0xF8,0x0C,0x04,0x04,0x04,0x0C,0xF8,0xF0, // 9 0x00,0x21,0x23,0x22,0x22,0x32,0x1A,0x0F,0x07, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // <space> 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; void lcd_pd10(unsigned n, unsigned x, unsigned y) // Print 10x16 digit { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 + y; lcd_send(c, 2, lcd_command); lcd_send(num10x16[n], 9, lcd_data); c[1] = 0x41 + y; lcd_send(c, 2, lcd_command); lcd_send(num10x16[n] + 9, 9, lcd_data); }
  6. 11 points
    cubeberg

    Etch A Sketch

    This is an LCD version of an Etch-A-Sketch. Made this for my daughter's 5th birthday. LCD is a 128*128 Powertip T6963c LCD 5v for the LCD and LP is supplied by a 5v cell phone backup 2x 100k linear pots used for control Negative contrast (-13v) is currently supplied by two 9v batteries - I'll be making one with a MC34063 later The shake detection was salvaged from a toy - no idea where to get them (I need to turn down the number of shakes needed) I used an old Floppy Drive cable to connect the LCD to an LP booster made on perfboard Many thanks to RobG for his assistance (see here for more information) Source is attached. This used every pin on the 2553, including XIN/XOUT. I have another of these for another project - I'll need to add an I/O expander to reduce the pin count. I'll post some pictures of the inside later EtchASketch_V2.zip
  7. 9 points
    EDIT: Here's another really nice DCO calibration program. It's part of TinyOS-msp430. http://code.google.com/p/tinyos-msp430/ It's the file called calibrate-dco. As gordon, who discovered it said, "[it] appears to be a quite beefed-up DCO calibration app, with goodies on the side (taking silicon errata into account for various devices when calibrating)." It looks like it's built to be solid and reliable. I skimmed over the code and didn't see anything for calculating SegA checksum, but that usually isn't an issue for most non-critical applications. EDIT: maxpenna shared another version of the DCO calibration program. This is the one to use if you need a correct SegmentA checksum. It uses the C calibration function. You can get it here http://www.43oh.com/forum/download/file.php?id=292 WARNING: Some of the programs in this thread will erase Segment A and only put back the DCO calibrations. They should not be used with chips that have an ADC12, as this process will destroy the ADC12 calibrations. Some of the programs do not calculate the Segment A checksum, so the integrity of Segment A cannot be verified during runtime after calibration. Still, I've been using it without any problems. There's been a bit of a discussion in another thread about an idea simpleavr had for adding the dco calibration constants to flash for the Value Line series. I've cobbled together some pieces of TI code and have it working. It seems to work great. Here's how I did it with CCS. I think the people on linux should be able to reproduce the result with a little adaptation. This requires the TI DCO library I modified in another thread and an external LF crystal like the one with Launchpad. http://naturetm.com/files/Launchpad%20setDCO%20lib.rar First, run this to calibrate the constants and write them to flash: EDIT: 43oh member zeke has made some changes to the code for finer tuning. Check page 2 for his code. //****************************************************************************** // MSP430F20xx Demo - DCO Calibration Constants Programmer // // NOTE: THIS CODE REPLACES THE TI FACTORY-PROGRAMMED DCO CALIBRATION // CONSTANTS LOCATED IN INFOA WITH NEW VALUES. USE ONLY IF THE ORIGINAL // CONSTANTS ACCIDENTALLY GOT CORRUPTED OR ERASED. // // Description: This code re-programs the F2xx DCO calibration constants. // A software FLL mechanism is used to set the DCO based on an external // 32kHz reference clock. After each calibration, the values from the // clock system are read out and stored in a temporary variable. The final // frequency the DCO is set to is 1MHz, and this frequency is also used // during Flash programming of the constants. The program end is indicated // by the blinking LED. // ACLK = LFXT1/8 = 32768/8, MCLK = SMCLK = target DCO // //* External watch crystal installed on XIN XOUT is required for ACLK *// // // MSP430F20xx // --------------- // /|\| XIN|- // | | | 32kHz // --|RST XOUT|- // | | // | P1.0|--> LED // | P1.4|--> SMLCK = target DCO // // A. Dannenberg // Texas Instruments Inc. // May 2007 // Built with CCE Version: 3.2.0 and IAR Embedded Workbench Version: 3.42A //****************************************************************************** #include "msp430x20x1.h" #include "DCO_Library.h" unsigned char CAL_DATA[8]; // Temp. storage for constants volatile unsigned int i; int j; char *Flash_ptrA; // Segment A pointer void Set_DCO(unsigned int setting); void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT for (i = 0; i < 0xfffe; i++); // Delay for XTAL stabilization P1OUT = 0x01; // Red LED on P1SEL = 0x10; // P1.4 SMCLK output P1DIR = 0x51; // P1.0,4,6 output j = 0; // Reset pointer Set_DCO(TI_DCO_16MHZ); // Set DCO and obtain constants CAL_DATA[j++] = DCOCTL; CAL_DATA[j++] = BCSCTL1; Set_DCO(TI_DCO_12MHZ); // Set DCO and obtain constants CAL_DATA[j++] = DCOCTL; CAL_DATA[j++] = BCSCTL1; Set_DCO(TI_DCO_8MHZ); // Set DCO and obtain constants CAL_DATA[j++] = DCOCTL; CAL_DATA[j++] = BCSCTL1; Set_DCO(TI_DCO_1MHZ); // Set DCO and obtain constants CAL_DATA[j++] = DCOCTL; CAL_DATA[j++] = BCSCTL1; Flash_ptrA = (char *)0x10C0; // Point to beginning of seg A FCTL2 = FWKEY + FSSEL0 + FN1; // MCLK/3 for Flash Timing Generator FCTL1 = FWKEY + ERASE; // Set Erase bit FCTL3 = FWKEY + LOCKA; // Clear LOCK & LOCKA bits *Flash_ptrA = 0x00; // Dummy write to erase Flash seg A FCTL1 = FWKEY + WRT; // Set WRT bit for write operation Flash_ptrA = (char *)0x10F8; // Point to beginning of cal consts for (j = 0; j < 8; j++) *Flash_ptrA++ = CAL_DATA[j]; // re-flash DCO calibration data FCTL1 = FWKEY; // Clear WRT bit FCTL3 = FWKEY + LOCKA + LOCK; // Set LOCK & LOCKA bit P1OUT = 0; while (1) { P1OUT ^= BIT6; // Toggle green LED for (i = 0; i < 0x4000; i++); // SW Delay } } void Set_DCO(unsigned int setting){ volatile unsigned int I; // P1DIR |= BIT0; // P1.0 output BCSCTL1 &= ~XTS; // external source is LF; BCSCTL3 &= ~(LFXT1S0 + LFXT1S1); // watch crystal mode BCSCTL3 |= XCAP0 + XCAP1; // ~12.5 pf cap on the watch crystal as recommended for( I = 0; I < 0xFFFF; I++){} // delay for ACLK startup if(TI_SetDCO(setting) == TI_DCO_NO_ERROR) // if setting the clock was successful, P1OUT |= BIT0; // bring P1.0 high (Launchpad red LED) else while(1); // trap if setting the clock isn't successful } The green light should start blinking in a few seconds, indicating success. Now we need to modify some linker and header files so CCS knows about the new calibrations. I'm using the MSP430G2231, so I duplicated the file C:\Program Files (x86)\Texas Instruments\ccsv4\msp430\include\msp430g2231.cmd in that directory, and renamed the duplicate as msp430g2231_mod.cmd. Now I can edit it without affecting the original. Edit msp430g2231_mod.cmd and change the calibration section at the bottom to this: /************************************************************ * Calibration Data in Info Mem ************************************************************/ CALDCO_16MHZ = 0x10F8; CALBC1_16MHZ = 0x10F9; CALDCO_12MHZ = 0x10FA; CALBC1_12MHZ = 0x10FB; CALDCO_8MHZ = 0x10FC; CALBC1_8MHZ = 0x10FD; CALDCO_1MHZ = 0x10FE; CALBC1_1MHZ = 0x10FF; Now the linker knows we have calibration data in that section when we use the modified file. When we create a new project in CCS or modify an existing one, CCS will want to use the default linking file, and the default header. Here's an example about how to configure a project to use the new constants. First create a new project. Within the project, there should be a file called something like lnk_msp430g2231.cmd. The last line of that file should be something like -l msp430g2231.cmd. Open it, and change the last line to reflect the new linker file so it reads -l msp430g2231_mod.cmd. Next, add the appropriate header file to the project. Right-click the project folder in the left pane of CCS and click "Add files to project..." In this example, I'm using C:\Program Files (x86)\Texas Instruments\ccsv4\msp430\include\msp430g2231.h. Now we have a copy of the file in the project we can safely edit. Edit the file so the calibration section looks like this: /************************************************************ * Calibration Data in Info Mem ************************************************************/ #ifndef __DisableCalData SFR_8BIT(CALDCO_16MHZ); /* DCOCTL Calibration Data for 16MHz */ SFR_8BIT(CALBC1_16MHZ); /* BCSCTL1 Calibration Data for 16MHz */ SFR_8BIT(CALDCO_12MHZ); /* DCOCTL Calibration Data for 12MHz */ SFR_8BIT(CALBC1_12MHZ); /* BCSCTL1 Calibration Data for 12MHz */ SFR_8BIT(CALDCO_8MHZ); /* DCOCTL Calibration Data for 8MHz */ SFR_8BIT(CALBC1_8MHZ); /* BCSCTL1 Calibration Data for 8MHz */ SFR_8BIT(CALDCO_1MHZ); /* DCOCTL Calibration Data for 1MHz */ SFR_8BIT(CALBC1_1MHZ); /* BCSCTL1 Calibration Data for 1MHz */ #endif /* #ifndef __DisableCalData */ Now you can begin using the new calibration data in your program. When you #include the msp430g2231.h file, put it in quotes instead of <> to ensure it's loading the modified header local to the project directory. I tested the constants with a cheap oscilloscope and they seemed correct. My scope doesn't have great accuracy at high frequencies so they might be a little off, but nothing I can detect. It works with my composite video prog, so that's a pretty good sign. EDIT: Oops! I had an incorrectly named file in these instructions. Corrected.
  8. 9 points
    not sure this is considered a project. the package i am providing allows one to play w/ TTS on a launchpad w/ g2553 chip. i did not create the TTS s/w, credit belongs to webbot at http://webbot.org.uk instead of publishing derived code, i am publishing a perl script to convert his code for msp430 use. this allows me to avoid licensing issues and i think anyone want to implement this should look at the original source. the speech system is not high quality but fun to use in projects. i.e. talking clocks, alert / alarm modules, robot voices, etc. . s/w only speech synthesizer technique from webbot . one pin pwm. . text-to-speech and synthesizer code under 10k. . fits in a g2553 mcu. source https://github.com/simpleavr/webbot_talk430 . hosted in github, search for "webbot_talk430" project . there are 4 files. . zconv.pl converts the webbotlib speech source codes to a msp430 compatible source. . talk.c TTS exercising firmware, talks to PC at 9600bps turns what u type into speech. . uart.h uart code . common.h some macros i used. . pwm hardware hook-up is in talk.c, just one resistor and one capacitor, can be improved by using better filters. instructions (as in README and help from conversion script) this is a perl script to convert the webbotlib speech sythesizer code from avr to msp430 usage. this script is use at your own risk giftware. webbotlib has it's own license terms. . locate "webbotlib" and download version 1.x, i tried w/ webbotavrclib-1.35 . inside the package locate and extract the Audio/Text2Speech directory . u will see the following files Text2Speech.h phoneme2sound.c phonemeWriter.c sound2noise.c speech2phoneme.c speechWriter.c vocab_en.c . place these files in a same directory as this (zconv.pl) script . run it again and this script will . extract what's needed, ignore others . combine into a tighter package name "../webbot_speech.h" . replace avr controls w/ msp430 controls . to use it in your firmware, do . include "webbot_speech.h" . say("Hello World"); . enjoy and thanks webbot <<< IMPORTANT >>> please observe the fact that webbotlib is GPL licensed and use / share accordingly will be glad to answer questions anyone may have in this thread. happy hacking.
  9. 9 points
    username

    MSP430 PC GUI Eval Tool

    Hey all, I was working on a GUI that would be able to control any and all functions on a particular MSP430 processor from a PC GUI. Only limitations a course would be P1.1 and P1.2 which are required for GUI communication. This is very very very much so a work in progress. Currently i'm writing it using WX Widgets libraries to simplify the GUI aspect of it. However, currently i'm not using a "form builder" so it is going to take a handy amount of time to write. Good c++ experience though.... This GUI would be very handy to have from a hardware/software test debug standpoint. For example, say I wanted to test out some SPI sensor I purchased, I could use a GUI such as this to send custom SPI packets to the device i'm testing and read/observe results live. That as opposed to having to write a micro.c file to do the communication and re write it every time something doesn't work. Many thanks to rick and suggaraddict for assistance on c++ coding help GUI Download Rev 0.5 : https://github.com/NateZimmer/MSP430-Ev ... I_EVAL.exe (windows.exe, click "view raw" to download) (feedback on whether or not it works for you would be great. Gaurenteed to work with a FTDI RS232 -> USB. However, launchpad drivers suppperr flakey and crapey ) Remember to flash https://github.com/NateZimmer/MSP430-Ev ... er/GUI.cpp to your MSP430G2553 Source Code is now at https://github.com/NateZimmer/MSP430-Eval-Tool (your going to need to download and BUILD the wx libraries to get this to work) Main Connect tab: Implemented Digital Output tab: Implemented Digital Input tab: Implemented SPI Tab: Implemented but not fully tested Analog Input tab: In Progress Connection Issue Debug 1. Make sure you got a Rev1.5 launchpad with a G2553 micro flashed with the proper code provided. Also, makesure your jumpers are set to hardware uart. 2. If that doesn't work... sadly, the TI RS232 <-> USB sucks hardcore compared to FTDI that always works. Once a connection is established, it works fine. However, getting that first connection can be a super pain. If your having connection issues, open up a terminal program(such as putty), connect to the com port of your msp430, and press the "1" key and ONLY the 1 key. If your msp430 is working properly you should receive "PING" back. At that point, close the terminal program and open up my .exe program. At that point, it should connect fine.
  10. 8 points
    RobG

    3V LaunchPad

    Here is one way to temporarily lower LaunchPad's 3.6V supply down to 3V (or 3.3V) Attach 220k (240k) resistor in parallel to R8. The values are for LaunchPad v1.5. LP v1.4 divider's values are 10 times smaller, so adjust as necessary. Also, LP will not work below 3V.
  11. 8 points
    oPossum

    USCI UART bit rate setting

    The MSP430x2xx Family User's Guide has several forumulas and tables for UART bit rate calculation. Makes it look more complicated than it is. Here is simple code to setup the bit rate divisor and modulator (for oversampling mode). First you need to know the SMCLK frequency and the desired bit rate. const unsigned long smclk_freq = 16000000; // SMCLK frequency in hertz const unsigned long bps = 19200; // Async serial bit rate Then a 20 bit divisor value can be calculated. const unsigned long brd = (smclk_freq + (bps >> 1)) / bps; // Bit rate divisor Shift and mask to setup the registers. UCA0BR1 = (brd >> 12) & 0xFF; // High byte of whole divisor UCA0BR0 = (brd >> 4) & 0xFF; // Low byte of whole divisor UCA0MCTL = ((brd << 4) & 0xF0) | UCOS16; // Fractional divisor, oversampling mode That's it! A function for UART setup at a specified bit rate... const unsigned long smclk_freq = 16000000; // SMCLK frequency in hertz void setup_UART(const unsigned long bps) // Async serial bit rate { const unsigned long brd = (smclk_freq + (bps >> 1)) / bps; // Bit rate divisor UCA0CTL1 = UCSWRST; // Hold USCI in reset to allow configuration UCA0CTL0 = 0; // No parity, LSB first, 8 bits, one stop bit, UART (async) UCA0BR1 = (brd >> 12) & 0xFF; // High byte of whole divisor UCA0BR0 = (brd >> 4) & 0xFF; // Low byte of whole divisor UCA0MCTL = ((brd << 4) & 0xF0) | UCOS16; // Fractional divisor, oversampling mode UCA0CTL1 = UCSSEL_2; // Use SMCLK for bit rate generator, release reset }
  12. 8 points
    oPossum

    Nokia 5110 C++ Template Class

    This is a C++ template for the Nokia 5100 that uses software SPI. Inspired by Rickta59's GPIO template Using C++ templates allow the compiler to make decisions at compile time that would normally occur (repeatedly) at execution time. This results in smaller and faster code. They also are type safe and compatible with source level debugging (unlike macros). Software SPI is used to make the code very portable to the entire MSP430 family and allow flexible pin assignment. Four wiring schemes are supported. This is the syntax for creating an instance of the template. Nokia5110< \ // Template name P1OUT, // Output port for clock and data BIT0, // Clock pin bitmask BIT1, // Data pin bitmask P1OUT, // Output port for DC (data/command), use PNONE if DC line is not used BIT2, // DC pin bitmask (or clock stretch cycles if DC line is not used) P1OUT, // Output port for CE (chip enable), use PNONE if CE line is not used BIT3, // CE pin bitmask 10000> \ // Reset cycle count lcd; // Object name The class template namespace nokia5110 { unsigned char PNONE; typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; template struct Nokia5110 { void write(const unsigned char *cmd, unsigned len, const lcd_cmd_type type = lcd_data); void reset(void); void init(void); void vert(void); void home(void); void pos(unsigned char x, unsigned char y); void clear(unsigned char x = 0); void bitmap(const unsigned char *bmp, signed char x, signed char y, unsigned char w, unsigned char h); inline void bitmap(const unsigned char *bmp, signed char x, signed char y) { bitmap(bmp + 2, x, y, bmp[0], bmp[1]); }; void print(char c); inline void print(unsigned char x, unsigned char y, char c) { pos(x, y); print(c); }; void print(char *s); inline void print(unsigned char x, unsigned char y, char *s) { pos(x, y); print(s); }; void printv(unsigned char x, unsigned char y, char *s); }; template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::write(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; if(&_EP != &PNONE) _EP &= ~_CE; do { mask = 0x0080; do { if(*cmd & mask) { _SP |= _DATA; _SP &= ~_CLK; } else { _SP &= ~(_CLK | _DATA); } _SP |= _CLK; mask >>= 1; } while(!(mask & 1)); if(&_CP == &PNONE) { __delay_cycles(_DC); } else { if(!type) _CP &= ~_DC; } if(*cmd & mask) { _SP |= _DATA; _SP &= ~_CLK; } else { _SP &= ~(_CLK | _DATA); } _SP |= _CLK; if(&_CP != &PNONE) _CP |= _DC; if(!(type & 2)) ++cmd; } while(--len); if(&_EP != &PNONE) _EP |= _CE; } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::reset(void) { // --- Set initial state of CLK, DC and CE as needed // * = output used for reset if(&_CP == &PNONE) { if(&_EP != &PNONE) { // CLK*, DATA, CE _EP |= _CE; } // else // CLK*, DATA } else { if(&_EP != &PNONE) { // CLK, DATA, DC*, CE if(&_SP == &_EP) { _SP |= (_CLK | _CE); } else { _SP |= _CLK; _EP |= _CE; } } // else // CLK, DATA, DC* } // --- Reset pulse on CLK or DC as needed if(&_CP == &PNONE) { // No DC port, use CLK to reset _SP &= ~_CLK; __delay_cycles(_RST); _SP |= _CLK; } else { // Use DC to reset _CP &= ~_DC; __delay_cycles(_RST); _CP |= _DC; } __delay_cycles(_RST); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::init(void) { static const unsigned char init[] = { 0x20 | 0x01, // Function set - extended instructions enabled //0x80 | 64, // Set vop (contrast) 0 - 127 0x80 | 70, // Higher contrast improves animation 0x04 | 2, // Temperature control 0x10 | 3, // Set bias system 0x20 | 0x00, // Function set - chip active, horizontal addressing, basic instructions 0x08 | 0x04 // Display control - normal mode }; write(init, sizeof(init), lcd_command); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::vert(void) { static const unsigned char v[] = { 0x20 | 0x02, // Function set - chip active, vertical addressing, basic instructions }; write(v, sizeof(v), lcd_command); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::home(void) { static const unsigned char home[] = { 0x40, 0x80 }; write(home, sizeof(home), lcd_command); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::pos(unsigned char x, unsigned char y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; write(c, sizeof(c), lcd_command); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::clear(unsigned char x) { home(); write(&x, 504, lcd_data_repeat); home(); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::bitmap(const unsigned char *bmp, signed char x, signed char y, unsigned char w, unsigned char h) { unsigned char c[2]; unsigned char ww; if(x < 0) { ww = w + x; bmp -= x; x = 0; } else if(x + w >= 84) { ww = (84 - x); } else { ww = w; } c[0] = 0x80 | x; c[1] = 0x40 | y; while(h--) { write(c, sizeof(c), lcd_command); ++c[1]; write(bmp, ww); bmp += w; } } static const unsigned char font[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x5F, 0x00, 0x00, // ! 0x00, 0x07, 0x00, 0x07, 0x00, // " 0x14, 0x7F, 0x14, 0x7F, 0x14, // # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // $ 0x23, 0x13, 0x08, 0x64, 0x62, // % 0x36, 0x49, 0x56, 0x20, 0x50, // & 0x00, 0x08, 0x07, 0x03, 0x00, // ' 0x00, 0x1C, 0x22, 0x41, 0x00, // ( 0x00, 0x41, 0x22, 0x1C, 0x00, // ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // * 0x08, 0x08, 0x3E, 0x08, 0x08, // + 0x00, 0x40, 0x38, 0x18, 0x00, // , 0x08, 0x08, 0x08, 0x08, 0x08, // - 0x00, 0x00, 0x60, 0x60, 0x00, // . 0x20, 0x10, 0x08, 0x04, 0x02, // / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 0x42, 0x61, 0x51, 0x49, 0x46, // 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 0x27, 0x45, 0x45, 0x45, 0x39, // 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 0x41, 0x21, 0x11, 0x09, 0x07, // 7 0x36, 0x49, 0x49, 0x49, 0x36, // 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 0x00, 0x00, 0x14, 0x00, 0x00, // : 0x00, 0x00, 0x40, 0x34, 0x00, // ; 0x00, 0x08, 0x14, 0x22, 0x41, // < 0x14, 0x14, 0x14, 0x14, 0x14, // = 0x00, 0x41, 0x22, 0x14, 0x08, // > 0x02, 0x01, 0x51, 0x09, 0x06, // ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // A 0x7F, 0x49, 0x49, 0x49, 0x36, // B 0x3E, 0x41, 0x41, 0x41, 0x22, // C 0x7F, 0x41, 0x41, 0x41, 0x3E, // D 0x7F, 0x49, 0x49, 0x49, 0x41, // E 0x7F, 0x09, 0x09, 0x09, 0x01, // F 0x3E, 0x41, 0x49, 0x49, 0x7A, // G 0x7F, 0x08, 0x08, 0x08, 0x7F, // H 0x00, 0x41, 0x7F, 0x41, 0x00, // I 0x20, 0x40, 0x41, 0x3F, 0x01, // J 0x7F, 0x08, 0x14, 0x22, 0x41, // K 0x7F, 0x40, 0x40, 0x40, 0x40, // L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // M 0x7F, 0x04, 0x08, 0x10, 0x7F, // N 0x3E, 0x41, 0x41, 0x41, 0x3E, // O 0x7F, 0x09, 0x09, 0x09, 0x06, // P 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q 0x7F, 0x09, 0x19, 0x29, 0x46, // R 0x26, 0x49, 0x49, 0x49, 0x32, // S 0x01, 0x01, 0x7F, 0x01, 0x01, // T 0x3F, 0x40, 0x40, 0x40, 0x3F, // U 0x1F, 0x20, 0x40, 0x20, 0x1F, // V 0x3F, 0x40, 0x38, 0x40, 0x3F, // W 0x63, 0x14, 0x08, 0x14, 0x63, // X 0x03, 0x04, 0x78, 0x04, 0x03, // Y 0x61, 0x51, 0x49, 0x45, 0x43, // Z 0x00, 0x7F, 0x41, 0x41, 0x41, // [ 0x02, 0x04, 0x08, 0x10, 0x20, // '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // ] 0x04, 0x02, 0x01, 0x02, 0x04, // ^ 0x80, 0x80, 0x80, 0x80, 0x80, // _ 0x00, 0x03, 0x07, 0x08, 0x00, // ' 0x20, 0x54, 0x54, 0x54, 0x78, // a 0x7F, 0x28, 0x44, 0x44, 0x38, // b 0x38, 0x44, 0x44, 0x44, 0x28, // c 0x38, 0x44, 0x44, 0x28, 0x7F, // d 0x38, 0x54, 0x54, 0x54, 0x18, // e 0x00, 0x08, 0x7E, 0x09, 0x02, // f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g 0x7F, 0x08, 0x04, 0x04, 0x78, // h 0x00, 0x44, 0x7D, 0x40, 0x00, // i 0x00, 0x20, 0x40, 0x40, 0x3D, // j 0x00, 0x7F, 0x10, 0x28, 0x44, // k 0x00, 0x41, 0x7F, 0x40, 0x00, // l 0x7C, 0x04, 0x78, 0x04, 0x78, // m 0x7C, 0x08, 0x04, 0x04, 0x78, // n 0x38, 0x44, 0x44, 0x44, 0x38, // o 0xFC, 0x18, 0x24, 0x24, 0x18, // p 0x18, 0x24, 0x24, 0x18, 0xFC, // q 0x7C, 0x08, 0x04, 0x04, 0x08, // r 0x48, 0x54, 0x54, 0x54, 0x24, // s 0x04, 0x04, 0x3F, 0x44, 0x24, // t 0x3C, 0x40, 0x40, 0x20, 0x7C, // u 0x1C, 0x20, 0x40, 0x20, 0x1C, // v 0x3C, 0x40, 0x30, 0x40, 0x3C, // w 0x44, 0x28, 0x10, 0x28, 0x44, // x 0x4C, 0x90, 0x90, 0x90, 0x7C, // y 0x44, 0x64, 0x54, 0x4C, 0x44, // z 0x00, 0x08, 0x36, 0x41, 0x00, // { 0x00, 0x00, 0x77, 0x00, 0x00, // | 0x00, 0x41, 0x36, 0x08, 0x00, // } 0x02, 0x01, 0x02, 0x04, 0x02, // ~ 0x00, 0x06, 0x09, 0x09, 0x06, // degrees }; template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::print(char c) { write(&font[c - 32][0], 5); write(&font[0][0], 1); } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::print(char *s) { while(*s) { write(&font[*s - 32][0], 5); write(&font[0][0], 1); ++s; } } template void Nokia5110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RST>::printv(unsigned char x, unsigned char y, char *s) { while(*s) { pos(x, y); ++y; write(&font[*s - 32][0], 5); write(&font[0][0], 1); ++s; } } } // namespace Test code #include "msp430g2553.h" #include "nokia5110tl.h" using namespace nokia5110; static const unsigned char ti[] = { 48, 48 / 8, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00,0x00,0xF0, 0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFC,0xFC,0x3C,0x80,0xFC,0xFD, 0xFD,0xFD,0x3D,0x81,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00, 0x00,0x1C,0x7C,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF7,0xF0,0xF0,0x00,0xF0,0xFF,0xFF,0xFF, 0xFF,0x0F,0xF0,0xF0,0xF0,0xF0,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xF0,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x3F,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,0x3F,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0x1F,0x00,0x1E,0x1F,0x1F,0x1F,0x1F, 0x01,0x1E,0x1F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x7F,0x7F,0x3F,0x3F,0x3F,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x03, 0x0F,0x3F,0xFF,0xFE,0xFE,0xFE,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0x7F,0x1F,0x07,0x03,0x03,0x01,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x07,0x0F,0x0F,0x1F,0x1F,0x3F, 0x3F,0x3F,0x3F,0x1C,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; static const unsigned char g0[] = { 40, 40 / 8, 0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x20,0x10,0x20, 0x20,0x40,0x80,0x80,0x00,0x00,0xE0,0x1E,0x02,0x02, 0x0C,0x30,0xC0,0x80,0x40,0x20,0x20,0x10,0x08,0x08, 0xD0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0xC0,0x40,0x40,0x40,0x40,0x40,0x43,0x6C,0x10, 0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E, 0x11,0x10,0x10,0x08,0x08,0x08,0x88,0xB0,0x40,0x00, 0x00,0x03,0x02,0x04,0x84,0x88,0x48,0x70,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x1E,0x12,0x21,0x21,0x20,0x40,0xC0,0x00, 0x00,0x02,0x0D,0x11,0x10,0x10,0x10,0x08,0x08,0x88, 0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x00,0x00, 0x08,0x36,0xC2,0x02,0x02,0x02,0x02,0x02,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x0B, 0x10,0x10,0x08,0x04,0x04,0x02,0x01,0x03,0x0C,0x30, 0xC0,0x40,0x78,0x07,0x00,0x00,0x01,0x01,0x02,0x04, 0x04,0x08,0x04,0x03,0x00,0x00,0x00,0x00,0x00,0x00 }; static const unsigned char g1[] = { 40, 40 / 8, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30, 0xC8,0x08,0x04,0x08,0x30,0x40,0x80,0x80,0xC0,0x38, 0x06,0x02,0x02,0x1E,0xE0,0x00,0x00,0x80,0x80,0x40, 0x40,0x20,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x30,0x4C,0x84,0x84,0x04,0x08,0x08,0x08, 0x09,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00, 0x20,0x58,0x44,0x43,0x40,0x80,0x80,0x80,0x80,0x00, 0x00,0xE0,0x20,0x10,0x10,0x10,0x09,0x0E,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x70,0x90,0x08,0x08,0x08,0x04,0x07,0x00, 0x00,0x01,0x01,0x01,0x01,0x02,0xC2,0x22,0x1A,0x04, 0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x90, 0x10,0x10,0x10,0x20,0x21,0x21,0x32,0x0C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x02,0x04,0x02, 0x02,0x01,0x01,0x00,0x00,0x07,0x78,0x40,0x40,0x60, 0x1C,0x03,0x01,0x01,0x02,0x0C,0x10,0x20,0x10,0x13, 0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; static const unsigned char g2[] = { 40, 40 / 8, 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFC,0x04,0x02,0x02,0x0C,0x30,0xC0, 0x80,0x80,0x60,0x18,0x06,0x02,0xC4,0x3C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x03,0x04,0x19,0xA1,0x42,0x02, 0x04,0x04,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x02,0x04,0x04, 0x02,0x82,0x42,0x21,0x11,0x0A,0x04,0x00,0x00,0x00, 0x00,0x1C,0x14,0x22,0x22,0x22,0x41,0x41,0x80,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x01,0x82,0x82,0x44,0x44,0x44,0x28,0x38,0x00, 0x00,0x00,0x00,0x20,0x50,0x88,0x84,0x42,0x41,0x40, 0x20,0x20,0x40,0xC0,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x20,0x20, 0x40,0x42,0x85,0x98,0x20,0xC0,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x3C,0x23,0x40,0x60,0x18,0x06,0x01,0x01, 0x03,0x0C,0x30,0x40,0x40,0x20,0x3F,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00 }; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; static const unsigned LCD_DC = BIT0; static const unsigned LCD_CE = BIT4; static const unsigned LCD_CLK = BIT5; static const unsigned LCD_BACKLIGHT = BIT6; static const unsigned LCD_DATA = BIT7; int main(void) { WDTCTL = WDTPW | WDTHOLD; P1REN = RXD | SWITCH; P1DIR = LCD_DC | LCD_CE | LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P1OUT = LCD_CLK | LCD_DC | RXD | SWITCH | LCD_CE | LCD_BACKLIGHT; Nokia5110 lcd; lcd.reset(); lcd.init(); lcd.clear(); for(int x = -47; x < 14; ++x) { lcd.bitmap(ti, x, 0); __delay_cycles(20000); } lcd.printv(0, 0, "MSP430"); lcd.printv(70, 0, "Nokia"); lcd.printv(78, 2, "5110"); __delay_cycles(2000000); lcd.clear(); for(int x = 14; x < 84; ++x) { lcd.bitmap(ti, x, 0); __delay_cycles(20000); } lcd.home(); char c; for(c = 33; c <= 116; ++c) { lcd.print(c); } __delay_cycles(2000000); lcd.clear(); lcd.print(0, 5, "Template Class"); for(; { for(signed char x = -39; x < 84; ) { lcd.bitmap(g0, x++, 0); __delay_cycles(100000); lcd.bitmap(g1, x++, 0); __delay_cycles(100000); lcd.bitmap(g2, x++, 0); __delay_cycles(100000); } } return 0; }
  13. 7 points
    RobG

    Nokia 7110 Library

    Here's my version of Nokia 7110 library. Just text for now, but graphics will be included later on. Enjoy! main.cfont_5x7.h sed1565.c sed1565.h
  14. 7 points
    RobG

    CC2500 Breakout/BoosterPack

    Excellent. Probably much cleaner than the rotary tool I was thinking of using. :? Here's the tutorial
  15. 7 points
    RobG

    CC2500 Breakout/BoosterPack

    After going back and forth all weekend on what jumpers to include, here's the "final" design. The board includes: 1 Booster pack 2 G2xx3/CC2500 boards 2 breakout boards compatible with booster pack 1 breakout board with single row header (I know this is one board too many, so if Seeed rejects it, I will remove single row breakout.) Booster pack is designed to work with G2xx3 chips (is there any need for G2xx1/2 compatibility?) Port mapping: P1.1 - MISO (CC2500/SPI 2) P1.2 - MOSI (CC2500/SPI 2) P1.4 - SCLK (CC2500/SPI 2) P1.5 - SCLK (CC2500/SPI 1, Memory) P1.6 - MISO (CC2500/SPI 1, Memory) P1.7 - MOSI (CC2500/SPI 1, Memory) P2.0 - CS (CC2500/SPI 1) P2.1 - GDO0 (CC2500/SPI 1) P2.2 - GDO2 (CC2500/SPI 1) P2.3 - GDO2 (CC2500/SPI 2) or Memory Hold P2.4 - GDO0 (CC2500/SPI 2) or Memory CS P2.5 - CS (CC2500/SPI 2) When memory is not used, CS should be jumpered to Vcc, same with Hold. What thickness are we going with? 1.0mm would be great for MCU boards, but might be too thin for the booster pack. 1.2mm should be perfect. I will post the complete set of files later today.
  16. 7 points
    Here's an updated CCS project for the Anaren Air Booster Pack. It's quite obvious they're not familiar with CCS. Included directories are very vague and leave header includes looking like "../../../HAL/HAL.h" instead of simply, "HAL.h". The code is a mess as well as the project's documentation lacks notes on how to configure the CCS project. CCS v5.1 Project [attachment=2]Anaren_CCS.zip[/attachment] CCS v4.2 Project [attachment=0]Anaren_CCS_v4.zip[/attachment] Originally, it required you to create a new workspace in the /Firmware folder then import the project. With this project, you can import into any workspace. 2 warnings exist after compiling but don't seem to cause malfunction. [attachment=1]warning.JPG[/attachment] Consider this repair in the BETA stages, there are a few more changes that should be made for all-round usability but it works to get it going and test it. NOTE: This is a CCS project you need to import. In CCS, click "Project -> Import Existing CCS Project" then browse to the unzipped project folder.
  17. 6 points
    zeke

    Anyone notice the 8-pin MSP430 yet?

    Presenting the MSP430G2230. Only 8 pins!
  18. 6 points
    NatureTM

    Launchpad TV output

    I got my Launchpad to output B/W composite video. It only displays one image without re-flashing though, so it's not extremely useful. The only external components are two resistors and a crystal oscillator. I plan on writing it up on my blog and releasing the source sometime this week, unless I get too busy with school. I wasn't sure if I'd be able to ever get this working, so I'm feelin' pretty good right now! Proof ;-) Code for use with an external oscillator: Launchpad_TV_Out.rar Code for if you have your DCO configuration constants calibrated: Launchpad_TV_Out_for_calibrated.rar EDIT: Here's the writeup! http://naturetm.com/?p=47
  19. 6 points
    larsie

    Wrist watch project

    I've now got the first batch of the 250 SPI B/W small LCD displays that I ordered, but I'm not quite sure what to do with them. The obvious thought is a booster pack, but since this is quite a small display I'm thinking maybe I could make a watch kit with it. I might also make some booster packs eventually. The display data sheet is here. It is SPI-based, but it uses a 9-bit SPI interface. The first bit is decides whether it's a write to the display memory or a config-command. As far as I understand, the USCI-chips don't support 9 bits, while the USI ones do. I've made a bitbanging driver for it so far, and it works ok. I guess with an HW-driver it'll be updating faster. I'll see if maybe it can be tweaked onto the USCI peripheral by sending 2 bytes. The features are: - small (34 x 30 mm physical size. Viewing area 28x19) - 3-line SPI 9-bit (Driver Sitronix ST7579-G2) - low power - backlight - black and white LCD - approx 3 USD cost I made a watch with the earlier LCD display (i2c-version) I was using. Here's a picture. The SPI-display is slightly smaller, but the display quality is similar (slightly better on the SPI display). I will have 250 of these, so if anyone is interested in this display for projects, let me know. I'm planning on making some packs that are finished soldered and can be sold in the shop, but it's relatively easy to solder these displays onto a board for your own projects also. So I'm planning on making a watch-board for the new display. It'll have the display soldered onto one side. The other side will have space for example for a MSP430G2452. The design will be open source. Any suggestions to peripherals and buttons? I could put an accelerometer there, for example to detect taps on the display. I guess I could also put a radio in it??? Edit: Have changed the picture file to a newer version.
  20. 6 points
    fj604

    Launchpad + AS1106 + 8x8 LED Matrix = fun!

    The 2211 chip that came with the Launchpad is not useless! Here is what can be done with it: I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies. Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red). A piezo buzzer can be added for (very annoying) sound effects beeps. I thought the whole thing has a certain 1970's touch to it #include #include #define DATA_PIN BIT4 // data for AS1106 #define LOAD_PIN BIT5 // load for AS1106 #define CLOCK_PIN BIT7 // clock for AS1106 #define BL_PIN BIT1 // Left button #define BR_PIN BIT2 // Right button #define SPEED_PIN BIT3 // Speed increase button #define GREEN_PIN BIT6 // Green LED on Launchpad (LED2) #define RED_PIN BIT0 // Red LED on Launchpad (LED1) #define BUZZER_PIN BIT7 // Piezo BUZZER = XOUT = P2.7 #define MAX_DELAY 2500 // Slowest speed #define MIN_DELAY 1000 // Fastest speed #define STEP_DELAY 300 // Speed step #define TONE_CATCH 5 // Tone on catch #define TONE_MISS 10 // Tone on miss #define TONE_CHIRP 2 // Chirp tone #define CHIRP_DUR 10 // Chirp duration #define TONE_DUR 300 // Catch / miss tone duration unsigned char bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; volatile unsigned char t = 3, // catcher x position refresh = 0, // flag to refresh matrix refreshing = 0; // set during refresh volatile unsigned int clk = 0, // clock ticks, 1 tick = 1/10000 sec delay = MAX_DELAY, // step delay tone = 0, // tone frequency, 1 = 5000 Hz, 5 = 1000 Hz sound = 0; // sound duration in ticks void pixel_set(unsigned char x, unsigned char y) { bitmap[x] |= 1 << y; } void pixel_clear(unsigned char x, unsigned char y) { bitmap[x] &= ~ (1 << y); } // Shift out data, MSB first void shiftout(unsigned char data) { unsigned char n; for (n = 0; n < 8; n++) { if (data & BIT7) P1OUT |= DATA_PIN; else P1OUT &= ~DATA_PIN; P1OUT |= CLOCK_PIN; P1OUT &= ~CLOCK_PIN; data <<= 1; } } // Send command to AS1106 void as1106(unsigned char addr, unsigned char data) { P1OUT &= ~LOAD_PIN; shiftout(addr); shiftout(data); P1OUT |= LOAD_PIN; } // show bitmap on the matrix void display(void) { refreshing = 1; // Set flag so that we do not change bitmap during refresh unsigned char c; for (c = 0; c < 8; c++) as1106(c + 1, bitmap[c]); refreshing = 0; // Clear flag } void as1106_init(void) { P1DIR |= (DATA_PIN | LOAD_PIN | CLOCK_PIN); P1OUT |= LOAD_PIN; P1OUT &= ~CLOCK_PIN; as1106(0x0C, 0x00); // shutdown display(); as1106(0x0F, 0x01); // Test on _delay_cycles(400000); as1106(0x0F, 0x00); // Test off _delay_cycles(200000); as1106(0x0E, BIT1); // Reset all control registers _delay_cycles(100); as1106(0x0E, 0); // Normal operation as1106(0x09, 0x00); // No decode as1106(0x0A, 0x0F); // full brightness as1106(0x0B, 0x0F); // display all rows as1106(0x0C, 0x81); // No shutdown } void clock_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; } void timer_init(void) { TACCR0 = 100; // Generate interrupt 10000 times per second TACTL = TASSEL_2 + MC_1; // SMCLK, upmode TACCTL0 = CCIE; // Capture/Compare Interrupt Enable } void sound_init(void) { P2SEL = 0; // Disable XIN/XOUT P2DIR = BUZZER_PIN; // XOUT pin = P2.7 } void intro_sound(void) { for (tone = 10; tone > 0; tone--) { sound = 500; while(sound) ; } } void button_init(void) { P1DIR &= ~(BL_PIN | BR_PIN | SPEED_PIN); P1OUT |= BL_PIN | BR_PIN ; // Pull up P1REN |= BL_PIN | BR_PIN ; // Enable pull up resistors P1IE |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt enable P1IES |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt Edge Select high-to-low P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // Clear interrupt flag } void main(void) { unsigned char n, p; // n = dot y, p = catcher x, WDTCTL = WDTPW + WDTHOLD; // Disable watchdog timer (WDT) as1106_init(); button_init(); timer_init(); sound_init(); P1DIR |= RED_PIN | GREEN_PIN; P1OUT &= ~(RED_PIN | GREEN_PIN); pixel_set(t, 7); pixel_set(t+1, 7); _enable_interrupts(); intro_sound(); for (; { p = rand() % 8; // random drop x position for (n = 0; n < 7; n++) // drop { pixel_set(p, n); display(); refresh = 0; clk = 0; tone = TONE_CHIRP; sound = CHIRP_DUR; while (clk < delay) { _low_power_mode_0(); if (refresh) { display(); refresh = 0; } } pixel_clear(p, n); } if ((p == t) || (p == t + 1)) // if caught { P1OUT |= GREEN_PIN; P1OUT &= ~RED_PIN; tone = TONE_CATCH; } else { P1OUT &= ~GREEN_PIN; P1OUT |= RED_PIN; tone = TONE_MISS; } sound = TONE_DUR; } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { if (P1IFG & SPEED_PIN) // speed switch { if ((delay-= STEP_DELAY) < MIN_DELAY) delay = MAX_DELAY; } else if ((P1IFG & BL_PIN) && (t > 0) && !refreshing) // left { pixel_clear(t + 1, 7); pixel_set(--t, 7); refresh = 1; } else if ((P1IFG & BR_PIN) && (t < 6) && !refreshing) // right { pixel_clear(t, 7); pixel_set(++t + 1, 7); refresh = 1; } P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // clear interrupt flags for all buttons _low_power_mode_off_on_exit(); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { static unsigned int z; clk++; if (sound) { --sound; if ((z++ % tone) == 0) P2OUT ^= BUZZER_PIN; // vibrate buzzer } _low_power_mode_off_on_exit(); }
  21. 6 points
    flexnodes

    Power-BoosterPack

    As promised I attach some photos: schematics: As you can see I added some screw holes to the board. You can find more info at: http://www.flexnodes.com
  22. 6 points
    chibiace

    LP screw terminal power connector

    I needed something to test my lm2576 3.3v switch mode power supply that i had built the other day, so i desoldered the male headers at the bottom of the launchpad and slotted in a cheap 2 way screw terminal. there is a tiny bit of over hang and only G G V are visible on the silkscreen but that is alright for me. im sure to use this for battery hookup in the future. if those 0.1" screw terminals weren't so damned expensive i would replace all of the other female headers with them. :twisted:
  23. 6 points
    oPossum

    Comprehensive DCO calibration

    The G series MSP430 have 1 or 4 factory calibrations for the DCO stored in info segment A. This firmware will store 96 DCO calibrations in info segments B, C, and D. After these calibrations have been stored it is possible to set the DCO to any frequency from 125 kHz to 16 MHz (128:1 range) with a simple function call... set_dco.c #include static const unsigned * const InfoSegA = (unsigned *)0x10C0; static const unsigned * const InfoSegD = (unsigned *)0x1000; int set_dco(const unsigned f) // Set DCO to specified frequency in kHz { // unsigned i; // unsigned rsel = 0; // Init rsel/dco unsigned dco = 0x20; // const unsigned *cal = InfoSegD; // Begin at info segment D do { // if(f >= cal[0] && f < cal[1]) { // Frequency in range? i = cal[1] - cal[0]; // Interpolate for MODx dco += ((((f - cal[0]) * 0x20) + (i >> 1)) / i); DCOCTL = dco; // Set the DCO/MOD BCSCTL1 = rsel; // and RSEL return 0; // Return success } // if((dco += 0x20) == 0xC0) // Next DCO/RSEL dco = 0x20, ++rsel, ++cal; // } while(++cal < InfoSegA); // While in info seg D, C, or B return -1; // Return failure } Make sure that CCS is configured to write only to main memory so the calibration constants are not erased. This is not the default configuration. The following code writes the DCO calibrations to the info segments. This only has to be done once for a specific chip. A 32 kHz crystal must be installed while this code is running, but is no longer needed once the calibration has been written. Serial output while running calibration... Info Segments - before calibration D 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, C 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, B 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65535, A 35969,5886,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,4112,32786,0, 32209,742,0,32332,437,0,2302,65535, 65535,65535,65535,2049,36757,36504,36234,34506, Calibrating... 99,106,114,123,134,146,122,131, 141,152,165,181,170,182,196,212, 229,251,241,258,278,299,323,354, 338,361,388,418,450,493,484,517, 555,596,642,703,673,718,770,827, 891,973,949,1012,1084,1163,1252,1368, 1348,1437,1538,1649,1776,1937,1924,2048, 2189,2346,2526,2754,2738,2911,3109,3329, 3586,3906,3520,3746,4007,4299,4635,5057, 4731,5032,5374,5761,6213,6770,6427,6824, 7278,7786,8385,9123,9350,9928,10587,11331, 12209,13283,12672,13414,14248,15200,16325,17715, Info Segments - after calibration D 99,106,114,123,134,146,122,131, 141,152,165,181,170,182,196,212, 229,251,241,258,278,299,323,354, 338,361,388,418,450,493,484,517, C 555,596,642,703,673,718,770,827, 891,973,949,1012,1084,1163,1252,1368, 1348,1437,1538,1649,1776,1937,1924,2048, 2189,2346,2526,2754,2738,2911,3109,3329, B 3586,3906,3520,3746,4007,4299,4635,5057, 4731,5032,5374,5761,6213,6770,6427,6824, 7278,7786,8385,9123,9350,9928,10587,11331, 12209,13283,12672,13414,14248,15200,16325,17715, A 35969,5886,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,4112,32786,0, 32209,742,0,32332,437,0,2302,65535, 65535,65535,65535,2049,36757,36504,36234,34506, Done Calibration code... main.c #include void putc(unsigned); // serial_tx.asm void puts(char *); // serial_tx.asm void utoa(unsigned, char *); // utoa.asm static char s[8]; // buffer for printing unsigned int // Address of info segments static unsigned * const InfoSegA = (unsigned *)0x10C0; static unsigned * const InfoSegB = (unsigned *)0x1080; static unsigned * const InfoSegC = (unsigned *)0x1040; static unsigned * const InfoSegD = (unsigned *)0x1000; /* Freq = Count / Time Freq = Count / (100 / 32768) <-- 100 cycles of 32.768 kHz clock Freq (kHz) = Count / ((100 / 32768) * 1000) Freq (kHz) = Count / 3.0517578125 Freq (kHz) = Count * (1 / 3.0517578125) Freq (kHz) = Count * 0.32768 Freq (kHz) ~= (Count * 21475) / 65536 Freq (kHz) ~= (Count * 21475) >> 16 Freq (kHz) ~= ((Count * 21475) + 32768) >> 16 <-- Better rounding */ void cal_block(unsigned *d, unsigned rsel, unsigned dco) { unsigned n = 32; // Do 32 cals beginning at rsel/dco with // dco range of 0x20 to 0xA0 using 0x20 step // - no modulation - 6 values per rsel DCOCTL = 0; // DCOCTL = dco & 0xE0; // Set initial DCOx, MODx == 0 BCSCTL1 = rsel; // Set initial RSELx do { // __delay_cycles(3277); // Wait for DCO to settle for 100 ms TACTL |= TACLR; // Clear TimerA TACTL |= MC_2; // Start counting - TimerA continuous mode __delay_cycles(100 - 5); // 100 cycles (5 overhead from previous and next instructions) TACTL &= ~MC_2; // Stop counting - TimerA stop mode // *d++ = ((21475L * TAR) + 32768) >> 16; // Scale to kHz // if((DCOCTL += 0x20) == 0xE0) { // Increment DCOx, check if limit reached DCOCTL = 0x20; // Reset DCOx ++BCSCTL1; // Next RSELx } // } while(--n); // } void cal_write(const unsigned *c, unsigned *f) { // Write one full info segment - 64 bytes / 32 ints unsigned n; // DCOCTL = 0; // Run DCO at 1 MHz BCSCTL1 = CALBC1_1MHZ; // DCOCTL = CALDCO_1MHZ; // FCTL2 = FWKEY | FSSEL_2 | 3 - 1; // SMCLK / 3 for flash timing generator FCTL3 = FWKEY; // Clear lock bit FCTL1 = FWKEY | ERASE; // Set erase bit *f = 0; // Dummy write to erase segment FCTL1 = FWKEY | WRT; // Set wrt bit for write operation n = 32; do *f++ = *c++; while(--n); // Write flash FCTL1 = FWKEY; // Clear wrt bit FCTL3 = FWKEY | LOCK; // Set lock bit } void show_block(const unsigned *c) { // Show 32 unsigned ints unsigned a, b; // a = 4; // 4 lines do { // b = 8; // 8 columns do { // utoa(*c++, s); // Unsigned to ASCII puts(s); // Print it putc(','); // } while(--; // Next column puts("\r\n"); // Next line } while(--a); // } void show_all_info_segs(void) { puts("D\r\n"); // show_block(InfoSegD); // D puts("C\r\n"); // show_block(InfoSegC); // C puts("B\r\n"); // show_block(InfoSegB); // B puts("A\r\n"); // show_block(InfoSegA); // A puts("\r\n"); // } void main(void) // { // unsigned n; // unsigned cal[32]; // WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset // TACTL = TASSEL_2; // SMCLK (DCO) // P1DIR = BIT1; // P1OUT = BIT1; // // BCSCTL3 = XCAP_2; // 10 pF n = 0; // do { // - Wait for MSP430 to detect clock is stable IFG1 &= ~OFIFG; // Clear OFIFG while(--n); // Wait a while } while(IFG1 & OFIFG); // Loop until OFIFG remains cleared BCSCTL2 = SELM1 | SELM0; // - Use LFXT1CLK (32 kHz xtal) as clock source // // Show all the info segments before calibration puts("Info Segments - before calibration\r\n"); show_all_info_segs(); // // puts("Calibrating...\r\n"); // Do calibration and write to flash info segments D to B // cal_block(cal, 0, 0x20); // Calibrate cal_write(cal, InfoSegD); // Write show_block(cal); // Show on terminal // cal_block(cal, 5, 0x60); // cal_write(cal, InfoSegC); // show_block(cal); // // cal_block(cal, 10, 0xA0); // cal_write(cal, InfoSegB); // show_block(cal); // // puts("\r\n"); // // Show all the info segments after calibration has been written puts("Info Segments - after calibration\r\n"); show_all_info_segs(); // // puts("Done\r\n"); // // for(;; // } serial_tx.asm .cdecls C, LIST, "msp430g2211.h" .text .def putc, puts ; 32768 Hz / 2400 bps = 13.6533 cycles per bit ~= 41 cycles per 3 bits (14 / 13 / 14) ; Char to tx in R12 putc: or #0x0100, R12 ; Stop bit mov #0x02, R15 ; Serial out bitmask jmp bit_start ; ; bit0: jmp $ + 2 ; jmp $ + 2 ; nop ; rra R12 ; Bit 0/3/6 jc bit0h ; bic.b R15, &P1OUT ; 14/55/96 jmp bit1 ; bit0h: bis.b R15, &P1OUT ; 14/55/96 jmp bit1 ; ; bit1: jmp $ + 2 ; jmp $ + 2 ; rra R12 ; Bit 1/4/7 jc bit1h ; bic.b R15, &P1OUT ; 27/68/109 jmp bit2 ; bit1h: bis.b R15, &P1OUT ; 27/68/109 jmp bit2 ; ; bit2: jmp $ + 2 ; jmp $ + 2 ; nop ; rra R12 ; Bit Start/2/5/Stop jc bit2h ; bit_start: bic.b R15, &P1OUT ; 0/41/82/- jmp bit0 ; bit2h: bis.b R15, &P1OUT ; -/41/82/123 jne bit0 ; ret_ins: ret puts: ; Tx string using putc mov R12, R14 ; String pointer in R12, copy to R14 putsloop: ; mov.b @R14+, R12 ; Get a byte, inc pointer tst.b R12 ; Test if end of string jz ret_ins ; Yes, exit... call #putc ; Call putc jmp putsloop ; ; .end ; utoa.asm .cdecls C, LIST, "msp430g2211.h" .text .def utoa utoa: ; --- Unsigned to ASCII --- ; - Range 0 to 65535 ; - Leading zeros supressed push R10 ; clr R14 ; Clear packed BCD .loop 13 ; Do 13 bits rla R12 ; Get bit 15 to 3 of binary dadd R14, R14 ; Multiply BCD by 2 and add binary bit .endloop ; clr R15 ; Clear digit 1 of packed BCD .loop 3 ; Do 3 bits rla R12 ; Get bit 2 to 0 of binary dadd R14, R14 ; Multiply BCD by 2 and add binary bit dadd R15, R15 ; .endloop ; swpb R14 ; Swap digit order mov R14, R12 ; Copy packed BCD digits and #0x0F0F, R12 ; Mask digits 5 & 3 rra R14 ; Shift digits 4 & 2 to lower nibble rra R14 ; rra R14 ; rra R14 ; and #0x0F0F, R14 ; Mask digits 4 & 2 mov #('0' << 8) | '0', R10 ; Make ASCII add R10, R12 ; add R10, R14 ; add R10, R15 ; cmp.b R10, R15 ; Is first digit a 0? jne dig5 ; No... cmp.b R10, R14 ; Is second digit a 0? jne dig4 ; No, only the first... cmp.b R10, R12 ; Is third digit a 0? jne dig3 ; No, only the first two... cmp R10, R14 ; Is fourth digit a 0? (second is zero) jne dig2 ; No, only the first three... dig1: ; First four digits are all 0 swpb R12 ; Fifth digit to string mov.b R12, 0(R13) ; inc R13 ; clr.b 0(R13) ; NULL terminate string pop R10 ; ret ; Return ; dig5: ; mov.b R15, 0(R13) ; First digit to string inc R13 ; dig4: ; mov.b R14, 0(R13) ; Second digit to string inc R13 ; dig3: ; mov.b R12, 0(R13) ; Third digit to string inc R13 ; dig2: ; swpb R14 ; Fourth digit to string mov.b R14, 0(R13) ; inc R13 ; jmp dig1 ;
  24. 6 points
    oPossum

    Setting up the DCO properly

    That is very similar to what I see with the Fraunchpad (MSP430FR5739). I can't get the scope to really sync well. The G series chips seem reasonably stable. This is a G2553 with these settings for DCO: DCOCTL 0x00 BCSCTL1 0x87 BCSCTL2 0x00 BCSCTL3 0x05 Looks like about +/- 3 ns jitter. With the modulator set to 7, there are clearly two different clock periods in use. 16 MHz factory calibration.
  25. 6 points
    In case somebody want to use that Launchpad's feature I made the connector with some 26/6 staples
×