Kman 3 Posted June 1, 2012 Share Posted June 1, 2012 I'm doing a kit for my Boy Scouts Electronics Merit badge. Rather than buy a premade boring analog kit, I decided to make a kit using the launchpad. I wanted to do something fun for the boys, and teach them programming too. It's got cool lights, two pots for analog control of a game, and two switches for "fire" buttons too. I decided to use the AS1116 because I wasn't aware of the AS1106 which was code compatible with the MAX7219 (5V), so the code here is very similar to fj604's project, only modified since I wanted the game to be slightly different (like PONG) and I'm using the AS1116, which has some redeeming features. It's got row brightness controls and some diagnostics that can be run on the LEDs. It runs about $5 from Digikey IIRC. The project hardware is very simple, just a booster style PCB (2"x2") that I designed with an 8x8 LED matrix, and a potentiometer and push button on both sides of the matrix. The AS1116 and a few discretes are on the back and the other pins are brought out as test points. I have a lot of digital design experience, so the hardware worked the first time (after breadboarding and correcting for the weird AS1116 data sheet connections), but code was never my strong point. Now I'm trying to integrate some ADC10 reading into the code, and I'm a little foggy on how this is done, but I'll see what I can do. It's probably not worth posting my code at this point since it's just modified code from fj604's AS1106 project, but when I get done I hope it to be more like PONG. If I get to that point, I'll post it. I do have a complete PCB board file in Allegro that I can send or upload, or maybe I should upload the gerbers? I'm not sure how it's usually done here. I'm also mostly web page illiterate, so I'm not quite sure how to post an image? Does it have to be a url from another site? Thanks! I hope this project helps someone. It's been fun to do. Quote Link to post Share on other sites
bluehash 1,581 Posted June 1, 2012 Share Posted June 1, 2012 Sharing an image is easy. When you post your reply, look below. There will be two tabs - options and upload attachment. Click "upload attachment" and it should be explanatory from there. If you fall into trouble, let us know here. You can upload the gerbers. A picture of your board also helps. Many members over here work with Eagle or Kicad as the hobbyist version is free upto a certain size. Kicad is free. Quote Link to post Share on other sites
Kman 3 Posted June 4, 2012 Author Share Posted June 4, 2012 Thanks! I wasn't sure if when I uploaded it, it would understand that it was a picture. dacoffey and SugarAddict 2 Quote Link to post Share on other sites
bluehash 1,581 Posted June 4, 2012 Share Posted June 4, 2012 Looks good! I'm sure the boys will wear those buttons off Quote Link to post Share on other sites
Kman 3 Posted June 5, 2012 Author Share Posted June 5, 2012 I'm adding the ADC10 code to read channel 5. I got the stand alone sample program working on channel 5 (yeah!), but I'm wondering about how to integrate it into the other program. My main question is this: In the stand alone program, the adc has its own interrupt routine. Is it good practice to combine interrupt routines, or should everything have its own? Both programs use timer A0, so I guess unless they're set to the same clock, I'll have to use two timers? Also, the stand alone program has this statement: __bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit What is that? Thanks! Quote Link to post Share on other sites
SugarAddict 227 Posted June 5, 2012 Share Posted June 5, 2012 You're setting the bits for CPUOFF sleep mode and General Interrupts Enabled. Section 2.3 is what you want to read. Kman 1 Quote Link to post Share on other sites
Kman 3 Posted June 6, 2012 Author Share Posted June 6, 2012 Thanks! It seems kind of obvious now that you point it out . What about the ISR question? Is there a rule or a good practice there to follow about that? Speaking of good practices, can anyone point me to something that tells good software practices in general, or for the MSP430 specifically? That would be great. Thanks for all your help guys! :thumbup: Quote Link to post Share on other sites
Kman 3 Posted June 7, 2012 Author Share Posted June 7, 2012 I have a new question. How do I reference a memory location like a variable? Set up a pointer, right? For some reason I can't seem to do that. I've got this: volatile short *R4 = 0x21C; // Potentiometer R4 data at 0x21C and it's giving me an error saying: 145 a value of type "int" cannot be used to initialize an entity of type "volatile short *" Can anyone give me a pointer on pointers ;-) Is there some code I can look at? Thanks! Quote Link to post Share on other sites
oPossum 1,083 Posted June 7, 2012 Share Posted June 7, 2012 volatile short *R4 = (short *)0x21C; It is rare to have to do something like that. I suspect you may be approaching something the wrong way. Quote Link to post Share on other sites
Kman 3 Posted June 7, 2012 Author Share Posted June 7, 2012 That's certainly possible. Maybe I'll back up a bit. All I'm trying to do is define a variable that contains the result of an ADC10 conversion. The ADC10 conversion in the sample code I got put the result at 0x21C, so I thought I could just define a pointer variable, but maybe that's not the best way to do this? Thanks for any info. I'm running blind here, but learning a lot! Quote Link to post Share on other sites
Kman 3 Posted June 11, 2012 Author Share Posted June 11, 2012 I've solved the variable problem, I didn't know I could just reference ADC10MEM and be done with it. That's much easier. Now I have a new problem. Due to the fact that I wasn't sure if I would be using sound on my final board, I didn't specifically wire the board for a speaker, and did an old mistake. While I made sure to make every unused pin available in test points, I forgot to include a few ground connections! So I'm using an output for ground, pulling it low to be the ground side of the speaker. This works great, and should make it really easy to do a mute function. The program is written with interrupt for the buttons. I've taken them all out except for one button, which I hoped to make the mute button. All it has to do is pull the output for P2.4 high. My statement is : P2OUT |= SPK_GND; (where SPK_GND is BIT4) Previous P2 statements are: P2SEL = 0; // Disable XIN/XOUT P2DIR = BUZZER_PIN + SPK_GND; // XOUT pin = P2.7 and P2.4 are outputs P2IES |= SPEED_PIN; P2IFG &= ~ (SPEED_PIN); (to generate the interrupt, I haven't yet renamed it from SPEED PIN) While Code composer seems to have no problem executing the statement, it doesn't change the P2OUT register and it doesn't pull the output high. Does anyone see why? Thanks! Quote Link to post Share on other sites
Kman 3 Posted June 20, 2012 Author Share Posted June 20, 2012 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 gordon 1 Quote Link to post Share on other sites
bluehash 1,581 Posted June 20, 2012 Share Posted June 20, 2012 Well done.. and thanks for sharing! Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.