Leaderboard


Popular Content

Showing most liked content since 10/16/2010 in all areas

  1. 29 likes
    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. 20 likes
    This is a simple voltage and current meter that uses the TI INA219 chip. Voltage measurement range is 0 to 26 volts with 4 mV resolution and current measurement range is -4 to +4 amps with 1 mV resolution (when using a 0.01 ohm shunt). The specs are inferior to a pair of quality multimeters, but it is a fraction of the price and shows wattage in addition to voltage and current. The Nokia 5110 display is used so the firmware could be enhanced to do simple graphing. Sending the measurements to a computer could also be done. Using the INA219 makes for a very simple circuit. The normal display is three lines with voltage, amperage and wattage. Pressing the P1.3 switch will show the 6 registers in the INA219 in hex and signed decimal. The code is written in C++ and uses templates for the LCD, IIC and INA219. Software SPI and IIC is used for maximum portability. vam.zip
  3. 15 likes
    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. 14 likes
    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
  5. 14 likes
    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. 13 likes
    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); }
  7. 12 likes
    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
  8. 11 likes
    Here is a video of the music player in action: The code has the following functionality: Initilise the FAT16 filesystem from an SD card in a single command - ThinFAT_Init(); Open Files on an SD card and allow you to test each filename with a callback Index Files by useing the same command as to open them with a callback returns data on if the filestream is active returns data on how far the file has been read through callback for when end of the file is reached Open Folders on an SD card - Untested and limitations: uses 54 bytes of ram approx dosen't support fragmented files reads file end to closest sector, not byte (for performance reasons) reads FAT16 filesystems, not FAT32 has no support for long filenames (it would be silly on these chips) I dont know if this fits within a 2K flash when implementing this on your own chip keep in mind you change which wire is the Chip select in the MMC.h file it is based on the DIY Life Talking MSP430 Project. It has been designed for the MSP430G2352 but if you remove the CCR2 References it should work for other projects easily. Wireing: P1.2 - TA0.1 P1.3 - Input for Playing and Changing Music File P1.4 - TA0.2 P1.5 - SMCLK out - SD Card Pin 5 P1.6 - SDO - SD Card Pin 2 MOSI P1.7 - SDA - SD Card Pin 7 MISO P2.7 - CS - SD Card Pin 1 Card Select VCC - SD Card Pin 3,6 Ground - SD Card Pin 4 the pins on the SD card are counted from the first contact after the recessed contact across, the recessed one is 9. you can check on wikipedia when your unsure. SD Media Player.zip
  9. 11 likes
    MSP430F5510 development board, inspired by xpg, with LaunchPad compatible headers.
  10. 10 likes
    This is derived from NTSC/PAL video text display using launchpad. video_vga.asm is a drop in replacement for video.asm. The sync timing is for 640x480 60 Hz VGA mode. A 330 ohm resistor must be used on all video lines (R/G/B) because they are analog signals with a max allowed voltage of about 1 volt. Only one of the R/G/B lines can be driven directly from the MSP430 due to current limitations. A buffer such as 74AHC125 could be used to drive all three if desired. Wiring Gnd ------------ Ground [5, 6, 7, 8, 10] P1.5 ----------- Vertical Sync [14] P1.6 ----------- Horizontal Sync [13] P1.7 ---[330]--- Video out (R [1], G [2] or B [3]) video_vga.asm ; ; Copyright © 2011, 2012 Kevin Timmerman ; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . ; .cdecls C, LIST, "msp430g2553.h" .text .def video_init ; void video_init(void); - Initialize video generation .def field_timer ; extern int field_timer; - Decremented at each vsync .def vblank ; Vertical blanking flag .ref text ; Text buffer .ref font ; Font 8 x 12, grouped by row ; ; .bss sync_state, 2 ; Sync state .bss sync_lines, 2 ; Lines until next sync state .bss video_state, 2 ; Video generation state .bss vblank, 2 ; Vertical blanking flag .bss field_timer, 2 ; Decrement at vsync .bss text_ptr, 2 ; Text buffer pointer .bss font_ptr, 2 ; Font pointer ; --- Ideal VGA 640 x 480 60 Hz Timing --- ; Pixel clock 25.175 MHz ; Horizontal 31.47 kHz (pix / 800, negative sync) ; Vertical 59.94 Hz (pix / 420000, negative sync) ; - Horizontal timing (line) ; Scanline part Pixels Time [
  11. 9 likes
    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.
  12. 9 likes
    Here's the web server software for my Ethernet booster pack. The software is 95% ready, still few bugs and some cleanup left, I will post the code when complete. The whole thing takes up just under 4.5KB and uses ~400 bytes of RAM, so you still have ~11KB and ~100 bytes of RAM left for whatever you need. BTW, I really need to get a tripod for my iPhone
  13. 9 likes
    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.
  14. 9 likes
    This is software IIC master code in C, and C++ using a class and a template. There is also code/class/template for IIC EEPROM and the INA219 voltage/current monitor. There are 4 core functions... init(); // Init GPIO start(); // Start bit stop(); // Stop bit unsigned ex(unsigned); // Data byte & ack exchange and 3 helper functions that call ex();... unsigned write(unsigned); // Write a byte & ack, verify unsigned read(); // Read a byte & ack unsigned read_nak(); // Read a byte & nak Creating an instance of the IIC object is different for each code type: C: T_IIC iic = { BIT1, BIT0, &P2IN, &P2OUT, &P2DIR }; C++ class: CSWIIC iic(BIT1, BIT0, P2IN, P2OUT, P2DIR); C++ template: CSWIIC iic; The C functions require that the IIC structure be passed as the first argument. The C++ class and template use the usual class.function notation. C: iic_start(&iic); a = iic_write(&iic, ; iic_stop(&iic); C++ class or template: iic.start(); a = iic.write(; iic.stop(); This is a disassembly of the start() function for all three versions. The C function and C++ class member function are identical code! The template allows the compiler to do compile time optimization that can not be done with C or C++ class. It is smaller, faster and uses less RAM. iic.zip
  15. 9 likes
    http://www.youtube.com/watch?v=teZyF1IDtCM Not much info yet, just the price; a $4.99 kit. :clap: EDIT1: Found an LCD boosterpack for this board http://www.kentecdisplay.com/uploads/so ... ide_03.pdf EDIT2: by the name of the boosterpack It looks like there will be one of the LM4F120 Series http://www.ti.com/mcu/docs/mculuminaryf ... mostrecent
  16. 9 likes
    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.
  17. 8 likes
    Thanks @Rei Vilo. To all, the award goes to you too. Thanks for being wonderful members.
  18. 8 likes
    Hi, Though I'd like to share a little project of mine, which I have spend the last couple of weeks on. It's a small IPv6 stack running on the MSP430G2553 and using Microchip's ENC28J60 as MAC/PHY. I got a breakout board for the ENC28J60 cheap off e-bay (~ 7 $) and wondered if it was possible to create a small IPv6 stack using the MSP430-value line. I know that it is a bit silly, as there both are plenty of IP stacks around for the bigger MCUs and the WizNet chips (which do TCP/IP for you) are actually cheaper than the ENC28J60. But I just had to try doing it myself :oops: Now, as the 2553 only has 512 bytes of RAM I am using an external SPI SRAM chip to provide a bit more storage as an IPv6 address takes up 16 bytes and you can't get around storing around 4 or 5 of them. Currently, around 2K are used of the SPI ram. Currently supported are basic IPv6 things such as obtaining an address from Router Advertisments (only 1, though), ICMP ping, UDP transmission, and a very simple TCP implementation that support both server and client mode. The implementation is very rough (especially TCP), so I won't share it at this time. I have implemented a small proof-of-concept demonstrating the Launchpads ability to send a simple HTTP request to a web server through the ENC28J60. In order to show this, I have created two PHP scripts. One that appends a line to a file, with a time stamp, and the other displaying the file. The latter page is reloaded once a second. The Launchpad is programmed to send a request to the first page every time a button is pushed. The example takes up 11162 bytes, but does contain a fair amount of debugging strings and the size could probably be brough down a bit. The following short video-clip shows the thing working: I'll post a bit more interesting videos of Wireshark soon. I have a small project where I will use this, but for future projects I think I'll prefer using a WizNet chip
  19. 8 likes
    Hi Guys, I just fought my way through the process of getting a Launchpad to operate as a standalone programmer on a virgin windows machine. I didn't want to install CCS5.1 to get this to work. I want the bare minimum install footprint because this is going on a production line computer. Follow this process and it should work for you too. 1. Install the FET430UIF Low-Level USB VCP Drivers. 2. Plug in the Launcpad to the PC and let the PC assign the drivers to the Launchpad. 3. Open up a command console and enter the command "devmgmt.msc". 4. Verify the "MSP430 Application UART" is present under Ports (Com & LPT). 5. Download and Install the FET Pro430 Lite from Elpotronics. 6. Open up FETPro430 and click on Setup->Connection/DeviceReset. 7. Verify that the COM Port is USB (Automatic) 8. Open code file and select your TI .out or Intel .hex file. 9. Select the Microcontroller group and target ie: MSP430G2xx, MSP430G2553 10. Connect GND, TEST and RESET (at minimum) to your target board. Make sure there's power applied to your circuit in this case. 11. Click on AUTO PROG. and watch your target get programmed. This should work on your windows PC that does has never had CCS installed on it. And you won't have to resort to installing it either. Also, this should work with any version of Launchpad.
  20. 8 likes
    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.
  21. 8 likes
    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 }
  22. 8 likes
    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; }
  23. 7 likes
    This project was put on hold over the holidays. It's always a busy time, plus the club doesn't hold meets over summer. But I have just completed another 10 units. More of the same, but thought you guys might enjoy some more photos. I couldn't get the same batteries as the last batch, which were 650mAh, these have much smaller 220mAh. But this still provides about 4 hours of run time. The uBlox GPS modules are a huge improvement. Even without the SAW filter in the RF path and the sub-optimal PCB size compared to the antenna. These find more GPS satellites faster than the G.top modules, plus they also use glonass which doubles the visible satellites.
  24. 7 likes
  25. 7 likes
    [tipdf]SLAU144[/tipdf] chapter 24 describes the calibration data stored as TLV (tag length value) in info segment A. There are ADC values for 30C and 85C for both 1.5V and 2.5V internal reference voltages. These temperature calibration values can be used to improve the accuracy of the internal temperature sensor. There is no explanation of how to do this, so here is how to do it... Before using calibration data it would be a good idea to validate the info segment checksum. The checksum is stored in the first word and is the negative of the XOR of all of the following words. This function will return 0 for a valid checksum. unsigned verify_info_chk(const unsigned * const begin, const unsigned * const end) { const unsigned *p = begin + 1; // Begin at word after checksum unsigned chk = 0; // Init checksum while(p < end) chk ^= *p++; // XOR all words in segment return chk + *begin; // Add checksum - result will be zero if checksum is valid } Each chunk of calibration data in the info segment has a unique tag. This function will search for a specified tag and return a pointer to it. It will return NULL if the tag is not found. void const *find_tag(const unsigned * const begin, const unsigned * const end, const unsigned tag) { const unsigned *p = begin + 1; // Begin at word after checksum do { // const unsigned d = *p++; // Get a word if((d & 0xFF) == tag) return (void *)p; // Return pointer if tag matches p += (d >> 9); // Point to next tag } while(p < end); // While still within segment return 0; // Not found, return NULL pointer } A structure would be handy for using the ADC/temperature cal data... typedef struct { unsigned adc_gain; // ADC gain unsigned adc_offet; // ADC offset unsigned ref15; // ADC value of 1.5 volt input when using 1.5 volt reference unsigned t3015; // ADC value of 30C when using 1.5 volt reference unsigned t8515; // ADC value of 85C when using 1.5 volt reference unsigned ref25; // ADC value of 2.5 volt input when using 2.5 volt reference unsigned t3025; // ADC value of 30C when using 2.5 volt reference unsigned t8525; // ADC value of 85C when using 2.5 volt reference } TCAL; Find tag 0x08 and setup a pointer to the struct... const TCAL * const cal = (TCAL *)find_tag(info_seg_a, info_seg_a_end, 0x08); Now the scale and offset values for the temperature conversion formulas can be calculated... const long cc_scale = ((85L - 30) << 16) / (cal->t8515 - cal->t3015); const long cc_offset = -(cal->t3015 * cc_scale) + (30L << 16) + (1 << 15); const long ck_offset = cc_offset + (273.15 * (1L << 16)); const long cf_scale = ((185L - 86) << 16) / (cal->t8515 - cal->t3015); const long cf_offset = -(cal->t3015 * cf_scale) + (86L << 16) + (1 << 15); Those formulas show the derivation, they can be simplified and combined with basic error handling. The scale and offset values will be zero if the checksum is invalid or the calibration data is not found. const TCAL * const cal = (TCAL *)(verify_info_chk(info_seg_a, info_seg_a_end) \ ? 0 \ : find_tag(info_seg_a, info_seg_a_end, 0x08)); const long cc_scale = cal ? 3604480L / (cal->t8515 - cal->t3015) : 0; const long cf_scale = cal ? 6488064L / (cal->t8515 - cal->t3015) : 0; const long cc_offset = cal ? 1998848L - (cal->t3015 * cc_scale) : 0; const long cf_offset = cal ? 5668864L - (cal->t3015 * cf_scale) : 0; const long ck_offset = cc_offset + 17901158L; In some cases you may want to default to the uncalibrated values... const TCAL * const cal = (TCAL *)(verify_info_chk(info_seg_a, info_seg_a_end) \ ? 0 \ : find_tag(info_seg_a, info_seg_a_end, 0x08)); const long cc_scale = cal ? 3604480L / (cal->t8515 - cal->t3015) : 27069L; const long cf_scale = cal ? 6488064L / (cal->t8515 - cal->t3015) : 48724L; const long cc_offset = cal ? 1998848L - (cal->t3015 * cc_scale) : -18169625L; const long cf_offset = cal ? 5668864L - (cal->t3015 * cf_scale) : -30634388L; const long ck_offset = cc_offset + 17901158L; Now the actual scaled temperature values can be calculated with the usual scale/offset formulas... dcc = ((cc_scale * adc) + cc_offset) >> 16; // C calibrated dkc = ((cc_scale * adc) + ck_offset) >> 16; // K calibrated dfc = ((cf_scale * adc) + cf_offset) >> 16; // F calibrated Code for Nokia 7110 that will show uncalibrated (left) and calibrated (right) temperature in F, C, and K. main.cpp #include #include #include "nokia7110tl.h" using namespace nokia7110; /* 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 Button */ // 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; // Print integer from -999 to 9999 using 12 x 16 font void print_int(int i, unsigned x, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned e = x; x += 48; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; 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 > e) lcd.pd12(10, x -= 12, y); } // Print integer from -999 to 9999 using 6 x 8 font void print_int_small(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, ' '); } #pragma vector = ADC10_VECTOR // ADC conversion complete interrupt __interrupt void ADC10_ISR(void) // { // __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // unsigned verify_info_chk(const unsigned * const begin, const unsigned * const end) { const unsigned *p = begin + 1; // Begin at word after checksum unsigned chk = 0; // Init checksum while(p < end) chk ^= *p++; // XOR all words in segment return chk + *begin; // Add checksum - result will be zero if checksum is valid } void const *find_tag(const unsigned * const begin, const unsigned * const end, const unsigned tag) { const unsigned *p = begin + 1; // Begin at word after checksum do { // const unsigned d = *p++; // Get a word if((d & 0xFF) == tag) return (void *)p; // Return pointer if tag matches p += (d >> 9); // Point to next tag } while(p < end); // While still within segment return 0; // Not found, return NULL pointer } typedef struct { unsigned adc_gain; // ADC gain unsigned adc_offet; // ADC offset unsigned ref15; // ADC value of 1.5 volt input when using 1.5 volt reference unsigned t3015; // ADC value of 30C when using 1.5 volt reference unsigned t8515; // ADC value of 85C when using 1.5 volt reference unsigned ref25; // ADC value of 2.5 volt input when using 2.5 volt reference unsigned t3025; // ADC value of 30C when using 2.5 volt reference unsigned t8525; // ADC value of 85C when using 2.5 volt reference } TCAL; int main(void) { unsigned adc; int dc, dk, df; // Temperature in degrees C, K, and F int dcc, dkc, dfc; // Calibrated temperatures const unsigned * const info_seg_a = (unsigned *)0x10C0; // Address of info segement A const unsigned * const info_seg_a_end = info_seg_a + 32; // 32 words in each segment 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 // _EINT(); // Enable interrupts // const TCAL * const cal = (TCAL *)(verify_info_chk(info_seg_a, info_seg_a_end) \ ? 0 \ : find_tag(info_seg_a, info_seg_a_end, 0x08)); const long cc_scale = cal ? 3604480L / (cal->t8515 - cal->t3015) : 0; const long cf_scale = cal ? 6488064L / (cal->t8515 - cal->t3015) : 0; const long cc_offset = cal ? 1998848L - (cal->t3015 * cc_scale) : 0; const long cf_offset = cal ? 5668864L - (cal->t3015 * cf_scale) : 0; const long ck_offset = cc_offset + 17901158L; lcd.reset(); // lcd.init(); // // lcd.clear(); // // for(; { // for-ever ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion __bis_SR_register(LPM0_bits + GIE); // Sleep until conversion complete adc = ADC10MEM; // Read ADC // // Convert to temperature dc = ((27069L * adc) - 18169625L) >> 16; // C dk = ((27069L * adc) - 268467L) >> 16; // K df = ((48724L * adc) - 30634388L) >> 16; // F // dcc = ((cc_scale * adc) + cc_offset) >> 16; // C calibrated dkc = ((cc_scale * adc) + ck_offset) >> 16; // K calibrated dfc = ((cf_scale * adc) + cf_offset) >> 16; // F calibrated // // Display on LCD print_int(df, 0, 0); // Degrees F print_int(dfc, 48, 0); // Degrees F calibrated print_int(dc, 0, 3); // Degrees C print_int(dcc, 48, 3); // Degrees C calibrated print_int(dk, 0, 6); // Degrees K print_int(dkc, 48, 6); // Degrees K calibrated } // return 0; } nokia7110tl.h 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(!type) { if(&_CP == &PNONE) { __delay_cycles(_DC); } else { _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[] = { 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 }; */ 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[] = { 0x40, 0x80 }; 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]; /*c[0] = 0x80 | x; c[1] = 0x40 | y;*/ 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) { #if 0 home(); write(&x, 504, lcd_data_repeat); home(); #else for(unsigned y = 0; y < 9; ++y) { pos(0, y); write(&x, 96, lcd_data_repeat); } #endif } 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) { #if 0 unsigned yy = y + h; unsigned char c[2]; c[0] = 0x80 | x; for(;y < yy; ++y) { c[1] = 0x40 | y; write(c, sizeof(c), lcd_command); write(&z, w, lcd_data_repeat); } #else 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); } #endif } 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; } /*c[0] = 0x80 | x; c[1] = 0x40 | y;*/ 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[1]; ++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
  26. 7 likes
    Hi, Excuses for the long post !! After I found out that I could detect signals in the 433Mhz band using an AIR Boosterpack (See viewtopic.php?f=9&t=2944 AIR Boosterpack RF spectrum display) I wanted to display the data I was receiving on the 433 Mhz band. Ive got a Lacrosse temperature/humidity logger WS8610 that can read up to 3 external sensors and log the data. These sensors broadcast their data every minute or so. Since I have a logger and I have a display you might ask why do you want to read the data ? My motivation for this is simple, the datalogger can only capture data for about 1.5 weeks and then the oldest readings are overwritten and I need to connect the datalogger to an old PC (that still has a USB-DB9 connector) to be able to store the data. If I can read the data directly from the sensors I can hook them up in a system I allready have running with several other sensors (gas, electricityusage, inside temperature) and store the data on the internet. For those that are interested I am using Thingspeak to store my data. So last week I started to look at a way to decode the signals I saw coming in using my spectrum display. Luckily other people had allready done a lot of work on the protocol used by the sensors. The sensordata is send using On/Off keying (OOK). See http://ftp.f6fbb.org/domo/sensors/tx_signals.php and http:/ftp.f6fbb.org/domo/sensors/tx3_th.php After a few days of trial-and-error approach (thanks go to larsie for the feedback and ideas !) I actually was getting signals that could be the sensordata. To cut a long story short, using asynchronous CarrierSense detection on the AIR BOOSTERPACK and timing the length of the pulse coming in I can read the incoming data from several sensors. The software is definitely in its alpha stages. I can read one sensor properly but reading multiple sensors does give problems, if I set the RF registers to read the nearby sensor (3-4 meters away) the far away sensor (>10m) doesnt get picked up. When I set to register to read the far away, the nearby gets picked up but the data isnt as it should be anymore. Here's an image to show that I get results that are the same as my LaCrosse display shows (from left to right : MSP430 sandwich, Lacrosse display and a not active TX3TX sensor). The code below is based on the same GLCD and TI libraries as I used for my spectrum display project. regards CorB /* * This file is licensed under BSD. The simplicity code is originally copyright Texas Instruments, * but has been adapted by Lars Kristian Roland/Cor Berrevoets * the glcd code is by Lars Kristian Roland/Cor Berrevoets * the tx3th sensor information can be found at http://ftp.f6fbb.org/domo/sensors/tx3_th.php * the sensor first sends temperature information consisting of 11 blocks of 4 bits repeated once * the sensor secondly sends humidity information consisting of 11 blocks of 4 bits repeated once * block 0 is 0000 * block 1 is 1010 (preamble) * block 2 is sensortype 0=temperature E=humidity * block 3 is 4 bits sensorID * block 4 is 3 bits sensorID and another bit (unknown purpose) * block 5/6/7 is temperature in tenths degrees BCD +50 degrees * block 5/6/7 is humidity in tenths percent BCD (always ending at 0) * block 8 = block 6 * block9 = block 7 * block10 = CRC * */ //******************************************************************* // // G2553 // // ----------------- // | | // |VCC GND | // S1/GDO2 |P1.0 P2.6|-RF-GDO0 // S2 |P1.1 P2.7|-RF-CS // SW1 |P1.2 | // LED |P1.3 | // glcd-cs |P1.4 P1.7|<-------| glcd/RF-MISO //glcd/RF-CLK |P1.5 P1.6|------->| glcd/RF-SOMI // S3 |P2.0 P2.5| // S4 |P2.1 P2.4| // |P2.2 P2.3| // | | // ----------------- //******************************************************************* #include "ti/include.h" #include "../library/glcd_charset.c" #include "../library/glcd.c" unsigned long clockcnt=0,lastclock=0; unsigned long lastpulsecnt, pulsecnt=0,faultcount=0, pulselength=0; unsigned char bit; // bitcounter 0..3 unsigned char bitdata[4]={8,4,2,1}; // to create a nibble readout with MSB first coming in char pulseindx; // nibblecounter unsigned char pulsetrain[50],oldpulse[50]; // datapackage storage unsigned char line=4; // display of temperature datapackage int rssi; #define signal BIT2 void setup433MhzOOKreader() { TI_CC_SPIStrobe(TI_CCxxx0_SIDLE); // set IDLE TI_CC_SPIWriteReg(TI_CCxxx0_FREQ2, 0x10); // Freq control word, high byte TI_CC_SPIWriteReg(TI_CCxxx0_FREQ1, 0x00); //Freq control word, mid byte. TI_CC_SPIWriteReg(TI_CCxxx0_FREQ0, 0x00); //Freq control word, low byte. TI_CC_SPIWriteReg(TI_CCxxx0_MDMCFG4, 0x06); // RX bandwidth and datarate MSB TI_CC_SPIWriteReg(TI_CCxxx0_MDMCFG3, 0x37); // datarate LSB TI_CC_SPIWriteReg(TI_CCxxx0_MDMCFG2, 0x00); // no preamble/sync TI_CC_SPIWriteReg(TI_CCxxx0_MDMCFG1, 0x00); // CHANNEL SPACING and channelspacing MSB TI_CC_SPIWriteReg(TI_CCxxx0_MDMCFG0, 0xe5); // x2E5=200Khz x1E5=100Khz x0E5=50Khz x000=25Khz, CHANNEL SPACING LSB TI_CC_SPIWriteReg(TI_CCxxx0_FOCCFG, 0x00); // no IF compensation for OOK TI_CC_SPIWriteReg(TI_CCxxx0_AGCCTRL2,0xc3); // receiver gain 0xAB A B TI_CC_SPIWriteReg(TI_CCxxx0_PKTCTRL0, 0x32); //asynchronous mode TI_CC_SPIWriteReg(TI_CCxxx0_IOCFG0, 0x0E); //GDO0Output Pin Configuration asynchronous output TI_CC_SPIWriteReg(TI_CCxxx0_CHANNR, 48); // change radio to channel 48 in 432Mhz band (50khz spacing) TI_CC_SPIStrobe(TI_CCxxx0_SRX); // goto receivermode } void main (void) { WDTCTL = WDT_MDLY_0_064; // WDT ~0.064ms interval timer IE1 |= WDTIE; // Enable WDT interrupt P1DIR |=signal; TI_CC_GDO0_PxIES &= ~TI_CC_GDO0_PIN; // Int on falling edge (end of pkt) TI_CC_GDO0_PxIFG &= ~TI_CC_GDO0_PIN; // Clear flag for GDO0 TI_CC_GDO0_PxIE |= TI_CC_GDO0_PIN; // Enable interrupt on end of packet RF_init(); // SETUP ANAREN AIR RF __delay_cycles(50000); SPISetup(); // Initialize SPI Display __delay_cycles(100000); clear(); __delay_cycles(100000); //reset the pulsetrain indices and storage arrays for (pulseindx=0; pulseindx<49; pulseindx++) { pulsetrain[pulseindx]=0; oldpulse[pulseindx]=0; } pulsecnt=0; pulseindx=0; bit=0; lastpulsecnt=0; writeInt(0,2,faultcount,3); // display faults writeInt(48,2,pulsecnt,3); // display how many packages have been read setup433MhzOOKreader(); __enable_interrupt(); // enable all interrupts while (1) // do for ever { if (pulsecnt>lastpulsecnt) // display data only of we have received a package { setcharmode(1); // switch to large characters if ((oldpulse[1]==0x0a) && (oldpulse[12]==0x0a)) { unsigned int temperature; unsigned int humidity; temperature=(oldpulse[5]-5)*100+oldpulse[6]*10+oldpulse[7]; writeInt(0,0,temperature,3); humidity=oldpulse[27]*10+oldpulse[28]; writeInt(48,0,humidity,3); lastpulsecnt=pulsecnt; } writeInt(0,2,faultcount,3); // display faults writeInt(48,2,pulsecnt,3); // display how many packages have been read setcharmode(0); // switch to small characters unsigned char temp; char i,x; x=0; // show the 11 nibbles of the temperature part of the package for (i=0; i<11; i++) { temp = oldpulse[i]; temp += (temp > 9) ? 'A' - 10 : '0'; writeChar(x,line,temp); x=x+6; } writeInt(x+6,line,rssi,4); } } } #pragma vector = WDT_VECTOR // - Watchdog timer interrupt vector __interrupt void wdt_isr(void) // This interrupt will occur once per second { // clockcnt++; } #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) { // REALLY cheap debounce. // We can get away with this because we're edge triggering but reading the level. if(TI_CC_GDO0_PxIFG & TI_CC_GDO0_PIN) { pulselength=clockcnt-lastclock; lastclock=clockcnt; if (pulselength<30) {pulsetrain[pulseindx]|=bitdata[bit];} bit++; if (bit==4) {bit=0; pulseindx++;} if (pulseindx>43) // we got all 44 bits in { rssi=TI_CC_SPIReadStatus(TI_CCxxx0_RSSI); __delay_cycles(100); if(rssi < 128) // convert RSSI readout to dB { rssi = -((rssi/2) - 74); } else{ rssi = -(((rssi - 256)/2) - 74); } char j; for (j=0; j<45; j++) {oldpulse[j]=pulsetrain[j]; pulsetrain[j]=0; } //reset indices pulseindx=0; bit=0; //increment the line to display the datapackage line++; if (line>7) {line=4;} if (oldpulse[1]==0x0a) {pulsecnt++;} else {faultcount++; pulsecnt++;} // add a long delay after reading the 44th nibble, corrects possible mistakes also __delay_cycles(10000000); } TI_CC_GDO0_PxIFG &= ~TI_CC_GDO0_PIN; // After pkt RX, this flag is set. } }
  27. 7 likes
    [EDIT] The newest version of this BP is here [EDIT] This project is finished Available in the 43oh Store. This is still in it's early phase, so I need your input. Here are some specs so far: 1. WIZnet's W5200 Ethernet controller chip (W5100 or W5300 is an option.) 2. MagJack 3. LDO + power header 4. Opto isolators (2 or more) + output header 5. Input/output header for switches/keypad/display 6. DIP or SMD MSP430G I have decided to use W5200 because... "W5200 chip is a Hardwired TCP/IP embedded Ethernet controller that enables easier internet connection for embedded systems using SPI (Serial Peripheral Interface). The W5200 is composed of a fully hardwired market-proven TCP/IP stack and an integrated Ethernet MAC & PHY. Hardwired TCP/IP stack supports TCP, UDP, IPv4, ICMP, ARP, IGMP, and PPPoE. By using W5200, users can implement the Ethernet application they need by using a simple socket program instead of handling a complex Ethernet Controller." In other words, you do not have to deal with or waste resources on TCP/IP stack. The board could be used as a booster pack or free standing, hence MSP430G and LDO. The total cost should be around $10 or less. Few decisions I have to make: 2 or more opto couplers? DIP, SMD or both (MSP430G?) Any suggestions?
  28. 7 likes
    So I finally tried out the replacement cdc_acm module from the e2e forums. I'm using the file 7028.msp430_patch.zip mentioned in this post http://e2e.ti.com/support/low_power_rf/f/156/p/53610/697952.aspx#697952. I'm using Ubuntu 11.04 with a 2.6.38 kernel. Installing this patch solved my problems with /dev/ttyACM0 and linux. I tried the unmodified Energia ASCII Table example. The code in that sample doesn't wait before sending data, however with this patched module it just works. With the old cdc-acm my whole UI would freeze for multiple 10s of seconds. There are some things that are different from an Arduino. The main one being the launchpad doesn't reset the msp430g2553 chip when it receives a DTR line signal down the /dev/ttyACM0. What does that mean? Opening and closing the serial console doesn't restart the msp430. When you open the window you must press the reset button on the launchpad to have it restart like an Arduino would. However, with this patch, the UI doesn't lock up any more or stall when you first run it. You also don't have to be worried about printing data when the serial console isn't open. Two other people have also tried this patch with similar good results. One system was Ubuntu 10.04/2.6.something kernel and arch linux with a 3.something kernel. I think this goes a long way to addressing stability for the linux version of Energia. Yay Gerald Stanje the kernel hacker author. +1 -rick
  29. 7 likes
    Hi all, One of the things I wanted to build using the Anaren AIR boosterpack was a mini spectrum "analyzer" or spectrum display. During my holiday i found time to play around with both the AIR boosterpack and the LCD module Lars has produced. Although the AIR boosterpack was designed for 868/915 Mhz it seems the receiver inside (CC110L) is sensitive also in the 433Mhz region. The code I share with this mail allows the user (by changing a define) to switch between 3 frequencybands. Two buttons of the LCD package allow change of RX bandwidth and channelwidth. THe screenshot below shows a typical readout in the 433 Mhz band in a room where a Lacross TX3TH temperature/humidity sensor is sending data every minute using an OOK protocol (Request: if anybody knows how to receive/decode OOK using a CC110L please inform me). The display shows the low and high frequency in Mhz and shows the frequency with the highest readout (RSSI in dB) in kHz and as a blinking line in the graph. If you want to know more about this mini spectrum display or have other comments, feel free to share them. regards CorB spectrum.zip glcd.zip
  30. 7 likes
    I received the datalogger board today and after multiple hw tests to verify that board functioned as expected, I completed the first of 3 test boards. The last test is to verify that the memory chip is recording correctly, and until then I can't say that this board is 100% functional. though the current 80% has me pretty excited. Now I'm waiting for the addon board that is breaks out the datalogging pins plus a couple other pins to a usable 3 pin configuration. Images included in this set have the button 1 switch which allows for it to be a reset or action button, and the male header pins. The other image shows the board pre switch and header mockup and also conected to the lp which allows me to do the uploading of programs. Very happy so far. Tonight and tomorrow I'll work on getting the memory chip tested as well as doing some initial tests on the data input ports(adc channels)and communications port(hw uart).
  31. 7 likes
    First board (actually it's the second one) assembled and working as expected! Shipping rest of the boards to SA tomorrow.
  32. 7 likes
    I came across the port of the Arduino IDE for the msp430 (it is called Energia) reading another post. Robert Wessels has made a great first stab at supporting the Arduino API for the launchpad boards. It already works on the MacOSX and Windows platforms. Edit: [i checked in the changes to git for the linux version [Linux isn't up to snuff yet, but I'll be helping him getting that working properly.] . It should be good to go. If you are running linux and have a working java, msp430-gcc and mspdebug. For a limited time you can try out an early snapshot by downloading this linux32 distribution. http://xxxxxxx It is large (~15MB) and will be changing so it will only available at that URL for a limited time. Download the tar file and unpack it. To launch, just run the energia shell script. In the Energia IDE, select the Tools menu pick one of the Launchpad boards. Set the Programmer to rf2500 and then pick your serial port. The picture below is a snapshot of the ASCIITable example found in the Communications section. The example uses the Hardware serial port of the msp430g2553 so make sure your TX/RX jumpers are set correctly. You can also find some msp430 specific examples. I haven't tested all the examples so don't expect everything to work. This is a very early version of Energia. If you are a java weenie and want to checkout the source code, you can find it up on github: https://github.com/energia/Energia -rick
  33. 7 likes
    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
  34. 7 likes
    Excellent. Probably much cleaner than the rotary tool I was thinking of using. :? Here's the tutorial
  35. 7 likes
    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.
  36. 7 likes
    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.
  37. 7 likes
    Hi all, Browsing through DangerousPrototypes blog today, I saw Ian posted some info about TI's floating-point Piccolo MCUs. Texas Instruments has development boards with F28069 in memory stick form factor on promotion now ($11 if using the promo code Piccolo11 at checkout). It seems those Piccolo MCUs have far more capabilities than I'll ever use in my simplistic projects, but there are a lot of clever people in this forum who may find it useful You can see and order the dev board here: https://estore.ti.com/TMDX28069USB-F28069-Piccolo-controlSTICK-P2257C43.aspx?DCMP=F2806x&HQS=Other+EM+f2806x_em1_es Here is where I got this tip from: http://dangerousprototypes.com/2011/01/13/c2000-piccolo-f2806x-mcu/ Cheers, zn
  38. 6 likes
  39. 6 likes
  40. 6 likes
    To start off, this is not a tutorial but more of a quick how-to. The 1st post will be about using your Bus Pirate to "sniff" SPI data on your MSP430. Mostly, I'm posting this because I've never found clear instructions or examples on doing this but it's been important in solving a lot of programming issues. The Bus Pirate is the handiest tool on my bench -it's tiny and has a lot of functionality. Bus Pirate http://dangerousprototypes.com/docs/Bus_Pirate Hardware Connections Bus Pirate -> MSP430 CS -> UCBxSTE (or your CS pin) CLK ->UCBxCLK ( or your SPI clock pin) MISO -> UCBxSOMI (or your SPI input pin) MOSI -> UCBxSIMO (or your SPI output pin) AUX -> SPI D/C or other Trigger source. Software Alternative Sump Logic Analyzer Client http://www.lxtreme.nl/ols/ Labeled Client Project for MSP430 sump_no_signals.JPG[/attachment] Then, what I like to do, is create a logic trigger in my program. This is done, so I can determine exactly when I want to trigger the analyzer instead of relying on a clock source. This code can be placed in your main.c: // manual logic trigger for analyzer #define LOGIC_OUT P1OUT #define LOGIC_DIR P1DIR #define LOGIC_BIT BIT7 #define TRIGGER (LOGIC_OUT |= LOGIC_BIT) Here I used P1.7 on the MSP430F2274. Change the port and bit configuration to suit your hardware. This will be the pin we connect to the AUX pin of the Bus Pirate. Then you can place TRIGGER; anywhere you'd like to start analyzing your data. To being a capture, click on the 'Start Capturing' button. You'll be presented with a configuration window. Select the configuration for your hardware; Analyzer Port is the com port for the Bus Pirate. Leave the speed at 115200bps and for Device Type, select "Bus Pirate OLS mode". In the Acquisition tab, leave the default settings. Move to the Triggers tab; check the box to enable the trigger. For mode, select "Serial" and Channel to 0. The last step is select the Trigger Mask. We're using Channel 4 (AUX on the Bus Pirate), so put a check in the 5th box from the right. It should look like this: uart3.zipsump_no_annotations.JPG[/attachment] From here, select "Tools -> SPI Analyzer". The proper channel and mode settings were saved as part of the Labeled Client Project, so just press "Analyze". You will see all of the data that was transfer and at what times. Press close when you're done reading. The resulting window after Analyzing: [attachment=0]sump_annotations.JPG[/attachment] Here you can see that our Trigger was pulled high, CS was pulled low, no serial data was incoming, the clock is function properly and our outgoing datastream is "0xAE, 0x00, 0x10, 0x40, 0x00, 0x81, 0x8F....." (BTW, that's the init sequence for the OLED Booster Pack) To scan again, just press the "Repeat Capture" button. This also works for I2C, UART and other protocols (with different settings, of course).
  41. 6 likes
    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.
  42. 6 likes
    This is a project I have finished recently. Since I am lazy and didn't feel the need to use keys to access my apartment I decided to hook up an rfid reader to my back door, which in turn will open my breaker bar on my patio door. It is sorta of a kludge till I can find a stepper motor to replace my rube goldberg~esqe like device. How it lifts the breaker bar, it uses a solenoid to pull a large ball bearing that is attached to a string, that goes through a screw eye, then it attaches to the breaker bar. when the correct card is scanned it pulls on the balanced ball bearing, which it will then pull on the breaker bar to unlock the patio door. I use a overkill atx power supply to power everything, 12v for the solenoid, 5v for the rfid reader, and 3.3v to power the msp430. Since I have about 5 more atx power supplies sitting around, its not really a waste, just getting use out of something that taking up space in a closet. Please ignore all the ScotchLoks that I use. They are one of the best way to connect 2 wires together, and also having 2 bulk containers(2 wire and 3 wire, cnt 500), makes using them a no brainer on projects or other repairs. http://justinstech.org/2012/07/rfid-back-door-easy-access/ In the video, I am using the older code before I put the reset code in, so I had to manually reset the device every time the card was scanned. I am sure that the code can be improved but it works quite well at this time. It has been tested for the past few weeks with out issues. I know my delay kludge could be improved by use of the time but this leaves options open for upgrades at a later date (like adding a stepper motor). code http://justinstech.org/wp-content/uploads/2012/07/main.c //****************************************************************************** // MSP430F20xx Demo - Timer_A, Ultra-Low Pwr UART 2400 Echo, 32kHz ACLK // M. Buccini / L. Westlund // Texas Instruments Inc. // October 2005 // Built with CCE Version: 3.2.0 and IAR Embedded Workbench Version: 3.40A //****************************************************************************** // // This code has been modified to work with the parallax RFID reader for 2400 baud // It works and all but needs improvment. // Modifications By Justin Solarski // built with MSPGCC and a Text editor...CCE and IAR specific code commented out. // or removed. Delay may not work on CCE or IAR please use built in delay cycles. // Changed from running on the aclk/32K crystal to 8Mhz // removed all TX code because its not needed // // MSP430F20xx // ----------------- // /|\| XIN|- // | | | // --|RST XOUT|- // | | // | p1.7 |------\/\/>100ohm--->tip120 to solonoid // | | 2400 8N1 // | CCI0A/RXD/P1.1|<---------paralax RFID reader // #include //#include //for string comparison //string comparison failed do to non charactors in string........... #define RXD 0x02 // RXD on P1.1 // Conditions for 2400 Baud SW UART, ACLK = 32768 #define Bitime_5 1667 // old = 0x06 ~ 0.5 bit length + small adjustment #define Bitime 3333 // 0x0E = old aclk val 427us bit length ~ 2341 baud e=14 //****************************************************************************** unsigned int RXTXData; unsigned char BitCnt; unsigned char rfidtag[12]; unsigned int tagcnt = 0; unsigned char tag1[12] = {0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0d}; //tag replaced by 0xff for security reasons, I doubt any one wants to break into my place // but just in case they do, they will have to do it the hard way. //****************************************************************************** void RX_Ready (void); void __inline__ msp_delay(register unsigned int n); void Check_rfid(void); //****************************************************************************** void main (void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer BCSCTL1 = CALBC1_8MHZ; // Set range DCOCTL = CALDCO_8MHZ; // Set DCO step + modulation */ TACTL = TASSEL_2 + MC_2; // ACLK, continuous mode // tassel_1 =aclk tassel_2 = smclk P1SEL = RXD; // // P1DIR |= BIT0 + BIT7; P1OUT |= BIT0; P1OUT &= ~BIT7; //****************************************************************************** // Mainloop for (; { RX_Ready(); // UART ready to RX one Byte _BIS_SR(LPM0_bits + GIE); // Enter LPM3 w/ interr until char RXed //****************************************************************************** //when all 12 chars are recieved if ( tagcnt == 12) { Check_rfid(); } else { rfidtag[tagcnt] = RXTXData; tagcnt++; } } } //****************************************************************************** //check if RFID matches known rfid void Check_rfid(void) { unsigned char flag; unsigned int i; while(1) { for(i=0; i<12; i++) { if (rfidtag[i] == tag1[i]) // check tag against one in memory. { flag = 0x01; } //end if else { flag = 0x00; // moved to flag code 0x00, it was redundant to put reset code here // for(i=0; i<12; i++) //incoming data reset to 0 // { // rfidtag[i] = 0; // } // moved to flag code 0x00, it was redundant to put reset code here // tagcnt = 0; // WDTCTL = WDT_MRST_32; //reset msp430 after read and execute // while(1){ msp_delay(50000); } } //end else } //end for if (flag == 0x01) { P1OUT |= BIT7; msp_delay(50000); //delay kludge msp_delay(50000); msp_delay(50000); msp_delay(50000); msp_delay(50000); msp_delay(50000); msp_delay(50000); msp_delay(50000); //end of delay kludge for(i=0; i<12; i++) { rfidtag[i] = 0; } P1OUT ^= BIT7; tagcnt = 0; WDTCTL = WDT_MRST_32; //reset msp430 after read and execute while(1){ msp_delay(50000); } } //end if if (flag == 0x00) { for(i=0; i<12; i++) { rfidtag[i] = 0; } tagcnt = 0; WDTCTL = WDT_MRST_32; //reset msp430 after not the correct tag while(1){ msp_delay(50000); } } } //end while } //****************************************************************************** // delay loop used for mspgcc, if using IAR or CCE please replace with delay cycles void __inline__ msp_delay(register unsigned int n) { __asm__ __volatile__( " 1: \n" " dec %[n] \n" " jne 1b \n" : [n] "+r"(n)); } //****************************************************************************** // Function Readies UART to Receive Character into RXTXData Buffer void RX_Ready (void) { BitCnt = 0x8; // Load Bit counter CCTL0 = SCS + OUTMOD0 + CM1 + CAP + CCIE; // Sync, Neg Edge, Cap } //****************************************************************************** // Timer A0 interrupt service routine //#pragma vector=TIMERA0_VECTOR //__interrupt void Timer_A (void) __attribute__((interrupt(TIMERA0_VECTOR))) void CCR0_ISR(void) { CCR0 += Bitime; // Add Offset to CCR0 if( CCTL0 & CAP ) // Capture mode = start bit edge { CCTL0 &= ~ CAP; // Switch from capture to compare mode CCR0 += Bitime_5; } else { RXTXData = RXTXData >> 1; if (CCTL0 & SCCI) // Get bit waiting in receive latch RXTXData |= 0x80; BitCnt --; // All bits RXed? if ( BitCnt == 0) //>>>>>>>>>> Decode of Received Byte Here <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< { CCTL0 &= ~ CCIE; // All bits RXed, disable interrupt _BIC_SR_IRQ(LPM0_bits); // Clear LPM3 bits from 0(SR) } //>>>>>>>>>> Decode of Received Byte Here <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } } If you have any suggestions, questions or criticism please let me know **edit** there are pictures of the each component on my blogpost that is linked above
  43. 6 likes
    This is the first graph of data pulled from the data logger. This was a simple test, but verified saving and retrieving of data from the chip and some other test functions that I have been working on. This test used the WDT interval timer and took roughly an hour to run to collect 4096 samples. The variances in temp were due to the time and the amount of sun hitting the logger(2 rapid increases in temp). I think it's time to populate another board as I know the setup works and would allow me to start running longer tests while working on code.
  44. 6 likes
    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(); }
  45. 6 likes
    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
  46. 6 likes
    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:
  47. 6 likes
    I wanted to try out myself how well solar energy really works. Toward this aim I built a solar collector system for waterheating. A small boiler with a heat exchanger is used for heat storage and a ordinary heating pump for the water circulation. Temperature is measured at the collector, at the entrance and outlet of the heat exchanger and inside the heat storage boiler. As a controller for the pump I used an MSP430 launchpad. There were several problems I had to solve. First, temperature must be measured with a resolution of at least 1 degree. My preferred temperature sensor would have been a Pt100. This sensors are accurate and linear, but not very sensitive. With a 8 bit AD converter, a resolution of not more than 4 degree is possible, not good enough. Therefore I was forced to use NTC sensors. These are more sensitive, but not very accurate and the resistance changes with temperature not linearly, but exponentially. When I tried this out, I had to realize that the exponential function of C uses too much memory. Therefore, I had to find a replacement and I used a rational approximation that uses much less space and works satisfactorily accurate. Further, as the NTC sensors are not accurate enough, I had to measure the value of each and correct by software. To supervise the working of the controller I transmit the temperature values, the state of the pump and what action is taken to a PC that can be connected to the launchpad. This is actually not needed for the working of the controller, but it is nice to have a mean to see what is going on. Here is how the program works: After initialization we enter an infinite loop, that does the actual work. - First all 4 temperature values are measured. According to these values we decide what to do. - If the boiler temp is too high, we switch the pump of for security reasons. - If the pump is off and the collector temp is higher than the boiler temp by some margin and the collector temp is above some minimal value, we switch the pump on. - If the pump is on, we compare the entry and outlet temp of the heat exchanger. If these differ less than some value, that is, practically no heat is put into the boiler, we switch the pump off. - Finally we toggle a LED every 1 sec to indicate that the controller is working and wait 1 minute before we start the next cycle. To make the temperature measurement more reliable, I measure the temp 8 times and take the average. To minimize current consumption I put the CPU to sleep during wait. This controller now works for more than half an year to my full satisfaction. I get approx. 0.75 kW / m^2 heating power on a sunny day. I only regret, that we do not have more sunny days here in Switzerland. And here is the program that uses up nearly every bit of the 2K memory: ======================================================= /* Solar collector controller * Daniel Huber 1.7.2011 daniel_huber@sunrise.ch * * Measures Pin 1.3,1.4,1.5,1.7 with average * Calculates temperatures from measured values * If boiler temp too high -> do nothing * If collector temp > boiler+ TDelCol switch on pump, wait 1 min. * If temp of heatexchanger input < heat exchanger output+TDelEx switch off pump, repeat * send values to RS232 */ #include "msp430g2231.h" #include "stdbool.h" #include "stdlib.h" #include "math.h" #include "string.h" #define Tcm 110 // max temp at collector #define Tcmin 30 // min temp at collector #define Tbm 90 // max temp at boiler #define TDelCol 5 // min. temp between Collector and Boiler #define TDelEx 2 // min. temp between heat exchanger in and out #define TXD BIT1 // TXD on P1.1 #define Bit_time 104 // 9600 Baud, SMCLK=1MHz (1MHz/ 9600)=104 #define LEDR BIT0 // LED red #define LEDG BIT6 // LED green #define CHCOLLECT INCH_4 // measure channel collector #define CHHEATEXIN INCH_5 // measure channel heat exchanger #define CHHEATEXOUT INCH_3 // measure channel heat exchanger #define CHBOILER INCH_7 // measure channel boiler // pump on/off #define PumpOn {P1OUT |= BIT0;PumpOnFl=true; } #define PumpOff {P1OUT &= ~BIT0;PumpOnFl=false; } unsigned char BitCnt; // Bit count, used when transmitting byte unsigned int TXByte; // value sent over UART when Transmit() is called int i,j; // 'for' loop variable int TCol,TExIn,TExOut,TBoiler,channel; // Measured ADC Value bool PumpOnFl; // pump on flag unsigned int ADCValue; // aux. value to return value from interrupt short state; // state variable char msg[3]; float cor,R; // Function Definitions void Transmit(); //transmits a byte int Single_Measure(); //measures with avarage void Single_Measure0(); //measure single value void TransmitRecord(/*int TCol,int TExIn,int TExOut,int TBoiler,bool PumpOnFl*/); // transmit one record void delay1(); //delays 1 sec int P2T(int P); // ADC value to degree void main(void) { WDTCTL = WDTPW + WDTHOLD+WDTCNTCL; // Stop WDT: PWD,Hold, Counter Reset BCSCTL1 = CALBC1_1MHZ; // Set range DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz // BCSCTL3 |= LFXT1S1; // sets LFXT1Sx to 0b10, VLO mode, bit not set-> 32KHz BCSCTL3 |= XCAP_2; // set 10pF for cristal oscillator P1SEL |= TXD; // Connect TXD to timer pin P1DIR |= TXD | LEDR | LEDG; // use TX PumpOff; // Pump off state=1; __bis_SR_register(GIE); // interrupts enabled while(1){ msg[0]='\0'; // empty string cor=1.01*10.12;channel=CHCOLLECT; //correction factor: TCol=P2T(Single_Measure()); // collector temp cor=1*10.32;channel=CHHEATEXIN; TExIn=P2T(Single_Measure()); // heat exchanger in temp cor=0.89*10.21;channel=CHHEATEXOUT; // 0.937 takes care of R20 difference TExOut=3+P2T(Single_Measure()); // heat exchanger out temp, correct for NTC tolerance cor=1*10.32;channel=CHBOILER; TBoiler=P2T(Single_Measure());// boiler temp if (TBoiler > Tbm && state!=0) {PumpOff;state=0;strcat(msg,"st00");} switch(state){ case(0): if(TBoiler case(1): if((TCol>TBoiler+TDelCol) && TCol>=Tcmin) {PumpOn; state=2;strcat(msg,"12");} break; case(2): if(TExIn } TransmitRecord(); for(i=1;i<60;i++){ P1OUT ^= LEDG; // toggle LED at P1.6 delay1(); } } } // transmit one record void TransmitRecord(){ unsigned int k; i=0; while(i>=0){ switch(i){ case(0): k='SS'; break; case(1): k='tt'; break; case(2): k=TCol; break; case(3): k=TExIn; break; case(4): k=TExOut; break; case(5): k=TBoiler; break; case(6): if(PumpOnFl)k=1; else k=0; break; case(7): if (strlen(msg)>0){k=256*msg[1]+msg[0]; break;} else {i++;} case(8): k='EE'; break; case(9): {k='nn'; i=-2;} }; TXByte = (k & 0x00FF); // Set TXByte Transmit(); // Send TXByte = k >> 8; // Set TXByte to the upper 8 bits TXByte = TXByte & 0x00FF; Transmit(); i++; } } // averaged single measurement int Single_Measure(/*int channel*/){ int ADCAvg = 0; for (i = 0; i < 8; i++){ // add up values Single_Measure0(channel); ADCAvg += ADCValue; } ADCAvg >>= 3; // divide by 8 return ADCAvg; } /** * Reads ADC channel once, using AVCC as reference. **/ void Single_Measure0(/*int channel*/) { ADC10CTL0 &= ~ENC; // Disable ADC ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE; // 64 clock ticks, ADC On, enable ADC interrupt ADC10CTL1 = ADC10SSEL_3 +channel; // Set 'chan', SMCLK __delay_cycles(1000); ADC10CTL0 |= ENC + ADC10SC; // Enable and start conversion _BIS_SR(CPUOFF + GIE); // sleep CPU } /** * Transmits the value currently in TXByte. The function waits till it is * finished transmiting before it returns. **/ void Transmit() { TXByte |= 0x100; // Add stop bit to TXByte (which is logical 1) TXByte = TXByte << 1; // Add start bit (which is logical 0) BitCnt = 0xA; // Load Bit counter, 8 bits + ST/SP CCTL0 = OUT; // TXD Idle as Mark TACTL = TASSEL_2 + MC_2; // SMCLK, continuous mode CCR0 = TAR; // Initialize compare register CCR0 += Bit_time; // Set time till first bit CCTL0 = CCIS0 + OUTMOD0 + CCIE; // Set signal, intial value, enable interrupts while ( CCTL0 & CCIE ); // Wait for previous TX completion } /** * ADC interrupt routine. Pulls CPU out of sleep mode. **/ #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR (void) { ADCValue = ADC10MEM; // Saves measured value. __bic_SR_register_on_exit(CPUOFF); // Enable CPU so the main while loop continues } /** * Timer interrupt routine. This handles transmitting and receiving bytes. **/ #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { CCR0 += Bit_time; // Add Offset to CCR0 if ( BitCnt == 0) // If all bits TXed { TACTL = TASSEL_2; // SMCLK, timer off (for power consumption) CCTL0 &= ~ CCIE ; // Disable interrupt } else { CCTL0 |= OUTMOD2; // Set TX bit to 0 if (TXByte & 0x01) CCTL0 &= ~ OUTMOD2; // If it should be 1, set it to 1 TXByte = TXByte >> 1; BitCnt --; } } /** * function to get temperature from measured ADC value **/ int P2T(int P){ #define P0 0x3FF R=cor*P/(P0-P); return( (159.444F+R*(29.4008F-0.21077F * R))/(1+R*(0.59504F+0.0155797F * R)) ); } //put CPU to sleep for 1 sec void delay1(){ IE1 |= WDTIE; // Watchdog Interrupt Enable WDTCTL = WDTPW+WDTTMSEL+WDTSSEL; // Passwd(WDTPW), Counter-Mode //Intervall(WDTTMSEL),Timer= "0"(WDTCNTCL), //SourceClk=ACLCK(WDTSSEL),Sel:00=Clk/32768 01=Clk/8192 10:Clk/512 11=Clk/64 _BIS_SR(LPM3_bits + GIE); // put CPU to sleep LPM1_bits } // delay interrupt routine, wakes up CPU #pragma vector=WDT_VECTOR __interrupt void WATCHDOG_ISR (void){ // interrupt routine for delay WDTCTL = WDTPW + WDTHOLD+WDTCNTCL; //Password(WDTPW), //Watchdog stop(WDTHOLD),Counter=0, this resets register(exeption: hold bit) // IE1 &= ~WDTIE; // Watchdog Interrupt Disable __bic_SR_register_on_exit(LPM3_bits); // clear LPM3 bits so the main while loop continues } ========================================================
  48. 6 likes
    Here's an example using an MSP430G2553 and a 24LC256. It's not perfect but it works. It first writes "HELLO WORLD" too 0x0000 then reads it back and sends it out the UART at 9600. I hope it helps //#include //For mspgcc //#include //For mspgcc #include "msp430g2553.h" //Address word + "HELLO WORLD" unsigned char txdata[14] = {0x00, 0x00, 0x00, 0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x20, 0x57, 0x4F, 0x52, 0x4C, 0x44}; unsigned char rxdata[12]; unsigned char tx_byte_count; unsigned char rx_byte_count; unsigned char tx_byte_counter; unsigned char rx_byte_counter; unsigned char i; unsigned char tx_rx; void i2c_tx(unsigned char tx_count); void i2c_rx(unsigned char rx_count); void main(void) { WDTCTL = WDTPW + WDTHOLD; //Stop WDT BCSCTL1 = CALBC1_1MHZ; //Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; P1SEL = BIT1 + BIT2; //Set RXD and TXD P1SEL2 = BIT1 + BIT2; UCA0CTL1 |= UCSSEL_2; //Have USCI use SMCLK AKA 1MHz main CLK UCA0BR0 = 104; //Baud = 9600 UCA0BR1 = 0; UCA0MCTL = UCBRS_1; //Modulation UCA0CTL1 &= ~UCSWRST; //Start USCI P1SEL |= BIT6 + BIT7; //Set I2C pins P1SEL2|= BIT6 + BIT7; UCB0CTL1 |= UCSWRST; //Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; //I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; //Use SMCLK, keep SW reset UCB0BR0 = 12; //fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0I2CSA = 0b1010000; //Slave Address UCB0CTL1 &= ~UCSWRST; //Clear SW reset, resume operation IE2 |= UCB0TXIE; //Enable TX interrupt IE2 |= UCB0RXIE; //Enable RX interrupt __delay_cycles(20000); //Just a start up delay i2c_tx(13); //i2c TX 13 bytes(Address word + "HELLO WORLD" __delay_cycles(20000); //Allow 24LC256 to write data i2c_tx(2); //i2c TX address i2c_rx(12); //i2c RX data for(i = 1; i < 12; i++) { while(!(IFG2 & UCA0TXIFG)); UCA0TXBUF = rxdata[i]; //UART TX data } __bis_SR_register(CPUOFF + GIE); //Wait for a reset } void i2c_tx(unsigned char tx_count) { tx_rx = 0; tx_byte_count = tx_count + 1; tx_byte_counter = tx_count; // Load TX byte counter UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts // Remain in LPM0 until all data is TX'd } void i2c_rx(unsigned char rx_count) { tx_rx = 1; rx_byte_count = rx_count + 1; rx_byte_counter = rx_count; // Load RX byte counter UCB0CTL1 &= ~UCTR; // I2C RX UCB0CTL1 |= UCTXSTT; // I2C start condition __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts // Remain in LPM0 until all data is RX'd } //interrupt(USCIAB0TX_VECTOR) USCIAB0TX_ISR(void) #pragma vector = USCIAB0TX_VECTOR __interrupt void USCIAB0TX_ISR(void) //For mspgcc { if(tx_rx == 0) { if (tx_byte_counter > 0) //Check TX byte counter { UCB0TXBUF = txdata[tx_byte_count - tx_byte_counter]; // Load TX buffer tx_byte_counter--; //Decrement TX byte counter } else if(tx_byte_counter == 0) { UCB0CTL1 |= UCTXSTP; //I2C stop condition while (UCB0CTL1 & UCTXSTP); //Ensure stop condition got sent IFG2 &= ~UCB0TXIFG; //Clear USCI_B0 TX int flag __bic_SR_register_on_exit(CPUOFF); //Exit LPM0 } } else if(tx_rx == 1) { if (rx_byte_counter > 0) //Check RX byte counter { rxdata[rx_byte_count - rx_byte_counter] = UCB0RXBUF; rx_byte_counter--; //Decrement RX byte counter } else if(rx_byte_counter == 0) { UCB0CTL1 |= UCTXSTP; // I2C stop condition while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent rxdata[rx_byte_count - (rx_byte_counter + 1)] = UCB0RXBUF; rxdata[rx_byte_count - (rx_byte_counter + 1)] = UCB0RXBUF; IFG2 &= ~UCB0RXIFG; // Clear USCI_B0 RX int flag __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } } It hasn't been tested with CCS or IAR just mspgcc.
  49. 6 likes
    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.
  50. 6 likes
    In case somebody want to use that Launchpad's feature I made the connector with some 26/6 staples