Shop OBEX P1 Docs P2 Docs Learn Events
Panel 64 This is amazing — Parallax Forums

Panel 64 This is amazing

Ok, maybe not that amazing after all, but it does demonstrate recursion. I don't know if this can be done in SPIN but in C it works well. I did have a problem when I first tried this and found that I was running out of stack space and needed to make the array a little smaller.
Panel Maze
Here is a video of it drawing the maze, it's kind of fun to watch as it moves around filling in each little spot. The different colors display the path not taken. Red first path, Green second try, and Blue last try.
Panel Maze in Action
Here is a listing of the program that generated that maze:

#include <stdio.h>
#include <memory.h>
#include <propeller.h>
#include "Panel64.h"

// East, West, North, South
int dirx[4] = {1, 0, 0, -1};
int diry[4] = {0, 1, -1, 0};

void doMove(int, int, int);
int doCheck(int, int, int, int);
int length;
int Level;

int main(int argc, char** argv)
{
    // how far to move
    length = 3;

    _waitms(5000);

    Panel_Start();

    Panel_SetPixel(30, 0, 7);

    Level = 0;
    doMove(30, 0, 2);

    printf("Done\n");

    while (1)
    {
        _waitms(1000);
    }
}

void doMove(int x, int y, int d)
{
    char possible[3];
    int Possible = 3;
    int j=0;
    int i;
    int x1, y1;
    int C = 4;

    d = 3 - d; // move from direction
    for (i=0;i < 4;i++)
        if (i != d)
            possible[j++] = i;

    while (Possible > 0)
    {
        d = _rnd() % Possible;

        i = doCheck(x, y, possible[d], length);
        if (i == length)
        {
            x1 = x;
            y1 = y;
            for (i=0;i<length;i++)
            {
                x1 = x1 + dirx[possible[d]];
                y1 = y1 + diry[possible[d]];
                Panel_SetPixel(x1, y1, C);
                _waitms(15);
            }
            Level++;
            doMove(x1, y1, possible[d]);
            Level--;
        }
        C = C >> 1;

        Possible--;
        if (d < Possible)
            possible[d] = possible[Possible];
    }
}

int doCheck(int x, int y, int d, int a)
{
    int i;

    for (i=0;i<a;i++)
    {
        x = x + dirx[d];
        y = y + diry[d];
        if ((x < 0) || (x > 63))
            break;
        if ((y < 0) || (y > 63))
            break;
        if (Panel_GetPixel(x, y) != 0)
            break;
    }
    return i;
}

As a bonus, I also did the Game of Life.
Panel Life
This does not have any recursion in it but nonetheless shows graphics being drawn on the panel.
Here is that in action:
Panel Game of Life
There are several websites dedicated to this game so I need to say it here. I did add a little color to the formula where Red is dead, and Blue is new life. Anyway here is the source code for that program:

#include <stdlib.h>
#include <stdio.h>
#include <propeller.h>
#include "Panel64.h"

char LifeBoard[64][64];
char LifeBoard2[64][64];
char Setup[1024];

const char On = 0x07;
const char Off = 0x00;
const char RED = 0x04;
const char BLUE = 0x01;

void DoStart(void);
void DoMove(void);
int DoCheck(int, int);
void DoDraw(void);
void DoSetup(char*);


int main(int argc, char** argv)
{
    int i;

    Panel_Start();

    DoSetup("32,32 33,32 34,32 35,32 36,32 32,33 33,33 20,20 21,21 22,21 21,22 22,20 ");

    DoDraw();

    _waitms(5000);

    i = 0;
    while (1)
    {
        DoDraw();
        _waitms(250);
        DoMove();
        printf("Gen: %d\n", i++);
    }
}

void DoMove()
{
    int t;

    for (int i=0;i<64;i++)
    {
        for (int j=0;j<64;j++)
        {
            t = DoCheck(i, j);
            LifeBoard2[i][j] = Off;
            if (LifeBoard[i][j] > 0)
            {
                if ((t == 2) || (t == 3))
                    LifeBoard2[i][j] = On;
            }
            else
            {
                if (t == 3)
                    LifeBoard2[i][j] = On;
            }
        }
    }
    for (int i=0;i<64;i++)
        for (int j=0;j<64;j++)
            LifeBoard[i][j] = LifeBoard2[i][j];
}

int DoCheck(int x, int y)
{
    int Total = 0;

    for (int i=x-1;i<x+2;i++)
    {
        for (int j=y-1;j<y+2;j++)
        {
            if ((i < 0) || (i > 63))
                continue;
            if ((j < 0) || (j > 63))
                continue;
            if ((i == x) && (j == y))
                continue;
            if (LifeBoard[i][j] > 0)
                Total++;
        }
    }
    return Total;
}

void DoDraw()
{
    int t;
    int c;

    for (int i=0;i<64;i++)
        for (int j=0;j<64;j++)
        {
            c = LifeBoard[i][j];
            t = Panel_GetPixel(i,j);
            if ((t == 0) && (c > 0))
                c = BLUE;
            if (c == 0)
            {
                if ((t == BLUE) || (t == On))
                    c = RED;
            }
            Panel_SetPixel(i,j,c);
        }
}

void DoSetup(char *positions)
{
    char value[10];
    int x, y;
    int j;

    j = 0;
    x = 0;
    y = 0;
    for (int i=0;i<strlen(positions);i++)
    {
        if (positions[i] == ',')
        {
            x = atoi(value);
            j = 0;
            continue;
        }
        if (positions[i] == ' ')
        {
            y = atoi(value);
            j = 0;
            LifeBoard[x][y] = On;
            continue;
        }
        value[j++] = positions[i];
        value[j] = 0;
    }
}

Enjoy,

Mike

Comments

  • Very cool!

    Do you have a link to where I can find more information about the array? I'm curious what sort of color resolution it has.

    I've experimented with using a Propeller 1 to use shift registers to drive some 8x8 RGB arrays. One cog could drive two arrays with 15-bit color (5-bits each color) without the arrays having noticeable flicker. I really want to try using the P2 to drive the same arrays and see what sort of improvements I get.

    Thanks for posting your project. It was fun to see.

  • @"Duane Degn" ,

    Some time ago I bought a Sphero Bolt that had one of those displays on it and I wanted get one to play with but was unable to find the one they used on that unit. A lot of them are single color or are two big.

    I see that they are a little cheaper than when you were working on your project and they seem to be using a MAX7219 to drive them now.

    The driver for the panel can be found here.

    Basically, it just drives the three colors, so an 8 bit matrix 64x64 is used. No fancy PWM is used to dim each color. Since two rows of 64 or more are turned on at the same time logic would have to be developed to not show a color on each pass so that color would be brighter or dimmer.

    I guess that could be done by maybe counting the value of the color and then based on the refresh not show that color on that pass.

    At this point I don't see a need to do that.

    It also looks like I should be able to hook this panel up to my p1. I will have to give it a try.

    Mike

  • Got to love C,

    Took the code for the P2 and moved it over to the P1 and changed a few low level functions, and remapped the pins and even fixed some bugs I found in the P2 version in less than 20 minutes.

    Everything worked except the P1 is far to slow. Major flicker. Will need a more optimized driver and then I will probably run out of memory.

    Mike

  • This would really make some nice lessons for people learning programming.

    The problem with recursion you already discovered yourself. It is harder to understand the stack utilization. In your case the worst case would be that the random number would generate a path without any "fail" of the doCheck function ( return always returns length ) - which is in theory possible. That would mean that you have 64/3 * 64/3 * (bytes used on the stack per call).

    So, it is a good idea to keep the stack usage per call as low as possible. Besides the 3 call parameters and the return address and the return value, all your local variables need some space as well. This means that 6 int and 3 char count in as well per call.
    If you have a closer look, you have some variables which keep some state information for the recursion which is needed when the recursion comes back. But other variables are pure local variables which actually are initialized and used before the call to doMove. It is easily possible to move declaration of those variables out of the function (make them global) and re-use the variables in each level of the recursion.

    These would be i,j,x1,y1.

    4 int less on the stack per level is some nice saving.

    And the next level for a programming lesson would be to turn the recursion into an iteration. ;o) You might need the same amount of memory to keep track of the state, but you don't loose all those bytes for return address, return value and you'd stay in the same loop which saves the runtime for calling and returning. So, iterations usually need less memory and less runtime.

  • Here is a Spin version, which makes use of the Propeller Tool debugging to show the output. So, no LED Matrix needed to run it.
    It also spits out some recursion informations at the end.

    If you want it to run faster, just comment out the out() instruction in the doMove function.

  • I stand corrected, The P1 works just fine with panel64.

    I had to optimize the code for the P1 to get better performance but it work fine with a little noticeable flicker.

    I was running out of memory when I realized I don't need to include the font for the P1 since it's in ROM and removing that left plenty of room.

    I was able to run both the Maze program and the Life program with no issues.

    P1 Maze Program:

    #include "Panel64.h"
    
    #include "simpletools.h"
    
    
    // East, South, North, West
    int dirx[4] = {1, 0, 0, -1};
    int diry[4] = {0, 1, -1, 0};
    
    void doMove(int, int, int);
    int doCheck(int, int, int, int);
    char length;
    int Level;
    int i;
    int xx, yy;
    
    
    int main()
    {
        // how far to move
        length = 3;
    
        srand(2);
        pause(5000);
    
        Panel_Start();
    
        Panel_SetPixel(30, 0, 7);
    
        Level = 0;
        doMove(30, 0, 1);
    
        printi("Done\n");
    
    
      while(1)
      {
        // Add main loop code here.
    
      }  
    }
    
    void doMove(int x, int y, int d)
    {
        char possible[3];
        char Possible = 3;
        char j=0;
        char C = 4;
    
        d = 3 - d; // move from direction
        for (i=0;i < 4;i++)
            if (i != d)
                possible[j++] = i;
    
        while (Possible > 0)
        {
            d = rand() % Possible;
            i = doCheck(x, y, possible[d], length);
            if (i == length)
            {
                xx = x;
                yy = y;
                for (i=0;i<length;i++)
                {
                    xx = xx + dirx[possible[d]];
                    yy = yy + diry[possible[d]];
                    Panel_SetPixel(xx, yy, C);
                    pause(15);
                }
                Level++;
                doMove(xx, yy, possible[d]);
                Level--;
            }
            C = C >> 1;
    
            Possible--;
            if (d < Possible)
                possible[d] = possible[Possible];
        }
    }
    
    int doCheck(int x, int y, int d, int a)
    {
        int i;
    
        for (i=0;i<a;i++)
        {
            x = x + dirx[d];
            y = y + diry[d];
            if ((x < 0) || (x > 63))
                break;
            if ((y < 0) || (y > 63))
                break;
            if (Panel_GetPixel(x, y) != 0)
                break;
        }
        return i;
    }
    

    P1 Life Program:

    #include "Panel64.h"
    
    #include "simpletools.h"
    
    char LifeBoard[64][64];
    char LifeBoard2[64][64];
    char Setup[1024];
    
    const char On = 0x07;
    const char Off = 0x00;
    const char RED = 0x04;
    const char BLUE = 0x01;
    
    void DoStart(void);
    void DoMove(void);
    int DoCheck(int, int);
    void DoDraw(void);
    void DoSetup(char*);
    
    
    int main()
    {
        int i;
    
        Panel_Start();
    
        DoSetup("32,32 33,32 34,32 35,32 36,32 32,33 33,33 20,20 21,21 22,21 21,22 22,20 ");
    
        DoDraw();
    
        pause(5000);
    
        i = 0;
        while (1)
        {
            DoDraw();
            pause(250);
            DoMove();
            printi("Gen: %d\n", i++);
        }
    }
    
    void DoMove()
    {
        int t;
    
        for (int i=0;i<64;i++)
        {
            for (int j=0;j<64;j++)
            {
                t = DoCheck(i, j);
                LifeBoard2[i][j] = Off;
                if (LifeBoard[i][j] > 0)
                {
                    if ((t == 2) || (t == 3))
                        LifeBoard2[i][j] = On;
                }
                else
                {
                    if (t == 3)
                        LifeBoard2[i][j] = On;
                }
            }
        }
        for (int i=0;i<64;i++)
            for (int j=0;j<64;j++)
                LifeBoard[i][j] = LifeBoard2[i][j];
    }
    
    int DoCheck(int x, int y)
    {
        int Total = 0;
    
        for (int i=x-1;i<x+2;i++)
        {
            for (int j=y-1;j<y+2;j++)
            {
                if ((i < 0) || (i > 63))
                    continue;
                if ((j < 0) || (j > 63))
                    continue;
                if ((i == x) && (j == y))
                    continue;
                if (LifeBoard[i][j] > 0)
                    Total++;
            }
        }
        return Total;
    }
    
    void DoDraw()
    {
        int t;
        int c;
    
        for (int i=0;i<64;i++)
            for (int j=0;j<64;j++)
            {
                c = LifeBoard[i][j];
                t = Panel_GetPixel(i,j);
                if ((t == 0) && (c > 0))
                    c = BLUE;
                if (c == 0)
                {
                    if ((t == BLUE) || (t == On))
                        c = RED;
                }
                Panel_SetPixel(i,j,c);
            }
    }
    
    void DoSetup(char *positions)
    {
        char value[10];
        int x, y;
        int j;
    
        j = 0;
        x = 0;
        y = 0;
        for (int i=0;i<strlen(positions);i++)
        {
            if (positions[i] == ',')
            {
                x = atoi(value);
                j = 0;
                continue;
            }
            if (positions[i] == ' ')
            {
                y = atoi(value);
                j = 0;
                LifeBoard[x][y] = On;
                continue;
            }
            value[j++] = positions[i];
            value[j] = 0;
        }
    }
    

    The driver is in the attached zip file.

    Mike

Sign In or Register to comment.