Skip to content

Olduino/Z with the Colour LCD

 

This is just the same code I ran on the 1802 for this LCD but I think it’s noteworthy that it IS the same code.  Not exactly, of course, there are some assembly language helpers in each case but that’s almost all in the hspi3 library and controlled by the __CPUTYPE defs.  The other (I think) neat thing is how compatible the hardware is.  In the two left pictures below I’ve just moved the signal and power leads from the 1802 Olduino to exactly the same spots on the Olduino/Z.  I may not be brilliant but I am doggedly persistent.

I’m going to work at getting a web client and text output working bilingually on the two processors and use it for the hack-a-day retro edition.

/*==========================================================================
The LCD connection is the same as Nokia LCD5110 and  is a“8 Bit Pant Demo“

Just for ElecFreaks TFT01-2.2SP, which use SPI serial port and 240x320 pixel.
The driver is ILI9341.

by Elecfreaks

16-04-12 adapting for olduino/Z
==========================================================================*/
#include <cpuz80spd4port40.h>
#include <olduino.h>
#include <stdio.h>
#include <hspi3.h> //Z
#define LCD_WR    4-3   //clock
#define LCD_RS    3-3   //data/mosi
#define LCD_DC    6-3   //data/command
#define LCD_REST  5-3   //RESET
#define LCD_CS    7-3   //slave select
void lcdfiller(unsigned int, unsigned char);
void LCD_Writ_Bus(unsigned char value){
  digitalWrite(LCD_CS,LOW);
  spiSend(value);
  digitalWrite(LCD_CS,HIGH);
}

void LCD_Write_COM(unsigned char VL)
{
  digitalWrite(LCD_DC,LOW);
  LCD_Writ_Bus(VL);
}

void LCD_Write_DATA(unsigned char VL)
{
  digitalWrite(LCD_DC,HIGH);
  LCD_Writ_Bus(VL);
}

void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)
{//hah - this is already set up properly for endian-independance!
   printf("aset...");
   LCD_Write_COM(0x2a);
   LCD_Write_DATA(x1>>8);
   LCD_Write_DATA(x1);
   LCD_Write_DATA(x2>>8);
   LCD_Write_DATA(x2);

   LCD_Write_COM(0x2b);
   LCD_Write_DATA(y1>>8);
   LCD_Write_DATA(y1);
   LCD_Write_DATA(y2>>8);
   LCD_Write_DATA(y2);

   LCD_Write_COM(0x2C);
   printf("done\n");
}

void LCD_Init(void)
{
    digitalWrite(LCD_REST,LOW);
	delay(10);
    digitalWrite(LCD_REST,HIGH);
        LCD_Write_COM(0xCB);
        LCD_Write_DATA(0x39);
        LCD_Write_DATA(0x2C);
        LCD_Write_DATA(0x00);
        LCD_Write_DATA(0x34);
        LCD_Write_DATA(0x02);

        LCD_Write_COM(0xCF);
        LCD_Write_DATA(0x00);
        LCD_Write_DATA(0XC1);
        LCD_Write_DATA(0X30);

        LCD_Write_COM(0xE8);
        LCD_Write_DATA(0x85);
        LCD_Write_DATA(0x00);
        LCD_Write_DATA(0x78);

        LCD_Write_COM(0xEA);
        LCD_Write_DATA(0x00);
        LCD_Write_DATA(0x00);

        LCD_Write_COM(0xED);
        LCD_Write_DATA(0x64);
        LCD_Write_DATA(0x03);
        LCD_Write_DATA(0X12);
        LCD_Write_DATA(0X81);

        LCD_Write_COM(0xF7);
        LCD_Write_DATA(0x20);

        LCD_Write_COM(0xC0);    //Power control
        LCD_Write_DATA(0x23);   //VRH[5:0]

        LCD_Write_COM(0xC1);    //Power control
        LCD_Write_DATA(0x10);   //SAP[2:0];BT[3:0]

        LCD_Write_COM(0xC5);    //VCM control
        LCD_Write_DATA(0x3e);   //Contrast
        LCD_Write_DATA(0x28);

        LCD_Write_COM(0xC7);    //VCM control2
        LCD_Write_DATA(0x86);   //--

        LCD_Write_COM(0x36);    // Memory Access Control
        LCD_Write_DATA(0x48);   //was 48 C8	   //48 68??//28 E8 ??

        LCD_Write_COM(0x3A);
        LCD_Write_DATA(0x55);

        LCD_Write_COM(0xB1);
        LCD_Write_DATA(0x00);
        LCD_Write_DATA(0x18);

        LCD_Write_COM(0xB6);    // Display Function Control
        LCD_Write_DATA(0x08);
        LCD_Write_DATA(0x82);
        LCD_Write_DATA(0x27);
        LCD_Write_COM(0x11);    //Exit Sleep
        delay(120);

        LCD_Write_COM(0x29);    //Display on
        LCD_Write_COM(0x2c);
printf("init done\n");
}

void hwspilcdasm(){ //asm routines for hardware spi lcd
/*
	asm("	align 16\n"		//make sure lcdclearer jumps will fit on page
		"_lcdfiller:\n" 	//fills R12 bytes of lcd with R13.0
		"$$clrloop:\n"		//come back here for more
			"	dec 2\n	glo 13\n	str 2\n	out 6\n"	//send a fill byte
			"	dec R12\n"	//decrease counter
			"	glo R12\n	bnz $$clrloop\n" //back for more
			"	ghi R12\n	bnz $$clrloop\n" //til done
		"	cretn\n");	//and we're done
*/
}
void zfill(unsigned int n, unsigned char c){ //send n copies of c
	while (n-->0){
		spiSend(c);
	}
}

void Pant(unsigned char VL)
{
  int i;
  Address_set(0,0,240,320);
  digitalWrite(LCD_DC,HIGH);
  digitalWrite(LCD_CS,LOW);
  for(i=0;i<160;i++)
  {
  	zfill(480,VL);
  }
  digitalWrite(LCD_CS,HIGH);
}

void main(){
	//PIN4=0;
	LCD_Init();
	while(1){
		Pant(0xFF); //white
	  	Pant(0xE0);	//red
		Pant(0x18);	//blue
		Pant(0x07);	//green
		Pant(0xE7);	//yellow
		Pant(0x00);	//black
	}
}

#include <olduino.c>
#include <hspi3.c>

I always get the last word.

Olduino/Z Arduino Compatability

ooh, pretty! I wanted to document the connections to the arduino headers and I had seen these ascii pinouts so i tried adapting one from busyducks. I’m quite pleased with the result -it may even be useful!
On the extreme left and right are the labels for the arduino header positions, on the inside are labels for the two 30 pin connectors that go down to the Z80 CPU board.

The Arduino header positions are chosen to fit most shields that will work with an Arduino Uno including Ethernet, SD card, and prototype shields.

  • At the top left is parallel input pin 7 and output 7 which are used for serial I/O
  • below that is a spare chip select which can be used as a bidirectional I/O pin
  • next are output bits 0 to 6 – beside O6 there’s a wired in LED attached to that pin
  • next is the dedicated chip select 0 from the TP3465V SPI chip followed by MOSI, MISO, and SCK from the same chip
  • next is a ground
  • on the top right are five parallel input bits I5 down to I0 where the arduino would have its analog inputs
  • below that is a block of power connections.
		               /RST
           GND CTS VCC TXD RXD RTS
         +-------------------------+
         | [ ] [ ] [ ] [ ] [ ] [ ] |
        +                          +_____________________+
        |                          [RST]                 |
        |     [ ] A11              [BTN]      O0 [ ]     |
     I7 | [ ] [ ] A12                         O1 [ ] [ ] | I5
     O7 | [ ] [ ] A13                         O2 [ ] [ ] | I4
    CS1 | [ ] [ ] A14                         O3 [ ] [ ] | I3
     O0 | [ ] [+] A15                         O4 [+] [ ] | I2
     O1 | [ ] [ ] D4                          O5 [ ] [ ] | I1
     O2 | [ ] [ ] D3    +---------+ [ ]CS2    O6 [ ] [ ] | I0
     O3 | [ ] [ ] D5    |         | [ ]CS3    O7 [ ]     |
     O4 | [ ] [ ] D6    | TP3465V |          OUT [ ] [ ] | N/C
        |     [1] VCC   |         |          GND [1] [ ] | GND
     O5 | [ ] [ ] D2    |         |           I0 [ ] [ ] | GND5
 (*) O6 | [ ] [ ] D7    +---------+           I1 [ ] [ ] | VCC
    CS0 | [ ] [ ] D0                          I2 [ ] [ ] | N/C
   MOSI | [ ] [ ] D1                          I3 [ ]     |
   MISO | [ ] [+] /MREQ                       I4 [+]     |
    SCK | [ ] [ ] /IO                         I5 [ ]     |
    GND | [ ] [ ] /WE                         I6 [ ]     |
     NC | [ ] [ ] /OE                         I7 [ ]     |
        |     [ ] A10                        VCC [ ]     |
        |     [2] A9                         VIN [2]     |
        |     [ ] A8                      /RESET [ ]     |
        |     [ ] A7                         /IN [ ]     |
        |     [ ] A6                        /INT [ ]     |
        |     [ ] A5                       CLOCK [ ]     |
        |     [+] A4                       /WAIT [+]     |
        |     [ ] A3                        /RAM [ ]     |
        |     [ ] A2                        /ROM [ ]     |
        |     [ ] A1                        HALT [ ]     |
        |     [ ] A0                        /NMI [ ]     |
        |     [3] GND                        /M1 [3]     |
        |                                                |
        +------------------------------------------------+  

Thanks to	http://busyducks.com/ascii-art-arduinos

The actual schematic for the Olduino/Z is here, the Z80 CPU board schematic is in this manual.

Olduino/Z Hardware Walkthrough

I spent a little while doing cleanup today.   Besides this video I replaced the code-and-go programming demo video and fixed an error in a graphic on the about Olduino/Z page.

Bit Bang Serial 19200 Baud Receive

It works, but ick.  I’m doing this because I started it but I’m doubtful it’s going to work well enough to use.  Adjusting the timing isn’t that hard but it occurs to me that, since this is unbuffered, I would have to deal with each character received within a very  short time or put some pacing on the sender which might obviate the benefit.  Also, I need to add a timeout which complicates things further.  Even testing it seems tricky.

On the left below is the output from a simple test routine, it prints a ! then calls bbrecv and prints what it gets.  The T’s indicate a timeout but you can see me input 1,2,3.  On the right is the trace of my reads of the serial line.  The RX signal has dot’s in it where the logic analyzer is sampling the data bits.  My reads line up almost perfectly with them.  There is an extra read with a short gap that happens pretty well every time that is something to do with the monitor responding to an interrupt.  I tried disabling interrupts altogether and the extra read went away.

 

My code is below for the bbrecv routine and the test program.  Again, thanks and apologies to Jack Ganssle. http://www.ganssle.com/articles/auart.htm
Also thanks to http://wikiti.brandonw.net/index.php?title=Z80_Instruction_Set for the z80 instruction timing tables.

;
;  CIN - input a character to C within 1 second.
;  trying for 19.2 kbps, bit time is 52 uS, 208 cycles
;  character is returned in C with carry set if timeout occurs (also returns 'T')
;
cin:	di			;disable interrupts for timing
	ld	b,8		; bit count
	ld	hl,186*256	; timeout limit
ci1:	in	a,(0x40) 	; 11 read serial line
	and	0x80		; 7  isolate serial bit
	jp	z,gotit		; 10 wait till serial data comes
	in	a,(0x40) 	; 11 read serial line
	and	0x80		; 7  isolate serial bit
	jp	z,gotit		; 10 wait till serial data comes

	dec	l		; 4  check timeout limit
	jp	nz,ci1		; 10 go test again
	dec	h		;  4 check rest of to limit
	jp	nz,ci1		; 10 go test again
	jp	cto		; no character within tolimit*(84)/4 - approx 1s
gotit:	;we are now at the beginning of the start bit plus maybe 28 to 45 cycles

	ld	l,3		;  7 now need to wait 104 cycles - (45+7) already = 52 cycles
ci2:	dec	l		;  4
	jp	nz,ci2		; 10 wait till middle of start bit (3*14 is 42 more)
	jp	cnxt		; waste an extra 10 cycles (+10 makes 45+7+42+10=104)
cnxt:

ci3:	ld	l,14		;  7 next bit in 208-7 cycles, loop is 14, count is 14
ci4:	dec	l		;  4
	jp	nz,ci4		; 10 now wait one entire bit time
	nop			;  4 so total delay 7+14*14+4=207

	in	a,(0x40)	; 11 read serial character
	rla			;  4 shift into carry bit
	ld	a,c		;  4 this is where we assemble char
	rra			;  4 rotate it into the character from carry
	ld	c,a		;  4
	dec	b		;  4 dec bit count
	jp	z,ci5		; 10 j if done all bits (41 cycles to here)
	ld	l,10		;  7 next bit is in 208-68 cycles, loop above is 14, count is 10
	jp	jpci4		; 10 waste 10 cycles to balance out
jpci4:	jp	ci4		; 10 do next bit (68 cycles here!)
ci5:	ld	l,c		; return location per fastcall protocol

	or	a		; clear carry flag for success
	ei			; enable interrupts
	ret

cto:	ld	l,'T'		; failure is timeout
	scf			; set carry flag to show timeout
	ei			; enable interrupts
	ret

/*
   19.2kbps serial sending routine
   Thanks and apologies to Jack Ganssle
*/
#include <olduino.h>
#include <stdio.h>
void  bbsend(char c) __z88dk_fastcall{
	__asm
	;
	; Output the character in L
	;
			di	;disable interrupts for timing
			ld		c,l				; move input to c
			ld		hl,#0x0909		;preload bit time constants
	cout:   ld      b,10            ; # bits to send
	                                ; (start, 8 data, 2 stop)
	        xor     a               ; clear carry for start bit
	co1:    jp      nc,cc1          ; if carry, will set line high
			ld		a,0xFF			;set the line high
			out		(0x40),a
	        jp      cc2
	cc1:	ld		a,0x7F			; set serial line low
			out		(0x40),a		;set the line low
	        jp      cc2             ; idle; balance # cycles with those
	                                ; from setting output high
	cc2:    ld      l,h	     		; times per bit
	co2:    dec     l
	        jp      nz,co2          ; idle for most of one bit time
	        nop
	        nop
	        nop			          	; fill out bit time
	        scf                     ; set carry high for next bit
	        ld      a,c             ; a=character
	        rra                     ; shift it into the carry
	        ld      c,a
	        dec     b               ; --bit count
	        jp      nz,co1          ; send entire character
			ei	;enable interrupts
	        ret
	__endasm;
	c;
}
unsigned char bbrecv() __z88dk_fastcall{
	__asm
#include "bbrecv.inc"
	__endasm;
	return 'K';	//keep compiler happy
}
void main()
{
	unsigned char x;
	unsigned int j;
	while(1){
		delay(1000);
		bbsend('!');
		x=bbrecv();
		bbsend(x);
		bbsend(' ');
	}
}
#include <olduino.c>

Bit Bang Serial 19200 Baud Send

The other day I finally found some bit bang serial routines.  There are already nice serial routines in Josh’s monitor but they’re 9600 baud and tough to understand.  I wanted my own to try to get faster speed and code I could understand.

With apologies to Jack Ganssle I’ve adapted his sending code to the ZMC hardware and my limited Z80 coding skills.  I’ve removed the automatic baud rate detection and i’m only planning on speeds of 19.2 and above which simplifies my task a bit.

This is a first pass and I’m sure I’ll revise it once I move to the receive side.
The routine gets its input in L and immediately moves it to C which is where the Ganssle code kept it.  It preloads H with a loop constant.  After a start bit, it sends out the serial data LSB first (colour me surprised, I had never noticed that! I guess I’m just used to SPI). The start bit is stuffed into C to begin with then the data bits are RRA’d in one at a time and the stop bits are allowed for by forcing ones into C before each RRA.

The bit timing is done with a two instruction, 14 cycle loop run 9 times plus the baseline 74 cycles plus 3 noops to add up to a bit over 52 uS.

None of this is optimal but I’ve stuck close to the original code where I could.  When I get a receive routine working I’ll review it.  For sending only it might be possible to get to 38400 bps or a 26 uS bit time.  The problem is not the raw speed it’s the granularity of the timing.  At a 26 uS bit time I don’t think you could tolerate as much as a 1 uS error and the shortest Z80 instruction is 2 uS so it would be a matter of luck.  If there was no other complication, the baseline time is 18.5 uS, so just 3 noops would get you to 26.5 which might be close enough.  Anyway, I’ll wait til I’ve done a receive before I worry about it.

/*
   19.2kbps serial sending routine
*/
#include <olduino.h>
#include <stdio.h>
void  bbsend(char c) __z88dk_fastcall{
	__asm
	;
	; Output the character in L
	;
			di	;disable interrupts for timing
			ld		c,l				; move input to c
			ld		hl,#0x0909		;preload bit time constants
	cout:   ld      b,10            ; # bits to send
	                                ; (start, 8 data, 2 stop)
	        xor     a               ; clear carry for start bit
	co1:    jp      nc,cc1          ; if carry, will set line high
			ld		a,0xFF			;set the line high
			out		(0x40),a
	        jp      cc2
	cc1:	ld		a,0x7F			; set serial line low
			out		(0x40),a		;set the line low
	        jp      cc2             ; idle; balance # cycles with those
	                                ; from setting output high
	cc2:    ld      l,h	     		; times per bit
	co2:    dec     l
	        jp      nz,co2          ; idle for most of one bit time
	        nop
	        nop
	        nop			          	; fill out bit time
	        scf                     ; set carry high for next bit
	        ld      a,c             ; a=character
	        rra                     ; shift it into the carry
	        ld      c,a
	        dec     b               ; --bit count
	        jp      nz,co1          ; send entire character
			ei	;enable interrupts
	        ret
	__endasm;
}
void main()
{
	//printf("Hello From BBSEND\n");
	while(1){
		delay(500);
		//printf("U");
		bbsend('U');
	}
}
#include <olduino.c>

Bit Bang Serial

Josh has nice, interrupt driven, software serial routines in the Monitor of the Z80 Membership Card.  They’re rock solid but, at 9600 baud, they’re a bit slow for loading large programs and they’re all mixed in with the code for refreshing the front panel display.

I’ve been looking for alternatives for a while and finally by looking for “software uart” instead of bitbang serial, I found what seem like good candidates.  They’re written in Z80 assembly so they should be easy enough to incorporate in my bootloader.  They’re not interrupt driven but that’s fine for my use case.  I’ll only be calling them right after a reboot and I’ll just disable interrupts until I’m done.  The author only says he has reliable results to 9600 baud so I may, in the end, not do any better but we’ll see.

http://www.ganssle.com/articles/auart.htm

 

Code And Go! Olduino/Z is Styling Now!

Last week I had a go at automating the program load for the Olduino/Z to be like the 1802 Olduino.  I rewrote the python code today and it’s pretty serviceable.  The hardest thing was figuring out how to write a progress bar as the transfer happens.  My spouse actually figured out the algorithm for it.

In the video below, the editor window on the right is textpad with the helloworld.c program open. On the left is Br@y Terminal which is initially disconnected.  When I click the first button the compiler runs.  When I click the second button a dos window opens for the python bootloader.  It resets the Z80 then sends the compiled code over xmodem.  When that closes I click on the Terminal window, it connects, and we see the output.

The Python code looks worse than it is. There’s probably only 10 lines doing anything useful and the rest is just cruft of one sort or another.

from __future__ import print_function
import sys
import logging
logging.basicConfig()
import serial
try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO
from xmodem import XMODEM, CRC
from time import sleep
import os
if len(sys.argv)&amp;gt;1:
    filename=sys.argv[1]
else:
    filename=&amp;quot;a_code.bin&amp;quot;
fileSize=os.path.getsize(filename)
print (&amp;quot;File Size is&amp;quot;,fileSize)
xpect=(fileSize/128)

def getc(size, timeout=1):
    return port.read(size)
xcount=0
xprog=0
def putc(data, timeout=1):
    global xcount,xprog
    port.write(data)
    if (len(data)==128): # if it's a block
    	xcount=xcount+1
    	newprog=(xcount*50)/xpect
    	for x in range(newprog-xprog):
            sys.stdout.write(&amp;quot;#&amp;quot;)
        sys.stdout.flush()
        xprog=newprog
    sleep(0.001) # give device time to send ACK

#Main program starts here - define the serial port, set RTS off, then open it

port = serial.Serial(parity=serial.PARITY_NONE,
                     bytesize=serial.EIGHTBITS,
                     stopbits=serial.STOPBITS_ONE,timeout=5,xonxoff=0,
                     rtscts=0,dsrdtr=0,baudrate=9600)
port.rts=False
port.port='COM3'
port.open()
port.rts=True
sleep(0.001)
port.rts=False
sleep(0.1)
port.read(1) #ditch the 1st char - always seems to be 0

#open the file to be loaded
stream = open(filename,'rb')

#transfer the file
result=XMODEM(getc, putc).send(stream, quiet = 1)

stream.close()
port.close()

if result:
    print (&amp;quot;\ntransfer successful&amp;quot;)
    sleep(.25)
else:
    print (&amp;quot;\ntransfer unsuccessful&amp;quot;)
    x=raw_input(&amp;quot;press enter to continue...&amp;quot;);
Follow

Get every new post delivered to your Inbox.