Jump to content
43oh

Nokia 5110, '2452, small GUI [got pics!]


Recommended Posts

Just a small thing I've been playing with, has six microswitches and a pushbutton to latch in the instructions.

Four microswitches are connected to the lower nibble of port 2, one on bit7 of port 2, one on bit 4 of port 1, and the pushbutton is on bit 6 of port 2.

post-2374-135135544916_thumb.jpgpost-2374-135135545189_thumb.jpg

 

Available instructions are (CMD bit high):


  • [*:204z8i3q]0x0 (NIBBLE = x)Toggle backlight
    [*:204z8i3q]0x9 (NIBBLE = 1) Move the addr pointer back one char
    [*:204z8i3q]0x9 (NIBBLE = 0) Backspace
    [*:204z8i3q]0xA (NIBBLE = 1) Secondary font set (rearranged ASCII) - to be used with my external
PS/2 keyboard controller
[*:204z8i3q]0xA (NIBBLE = 0) Primary font - normal ASCII
[*:204z8i3q]0xB (NIBBLE = 1) Copy the value in the address char into the input char (I don't know what for)
[*:204z8i3q]0xB (NIBBLE = 0) Copy the value in the input char into the address char (much more useful)
[*:204z8i3q]0xC (NIBBLE = x) Write one char from input buffer
[*:204z8i3q]0xD (NIBBLE = x) Set text Address (14 x 6)
[*:204z8i3q]0xE (NIBBLE = x) Clear bank
[*:204z8i3q]0xF (NIBBLE = x) Clear LCD

 

(CMD bit low):


  • (NIBBLE = 1) Write upper nibble to input buffer
    (NIBBLE = 0) Write lower nibble to input buffer

The char preview is always available.

 

LCD code based on RobG's Nokia 5110

main.cpp

main.cpp

#include "msp430g2452.h"

//P1 I/O
#define LCD5110_SCE_PIN				BIT0
#define LCD5110_DC_PIN				BIT1
#define LCD5110_LED_PIN				BIT2
#define LCD5110_SCLK_PIN			BIT5
#define LCD5110_DN_PIN				BIT6
#define LCD5110_SELECT				P1OUT &= ~LCD5110_SCE_PIN
#define LCD5110_DESELECT			P1OUT |= LCD5110_SCE_PIN
#define LCD5110_SET_COMMAND			P1OUT &= ~LCD5110_DC_PIN
#define LCD5110_SET_DATA			P1OUT |= LCD5110_DC_PIN
#define LCD5110_COMMAND 0
#define LCD5110_DATA 1

//P2 I/O
#define INT 						BIT6
#define CS_INT0 					BIT5
#define CS_INT1 					BIT4

#define SPI_MSB_FIRST USICTL0 &= ~USILSB // or UCA0CTL0 |= UCMSB (USCIA) or USICTL0 &= ~USILSB (USI)
#define SPI_LSB_FIRST USICTL0 |= USILSB // or UCA0CTL0 &= ~UCMSB or USICTL0 |= USILSB (USI)
void writeStringToLCD(const char *string);
void writeCharToLCD(char c);
void writeBlockToLCD(char *byte, unsigned char length);
void writeGraphicToLCD(char *byte, unsigned char transform);
void writeToLCD(unsigned char dataCommand, unsigned char data);
void clearLCD();
void clearBank(unsigned char bank);
void setAddr(unsigned char xAddr, unsigned char yAddr);
void initLCD();

unsigned char currXAddr = 0; //TODO this will be used for tracking current addr
unsigned char currYAddr = 0; //not implemented

//char testBlock[8] = {0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
char testBlock[8] = { 0x00, 0x7F, 0x7F, 0x33, 0x33, 0x03, 0x03, 0x03 };
char testBlock2[8] = { 0x00, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00 };

char smileylarge1[8] = { 0xF0, 0x08, 0x04, 0x02, 0xF1, 0xF1, 0xF1, 0x01 };
char smileylarge2[8] = { 0x0F, 0x10, 0x20, 0x4C, 0x94, 0xA4, 0xA4, 0xA4 };
void printinfo(char* input);

#include "PCD8544.h"

char addr = 0;
char buffer = 0;
char string[4] = {'0', '0', '0', '0'};

const char* fontset = &font[0][0];

void main(void) {

WDTCTL = WDTPW + WDTHOLD; // disable WDT
BCSCTL1 = CALBC1_1MHZ; // 1MHz clock
DCOCTL = CALDCO_1MHZ;

P1OUT |= LCD5110_SCE_PIN;
P1REN |= BIT4;
P1DIR |= LCD5110_SCE_PIN + LCD5110_DC_PIN + LCD5110_LED_PIN;
P2OUT = P2REN = 0x8F | (P2IE = INT|CS_INT0);
P2IES = 0;
P2DIR = P2IFG = P2SEL = 0;

// setup USI
USICTL0 |= USIPE5 | USIPE6 | USIPE7 | USIMST | USIOE;
USICTL0 &= ~USILSB;
USICTL1 |= USICKPH; // + USIIE;
USICKCTL = USISSEL_2;
USICTL0 &= ~USISWRST;
USICNT = 0;

initLCD();
clearLCD();
P1OUT |= LCD5110_LED_PIN;
writeStringToLCD("Ready");
printinfo(0);

_EINT();
LPM0;
}

// /*
void translnibble(char nibble, char *byte) { *byte = "0123456789ABCDEF"[nibble & 0x0F]; }

bool strshrink(char* byte, char* str){
*byte = 0;
for(char i=0; i<2; i++){
	if((str[i] & 0xF0) == 0x30){
		*byte |= (str[i] & 0x0F);
	}
	else if((str[i] & 0xF0) == 0x40){
		*byte |= ((str[i] & 0x0F) + 9);
	}
	else return false;
	if(!i) *byte <<=4;
	if(i && ((*byte < 0x20)||(*byte > 0x7F)))  *byte = ' ';
}
return true;
}

void printinfo(char* input){
const char* box = fontset;
fontset = &font[0][0];
if(input){
	switch((*input>>4) & 0x7){
	case 0:	(*input & BIT7) ? translnibble(*input, string) : translnibble(*input, string+1); break;

	}
}

clearBank(5);
writeStringToLCD("0x00 ' '  0x00");

setAddr((2 * 6), 5);
writeCharToLCD(string[0]);
writeCharToLCD(string[1]);
strshrink(&buffer, string);

setAddr((12 * 6), 5);
writeCharToLCD(string[2]);
writeCharToLCD(string[3]);

setAddr((6 * 6), 5);
translnibble(addr>>4, string+2);
translnibble(addr, string+3);

fontset = box;
writeCharToLCD(buffer);

setAddr(((addr & 0xF) * 6), ((addr>>4) & 0xF));
}

void keyhandler(char command, char value){
if(command == 0x2) { (value & BIT7) ? (command=1) : (command=0); }
if(command){
	switch(value & 0xF){
	case 0x0: P1OUT ^= LCD5110_LED_PIN; break;
	case 0x9: {
		addr--;
		setAddr(((addr & 0xF) * 6), ((addr>>4) & 0xF));
		if(!(value & BIT7)){
			writeCharToLCD(' ');
			setAddr(((addr & 0xF) * 6), ((addr>>4) & 0xF));
		}
	} break;
	case 0xA: {
		const char* box;
		fontset = &font[0][0];
		clearBank(5);
		if(value & BIT7){
			box = &font2[0][0];
			writeStringToLCD("Secondary ");
		}
		else{
			box = &font[0][0];
			writeStringToLCD("Primary ");
		}
		writeStringToLCD("Font");
		__delay_cycles(1000000);
		printinfo(0);
		fontset = box;
		break;
	}
	case 0xB: {
		(value & BIT7) ? (addr=buffer):(buffer=addr);
		printinfo(0);
	} break;
	case 0xC: {
		writeCharToLCD(buffer);
		addr++;
		if((addr & 0xF) > 12){ addr &= ~0x0F; addr += 0x10;}
		if(((addr>>4) & 0xF) > 4){ addr=0;}
		printinfo(0);
	} break;
	case 0xD: setAddr(((buffer & 0xF) * 6), ((buffer>>4) & 0xF)); break;
	case 0xE: {
		clearBank((addr &= ~0xF) & 0x7);
		printinfo(0);
	} break;
	case 0xF: {
		clearLCD();
		addr=0;
		printinfo(0);
	} break;
	}
}
else {
	printinfo(&value);
}
}

char getbyte(char data){
P2OUT &= ~CS_INT0;
__delay_cycles(50);
USISRL = data;
USICNT |= 8;
while (!(USIIFG & USICTL1))
; // wait for SPI flag to trip
P2OUT |= CS_INT0;
return (char) USISRL;
}

#pragma vector=PORT2_VECTOR
__interrupt void inputs(void){
if(P2IFG & INT){
	__delay_cycles(200000);
	char a = (P2IN & 0x8F);
	keyhandler((P1IN & BIT4), a);
}

if(P2IFG & CS_INT0){
	(!(P2IN & CS_INT0));
	keyhandler(2, getbyte(0));
}

P2IFG = 0;
}// */

PCD8544.h

PCD8544.h

	#ifndef PCD8544_H_

#define PCD8544_H_

 

#define PCD8544_POWERDOWN 0x04

#define PCD8544_ENTRYMODE 0x02

#define PCD8544_EXTENDEDINSTRUCTION 0x01

 

#define PCD8544_DISPLAYBLANK 0x0

#define PCD8544_DISPLAYNORMAL 0x4

#define PCD8544_DISPLAYALLON 0x1

#define PCD8544_DISPLAYINVERTED 0x5

 

// H = 0

#define PCD8544_FUNCTIONSET 0x20

#define PCD8544_DISPLAYCONTROL 0x08

#define PCD8544_SETYADDR 0x40

#define PCD8544_SETXADDR 0x80

#define PCD8544_HPIXELS 84

#define PCD8544_VBANKS 6

#define PCD8544_MAXBYTES 504 // PCD8544_HPIXELS * PCD8544_VBANKS

 

// H = 1

#define PCD8544_SETTEMP 0x04

#define PCD8544_SETBIAS 0x10

#define PCD8544_SETVOP 0x80

 

//transform

#define NONE 0x00

#define FLIP_H 0x01

#define FLIP_V 0x02

#define ROTATE 0x04 // 90 deg CW

#define ROTATE_90_CW ROTATE

#define ROTATE_90_CCW (FLIP_H | FLIP_V | ROTATE)

#define ROTATE_180 (FLIP_H | FLIP_V)

 

static const char font[][5] = { // basic font

{0x00, 0x00, 0x00, 0x00, 0x00} // 20

,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !

,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "

,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #

,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $

,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %

,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &

,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '

,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (

,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )

,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *

,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +

,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,

,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -

,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .

,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /

,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0

,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1

,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2

,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3

,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4

,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5

,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6

,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7

,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8

,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9

,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :

,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;

,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <

,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =

,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >

,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?

,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @

,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A

,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B

,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C

,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D

,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E

,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F

,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G

,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H

,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I

,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J

,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K

,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L

,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M

,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N

,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O

,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P

,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q

,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R

,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S

,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T

,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U

,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V

,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W

,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X

,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y

,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z

,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [

,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c

Link to post
Share on other sites

I know, that's the next thing I will add to the above post (maybe video too). I was tired last night, and I knew that if I didn't get this thread started, I'd probably put it off for a few weeks... ;) I'm going to be expanding the description and improving the above code as well.

Link to post
Share on other sites

I would have pics up, but a little bit of flux in between the switches can cause problems...

 

BTW: why won't the following code work? I keep getting the error "expression must have arithmetic or enum type"

 

const char* fontset = &font[0][0];

void writeCharToLCD(char c) {
unsigned char i;
for (i = 0; i < 5; i++) {
	// here's the problem, trying to calculate the correct address
	writeToLCD(LCD5110_DATA, *(fontset * (c - 0x20) + i) ); // was "font[c-0x20][i]);"
}
writeToLCD(LCD5110_DATA, 0);
}

Link to post
Share on other sites

writeToLCD(LCD5110_DATA, fontset[ ( (c - 0x20) * 5) + i ] );

 

If you put a loop inside writeToLCD(), it will reduce code size and improve speed. Take a look at any of the code I have written for 5110/7110 to see how this works.

 

 

The translnibble() function can be reduced to this...

void translnibble(char nibble, char *byte) { *byte = "0123456789ABCDEF"[nibble & 0x0F]; }

Link to post
Share on other sites

Double thanks! That function reduction reduced my code by 150 bytes!

void writeToLCD(unsigned char dataCommand, unsigned char data) {
  LCD5110_SELECT;
  USISRL = data;
  USICNT |= 7;
  while (!(USIIFG & USICTL1))
  ; // wait for SPI flag to trip
  if (dataCommand) {
     LCD5110_SET_DATA;
  } else {
     LCD5110_SET_COMMAND;
  }
  USICNT |= 1;
  while (!(USIIFG & USICTL1))
  ; // wait for SPI flag to trip
  P1OUT |= LCD5110_DC_PIN;
  LCD5110_DESELECT;
}

 

Why would I need another loop here? And how would I add it in? (The serial is done with hardware USI)

 

Or do you mean in the arguments of writeToLCD()?

Link to post
Share on other sites

It is very common to have to send multiple command or data bytes to the LCD. So making a function that can send one or more bytes can eliminate for() loops and multiple write() calls from the rest of the LCD library code. It also reduces the manipulation of enable and data/command lines for a small improvement in efficiency.

 

Here is a function that can send command bytes, data bytes, or repeat a single data byte using USI. (note: untested)

typedef enum {
   lcd_command = 0,        // Array of one or more commands
   lcd_data = 1,           // Array of one or more bytes of data
   lcd_data_repeat = 2     // One byte of data repeated
} lcd_cmd_type;

void lcd_write(const lcd_cmd_type type, const unsigned char *data, unsigned len) {
  P1OUT &= ~LCD5110_SCE_PIN;                       // Enable LCD
  if(type == lcd_command) P1OUT &= ~LCD5110_DC_PIN;// Sending command?
  do {                                             //
      USISRL = *data;                              // Setup SPI data to send
      USICNT |= 8;                                 // Send 8 bits
      if(!(type & 2)) ++data;                      // Increment data pointer if not repeat
      while(!(USIIFG & USICTL1));                  // Wait for all bits to be sent
  } while(--len);                                  // While more data...
  P1OUT |= LCD5110_DC_PIN;                         // Default to data 
  P1OUT |= LCD5110_SCE_PIN;                        // Disable LCD
}                                                   //

 

The various support functions can then be more compact by elimination of for() loops and multiple write() calls...

void lcd_send_font_char(const unsigned char *font, unsigned width, const char c)
{
   // Send one char of a font with specified width (8 bit high)
   // Note: this function assumes the font begins with  character
   lcd_write(lcd_data, font + ((c - ' ') * width), width);
   lcd_write(lcd_data, font, 1);
}

void lcd_home(void)
{
   static const unsigned char home[] = { 0x40, 0x80 };
   lcd_write(lcd_command, home, sizeof(home));
}

void lcd_pos(unsigned char x, unsigned char y)
{
   unsigned char c[2];
   c[0] = 0x80 | x;
   c[1] = 0x40 | y;
   lcd_write(lcd_command, c, sizeof(c));
}

void lcd_clear(void)
{
   static const char x = 0;
   lcd_home();
   lcd_write(lcd_data_repeat, &x, 504);
   lcd_home();
}

void lcd_init(void)
{
   const unsigned char init[] = {
       PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION,
       PCD8544_SETVOP | 0x3F,
       PCD8544_SETTEMP | 0x02,
       PCD8544_SETBIAS | 0x03,
       PCD8544_FUNCTIONSET,
       PCD8544_DISPLAYCONTROL | PCD8544_DISPLAYNORMAL
   };
   lcd_write(lcd_command, init, sizeof(init)); 
}

 

Using the "object_action" function naming makes refactoring for the C++ "object.function" syntax easier.

Link to post
Share on other sites

Consider this code...

void lcd_init(void)
{
   const unsigned char init[] = {
       PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION,
       PCD8544_SETVOP | 0x3F,
       PCD8544_SETTEMP | 0x02,
       PCD8544_SETBIAS | 0x03,
       PCD8544_FUNCTIONSET,
       PCD8544_DISPLAYCONTROL | PCD8544_DISPLAYNORMAL
   };
   lcd_write(lcd_command, init, sizeof(init)); 
}

The init array will be sized based on the array initializer. The write to the LCD uses sizeof() to ensure the exact number of bytes is written. You can edit the array initializer without having to change any code and it will work properly.

 

This is an other way...

void lcd_init(void)
{
   const unsigned char init[8] = {
       PCD8544_FUNCTIONSET | PCD8544_EXTENDEDINSTRUCTION,
       PCD8544_SETVOP | 0x3F,
       PCD8544_SETTEMP | 0x02,
       PCD8544_SETBIAS | 0x03,
       PCD8544_FUNCTIONSET,
       PCD8544_DISPLAYCONTROL | PCD8544_DISPLAYNORMAL
   };
   lcd_write(lcd_command, init, 7); 
}

 

There are two mistakes. The compiler may warn you of one, but not the other.

 

So using sizeof() reduces the chances of making stupid mistakes in array sizing at both initialization and use.

Link to post
Share on other sites

Here's my $0.02 re optimization.

 

If you only have one SPI device attached, you can just select it once (CS) at the beginning and leave it selected. This will save you few clock cycles.

 

Because you send data much more often than command, you can write a separate function for command and for data and toggle data/command pin just in command function. You could even call data function from command function. Another few clock cycles saved.

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.

×
×
  • Create New...