Systems Programming www.atomicrhubarb.com/systems Lecture 4 Z16 Architecture and Programming
Section Topic Where in the books Zilog Zilog Zilog Zilog UM197 (ZNEO Z16F Series Flash Microcontroller Contest Kit User Manual) UM171 (ZiLOG Developer Studio II ZNEO User Manual) PS220 (ZNEO Z16F Series Product Specification) UM188 (ZNEO CPU Core User Manual)
Z16F 20 MHz ZNEO CPU 128 KB internal Flash memory with 16-bit access and In-Circuit Programming (ICP) 4 KB internal RAM with 16-bit access External interface allows seamless connection to external data memory and peripheral with: Six chip selects with programmable wait states 24-bit address bus supports 16 MB Selectable 8-bit or 16-bit data bus widths 12-channel, 10-bit Analog-to-Digital Converter (ADC) Operational Amplifier Analog Comparator 4-channel Direct Memory Access (DMA) controller supports internal or external DMA requests Two full-duplex 9-bit Universal Asynchronous Receiver/Transmitter (UARTs)
Z16F Internal Precision Oscillator (IPO) Inter-Integrated Circuit (I2C) master/slave controller Enhanced Serial Peripheral Interface (ESPI) 12-bit Pulse Width Modulation (PWM) module Three standard 16-bit timers with Capture, Compare and PWM capability Watchdog Timer (WDT) with internal RC oscillator 76 General-Purpose Input/Output (GPIO) pins 24 interrupts with programmable priority On-Chip Debugger (OCD) Voltage Brown-Out (VBO) protection Power-On Reset (POR) 2.7 V to 3.6 V operating voltage with 5 V-tolerant inputs
9 Devices in the Family
Differences in the 9 X
Part Number Z16F2811FI20SG
Address Space
Low ROM
Internal RAM Internal RAM is mainly employed for data and stacks. Internal RAM also contains program code for execution. The top (highest address) of internal RAM is always located at address FF_BFFFH. The bottom (lowest address) of internal RAM is a function of the amount of internal RAM available.
IO Memory 8 KB (8,192 bytes) of I/O memory space located at addresses FF_E000H through FF_FFFFH. The I/O memory addresses are reserved for control of the ZNEO CPU, the on-chip peripherals and the I/O ports. Attempts to read or execute from unavailable I/O memory addresses returns FFH. Attempts to write to unavailable I/O memory addresses produce no effect.
IO Memory Some control registers within the I/O memory provide read-only or write-only access.
What good is a write only register?
External Memory Many ZNEO CPU products support external data and address buses for connecting to additional external memories and/or memory-mapped peripherals. The external addresses are used for storing program code, data, constants and stack, etc. Attempts to read from or write to unavailable external addresses is undefined.
Endianness The ZNEO CPU accesses data in big endian order, that is, the address of a multi-byte word or quad points to the most significant byte.
Recall Endianness?
Big Endian
Bus Width The ZNEO CPU accesses 8-bit or 16-bit memories. The data buses of the internal nonvolatile memory and internal RAM are 16-bit wide. The internal peripherals are a mix of 8-bit and 16-bit peripherals.
Bus Width The external memory bus is configured as an 8-bit or 16-bit memory bus. If a Word or Quad operation occurs on a 16bit wide memory, the number of memory accesses depends on the alignment of the address.
Bus Width If the address is aligned on an even boundary, a Word operation takes one memory access and a Quad operation takes two memory accesses.
Bus Width If the address is on an odd boundary (unaligned), a Word operation takes two memory accesses and a Quad operation takes three memory accesses.
CPU Control Registers
GPIO Port A
Peripheral Address Map Many pages of this in the manual
GPIO General Purpose Input Output ports Maximum of 10 ports 8-bit ports (A K) Less available on physically smaller parts (not enough pins) Some not accessible or partially accessible
Alternate Function
Bits An 8-bit register 1 0 1 0 1 0 1 0 Bit 0 (LSB) Bit 7 (MSB) SET a bit (turn it on or 1 ) CLEAR a bit (turn it off or 0 ) Some folks say they RESET a bit to turn it off.
Port Configuration PxDD = Data Direction - Is the port we are interested in an input port or output port. PxIN Data Input PxOUT Data Output * Many more IO port registers.
In, Out Configure Port PDDD = 0xA5; Read from IN int i = PDIN; Write to OUT PDOUT = i;
In, Out Configure Port PDDD = 0xA5; Write to OUT PDOUT = i; Read from IN int i = PDIN; What do we read IN here?
In, Out (yeah, more) We don't always want to read/write BYTES Configure Port PADD = 0x0F; // low nibble IN, top nibble OUT Read from input, PA bit 1 int i = PDIN & 0x02; Write to output, set PA bit 6 PDOUT = PDOUT 0x40; PDOUT = 0x40;
Dan's Helpful Hint For remembering wich is it, a 1 or a 0 for setting the Data Direction: 1 looks like I(input) 0 looks like O(output)
For every port #define #define #define #define #define #define #define #define PAIN PAOUT PADD PAHDE PAAF PAAFH PAAFL PAOC (*(unsigned (*(unsigned (*(unsigned (*(unsigned (*(unsigned (*(unsigned (*(unsigned (*(unsigned char volatile near*)0xe100) char volatile near*)0xe101) char volatile near*)0xe102) char volatile near*)0xe103) short volatile near*)0xe104) char volatile near*)0xe104) char volatile near*)0xe105) char volatile near*)0xe106)
Oscillator Can have an external oscillator: Crystal Oscillator Low cost RC oscillator Internal Precision Oscillator (IPO) Watchdog Timer with internal RC oscillator
Overclocking The IDE will not allow specification of an Oscillator > 20 MHz. I have a ZNEO running at 24 MHz with no problems Modified Oscillator socket.
RC oscillator
Internal Oscillator
Watchdog Timer
Oscillator Control 2 Registers: OSCCTL (Oscillator Control) OSCDIV (Oscillator Divide)
OSCCTL
OSCDIV
Unlock The Registers First
Why would you need to unlock it (why is there a lock on it)?
Some C OSCCTL = 0xE7; OSCCTL = 0x18; OSCCTL = 0xE1; 0xE1 = 11100001 Turn on internal, external and watchdog. Select external.
Z16 Oscillator Selection Recipe 1. 2. 3. Unlock the oscillator control register Select the new register configuration Change the clock in the IDE project settings debugger setup to the new clock rate
No longer Magic // unlock the oscillator control register OSCCTL = 0xE7; OSCCTL = 0x18; // follow with ONE of these TWO settings // 0xA0 = 1010 0000 = internal 5.5 MHz OSCCTL = 0xA0; // 0x61 = 0110 0001 = external 18.432 Hz OSCCTL = 0x61;
project settings debugger setup What happens when we change these values?
Absolutely NOTHING!
Well, almost nothing. Changes 2 symbols that the LINKER can resolve SYS_CLK_FREQ SYS_CLK_SRC Still, nothing changes in our program What do we do with those symbols?
Get the Linker Symbols extern _Erom unsigned long SYS_CLK_FREQ; extern _Erom unsigned long SYS_CLK_SRC; void set_oscillator(void) { unsigned long clkfreq = ((unsigned long)&sys_clk_freq); unsigned long clksrc = ((unsigned long)&sys_clk_src); unsigned char myoscctl; myoscctl = OSCCTL & 0x1C; if (clksrc == 0) { myoscctl = 0x80; } else if (clksrc == 2) { myoscctl = 0x41; } else if (clksrc == 3) { myoscctl = 0x23; } if (myoscctl OSCCTL = OSCCTL = OSCCTL = } } > 0) { 0xE7; 0x18; myoscctl; // clear bits 0,1,5,6,7 // internal // external // watchdog // Unlock seq 1 // Unlock seq 2
Why do we care so much about this Oscillator thing?
Serial Port To confogure the serial port we need to know the oscillator (so we get an accurate baud rate) We can program the Z16 serial port at the register level (painful but necessary sometimes) or using the high-level API. We will discuss serial ports in depth. For now, we use the high-level API.
Serial IO #include <zneo.h> #include <sio.h> #include <stdio.h> void main(void) { int c=0; OSCCTL = 0xE7; OSCCTL = 0x18; OSCCTL = 0xC0; // Internal Oscillator init_uart(_uart0, 5529600, 57600); printf("hello World\n"); while(1) { if (kbhit()) { c = getchar(); printf("[%c]",c); } } }
#include <zneo.h> #include <sio.h> #include <stdio.h> Serial IO extern _Erom unsigned long SYS_CLK_FREQ; extern _Erom unsigned long SYS_CLK_SRC; void main(void) { int c=0; unsigned long clkfreq = ((unsigned long)&sys_clk_freq); unsigned long clksrc = ((unsigned long)&sys_clk_src); OSCCTL = 0xE7; OSCCTL = 0x18; // unlock the oscillator control register if (clksrc == 0) { // internal OSCCTL = 0xF0; } else if (clksrc == 2) { // external OSCCTL = 0xF1; } else if (clksrc == 3) { // watchdog OSCCTL = 0xF3; } init_uart(_uart0, clkfreq, 57600); printf("hello World\n"); printf("clock is %d %d\n", clksrc, clkfreq); while(1) { if (kbhit()) { c = getchar(); printf("[%c]",c); } } }
Things to remember: Low Level programming Understanding Hardware Understanding development tools Developing software to control hardware Software development at lowest level (assembly, C)