Jump to content

yyrkoon

Members
  • Content Count

    1,408
  • Joined

  • Last visited

  • Days Won

    34

Reputation Activity

  1. Like
    yyrkoon reacted to oPossum in How to use temperature calibration data   
    [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
  2. Like
    yyrkoon reacted to grahamf72 in (error = 57) on Energia   
    Do you have CCS running in the background? I've had this error if CCS was running. Beyond that, can't help sorry.
  3. Like
    yyrkoon reacted to Rickta59 in New CCS vs Energia discussion.   
    F_CPU is just a define constant that is added to the command line at compile time.
     

    msp430-g++ -DF_CPU=16000000 ....
     
    In Energia, it comes from the build.f_cpu=nnnn value in boards.txt
     
    https://github.com/energia/Energia/blob/master/hardware/msp430/boards.txt
     
    Explained here somewhat: http://code.google.com/p/arduino/wiki/Platforms
     
    -rick
  4. Like
    yyrkoon got a reaction from Oscarasimov in Noob looking for help reading directions.   
    P indicates a port, so Px.y, would be Port x, pin y.
     
    In the case of P1.7, that would be pin 7 on port 1.
  5. Like
    yyrkoon reacted to veryalive in Flashing Firmware on a DIY MSP Launchpad   
    Hi Yavin,
    I am trying this approach myself - copying the application in Flash (I do not call it EEPROM) of the 2553 on the Launchpad into my PC, then into a target 2553 via Spy By Wire (SBW).
    I found a free tool at:   http://www.elprotronic.com/download.html   -  then scroll to the bottom of the page.
    It is called     FET-Pro430 Lite Software.   The user interface is pretty easy.
    I in fact used this tool to copy / save / reprogam the firmware initially delivered with the 430 LP.
     
    Hope this is of use to you.
     
    O
  6. Like
    yyrkoon reacted to grodius in Bounty: $20,- to solve a simple project (OPEN )   
    (First off, I'm not fishing for the bounty as I don't have a 2231)
     
    The send stop condition within the master i2c looks very much like the 'restart' posted elsewhere on this forum.  It seems like you are trying a:
     
    start -> write -> incomplete/partial stop -> restart. ( not start -> write -> stop )
     
    It looks like you commented out the actual stop code but this is likely leaving your state as started from the restart.  You should remove any restart code as it isn't necessary within the scope of this project.  You are leaving your I2C in a start as you leave and prepare for the next when you want to have stopped.
     
    I highly recommend printing/dumping your i2c state variable to see where things are finally stopping.  What you are hoping to do on the master side can be achieved entirely with a start, write and stop command.  Have faith in that and lift some existing code that is known to do that.
     
    (I would recommend removing the state machine altogether and have this project as a simple non-interrupt synchronous loop.  As a simple project it would be soooo much simpler, then build it up from there).  Some of the functions you have copied are blocking synchronous style where the majority and framework is interrupt based.  Some of the flags being blocked upon might be read or set by the interrupt handler.
     
    So what do I recommend:
     
    throw away the master  i2c code
    use cde's i2c code from the latest i2c explorer
    write your  I2CMaster_Transmit ( ) in terms of blocking start-write-write...write,stop with maybe some modest delays in-between.  I'm pretty sure that can be done with either no or minimal interrupts
     
    (For the master side I see no reason why the i2c_explorer itself wouldn't be the quickest way to test)
     
    then when that is working get fancy
  7. Like
    yyrkoon reacted to spirilis in Energy harvesting   
    Really enjoyed the video in this blog post- http://thesignalpath.com/blogs/2013/01/03/tutorial-and-experiments-on-energy-harvesting-ics/'>http://thesignalpath.com/blogs/2013/01/03/tutorial-and-experiments-on-energy-harvesting-ics/
     
    Definite potential for using these with MSP430s, particularly with the built-in LDO outputs allowing voltages like 1.8V or a bit more.
     
    Found via Hack a Day. Cool experiments! I want a bench adjustable power supply now too lol... (but I won't be shelling out ~$3-4K for one, as it appears the one he has costs!)
     
    Sent from my Android tablet using Tapatalk
  8. Like
    yyrkoon got a reaction from abecedarian in Many items for sale. Electronic parts.   
    Store taken down. We simply can not beat the price of parts from China, and can not even ship parts for free within the US.
     
    Still, "wulfmans" on ebay will sell via auction. So if anyone is interrested in stuff you can look what he has up for sale on ebay.
  9. Like
    yyrkoon got a reaction from abecedarian in Post a pic of your home work bench, get a ..   
    Some of you have workstation areas that I am absolutely jealous over lol. 6 screens ? Unfortunately, we're solar powered here, and running a desktop with even one screen is a no go. I run two laptops, one which is my "dev machine" that is (cough) *infected* with JRE,. Where the other is my main system that does everything *else* but run JRE ( including RDP into the dev machine. ). In case it is not obvious already, I am not a big fan of anything Java.
     
    On the electronics workbench stuff, my buddy has an electronics "area" that spans 3 rooms. Each room being roughly 12' x 40' +. One room dedicated to electronic parts alone. One room for arcade board repair / boards to be repaired. The other room being kind of his living quarters / workstation area. Most people who come and visit do not say much other than "holy ****!". I'll take some pictures and post them up when i get the chance.
  10. Like
    yyrkoon reacted to simpleavr in webbotlib speech synthesizer on launchpad   
    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.
  11. Like
    yyrkoon got a reaction from Philipp in Post a pic of your home work bench, get a ..   
    Some of you have workstation areas that I am absolutely jealous over lol. 6 screens ? Unfortunately, we're solar powered here, and running a desktop with even one screen is a no go. I run two laptops, one which is my "dev machine" that is (cough) *infected* with JRE,. Where the other is my main system that does everything *else* but run JRE ( including RDP into the dev machine. ). In case it is not obvious already, I am not a big fan of anything Java.
     
    On the electronics workbench stuff, my buddy has an electronics "area" that spans 3 rooms. Each room being roughly 12' x 40' +. One room dedicated to electronic parts alone. One room for arcade board repair / boards to be repaired. The other room being kind of his living quarters / workstation area. Most people who come and visit do not say much other than "holy ****!". I'll take some pictures and post them up when i get the chance.
  12. Like
    yyrkoon got a reaction from cubeberg in Many items for sale. Electronic parts.   
    Store taken down. We simply can not beat the price of parts from China, and can not even ship parts for free within the US.
     
    Still, "wulfmans" on ebay will sell via auction. So if anyone is interrested in stuff you can look what he has up for sale on ebay.
  13. Like
    yyrkoon reacted to Rickta59 in New CCS vs Energia discussion.   
    The title of this conversation was "CCS vs Energia" so I was trying to focus my responses towards that topic.  However comparing CCS and Energia is like comparing apples and oranges. Energia is an IDE + examples + a peripheral Framework + a compiler + a libc/libm.  CCS is an IDE + a compiler + a libc/libm.  Most of the questions and posts have been really about the Energia Framework, specifically the Serial class for the msp430g2553.  So maybe the topic should be Energia classes vs low level peripheral register level coding.  The Energia Framework just packages up general purpose classes for ease of use that happen to conform to the existing Arduino API. This allows you to defer learning about the low level details of the msp430 peripherals and concentrate on making something.
     
    In Energia using an msp430g2553, Serial is implemented using HardwareSerial. The HardwareSerial class handles the generic case of reading and writing to the USCI peripheral. It is a general class not that isn't optimized for read or write. It doesn't know if you are going to do blocking reads or blocking writes so it automatically provides you will a TX and RX ring buffer.  You can insert or extract serial data with the ring buffers without worrying about dealing with interrupts or the USCI peripheral.  HardwareSerial inherits the Print class. The Print class provides generalized routines that can print any data type with many different base numbers. It doesn't optimize for a specific base implementation.
     
    If all you want to do is output characters on the USCI peripheral you can implement that in either CCS or msp430-gcc with about the same efficiency.  Energia really doesn't come into play.  If you did want to stick with Energia, you don't have to use the HardwareSerial class. You can use printf and putc.  Although if you use the msp430-gcc printf(), it will suck up an extra ~2k of code. But back to the comparison part of this topic, that 2k is still much less than the CCS printf version which also uses an insane amount of data space. You could use oPossum's or robg's printf() code. Although I think both of those require some minor changes to work with msp430-gcc.
     
    A third option is to write your own MinimalSerial output only class.  This one I sketched below only outputs const char strings or hex digits. However it adds less than 300 bytes to the size of your code. I implemented an insertion operator << overload so you can get the feeling that you are using an iostream class. However, it is just an illusion. There is no iostream on msp430-gcc.
     
    This example below weighs in at 470 bytes. The bare minumum sketch is 233 so I'm not adding too much to get debug output.  I'm sure you could write straight C code using either CCS or msp430-gcc that would be slightly smaller but I'm guessing it will still be around 350-400 bytes.
     
    /* * MinimalSerial.ino - output only Serial template class example for msp430g2553 */ #if !defined(__MSP430_HAS_USCI__) #error "This code only works on the msp430g2553 chip" #endif namespace { const char ascii_digits[] = "0123456789ABCDEF"; } template<uint32_t BPS=9600> struct USCISerial { void begin(uint32_t dummy /* be consistent with Serial class begin() */) { const uint32_t baud_rate_20_bits = (F_CPU + (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 = (baud_rate_20_bits >> 12) & 0xFF; // High byte of whole divisor UCA0BR0 = (baud_rate_20_bits >> 4) & 0xFF; // Low byte of whole divisor UCA0MCTL = ((baud_rate_20_bits << 4) & 0xF0) | UCOS16; // Fractional divisor, over sampling mode UCA0CTL1 = UCSSEL_2; // Use SMCLK for bit rate generator, then release reset // Configure P1.2 as UART controlled pin P1SEL = BIT2; // P1.2=TXD P1SEL2 = BIT2; // P1.2=TXD } void printHex(uint32_t n, uint16_t bits=32) { uint16_t mask = 0x0F; do { bits -= 4; write(ascii_digits[(n >> bits) & mask]); } while( bits ); } void print(const char *s) { while(*s) { write(*s); s++; } } void print(int8_t n) { printHex(n,8); } void print(uint8_t n) { printHex(n,8); } void print(int16_t n) { printHex(n,16); } void print(uint16_t n) { printHex(n,16); } void print(int32_t n) { printHex(n,32); } void print(uint32_t n) { printHex(n,32); } void print(float f) { printHex((uint32_t)f,32); } void write(uint8_t c) { while(!(IFG2 & UCA0TXIFG)); // wait for TX buffer to empty UCA0TXBUF = c; } }; typedef USCISerial<9600> USCISerial9600; USCISerial9600 DebugSerial; template<class T> inline USCISerial9600 &operator <<(USCISerial9600 &obj, T arg) { obj.print(arg); return obj; } void setup() { DebugSerial.begin(9600); DebugSerial << "\nMinimalSerial @ 9600 Baud\n"; } void loop() { uint16_t count=1; while ( 1 ) { DebugSerial << "count=0x" << count << "\n"; count++; if ( count == 0x200 ) { while(1); } } } #if 0 // you could implement printf with something like this #include <stdio.h> int putchar(int c) { DebugSerial.write(c); return 1; } /* ... then later printf("count=%d\n", count); */ #endif  
     
    -rick
  14. Like
    yyrkoon reacted to energia in New CCS vs Energia discussion.   
    If you would like to get rid of the entire framework and just want gcc then just create a tab called main.c and leave the main Sketch empty as Rick posted in this thread http://forum.43oh.com/topic/3204-changing-the-maincpp/. From that point on you are on your own and are responsible for configuring the peripherals, clocks, etc yourself. It seems though that is what you want so you should be golden with a main.c.
     
    A Sketch with main.c weighs in at 107 bytes.
     
    Robert
  15. Like
    yyrkoon reacted to pabigot in GPIO abstraction layer   
    The only thing to watch out for is that the structures you're using don't have a volatile qualifier on the peripheral registers, which is why this happens:
    Because they're not volatile, the compiler is free to optimize the bejeezus out of the code, possibly reordering and eliminating some assignments if it sees a subsequent assignment to the same variable with no intervening read. With some registers, putting both bit sets into a single operation would result in very wrong behavior. E.g., a sequence I wrote earlier today:

    UCB0CTL1 |= UCSWRST; UCB0CTL1 &= ~UCSWRST; might very well be completely eliminated, resulting in corrupted I2C reads.
     
    I suspect this could result in some very difficult to understand bugs. You can avoid the problem by adding volatile qualifiers in the structure declarations, though of course you'll lose the corresponding performance. However, the readability/simplicity would remain.
  16. Like
    yyrkoon reacted to Rickta59 in GPIO abstraction layer   
    [EDIT: This code concept has evolved into "fabooh" find it here on github https://github.com/RickKimball/msp430_code/tree/master/fabooh ]
     
    I've been experimenting with msp430-gcc uniarch trying out different approaches to implement a fast GPIO abstraction layer that hides some of the bitmask gymnastics we all seem to do when programming the msp430. I think I've come up with a reasonable approach and I'm hoping for some feedback from all of you.
     
    Normally to access the gpio ports on the msp430 with 'C', you use a bit ANDing (P1OUT & BIT4) and ORing ( P1OUT | BIT1 ) style of programming to read and write the bits. In the Arduino world, they hide all the bit magic using digitalRead and digitalWrite. The downside, at least on the Arduino, is those instructions aren't very efficient. However, reading code written for the Arduino API does seem somewhat more understandable than a bunch of ANDs and ORs.
     
    I want to make my code more readable, but I didn't want to sacrifice efficiency. After rejecting a few different methods, such as macros and functions with port tables, I came up with a C++ template that can be used to hide most of the bit mask programming and still manages to be reasonably efficient.
     
    Here is an example of toggling some leds on and off using a timer:
    /** * gpiotest.cpp - experimental c++ template based gpio abstraction * * author: rick@kimballsoftware.com * * 2012-01-14 - this version is about 168 bytes using msp430-gcc -Os */ #include #include #include "gpio.h" GPIO<0> LED1; // P1.0 GPIO<6> LED2; // P1.6 /** * millis - access to milliseconds since startup * * Note: because the value is stored in a 16 bit int, * it rolls over every 65536 milliseconds or 65.536 seconds */ static uint16_t millis; int main(void) { WDTCTL = WDT_MDLY_0_5; // Configure Watchdog as .5 second interval timer // use default ~1.024 MHz DCO clock digitalWrite(LED1,HIGH); digitalWrite(LED2,LOW); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); IE1 |= WDTIE; // Enable WDT interrupt while(1) { // Go to sleep until the Watchdog wakes us _BIS_SR(LPM0_bits + GIE); // The WDT_INTERVAL_ISR brings the CPU out of sleep mode // and we end up here, every 8 seconds or so if ( millis % 8192 == 0 ) { volatile int i=0; i=i; } } return 0; } /** * WDT_INTERVAL_ISR -Watchdog Timer ISR handler * * Note: assumes MCLK is 1.024MHz and we are using the * WatchDog Interval timer mode of WDT_MDLY_0_5 */ #ifdef __GNUC__ __attribute__( (interrupt (WDT_VECTOR)) ) #else #pragma vector = WDT_VECTOR __interrupt #endif void WDT_INTERVAL_ISR(void) { static uint8_t _500uSCount= 0; // count each .5mSec tick // every 2 ticks update the millis counter if ( (++_500uSCount % 2) == 0) { millis++; if ( millis % 64 == 0) { // toggle every 64 mSec, uses power of 2 value for smaller code LED1.toggle(); // our 2 leds are out of sync LED2.toggle(); // one is on when the other is off _BIC_SR_IRQ(LPM0_bits); // leave the ISR and exit sleep mode } } } To use the code you include "gpio.h" .. this declares the GPIO template and provides accessor functions for reading / writing and configuring the ports and pins. 
    To use the launchpad leds, just create an instance of the GPIO object for each pin:
    GPIO<0> LED1; // P1.0 GPIO<6> LED2; // P1.6This gives you an object that allows you to manipulate the pin values. You can do things like LED1.setHigh() or LED1.setLow() to toggle that pin HIGH and LOW. LED1.setHigh() is doing a P1OUT |= BIT0; under the covers. LED1.setLow does a P1OUT &= ~BIT0. 
    To use the pin as output you need to configure it. Normally you would do a P1SEL |= BIT0; to make it an output pin. With the gpio.h you can just use LED1.configureAsOutput(). As an example of how we can be more Arduino like, I provided a macro so you can also use "pinMode(LED1, OUTPUT)" instead.
     
    Because we are using c++ templates, the compiler is very efficient at optimizing the code. In the example, I use two 'C' statements to set the pin modes. However the compilers optimizes it down to a single msp430 instruction.
    pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); c04c: f2 d0 41 00 bis.b #65, &0x0022 ;#0x0041 c050: 22 00 In fact, the example code is only 168 bytes of msp430 object code. This seems very reasonable to me for readable C code. Using C++ templates with structures has another advantage over traditional C++ classes done Arduino style. if you don't use a function you don't incur any overhead. The compiler only generates object code for the functions you use. This allows you to create all the various helper methods you might want and not end up with giant code bloat. 
    I've concentrated on making the experiment work with the msp430g2553 and msp430g2231. However it should be fine with any of the 'G' series of chips. You need to look at the way the GPIO structure is laid out and make sure it maps to the special memory areas for your chip.
     
    This isn't a finished work, it is just a starting point for those hoping for some GPIO abstraction that that still yields efficient code. Take a look at the gpio.h and the sample code and tell me what you think. It should compile with both CCS and msp430-gcc ( although I haven't tried CCS )
     
    You can find the latest version of the code here:
     
    https://gist.github.com/1597239
     
    It has the following files:
     
    gpiotest.cpp - is the sample program
    gpio.h - the GPIO template declaration
     
    [Edited 1/28/2012] updated code with more implementation methods
     
    -rick
×
×
  • Create New...