Clemens Valens
/
LED_panel
32x64 3-color message board http://elektorembedded.blogspot.com/
Diff: main.cpp
- 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. + } +}