I've recently pushed an update to the z80ctrl repo which makes my new IO expander board accessible to the Z80.
To access these features, you'll need to pull the latest code from the Github repo, and then uncomment the IOX_BASE variables in the Makefile. After doing so, run 'make clean' and then 'make install' to rebuild and flash the new code. You can change the IOX_BASE if desired, but you must modify any code that will access the ports accordingly.
The board is exposed to the Z80 on 3 consecutive ports starting from IOX_BASE:
- Writing to port 0 selects device you want to control. The RTC is always device 0. The device number of the GPIO chips correspond to the address configured on the jumpers next to the chip. If you select a non-existent device, writing to the other registers will have no effect.
- Writing to port 1 selects the active register on the selected device.
- The registers for the RTC are fully documented in the DS1306+ datasheet. You can also find a concise list in rtc.h.
- The registers for the GPIO ports are fully documented in the MCP23S17 datasheet. You can also look in the iox.h file for a concise list of addresses. You will want to use the register addresses for bank 0 unless you change the bank configuration (read about this in the datasheet).
- Reading or writing port 2 returns or sets the value of the selected register on the selected chip. Refer to the appropriate datasheet for the meaning of the values written to the registers.
Here is a simple MBASIC program to flash an LED on the first GPIO port 10 times:
10 OUT 0, 1: REM select chip address 1 20 OUT 1, 0: REM select register IODIRA0 (0x00) 30 OUT 2, 0: REM set register value to 0 (all pins outputs) 40 OUT 1, &H12: REM select register GPIOA0 (0x12) 50 FOR I = 1 TO 10 60 OUT 2, 0: REM turn all pins on GPIOA off 70 FOR J = 1 TO 1000:NEXT 80 OUT 2, &HFF: REM turn all pins on GPIOA on 90 FOR J = 1 TO 1000:NEXT 100 NEXT
First, we select the IO direction register on the IO expander configured at address 1, and set all pins to outputs. Then we select the GPIO register. Within a loop, we toggle all of the pins on and then off, pausing between each transition.
Here is an MBASIC program demonstrating how to read the time from the RTC:
10 REM CONVERT VALUES FROM BCD 20 DEF FNBCD(V)=(V AND &HF0)/16*10+(V AND &HF) 30 OUT 0,0: REM SELECT RTC CHIP 40 REM READ DATE/TIME FROM RTC REGISTERS 50 OUT 1,6: YR=FNBCD(INP(2))+2000 60 OUT 1,5: MO=FNBCD(INP(2)) 70 OUT 1,4: DY=FNBCD(INP(2)) 80 OUT 1,2: HR=INP(2) 90 OUT 1,1: MI=FNBCD(INP(2)) 100 OUT 1,0: S=FNBCD(INP(2)) 110 REM HANDLE AM/PM 120 AMPM$="" 130 IF (HR AND &H40)=0 THEN 160: REM SKIP IF 24-HR FORMAT 140 IF HR AND &H20 THEN AMPM$="PM" ELSE AMPM$="AM" 150 HR=HR AND &H1F: REM GET RID OF AM/PM AND 12-HR BITS 160 HR=FNBCD(HR) 170 PRINT USING "The time is ##:##:##& on ##/##/####"; HR, MI, S, AMPM$, MO, DY, YR
First, we select the RTC device, then read the year, month, day, hour, minute, and second from the corresponding register on the chip. Next, we check to see if the 12 hour bit is set. If so, we check whether the time is currently AM or PM and then mask off those bits. Now, we convert all the values from BCD (as they are returned by the RTC) into binary so that BASIC can print them properly. Finally, we output the date and time in a standardized format.