Jump to content
Sign in to follow this  
SugarAddict

$4.30 LCD Booster - Sold Out

Recommended Posts

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. :-)

Share this post


Link to post
Share on other sites

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();
}

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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. ;-)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...