campbellsan 24 Posted May 11, 2012 Share Posted May 11, 2012 Hi, Got my boards a few days a go. Powered one up and it works a treat! Nice work SA. Hey, is anyone working on a graphics library? I noted RobG mentioned it somewhere, but I didn't see any other activity. If no one is actively progressing this, I'm up for it. Looking over the data sheet, implementing dot addressed graphics is going to be a challenge, because when addressing the board in serial mode it appears you cannot read the SED1565 display memory. There are more dots on the screen than there are bits in the G2553's SRAM which poses a problem if you can't read the current state of the display memory. Graphics data is refreshed in byte chunks, so you need to know what is already there when adding new points (if you want to plot two crossing lines for example). Now, I'm not going to let a little thing like that stop me. But if someone else is already working on it, HOLLER and I'll stop. I don't want to spoil someone else's party. :-) tom_launch_bav and GeekDoc 2 Quote Link to post Share on other sites
campbellsan 24 Posted May 15, 2012 Share Posted May 15, 2012 Ok, good progress. I have a library that will plot lines, circles and text across the whole display. To use it, you make calls to draw_line, draw_text etc. and then call render. When render is called the resulting image is plotted to the screen. You can't make arbitrary draw calls straight to the screen for the reasons given earlier; it is not possible to read the controllers display memory, so plotting has to be done in SRAM before writing the data to the display. If we had enough SRAM, direct plotting would be possible. However 512 bytes in a MS430G2553 is only enough to cover less than 43 lines out of the possible 65 display lines and in any case, we need SRAM for variables and stack. Instead, I keep a command list and use a smaller buffer of 96 bytes. When you call a draw command, it is added to the command list. When render is called, the commands are plotted into the 96 byte buffer and written to the display. This is repeated 8 times to cover the whole display. This isn't CPU efficient, but it is SRAM efficient. With a little more work, the code could be enhanced so that you can trade off the size of the buffer against the number of times the renderer has to repeat the plotting behaviour and write the data. CPU efficiency aside, it manages 16 frames a second plotting a clock face. I have implemented some optimizations in the drawing algorithms so that overhead is reduced if a particular graphic has no presence in a particular strip of the output buffer, but more could be done. Another tradeoff which limits the complexity of the image is the size of the command buffer, this requires 5 bytes per command. I have thought about SRAM usage here too and provided an extended render command that allows the user to specify pre and post plot command lists which can be anywhere in memory, including flash. So you can plot a static background, some dynamic graphics and a static foreground. Of course, the back and foreground can be semi static, since you don't have to point at the same static command lists for every render_extended call. I found that the pixels are not square, so circles come out as ellipses. I added a y axis scaling factor to rectify this. You will be able to turn this off at compile time if you don't want it, but it is quite quick. Text output is not scaled so that the fonts look as they were intended to. Apart from the SRAM/CPU tradeoff enhancement, I was thinking of adding a simple draw_point command, an XOR drawing mode and shape fill capabilities. Please let me know what you would like to see. Library source: /* * sed1565gr.c * * Created on: May 12, 2012 * Author: campbell */ #include "msp430g2553.h" #include "sed1565gr.h" #include "sed1565.h" #include "stddef.h" #include"msp_intrinsics.h" #include"font_5x7.h" #include #include #define MAX_COMMANDS 10 #define BUFFER_SIZE 96 #define PAGE_SIZE 8 #define NUM_PAGES 9 // private data GR_CMD dynamic_command_list[MAX_COMMANDS]; char buffer[bUFFER_SIZE]; int cmd_index = 0; int extent_y; // private functions int buffers_remain(void); void prepare_context(void); void perform(GR_CMD command); void write_lcd(); void perform_line(DRAW_LINE_CMD* cmd); void perform_circle(DRAW_CIRCLE_CMD* cmd); void perform_text(DRAW_TEXT_CMD* cmd); void inline plot(int x, int y); void inline plot_raw(int x, int y); int abs(int arg); void clear() { cmd_index = 0; } void draw_line(char start_x, char start_y, char end_x, char end_y) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_LINE; dynamic_command_list[cmd_index].dlc.start_x = start_x; dynamic_command_list[cmd_index].dlc.start_y = start_y; dynamic_command_list[cmd_index].dlc.end_x = end_x; dynamic_command_list[cmd_index].dlc.end_y = end_y; cmd_index++; } } void draw_circle(char center_x, char center_y, char radius) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_CIRCLE; dynamic_command_list[cmd_index].dcc.center_x = center_x; dynamic_command_list[cmd_index].dcc.center_y = center_y; dynamic_command_list[cmd_index].dcc.radius = radius; cmd_index++; } } void draw_text(char x, char y, char *str) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_TEXT; dynamic_command_list[cmd_index].dtc.x = x; dynamic_command_list[cmd_index].dtc.y = y; dynamic_command_list[cmd_index].dtc.string = str; cmd_index++; } } void render() { render_extended(0, NULL, 0, NULL); } int bfr_indx; void render_extended(char pre_cmd_count, GR_CMD* pre_cmd_list, char post_cmd_count, GR_CMD* post_cmd_list) { prepare_context(); while (buffers_remain()) { bfr_indx = 0; // First render the passed command list, if one was passed while (bfr_indx < pre_cmd_count) { perform(pre_cmd_list[bfr_indx++]); } // Now render the dynamic command list bfr_indx = 0; while (bfr_indx < cmd_index) { perform(dynamic_command_list[bfr_indx++]); } // Now render the second passed command list, if there is one bfr_indx = 0; while (bfr_indx < post_cmd_count) { perform(post_cmd_list[bfr_indx++]); } write_lcd(); } clear(); } void perform(GR_CMD command) { switch (command.cmd) { case CMD_LINE: perform_line(&(command.dlc)); break; case CMD_CIRCLE: perform_circle(&(command.dcc)); break; case CMD_TEXT: perform_text(&(command.dtc)); break; default: //error case, halt break; } } int inline abs(int arg) { if (arg > 0) { return arg; } else { return -arg; } } void inline swap(int *a, int * { int temp = *a; *a = *b; *b = temp; } void perform_line(DRAW_LINE_CMD* cmd) { // Bresenham line algorithm int dx = (cmd->end_x - cmd->start_x); int dy = (cmd->end_y - cmd->start_y); int x = cmd->start_x; int y = cmd->start_y; int endx = cmd->end_x; int endy = cmd->end_y; plot(x, y); if (abs(dx) > abs(dy)) { if (x > endx) { swap(&x, &endx); swap(&y, &endy); dx = -dx; dy = -dy; } int p = (dy - dx) << 1; while (x <= endx) { if (p < 0) { x = x + 1; p = p + (dy << 1); } else { x = x + 1; y = y + 1; p = p + ((dy - dx) << 1); } plot(x, y); } } else { if (y > endy) { swap(&x, &endx); swap(&y, &endy); dx = -dx; dy = -dy; } int p = (dx - dy) << 1; while (y <= endy) { if (p < 0) { y = y + 1; p = p + (dx << 1); } else { x = x + 1; y = y + 1; p = p + ((dx - dy) << 1); } plot(x, y); } } } void perform_circle(DRAW_CIRCLE_CMD* cmd) { int x = 0; int y = cmd->radius; plot(cmd->center_x + x, cmd->center_y - y); int p = 3 - (cmd->radius << 1); for (x = 0; x <= y; x++) { if (p < 0) { p = (p + (x << 2) + 6); } else { y = y - 1; p = p + ((((x - y) << 2) + 10)); } plot(cmd->center_x + x, cmd->center_y - y); plot(cmd->center_x - x, cmd->center_y - y); plot(cmd->center_x + x, cmd->center_y + y); plot(cmd->center_x - x, cmd->center_y + y); plot(cmd->center_x + y, cmd->center_y - x); plot(cmd->center_x - y, cmd->center_y - x); plot(cmd->center_x + y, cmd->center_y + x); plot(cmd->center_x - y, cmd->center_y + x); } } void perform_text(DRAW_TEXT_CMD* cmd) { if (cmd->y >= extent_y - 7 && cmd->y < extent_y + PAGE_SIZE) { char * str = cmd->string; int col = 0; while (*str) { char c = *str++; int char_col = 0; while (char_col < 5) { char slice = font[c - 0x20][char_col]; int row = 0; while (row < 7) { if (slice & 0x01) { plot_raw(cmd->x + col, cmd->y + row); } slice = slice >> 1; row++; } char_col++; col++; } col++; } } } void inline plot(int x, int y) { #ifndef DONT_SCALE y -= (y >> 3); #endif plot_raw(x, y); } void inline plot_raw(int x, int y) { if (y >= extent_y && y < (extent_y + PAGE_SIZE)) { buffer[x] |= 1 << (y - extent_y); } } void prepare_context() { extent_y = -PAGE_SIZE; } void write_lcd() { int i = 0; // Set column to 0x12, the left hand edge of the LCD writeCommand(0x02); // Column Address LSN writeCommand(0x11); // Column Address HSN writeCommand(0xB0 | (extent_y >> 3)); // Page Address for (; i < BUFFER_SIZE; i++) { writeData(buffer[i]); } } int buffers_remain() { memset(buffer, 0, BUFFER_SIZE); extent_y += PAGE_SIZE; return NUM_PAGES - (extent_y >> 3); } void initLCD() { P2OUT |= LCD_ON_PIN + LCD_BL_PIN; _delay_cycles(0xffff); P1OUT |= LCD_RESET_PIN; writeCommand(DISPLAY_REVERSE); writeCommand(LCD_BIAS_1_7); writeCommand(ADC_SELECT_REVERSE); writeCommand(COMMON_OUTPUT_MODE_NORMAL); writeCommand(V5_VOLTAGE_REG_RATIO | 2); writeCommand(ELECTRONIC_VOLUME); writeCommand(0x2E); // 2E writeCommand(POWER_CONTROLLER_ALL_ON); writeCommand(0x40); // display start line 0 writeCommand(DISPLAY_ON); } void write(char data, char registerSelect) { char c = 0; LCD_SELECT; registerSelect ? (SET_DATA) : (SET_COMMAND); while (c < 8) { (data & BIT7) ? (P2OUT |= LCD_SDATA_PIN) : (P2OUT &= ~LCD_SDATA_PIN); LCD_CLOCK; data <<= 1; c++; } LCD_DESELECT; } Library Header: /* * sed1565gr.h * * Created on: May 12, 2012 * Author: campbell */ #ifndef SED1565GR_H_ #define SED1565GR_H_ #include "sed1565.h" #include enum CMD { CMD_LINE, CMD_CIRCLE, CMD_ELLIPSE, CMD_TEXT }; typedef struct { char start_x; char start_y; char end_x; char end_y; } DRAW_LINE_CMD; typedef struct { char center_x; char center_y; char radius; } DRAW_CIRCLE_CMD; typedef struct { char center_x; char center_y; char focus_a; char focus_b; } DRAW_ELLIPSE_CMD; typedef struct { char x; char y; char * string; } DRAW_TEXT_CMD; union { DRAW_LINE_CMD dlc; DRAW_CIRCLE_CMD dcc; DRAW_ELLIPSE_CMD dec; DRAW_TEXT_CMD dtc; } DRAW_CMD_DATA; typedef struct { enum CMD cmd; union { DRAW_LINE_CMD dlc; DRAW_CIRCLE_CMD dcc; DRAW_ELLIPSE_CMD dec; DRAW_TEXT_CMD dtc; }; } GR_CMD; void clear(); void draw_line(char start_x, char start_y, char end_x, char end_y); void draw_circle(char center_x, char center_y, char radius); void draw_ellipse(char center_x, char center_y, char focus_a, char focus_; void draw_text(char x, char y, char * str); void render(); void render_extended(char pre_cmd_count, GR_CMD* pre_commmand_list, char post_cmd_count, GR_CMD* post_command_list); #endif /* SED1565GR_H_ */ Example main: /* * main.c */ #include "msp430g2553.h" #include "msp_intrinsics.h" #include #include "sed1565gr.h" char * msg = "www.43oh.com"; void main(void) { WDTCTL = WDTPW + WDTHOLD; // disable WDT BCSCTL1 = CALBC1_16MHZ; // 16MHz clock DCOCTL = CALDCO_16MHZ; P1OUT &= ~LCD_RESET_PIN; // keep low until init P1DIR |= LCD_RESET_PIN; P2OUT |= LCD_SCLK_PIN + LCD_CS_PIN + LCD_SDATA_PIN + LCD_DC_PIN + LCD_ON_PIN + LCD_BL_PIN; P2DIR |= LCD_SCLK_PIN + LCD_CS_PIN + LCD_SDATA_PIN + LCD_DC_PIN + LCD_ON_PIN + LCD_BL_PIN; initLCD(); draw_circle(48, 37, 36); draw_line(48, 38, 46, 5); draw_line(48, 38, 60, 55); draw_text(14, 48, msg); render(); } RobG and woodgrain 2 Quote Link to post Share on other sites
bluehash 1,581 Posted May 15, 2012 Share Posted May 15, 2012 Good stuff, good stuff. So, only one more question, where is the best place to put this code on the site? A few ways: 1. Attach it to your post. in this thread. This is the best. I'll create a link in the first post of this thread to this post. 2. Upload to github. Link it here 3. Upload to the code vault sub-forum. Quote Link to post Share on other sites
campbellsan 24 Posted May 15, 2012 Share Posted May 15, 2012 B#, I posted the code above as you suggested. I'd like to see it go into version control because I see more work being done on it and collaboration is more fun under a CM system. Note that this library relies on RobG's sed1565.h file available in the Code Vault. Thanks Rob. It does not rely on the sed1565.c file because I shamelessly plagiarised the parts of his excellent work I needed. Thanks Rob. This library also relies for text rendering on the font5x7.h file also available in the Code Vault. Another enhancement that occurs to me is to support all the fonts that I saw in there and have a font switch command, but that might start getting greedy on flash. Let's see where this goes. Enjoy! BTW, I'm sure no one expects any, but just in case, no warranties and all that usual stuff.... if heart monitor development is what you're doing with this, you're on your own. ;-) woodgrain and RobG 2 Quote Link to post Share on other sites
bluehash 1,581 Posted May 15, 2012 Share Posted May 15, 2012 I'd like to see it go into version control because I see more work being done on it and collaboration is more fun under a CM system. Normally people post their github link. You are free to do that. People can then fork it if needed. The above post works too. Quote Link to post Share on other sites
Rei Vilo 695 Posted May 26, 2012 Share Posted May 26, 2012 Great booster pack! Congratulations and thanks Quote Link to post Share on other sites
thesaxmachine 0 Posted May 29, 2012 Share Posted May 29, 2012 SugarAddict, Is it possible to get the Eagle footprint from you. I got some of the LCDs from DipMicro before they were sold out. Just looking for a better solution to connecting to the LCD besides soldering wires to the connector. Thanks Quote Link to post Share on other sites
SugarAddict 227 Posted May 29, 2012 Author Share Posted May 29, 2012 You have LCD's? Nice... I have some spare boards and a few of the prototype spacers left. PM me your address! I'll toss up the footprint when I get a chance tonight. Quote Link to post Share on other sites
thesaxmachine 0 Posted May 29, 2012 Share Posted May 29, 2012 PM sent Quote Link to post Share on other sites
SugarAddict 227 Posted May 29, 2012 Author Share Posted May 29, 2012 I'm too lazy today to copy it into it's own library... There's some other odds and ends in this lib (one of the few I've got going...). Enjoy (It's got the layout of the spacer on it, which is a good guideline of the LCD size/shape) 43oh-SugarAddict.lbr GeekDoc, bluehash and thesaxmachine 3 Quote Link to post Share on other sites
bluehash 1,581 Posted May 30, 2012 Share Posted May 30, 2012 hahaha! @ your sig. Quote Link to post Share on other sites
SugarAddict 227 Posted May 30, 2012 Author Share Posted May 30, 2012 You just now notice that bluehash? LOL!! Quote Link to post Share on other sites
dvdmiller90 0 Posted December 6, 2012 Share Posted December 6, 2012 Any ideas whether this BoosterPack is in stock again? Quote Link to post Share on other sites
Rei Vilo 695 Posted December 13, 2012 Share Posted December 13, 2012 I just ported the library for the Stellaris LaunchPad (=StellarPad) on Energia. The LCD BoosterPack works like a charm on the StellarPad. Are there plans to build another batch? Quote Link to post Share on other sites
pkonzak 1 Posted March 19, 2014 Share Posted March 19, 2014 Finally opened my LCD and found a dollar. Thanks SA. Now I need to finally get around to building my controller. Thanks for the great job and the cheap price. pine 1 Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.