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