Jump to content

fj604

Members
  • Content Count

    41
  • Joined

  • Last visited

  • Days Won

    4

Reputation Activity

  1. Like
    fj604 got a reaction from Simbo in Interfacing the Launchpad with MAX6956 I2C LED driver   
    Here is a slightly modified version that implements a repeat start on I2C.
     
    It is explained here:
    http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/t/105688.aspx
     

    /* * Interfacing MSP430G2231 to MAX6956 I2C LED driver and I/O expander Pins MSP430G2231 MAX6956 1 DVCC ----------- 28 V+ 8 P1.6/SCL ----------- 26 SCL 9 P1.7/SDA ----------- 25 SDA 14 DVSS ----------- 2 GND ----------- GND 3 GND ----------- GND 4 AD0 ----------- GND 27 AD1 ----------- GND 1 ISET -/\/39K\/\- GND 12 P19 ----------- LED1 13 P20 ----------- LED2 14 P21 ----------- LED3 8 P15 ----/ ----- GND Upon startup, LED1-3 will light up for about 1.5 seconds, then LED2-3 will start glowing intermittently. While a button on P15 is pushed, LED1 will change brightness. */ #include #define PIN_CS BIT1 #define PIN_SCL BIT6 #define PIN_SDA BIT7 #define DELAY 40000 #define MAX6956_ADDR 0x80 #define ACK 0x00 #define NACK 0xFF void i2c_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; P1REN |= PIN_SCL | PIN_SDA; // Enable pullup resistors for I2C termination P1OUT |= PIN_SCL | PIN_SDA; USICTL0 = USIPE7 | USIPE6 | USIMST | USISWRST; // SDA enable, SCL enable, Master mode, release USICTL1 = USII2C | USIIE ; // USICKPH; // CKPH = 1, I2C, Interrupt Enable; USICKCTL = USIDIV_7 | USISSEL_2 | USICKPL; // Clock / 128, SMCLK, SCL inactive is high USICTL0 &= ~USISWRST; // Release from reset } void timer_init(void) { TACTL = TASSEL_2 | MC_0; // SMCLK, stop timer TACCTL0 = CCIE; // Enable timer compare interrupt } void sleep(unsigned int count) { TACCR0 = count; // Load top value in compare register TACTL |= TACLR | MC_1; // Clear counter, start timer in up mode _low_power_mode_0(); // Sleep in LPM0 until interrupt TACTL &= ~MC_1; // Stop timer } void i2c_tx_start(void) { USISRL = 0; USICTL0 |= USIGE | USIOE; // Latch enable, SDA enable USICTL0 &= ~USIGE; // Latch disable } void i2c_tx_rpt_start(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0xFF; // Set MSB of USISRL to high USICNT = 1; // Transmit 1 bit for a surplus clock cycle _low_power_mode_0(); // Sleep in LPM0 until interrupt i2c_tx_start(); } void i2c_tx_stop(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0; USICNT = 1; // Transmit 1 bit _low_power_mode_0(); // Sleep in LPM0 until interrupt USISRL = 0xFF; USICTL0 |= USIGE; // Latch enable USICTL0 &= ~(USIGE | USIOE); // Latch disable, SDA disable } unsigned char i2c_tx_byte(unsigned char byte) { USICTL0 |= USIOE; USISRL = byte; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt USICTL0 &= ~USIOE; // SDA disable USICNT = 1; // Receive ACK bit _low_power_mode_0(); // Sleep in LPM0 until interrupt return USISRL & BIT0; // Return ACK = first bit of USISRL } unsigned char i2c_rx_byte(void) { unsigned char v; USICTL0 &= ~USIOE; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt v = USISRL; return v; } void i2c_tx_ack(unsigned char ack) { USICTL0 |= USIOE; USISRL = ack; // Send ACK/NACK bit USICNT = 1; _low_power_mode_0(); // Sleep in LPM0 until interrupt } void max6956_send(unsigned char addr, unsigned char data) { i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_byte(data); i2c_tx_stop(); } unsigned char max6956_recv(unsigned char addr) { unsigned char v; i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_rpt_start(); // Generate a repeat start i2c_tx_byte(MAX6956_ADDR | BIT0); v = i2c_rx_byte(); // Read and send NACK to stop transmission i2c_tx_ack(NACK); i2c_tx_stop(); return v; } void sense(void) { static unsigned char g=0; if (!(max6956_recv(0x2F) & BIT0)) // Send No-op command and check bit 0 = button pushed max6956_send(0x19, g++ << 4); // Increase brightness of LED on port 19 } void leds(unsigned char v) { max6956_send(0x1A, v | (0x0F-v) << 4); // control brightness of LEDs on ports 20-21 sense(); } void main(void) { signed char i=0; WDTCTL = WDTPW + WDTHOLD; // Disable WDT i2c_init(); timer_init(); _enable_interrupts(); max6956_send(0x0C, 0x00); // set ports 16-19 to LED max6956_send(0x0D, 0x00); // set ports 20-23 to LED max6956_send(0x0B, 0xFF); // set ports 12-15 to GPIO Input with pullup max6956_send(0x33, 0x01); // Port 19 on max6956_send(0x34, 0x01); // Port 20 on max6956_send(0x35, 0x01); // Port 21 on max6956_send(0x07, 0x01); // Display test on _delay_cycles(1500000); // ~ 1.5s pause max6956_send(0x07, 0x00); // Display test off max6956_send(0x04, BIT0 | BIT6); // Individual segment current control, normal operation for (; { for (i=0; i<0x10; i++) { leds(i); sleep(DELAY); } for (i=0x0F; i>=0; i--) { leds(i); sleep(DELAY); } } } #pragma vector=USI_VECTOR __interrupt void usi_interrupt(void) { USICTL1 &= ~USIIFG; // Clear interrupt flag _low_power_mode_off_on_exit(); // Return from LPM } #pragma vector=TIMERA0_VECTOR __interrupt void timer_A_interrupt(void) { _low_power_mode_off_on_exit(); // Return from LPM }
  2. Like
    fj604 got a reaction from Simbo in Interfacing the Launchpad with MAX6956 I2C LED driver   
    As promised here: http://www.43oh.com/forum/viewtopic.php?f=10&t=1015
     
    MAX6956 is an I2C LED driver / port expander, here is a demo:
     

    /* * Interfacing MSP430G2231 to MAX6956 I2C LED driver and I/O expander Pins MSP430G2231 MAX6956 1 DVCC ----------- 28 V+ 8 P1.6/SCL ----------- 26 SCL 9 P1.7/SDA ----------- 25 SDA 14 DVSS ----------- 2 GND ----------- GND 3 GND ----------- GND 4 AD0 ----------- GND 27 AD1 ----------- GND 1 ISET -/\/39K\/\- GND 12 P19 ----------- LED1 13 P20 ----------- LED2 14 P21 ----------- LED3 8 P15 ----/ ----- GND Upon startup, LED1-3 will light up for about 1.5 seconds, then LED2-3 will start glowing intermittently. While a button on P15 is pushed, LED1 will change brightness. */ #include #define PIN_CS BIT1 #define PIN_SCL BIT6 #define PIN_SDA BIT7 #define DELAY 40000 #define MAX6956_ADDR 0x80 #define ACK 0x00 #define NACK 0xFF void i2c_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; P1REN |= PIN_SCL | PIN_SDA; // Enable pullup resistors for I2C termination P1OUT |= PIN_SCL | PIN_SDA; USICTL0 = USIPE7 | USIPE6 | USIMST | USISWRST; // SDA enable, SCL enable, Master mode, release USICTL1 = USII2C | USIIE ; // USICKPH; // CKPH = 1, I2C, Interrupt Enable; USICKCTL = USIDIV_7 | USISSEL_2 | USICKPL; // Clock / 128, SMCLK, SCL inactive is high USICTL0 &= ~USISWRST; // Release from reset } void timer_init(void) { TACTL = TASSEL_2 | MC_0; // SMCLK, stop timer TACCTL0 = CCIE; // Enable timer compare interrupt } void sleep(unsigned int count) { TACCR0 = count; // Load top value in compare register TACTL |= TACLR | MC_1; // Clear counter, start timer in up mode _low_power_mode_0(); // Sleep in LPM0 until interrupt TACTL &= ~MC_1; // Stop timer } void i2c_tx_start(void) { USISRL = 0; USICTL0 |= USIGE | USIOE; // Latch enable, SDA enable USICTL0 &= ~USIGE; // Latch disable } void i2c_tx_stop(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0; USICNT = 1; // Transmit 1 bit _low_power_mode_0(); // Sleep in LPM0 until interrupt USISRL = 0xFF; USICTL0 |= USIGE; // Latch enable USICTL0 &= ~(USIGE | USIOE); // Latch disable, SDA disable } unsigned char i2c_tx_byte(unsigned char byte) { USICTL0 |= USIOE; USISRL = byte; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt USICTL0 &= ~USIOE; // SDA disable USICNT = 1; // Receive ACK bit _low_power_mode_0(); // Sleep in LPM0 until interrupt return USISRL & BIT0; // Return ACK = first bit of USISRL } unsigned char i2c_rx_byte(void) { unsigned char v; USICTL0 &= ~USIOE; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt v = USISRL; return v; } void i2c_tx_ack(unsigned char ack) { USICTL0 |= USIOE; USISRL = ack; // Send ACK/NACK bit USICNT = 1; _low_power_mode_0(); // Sleep in LPM0 until interrupt } void max6956_send(unsigned char addr, unsigned char data) { i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_byte(data); i2c_tx_stop(); } unsigned char max6956_recv(unsigned char addr) { unsigned char v; i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_stop(); i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR | BIT0); v = i2c_rx_byte(); // Read and send NACK to stop transmission i2c_tx_ack(NACK); i2c_tx_stop(); return v; } void sense(void) { static unsigned char g=0; if (!(max6956_recv(0x2F) & BIT0)) // Send No-op command and check bit 0 = button pushed max6956_send(0x19, g++ << 4); // Increase brightness of LED on port 19 } void leds(unsigned char v) { max6956_send(0x1A, v | (0x0F-v) << 4); // control brightness of LEDs on ports 20-21 sense(); } void main(void) { signed char i=0; WDTCTL = WDTPW + WDTHOLD; // Disable WDT i2c_init(); timer_init(); _enable_interrupts(); max6956_send(0x0C, 0x00); // set ports 16-19 to LED max6956_send(0x0D, 0x00); // set ports 20-23 to LED max6956_send(0x0B, 0xFF); // set ports 12-15 to GPIO Input with pullup max6956_send(0x33, 0x01); // Port 19 on max6956_send(0x34, 0x01); // Port 20 on max6956_send(0x35, 0x01); // Port 21 on max6956_send(0x07, 0x01); // Display test on _delay_cycles(1500000); // ~ 1.5s pause max6956_send(0x07, 0x00); // Display test off max6956_send(0x04, BIT0 | BIT6); // Individual segment current control, normal operation for (; { for (i=0; i<0x10; i++) { leds(i); sleep(DELAY); } for (i=0x0F; i>=0; i--) { leds(i); sleep(DELAY); } } } #pragma vector=USI_VECTOR __interrupt void usi_interrupt(void) { USICTL1 &= ~USIIFG; // Clear interrupt flag _low_power_mode_off_on_exit(); // Return from LPM } #pragma vector=TIMERA0_VECTOR __interrupt void timer_A_interrupt(void) { _low_power_mode_off_on_exit(); // Return from LPM }
  3. Like
    fj604 got a reaction from gordon in CIR - Infrared remote decoder for NEC protocol   
    A quick demo that can be converted to a library:
     

    /* Consumer IR receiver demo for the NEC protocol for MSP430G2231 (Launchpad) * Requires a 38kHz IR detector such as Vishay TSOP4838 on Pin 4 (P1.2/TA0.1) * Toggles a LED when a particular IR remote button is pushed * For more details on the NEC protocol, see http://www.sbprojects.com/knowledge/ir/nec.php */ #include // IR pulse timing constants in microseconds #define HP_MIN 8500 // Header pulse minimum duration #define HP_MAX 9500 // Header pulse maximum duration #define HPL_MIN 4100 // Header pulse low minimum duration #define HPL_MAX 4800 // Header pulse low maximum duration #define RPT_MIN 1900 // Repeat pulse minimum duration #define RPT_MAX 2500 // Repeat pulse minimum duration #define BP_MIN 450 // Bit pulse minimum duration #define BP_MAX 650 // Bit pulse maximum duration #define ONE_MIN 2000 // Logical one minumum duration #define ONE_MAX 2500 // Logical one maxumum duration #define ZERO_MIN 1000 // Logical zero minumum duration #define ZERO_MAX 1300 // Logical zero maxumum duration #define FALSE 0 #define TRUE (!FALSE) #define IR_REPEAT 0xFF00 // Special repeat code #define MAX_DATA_BITS 32 // NEC protocol provides for 32 bits of data #define PIN_IR BIT2 // This must be TA0.1 #define PIN_LED1 BIT0 // LED1 on the Launchpad #define BUTTON_POWER 0x00FD00FF // This is a code for the button to toggle the LED static volatile unsigned long data; void ir_init(void) { _disable_interrupts(); BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz to measure pulses in microseconds DCOCTL = CALDCO_1MHZ; P1DIR &= ~PIN_IR; // Set IR detector pin to input P1OUT |= PIN_IR; // Set resistor to pull-up P1REN |= PIN_IR; // Enable resistor P1SEL = PIN_IR; // set IR detector pin to peripheral function TACTL = TASSEL_2 | ID_0 | MC_2; // SMCLK, Continuous mode, Input divider /1 TACCTL1 = CM_3 | CCIS_0 | CAP | CCIE; // Capture on both edges, Capture Input Select CCI0A, Capture mode, Capture interrupt enable _enable_interrupts(); } void main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop the Watchdog timer P1DIR = PIN_LED1; // LED1 to output ir_init(); // Initialize IR detection for (; // Loop forever { _low_power_mode_0(); // Wait in LPM0 until the IR remote code is received // Set a breakpoint on the next line and add a watch for the data variable to see the IR code received if (data == BUTTON_POWER) // If remote power button is pressed P1OUT ^= PIN_LED1; // Toggle LED1 } } #pragma vector=TIMERA1_VECTOR __interrupt void timer_A_interrupt(void) { static unsigned char hp, hr; //header pulse received, header received static unsigned char br; // bits received static unsigned char pulse=FALSE; // pulse in progress static volatile unsigned char ready = FALSE; // data ready static unsigned int pw=0, bw=0; // pulse width, bit width unsigned int i = TAIV; // Read TAIV to clear CCIFG // Is this the beginning or the end of the pulse? if (!pulse) // start of the pulse { bw=TACCR1; // reset Timer TACTL |= TACLR; pulse=TRUE; // Has the header pulse just been received? if (hp) { // Is the header duration in range? if (bw>HPL_MIN && bw { hr=TRUE; br=0; data=0; ready=FALSE; } // is it a repeat code? else if (bw>RPT_MIN && bw { data=IR_REPEAT; hr=FALSE; ready=TRUE; } } // Was there a bit pulse before, and also has the header been received? if(hr && pw>BP_MIN && pw { if (bw>ONE_MIN && bw { data<<=1; data++; br++; } else if (bw>ZERO_MIN && bw { data<<=1; br++; } if (br==MAX_DATA_BITS) { hr=FALSE; ready=TRUE; } } hp=FALSE; } else // end of the pulse { pw=TACCR1; pulse=FALSE; // Is this a header pulse? if (pw>HP_MIN && pw { // set Header Pulse Received flag hp=TRUE; // Reset timer TACTL |= TACLR; } else { hp=FALSE; } } if (ready) { ready = FALSE; _low_power_mode_off_on_exit(); // Return from LPM } }
     
    (edited - added a pullup resistor setting)
  4. Like
    fj604 reacted to timotet in MSP430 USI i2c and Adafruit LCD backpack   
    Ive had this Adafruit Lcd backpack for a while and thought I would get it working with the launchpad.
    I'm using a sparkfun level converter, and I am powering the LCD via the TP1 port on the launchpad.
    For the i2c code I used fj604's from here: viewtopic.php?f=10&t=1037
    The code sends characters 2 different ways, one char at a time , and using oPossums tiny printf
    from here: viewtopic.php?f=10&t=1732
    Maybe someone can use this. The code is attached, its a CCSv5 project.
     
     
    here is my setup

     
    here is the backpack. (I added the led for debugging)

     
    small video(really impressive!)


    LCDi2cdemo.zip
  5. Like
    fj604 got a reaction from pine in CIR - Infrared remote decoder for NEC protocol   
    A quick demo that can be converted to a library:
     

    /* Consumer IR receiver demo for the NEC protocol for MSP430G2231 (Launchpad) * Requires a 38kHz IR detector such as Vishay TSOP4838 on Pin 4 (P1.2/TA0.1) * Toggles a LED when a particular IR remote button is pushed * For more details on the NEC protocol, see http://www.sbprojects.com/knowledge/ir/nec.php */ #include // IR pulse timing constants in microseconds #define HP_MIN 8500 // Header pulse minimum duration #define HP_MAX 9500 // Header pulse maximum duration #define HPL_MIN 4100 // Header pulse low minimum duration #define HPL_MAX 4800 // Header pulse low maximum duration #define RPT_MIN 1900 // Repeat pulse minimum duration #define RPT_MAX 2500 // Repeat pulse minimum duration #define BP_MIN 450 // Bit pulse minimum duration #define BP_MAX 650 // Bit pulse maximum duration #define ONE_MIN 2000 // Logical one minumum duration #define ONE_MAX 2500 // Logical one maxumum duration #define ZERO_MIN 1000 // Logical zero minumum duration #define ZERO_MAX 1300 // Logical zero maxumum duration #define FALSE 0 #define TRUE (!FALSE) #define IR_REPEAT 0xFF00 // Special repeat code #define MAX_DATA_BITS 32 // NEC protocol provides for 32 bits of data #define PIN_IR BIT2 // This must be TA0.1 #define PIN_LED1 BIT0 // LED1 on the Launchpad #define BUTTON_POWER 0x00FD00FF // This is a code for the button to toggle the LED static volatile unsigned long data; void ir_init(void) { _disable_interrupts(); BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz to measure pulses in microseconds DCOCTL = CALDCO_1MHZ; P1DIR &= ~PIN_IR; // Set IR detector pin to input P1OUT |= PIN_IR; // Set resistor to pull-up P1REN |= PIN_IR; // Enable resistor P1SEL = PIN_IR; // set IR detector pin to peripheral function TACTL = TASSEL_2 | ID_0 | MC_2; // SMCLK, Continuous mode, Input divider /1 TACCTL1 = CM_3 | CCIS_0 | CAP | CCIE; // Capture on both edges, Capture Input Select CCI0A, Capture mode, Capture interrupt enable _enable_interrupts(); } void main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop the Watchdog timer P1DIR = PIN_LED1; // LED1 to output ir_init(); // Initialize IR detection for (; // Loop forever { _low_power_mode_0(); // Wait in LPM0 until the IR remote code is received // Set a breakpoint on the next line and add a watch for the data variable to see the IR code received if (data == BUTTON_POWER) // If remote power button is pressed P1OUT ^= PIN_LED1; // Toggle LED1 } } #pragma vector=TIMERA1_VECTOR __interrupt void timer_A_interrupt(void) { static unsigned char hp, hr; //header pulse received, header received static unsigned char br; // bits received static unsigned char pulse=FALSE; // pulse in progress static volatile unsigned char ready = FALSE; // data ready static unsigned int pw=0, bw=0; // pulse width, bit width unsigned int i = TAIV; // Read TAIV to clear CCIFG // Is this the beginning or the end of the pulse? if (!pulse) // start of the pulse { bw=TACCR1; // reset Timer TACTL |= TACLR; pulse=TRUE; // Has the header pulse just been received? if (hp) { // Is the header duration in range? if (bw>HPL_MIN && bw { hr=TRUE; br=0; data=0; ready=FALSE; } // is it a repeat code? else if (bw>RPT_MIN && bw { data=IR_REPEAT; hr=FALSE; ready=TRUE; } } // Was there a bit pulse before, and also has the header been received? if(hr && pw>BP_MIN && pw { if (bw>ONE_MIN && bw { data<<=1; data++; br++; } else if (bw>ZERO_MIN && bw { data<<=1; br++; } if (br==MAX_DATA_BITS) { hr=FALSE; ready=TRUE; } } hp=FALSE; } else // end of the pulse { pw=TACCR1; pulse=FALSE; // Is this a header pulse? if (pw>HP_MIN && pw { // set Header Pulse Received flag hp=TRUE; // Reset timer TACTL |= TACLR; } else { hp=FALSE; } } if (ready) { ready = FALSE; _low_power_mode_off_on_exit(); // Return from LPM } }
     
    (edited - added a pullup resistor setting)
  6. Like
    fj604 got a reaction from Kman in Launchpad + AS1106 + 8x8 LED Matrix = fun!   
    The 2211 chip that came with the Launchpad is not useless! Here is what can be done with it:
     


     
    I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies.
     
    Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red).
     
    A piezo buzzer can be added for (very annoying) sound effects beeps.
     
    I thought the whole thing has a certain 1970's touch to it
     

    #include #include #define DATA_PIN BIT4 // data for AS1106 #define LOAD_PIN BIT5 // load for AS1106 #define CLOCK_PIN BIT7 // clock for AS1106 #define BL_PIN BIT1 // Left button #define BR_PIN BIT2 // Right button #define SPEED_PIN BIT3 // Speed increase button #define GREEN_PIN BIT6 // Green LED on Launchpad (LED2) #define RED_PIN BIT0 // Red LED on Launchpad (LED1) #define BUZZER_PIN BIT7 // Piezo BUZZER = XOUT = P2.7 #define MAX_DELAY 2500 // Slowest speed #define MIN_DELAY 1000 // Fastest speed #define STEP_DELAY 300 // Speed step #define TONE_CATCH 5 // Tone on catch #define TONE_MISS 10 // Tone on miss #define TONE_CHIRP 2 // Chirp tone #define CHIRP_DUR 10 // Chirp duration #define TONE_DUR 300 // Catch / miss tone duration unsigned char bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; volatile unsigned char t = 3, // catcher x position refresh = 0, // flag to refresh matrix refreshing = 0; // set during refresh volatile unsigned int clk = 0, // clock ticks, 1 tick = 1/10000 sec delay = MAX_DELAY, // step delay tone = 0, // tone frequency, 1 = 5000 Hz, 5 = 1000 Hz sound = 0; // sound duration in ticks void pixel_set(unsigned char x, unsigned char y) { bitmap[x] |= 1 << y; } void pixel_clear(unsigned char x, unsigned char y) { bitmap[x] &= ~ (1 << y); } // Shift out data, MSB first void shiftout(unsigned char data) { unsigned char n; for (n = 0; n < 8; n++) { if (data & BIT7) P1OUT |= DATA_PIN; else P1OUT &= ~DATA_PIN; P1OUT |= CLOCK_PIN; P1OUT &= ~CLOCK_PIN; data <<= 1; } } // Send command to AS1106 void as1106(unsigned char addr, unsigned char data) { P1OUT &= ~LOAD_PIN; shiftout(addr); shiftout(data); P1OUT |= LOAD_PIN; } // show bitmap on the matrix void display(void) { refreshing = 1; // Set flag so that we do not change bitmap during refresh unsigned char c; for (c = 0; c < 8; c++) as1106(c + 1, bitmap[c]); refreshing = 0; // Clear flag } void as1106_init(void) { P1DIR |= (DATA_PIN | LOAD_PIN | CLOCK_PIN); P1OUT |= LOAD_PIN; P1OUT &= ~CLOCK_PIN; as1106(0x0C, 0x00); // shutdown display(); as1106(0x0F, 0x01); // Test on _delay_cycles(400000); as1106(0x0F, 0x00); // Test off _delay_cycles(200000); as1106(0x0E, BIT1); // Reset all control registers _delay_cycles(100); as1106(0x0E, 0); // Normal operation as1106(0x09, 0x00); // No decode as1106(0x0A, 0x0F); // full brightness as1106(0x0B, 0x0F); // display all rows as1106(0x0C, 0x81); // No shutdown } void clock_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; } void timer_init(void) { TACCR0 = 100; // Generate interrupt 10000 times per second TACTL = TASSEL_2 + MC_1; // SMCLK, upmode TACCTL0 = CCIE; // Capture/Compare Interrupt Enable } void sound_init(void) { P2SEL = 0; // Disable XIN/XOUT P2DIR = BUZZER_PIN; // XOUT pin = P2.7 } void intro_sound(void) { for (tone = 10; tone > 0; tone--) { sound = 500; while(sound) ; } } void button_init(void) { P1DIR &= ~(BL_PIN | BR_PIN | SPEED_PIN); P1OUT |= BL_PIN | BR_PIN ; // Pull up P1REN |= BL_PIN | BR_PIN ; // Enable pull up resistors P1IE |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt enable P1IES |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt Edge Select high-to-low P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // Clear interrupt flag } void main(void) { unsigned char n, p; // n = dot y, p = catcher x, WDTCTL = WDTPW + WDTHOLD; // Disable watchdog timer (WDT) as1106_init(); button_init(); timer_init(); sound_init(); P1DIR |= RED_PIN | GREEN_PIN; P1OUT &= ~(RED_PIN | GREEN_PIN); pixel_set(t, 7); pixel_set(t+1, 7); _enable_interrupts(); intro_sound(); for (; { p = rand() % 8; // random drop x position for (n = 0; n < 7; n++) // drop { pixel_set(p, n); display(); refresh = 0; clk = 0; tone = TONE_CHIRP; sound = CHIRP_DUR; while (clk < delay) { _low_power_mode_0(); if (refresh) { display(); refresh = 0; } } pixel_clear(p, n); } if ((p == t) || (p == t + 1)) // if caught { P1OUT |= GREEN_PIN; P1OUT &= ~RED_PIN; tone = TONE_CATCH; } else { P1OUT &= ~GREEN_PIN; P1OUT |= RED_PIN; tone = TONE_MISS; } sound = TONE_DUR; } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { if (P1IFG & SPEED_PIN) // speed switch { if ((delay-= STEP_DELAY) < MIN_DELAY) delay = MAX_DELAY; } else if ((P1IFG & BL_PIN) && (t > 0) && !refreshing) // left { pixel_clear(t + 1, 7); pixel_set(--t, 7); refresh = 1; } else if ((P1IFG & BR_PIN) && (t < 6) && !refreshing) // right { pixel_clear(t, 7); pixel_set(++t + 1, 7); refresh = 1; } P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // clear interrupt flags for all buttons _low_power_mode_off_on_exit(); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { static unsigned int z; clk++; if (sound) { --sound; if ((z++ % tone) == 0) P2OUT ^= BUZZER_PIN; // vibrate buzzer } _low_power_mode_off_on_exit(); }
  7. Like
    fj604 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
  8. Like
    fj604 reacted to rockets4kids in Software debouncing   
    Jack Ganssle has a *great* article on debouncing, both hardware and software:
     
    http://www.eng.utah.edu/~cs5780/debouncing.pdf
     
    Definitely worth reading even if you already have a working debounce routine.
  9. Like
    fj604 reacted to oPossum in Software debouncing   
    Use a timer interrupt for debounce.
     

    volatile unsigned debounce = 0; volatile unsigned switch_on = 0; #pragma vector = TIMER0_A0_VECTOR __interrupt void Timer0_A0 (void) { // Debounce and check if switch state has changed if(switch_on != (0 != ((P1IN & BIT3) ? (debounce ? --debounce : 0) : (debounce = 25)))) { // Update switch state and test direction of transition if(switch_on = !switch_on) { // Switch has closed } else { // Switch has opened } } }
  10. Like
    fj604 reacted to oPossum in 16 channel software PWM using a single timer   
    This is a method of generating multichannel PWM using a single timer.

    A linked list of events is used to allow a very efficient ISR and very low CPU usage.

    This is the linked list entry structure...
    typedef struct { // PWM ISR info struct unsigned time; // Time for on/off action to occur unsigned port_off; // Port pins to turn off unsigned port_on; // Port pins to turn on void *next; // Next entry in linked list } TPWM; // ...and the ISR...
    (written in C for clarity, using assembly would reduce the size and execution time by half)
    __interrupt void Timer0_A1 (void) { volatile unsigned x = TAIV; // Clear interrupt flag P1OUT &= ~pa->port_off; // Port pins off P2OUT &= ~pa->port_off >> 8; // P1OUT |= pa->port_on; // Port pins on P2OUT |= pa->port_on >> 8; // pa = pa->next; // Next entry in list TACCR1 = pa->time; // Update timer compare time } When an interrupt occurs, the ports are updated, the timer is updated, and the next linked list entry is made the active entry. An interrupt will only occur when port pin(s) have to be changed. Very simple and efficient.

    The tradeoff is a rather complex chunk of code to maintain the linked list. The code shown here uses a simple, but rather crude, method of reducing glitches that can occur when the linked list is manipulated. The code will wait for the current pulse to complete before removing the list entry for it. There are better ways to do this, but the code is much more complex due to the many corner cases that must be handled. There is no disruption in the PWM output when PWM values are changed. All PWM outputs are active as long as the timer and global interrupt are enabled.

    The PWM period is 2^16 / SMCLK and the resolution is 2^8 / SMCLK (8 bit resolution).
    So for a 16 MHz clock:
    period = 65,536 / 16,000,000 = 4.096 ms
    resolution = 256 / 16,000,000 = 16 us
    frequency = 16,000,000 / 65,536 = 244.24 Hz

    An interrupt will occur no more than 17 times the base frequency when all 16 channels are active and set to different non-zero/non-max values. So at 16 MHz there will be no more than 4151 interrupts per second. Assuming 100 cycles per interrupt (the C compiler should be able to do better than that) would be 415,100 cycles per second of 16,000,000 or 2.6% CPU usage (worst case).

    The resolution can be increased as long as there are not other interrupts that use too many cycles. Consider the worst case scenario of two back-to-back PWM interrupts with another interrupt in between. The maximum time allowed for other interrupts is the PWM resolution minus the PWM ISR time - it has to squeeze in between back-to-back PWM interrupts.

    Complete code with test case...
    #include "msp430g2553.h" #include "string.h" typedef struct { // PWM ISR info struct unsigned time; // Time for on/off action to occur unsigned port_off; // Port pins to turn off unsigned port_on; // Port pins to turn on void *next; // Next entry in linked list } TPWM; // TPWM pw[17], *pi; // Array and inactive list head volatile TPWM *pa; // Active list entry void pwm_set(const unsigned pin, const unsigned time) { const unsigned mask = 1 << pin; // const unsigned tm = time & 0xFF00; // Limit closeness of entries TPWM *b, *p, *n; // // // -- Try to find existing active entry for this pin for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) { if(p->port_off & mask) { // Found it if(p->time == tm) return; // - Time is the same, nothing to do, return... while(pa != p); // Wait for this entry to be active while(pa == p); // Wait for this entry to become inactive // Safe to remove now if(p->port_off == mask) { // - Entry is only used for this pin, remove it b->next = p->next; // Remove link p->next = pi; // Add to inactive list pi = p; // } else { // - Entry is used for multiple pins p->port_off &= ~mask; // Remove this pin from the entry } // break; // Found the pin, so stop searching } // } // // - Update first entry in list if(tm == 0) { // If time is 0, turn off and return pw[0].port_on &= ~mask; // pw[0].port_off |= mask; // return; // } else { // If time is non-zero, turn on pw[0].port_on |= mask; // pw[0].port_off &= ~mask; // if(time == 0xFFFF) return; // If max, no need to turn off, so return } // // // Find where the new turn off entry will go for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) if(p->time >= tm) break; // Stop when an entry of >= time is found // if(p->time == tm) { // If same time, use existing entry p->port_off |= mask; // Add this pin return; // All done... } // // n = pi; // Get an entry from the inactive list pi = n->next; // // n->port_off = mask; // Setup new entry n->port_on = 0; // n->time = tm; // // n->next = p; // Insert in to active list b->next = n; // } void pwm_detach(unsigned pin) { const unsigned mask = 1 << pin; TPWM *b, *p; // Try to find existing active entry for this pin for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) { if(p->port_off & mask) { // Found it if(p->port_off == mask) { // Entry is only used for this pin, remove it _DINT(); if(pa == p) pa = p->next; b->next = p->next; _EINT(); p->next = pi; pi = p; } else { p->port_off &= ~mask; // Remove this pin from the entry } break; } } pw[0].port_on &= ~mask; // Don't turn on or off pw[0].port_off &= ~mask; } void pwm_init(void) { unsigned n; memset(pw, 0, sizeof(pw)); // Clear entire array pa = &pw[0]; // Active list always begins with first array element pa->next = &pw[0]; // Begin with only 1 entry in list pi = &pw[1]; // First inactive entry is second array element for(n = 1; n < sizeof(pw)/sizeof(TPWM) - 1; ++n) // Link the inactive entries pw[n].next = &pw[n + 1]; // // TACTL = TASSEL_2 + MC_2 + ID_0; // Setup timer, continuous mode, SMCLK/1 TACCTL1 = CCIE; // Enable timer interrupt _EINT(); // Enable interrupts } #pragma vector = TIMER0_A1_VECTOR __interrupt void Timer0_A1 (void) { volatile unsigned x = TAIV; // Clear interrupt flag P1OUT &= ~pa->port_off; // Port pins off P2OUT &= ~pa->port_off >> 8; // P1OUT |= pa->port_on; // Port pins on P2OUT |= pa->port_on >> 8; // pa = pa->next; // Next entry in list TACCR1 = pa->time; // Update timer compare time } // Test case for 2 channels - Launchpad LEDs void main(void) { unsigned n, o; WDTCTL = WDTPW | WDTHOLD; // Disable watchdog DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // P1DIR = BIT0 | BIT6; // Enable outputs for LEDs on Launchpad pwm_init(); // Initialize software PWM for(;;-) { pwm_set(0, n); pwm_set(6, o); n += 100; o += 123; __delay_cycles(100000); } } // Test case for all 16 channels void main(void) { unsigned n; unsigned w[16]; unsigned r[16] = { 100, 103, 106, 109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142, 145 }; WDTCTL = WDTPW | WDTHOLD; // Disable watchdog DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // P2SEL = 0; // Allow P2.6 and P2.7 to be used as GPIO P1DIR = P2DIR = 0xFF; // Enable all P1 and P2 pins as output pwm_init(); // Initialize software PWM for(;;-) { for(n = 0; n < 16; ++n) { pwm_set(n, w[n]); w[n] += r[n]; } __delay_cycles(100000); } } Two PWM outputs with the pulse with changing at different rates (lower trace is faster). The two outputs are the Launchpad LEDs.




    All 16 channels.

  11. Like
    fj604 reacted to SirZusa in TLC5940 - "Full Implementation"   
    hi there, as i already said in another thread i was working on the code for controlling the TLC5940. here it is!
     
    100% working. specification-sheet i used to code this: http://www.ti.com/lit/sw/slvc106/slvc106.pdf
     


    [*:1o0bnvvd]supports DC
    [*:1o0bnvvd]supports Grayscale
    [*:1o0bnvvd]recommendet implementation from the Programming Flow Chart
    [*:1o0bnvvd]does not support EEPROM-programming (cause you will need 22 Volts for this)
    [*:1o0bnvvd]actually does not support "Use DC from EEPROM-mode"
    [*:1o0bnvvd]LOD-Check not implemented for now ... but i think i will do this soon if i find more literature on this
     

    /*** TLC5940 ***/ #include "msp430g2553.h" #include "stdbool.h" // How many TLC's daisy-chained #define n 2 /*** Ports ***/ // STATUS-LED #define PORT_STATUS_LED P1OUT #define PIN_STATUS_LED BIT0 // BLANK - to pin 23 of every TLC5940 #define PORT_BLANK P1OUT #define PIN_BLANK BIT3 // XLAT - to pin 24 of every TLC5940 #define PORT_XLAT P1OUT #define PIN_XLAT BIT4 // SCLK - to pin 25 of every TLC5940 #define PORT_SCLK P1OUT #define PIN_SCLK BIT5 // SIN - to pin 26 of first TLC5940 #define PORT_SIN P1OUT #define PIN_SIN BIT6 // GSCLK - to pin 18 of every TLC5940 #define PORT_GSCLK P1OUT #define PIN_GSCLK BIT7 // DCPRG - to pin 19 of every TLC5940 #define PORT_DCPRG P2OUT #define PIN_DCPRG BIT3 // VPRG - to pin 27 of every TLC5940 #define PORT_VPRG P2OUT #define PIN_VPRG BIT4 /*** DO NOT EDIT BELOW ***/ #define x (n * 16) /*** Brightness-Correction ***/ const int pwm_table[256] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,28,30,32,34,36,38,40,42, 44,46,48,50,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100,103,106,110,114,118,122,126,130, 134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,195,200,205,210,215,220,225,230,235, 240,245,250,255,260,265,270,275,280,285,290,297,304,311,318,325,332,339,346,353,360,367,374,381, 388,395,402,409,416,423,430,437,444,451,458,467,476,485,494,503,512,521,530,539,548,557,566,575, 584,593,602,611,626,641,656,671,686,701,716,731,746,761,776,791,806,821,836,851,866,881,896,916, 936,956,976,996,1016,1036,1056,1076,1096,1116,1136,1156,1176,1196,1221,1246,1271,1296,1321,1346, 1371,1396,1421,1446,1471,1496,1526,1556,1586,1616,1646,1676,1706,1736,1766,1796,1826,1856,1886, 1921,1956,1991,2026,2061,2096,2131,2166,2201,2236,2271,2306,2341,2376,2411,2446,2481,2516,2551, 2586,2621,2656,2691,2726,2761,2796,2832,2868,2904,2940,2976,3012,3048,3084,3120,3156,3192,3228, 3266,3304,3342,3380,3418,3456,3494,3532,3572,3612,3652,3692,3737,3782,3827,3872,3917,3962,4007,4095 }; // holds the actual DC-data for each channel char dc_data[x] = {63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63}; /*** Variables ***/ // first PWM-cycle after Dot Correction Data has changed? bool FirstCycleFlag; // holds the actual PWM-data for each channel int gs_data[x]; /*** small userdefined Functions ***/ // useful macros #define setLow(port, pin) { port &= ~pin; } #define setHigh(port, pin) { port |= pin; } #define pulse(port, pin) { setHigh(port, pin); setLow(port, pin); } #define toggle(port, pin) { port ^= pin; } /*** transfer DC-data to our TLC5940's ***/ void send_DC() { // DCPRG - dont use DC-EEPROM setHigh(PORT_DCPRG, PIN_DCPRG); // VPRG - set Dot Correction Input setHigh(PORT_VPRG, PIN_VPRG); // Reset Counter int Counter = 0; // clock in Dot Correction Data for (; { // Counter > n * 96 - 1 // 6 Bits * 16 Channels = 96 if (Counter > (n * 96 - 1)) { pulse(PORT_XLAT, PIN_XLAT); break; } else { // Set SIN to DC Data[Counter] // MSB first if ((dc_data[Counter / 6] >> (5 - (Counter % 6))) & 0x01) { setHigh(PORT_SIN, PIN_SIN); } else { setLow(PORT_SIN, PIN_SIN); } pulse(PORT_SCLK, PIN_SCLK); Counter++; } } // dont save to EEPROM - so we finish here // set FirstCycleFlag to true FirstCycleFlag = true; } /*** transfer PWM-data to our TLC5940's ***/ void send_GS() { if (FirstCycleFlag == true) { setLow(PORT_VPRG, PIN_VPRG); } // Reset Counters int GSCLK_Counter = 0; int Data_Counter = 0; setLow(PORT_BLANK, PIN_BLANK); // clock in PWM Data for (; { if (GSCLK_Counter > 4095) { setHigh(PORT_BLANK, PIN_BLANK); pulse(PORT_XLAT, PIN_XLAT); if (FirstCycleFlag == true) { pulse(PORT_SCLK, PIN_SCLK); } FirstCycleFlag = false; break; } else { // 12 Bit * 16 Channels = 192 if (Data_Counter > (n * 192 - 1)) { pulse(PORT_GSCLK, PIN_GSCLK); GSCLK_Counter++; } else { // Set SIN to GS Data[Counter] // MSB first if ((gs_data[Data_Counter / 12] >> (11 - (Data_Counter % 12))) & 0x01) { setHigh(PORT_SIN, PIN_SIN); } else { setLow(PORT_SIN, PIN_SIN); } pulse(PORT_SCLK, PIN_SCLK); Data_Counter++; pulse(PORT_GSCLK, PIN_GSCLK); GSCLK_Counter++; } } } } /*** Our main program ***/ void main(void) { // stop WDT - WDTPW is the password needed for every write and then tell watchdog to hold WDTCTL = WDTPW | WDTHOLD; BCSCTL1 = CALBC1_16MHZ; DCOCTL = CALDCO_16MHZ; // initialize the ports connected to the TLC's // set ports to output-direction P1DIR |= (PIN_STATUS_LED|PIN_BLANK|PIN_XLAT|PIN_SCLK|PIN_SIN|PIN_GSCLK); P1OUT = 0x00; P2DIR |= (PIN_DCPRG|PIN_VPRG); P2OUT = 0x00; // initialize TLC5940 setLow(PORT_GSCLK, PIN_GSCLK); setLow(PORT_SCLK, PIN_SCLK); setLow(PORT_DCPRG, PIN_DCPRG); setHigh(PORT_VPRG, PIN_VPRG); setLow(PORT_XLAT, PIN_XLAT); setHigh(PORT_BLANK, PIN_BLANK); // force first cycle send_DC(); send_GS(); // enable interrupts __bis_SR_register(GIE); while(1) { // load gs-data here // update PWM-data permanently send_GS(); } }
  12. Like
    fj604 got a reaction from bluehash in Talking clock   
    Would anyone be interested in a talking clock made with MSP430G2231? Also using AS1108 4-digit 7-segment LED driver and a ChipcCorder ISD1700 series for voice.
     
    What a mess
     
    Clock speaks in Russian



  13. Like
    fj604 got a reaction from dacoffey in Talking clock   
    Would anyone be interested in a talking clock made with MSP430G2231? Also using AS1108 4-digit 7-segment LED driver and a ChipcCorder ISD1700 series for voice.
     
    What a mess
     
    Clock speaks in Russian



  14. Like
    fj604 reacted to oPossum in Using the internal temperature sensor   
    TI has some sample code for the internal temperature sensor, but it does not explain how to scale the ADC reading to useful units of degrees. Here is a step-by-step explanation of how to do the scaling with integer math for degrees C, K and F. There is also sample code to display the temperature on a Nokia 5110 LCD.

     
    The data sheet (SLAU144) has this formula for converting temperature in degrees Celsius to voltage.
    V = 0.00355 * C + 0.986

    What we need is a formula for converting voltage to temperature.
     
    Rewrite the data sheet fomula with temperature on the left
    0.00355 * C + 0.986 = V
     
    Divide both sides by 0.00355
    C + 277.75 = V / 0.00355
     
    Subtract 277.75 from both sides
    C = V / 0.00355 - 277.75
     
    Now we have a formula for converting voltage to temperature.
     
    The data sheet has this formula for converting voltage to ADC value, once again the opposite of what we neeed.

    For Vref- == 0
    A = 1023 * V / Vref
     
    Swap sides
    1023 * V / Vref = A
     
    Multiply by Vref
    1023 * V = A * Vref
     
    Divide by 1023
    V = A * Vref / 1023
     
    For a 1.5V reference
    V = A * 1.5 / 1023
     
    Simplify
    V = A * 0.0014663
     
    Substitute ADC conversion forumula for voltage in the temperature conversion formula.
    C = A * 0.0014663 / 0.00355 - 277.75
     
    Simplify
    C = A * 0.413 - 277.75
     
    Now we have a formula to convert ADC reading to temperature.
    It uses real numbers, so floating point math is required for good precision.
    Floating point is slow and requires more flash, so let's use integer math instead.
    Multiply by 65536 (2^16) and then divide by the same.
    C = (A * 27069 - 18202393) / 65536
     
    Use a right shift instead of divide. This will become a move of high word to low word.
    C = (A * 27069 - 18202393) >> 16
     
    Add 0.5 * 65536 to impove rounding.
    C = (A * 27069 - 18202393 + 32768) >> 16
     
    Simplify.
    C = (A * 27069 - 18169625) >> 16
     
    So that is how to go from ADC to degrees Celsius.
     
     
    To convert degrees C to degees K.
    K = C + 273.15
     
    Applied to ADC to degrees C conversion formula.
    K = (A * 27069 - 18169625) >> 16 - 273.15
     
    Implement with integer math by multiplying by 65536
    K = (A * 27069 - 18169625 - 17,901,158) >> 16
     
    Simplify.
    K = (A * 27069 - 268467) >> 16
     
    To convert degrees C to degrees F.
    F = C * 9 / 5 + 32
     
    Applied to voltage to degrees C conversion forumula
    F = (V / 0.00355 - 277.75) * 9 / 5 + 32
     
    Multiply by 9
    F = (V / 0.0003944 - 2499.75) / 5 + 32
     
    Divide by 5
    F = (V / 0.0019722 - 499.95) + 32
     
    Add 32
    F = V / 0.0019722 - 467.95
     
    Substitute ADC to voltage forumula
    F = A * 0.0014663 / 0.0019722 - 467.95
     
    Simplifiy
    F = A * 0.7435 - 467.95
     
    Convert to integer
    F = (A * 48724 - 30667156) >> 16
     
    Improve rounding
    F = (A * 48724 - 30667156 + 32768) >> 16
     
    Simplify
    F = (A * 48724 - 30634388) >> 16
     
    So now we have three formulas to convert ADC reading to degrees C, K and F using fast and compact integer math.
    C = (A * 27069 - 18169625) >> 16
    K = (A * 27069 - 268467) >> 16
    F = (A * 48724 - 30634388) >> 16
     
    Using the ADC value, rather than a different temperature scale, will ensure greatest precision for each temperature scale.
     
    main.c

    #include #include #include "lcd.h" #define ADC_SLEEP // Sleep during ADC conversion //#define SHOW_ADC // Show ADC raw and ADC millivolts // Print integer from -999 to 9999 using 12 x 16 font void print_int(int i, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; unsigned x = 48; do { d = div(d.quot, 10); pd12(d.rem, x -= 12, y); } while(d.quot); if(neg) pd12(14, x -= 12, y); while(x) pd12(10, x -= 12, y); } // Print integer from 0 to 9999 vertically using 6 x 8 font void print_v(int i, unsigned x) { unsigned y = 4; unsigned c; if(i < 0 || i > 9999) return; div_t d; d.quot = i; do { d = div(d.quot, 10); c = d.rem + '0'; lcd_print((char *)&c, x, --y); } while(d.quot); c = ' '; while(y) lcd_print((char *)&c, x, --y); } void main(void) { unsigned adc; // ADC value int c, k, f; // Temperature in degrees C, K, and F unsigned mv; // ADC reading in millivolts // WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset // lcd_init(); // Initialize LCD lcd_clear(0); // pd12(15, 48, 0); // Degrees pd12(17, 59, 0); // F pd12(15, 48, 2); // Degrees pd12(16, 58, 2); // C pd12(15, 48, 4); // Degrees pd12(18, 59, 4); // K #ifdef SHOW_ADC // lcd_print("Am", 72, 4); // AD / mV lcd_print("DV", 72, 5); // #endif // // ADC10CTL0 = 0; // Configure ADC ADC10CTL1 = INCH_10 | ADC10DIV_3; // ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE; //ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE | REF2_5V; #ifdef ADC_SLEEP // ADC10CTL0 |= ADC10IE; // Enable ADC conversion complete interrupt #endif // // for(; { // for-ever #ifdef ADC_SLEEP // ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion __bis_SR_register(LPM0_bits + GIE); // Sleep until conversion complete #else // ADC10CTL0 &= ~ADC10IFG; // Clear conversion complete flag ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete #endif // // adc = ADC10MEM; // Read ADC // // Convert to temperature c = ((27069L * adc) - 18169625L) >> 16; // Vref = 1.5V //c = ((45115L * adc) - 18169625L) >> 16; // Vref = 2.5V // k = ((27069L * adc) - 268467L) >> 16; // Vref = 1.5V //k = ((45115L * adc) - 268467L) >> 16; // Vref = 2.5V // f = ((48724L * adc) - 30634388L) >> 16; // Vref = 1.5V //f = ((81206L * adc) - 30634388L) >> 16; // Vref = 2.5V // // Convert to millivolts mv = (96094L * adc + 32768) >> 16; // Vref = 1.5V //mv = (160156L * adc + 32768) >> 16; // Vref = 2.5V // // Display on LCD print_int(f, 0); // Degrees F print_int(c, 2); // Degrees C print_int(k, 4); // Degrees K // #ifdef SHOW_ADC // print_v(adc, 72); // ADC print_v(mv, 78); // ADC millivolts #endif // // //__delay_cycles(100000); // } // } #pragma vector = ADC10_VECTOR // ADC conversion complete interrupt __interrupt void ADC10_ISR(void) // { // __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } //
     
    lcd.h

    typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type); void lcd_home(void); void lcd_pos(unsigned char x, unsigned char y); void lcd_clear(unsigned char x); void lcd_init(void); void lcd_print(char *s, unsigned x, unsigned y); void pd12(unsigned n, unsigned x, unsigned y);
     
    lcd.c

    #include #include "lcd.h" //static const unsigned TXD = BIT1; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; static const unsigned LCD_CLK = BIT5; static const unsigned LCD_BACKLIGHT = BIT6; static const unsigned LCD_DATA = BIT7; static const unsigned LCD_DC = BIT0; // PORT2 static const unsigned LCD_CE = BIT1; // PORT2 void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; P2OUT &= ~LCD_CE; do { mask = 0x0080; do { if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; mask >>= 1; } while(!(mask & 1)); if(!type) P2OUT &= ~LCD_DC; if(*cmd & mask) { P1OUT &= ~LCD_CLK; P1OUT |= LCD_DATA; } else { P1OUT &= ~(LCD_CLK | LCD_DATA); } P1OUT |= LCD_CLK; P2OUT |= LCD_DC; if(!(type & 2)) ++cmd; } while(--len); P2OUT |= LCD_CE; } static const unsigned char home[] = { 0x40, 0x80 }; void lcd_home(void) { lcd_send(home, sizeof(home), lcd_command); } void lcd_pos(unsigned char x, unsigned char y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); } void lcd_clear(unsigned char x) { lcd_home(); lcd_send(&x, 504, lcd_data_repeat); lcd_home(); } void lcd_init(void) { static const unsigned char init[] = { 0x20 + 0x01, // function set - extended instructions enabled //0x80 + 64, // set vop (contrast) 0 - 127 0x80 + 66, // set vop (contrast) 0 - 127 0x04 + 0x02, // temperature control 0x10 + 0x03, // set bias system 0x20 + 0x00, // function set - chip active, horizontal addressing, basic instructions 0x08 + 0x04 // display control - normal mode }; P1REN = RXD | SWITCH; P1DIR = LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P1OUT = LCD_CLK | RXD | SWITCH | LCD_BACKLIGHT; P2REN = 0; P2DIR = LCD_DC | LCD_CE; P2OUT = LCD_CE; __delay_cycles(20000); P2OUT |= LCD_DC; __delay_cycles(20000); lcd_send(init, sizeof(init), lcd_command); } static const unsigned char font6x8[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 20 32 0x00, 0x00, 0x5F, 0x00, 0x00, // 21 33 ! 0x00, 0x07, 0x00, 0x07, 0x00, // 22 34 " 0x14, 0x7F, 0x14, 0x7F, 0x14, // 23 35 # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24 36 $ 0x23, 0x13, 0x08, 0x64, 0x62, // 25 37 % 0x36, 0x49, 0x56, 0x20, 0x50, // 26 38 & 0x00, 0x08, 0x07, 0x03, 0x00, // 27 39 ' 0x00, 0x1C, 0x22, 0x41, 0x00, // 28 40 ( 0x00, 0x41, 0x22, 0x1C, 0x00, // 29 41 ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A 42 * 0x08, 0x08, 0x3E, 0x08, 0x08, // 2B 43 + 0x00, 0x40, 0x38, 0x18, 0x00, // 2C 44 , 0x08, 0x08, 0x08, 0x08, 0x08, // 2D 45 - 0x00, 0x00, 0x60, 0x60, 0x00, // 2E 46 . 0x20, 0x10, 0x08, 0x04, 0x02, // 2F 47 / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 30 48 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 31 49 1 0x42, 0x61, 0x51, 0x49, 0x46, // 32 50 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 33 51 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 34 52 4 0x27, 0x45, 0x45, 0x45, 0x39, // 35 53 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 36 54 6 0x41, 0x21, 0x11, 0x09, 0x07, // 37 55 7 0x36, 0x49, 0x49, 0x49, 0x36, // 38 56 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 39 57 9 0x00, 0x00, 0x14, 0x00, 0x00, // 3A 58 : 0x00, 0x00, 0x40, 0x34, 0x00, // 3B 59 ; 0x00, 0x08, 0x14, 0x22, 0x41, // 3C 60 < 0x14, 0x14, 0x14, 0x14, 0x14, // 3D 61 = 0x00, 0x41, 0x22, 0x14, 0x08, // 3E 62 > 0x02, 0x01, 0x51, 0x09, 0x06, // 3F 63 ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // 40 64 @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // 41 65 A 0x7F, 0x49, 0x49, 0x49, 0x36, // 42 66 B 0x3E, 0x41, 0x41, 0x41, 0x22, // 43 67 C 0x7F, 0x41, 0x41, 0x41, 0x3E, // 44 68 D 0x7F, 0x49, 0x49, 0x49, 0x41, // 45 69 E 0x7F, 0x09, 0x09, 0x09, 0x01, // 46 70 F 0x3E, 0x41, 0x49, 0x49, 0x7A, // 47 71 G 0x7F, 0x08, 0x08, 0x08, 0x7F, // 48 72 H 0x00, 0x41, 0x7F, 0x41, 0x00, // 49 73 I 0x20, 0x40, 0x41, 0x3F, 0x01, // 4A 74 J 0x7F, 0x08, 0x14, 0x22, 0x41, // 4B 75 K 0x7F, 0x40, 0x40, 0x40, 0x40, // 4C 76 L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // 4D 77 M 0x7F, 0x04, 0x08, 0x10, 0x7F, // 4E 78 N 0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F 79 O 0x7F, 0x09, 0x09, 0x09, 0x06, // 50 80 P 0x3E, 0x41, 0x51, 0x21, 0x5E, // 51 81 Q 0x7F, 0x09, 0x19, 0x29, 0x46, // 52 82 R 0x26, 0x49, 0x49, 0x49, 0x32, // 53 83 S 0x01, 0x01, 0x7F, 0x01, 0x01, // 54 84 T 0x3F, 0x40, 0x40, 0x40, 0x3F, // 55 85 U 0x1F, 0x20, 0x40, 0x20, 0x1F, // 56 86 V 0x3F, 0x40, 0x38, 0x40, 0x3F, // 57 87 W 0x63, 0x14, 0x08, 0x14, 0x63, // 58 88 X 0x03, 0x04, 0x78, 0x04, 0x03, // 59 89 Y 0x61, 0x51, 0x49, 0x45, 0x43, // 5A 90 Z 0x00, 0x7F, 0x41, 0x41, 0x41, // 5B 91 [ 0x02, 0x04, 0x08, 0x10, 0x20, // 5C 92 '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // 5D 93 ] 0x04, 0x02, 0x01, 0x02, 0x04, // 5E 94 ^ 0x80, 0x80, 0x80, 0x80, 0x80, // 5F 95 _ 0x00, 0x03, 0x07, 0x08, 0x00, // 60 96 ' 0x20, 0x54, 0x54, 0x54, 0x78, // 61 97 a 0x7F, 0x28, 0x44, 0x44, 0x38, // 62 98 b 0x38, 0x44, 0x44, 0x44, 0x28, // 63 99 c 0x38, 0x44, 0x44, 0x28, 0x7F, // 64 100 d 0x38, 0x54, 0x54, 0x54, 0x18, // 65 101 e 0x00, 0x08, 0x7E, 0x09, 0x02, // 66 102 f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // 67 103 g 0x7F, 0x08, 0x04, 0x04, 0x78, // 68 104 h 0x00, 0x44, 0x7D, 0x40, 0x00, // 69 105 i 0x00, 0x20, 0x40, 0x40, 0x3D, // 6A 106 j 0x00, 0x7F, 0x10, 0x28, 0x44, // 6B 107 k 0x00, 0x41, 0x7F, 0x40, 0x00, // 6C 108 l 0x7C, 0x04, 0x78, 0x04, 0x78, // 6D 109 m 0x7C, 0x08, 0x04, 0x04, 0x78, // 6E 110 n 0x38, 0x44, 0x44, 0x44, 0x38, // 6F 111 o 0xFC, 0x18, 0x24, 0x24, 0x18, // 70 112 p 0x18, 0x24, 0x24, 0x18, 0xFC, // 71 113 q 0x7C, 0x08, 0x04, 0x04, 0x08, // 72 114 r 0x48, 0x54, 0x54, 0x54, 0x24, // 73 115 s 0x04, 0x04, 0x3F, 0x44, 0x24, // 74 116 t 0x3C, 0x40, 0x40, 0x20, 0x7C, // 75 117 u 0x1C, 0x20, 0x40, 0x20, 0x1C, // 76 118 v 0x3C, 0x40, 0x30, 0x40, 0x3C, // 77 119 w 0x44, 0x28, 0x10, 0x28, 0x44, // 78 120 x 0x4C, 0x90, 0x90, 0x90, 0x7C, // 79 121 y 0x44, 0x64, 0x54, 0x4C, 0x44, // 7A 122 z 0x00, 0x08, 0x36, 0x41, 0x00, // 7B 123 { 0x00, 0x00, 0x77, 0x00, 0x00, // 7C 124 | 0x00, 0x41, 0x36, 0x08, 0x00, // 7D 125 } 0x02, 0x01, 0x02, 0x04, 0x02, // 7E 126 ~ 0x00, 0x06, 0x09, 0x09, 0x06, // 7F 127 degrees }; void lcd_print(char *s, unsigned x, unsigned y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 | y; lcd_send(c, sizeof(c), lcd_command); while(*s) { lcd_send(&font6x8[*s - 32][0], 5, lcd_data); lcd_send(&font6x8[0][0], 1, lcd_data); ++s; } } static const unsigned char num11x16[19][11 * 2] = { 0x00,0xF0,0xFC,0xFE,0x06,0x02,0x06,0xFE,0xFC,0xF0,0x00, // 0 0x00,0x07,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x07,0x00, 0x00,0x00,0x08,0x0C,0xFC,0xFE,0xFE,0x00,0x00,0x00,0x00, // 1 0x00,0x20,0x20,0x20,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x00, 0x00,0x0C,0x0E,0x06,0x02,0x02,0x86,0xFE,0x7C,0x38,0x00, // 2 0x00,0x30,0x38,0x3C,0x36,0x33,0x31,0x30,0x30,0x38,0x00, 0x00,0x0C,0x0E,0x86,0x82,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 3 0x00,0x18,0x38,0x30,0x20,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x00,0xC0,0x20,0x18,0x04,0xFE,0xFE,0xFE,0x00,0x00, // 4 0x00,0x03,0x02,0x02,0x02,0x22,0x3F,0x3F,0x3F,0x22,0x02, 0x00,0x00,0x7E,0x7E,0x46,0x46,0xC6,0xC6,0x86,0x00,0x00, // 5 0x00,0x18,0x38,0x30,0x20,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0xC0,0xF0,0xF8,0xFC,0x4C,0xC6,0xC2,0x82,0x00,0x00, // 6 0x00,0x0F,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0x06,0x06,0x06,0x06,0x06,0xC6,0xF6,0x3E,0x0E,0x00, // 7 0x00,0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,0x00, 0x00,0x38,0x7C,0xFE,0xC6,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 8 0x00,0x0E,0x1F,0x3F,0x31,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x78,0xFC,0xFE,0x86,0x02,0x86,0xFE,0xFC,0xF8,0x00, // 9 0x00,0x00,0x00,0x21,0x21,0x31,0x1D,0x1F,0x0F,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // : 0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // . 0x00,0x38,0x38,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x00,0x00, // / 0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // - 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x00,0x18,0x3C,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xF0,0xF8,0x0C,0x06,0x02,0x02,0x02,0x02,0x0E,0x0C,0x00, // C 0x03,0x07,0x0C,0x18,0x10,0x10,0x10,0x10,0x1C,0x0C,0x00, 0xFE,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, // F 0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xFE,0xFE,0x40,0xE0,0xB0,0x18,0x0C,0x06,0x02,0x00,0x00, // K 0x1F,0x1F,0x00,0x00,0x01,0x03,0x06,0x0C,0x18,0x10,0x00 }; void pd12(unsigned n, unsigned x, unsigned y) { unsigned char c[2]; c[0] = 0x80 | x; c[1] = 0x40 + y; lcd_send(c, 2, lcd_command); lcd_send(num11x16[n], 11, lcd_data); c[1] = 0x41 + y; lcd_send(c, 2, lcd_command); lcd_send(num11x16[n] + 11, 11, lcd_data); }
  15. Like
    fj604 reacted to gordon in Programming/Debugging with external power   
    Conect test, reset and ground (but NOT VCC) from the FET to your target.
  16. Like
    fj604 got a reaction from gordon in Launchpad + AS1106 + 8x8 LED Matrix = fun!   
    The 2211 chip that came with the Launchpad is not useless! Here is what can be done with it:
     


     
    I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies.
     
    Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red).
     
    A piezo buzzer can be added for (very annoying) sound effects beeps.
     
    I thought the whole thing has a certain 1970's touch to it
     

    #include #include #define DATA_PIN BIT4 // data for AS1106 #define LOAD_PIN BIT5 // load for AS1106 #define CLOCK_PIN BIT7 // clock for AS1106 #define BL_PIN BIT1 // Left button #define BR_PIN BIT2 // Right button #define SPEED_PIN BIT3 // Speed increase button #define GREEN_PIN BIT6 // Green LED on Launchpad (LED2) #define RED_PIN BIT0 // Red LED on Launchpad (LED1) #define BUZZER_PIN BIT7 // Piezo BUZZER = XOUT = P2.7 #define MAX_DELAY 2500 // Slowest speed #define MIN_DELAY 1000 // Fastest speed #define STEP_DELAY 300 // Speed step #define TONE_CATCH 5 // Tone on catch #define TONE_MISS 10 // Tone on miss #define TONE_CHIRP 2 // Chirp tone #define CHIRP_DUR 10 // Chirp duration #define TONE_DUR 300 // Catch / miss tone duration unsigned char bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; volatile unsigned char t = 3, // catcher x position refresh = 0, // flag to refresh matrix refreshing = 0; // set during refresh volatile unsigned int clk = 0, // clock ticks, 1 tick = 1/10000 sec delay = MAX_DELAY, // step delay tone = 0, // tone frequency, 1 = 5000 Hz, 5 = 1000 Hz sound = 0; // sound duration in ticks void pixel_set(unsigned char x, unsigned char y) { bitmap[x] |= 1 << y; } void pixel_clear(unsigned char x, unsigned char y) { bitmap[x] &= ~ (1 << y); } // Shift out data, MSB first void shiftout(unsigned char data) { unsigned char n; for (n = 0; n < 8; n++) { if (data & BIT7) P1OUT |= DATA_PIN; else P1OUT &= ~DATA_PIN; P1OUT |= CLOCK_PIN; P1OUT &= ~CLOCK_PIN; data <<= 1; } } // Send command to AS1106 void as1106(unsigned char addr, unsigned char data) { P1OUT &= ~LOAD_PIN; shiftout(addr); shiftout(data); P1OUT |= LOAD_PIN; } // show bitmap on the matrix void display(void) { refreshing = 1; // Set flag so that we do not change bitmap during refresh unsigned char c; for (c = 0; c < 8; c++) as1106(c + 1, bitmap[c]); refreshing = 0; // Clear flag } void as1106_init(void) { P1DIR |= (DATA_PIN | LOAD_PIN | CLOCK_PIN); P1OUT |= LOAD_PIN; P1OUT &= ~CLOCK_PIN; as1106(0x0C, 0x00); // shutdown display(); as1106(0x0F, 0x01); // Test on _delay_cycles(400000); as1106(0x0F, 0x00); // Test off _delay_cycles(200000); as1106(0x0E, BIT1); // Reset all control registers _delay_cycles(100); as1106(0x0E, 0); // Normal operation as1106(0x09, 0x00); // No decode as1106(0x0A, 0x0F); // full brightness as1106(0x0B, 0x0F); // display all rows as1106(0x0C, 0x81); // No shutdown } void clock_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; } void timer_init(void) { TACCR0 = 100; // Generate interrupt 10000 times per second TACTL = TASSEL_2 + MC_1; // SMCLK, upmode TACCTL0 = CCIE; // Capture/Compare Interrupt Enable } void sound_init(void) { P2SEL = 0; // Disable XIN/XOUT P2DIR = BUZZER_PIN; // XOUT pin = P2.7 } void intro_sound(void) { for (tone = 10; tone > 0; tone--) { sound = 500; while(sound) ; } } void button_init(void) { P1DIR &= ~(BL_PIN | BR_PIN | SPEED_PIN); P1OUT |= BL_PIN | BR_PIN ; // Pull up P1REN |= BL_PIN | BR_PIN ; // Enable pull up resistors P1IE |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt enable P1IES |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt Edge Select high-to-low P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // Clear interrupt flag } void main(void) { unsigned char n, p; // n = dot y, p = catcher x, WDTCTL = WDTPW + WDTHOLD; // Disable watchdog timer (WDT) as1106_init(); button_init(); timer_init(); sound_init(); P1DIR |= RED_PIN | GREEN_PIN; P1OUT &= ~(RED_PIN | GREEN_PIN); pixel_set(t, 7); pixel_set(t+1, 7); _enable_interrupts(); intro_sound(); for (; { p = rand() % 8; // random drop x position for (n = 0; n < 7; n++) // drop { pixel_set(p, n); display(); refresh = 0; clk = 0; tone = TONE_CHIRP; sound = CHIRP_DUR; while (clk < delay) { _low_power_mode_0(); if (refresh) { display(); refresh = 0; } } pixel_clear(p, n); } if ((p == t) || (p == t + 1)) // if caught { P1OUT |= GREEN_PIN; P1OUT &= ~RED_PIN; tone = TONE_CATCH; } else { P1OUT &= ~GREEN_PIN; P1OUT |= RED_PIN; tone = TONE_MISS; } sound = TONE_DUR; } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { if (P1IFG & SPEED_PIN) // speed switch { if ((delay-= STEP_DELAY) < MIN_DELAY) delay = MAX_DELAY; } else if ((P1IFG & BL_PIN) && (t > 0) && !refreshing) // left { pixel_clear(t + 1, 7); pixel_set(--t, 7); refresh = 1; } else if ((P1IFG & BR_PIN) && (t < 6) && !refreshing) // right { pixel_clear(t, 7); pixel_set(++t + 1, 7); refresh = 1; } P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // clear interrupt flags for all buttons _low_power_mode_off_on_exit(); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { static unsigned int z; clk++; if (sound) { --sound; if ((z++ % tone) == 0) P2OUT ^= BUZZER_PIN; // vibrate buzzer } _low_power_mode_off_on_exit(); }
  17. Like
    fj604 reacted to DanAndDusty in Halloween Motion Triggered Sound and Light Thingamajig   
    Hi all,

    For Halloween this year I wanted to make a motion triggered device that screams at you and makes some UV lights flash/flicker.

    Here is a video of the device in action.



    My first thought was to use an SD Card but I couldn't get this to work (this was my first attempt with the hardware UART and I have a feeling I may be able to get a little further now.. so who knows)

    When the attempt with the SD Card failed I decided to use a 25AA1024 SPI 1Mbit EEPROM. As the EEPROM doesn't connect directly to a PC I needed a way to get the sound files onto the device. So I added a serial control menu that allows uploading via XMODEM. I chose XMODEM as the packet size is only 128 bytes, the protocol requirements are minimal and just about every comms program has the protocol built it. Because of the 128 bytes for the buffer and the serial requirements I used a G2553. For the serial comms I used an FT232 breakout circuit running at 460800 baud and she screams along happily. I have also tried it using the UART->USB of the launchpad at 9600 and it works there too, albeit much slower.

    Once the sound data is on the eeprom I store it as 8 bit unsigned raw samples. These I send out to a DAC. The only DAC I had to hand was a MAX5159. The only problem with this was there is an internal gain of 2x. So I had to divide Vcc by 2 to provide the DACs reference. Easy I thought.. Voltage Divider to the rescue.. However when I tried this I found that the voltage would jump about over quite a largish range. Further reading of the 5159 datasheet unearthed this little tidbit.. The reference input impedance ranges from 18k ohms (1558 hex) to several giga ohms.. Ouch... Thats gunna throw my little voltage divider off a little.. So I remember reading at some point that op-amps can be used to buffer inputs for just such situations. Ive not used an op-amp before but had a few LM324 quad op-amps to hand. Set one of the devices up as a voltage follower and bingo.. She worked.. I pass the output from the DAC through an AC Coupling Capacitor into a computer speaker amplifier. The quality isn't suitable for an audiophile but for this it works wonders.

    I also wanted some flashing lights.. (hey.. who doesn't like flashing lights).. I wanted these to flash roughly in time with the audio. So after a sound file has been uploaded the 430 runs a very simplistic peak counting routine. It finds the highest peak that occurs atleast 40 times in the sound file. It only checks the highest so the deepest trough won't trigger it, but Im happy with the results. When the sound is played back, each time this peak value is passed the lights are triggered. I used 10 UV LEDs driven via an NPN Transistor. The voltage drop of the transistor meant that they weren't as bright as I would like at 3.3V so I power them from 4 rechargeable AA batteries (4.8-5.0v ish) and use a 3.3V LDO for the rest of the circuit. The batteries Im using are 2900 mAh ones and during my testing they have been running the device for a week with no sign of giving out. So lasting all night shouldn't be a worry

    To trigger the sounds Im using an LDR as the lower resistor of a voltage divider. I compare this voltage to one from a potentiometer (for adjusting to different light levels) using another device in the LM324 quad op-amp. This triggers an interrupt on a pin on P2. For feedback on adjusting the pot I use another op-amp triggered from the same voltages to drive an LED. Adjust the pot until the LED comes on.. back off a little so it has just turned off and bingo.. Its primed and ready. There will be a light source the other side of the (narrow) garden that will be blocked by someone walking past it and this will trigger the playback of the sound/light combo.

    Because I don't want to have a PC down in the garden to setup I added a "playback" mode which is triggered if P2.4 is tied high at power on.

    Future changes that may (or may not.. depending on time free and success) include...
    Using an SD Card to store the sound samples. The EEPROM is 1MBit or 128KBytes. at 22KHz 8 bits this can store less than 6 seconds of sound.
    Compressing the sound files using something simple like uLaw or ADPCM. I have no experience with sound compression so lets see how this goes.
    Adding an audio amplifier circuit At the moment the audio is amplified using PC speakers with built in amplifier. If I can add this to the circuit it would be neater. I tried this using an LM386 but the quality was abysmal. Im still getting my head around analogue circuitry so Its not a total surprise

    When the POTM is announced I think I'll enter this as its not often I document my projects as detailed as I should. I tend to leap on to the next brain spasm. This time though, the code is neatish and well documented. Im even drawing up a schematic over the next couple of nights (never used eagle before so bear with me on this)

    Hope you enjoyed my little ramble, over the next few nights I should be posting the code and schematic. For now though I am off to bed.

    Enjoy,

    Dan
  18. Like
    fj604 got a reaction from oPossum in Launchpad + AS1106 + 8x8 LED Matrix = fun!   
    The 2211 chip that came with the Launchpad is not useless! Here is what can be done with it:
     


     
    I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies.
     
    Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red).
     
    A piezo buzzer can be added for (very annoying) sound effects beeps.
     
    I thought the whole thing has a certain 1970's touch to it
     

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


     
    I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies.
     
    Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red).
     
    A piezo buzzer can be added for (very annoying) sound effects beeps.
     
    I thought the whole thing has a certain 1970's touch to it
     

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


     
    I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies.
     
    Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red).
     
    A piezo buzzer can be added for (very annoying) sound effects beeps.
     
    I thought the whole thing has a certain 1970's touch to it
     

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


     
    I decided to drive a LED matrix with the AS1106 chip from Austria Microsystems. It is a an equivalent of MAX7219 but can work with 3.3V that Launchpad supplies.
     
    Two buttons on a breadboard move the "catcher", and a single button on the Launchpad changes the game speed. Launchpad LEDs are used to indicate a catch (green) or a miss (red).
     
    A piezo buzzer can be added for (very annoying) sound effects beeps.
     
    I thought the whole thing has a certain 1970's touch to it
     

    #include #include #define DATA_PIN BIT4 // data for AS1106 #define LOAD_PIN BIT5 // load for AS1106 #define CLOCK_PIN BIT7 // clock for AS1106 #define BL_PIN BIT1 // Left button #define BR_PIN BIT2 // Right button #define SPEED_PIN BIT3 // Speed increase button #define GREEN_PIN BIT6 // Green LED on Launchpad (LED2) #define RED_PIN BIT0 // Red LED on Launchpad (LED1) #define BUZZER_PIN BIT7 // Piezo BUZZER = XOUT = P2.7 #define MAX_DELAY 2500 // Slowest speed #define MIN_DELAY 1000 // Fastest speed #define STEP_DELAY 300 // Speed step #define TONE_CATCH 5 // Tone on catch #define TONE_MISS 10 // Tone on miss #define TONE_CHIRP 2 // Chirp tone #define CHIRP_DUR 10 // Chirp duration #define TONE_DUR 300 // Catch / miss tone duration unsigned char bitmap[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; volatile unsigned char t = 3, // catcher x position refresh = 0, // flag to refresh matrix refreshing = 0; // set during refresh volatile unsigned int clk = 0, // clock ticks, 1 tick = 1/10000 sec delay = MAX_DELAY, // step delay tone = 0, // tone frequency, 1 = 5000 Hz, 5 = 1000 Hz sound = 0; // sound duration in ticks void pixel_set(unsigned char x, unsigned char y) { bitmap[x] |= 1 << y; } void pixel_clear(unsigned char x, unsigned char y) { bitmap[x] &= ~ (1 << y); } // Shift out data, MSB first void shiftout(unsigned char data) { unsigned char n; for (n = 0; n < 8; n++) { if (data & BIT7) P1OUT |= DATA_PIN; else P1OUT &= ~DATA_PIN; P1OUT |= CLOCK_PIN; P1OUT &= ~CLOCK_PIN; data <<= 1; } } // Send command to AS1106 void as1106(unsigned char addr, unsigned char data) { P1OUT &= ~LOAD_PIN; shiftout(addr); shiftout(data); P1OUT |= LOAD_PIN; } // show bitmap on the matrix void display(void) { refreshing = 1; // Set flag so that we do not change bitmap during refresh unsigned char c; for (c = 0; c < 8; c++) as1106(c + 1, bitmap[c]); refreshing = 0; // Clear flag } void as1106_init(void) { P1DIR |= (DATA_PIN | LOAD_PIN | CLOCK_PIN); P1OUT |= LOAD_PIN; P1OUT &= ~CLOCK_PIN; as1106(0x0C, 0x00); // shutdown display(); as1106(0x0F, 0x01); // Test on _delay_cycles(400000); as1106(0x0F, 0x00); // Test off _delay_cycles(200000); as1106(0x0E, BIT1); // Reset all control registers _delay_cycles(100); as1106(0x0E, 0); // Normal operation as1106(0x09, 0x00); // No decode as1106(0x0A, 0x0F); // full brightness as1106(0x0B, 0x0F); // display all rows as1106(0x0C, 0x81); // No shutdown } void clock_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; } void timer_init(void) { TACCR0 = 100; // Generate interrupt 10000 times per second TACTL = TASSEL_2 + MC_1; // SMCLK, upmode TACCTL0 = CCIE; // Capture/Compare Interrupt Enable } void sound_init(void) { P2SEL = 0; // Disable XIN/XOUT P2DIR = BUZZER_PIN; // XOUT pin = P2.7 } void intro_sound(void) { for (tone = 10; tone > 0; tone--) { sound = 500; while(sound) ; } } void button_init(void) { P1DIR &= ~(BL_PIN | BR_PIN | SPEED_PIN); P1OUT |= BL_PIN | BR_PIN ; // Pull up P1REN |= BL_PIN | BR_PIN ; // Enable pull up resistors P1IE |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt enable P1IES |= BL_PIN | BR_PIN | SPEED_PIN; // Interrupt Edge Select high-to-low P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // Clear interrupt flag } void main(void) { unsigned char n, p; // n = dot y, p = catcher x, WDTCTL = WDTPW + WDTHOLD; // Disable watchdog timer (WDT) as1106_init(); button_init(); timer_init(); sound_init(); P1DIR |= RED_PIN | GREEN_PIN; P1OUT &= ~(RED_PIN | GREEN_PIN); pixel_set(t, 7); pixel_set(t+1, 7); _enable_interrupts(); intro_sound(); for (; { p = rand() % 8; // random drop x position for (n = 0; n < 7; n++) // drop { pixel_set(p, n); display(); refresh = 0; clk = 0; tone = TONE_CHIRP; sound = CHIRP_DUR; while (clk < delay) { _low_power_mode_0(); if (refresh) { display(); refresh = 0; } } pixel_clear(p, n); } if ((p == t) || (p == t + 1)) // if caught { P1OUT |= GREEN_PIN; P1OUT &= ~RED_PIN; tone = TONE_CATCH; } else { P1OUT &= ~GREEN_PIN; P1OUT |= RED_PIN; tone = TONE_MISS; } sound = TONE_DUR; } } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { if (P1IFG & SPEED_PIN) // speed switch { if ((delay-= STEP_DELAY) < MIN_DELAY) delay = MAX_DELAY; } else if ((P1IFG & BL_PIN) && (t > 0) && !refreshing) // left { pixel_clear(t + 1, 7); pixel_set(--t, 7); refresh = 1; } else if ((P1IFG & BR_PIN) && (t < 6) && !refreshing) // right { pixel_clear(t, 7); pixel_set(++t + 1, 7); refresh = 1; } P1IFG &= ~ (BL_PIN | BR_PIN | SPEED_PIN); // clear interrupt flags for all buttons _low_power_mode_off_on_exit(); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { static unsigned int z; clk++; if (sound) { --sound; if ((z++ % tone) == 0) P2OUT ^= BUZZER_PIN; // vibrate buzzer } _low_power_mode_off_on_exit(); }
  22. Like
    fj604 got a reaction from krzyk2 in Interfacing the Launchpad with MAX6956 I2C LED driver   
    As promised here: http://www.43oh.com/forum/viewtopic.php?f=10&t=1015
     
    MAX6956 is an I2C LED driver / port expander, here is a demo:
     

    /* * Interfacing MSP430G2231 to MAX6956 I2C LED driver and I/O expander Pins MSP430G2231 MAX6956 1 DVCC ----------- 28 V+ 8 P1.6/SCL ----------- 26 SCL 9 P1.7/SDA ----------- 25 SDA 14 DVSS ----------- 2 GND ----------- GND 3 GND ----------- GND 4 AD0 ----------- GND 27 AD1 ----------- GND 1 ISET -/\/39K\/\- GND 12 P19 ----------- LED1 13 P20 ----------- LED2 14 P21 ----------- LED3 8 P15 ----/ ----- GND Upon startup, LED1-3 will light up for about 1.5 seconds, then LED2-3 will start glowing intermittently. While a button on P15 is pushed, LED1 will change brightness. */ #include #define PIN_CS BIT1 #define PIN_SCL BIT6 #define PIN_SDA BIT7 #define DELAY 40000 #define MAX6956_ADDR 0x80 #define ACK 0x00 #define NACK 0xFF void i2c_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; P1REN |= PIN_SCL | PIN_SDA; // Enable pullup resistors for I2C termination P1OUT |= PIN_SCL | PIN_SDA; USICTL0 = USIPE7 | USIPE6 | USIMST | USISWRST; // SDA enable, SCL enable, Master mode, release USICTL1 = USII2C | USIIE ; // USICKPH; // CKPH = 1, I2C, Interrupt Enable; USICKCTL = USIDIV_7 | USISSEL_2 | USICKPL; // Clock / 128, SMCLK, SCL inactive is high USICTL0 &= ~USISWRST; // Release from reset } void timer_init(void) { TACTL = TASSEL_2 | MC_0; // SMCLK, stop timer TACCTL0 = CCIE; // Enable timer compare interrupt } void sleep(unsigned int count) { TACCR0 = count; // Load top value in compare register TACTL |= TACLR | MC_1; // Clear counter, start timer in up mode _low_power_mode_0(); // Sleep in LPM0 until interrupt TACTL &= ~MC_1; // Stop timer } void i2c_tx_start(void) { USISRL = 0; USICTL0 |= USIGE | USIOE; // Latch enable, SDA enable USICTL0 &= ~USIGE; // Latch disable } void i2c_tx_stop(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0; USICNT = 1; // Transmit 1 bit _low_power_mode_0(); // Sleep in LPM0 until interrupt USISRL = 0xFF; USICTL0 |= USIGE; // Latch enable USICTL0 &= ~(USIGE | USIOE); // Latch disable, SDA disable } unsigned char i2c_tx_byte(unsigned char byte) { USICTL0 |= USIOE; USISRL = byte; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt USICTL0 &= ~USIOE; // SDA disable USICNT = 1; // Receive ACK bit _low_power_mode_0(); // Sleep in LPM0 until interrupt return USISRL & BIT0; // Return ACK = first bit of USISRL } unsigned char i2c_rx_byte(void) { unsigned char v; USICTL0 &= ~USIOE; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt v = USISRL; return v; } void i2c_tx_ack(unsigned char ack) { USICTL0 |= USIOE; USISRL = ack; // Send ACK/NACK bit USICNT = 1; _low_power_mode_0(); // Sleep in LPM0 until interrupt } void max6956_send(unsigned char addr, unsigned char data) { i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_byte(data); i2c_tx_stop(); } unsigned char max6956_recv(unsigned char addr) { unsigned char v; i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_stop(); i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR | BIT0); v = i2c_rx_byte(); // Read and send NACK to stop transmission i2c_tx_ack(NACK); i2c_tx_stop(); return v; } void sense(void) { static unsigned char g=0; if (!(max6956_recv(0x2F) & BIT0)) // Send No-op command and check bit 0 = button pushed max6956_send(0x19, g++ << 4); // Increase brightness of LED on port 19 } void leds(unsigned char v) { max6956_send(0x1A, v | (0x0F-v) << 4); // control brightness of LEDs on ports 20-21 sense(); } void main(void) { signed char i=0; WDTCTL = WDTPW + WDTHOLD; // Disable WDT i2c_init(); timer_init(); _enable_interrupts(); max6956_send(0x0C, 0x00); // set ports 16-19 to LED max6956_send(0x0D, 0x00); // set ports 20-23 to LED max6956_send(0x0B, 0xFF); // set ports 12-15 to GPIO Input with pullup max6956_send(0x33, 0x01); // Port 19 on max6956_send(0x34, 0x01); // Port 20 on max6956_send(0x35, 0x01); // Port 21 on max6956_send(0x07, 0x01); // Display test on _delay_cycles(1500000); // ~ 1.5s pause max6956_send(0x07, 0x00); // Display test off max6956_send(0x04, BIT0 | BIT6); // Individual segment current control, normal operation for (; { for (i=0; i<0x10; i++) { leds(i); sleep(DELAY); } for (i=0x0F; i>=0; i--) { leds(i); sleep(DELAY); } } } #pragma vector=USI_VECTOR __interrupt void usi_interrupt(void) { USICTL1 &= ~USIIFG; // Clear interrupt flag _low_power_mode_off_on_exit(); // Return from LPM } #pragma vector=TIMERA0_VECTOR __interrupt void timer_A_interrupt(void) { _low_power_mode_off_on_exit(); // Return from LPM }
  23. Like
    fj604 got a reaction from timotet in Interfacing the Launchpad with MAX6956 I2C LED driver   
    Here is a slightly modified version that implements a repeat start on I2C.
     
    It is explained here:
    http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/t/105688.aspx
     

    /* * Interfacing MSP430G2231 to MAX6956 I2C LED driver and I/O expander Pins MSP430G2231 MAX6956 1 DVCC ----------- 28 V+ 8 P1.6/SCL ----------- 26 SCL 9 P1.7/SDA ----------- 25 SDA 14 DVSS ----------- 2 GND ----------- GND 3 GND ----------- GND 4 AD0 ----------- GND 27 AD1 ----------- GND 1 ISET -/\/39K\/\- GND 12 P19 ----------- LED1 13 P20 ----------- LED2 14 P21 ----------- LED3 8 P15 ----/ ----- GND Upon startup, LED1-3 will light up for about 1.5 seconds, then LED2-3 will start glowing intermittently. While a button on P15 is pushed, LED1 will change brightness. */ #include #define PIN_CS BIT1 #define PIN_SCL BIT6 #define PIN_SDA BIT7 #define DELAY 40000 #define MAX6956_ADDR 0x80 #define ACK 0x00 #define NACK 0xFF void i2c_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; P1REN |= PIN_SCL | PIN_SDA; // Enable pullup resistors for I2C termination P1OUT |= PIN_SCL | PIN_SDA; USICTL0 = USIPE7 | USIPE6 | USIMST | USISWRST; // SDA enable, SCL enable, Master mode, release USICTL1 = USII2C | USIIE ; // USICKPH; // CKPH = 1, I2C, Interrupt Enable; USICKCTL = USIDIV_7 | USISSEL_2 | USICKPL; // Clock / 128, SMCLK, SCL inactive is high USICTL0 &= ~USISWRST; // Release from reset } void timer_init(void) { TACTL = TASSEL_2 | MC_0; // SMCLK, stop timer TACCTL0 = CCIE; // Enable timer compare interrupt } void sleep(unsigned int count) { TACCR0 = count; // Load top value in compare register TACTL |= TACLR | MC_1; // Clear counter, start timer in up mode _low_power_mode_0(); // Sleep in LPM0 until interrupt TACTL &= ~MC_1; // Stop timer } void i2c_tx_start(void) { USISRL = 0; USICTL0 |= USIGE | USIOE; // Latch enable, SDA enable USICTL0 &= ~USIGE; // Latch disable } void i2c_tx_rpt_start(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0xFF; // Set MSB of USISRL to high USICNT = 1; // Transmit 1 bit for a surplus clock cycle _low_power_mode_0(); // Sleep in LPM0 until interrupt i2c_tx_start(); } void i2c_tx_stop(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0; USICNT = 1; // Transmit 1 bit _low_power_mode_0(); // Sleep in LPM0 until interrupt USISRL = 0xFF; USICTL0 |= USIGE; // Latch enable USICTL0 &= ~(USIGE | USIOE); // Latch disable, SDA disable } unsigned char i2c_tx_byte(unsigned char byte) { USICTL0 |= USIOE; USISRL = byte; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt USICTL0 &= ~USIOE; // SDA disable USICNT = 1; // Receive ACK bit _low_power_mode_0(); // Sleep in LPM0 until interrupt return USISRL & BIT0; // Return ACK = first bit of USISRL } unsigned char i2c_rx_byte(void) { unsigned char v; USICTL0 &= ~USIOE; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt v = USISRL; return v; } void i2c_tx_ack(unsigned char ack) { USICTL0 |= USIOE; USISRL = ack; // Send ACK/NACK bit USICNT = 1; _low_power_mode_0(); // Sleep in LPM0 until interrupt } void max6956_send(unsigned char addr, unsigned char data) { i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_byte(data); i2c_tx_stop(); } unsigned char max6956_recv(unsigned char addr) { unsigned char v; i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_rpt_start(); // Generate a repeat start i2c_tx_byte(MAX6956_ADDR | BIT0); v = i2c_rx_byte(); // Read and send NACK to stop transmission i2c_tx_ack(NACK); i2c_tx_stop(); return v; } void sense(void) { static unsigned char g=0; if (!(max6956_recv(0x2F) & BIT0)) // Send No-op command and check bit 0 = button pushed max6956_send(0x19, g++ << 4); // Increase brightness of LED on port 19 } void leds(unsigned char v) { max6956_send(0x1A, v | (0x0F-v) << 4); // control brightness of LEDs on ports 20-21 sense(); } void main(void) { signed char i=0; WDTCTL = WDTPW + WDTHOLD; // Disable WDT i2c_init(); timer_init(); _enable_interrupts(); max6956_send(0x0C, 0x00); // set ports 16-19 to LED max6956_send(0x0D, 0x00); // set ports 20-23 to LED max6956_send(0x0B, 0xFF); // set ports 12-15 to GPIO Input with pullup max6956_send(0x33, 0x01); // Port 19 on max6956_send(0x34, 0x01); // Port 20 on max6956_send(0x35, 0x01); // Port 21 on max6956_send(0x07, 0x01); // Display test on _delay_cycles(1500000); // ~ 1.5s pause max6956_send(0x07, 0x00); // Display test off max6956_send(0x04, BIT0 | BIT6); // Individual segment current control, normal operation for (; { for (i=0; i<0x10; i++) { leds(i); sleep(DELAY); } for (i=0x0F; i>=0; i--) { leds(i); sleep(DELAY); } } } #pragma vector=USI_VECTOR __interrupt void usi_interrupt(void) { USICTL1 &= ~USIIFG; // Clear interrupt flag _low_power_mode_off_on_exit(); // Return from LPM } #pragma vector=TIMERA0_VECTOR __interrupt void timer_A_interrupt(void) { _low_power_mode_off_on_exit(); // Return from LPM }
  24. Like
    fj604 got a reaction from timotet in Interfacing the Launchpad with MAX6956 I2C LED driver   
    As promised here: http://www.43oh.com/forum/viewtopic.php?f=10&t=1015
     
    MAX6956 is an I2C LED driver / port expander, here is a demo:
     

    /* * Interfacing MSP430G2231 to MAX6956 I2C LED driver and I/O expander Pins MSP430G2231 MAX6956 1 DVCC ----------- 28 V+ 8 P1.6/SCL ----------- 26 SCL 9 P1.7/SDA ----------- 25 SDA 14 DVSS ----------- 2 GND ----------- GND 3 GND ----------- GND 4 AD0 ----------- GND 27 AD1 ----------- GND 1 ISET -/\/39K\/\- GND 12 P19 ----------- LED1 13 P20 ----------- LED2 14 P21 ----------- LED3 8 P15 ----/ ----- GND Upon startup, LED1-3 will light up for about 1.5 seconds, then LED2-3 will start glowing intermittently. While a button on P15 is pushed, LED1 will change brightness. */ #include #define PIN_CS BIT1 #define PIN_SCL BIT6 #define PIN_SDA BIT7 #define DELAY 40000 #define MAX6956_ADDR 0x80 #define ACK 0x00 #define NACK 0xFF void i2c_init(void) { BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; P1REN |= PIN_SCL | PIN_SDA; // Enable pullup resistors for I2C termination P1OUT |= PIN_SCL | PIN_SDA; USICTL0 = USIPE7 | USIPE6 | USIMST | USISWRST; // SDA enable, SCL enable, Master mode, release USICTL1 = USII2C | USIIE ; // USICKPH; // CKPH = 1, I2C, Interrupt Enable; USICKCTL = USIDIV_7 | USISSEL_2 | USICKPL; // Clock / 128, SMCLK, SCL inactive is high USICTL0 &= ~USISWRST; // Release from reset } void timer_init(void) { TACTL = TASSEL_2 | MC_0; // SMCLK, stop timer TACCTL0 = CCIE; // Enable timer compare interrupt } void sleep(unsigned int count) { TACCR0 = count; // Load top value in compare register TACTL |= TACLR | MC_1; // Clear counter, start timer in up mode _low_power_mode_0(); // Sleep in LPM0 until interrupt TACTL &= ~MC_1; // Stop timer } void i2c_tx_start(void) { USISRL = 0; USICTL0 |= USIGE | USIOE; // Latch enable, SDA enable USICTL0 &= ~USIGE; // Latch disable } void i2c_tx_stop(void) { USICTL0 |= USIOE; // SDA enable USISRL = 0; USICNT = 1; // Transmit 1 bit _low_power_mode_0(); // Sleep in LPM0 until interrupt USISRL = 0xFF; USICTL0 |= USIGE; // Latch enable USICTL0 &= ~(USIGE | USIOE); // Latch disable, SDA disable } unsigned char i2c_tx_byte(unsigned char byte) { USICTL0 |= USIOE; USISRL = byte; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt USICTL0 &= ~USIOE; // SDA disable USICNT = 1; // Receive ACK bit _low_power_mode_0(); // Sleep in LPM0 until interrupt return USISRL & BIT0; // Return ACK = first bit of USISRL } unsigned char i2c_rx_byte(void) { unsigned char v; USICTL0 &= ~USIOE; USICNT = 8; _low_power_mode_0(); // Sleep in LPM0 until interrupt v = USISRL; return v; } void i2c_tx_ack(unsigned char ack) { USICTL0 |= USIOE; USISRL = ack; // Send ACK/NACK bit USICNT = 1; _low_power_mode_0(); // Sleep in LPM0 until interrupt } void max6956_send(unsigned char addr, unsigned char data) { i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_byte(data); i2c_tx_stop(); } unsigned char max6956_recv(unsigned char addr) { unsigned char v; i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR); i2c_tx_byte(addr); i2c_tx_stop(); i2c_tx_start(); i2c_tx_byte(MAX6956_ADDR | BIT0); v = i2c_rx_byte(); // Read and send NACK to stop transmission i2c_tx_ack(NACK); i2c_tx_stop(); return v; } void sense(void) { static unsigned char g=0; if (!(max6956_recv(0x2F) & BIT0)) // Send No-op command and check bit 0 = button pushed max6956_send(0x19, g++ << 4); // Increase brightness of LED on port 19 } void leds(unsigned char v) { max6956_send(0x1A, v | (0x0F-v) << 4); // control brightness of LEDs on ports 20-21 sense(); } void main(void) { signed char i=0; WDTCTL = WDTPW + WDTHOLD; // Disable WDT i2c_init(); timer_init(); _enable_interrupts(); max6956_send(0x0C, 0x00); // set ports 16-19 to LED max6956_send(0x0D, 0x00); // set ports 20-23 to LED max6956_send(0x0B, 0xFF); // set ports 12-15 to GPIO Input with pullup max6956_send(0x33, 0x01); // Port 19 on max6956_send(0x34, 0x01); // Port 20 on max6956_send(0x35, 0x01); // Port 21 on max6956_send(0x07, 0x01); // Display test on _delay_cycles(1500000); // ~ 1.5s pause max6956_send(0x07, 0x00); // Display test off max6956_send(0x04, BIT0 | BIT6); // Individual segment current control, normal operation for (; { for (i=0; i<0x10; i++) { leds(i); sleep(DELAY); } for (i=0x0F; i>=0; i--) { leds(i); sleep(DELAY); } } } #pragma vector=USI_VECTOR __interrupt void usi_interrupt(void) { USICTL1 &= ~USIIFG; // Clear interrupt flag _low_power_mode_off_on_exit(); // Return from LPM } #pragma vector=TIMERA0_VECTOR __interrupt void timer_A_interrupt(void) { _low_power_mode_off_on_exit(); // Return from LPM }
  25. Like
    fj604 reacted to gordon in Interfacing the Launchpad with MAX6957 SPI LED Driver   
    Might it be this? (Also the MAX6957 data sheet page 7, towards the bottom.)
×
×
  • Create New...