Well, I figured it all out and the boys got their projects done! As a professional engineer, I typically have a 30% failure rate of the boards after they come out of the assembly shop. Somewhere there's a solder bridge or bad connection. The boys matched that rate, so most of the boards worked! I was very happy.
Since this is the second time most of them had soldered (we practiced once), this was a great achievement for them.
Besides the analog control of the paddle, I made it so that when you do well, the game gets quicker. When you do badly, the game ends. When the game ends, it displays the "level" you got to in the number of rows it lights up.
Attached here are the instructions for assembling the boards, and some pictures of the boards. Thanks to everyone who helped this happen! It's certainly not the best code, but it works! Thanks fj604 for the base code!
/*
* Main.c
*
* Created on: May 4, 2012
* Author: Keven Coates (modified code from fj604's code)
*/
#include
#include
#define DATA_PIN BIT2 // data for as1116
#define LOAD_PIN BIT0 // load for as1116
#define CLOCK_PIN BIT4 // clock for as1116
#define MUTE_PIN BIT3 // Left button
#define BR_PIN BITC // Right button
#define SPEED_PIN BIT2 // Speed increase button
#define GREEN_PIN BIT6 // Green LED on Launchpad (LED2)
#define RED_PIN BIT9 // Red LED on Launchpad (LED1)
#define BUZZER_PIN BIT7 // Piezo BUZZER = XOUT = P2.7
#define SPK_GND BIT4 // P2.4 used as speaker ground
#define MAX_DELAY 2500 // Slowest speed
#define MIN_DELAY 600 // Fastest speed
#define STEP_DELAY 100 // 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
pot, // ADC10 value shifted right to divide
refresh = 0, // flag to refresh matrix
refreshing = 0, // set during refresh
sound_enable = 0; // sound enable variable
volatile unsigned int
wins=10, // wins = number of catches
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, LSB first
void shiftout(unsigned char data)
{
unsigned char n;
for (n = 0; n < 8; n++)
{
if (data & BIT0)
P1OUT |= DATA_PIN; //Set data
else
P1OUT &= ~DATA_PIN;
P1OUT |= CLOCK_PIN; //Toggle clock pin
P1OUT &= ~CLOCK_PIN;
data >>= 1;
}
}
// Send command to as1116
void as1116(unsigned char addr, unsigned char data)
{
P1OUT &= ~LOAD_PIN;
shiftout(data);
shiftout(addr);
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++)
as1116(c + 1, bitmap[c]);
refreshing = 0; // Clear flag
}
void as1116_init(void)
{
unsigned char x,y;
P1DIR |= (DATA_PIN | LOAD_PIN | CLOCK_PIN);
P1OUT |= LOAD_PIN; // Turn on load pin (high = no activity)
P1OUT &= ~CLOCK_PIN;
as1116(0x0C, 0x00); // Turn off display
as1116(0x0E, BIT1); // Reset all control registers
_delay_cycles(100);
as1116(0x09, 0x00); // Decode mode = Matrix
as1116(0x0A, 0x0B); // Global brightness, med-high
as1116(0x0E, 0x00); // Normal operation
as1116(0x0B, 0x07); // Scan limit, all LEDs
_delay_cycles(100);
as1116(0x0C, 0x81); // Turn on (no shutdown)
as1116(0x01, 0xff); // test row
refreshing = 1; // Set flag so that we do not change bitmap during refresh
for (y = 0; y < 8; y++) // Have fun with little light show and test
{
for (x = 1; x <= 0x80; x=x+0)
{
as1116(y + 1, x);
_delay_cycles(10000);
if (x!=0x80)
x <<=1;
else
x++;
}
}
refreshing = 0; // Clear flag
display();
}
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 + SPK_GND; // XOUT pin = P2.7 and P2.4 are outputs
P2OUT &= SPK_GND; // pull bit 4, P2.4 low to serve as ground for speaker
}
void intro_sound(void)
{
for (tone = 10; tone > 0; tone--)
{
sound = 500;
while(sound)
;
}
}
void button_init(void)
{
P1DIR &= ~(MUTE_PIN | BR_PIN);
P1OUT |= MUTE_PIN | BR_PIN ; // Pull up
P1REN |= MUTE_PIN | BR_PIN ; // Enable pull up resistors
P1IE |= MUTE_PIN | BR_PIN; // Interrupt enable
P1IES |= MUTE_PIN | BR_PIN; // Interrupt Edge Select high-to-low
P2IES |= SPEED_PIN;
P1IFG &= ~ (MUTE_PIN | BR_PIN); // Clear interrupt flag
P2IFG &= ~ (SPEED_PIN);
}
void adc_init(void)
{
ADC10CTL1 = CONSEQ_2+INCH_5; // Repeat single channel and select channel 5
ADC10CTL0 = 0x0000 + ADC10SHT_2 + MSC + REFON + ADC10ON + ADC10IE; //Use Vcc and ground as reference, S+H=8, mult sample conversion,reference on, ADC10 on, interrupts on
__enable_interrupt(); // Enable interrupts.
LPM0; // Wait for delay. Low power mode off on exit
__disable_interrupt();
ADC10DTC1 = 0x02; // 2 conversions
ADC10AE0 |= 0x20; // P1.5 ADC option select
}
void main(void)
{
unsigned char n, p; // n = dot y, p = catcher x,
WDTCTL = WDTPW + WDTHOLD; // Disable watchdog timer (WDT)
as1116_init();
button_init();
timer_init();
sound_init();
adc_init();
sound_enable = 1; // Sound is on by default unless switched off by the button
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
{
ADC10CTL0 &= ~ENC;
while (ADC10CTL1 & BUSY); // Wait if ADC10 core is active
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
__bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit
pixel_set(p, n);
display();
refresh = 0;
clk = 0;
tone = TONE_CHIRP;
sound = CHIRP_DUR;
pot=~(ADC10MEM>>7) & 0x07; //pot value is inverse (otherwise paddle goes backwards) of ADC result divided into usable number
if ((pot < t) && (t > 0) && !refreshing) //move up if pot value < current position of paddle and paddle isn't at top already
{
pixel_clear(t + 1, 7);
pixel_set(--t, 7);
refresh = 1;
}
if ((pot > t) && (t < 6) && !refreshing) //move down if value > current position
{
pixel_clear(t, 7);
pixel_set(++t + 1, 7);
refresh = 1;
}
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;
wins++;
if ((wins > 14) && (delay >= MIN_DELAY)) // If you win more than 7 times, game speeds up
{
delay = delay - STEP_DELAY;
wins = 7;
}
}
else
{
P1OUT &= ~GREEN_PIN;
P1OUT |= RED_PIN;
tone = TONE_MISS;
wins--; // Every miss counts against your wins
if (wins <= 0) // If you miss more than 7 (default win number) than game ends
{
if(delay <= 600)
bitmap[0]=0xFF;
if(delay <= 700)
bitmap[1]=0xFF;
if(delay <= 800)
bitmap[2]=0xFF;
if(delay <= 1000)
bitmap[3]=0xFF;
if(delay <= 1400)
bitmap[4]=0xFF;
if(delay <= 1700)
bitmap[5]=0xFF;
if(delay <= 2100)
bitmap[6]=0xFF;
if(delay <= 2400)
bitmap[7]=0xFF;
display();
while(1); // Loop forever and end game
}
}
sound = TONE_DUR;
}
}
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
if ((P1IFG & MUTE_PIN) && (t > 0) && !refreshing) // Sound off
sound_enable = ~sound_enable & 0x01; // toggle sound enable
P1IFG &= ~ (MUTE_PIN | BR_PIN); // clear interrupt flags for all buttons
_low_power_mode_off_on_exit();
}
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
static unsigned int z;
clk++;
if (sound && sound_enable)
{
--sound;
if ((z++ % tone) == 0)
P2OUT ^= BUZZER_PIN; // vibrate buzzer
}
_low_power_mode_off_on_exit();
}
// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
__bic_SR_register_on_exit(CPUOFF); // Clear CPUOFF bit from 0(SR)
}
Matrix Electronics Merit Badge Kit Instructions.zip
MSP430LEDBooster.zip
Launchpad_LEDMatrix_schematic.pdf
lol I wouldn't say full of yourself, but confident in your knowledge.
Its always nice to have another engineer here, because some of us hackers lack on some information in electronics.
Hi Kman,
Thanks for the wonderful introduction. Welcome to the Forum. We could certainly use your help here around the forums. Make sure you put up your biy scout project in the Projects section.
The following posts describe a simple method of CIR capture that can be done with almost any MSP430. The prototype shown uses the Launchpad with a MSP430G2211.
This device that will show in detail the signal sent by IR remote controls. It measures the carrier frequency, on duration, off duration, and pulse count per burst. The Launchpad fitted with a MSP430G2211 running at 1 MHz sends all this information to a PC where a Windows program is used for graphic display. Capture is done in real time and there is no limit to the duration.
The following screen captures are from a remote using RC5 and another using NEC protocol. The top line shows the carrier frequency and a summary of the first 300 milliseconds. The following lines show the details - on duration, off duration, and pulse count.
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();
}