20x2 i2c OLED driver

Memran

Member
Hi everyone :)

I've been working on a mini project to drive the 20x4 OLED display from i2c. I thought about buying a board from China but where's the fun in that?

Using the firmware from the AXE134Y, and hacking it to run on a 20x2, I was able to reproduce the same instruction set as on the Picaxe board, but via i2c :)
One annoying thing about the 20x2 is that C.6 is IN only. I could not use outpinsB, as the i2c pins are there, so I had to use A.0 as a replacement for C.6.
The 3 outputs on the AXE134Y are also working, but there is 4 now. B.0,1,2,3 pins can be set with a hi2cout 0,(255,x) command.
I guess this will still work woth most OLED or LCD displays, but I have no others to test with.

My board is just on stripboard, as I am too lazy to make a PCB right now. It certainly doesn't look anywhere as neat as a 'real' Picaxe board, but it works! :)
If there is interest in it, I will post the code and schematic.
 

Attachments

Memran

Member
Sure :)

Code:
#picaxe 20x2

; ********************************************
; Note you must uncomment just one of these two options
; depending on whether you have an LCD or OLED module
#define use_OLED
;#define use_LCD
; ********************************************

; Supported Commands
; 0-7, 8-15	CGRAM characters
; 16-252	normal ASCII characters, according to selected character map table
; 253, X	display 16 character pre-saved message from EEPROM memory, X can be 0-15
; 254, X	LCD command, X can be 0 to 255 
; 255, X	control outputs C.2, C.1, C.0 (via lower 3 bits of X)
;		So, if using a backlit LCD with the active low transistor driver
;		on output C.2, then 255,%000 switches backlight on and 255,%100 switches off


#define use_welcome	; display the welcome message upon power up
symbol line_length = 16	; change to 20 for displays with 20 character lines

'symbol spare0 	= B.0 ; spare output 0
'symbol spare1 	= B.1 ; spare output 1
'symbol spare2 	= B.2 ; spare output 2
'symbol spare3 	= B.3 ; spare output 3
symbol enable 	= B.4	; LCD enable
symbol rs 		= B.6	; LCD RS 
symbol mode 	= b5	; 0 = character, 1 = command, 2 = message, 3 = outputs

; LCD data pins are on C.0 to C.7, B.6 instead of C.6

; Store the 16 character user defined messages in EEPROM data memory
; First two messages are optionally used as welcome message

; If using a display with 20 characters you will need to edit 
; the start addresses to be multiples of 20 (currently at 16) 
; and add 4 characters to each message.
; Please remember 4 line displays always use the strange 1-3-2-4 layout.

#ifdef use_OLED		
EEPROM $00, ("    i2c OLED    ") 	; store msg in the EEPROM memory
#else
EEPROM $00, ("     i2c LCD    ") 	; store msg in the EEPROM memory
#endif

EEPROM $10, (" www.picaxe.com ") 	; store msg in the EEPROM memory

EEPROM $20, ("This is msg 2   ") 	; store msg in the EEPROM memory
EEPROM $30, ("This is msg 3   ") 	; store msg in the EEPROM memory
EEPROM $40, ("This is msg 4   ") 	; store msg in the EEPROM memory
EEPROM $50, ("This is msg 5   ") 	; store msg in the EEPROM memory
EEPROM $60, ("This is msg 6   ") 	; store msg in the EEPROM memory
EEPROM $70, ("This is msg 7   ") 	; store msg in the EEPROM memory
EEPROM $80, ("This is msg 8   ") 	; store msg in the EEPROM memory
EEPROM $90, ("This is msg 9   ") 	; store msg in the EEPROM memory
EEPROM $A0, ("This is msg 10  ") 	; store msg in the EEPROM memory
EEPROM $B0, ("This is msg 11  ") 	; store msg in the EEPROM memory
EEPROM $C0, ("This is msg 12  ") 	; store msg in the EEPROM memory
EEPROM $D0, ("This is msg 13  ") 	; store msg in the EEPROM memory
EEPROM $E0, ("This is msg 14  ") 	; store msg in the EEPROM memory
EEPROM $F0, ("This is msg 15  ") 	; store msg in the EEPROM memory

gosub LCD_init
	
; display welcome message if desired
#ifdef use_welcome
	low rs	
	let outpinsA = 128 >> 6
	let outpinsC = 128	; Top line
	pulsout enable,1
	let b1 = 0
	gosub msg			; Message #0	
	
	low rs	
	let outpinsA = 192 >> 6
	let outpinsC = 192
	pulsout enable,1		; 2nd line
	let b1 = 1			
	gosub msg			; Message #1
#endif

; Configure the address here
hi2csetup i2cslave, %10100000 'set up slave mode

setintflags %01000000,%01000000 'set up i2c interrupt		

main:
	goto main

interrupt:
	'get digits from scratch
	let bptr = 0
	get bptr,b0
	do while b0 > 0 or mode > 0		
		if b0 < 253 and mode = 0 then
			let outpinsA = b0 >> 6
			let outpinsC = b0		; output the data
			pulsout enable,1  	; pulse the enable pin to send data.
			high rs			; stay in character mode
		else if b0 < 253 and mode = 2 then
			let b1 = b0			; b1 is the message number
			gosub msg
		else if b0 < 253 and mode = 3 then
			let b1 = b0			; b1 is the command param
			gosub out
		else if b0 = 254 then
			low rs 	     		; change to command mode for next character
		else if b0 = 253 then
			let mode = 2		; change to message mode
		else ; must be 255
			let mode = 3		; change output mode
		endif
		
		put bptr,0
		inc bptr
		get bptr,b0
	loop 
	hi2cflag = 0 'reset flag
	setintflags %01000000,%01000000 'reset i2c interrupt
	return
		
LCD_init:
	'set up output pins
	let dirsA = %00000001
	let dirsB = %01011111
	let dirsC = %11111111
	low rs

#ifdef use_OLED
	; Winstar OLED Module Initialisation
	; according to WS0010 datasheet (8 bit mode)

	pause 500 			; Power stabilistation = 500ms

	; Function set - select only one of these 4 character table modes
	;let pinsB = %00111000 	; 8 bit, 2 line, 5x8 , English_Japanese table
	let outpinsC = %00111001 	; 8 bit, 2 line, 5x8 , Western_European table1
	;let pinsB = %00111010 	; 8 bit, 2 line, 5x8 , English_Russian  table
	;let pinsB = %00111011 	; 8 bit, 2 line, 5x8 , Western_European table2
	
	pulsout enable,1  	; 
		
	let outpinsC = %00001100	; Display on, no cursor, no blink
	pulsout enable,1 	

	let outpinsC = %00000001 	; Display Clear
	pulsout enable,1
	pause 7				; Allow 6.2ms to clear display

	setfreq m64				; now change to 64Mhz

	let outpinsC = %00000010 	; Return Home
	pulsout enable,1

	let outpinsC = %00000110 	; Entry Mode, ID=1, SH=0
	pulsout enable, 1
#else
	; Standard LCD Module Initialisation
	pause 15 				; Wait 15ms for LCD to reset.

	let outpinsC = %00110000 	; 8 bit, 2 line
	pulsout enable,1  		; Send data by pulsing enable
	pause 5 				; Wait 5 ms
	pulsout enable,1 	 		; Send data 48 again
	pulsout enable,1  		; Send data 48 again
	
	setfreq m64				; now change to 64Mhz

	let outpinsC = %00111000 	; LCD  - 8 bit, 2 line, 5x8  
	pulsout enable,1
			
	let outpinsC = %00000001	; Clear Display
	pulsout enable,1 	
	pause 8				; 8 = 2ms at 16MHz
	
	let outpinsC = %00000010 	; return home
	pulsout enable,1

	let outpinsC = %00000110	; Entry mode
	pulsout enable,1 	
	pause 1			

	let outpinsC = %00001100	; Display on, no cursor, no blink
	pulsout enable,1 
#endif
	high rs				; Leave in character mode
		
	return

; display message from EEPROM sub routine
; message number 0-15 must be in b1 when called
; uses (alters) b1, b2, b3, b4
msg:
	high rs
	let b2 = b1 * line_length
						; EEPROM start address is 0 to 15 multiplied by 16
	let b3 = b2 + line_length - 1 ; end address is start address + (line_length - 1)
	for b4 = b2 to b3			; for 16 times
		read b4,b0
		let outpinsA = b0 >> 6		
		let outpinsC = b0		; output the data
		pulsout enable,1  	; pulse the enable pin to send data.
	next b4
	let mode = 0			; back to character mode
	return
	
out:
	let outpinsB = b1 & %00001111 ; Set B.0 B.1 B.2 B.3
	high rs
	let mode = 0			; back to character mode
	return

; Check end user has defined just one type of display
#ifndef use_OLED
#ifndef use_LCD
#error "Oops - no OLED / LCD type defined at top of program!"
#endif
#endif

#ifdef use_OLED
#ifdef use_LCD
#error "Oops - both OLED / LCD types defined at top of program!"
#endif
#endif
I've included a schematic, pcb, and stripboard drawing.

I hope some people find this useful! :D
 

Attachments

inglewoodpete

Senior Member
@memran: Well done. I'm in the process of completing similar but slightly larger project, so I was very interested in how you went about it.

One point of concern is your use of the lower RAM in the interrupt routine. You are possibly not aware that byte registers b0 to b55 map to RAM locations 0 to 55. You are referencing these same locations using "bPtr", which could cause unexpected results if you try to use the same register/memory locations elsewhere in the code.

As an example of the confusing code (you're lucky that you did not tread on your own toes :)):
Code:
	let bptr = 0     'Set byte pointer to start of RAM (Ie b0)
	get bptr,b0      'Load the contents of b0 into b0
If would be far safer to transfer data from the scratchpad into higher RAM Eg byte 56 upwards.
 

Memran

Member
Thanks for your comment :)

I agree, at first glance it does look a bit odd, but don't forget that the pointer stuff is inside a loop, and that only b0 is used to output the data to the display (b1,b2,b3,b4 are only used in the pre-defined message section, which has its own loop). Next time around, the pointer will be 2 (b1) and it copies that to b0, then 3 (b2) etc. The loop continues until b0 is 0 (no more data).

There are probably a few optimizations to do, but the above was intentional. :)
 
Top