Jump to content

8x8 LED Matrix board with AS1116 chip with PONG?

Recommended Posts

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.

Link to post
Share on other sites

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.

Link to post
Share on other sites

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?



Link to post
Share on other sites

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!


Link to post
Share on other sites

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?



Link to post
Share on other sites

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!

Link to post
Share on other sites

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?



Link to post
Share on other sites
  • 2 weeks later...

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)

#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
            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;
      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;

      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
      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
      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);
      	     if (x!=0x80)
      	    	 x <<=1;
      refreshing = 0;                  // Clear flag

   void clock_init(void)
       BCSCTL1 = CALBC1_1MHZ;          // Set DCO

   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;

   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
   	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)

      sound_enable = 1;							// Sound is on by default unless switched off by the button

      P1OUT &= ~(RED_PIN | GREEN_PIN);

      pixel_set(t, 7);
      pixel_set(t+1, 7);

      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);
            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)
               if (refresh)
                  refresh = 0;
            pixel_clear(p, n);
         if ((p == t) || (p == t + 1))                 // if caught
            P1OUT |= GREEN_PIN;
            P1OUT &= ~RED_PIN;
            tone = TONE_CATCH;
            if ((wins > 14) && (delay >= MIN_DELAY))   // If you win more than 7 times, game speeds up
           	delay = delay - STEP_DELAY;
           	wins = 7;
            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)
           	if(delay <= 700)
           	if(delay <= 800)
           	if(delay <= 1000)
           	if(delay <= 1400)
           	if(delay <= 1700)
           	if(delay <= 2100)
           	if(delay <= 2400)
           	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

   #pragma vector=TIMER0_A0_VECTOR
   __interrupt void Timer_A (void)
      static unsigned int z;
      if (sound && sound_enable)
         if ((z++ % tone) == 0)
            P2OUT ^= BUZZER_PIN;              // vibrate buzzer

   // 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



Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...