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

Dependencies:   mbed

Revision:
0:7a63bd42cf24
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed May 05 12:04:34 2010 +0000
@@ -0,0 +1,543 @@
+#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.
+  }
+}