32x64 3-color message board http://elektorembedded.blogspot.com/

Dependencies:   mbed

main.cpp

Committer:
Clemo
Date:
2010-05-05
Revision:
0:7a63bd42cf24

File content as of revision 0:7a63bd42cf24:

#include "mbed.h"
#include "font.h"
#include "arial_8pt.h"
#include "ini.h"
#include <ctype.h>


#define ROWS  16 /* Two rows are drawn "in parallel". */
#define COLUMNS  64

typedef enum
{
  kOrange = 0,
  kGreen = 1,
  kRed = 2,
  kBlack = 3
}
color_t;

typedef struct
{
  unsigned char pixel[COLUMNS]; // Contains data for 2 rows!
}
row_t;

typedef struct
{
  int rows;
  int columns;
  row_t row[ROWS];
}
matrix_t;

matrix_t Panel;

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

DigitalOut Enable(p5); // enable
DigitalOut Clock(p15); // clock per bit
DigitalOut Latch(p14); // latch per line
DigitalOut R1(p6); // Red upper half
DigitalOut R2(p7); // Red lower half
DigitalOut G1(p12); // Green upper half
DigitalOut G2(p13); // Green lower half
BusOut Row(p8,p9,p10,p11);

DigitalIn Switch(p20); // Day selection switch.

#define PANEL_ON  Enable = 0
#define PANEL_OFF  Enable = 1
#define CLOCK_DATA  Clock=0; Clock=1
#define LATCH_DATA  Latch=0; Latch=1

Serial PC(USBTX,USBRX); // USB serial port.
LocalFileSystem local("local"); // File system.
const char *ini_file = "/local/pages.ini";

#define MAX_LINES  (4)
#define MAX_LINE_LENGTH  (128) /* Use very long lines for easy scrolling, we have plenty of memory. */
#define MAX_PAGES  (7)
#define SCROLL_GAP  (24)  /* in pixels */


typedef struct
{
  char str[MAX_LINE_LENGTH];
  color_t color;
  int x;
  int y;
  int scroll;  // <0=left, 0=no scroll, >0=right
  int strlen_pixels; // String length in pixels.
}
line_t;

typedef struct
{
  line_t line[MAX_LINES];
  int hide;
}
page_entry_t;

page_entry_t pages[MAX_PAGES];


struct
{
  float scroll_speed;
  float brightness;
}
app_data;


void led_sweep();
void panel_load(unsigned char *p_pixel, int nr_of_pixels);
void panel_refresh_row(int nr, row_t *p_pixel_data, int nr_of_pixels);
void panel_refresh(matrix_t *p_panel);
void panel_init(matrix_t& panel, int rows, int columns);
void panel_fill_row(row_t *p_row, unsigned char value, int count);
void panel_fill_matrix(matrix_t& panel, unsigned char value);
void put_pixel(matrix_t& panel, unsigned int x, unsigned int y, color_t color);
int put_char_xy(matrix_t& panel, char ch, unsigned int x, unsigned int y, const FONT_INFO *p_font, color_t color);
int put_string_xy(matrix_t& panel, char *p_str, unsigned int x, unsigned int y, const FONT_INFO *p_font, color_t color);
int measure_string(char *p_str, const FONT_INFO *p_font);
void clear_display(void);
int is_valid_char(char ch);
color_t str_to_color(const char *p_str);
int ini_file_handler(void* user, const char* section, const char* name, const char* value);
void page_pre_init(void);
void page_post_init(const FONT_INFO *p_font);
int page_next(int page);
int page_show(matrix_t& panel, int p);


void led_sweep()
{
  led1 = 0;
  led2 = 0;
  led3 = 0;
  led4 = 0;
  led1 = 1;
  wait(0.05);
  led1 = 0;
  led2 = 1;
  wait(0.05);
  led2 = 0;
  led3 = 1;
  wait(0.05);
  led3 = 0;
  led4 = 1;
  wait(0.05);
  led4 = 0;
}


// Clock the data into the display.
// This is where the bit-banging happens.
void panel_load(unsigned char *p_pixel, int nr_of_pixels)
{
  int i;
  for (i=0; i<nr_of_pixels; i++)
  {
    unsigned char pixel = p_pixel[i];
    R1 = pixel & 0x01;
    pixel >>= 1;
    G1 = pixel & 0x01;
    pixel >>= 1;
    R2 = pixel & 0x01;
    pixel >>= 1;
    G2 = pixel & 0x01;
    CLOCK_DATA;
  }
  LATCH_DATA;
}


void panel_refresh_row(int nr, row_t *p_pixel_data, int nr_of_pixels)
{
  Row = nr;
  panel_load(p_pixel_data->pixel,nr_of_pixels);
  PANEL_ON;
  wait(app_data.brightness);
  PANEL_OFF;
}


void panel_refresh(matrix_t *p_panel)
{
  int i;
  for (i=0; i<p_panel->rows; i++)
  {
    panel_refresh_row(i,&p_panel->row[i],p_panel->columns);
  }
}


void panel_init(matrix_t& panel, int rows, int columns)
{
  CLOCK_DATA; // Init the clock line
  LATCH_DATA; // Reset the shift registers
  panel.rows = rows;
  panel.columns = columns;
  panel_fill_matrix(panel,kBlack); // Clear display.
}


void panel_fill_row(row_t *p_row, unsigned char value, int count)
{
  int i;
  for (i=0; i<count; i++)
  {
    p_row->pixel[i] = value;
  }
}


void panel_fill_matrix(matrix_t& panel, unsigned char value)
{
  int i;
  for (i=0; i<panel.rows; i++)
  {
    // Two rows are stored in one row...
    panel_fill_row(&panel.row[i],(value<<2)+value,panel.columns);
  }
}


void put_pixel(matrix_t& panel, unsigned int x, unsigned int y, color_t color)
{
  unsigned char mask;
  unsigned char c;

  if (x<panel.columns && y<2*panel.rows)
  {
    mask = 0x03;
    c = color;
    if (y>=panel.rows)
    {
      // Remember: two rows are stored in one.
      y -= panel.rows;
      c <<= 2;
      mask <<= 2;
    }
    c |= ~mask; // Do not overwrite the other pixel.
    panel.row[y].pixel[x] |= mask; // Clear pixel (pixels are active low!).
    panel.row[y].pixel[x] &= c; // Add in the zeroes.
  }
}


int put_char_xy(matrix_t& panel, char ch, unsigned int x, unsigned int y, const FONT_INFO *p_font, color_t color)
{
  int i, h, w;
  if (p_font!=NULL)
  {
    i = ch - p_font->start_char;
    int width = p_font->p_character_descriptor[i].width;
    int second_byte = 0;
    if (width>8)
    {
      // Some wide characters are coded in two bytes.
      second_byte = width - 8;
      width = 8;
    }
    int offset = p_font->p_character_descriptor[i].offset;
    const uint8_t *p_char = p_font->p_character_bitmaps + offset;
    uint8_t mask;
    for (h=0; h<p_font->height; h++)
    {
      // Plot pixels for first byte.
      mask = 0x80;
      for (w=0; w<width; w++)
      {
        if ((*p_char&mask)!=0) put_pixel(panel,x+w,y+h,color);
        mask >>= 1;
      }
      if (second_byte>0)
      {
        // Handle 2nd byte of extra wide characters.
        p_char += 1;
        mask = 0x80;
        for (w=0; w<second_byte; w++)
        {
          if ((*p_char&mask)!=0) put_pixel(panel,x+w+8,y+h,color);
          mask >>= 1;
        }
      }
      p_char += 1;
    }
    return p_font->p_character_descriptor[i].width;
  }
  return 0;
}


int put_string_xy(matrix_t& panel, char *p_str, unsigned int x, unsigned int y, const FONT_INFO *p_font, color_t color)
{
  int _x = 0;
  while (*p_str!=0)
  {
    _x += put_char_xy(panel,*p_str,x+_x,y,p_font,color);
    _x += 1;
    p_str += 1;
  }
  return _x>0? _x-1 : 0;
}


// Return the length of a string in pixels when printed using the provided font.
int measure_string(char *p_str, const FONT_INFO *p_font)
{
  int i;
  int strlen_pixels = 0;
  if (p_font!=NULL)
  {
    while (*p_str!=0)
    {
      i = *p_str - p_font->start_char;
      strlen_pixels += p_font->p_character_descriptor[i].width;
      strlen_pixels += 1;
      p_str += 1;
    }
  }
  return strlen_pixels;
}


void clear_display(void)
{
  panel_fill_matrix(Panel,kBlack);
}


int is_valid_char(char ch)
{
  return (ch>=' ' && ch<='~');
}

#define IS_SECTION(a)  if (strcmp(p_section,(a))==0)
#define IS_NAME(a)  if (strcmp(p_name,(a))==0)
#define IS_VALUE(a)  if (strcmp(p_value,(a))==0)

color_t str_to_color(const char *p_str)
{
  color_t color = kGreen;
  if (strcmp(p_str,"green")==0) color = kGreen;
  else if (strcmp(p_str,"orange")==0) color = kOrange;
  else if (strcmp(p_str,"red")==0) color = kRed;
  else if (strcmp(p_str,"black")==0) color = kBlack;
  return color;
}


int ini_file_handler(void *p_user, const char *p_section, const char *p_name, const char *p_value)
{
  // Called from ini.c
  
  int p = 0;
  
  IS_SECTION("global")
  {
    IS_NAME("brightness") app_data.brightness = atof(p_value)/10000.0;
    else IS_NAME("speed") app_data.scroll_speed = atof(p_value)/1000.0;
  }
  else IS_SECTION("page1") p = 0;
  else IS_SECTION("page2") p = 1;
  else IS_SECTION("page3") p = 2;
  else IS_SECTION("page4") p = 3;
  else IS_SECTION("page5") p = 4;
  else IS_SECTION("page6") p = 5;
  else IS_SECTION("page7") p = 6;

       IS_NAME("hide") pages[p].hide = atoi(p_value);
  else IS_NAME("text1") strcpy(pages[p].line[0].str,p_value);
  else IS_NAME("text2") strcpy(pages[p].line[1].str,p_value);
  else IS_NAME("text3") strcpy(pages[p].line[2].str,p_value);
  else IS_NAME("text4") strcpy(pages[p].line[3].str,p_value);
  else IS_NAME("color1") pages[p].line[0].color = str_to_color(p_value);
  else IS_NAME("color2") pages[p].line[1].color = str_to_color(p_value);
  else IS_NAME("color3") pages[p].line[2].color = str_to_color(p_value);
  else IS_NAME("color4") pages[p].line[3].color = str_to_color(p_value);
  else IS_NAME("x1") pages[p].line[0].x = atoi(p_value);
  else IS_NAME("x2") pages[p].line[1].x = atoi(p_value);
  else IS_NAME("x3") pages[p].line[2].x = atoi(p_value);
  else IS_NAME("x4") pages[p].line[3].x = atoi(p_value);
  else IS_NAME("y1") pages[p].line[0].y = atoi(p_value);
  else IS_NAME("y2") pages[p].line[1].y = atoi(p_value);
  else IS_NAME("y3") pages[p].line[2].y = atoi(p_value);
  else IS_NAME("y4") pages[p].line[3].y = atoi(p_value);
  else IS_NAME("scroll1") pages[p].line[0].scroll = atoi(p_value);
  else IS_NAME("scroll2") pages[p].line[1].scroll = atoi(p_value);
  else IS_NAME("scroll3") pages[p].line[2].scroll = atoi(p_value);
  else IS_NAME("scroll4") pages[p].line[3].scroll = atoi(p_value);

  // Return 0 on error.
  return 1;
}


void page_pre_init(void)
{
  int i, j;
  int y;
  // Set zero default values.
  memset(&pages,0,sizeof(pages));
  for (j=0; j<MAX_PAGES; j++)
  {
    y = 0;
    for (i=0; i<MAX_LINES; i++)
    {
      // Set non-zero default values.
      pages[j].line[i].color = kBlack;
      pages[j].line[i].y = y;
      y += 8;
    }
  }
}


void page_post_init(const FONT_INFO *p_font)
{
  int i, j;
  for (j=0; j<MAX_PAGES; j++)
  {
    for (i=0; i<MAX_LINES; i++)
    {
      // We need to know the length in pixels of each string.
      pages[j].line[i].strlen_pixels = measure_string(pages[j].line[i].str,p_font);
    }
  }
}


int page_next(int page)
{
  page += 1;
  if (page<0) page = MAX_PAGES - 1;
  else if (page>=MAX_PAGES) page = 0;
  return page;
}


int page_show(matrix_t& panel, int p)
{
  int i;
  clear_display();
  // Show page if not hidden.
  if (pages[p].hide==0)
  {
    for (i=0; i<MAX_LINES; i++)
    {
      int x = pages[p].line[i].x;
      // Print the string.
      put_string_xy(panel,pages[p].line[i].str,x,pages[p].line[i].y,&arial_8pt_font_info,pages[p].line[i].color);
      // Handle scrolling.
      if (pages[p].line[i].scroll!=0)
      {
        // Simply print the string a second time, display clipping will prevent 
        // artifacts when a string is partly printed off-screen.
        if (pages[p].line[i].scroll<0)
        {
          // Scroll to the left.
          x += (pages[p].line[i].strlen_pixels + SCROLL_GAP);
          // Reset the starting point when the string is completely off screen.
          if (pages[p].line[i].x+pages[p].line[i].strlen_pixels<0) pages[p].line[i].x = x;
        }
        else if (pages[p].line[i].scroll>0)
        {
          // Scroll to the right.
          x -= (pages[p].line[i].strlen_pixels + SCROLL_GAP);
          // Reset the starting point when the string is completely off screen.
          if (pages[p].line[i].x>=COLUMNS) pages[p].line[i].x = x;
        }
        put_string_xy(panel,pages[p].line[i].str,x,pages[p].line[i].y,&arial_8pt_font_info,pages[p].line[i].color);
        
        // Update x position.
        pages[p].line[i].x += pages[p].line[i].scroll;
      }
    }
  }
  else p = page_next(p); // Page is hidden, move on to the next page.

  return p;
}


int main() 
{
  int page = 0;
  int debounce = 0;
  const FONT_INFO *p_font = &arial_8pt_font_info;

  PC.printf("\nmbed LED panel experiments\n");
  led_sweep();

  led1 = 0;
  led2 = 0;
  led3 = 0;
  led4 = 0;
  
  // Set some default values.
  app_data.brightness = 0.0005;
  app_data.scroll_speed = 0.001;

  // Setup display.
  PANEL_OFF;
  panel_init(Panel,ROWS,COLUMNS);

  // Read page data.
  page_pre_init();
  int error = false;
  error = ini_parse(ini_file,ini_file_handler,0);
  page_post_init(p_font);
  
  //sprintf(pages[0].line[0].str,"%0.3f",app_data.scroll_speed);
  
  while (1)
  {
    if (error==true)
    {
      put_string_xy(Panel,"file not found",0,0,p_font,kRed);
    }
    else
    {
      page = page_show(Panel,page);
    }

    // Handle next-page switch.
    if (Switch==1)
    {
      if (debounce<10) debounce += 1;
      else if (debounce==10)
      {
        debounce += 1;
        page = page_next(page);
        clear_display();
      }
    }
    else debounce = 0;
            
    /*if (PC.readable()) 
    {
      int ch;
      ch = PC.getc();
      PC.putc(ch);
      if (ch=='B') color = kBlack;
      else if (ch=='R') color = kRed;
      else if (ch=='O') color = kOrange;
      else if (ch=='G') color = kGreen;
      else if (ch=='x') x -= 1;
      else if (ch=='X') x += 1;
      else if (ch=='y') y -= 1;
      else if (ch=='Y') y += 1;
      PC.printf("\tx=%d, y=%d, color=%d\n",x,y,color);
      clear_display();
    }*/
    
    panel_refresh(&Panel); // Call regularly!
    wait(app_data.scroll_speed); // Slow down, but not too much.
  }
}