Vous êtes sur la page 1sur 77

This board is a do-it-yourself (DIY) demonstration board (i.e.

learning kit, etc) for the 40-pin Zilog Encore Microcontroller. To those students who want to build their own 40-pin zilog encore board, you can download the following PCB: http://www.4shared.com/file/49217672/895f207/Z8F_copper_bottom.html Components layout guide: http://www.4shared.com/file/49217728/94f5856b/Z8F_component_guide.html Schematic for the zilog encore board: http://www.4shared.com/file/36813874/391cb7f9/zilog_encore_schematic.html bills of materials (BOM): http://www.4shared.com/file/37196950/8b539ada/zilog_encore_bills_of_materials.html 3D preview of the circuit: here is the finished DIY board:

the simplified bread-board version of the schematic can be downloaded in this link: http://www.4shared.com/file/37381053/f0f440ed/zilog_encore_breadboard.html

I will use a Presensitized PCB. I bought it from E-gizmo. The design needs 3.5 x 3.5 inches single-sided presensitized PCB. Im gonna print the PCB design sa isang tracing paper. I can also use an acetate for that, yung for color printer variety and not the ordinary one. Click PRINT sa Ultiboard window and set ZOOM OPTION to 100%. In the LAYERS TO PRINT column, include the COPPER BOTTOM and BOARD OUTLINE layer, as shown in the figure below

Pwede rin tingnan ang PRINT PREVIEW. It would be better to set to GRAYSCALE ang print properties and choose the best PRINT QUALITY if your printer has that property. Btw, im using an inkjet printer. Mas oki sana kung laser printer, the best talaga ang result ng printing kaso wala ako nito

Here is the result. On the right side, tracing paper yan and the one on the left is an acetate. I cut the presensitized PCB to 3.5 x 3.5 inches size using an ordinary cutter. Medyo nakapagod ng kunti ang cutting, it took me about 10 minutes . Im kinda careful para hindi magasgas ang white side ng PCB

Now its time to expose the presensitized PCB. Im using this home-made light source, made of 4 10-watt fluorescent tubes.

Ilalatag ko sa floor ang plyboard cover ng light source ko. Then sa center ng plyboard, ilalagay ko ang PCB and on top of the PCB ipapatong ko ang tracing paper with the printed PCB design. Ang on top na naman ipapatong ko ang glass cover. Refer to figure below sa tamang position. Take note hindi ko pa inalis ang white cover/coating ng presensitized PCB. Pina-position ko muna And sorry for the crappy pictures, wala kasi ako magandang digital camera If na estimate nyo na ang position, you may now remove the white coating ng PCB, and IMMEDIATELY put the tracing paper design on top, followed by the glass, then the lightsource. Tapos turn on the light source and expose the whole design for about 10 minutes.

After 10 minutes, turn off the light source and remove the cover.

Ibababad ko na agad ang exposed PCB sa PCB developer compound solution. In about 2 minutes, completely removed na ang lightexposed greenish resin layer and makikita na ang copper beneath it. Yung unexposed parts, hindi matatangal and even hardened by the solution itself. Very clear talaga ang itsura ng circuit.

After 2 minutes, remove the PCB and wash in running water. Here is the result

Etching time! It took me more or less 15 minutes.

After etching, remove and wash with running water.

Using acetone and tissue/cotton, remove the remaining resin layer (the green one). Then wash with running water. You can also use a detergent para malinis completely ang board. Then patuyuin.

Pagkatapos, check nyo ang PCB if there are tiny copper shorts between adjacent traces. Tingnan nyo ang board against the light para mas klaro. Check and recheck. And use an ordinary cutter to remove the copper shorts.

Then drill all the holes. I suggest na download nyo ang component guide which i posted previously, and print it para may guide kayo sa position ng mga components during drilling and later during soldering. After drilling, the first thing you should do is solder the jumper wires, there are 3 of these. 2 of the jumper wires are located beneath the IC sockets so I believe na dapat unahin ito para hindi makalimutan. The other jumper is beneath the LM317 TO-220 IC

Unahin muna natin i-solder ang power supply circuit. First is the DC jack connector.

Solder it properly. Here is the proper hole position for the DC jack.

After the DC jack, isunod nyo ang C1 (220uF/25V) electrolytic capacitor and the LM317 adjustable voltage regulator. Bend the LM317 pins para horizontal ang position ng regulator.

Solder the LM317 tapos i-solder din ang R2 (360 ohms ), R1 (220 ohms), R3 (330/460 ohms), and LED1 (3mm RED). Test the power supply. Plug-in the DC adopter (you may use from 9V to 25V). The LED will turn on a bright red. Also measure the output voltage of the LM317 & it should be 3.3V.

A little explanation on the power supply circuit. C1 electrolytics capacitor is used to minimize/eliminate ripples from the DC adaptor. The DC adoptor can be from 9 V to 25 V. Do not use beyond 25 V or else C1 (220uF/25V) will "pop". The LM317 is configured to output 3.3 V (using R1 & R2) since the power requirement for the Zilog Encore! microcontroller IC is 3.3 V (uController datasheets say 3.0 - 3.6 V, so better use the middle value )

The LM7805 fixed voltage regulator will supply 5V to the MAX232 IC. The MAX232, btw, is used for serial communication. So you have two power supplies, a 3.3V output & a 5V output.

After successful testing of the 3.3 V output, add the LM7805 regulator. Then test it also.

TESTING TIP: Gamitin nyo ang ilong and fingers hehe Habang naka on ang supply, touch the regulator kung umiinit ba. Gamitin din ang ilong at amoyamoyin baka "fried" na ang mga elec. capacitors

Add the 1x3 male header pins (J10). Solder also C6 (100uF elec), C5 (100 nF cer), C2 (100 nF cer), & C4 (100uF elec). These 4 capacitors will reduce further voltage supply ripples.

Next is to solder R11 (3K ohms), R12 (1k ohms), R12 (1k ohm trimmer pot), and TL431 (U3). This circuit by the way is an adjustable voltage regulator with an output from 2.5 V to 3.2 V. It is used by the microcontroller as an external voltage reference for the ADC module. After soldering, test it by varying the trimmer pot, and you should have the voltage values ranging from approximately 2.5 to 3.2 V.

Lets add the 16-pin IC socket for the MAX232. But before that, solder nyo muna ang R4 (10kohm), since nasa ilalim yan ng 16-pin IC socket.

Solder the 16-pin IC. Sa figure makikita ang 10kohm resistor (R4) nasa ilalim ng IC socket.

Then solder the 5 1-uF/16V electrolytic capacitors. Make sure that the polarity of the 5 capacitors is correct, else debugging/programming of the microcontroller is not possible.

Dagdag natin ang reset circuit. Solder the ff. components: (1) tack switch (J4, pushbutton) (2) C15 (10-uF electrolytic capacitor) (3) R9 (1k ohm)

Solder also the following (1) C3 (100 nF ceramic) (2) D1 (1N4148 switching diode) Take note the C3, D1 and R9 are beneath the 40-pin IC socket.

Then put the 40-pin DIP socket.

Next, the oscillator circuit. Solder the following: (1) C7 & C8 (22 pF ceramic) (2) R7 (220 ohms) (3) R8 (100k ohms) (4) X1 (18.432 MHz crystal oscillator)

Add the 2 male header pins, J5 & J6.

Using a jumper pin, J5 is used to connect/disconnect the analog ground of the ADC pins from the digital ground. J6 is used to connect the external voltage reference circuit (consists of TL431, R11, R10, & R12) to the VREF pin (pin25) of the zilog encore ucontroller. Without the external voltage reference connected at pin25, the ADC module of the ucontroller can only measure from 0 to 2 volts. Using the external vref, the voltage input to the adc can be measured from 0 to about 3.2 volts. As an example above, the analog ground is connected to digital ground (since there is a jumper) and the external voltage reference is NOT connected to VREF pin (pin25) since there is no jumper.

solder the following (1)LED2 & LED3 (3mm LED green) (dont forget about proper position of the LED's cathode/anode) (2) R5 & R6 (330 ohm) (3) J12 1x2 SIL pin (single-in-line) (or you may use 1x2 Female header pin)

Solder the 2 Female DB9 connectors

All the remaining unsoldered parts are SIL connectors. You may add/solder them and your done already. You may also use female header pins instead of SIL. I personally dont recommend using Male header pins because of difficulty of connecting the I/O pins of the microcontroller. Kailangan kasi ng female headers with wires para maconnect to the male headers.

Ito ang board that have SIL pins

Here is another board with Female header pins

If you will use the DIY board above, all you will need is an ordinary rs232 data cable. Marami yan you can buy sa mga PC stores. No need to use the USB cable, coz marami cant afford to but the full kit from zilog. I havent used a USB smart cable before, but functionaly pareho lang sila ng serial smart cable. Actually, the circuitry of the serial smart cable is already included na sa board, thats why all you will need is rs232 data cable. But for the sake of modification yes we can include the typical 2x3 male header pin to accomodate the USB serial cable, and make the necessary connections to the Vcc, GND, DBG pin, and Reset pin. No problem pwede yan

During assembly, ALWAYS check the next electronic component that will be soldered (capacitors, resistors, LEDs,diode) to make sure na hindi sira, shorted or open. After all components are soldered, huwag muna ilagay ang MAX232 IC and Z8F6421 IC. Power up the board. The red LED (power indicator) should alway be on. Again, check the output power of LM317 (3.3V) and LM7805 (5.0 V). Make sure na hindi umiinit ang dalawang voltage regulator. Kung umiinit, remove immediately the

dc adaptor and recheck thoroughly the PCB for shorted connection. Check also the following pins for indicated voltages. The pins highlighted by the RED circles should be 3.3 V and BLUE circles for 5V. PIN9 of the microcontroller is connected to the reset circuit. It should have 3.3 V. When the tack switch is pressed, the voltage should go down to 0V.

Then add the MAX232 IC, make sure na hindi baligtad ang pins! Huwag muna ilagay ang microcontroller. Power up the board and check pin 16 if it is 5V. Check also kung if umiinit ang MAX232, if it is remove immediately the power and check for copper shorts. If ok ang MAX232, add the Zilog Microcontroller IC and power up the board. Check again all the power pins as indicated sa picture above. To test the DIY board first we need to install ZDS 2. You can download the free Zilog Developer Studio (ZDS II) IDE from this link http://www.zilog.com/software/zds2.asp the current version is 4.10.1. Filesize ng installer is 9.8 MB. here is a screenshot of the IDE

Here is the pin configuration of the 40-pin Zilog Encore! MCU Im currently using a Z8F6421 40-pin DIP chip. Z8 means the MCU uses the 8-bit Z8 CPU architecture. F means it has flash program memory. 64 means it has 64k flash program memory that you can use to store your program. 21 is the version of the chip. There are also Z8F1621 (16k memory), Z8F2421 (24k memory size), Z8F3221 (32k memory size), & Z8F4821 (48k memory size) versions. As far as i know, E-gizmo have these chips. The 64k chip is P620 while the 16k version is P455 pesos. some of the main features of the Z8F MCU, 40-pin version 20 MHz eZ8 CPU Up to 64 KB Flash memory Up to 4 KB register RAM 10-channel, 10-bit ADC 2 UART Inter-integrated circuit (I2C) Serial Peripheral Interface (SPI) 2 IRDA encoder/decoder 3 16-bit timers 3 channel DMA 31 GPIO pins 24 interrupts with configurable priority On-Chip Debugger <-- the best 3.0 3.6 V operating voltage with 5 V-tolerant inputs Lets build our first zilog encore project. You will need your DIY board, a serial data cable, a dc adopter (9-25V output), and a running ZDS 2 IDE on your PC. Let us make the "blinker" project. The blinker project is probably the simplest project you can make, equivalent to the "Hello, World!" in PC programming. An LED (in series with a 330-ohm resistor) is connected to PIN 1 (PD4/RXD1 pin) of the Z8F MCU, and we will write an embedded C program that will make the MCU to "blink" (turn on/off) the LED. First, run the ZDS II application. You will see an empty IDE workspace

Then, click File > New Project Click the Browse button. I choose the project name blinker, but you can name it any name you want, though it would be good to make it descriptive. You can also choose your own desired directory, but in this example i choose Desktop\Zilog Codes\Blinker\ Then select the other remaining options as shown in the picture above. If you are using, a Z8F1621 chip, then select that name in the CPU drop-down menu. If you are using an older Z8F6401 chip, select Z8Encore_640_Family in the CPU Family drop-down menu, then Z8F6401 in the CPU drop-down menu Click Finish Select File > New File... An empty text editor window will appear. Type the following C code.
#include <ez8.h> void delay(void) //software delay { int i,j; for(i = 0x00; i<0xFF; i++) for(j = 0x00; j<0xFF; j++); } void main() { PDDD &= ~0x10; //PD4 pin is output PDAF &= ~0x10; //Alternate Function off PDOC &= ~0x10; //Push-pull output PDHDE |= 0x10; //High-Drive Enable on PDOUT &= ~0x10; //initial output low while(1){ delay(); PDOUT ^= 0x10; } } //call delay routine //toggle output

Then save file as main.c. You may choose any valid name as long as it ends with .c. You can name it blinker.c, for example. Save it in the project directory (i.e. Desktop\Zilog Codes\blinker directory)

Btw, you can change the font, font size, background color, etc of the editor window. Select Tools > Option > Editor and adjust desired options. I have poor eyesight so i change the font size to 12, instead of the default 8. I also changed the background color.

The next thing to do is add the main.c file to the project. To do this, select Project > Add Files... Select the main.c file and click Add.

The main.c file is now listed in the Project Workspace window in the left side. It is below the the Project Files graphical tree display, as shown below.

Its time to compile the main.c. Select Build > Compile (or click the Compile/Assemble button at the top). The appropriate message will be displayed in the Output Window at the bottom of the IDE. Since there were no syntax errors, the compilation was succesfull (i.e. Build completed.), as shown below. The compilation process will generate the object code of the main.c file.

Next, select Build > Build (or click the Build button at the top) to build the project. The build process will invoke the linker and the assembler to generate the actual machine code for the Z8F6421 MCU.

At this time, you may open the project directory to see what files were generated during the project build. The most important file is the blinker.lod file. This is the actual binary code (machine code) that will be downloaded to the MCU program memory. This .lod file is similar to the .hex files of PIC MCUs.

Before we can download the machine file, we need to configure the debugging/programming interface. Select Project > Settings. In the Debugger tab, select the Serial checkbutton (this will deselect the Simulator) Click the Communication button. Select a baudrate of 57600 and choose which COM port you will use. I choose COM1, since i will connect my DIY board to COM1 of my PC. If you are not sure what COM port your PC have, open Device Manager and check. Click OK.

Next, click Setup and indicate the crystal frequency of your DIY board. I will choose 18.432 MHz since that is frequency on my board. Click OK. Rebuild project if prompted.

In the DIY board, connect PIN 1 of the Z8F MCU to one of the 2 LEDs using a short solid copper wire (the green wire). Connect the board to COM1 as shown below, using an ordinary RS232 data cable (you can buy this in your local PC store, but im using a DIY cable made from junk HD data cables ).

In the ZDS 2 IDE, click the Connect to Target button to start communicating with the Z8F board.

An appropriate message will be displayed in the Output window, indicating succesfull communication or not.

If communication failed, check again if you are using the correct COM port. If you are sure that the COM port is correct, check the hardware (i.e. no power connection (this is easy ), chips not ok (mahirap ito ), etc) If communication is succesfull, you can now download the code. Click the Download Code button at the top.

An appropriate message is displayed in the Output Window, indicating success or download failure. Now, click the Go button. The LED on the board should blink a few times each second

Congratulation on your first Zilog Encore! project


Important: Remove the power first before disconnecting the Z8F board from the COM port!
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

LED BLINKER
To help me explain how Z8F MCU (& C programming) works, lets us make a simpler C program. This program will simply turn on an LED connected to PIN 1 of the Z8F MCU. I want to avoid explaining user-defined functions in the meantime, which the previous "blinker" program uses. I'm assuming students have limited or zero knowledge on C programming.
#include <ez8.h> void main() { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT |= 0x10; while(1); }

//PD4 //PD4 //PD4 //PD4 //PD4

is output alternate function is off is a push/pull output high-drive enable on output a logic 1 (i.e. 3.3 V) //infinite loop

Now, build the program as a new project in the ZDS 2 IDE and run the program in the DIY board.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Let me explain the Z8F C code line by line. First, we have #include <ez8.h> This line is a preprocessor directive (its just a name, dont be scared ).

With this line, you are instructing the C compiler to include ez8.h file (as well as some other appropriate library files that are included in the ez8.h file) into your program so that you can use variables or functions that are declared in these files. Virtually all Z8F C application programs will have the #include <ez8.h> at the top of the code. The ez8.h file can be found in the .. \ZiLOG\ZDSII_Z8Encore!_4.10.1\include\zilog directory. You may open this file using notepad. The names of various Z8F internal registers are declared inside.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Next line is the main function. void main ( ) { //your code goes here ?? ?? ?? }

All Z8F C programs may have one or more functions, but the main function must always be present since this is the first function (possibly the only) that the MCU will execute. All codes inside the curly braces { } are said to be inside the body of the main function. Before we discuss the lines of code inside the main function, let me explain a little about the Z8F architecture. Inside the Z8F MCU (MicroController Unit) chip are several 8-bit internal registers that controls the various hardware peripherals of the MCU. This is true for all microcontrollers, irregardless of manufacturer. You can think of these special purpose registers as tiny switches that you can turn on and turn off. (Physically, a register is made up of flip-flops which can hold binary logic values in the form of stored electric charges. But the physical make-up of the registers is not important for us MCU users.). Since the internal registers are normally 8 bits wide, then a register can be thought of as a group of 8 switches, 1 switch corresponds to a bit. These tiny switches can be turn on or turn off by writing a 1 or 0, respectively. Z8F internal registers also have names. The PDDD register for example, is the Port D Data Direction register. Another is the ADCCTL, the ADC Control register. Refer to the Z8F 64k Series datasheet to learn all about its internal registers. When you write a C program for any MCU for the first time, think of it simply as turning on/off of the these register switches, to make the MCU operate in a manner that you desired. This will hopefully make you feel better when climbing up the steep MCU application development learning curve.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Next line, PDDD &= ~0x10; //PD4 is output pin

this is the first line inside the main function. It configures Pin 1 (PD4/RXD1) as an output pin. An output pin can be software-controlled to send a Logic 1 (outputs 3.3 V with respect to ground) or a Logic 0 (0 V, ground level) at its terminal. Port D Data Direction (PDDD) register is just one of the many internal registers (switches) inside the Z8F MCU. PDDD is also one of the six (6) internal registers that control directly the behavior of the Port D pins. PDDD is 8-bit wide (there are 8 switches to control), and controls whether a specific Port D pins is an output or an input. Clearing (writing a 0) a bit in the PDDD register will make the corresponding Port D bit as an output pin. Setting (writing a 1) a bit in the PDDD register will make a corresponding Port D pin as an input pin. For example, if you want to configure the following Port D pins PD0 (pin 13) = output PD1 (pin 12) = output PD3 (pin 2) = output PD4 (pin 1) = input PD5 (pin 40) = input PD6 (pin 34) = input Then, the PDDD register should have a binary value of 11110000 (or 0xF0 in hexadecimal). In ZDS C, we write the code, PDDD = 0xF0;

Btw, there is no PD2 and PD7 pins (unimplemented) for the 40-pin version, even though they have corresponding PDDD bits. Writing a 1 or 0 to PDDD bits 2 and 7 will not accomplish anything. If we want to change the content (or value) of the PDDD register and assign it a value of 0xF0 (hexadecimal number starts with the common 0x notation!), the line PDDD = 0xF0; is acceptable. Take note that this assignment will affect ALL the 8 bits in the PDDD register. OFTENTIMES, we may need to change (set or clear) ONLY 1 bit (or maybe few) and leave all other bits unchanged. For example, if we want to clear the PDDD<4> bit, the line PDDD = 0x00; is not advisable, since bits PDDD<3:0> & PDDD<7:5> are also cleared! The solution to this dilemma is simple. Follow this C coding rule from now on, (and DONT forget!) -------------------------------------------------1. To clear a bit, AND this bit with 0. 2. To set a bit, OR this bit with 1. -------------------------------------------------Applying the rule above, the code PDDD &= ~0x10; is used instead of PDDD = 0x00; Now we know what this line (PDDD &= ~0x10;) does. It simply clears (writes a 0) the PDDD<4> bit and does not affect the other bits in the register. This C line is also equivalent to PDDD = PDDD & ~0x10; If you are new to C programming and the actual operation of this line is a mystery to you symbols --------------------------------------------------= (ASSIGNMENT operator) & (bitwise ADD operator) ~ (bitwise COMPLEMENT operator) &= (bitwise AND and ASSIGN operator) --------------------------------------------------, understand the following

The hexadecimal 0x10 in binary is 0001 0000. ~(0x10) will become 1110 1111 since all bits are complemented (or inverted) by the bitwise complement operator ~. (NOTE: bitwise means that the operator ACT on each individual bits ) PDDD & ~0x10 will perform bit by bit AND operation between the two. For example, if PDDD has a current value 0x7A (0111 1010, this value could have been assigned previously or could be a random value during device power-up or reset), then the AND operation 0111 1010 & 1110 1111 0110 1010 (PDDD) ~(0x10) <-- only bit 4 is changed

Compare the value of the result with PDDD; notice that only bit 4 is changed (from 1 to 0, cleared), which is what we want! The result of the AND operation is assigned back to PDDD, overwriting the previous value of 0x7A. PDDD now has a value of 0x6A. PDDD<4> is 0, which means that PD4 pin is an output pin. (All other Port D pins is of no concern right now, since in our circuit they are unconnected)
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

The next line in the main function is PDAF &= ~0x10; //PD4 alternate function is off

This line will disable the alternate function of pin1. Pin1 of the Z8F MCU (PD4/RXD1) has 2 functions. The first is as a GPIO (General Purpose Input/Output) pin (i.e PD4). It can be used either as an output or as an input pin. The GPIO function is the default function. The second function of Pin 1 is as a receiver pin (RXD1) of the UART1 hardware of the MCU. This is its alternate function. Since we dont need to use the alternate function (we are not using UART1 for serial communication, Pin 1 is configured as an output pin in the previous line), the alternate function of the pin must be explicitly turned off by clearing the corresponding bit in the Port D Alternate Function Rgister (PDAF). PDAF is another internal register of the Z8F MCU that directly affect the operation of the Port D pins. To enable the alternate function of a Port D pin, set the corresponding bit in the PDAF register. (ex. if PD4 is used for serial communication, PDAF<4> should be 1). To disable the alternate function of a Port D pin, clear the corresponding bit in the PDAF register. (e. if PD4 is used as an output pin (ex. an LED is connected) or as an input (ex. a PUSHBUTTON is connected), PDAF<4> should be 0) Note: The same applies to other port pins. For example, to enable/disable Port C alternate functions, write to the PCAF register.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

The third line inside the main function is PDOC &= ~0x10; //PD4 is a push/pull output

The value of the Port D Output Control (PDOC) register determine whether a Port D output pin is a PUSH/PULL output pin or as an OPEN-DRAIN output pin. Push/pull output is the typical behavior of an output pin. Sending a logic high (by writing to the PDOUT register, for example, more on this later) at the pin mean a 3.3 V will appear at the pin terminal; 0 V if logic low. If the output pin is configured as an open-drain output, sending a logic high at the pin means the pin terminal will be disconnected from the internal MCU circuitry. The pin is said to be floating, since it does not output any voltage signal (your VOM might say there is 0 volt at the pin, that is, pin is grounded; but to be exact, the pin is not connected (virtual disconnection by high-impedance) to anything, not even to circuit ground). An appropriate pull-up resistor is needed at the pin terminal (the other terminal of the resistor is connected to the positive supply) to output a positive voltage

corresponding to high logic. If a logic low is send at the output pin, 0 V will appear. To configure an output pin for push/pull operation, the corresponding bits of the output control register of this port should be cleared. If pin is used for open-drain operation, register bits should be set. In our example program, since PD4 will be used as push/pull, the PDOC<4> should be 0. If we mistakenly set PDOC<4>, the LED will not turn on.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

The fourth line is PDHDE |= 0x10; //PD4 high-drive enable on

This will enable the PD4 pin to provide a high-current output. Port D High Drive Enable (PDHDE) register controls the high-current output drive operation of the Port D pins. If the PDHDE bits is set (high current drive on), the corresponding Port D pins can source as much as 25mA. If the PDHDE bits are cleared (high current drive off), the corresponding Port D pins can only provide 1-2 mA. In our simple project, since an LED is connected at the PD4 pin we need to enable the high-drive operation since the LED will normally require 10-20 mA current. The line PDHDE |= 0x10; will set PDHDE<4> (remember, to set a bit, OR it bit with 1). The symbol | is the bitwise OR operator.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Next line is PDOUT |= 0x10; //PD4 output a logic 1 (i.e. 3.3 V) The line above will send a logic 1 at PD4 pin (AGAIN, ORing a bit with 1 will set this bit!). The LED will light up since a 3.3 V will appear at the pin. Port D Output Data Register (PDOUT) contains the 8-bit data that is sent to the Port D output pins. The value stored in the PDOUT register will only appear if the corresponding pin is configured as an output pin (by clearing the corresponding bits in the PDDD register) and the alternate function is disabled (by clearing corresponding bits in the PDAF register).
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

last line while(1); //infinite loop

this is an infinite loop (the condition is always true, i.e. 1 = true) that does nothing other than to keep the MCU from exiting the main program. the summary of the simple c program: configure PD4 pin as output disable PD4 alternate function configure PD4 pin for push/pull output enable PD4 high-current drive output a 3.3 voltage (logic 1) at PD4

all you get is a conducting LED (on) on your DIY board.


Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Lets modify the previous sample code a little If you decide to use PC5 pin (pin 3) instead of PD4 (pin 1) you may need to change the register names (i.e. PCDD instead of PDDD). In your DIY board, connect the LED to pin 3 instead of pin 1. Here is the complete code. Build and run with ZDS IDE.
#include <ez8.h> void main() { PCDD &= ~0x20; PCAF &= ~0x20; PCOC &= ~0x20; PCHDE |= 0x20; PCOUT |= 0x20; while(1); }

//PC5 //PC5 //PC5 //PC5 //PC5

is output alternate function is off is a push/pull output high-drive enable on output a logic 1 (i.e. 3.3 V)

//infinite loop

We also change to 0x20 from 0x01 since PC5 corresponds to bit 5 (0x05 = 0010 0000, review your binary/hexadecimal notation )
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Another similar example. if you use PD0 (pin 13), this pin doesnt have an alternate function (check the datasheet) so we dont need to enable/disable its alternate function (i.e. there is no PDDD &= ~0x01; in the code below).
#include <ez8.h> void main() { PDDD &= ~0x01; PDOC &= ~0x01; PDHDE |= 0x01; PDOUT |= 0x01; while(1); }

//PD0 //PD0 //PD0 //PD0

is output is a push/pull output high-drive enable on output a logic 1 (i.e. 3.3 V)

//infinite loop

Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Using Functions
In the meantime, lets learn how to use C functions.

A function is simply a self-contained block of C code. A function has a name, and you can call/invoke a function using its name. When a function is called/invoked, the C code inside this function (that is, the function body!) is executed. Why do we need to use functions? 1. Functions can be invoked anywhere in the program. Which means that a particular code (the code inside the function) can be used again and again, resulting to a much smaller program size. Without functions, programs can grow very, very large since you would need to replicate the same code again and again at several points in the program. 2. Using functions allow us to easily break a large program into manageable chunks. Thinks of functions as subprograms inside your C program. Lets use the original blinker program to explain how functions work.
#include <ez8.h> void delay(void) //software delay { int i,j; for(i = 0x00; i<0xFF; i++) for(j = 0x00; j<0xFF; j++); //delay loop consisting of nested FOR loops with empty statements } void main() { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; while(1){ delay(); PDOUT ^= 0x10; } }

//PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on //initial output low //infinite loop //call delay() routine //toggle output

Build the C program in the ZDS II IDE, download the code to Zilog Encore! board, and run it. Don t forget to connect PD4 pin (pin 1) to the on-board LED (there are two LEDs, use one). The LED will blink a few times each second. Read through the blinker code. The first line is the familiar preprocessor directive.
#include <ez8.h>

After the preprocessor directive is a C function. Notice that it is outside the main( ) function.
void delay(void) //software delay { int i,j; for(i = 0x00; i<0xFF; i++) for(j = 0x00; j<0xFF; j++) ; //this nested for loops //introduce delay }

The name of the function is delay. The first void means that the function do not pass a value after it finished executing, back to the main function (i.e. return a value) and the second void inside the parenthesis means that the function does not accept a value when it is invoked. The C code inside the curly braces is the function body. A quick glance at the body of the delay function shows that it is composed of two nested FOR loops that does nothing useful other than to waste CPU execution time (by incrementing from 0 to 255, 255 times!). This function is a rather simple way of delaying CPU execution for a fraction of a second, a software delay! The use of the delay function will allow us to notice the blinking (on & off & on!) action of the LED. Without it, the LED will blink very fast (several thousand times per second), giving the illusion that it is always on!

Note1: You can use any name for your function, but it is best to use descriptive names. Obviously, "delay" is a good name for the above function Note2: A main( ) function is actually a special function. The "delay" function above is technically called a user-defined function. The first five lines inside the main( ) function will configure PD4 pin as an output pin.
PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; //PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on //initial output low

Next is an infinite while( ) loop. (i.e. the condition will always be true since the value inside the parenthesis is 1, and 1 = TRUE).
while(1){ delay(); PDOUT ^= 0x10; } //infinite loop //call delay() routine //toggle output

The line
delay( );

will invoke the delay function. The CPU will execute the block of code inside the delay function. After the CPU finish executing the delay function, it will then execute the instruction.
PDOUT ^= 0x10;

The ^ operator is the bitwise XOR operator. XORing a bit with 1 will toggle this bit. *dont forget this Since the lines
delay( ); PDOUT ^= 0x10;

is inside an infinite while loop, the result will be a blinking LED connected at PD4.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Read again the blinker code. Notice that the delay( ) function is written before the main( ) function. If you wrote the delay( ) function after main( ) like in the example below,
#include <ez8.h> void main() { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; while(1){ delay(); PDOUT ^= 0x10; } } void delay(void) //software delay { int i,j; for(i = 0x00; i<0xFF; i++) for(j = 0x00; j<0xFF; j++) ; }

//PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on //initial output low //infinite loop //call delay() routine //toggle output

you will get a compilation error message like this one WARNING (197) No function prototype "delay" in scope This is because when the compiler encounters the line
delay( );

inside the while loop, the compiler doesnt know what delay( ) is. This error is similar to using a C variable that is not declared before usage. This error can be remedied by writing the delay( ) function before the main( ) function, as in the original example.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Another solution is to write a function prototype before the main( ) function, like in the example below
#include <ez8.h> void delay(void); void main() { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; //This is the function prototype for the //delay function

//PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on

PDOUT &= ~0x10; while(1){ delay(); PDOUT ^= 0x10; } }

//initial output low //infinite loop //call delay() routine //toggle output

void delay(void) //software delay { int i,j; for(i = 0x00; i<0xFF; i++) for(j = 0x00; j<0xFF; j++) ; }
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

This is another blinker example, using 2 functions; init_LED( ) and delay ( ). Read the code below and see that the main( ) function become quite simplified since the block of code for PD4 pin initialization was transferred inside another function (i.e. init_LED( ) ). This improves readability, especially for more complex C programs.
#include <ez8.h> void delay(void); void init_LED(void); void main() { init_LED(); while(1){ delay(); PDOUT ^= 0x10; } } void init_LED(void) { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; } void delay(void) { int i,j; //function prototypes

//initialize PD4 pin //call delay routine //toggle output

//PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on //initial output low

Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Another example on C functions. In your C program, you can call other function(s) from within a function, like in this modified blinker program. (Youve gotta love the blinker )
#include <ez8.h> void delay(void); void init_LED(void); void call_func(void); void main() { call_func(); while(1){ delay(); PDOUT ^= 0x10; } } void call_func(void) { init_LED(); } void init_LED(void) { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; } void delay(void) { int i,j; for(i=0;i<=0xFF;i++) for(j=0;j<=0xFF;j++) ; } //this function will call //another function //call delay function //toggle output //function prototypes

//PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on //initial output low

In the code above, there are 3 user-defined functions; delay( ), init_LED( ), and call_func( ). At the start of the main( ) program, call_func( ) is invoked, which in turn will invoke the init_LED( ) function.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Another another example on C functions. When you call a function, you may pass value(s) to it. The function will then receive that value to be used for calculation, etc. In the next modified blinker example below (not again! ), we will modify the delay( ) function in order for it to accept an integer value. This value will be used to change the time delay of this delay( ) function.

#include <ez8.h> void delay(int value1); void init_LED(void); void main() { init_LED(); while(1){ PDOUT |= 0x10; delay(0x3FF); PDOUT &= ~0x10; delay(0x200); } } void init_LED(void) { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; } void delay(int value1) { int i,j; for(i=0;i<=value1;i++) for(j=0;j<=value1;j++) ; } //count from 0 to value //count from 0 to value //turn on LED //call delay( ) function and pass 1024 (3FF hex) to it //turn off LED //call delay( ) function and pass 512 (200 hex) to it //function prototypes

//PD4 pin is output //Alternate Function off //Push-pull output //High-Drive Enable on //initial output low

When you run the code in your board, you will notice a blinking LED that has an ON period 4x longer than the OFF period. Note: In the example above, the delay( ) function accepts only a single value, named value1 (ahahay! you can make functions that accepts more than 1 value. ). However,

Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Let us make a simple C program that will read an input pin. To do this, we will connect a pushbutton to PC1 pin (configured as an input pin) and an LED to PD0 pin (configured as an output pin). If the pushbutton is pressed the LED will turn on; if not pressed, LED is off. It cant get simpler than this. Refer to the figure below on how to connect a push button to a Z8F pin. We will need an additional 10k resistor.

If the pushbutton is not pressed, PC1 will read a logic 1 (approximately 3.3V at PC1 pin); if the button is pressed, it will read a logic 0 (0V). Set-up the Z8F board. Connect the PD0 pin to one of the on-board LED. In the picture below, the green wire connects the LED to PD0 pin; the red wire connects the input push button to PC1 pin; the blue wire connect to ground; the white wire provides 3.3V to the 10k resistor.

Power up the board. Run ZDS, make a new project and use the sample C program below.
#include <ez8.h> void init_pushbutton(void) { PCDD |= 0x02; PCAF &= ~0x02; } void init_LED(void) { PDDD &= ~0x01; PDOC &= ~0x01; PDHDE |= 0x01; PDOUT &= ~0x01; }

//PC1 pin is input //PC1 pin alternate function is off

//PD0 //PD0 //PD0 //PD0

pin is output pin is push/pull output high current drive circuitry is on pin initially output 0 voltage

void main() { unsigned char push_button = 0x00; init_pushbutton(); init_LED(); while(1) { //configure PC1 pin //configure PD0 pin

push_button = PCIN & 0x02; if (push_button == 0x00) PDOUT |= 0x01; if (push_button == 0x02) PDOUT &= ~0x01; } }

//read PC1 pin //if pushbutton is pressed //turn on LED //if pushbutton is NOT pressed //turn off LED

Download the code and run it. Try pressing the pushbutton and the LED will lit up.

At this time, the student should learn the following: Learn to create simple Z8F C programms in ZDS II IDE. Familiarize with internal registers that control the Z8F GPIO pins (PDDD, PDAF, PDOC, PDHDE, PDOUT, PDIN for port D, for example) Configure GPIO pin as input or output pins Comfortably use the following bit-wise operators: OR (|) , AND (&), INVERT (~), and XOR (^) for manipulating individual bits in registers and other variables. Use C functions to structure the program.

Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Interrupts on Z8 Encore MCU


Why use interrupts? (the WHAT I will explain later, maybe much later ) In order to understand the simple concept of INTERRUPTS, let me give you a wonderfully brilliant analogy. Today, you are expecting a visitor. This visitor called you up the day before and told you that he will drop by at your house to talk about a few things. However, he forgot to tell you what time he will come. If you are really expecting this person to come any moment, you might keep on glancing at the window many times during the hour. Or you might totally stop doing house chores and decided to just stand near the window to wait for the visitor, which could arrived any moment or maybe after the next 4 hours . Now this is rather silly, isnt it? You might ask me if there is a better way to wait for a house visitor than polling/checking/looking at the window. I know, I know. The best thing you would rather do is to keep on working while waiting for the (*drums!) (tada!) DOOR BELL to ring. Now, this wonderful invention, the door bell (an interrupting mechanism) allows you to be INTERRUPTED by a visitor standing outside your gate. Genius, right? So you have two ways of knowing when the visitor arrives: (1) polling (waiting/glancing for hours at the window ) and (2) interrupts (using doorbell) (for the sake of discussion, lets assume you can't do both. If you insist you can, i'll force you to explain WHY their is
GRAVITY )

The same concept applies to microcontroller operation. In the silly analogy above, think of the house as the microcontroller, you as the CPU (that is inside the microcontroller), the visitor as an interrupt source (An interrupt source could be a changed logic signal at a particular input pin, a timer that finished counting, a character arriving at a communication port, etc.), and the wonderful door bell as an interrupt controller (the visitor
interrupts the doorbell, which in turns interrupts you. You can see it that way. Hopefully you get the point already, if not read again. Or the read again the whole thread hehe.)

Using interrupt in your programs has lots of benefits. Mainly, it allows you to momentarily stop main( ) program execution and let the CPU execute a special function instead that responds to the interrupt (this function is called an Interrupt Service Routine, ISR). This is the fundamental to MCU multitasking, and greatly expands the capability of the program. With interrupts, you can turn on an LED when a button is pressed, at the same time wait for a character being transmitted from PC to the MCU, or wait until the ADC finishes acquiring digital values of analog signal, or typing a response to a thread in E-lab.ph forum, or nuke a city somewhere in Africa. You feel powerful if you know everything about interrupts hehe. Simply put, using Interrupts result to a more efficient code since precious CPU execution time is not wasted on polling. For a Z8F MCU, there are 24 possible interrupt sources. 12 of these interrupts come from the GPIO pins configured as input pins. They detect external hardware signals, like when a signal transitions from high to low (i.e. falling) or low to high (i.e. not climbing, but rising hehe ). The remaining 12 signals come from the on-chip peripherals like Timer registers, analog-to-digital converter, UARTs, etc. Z8F GPIO pin as an Interrupt Source Let's have a simple Z8F C program that responds to an interrupt from an input pin. This is simply a modified program of the input pushbutton in our last example. A pushbutton is connected to PA6 (pin 36) configured as an input pin and an LED connected to PD0 pin (pin 13).

The C program will perform the following: when the MCU detects the falling edge of the signal at PA6 (when the PB is pressed), an interrupt will be generated internally (the hardware mechanism of interrupt btw, is transparent to the user. You and me don't see it
but it happen inside the MCU ).

The interrupt will signal to the CPU and the CPU will execute an Interrupt Service Routine (ISR). The ISR will simply toggle the LED at PD0 (that is, if the LED is on initially, it will be turned off when PB is pressed and vice versa). Here is the sample code:
//Pushbutton connected to PA6 //LED connected to PD0 #include <ez8.h> void init_LED(void){ PDDD &= ~0x01; PDOC &= ~0x01; PDHDE |= 0x01; PDOUT &= 0x01; } #pragma interrupt void isr_PUSHBUTTON(void) { DI(); PDOUT ^= 0x01; EI(); } void init_PUSHBUTTON(void) { DI(); PADD |= 0x40; PAAF &= ~0x40; IRQPS &= ~0x40; IRQES &= ~0x40; //PD0 //PD0 //PD0 //PD0 is output is push/pull output HDE is on is initially low

//this is the Interrupt Service Routine //disable global interrupt //toggle PD0 output voltage //enable global interrupt //initialize PA6 //disable all interrupts //PA6 is input //PA6 alternate function is off //configure PA6 to detect interrupt //configure PA6 to detect falling edge

SET_VECTOR(P6AD, isr_PUSHBUTTON); //tells the CPU that when it detect an // interrupt at PA6 (P6AD is the code, declared in ez8.h) // the CPU will execute the isr_PUSHBUTTON() ISR IRQ1ENH |= 0x40; IRQ1ENL |= 0x40; IRQ1 &= ~0x40; EI(); } void main() { init_LED(); init_PUSHBUTTON(); while(1) { } } //do nothing, just wait for interrupt // to occur, then execute ISR //enable PA6 interrupt and priority //cleard PA6 interrupt flag //enable all interrupts

//initialize PA6 //initialize PD0

Download and run the program in your DIY board. Press the PB repeatedly and the LED will toggle accordingly. Lets dissect the above code. The first line in the main( ) function invokes the init_LED( ) function.
init_LED(); //initialize PA6

This will initialize the PD0 pin as output.


void init_LED(void){ PDDD &= ~0x01; PDOC &= ~0x01; PDHDE |= 0x01; PDOUT &= 0x01; } //PD0 //PD0 //PD0 //PD0 is output is push/pull output HDE is on is initially low

Take note that PD0 doesnt have an alternate function so we dont have to modify the PDAF register. The 2nd line in the main( ) function is
init_PUSHBUTTON(); //initialize PD0

and will initialize the PA6 pin as an input as well as configure it as an interrupt source. The first line inside the init_PUSHBUTTON( ) is
DI(); //disable all interrupt

DI means Disable Interrupt. To be accurate, this function tells the CPU to IGNORE ALL interrupts. We dont want the CPU to be interrupted while it is configuring an interrupt source. This is a good programming technique to disable ALL interrupt from triggering every time you configure an interrupt source, for precautionary measure. You might argue that there is only 1 interrupt source (PA6), and we havent configured it yet, so how can the CPU be interrupted by ANOTHER interrupt when there is only 1 interrupt in the first place geezz! . Thats is correct you argue correctly, but good programming practice is desired, no matter how simple the program is. It is probable that a lightning or the Great Pumpkin can trigger the interrupt controller circuitry, when you least expect it hehe The next 2 lines
PADD |= 0x40; PAAF &= ~0x40; //PA6 is input //PA6 alternate function is off

Configure PA6 as input and turn off its alternate function (which is as an SCL pin for the I2C module). The next two lines modifies the Interrupt Port Select register (IRQPS) and the Interrupt Edge Select register (IRQES).
IRQPS &= ~0x40; IRQES &= ~0x40; //configure PA6 to detect interrupt //configure PA6 to detect falling edge

Clearing a bit in the IRQPS configures a corresponding port A (Port D if set) bit as an interrupt source. Clearing a bit in the IRQES configures the pin to generate an interrupt when it detects a signal transition from high to low (i.e. falling edge). When the button is pressed, falling edge is detected when the 3.3V changed to 0v. The next line is
SET_VECTOR(P6AD, isr_PUSHBUTTON);

SET_VECTOR( ) is a built-in function. The line above tells the MCU to go to the starting address (i.e. go to address = to vector) of the isr_PUSHBUTTON( ) interrupt servicing function and start executing the instructions in that address, when an interrupt from PA6 occur. (P6AD = interrupt from PA6 pin, declared in the ez8.h file). These 2 lines below enable the PA6 interrupt mask, which means that the PA6 interrupt configured in the previous lines can now ?really interrupt the CPU. And it has the highest priority, meaning the CPU will respond to it first if for example that another interrupt source (configured with a lesser priority) triggered at the same time. Yeah yeah there is only 1 interrupt,
priorities is irrelevant Duh! More examples later about multiple interrupt sources.

IRQ1ENH |= 0x40; IRQ1ENL |= 0x40;

//enable PA6 interrupt and has highest priority

2nd to the last line


IRQ1 &= ~0x40; //clear PA6 interrupt flag

Every time the PA6 interrupt triggers, bit 6 in the Interrupt Request 1 (IRQ1) register will be set, indicating to the CPU that this interrupt source (PA6) REQUESTS to be responded to. This is how the CPU knows what interrupt source triggered, by checking which interrupt flag in the IRQ1 register is set. If the interrupt is vectored (by the SET_VECTOR( ) function), the CPU will execute the ISR as soon as it can. After the ISR finished executing, the CPU automatically clears the interrupt flag. Last line
EI(); //enable all interrupts

commands the CPU to start responding to any interrupt that occurs. If you check the whole program, you will notice that there are 4 functions: main( ) init_LED( ) init_PUSHBUTTON( ) isr_PUSHBUTTON( ) The 4th function above is the ISR. You can name the ISR function any name you want (like paranz_pogi( )) and declared it like
#pragma interrupt void paranz_pogi(void) { }

and that would be ok. The ISR declaration is proceeded by

#pragma interrupt

//this is the Interrupt Service Routine

which tells the compiler that the next function declared immediately below this line is an ISR. In our example, we have the ISR code
#pragma interrupt void isr_PUSHBUTTON(void) { DI(); PDOUT ^= 0x01; EI(); } //this is the Interrupt Service Routine //disable global interrupt //toggle PD0 output voltage //enable global interrupt

and a quick check at the block of code inside the function body simply tells us that the ISR toggles the PD0 output when invoked. It is important to disable all interrupts (DI( );) at the start of the ISR and enable at the end of the ISR (EI( );). Why? Because we dont want the CPU to be interrupted again (by another source) while it is being currently interrupted. Remember that a CPU is a sequential machine and it can only do one thing at a time, albeit, at a very fast speed like millions of times per second. It is oki for the CPU to be interrupted while executing main( ) codes but it is not oki to be interrupted while executing ISR, mainly due to internal mcu hardware limitations. An ISR cannot be invoked by a user code. It is a special kind of function that is only automatically executed when invoked by the MCU hardware itself (i.e. the Z8F interrupt controller). The SET_VECTOR( ) command will tell the interrupt controller to command the CPU to jump to the appropriate ISR when the correct interrupt source triggers. Z8F GPIO interrupt + "blinker" program In the previous GPIO interrupt sample program, you will notice that the WHILE loop in the main( ) function is empty. It simply does nothing. The program merely does nothing other than to wait for the interrupt to trigger again and again. We deliberately made that to greatly simplify the discussion on interrupt by using a simple program as an example. Now its time to let the MCU do other things while waiting for interrupts to trigger. Its time to multitask the Z8F MCU.

In this next sample program, we will simply combine the blinker and the GPIO interrupt program. The new main( ) will have a non-empty WHILE loop that toggles an LED connected to PD1 pin while another LED connected to PD0 is toggled by the interrupt coming from a pushbutton connected to PA6 pin. In your DIY board, simply improve the previous set-up by connecting the PD1 pin (using a solid wire) to the 2nd on-board LED. Here is the improved sample code:
//Pushbutton connected to PA6 //LED1 connected to PD0 //LED2 connected to PD1 #include <ez8.h> void init_LED(void){

PDDD &= ~0x03; PDOC &= ~0x03; PDHDE |= 0x03; PDOUT &= 0x03; } #pragma interrupt void isr_PUSHBUTTON(void) { DI(); PDOUT ^= 0x01; EI(); } void init_PUSHBUTTON(void) { DI(); PADD |= 0x40; PAAF &= ~0x40; IRQPS &= ~0x40; IRQES &= ~0x40;

//PD0 //PD0 //PD0 //PD0

& & & &

PD1 PD1 PD1 PD1

are are HDE are

output push/pull output are on initially low

//this is the Interrupt Service Routine //disable global interrupt //toggle LED1 //enable global interrupt //initialize PA6 //disable all interrupts //PA6 is input //PA6 alternate function is off //configure PA6 to detect interrupt //configure PA6 to detect falling edge

SET_VECTOR(P6AD, isr_PUSHBUTTON); //tells the CPU that when it //detect an interrupt at PA6 (P6AD is the code, declared in ez8.h, //the CPU will execute the isr_PUSHBUTTON() ISR IRQ1ENH |= 0x40; IRQ1ENL |= 0x40; IRQ1 &= ~0x40; EI(); } void delay(void) { int i,j; for(i=0;i<=0XFF;i++) for(j=0;j<=0XFF;j++) ; } void main() { init_LED(); init_PUSHBUTTON(); while(1) { delay(); PDOUT ^= 0x02; } } //enable PA6 interrupt //cleard PA6 interrupt flag //enable all interrupts

//delay loop

//initialize PA6 //initialize PD0

//call delay( ) //toggle LED2

Download and run the program in your Z8F board. The LED connected to PD1 pin will blink few times per second. And if you press the PB, the other LED connected to PD0 pin will toggle.

Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Timers on Z8 Encore! (Introduction...)


Many simple to advance MCU applications require time-related operations. Oftentimes, the MCU applications involve: providing timing reference to control physical events for known periods (i.e. blinking an LED every second) counting a repetitive event (i.e. count the number of times a pushbutton is pressed) measuring the time duration of some physical events (i.e. how many seconds a switch is open or how many milliseconds a logic signal is high). To perform such time-related functions, microcontrollers contain timer peripherals as part of their hardware architecture (NOTE1). Timers are just specialized internal registers that typically hold 8/16-bit values (NOTE2). In its basic operation, 8-bit timers count from 0 - 255 and 16-bit timer from 0 - 65535. When a timer finished counting (i.e. timer value = 255 for 8-bit timers), it may a signal an interrupt to the CPU.

NOTE1: A peripheral refers to an auxiliary circuitry inside a microcontroller that is functionally not part of the embedded computer. Remember that the embedded computer inside the microcontroller consists of the usual CPU, RAM, ROM, & GPIO. Other auxiliary circuits are added to this embedded computer to make it a powerful, versatile computing device. Example of MCU peripherals are Timers, UART, Analog-to-digital converters (ADC), Direct Memory Access (DMA) Controllers, etc. In microcontroller literatures, peripherals are also called as modules (i.e. timer peripheral = timer module). A microcontroller device, therefore, can be viewed strictly as an embedded computer plus the peripherals. NOTE2: Dont think of Timers as similar to a WALL CLOCK that gives you the absolute time in hour, minutes, and seconds . A wall clock is, well, a timer but a timer peripheral inside a microcontroller is absolutely not a clock However, it is possible to build a microcontroller-based DIY wall clock that uses the timer peripheral of the microcontroller.

The 40-pin version of the Z8 Encore! has 3 16-bit timer modules; Timer 0, Timer 1, & Timer 2. It can operate in several modes. According to the Z8 Encore data sheet, the different timer modes are the following: ONE-SHOT mode CONTINUOUS mode COUNTER mode PWM mode CAPTURE mode COMPARE mode GATED mode CAPTURE/COMPARE mode The first 3 modes are rather simple to use, while the last 5 timer modes are fairly advanced. (don't fret about learning all modes. In time, you will learn how to use all of these ) You may refer to the device data sheet for a description on the operation of different timer modes. To get us started using the Z8F timer module, we will start with the continuous mode. In other microcontrollers, Continuous mode is also called the timer mode (like in PICmicro devices).

In this discussion, lets assume we are using Timer 0. In Continuous mode, the Timer 0 counts from 1 - 65535 (216 - 1). The timer increment (increase by 1) every clock cycle (NOTE1). If the microcontroller has an 18.432 Mhz crystal oscillator, the clock period is 1/18.432 x 10^6 = 0.54253 nanoseconds. Therefore, Timer 0 increments every 0.54253 nansosecond. This also means that Timer 0 finished counting from 1 - 65535 every 0.54253 x 65535 = 3.5555 milliseconds (NOTE2). This is called the time-out period (in Zilog literature). When Timer 0 reaches the final value (65535), it overflows back to 0, generates an interrupt (if interrupt is enabled) and starts counting again. This goes on and on, until the timer is disabled (via software) or when the microcontroller is shut down. NOTE1: A timer generally operates independent of the CPU except only in a few parts of the main( ) program when the timer is initialized, enabled (normally at the start of main( )), restarted, or disabled. The timer increments without software control since the incrementing mechanism is done completely in hardware (i.e. timer input is the system clock). You can think of the timer as running in parallel (that is, simultaneously) with the main( ) program, once it is enabled. The advantage of timers (this should be obvious at this time ) is that the MCU program is mainly relieved of performing time-related functions, letting the CPU perform other more meaningful operations and responding only to timer interrupts from time to time. NOTE2: For some strange reasons, the Z8F timers starts counting from 1 and not 0. Oftentimes, it is desirable to slow down the timer. This is done using PRESCALER. If a prescaler value is 2, that means the Timer 0 increments every 2 clock cycles, instead of 1 (the default prescaler value). If the prescaler is 32, the timer increments once every 32 clock cycles. For the Z8F timers, the possible prescaler values are 1, 2, 4, 8, 16, 32, 64, and 128. As an example for a Z8F MCU with an 18.432 MHz crystal, the slowest time-out period for Timer 0 is when it is counting from 1 - 65535 with a prescaler of 128, resulting to a time-out period of 0.455 second (0.54253 nanosecond x 65535 x 128). If you want the time-out period to be more than 0.455 second, you may use a lower crystal oscillator. However, a lower crystal oscillator this may not be a good thing since the whole system suffers a decrease in performance due to a lower system clock. If we want a fast time-out period, we may use a lower prescaler value and/or use a reload value lesser than 65535. This means that Timer 0 will count from 1 to the selected reload value. The fastest possible time-out period occurs when using a prescale value of 1, reload value of 2 (that is, timer counts from 1 to 2!), and a maximum system clock of 20 Mhz (NOTE1) . NOTE1: If you still want faster a time-out period, you may overclock the Z8F MCU , but do so at your own risk with 21+ or 22+ Mhz crystal. Z8F Timer as an Interrupt Source The relevant internal registers needed to configure Timer 0 in continuous mode are the following: Timer 0 Control Register 1 (T0CTL1) (NOTE1) This register will enable/disable the timer, configure the timer mode, and assign the prescaler value. . Start

FIGURE 1. T0CTL1 register

TEN (Timer ENable bit) 1 - Enable timer 2 - Disable timer PRES (prescaler) 000 = Divide by 1 001 = Divide by 2 010 = Divide by 4 011 = Divide by 8 100 = Divide by 16 101 = Divide by 32 110 = Divide by 64 111 = Divide by 128 TMODE (Timer mode) 000 = ONE-SHOT mode 001 = CONTINUOUS mode 010 = COUNTER mode 011 = PWM mode 100 = CAPTURE mode 101 = COMPARE mode 110 = GATED mode 111 = CAPTURE/COMPARE mode Timer 0 High and Low Byte registers (T0H & T0L) - These two 8-bit registers contains the 16-bit value of Timer 0. Timer Reload High and Low Bytes registers (T0RH & T0RL) - These two 8-bit registers contains the 16-bit reload value. If T0H & T0L equals T0RH and T0RL, Timer 0 overflows and reset back to 1 and starts incrementing again. To use the Timer 0 as an interrupt source, we also need to configure the following interrupt control registers: Interrupt 0 Enable High & Low Registers (IRQ0ENH & IRQ0ENL) These two 8-bit registers enable Timer 0 interrupt and select level of priority. Refer to Figure 4 below for appropriate settings of the T0ENH and T0ENL bits.

FIGURE 2. IRQ0ENH register

FIGURE 3. IRQ0ENL register

FIGURE 4. Interrupt enable and priority settings. To configure Timer 0 in Continuous mode, follow these steps: 1. Disable timer by clearing the TEN bit in the T0CTL1 register. 2. Assign prescaler value in PRES bits in the T0CTL1 register. 3. Assign timer mode values in TMODE bits T0CTL1 register. 4. Assign initial timer value to T0H and T0L registers (normally it is 1). The Timer 0 will increment from this initial value to reload value but this will only affect the first time-out period. Every succeeding timer overflow, counting will start at 1. 5. Assign reload value to T0RH and T0RL register. 6. Enable Timer 0 interrupt in the IRQ0ENH and IRQ0ENL (T0ENH & T0ENL bits) registers and assign interrupt priorities. Refer to Figure 4 above for the appropriate settings. 7. Clear the Timer 0 interrupt flag (T0I bit in the IRQ0 register) 8. Enable timer by setting the TEN bit. The timer will start incrementing immediately. Lets make a program that will toggle an LED connected to PD4 pin every time Timer 0 reloads. Every time the Timer 0 value (stored in the T0H and T0L registers) reaches the reload value stored in the reload registers (T0RH & T0RL), the Timer 0 will reset back to 1, generates an interrupt, and causes the CPU to execute the ISR. The ISR will simply toggle the PD4 pin. We will configure Timer 0 in continuous mode and also as an interrupt source. We will use a prescaler of 128 and a reload value of 65535; this will result to a time-out period of about 0.455 seconds. Connect one of the two on-board LED to PD4 pin. Build the sample program below, download to board, and run. The LED will blink once every 0.91 seconds.
#include<ez8.h> #pragma interrupt void isr_Timer0(void) { DI(); PDOUT ^= 0x10; EI(); }

//disable interrupt //toggle PD4 output //enable interrupt

//this function will configure PD4 pin void init_LED(void) { PDDD &= ~0x10; //PD4 is output PDAF &= ~0x10; //PD4 AF off PDOC &= ~0x10; //PD4 open-drain circuitry is off PDHDE |= 0x10; //PD4 high drive circuitry is enabled PDOUT |= 0x10; //PD4 is initially high, LED is on }

//this function will configure Timer 0 void init_Timer0(void) { int reload_val = 65535; DI(); T0CTL1 &= ~0x80; T0CTL1 |= (0x07 << 3) | 0x01; T0H = 0x00; T0L = 0x01; T0RH = (reload_val >> 8); T0RL = (reload_val & ~0xFF00); SET_VECTOR(TIMER0, isr_Timer0); IRQ0ENH |= 0x20; IRQ0ENL |= 0x20; IRQ0 &= ~0x20; T0CTL1 |= 0x80; EI(); } void main(void) { init_LED(); init_Timer0(); while(1); }

//reload value //disable interrupt //disable Timer 0 //prescaler = 128 //continuous mode //initial value is 1 //Timer 0 increments from 1 to 65535 //RELOAD value loaded //when TIMER0 interrupt occurs, tell //CPU to execute the isr_TIMER0 ISR //enable Timer 0 interrupt with //LEVEL 3 priority //clear previous/pending TIMER0 int //enable timer 0 //enable interrupt

//initialize PD4 //initialize Timer 0 //infinite loop

Lets improve the previous blinker example with another modified blinker program (again! ). 2 LEDs will blink this time. The first LED (connected at PA2) will toggle every time TIMER0 reloads, and the second LED (connected at PD4) will toggle under software delay. here is the sample code:
#include <ez8.h> #define RELOAD_VALUE 65535 //this is a software delay void delay(void) { int i,j; for(i=0;i<0xFF;i++) for(j=0;j<0xFF;j++) ; } void init_LEDS(void) { PDDD &= ~0x10; PDAF &= ~0x10; //this is the reload value

//initialize PD4 pin

PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= 0x10; PADD &= ~0x04; PAAF &= ~0x04; PAOC &= ~0x04; PAHDE |= 0x04; PAOUT &= 0x04; } //this is the TIMER0 interrupt service routine #pragma interrupt void isr_TIMER0(void) { DI(); PAOUT ^= 0x04; //toggle PA2 output EI(); } //initialize TIMER0 in Continous mode, reloads every 0.455 seconds void init_TIMER0(void) { DI(); //disable all interrupt T0CTL1 &= ~0x80; T0CTL1 |= (0x07 << 3) | 0x01; T0H = 0x00; T0L = 0x01; T0RH = (RELOAD_VALUE >> 8); T0RL = (RELOAD_VALUE & ~0xFF00); //disable Timer 0 //prescaler = 128 //continuous mode //initial value is 1 //Timer 0 increments from 1 to 65535 //RELOAD value loaded //higher byte of the 16-bit RELOAD_VALUE //is shifted 8 bits to the right //and stored to T0RH register //higher byte is zeroed and //lower byte of the 16-bit RELOAD_VALUE //is stored to T0RL register //when TIMER0 interrupt occurs, tell //CPU to execute the isr_TIMER0 ISR //enable Timer 0 interrupt with //LEVEL 3 priority //clear previous/pending TIMER0 int //enable timer 0 //enable all interrupt //initialize PA2 pin

SET_VECTOR(TIMER0, isr_TIMER0); IRQ0ENH |= 0x20; IRQ0ENL |= 0x20; IRQ0 &= ~0x20; T0CTL1 |= 0x80; EI(); } void main() { init_LEDS(); init_TIMER0(); while(1) { delay(); PDOUT ^= 0x10; } }

//initialize PD4 and PA2 as output pins //initialize TIMER0 as continuous mode timer

//call delay() function //toggle PD4 output

download and run the program. 1 LED will blink several times per second while the other LED will blink approximately every 0.91 (2 x 0.455) seconds.

Heres another example using multiple interrupt sources. TIMER0 and TIMER1 are used as interrupt sources. LED1 (at PA2 pin) will toggle every time TIMER 0 reloads and LED2 (at PD3 pin) will toggle when TIMER 1 reloads. A third LED (at PD4) will toggle under software delay. Sample code:
/*hardware: - LED1 at PA2 (blink under TIMER0, 0.455 seconds reload period) - LED2 at PD3 (blink under TIMER1, 0.455/2 seconds reload period) - LED3 at PD4 (blink under software delay, a few timers per second) */ #include <ez8.h> #define RELOAD_VALUE1 65535 #define RELOAD_VALUE2 32767 //this is a software delay void delay(void) { int i,j; for(i=0;i<0xFF;i++) for(j=0;j<0xFF;j++) ; } void init_LEDS(void) { PDDD &= ~0x18; PDAF &= ~0x18; PDOC &= ~0x18; PDHDE |= 0x18; PDOUT &= ~0x18; PADD &= ~0x04; PAAF &= ~0x04; PAOC &= ~0x04; PAHDE |= 0x04; PAOUT &= ~0x04; } //this is the TIMER0 interrupt service routine #pragma interrupt void isr_TIMER0(void) { DI(); PAOUT ^= 0x04; //toggle PD3 output EI(); } //this is the TIMER1 interrupt service routine #pragma interrupt void isr_TIMER1(void) { DI(); PDOUT ^= 0x08; //toggle PA2 output EI(); } //this is the reload value for TIMER0 //this is the reload value for TIMER1

//initialize PD4 and PD3 pin

//initialize PA2 pin

//initialize TIMER0 in Continous mode, reloads every 0.455 seconds void init_TIMER0(void) { DI(); //disable all interrupt T0CTL1 &= ~0x80; T0CTL1 |= (0x07 << 3) | 0x01; T0H = 0x00; T0L = 0x01; //disable Timer 0 //prescaler = 128 //continuous mode

//initial value is 1 //Timer 0 increments from 1 to 65535 //RELOAD value loaded T0RH = (RELOAD_VALUE1 >> 8); //higher byte of the 16-bit RELOAD_VALUE is shifted // 8 bits to the right and stored to T0RH register T0RL = (RELOAD_VALUE1 & ~0xFF00); //higher byte is zeroed and lower byte of the 16-bit // RELOAD_VALUE is stored to T0RL register SET_VECTOR(TIMER0, isr_TIMER0); IRQ0ENH |= 0x20; IRQ0ENL |= 0x20; IRQ0 &= ~0x20; T0CTL1 |= 0x80; EI(); } //initialize TIMER0 in Continous mode, reloads every 0.455 seconds void init_TIMER1(void) { DI(); //disable all interrupt T1CTL1 &= ~0x80; T1CTL1 |= (0x07 << 3) | 0x01; //disable Timer 0 //prescaler = 128 //continuous mode T1H = 0x00; //initial value is 1 T1L = 0x01; //Timer 1 increments from 1 to 65535 //RELOAD value loaded T1RH = (RELOAD_VALUE2 >> 8); //higher byte of the 16-bit RELOAD_VALUE2 is shifted 8 // bits to the right and stored to T1RH register T1RL = (RELOAD_VALUE2 & ~0xFF00); //higher byte is zeroed and lower byte of the 16-bit // RELOAD_VALUE2 is stored to T1RL register SET_VECTOR(TIMER1, isr_TIMER1); IRQ0ENH |= 0x40; IRQ0ENL &= ~0x40; IRQ0 &= ~0x40; T1CTL1 |= 0x80; EI(); } void main() { init_LEDS(); init_TIMER0(); init_TIMER1(); while(1) { delay(); PDOUT ^= 0x10; } } //when TIMER1 interrupt occurs, tell //CPU to execute the isr_TIMER1 ISR //enable Timer 1 interrupt with //LEVEL 2 priority //clear previous/pending TIMER1 interrupt //enable Timer 1 //enable all interrupt //when TIMER0 interrupt occurs, tell CPU to execute //the isr_TIMER0 ISR //enable Timer 0 interrupt with //LEVEL 3 priority //clear previous/pending TIMER0 int //enable Timer 0 //enable all interrupt

//initialize PD4 and PA2 as output pins //initialize TIMER0 as continuous mode timer //initialize TIMER1 as continuous mode timer

//call delay() function //toggle PD4 output

Download and run the program. You will have 3 blinking LEDs. NOTE: Since the DIY board has only 2 on-board LEDs, just use another LED mounted on an extra breadboard. LED2 will blink twice faster than LED1 since Timer 1 reload twice faster than Timer 0. Both timers are in continuous mode with prescaler value of 128. Timer 0 has the maximum reload value of 65535 while Timer 1 reload value is half of Timer 0. Timer 0 and Timer 1 are configured as interrupt sources. Timer 0 interrupt has LEVEL 3 priority while Timer 1 has LEVEL 2 priority, which means that if both timer interrupts triggers at exactly the same time, Timer 0 will be executed first since it has a higher priority.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Lets have another example, this time with 3 interrupt sources, 1 each from Timer 0 and Timer 1, and another from an input pin. Refer to the circuit below. (the oscillator circuits, VREF circuits, and DBG circuits are not shown for simplicity)

There are 4 LEDs and 2 pushbuttons. LED2 (connected at PD3) will turn on/off every 0.455 seconds, that is, every time TIMER 0 reloads. LED3 (at PA2 pin) will do the same every 0.455/2 seconds when TIMER 1 reloads. LED1, at PD4 pin, will toggle if Pushbutton1 (at PD6 pin) is pressed. In addition, LED4 (connected at PB0) will turn on if Pusbutton2 (at PD1 pin) is pressed; off otherwise. In the sample program, Timer 0 is configured in Continuous mode with a prescaler of 128 and reload value of 65535. Timer 1 is also configured in Continuous mode, with prescaler of 128 but with a reload value of 32767, half of Timer 0s. (hence, Timer 1 will reload twice faster than Timer 0, and LED3 blink twice faster than LED2). The timer interrupts are also enabled. PD6 is configured as GPIO interrupt source, with falling edge detection.

The sample program will essentially demonstrate the benefits of using interrupts to multitask the Z8F microcontroller. There are three interrupt service routines (ISRs). There are also 4 tasks, three in the ISRs and 1 inside the infinite loop within the main() function. In Summary: - LED1 at PD4 (toggle when Pushbutton1 is pressed, via ISR) - LED2 at PD3 (blink under TIMER0, 0.455 seconds reload) - LED3 at PA2 (blink under TIMER1, 0.455/2 seconds reload) - LED4 at PB0 (Turn on when Pushbutton 2 is pressed) - Pushbutton1 at PD6 (PD6 configured as interrupt source, falling edge signal) - Pushbutton2 at PD1 (PD1 configured as input)
#include <ez8.h> #define RELOAD_VALUE1 65535 #define RELOAD_VALUE2 32767 void init_LEDS(void) { PDDD &= ~0x18; PDAF &= ~0x18; PDOC &= ~0x18; PDHDE |= 0x18; PDOUT &= ~0x18; PADD &= ~0x04; PAAF &= ~0x04; PAOC &= ~0x04; PAHDE |= 0x04; PAOUT &= ~0x04; PBDD &= ~0x01; PBAF &= ~0x01; PBOC &= ~0x01; PBHDE |= 0x01; PBOUT &= ~0x01; } void init_PUSHBUTTON2(void) { PDDD |= 0x02; PDAF &= ~0x02; } //this is the TIMER0 interrupt service routine #pragma interrupt void isr_TIMER0(void) { DI(); PAOUT ^= 0x04; EI(); } //this is the reload value for TIMER0 //this is the reload value for TIMER1

//initialize PD4 and PD3 pin as output

//initialize PA2 pin as output

//initialize PB0 pin as output

//toggle PA2 output

//this is the TIMER1 interrupt service routine #pragma interrupt void isr_TIMER1(void) { DI(); PDOUT ^= 0x08; EI();

//toggle PD3 output

} //this is the PD6 interrupt service routine #pragma interrupt void isr_PUSHBUTTON1(void) { DI(); PDOUT ^= 0x10; EI(); } void init_PUSHBUTTON1(void) { DI(); PDDD |= 0x40; PDAF &= ~0x40; IRQPS |= 0x40; IRQES &= ~0x40; at //PD6 (P6AD is the code, declared in ez8.h, the CPU will //execute the isr_PUSHBUTTON() ISR IRQ1ENH &= ~0x40; IRQ1ENL |= 0x40; IRQ1 &= ~0x40; EI(); } //initialize TIMER0 in Continous mode, reloads every 0.455 seconds void init_TIMER0(void) { DI(); //disable all interrupt T0CTL1 &= ~0x80; T0CTL1 |= (0x07 << 3) | 0x01; T0H = 0x00; T0L = 0x01; T0RH = (RELOAD_VALUE1 >> 8); //disable Timer 0 //prescaler = 128 //continuous mode //initial value is 1 //Timer 0 increments from 1 to 65535 //enable PD6 interrupt //with LEVEL 1 priority //clear PD6 interrupt flag //enable all interrupts

//disable global interrupt //toggle PD4 output voltage //enable global interrupt //initialize PD6 //disable all interrupts //PD6 is input //PD6 alternate function is off //configure PD6 to detect interrupt //configure PD6 to detect falling edge //tells the CPU that when it detect an interrupt

SET_VECTOR(P6AD, isr_PUSHBUTTON1);

//RELOAD value loaded //higher byte of the 16-bit RELOAD_VALUE is shifted 8 //bits to the right and stored to T0RH register T0RL = (RELOAD_VALUE1 & ~0xFF00); //higher byte is zeroed and lower byte of the 16-bit //RELOAD_VALUE1 is stored to T0RL register SET_VECTOR(TIMER0, isr_TIMER0); //when TIMER0 interrupt occurs, tell CPU to execute //the isr_TIMER0 ISR IRQ0ENH |= 0x20; IRQ0ENL |= 0x20; IRQ0 &= ~0x20; T0CTL1 |= 0x80; EI(); } //enable Timer 0 interrupt with //LEVEL 3 priority //clear previous/pending TIMER0 int //enable timer 0 //enable all interrupt

//initialize TIMER0 in Continuous mode, reloads every 0.455 seconds void init_TIMER1(void) { DI(); //disable all interrupt T1CTL1 &= ~0x80; T1CTL1 |= (0x07 << 3) | 0x01; T1H = 0x00; T1L = 0x01; T1RH = (RELOAD_VALUE2 >> 8); //disable Timer 0 //prescaler = 128 //continuous mode //initial value is 1 //Timer 0 increments from 1 to 65535

//RELOAD value loaded //higher byte of the 16-bit RELOAD_VALUE2 is shifted 8 //bits to the right and stored to T0RH register T1RL = (RELOAD_VALUE2 & ~0xFF00); //higher byte is zeroed and lower byte of the 16-bit //RELOAD_VALUE is stored to T0RL register SET_VECTOR(TIMER1, isr_TIMER1); //when TIMER0 interrupt occurs, tell CPU to execute the //isr_TIMER0 ISR IRQ0ENH |= 0x40; IRQ0ENL &= ~0x40; IRQ0 &= ~0x40; T1CTL1 |= 0x80; EI(); } void main() { unsigned char val = 0x00; init_LEDS(); init_TIMER0(); init_TIMER1(); init_PUSHBUTTON1(); init_PUSHBUTTON2(); while(1) { val = PDIN & 0x02; if (val == 0x02) PBOUT &= ~0x01; if (val == 0x00) PBOUT |= 0x01; } } //initialize //initialize //initialize //initialize PD4 and PA2 as output pins TIMER0 as continuous mode timer TIMER1 as continuous mode timer PD6 pin as interrupt source //enable Timer 0 interrupt with //LEVEL 2 priority //clear previous/pending TIMER0 int //enable timer 0 //enable all interrupt

//read PD1 input pin //if Pushbutton2 is not pressed //LED4 is off //if Pushbutton2 is pressed //LED4 is on

As shown in the sample code, Timer 0 has LEVEL 3 interrupt priority, Timer 1 has LEVEL 2, and PD6 interrupt has LEVEL 1.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

if two interrupts have the same priority level, the one with the higher priority as indicated in the figure below will be responded first by the CPU.

Table 23. Interrupt Vectors in Order of Priority (page 65, Zilog Encore 64k Series data sheet) As an example, if I modify the above sample code and assign a LEVEL 3 priority to the 3 interrupts, Timer 1 ISR will be executed first if they all trigger at the same time, followed by Timer 0 ISR and then PD6 ISR.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Next discussion, Let us make another blinker program (the nth "blinker" program ), this time, using Timer 0 with no interrupt and enabling the PA1 alternate function (i.e. T0OUT). The PA1/T0OUT pin has function that depends on the Timer 0 operating mode. If Timer 0 is configured in Continuous mode, and PA1/T0OUT alternate function is enabled, PA1 pin will toggle every time Timer 0 finished counting (i.e. reload, overflows). Initial output value of PA1 is controlled by the TPOL bit in the T0CTL1 register. If TPOL is 1, initial value is logic high; if TPOL is 0, then output is initially low. To test the sample code below, connect an LED to PA1 pin.

/* HARDWARE DESCRIPTION: - LED connected at PA2 PROGRAM DESCRIPTION: - Timer 0 = Continuous mode, Prescaler = 128, Reload value = RELOAD_VALUE - Timer 0 output (PA1/T0OUT) is enabled (i.e. PA1 alternate function is on) - Timer 0 interrupt is disabled - Every time Timer 0 reloads, PA1 automatically toggles, turning on/off the LED - PA1 is initially high since TPOL bit is 1 */ #include<ez8.h> #define RELOAD_VALUE 65535 //this function will configure Timer 0 void init_Timer0(void) { DI(); T0CTL1 &= ~0x80; T0CTL1 |= (0x07 << 3) | 0x01; T0CTL1 |= 0x40; T0H = 0x00; T0L = 0x01; T0RH = (RELOAD_VALUE >> 8); T0RL = (RELOAD_VALUE & ~0xFF00); IRQ0ENH &= ~0x20; IRQ0ENL &= ~0x20; PAAF |= 0x02; T0CTL1 |= 0x80; EI(); } void main(void) { init_Timer0(); while(1); }

//disable interrupt //disable Timer 0 //prescaler = 128 //continuous mode //TPOL = 1 //initial value is 1 //Timer 0 increments from 1 to 65535 //RELOAD value loaded

//disable Timer 0 interrupt //enable PA1 alternate function //enable timer 0 //enable interrupt

//initialize Timer 0 //infinite loop

Download and run the program. The LED will toggle every 0.455 seconds.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Polled interrupts
In all our previous example programs about interrupts, we have been using VECTORED interrupts. Vectored interrupts always use interrupt service routines (ISR), that is, when the interrupt occurs, the CPU vectors (i.e. go to) automatically to the ISR starting address and execute the ISR code. In contrast to vectored interrupt, we can also use POLLED interrupts which do not require ISRs. With polled interrupts, an interrupt flag is set when an interrupt triggers and it is up to the main( ) program to check/poll for this interrupt flag and perform necessary actions. The CPU is not forced to immediately execute an ISR unlike with vectored interrupts. To demonstrate polled interrupts, we may need to make another modified blinker program (our nth + 1 blinker example ). An LED is connected to PD4 and Timer 0 is configured as continuous mode timer with a reload period of 0.455 seconds. When Timer 0 reloads, T0I bit (bit 5) in the IRQ0 register is set (this is the interrupt flag for Timer 0). The main() program will check if T0I is set, toggles the PD4 output, and clear the T0I flag. sample program:
/* AIM: - demonstrate POLLED interrupts (in contrast to VECTORED interrupts) HARDWARE DESCRIPTION: - LED connect at PD4 pin - Timer 0 is continuous mode, reload period is 0.455 seconds PROGRAM DESCRIPTION: - PD4 pin is output - Timer 0 initialized as continuous mode - Timer 0 vector interrupt is disabled - main() program poll T0I bit; if set, PD4 is toggled */ #include <ez8.h> #define RELOAD_VALUE 65535 #define PRESCALER 0x07 #define TMODE 0x01 void init_LED(void) { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT |= 0x10; } //reload value //prescaler = 128 //timer mode = cont. mode

//PD4 pin is output

void init_TIMER0(void) { DI(); T0CTL1 &= ~0x80; //disable Timer 0 T0CTL1 |= (PRESCALER << 3) | TMODE; //assign prescaler and timer mode T0H = 0x00; T0L = 0x01; //Timer 0 initial value = 1

T0RH = (RELOAD_VALUE >> 8); T0RL = (RELOAD_VALUE & ~0xFF00); IRQ0ENH &= ~0x20; IRQ0ENL &= ~0x20; IRQ0 &= ~0x20; T0CTL1 |= 0x80; EI(); } void main() { init_LED(); init_TIMER0(); while(1) { if ((IRQ0 & 0x20) == 0x20) { PDOUT ^= 0x10; IRQ0 &= ~0x20; } } }

//assign reload value //Timer 0 interrupt disabled //use polled interrupt //clear interrupt flag //enable Timer 0

//initialize PD4 pin //initialize Timer 0

//check if T0I bit is set //toggle PD4 //clear T0I bit

download and run the program. You will get a blinking LED
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Z8F Timer in One-Shot mode


In our previous sample programs using timers, Timer 0/1 are configured in Continuous Mode. This time, lets use TIMER 0 as ONE-SHOT MODE timer. In ONE-SHOT mode, TIMER 0 counts from start value to reload value only once (unlike in Cont. mode where it starts incrementing again), then the timer shutdown. It can be enabled again via software control. An interrupt can also be triggered when the timer reloads. In the next sample program, a pushbutton is connected to PD1 pin. If the pushbutton is pressed, PD4 is set and turn on the connected LED, and enables the TIMER 0 (it starts incrementing immediately). TIMER 0 overflows (i.e. reload) after 0.455 seconds and trigger an interrupt. The ISR will simply turn off the LED. The pushbutton can be pressed again to turn on the LED and enable TIMER 0 once more. here is the sample code:
/* AIM: - demonstrate TIMER0 as one-shot mode timer HARDWARE DESCRIPTION: - LED at PD4 pin - PUSHBUTTON at PD1 pin PROGRAM DESCRIPTION: - PD4 is output - PD1 is input - TIMER 0 as one-shot mode timer, with 0.455 reload period - If pushbutton is pressed, LED turn on, Timer 0 is enabled, counts from start value (1) to reload value (65535), overflows,

shutdowns, and trigger an interrupt - The ISR turn off the LED */ #include <ez8.h> #define RELOAD_VALUE 65535 #define PRESCALER 0x07 #define TMODE 0x00 void init_LED(void) { PDDD &= ~0x10; PDAF &= ~0x10; PDOC &= ~0x10; PDHDE |= 0x10; PDOUT &= ~0x10; } void init_Pushbutton(void) { PDDD |= 0x02; } #pragma interrupt void isr_TIMER0(void) { DI(); PDOUT &= ~0x10; EI(); } //reload value //prescaler = 128 //timer mode = one-shot mode

//PD4 is output pin

//PD1 is input pin

//disable interrupt //turn off the LED //enable interrupt

void init_TIMER0(void) { DI(); T0CTL1 &= ~0x80; //disable Timer 0 T0CTL1 |= (PRESCALER << 3) | TMODE; //assign prescaler and timer mode T0H = 0x00; T0L = 0x01; T0RH = (RELOAD_VALUE >> 8); T0RL = (RELOAD_VALUE & ~0xFF00); SET_VECTOR(TIMER0, isr_TIMER0); IRQ0ENH &= ~0x20; IRQ0ENL |= 0x20; IRQ0 &= ~0x20; T0CTL1 |= 0x80; EI(); } void main() { init_LED(); init_Pushbutton(); init_TIMER0(); while(1) { if ((PDIN & 0x02) == 0x00) //if pushbutton is pressed { PDOUT |= 0x10; //turn on LED, then T0CTL1 |= 0x80; //enable TIMER0 //TIMER0 reload only once, trigger an //interrupt, and then shutdown //CPU execute the isr_TIMER0() }} } //Timer 0 initial value = 1 //assign reload value //if TIMER0 interrupt occurs, tell CPU to execute // isr_TIMER0() //Timer 0 interrupt enabled //with LEVEL 1 priority //clear interrupt flag //enable Timer 0

//initialize PD4 as output //initialize PD1 as input //initialize TIMER0 as ONE-SHOT MODE //with reload period of 0.455 second

Timer in PWM mode


Another Timer mode is the PWM mode. In PWM mode, TIMER 1 sends a PWM signal to the Timer output pin (PC1/T1OUT (pin 28) for TIMER 1, PA1/T0OUT (pin 6) for Timer 0). If TPOL bit is set to 1, PC1/T1OUT is initially high when Timer 1 starts incrementing. If the 16-bit Timer value is equal to the 16-bit PWM value, T1OUT goes low. Timer 1 continues counting until it reached the 16-bit reload value, reload, then starts incrementing again (NOTE1). Refer to the figure below.

NOTE1: When Timer 1 reloads, an interrupt can also be generated. In our next example, we wont use the interrupt. As an example, lets generate a 1khz PWM signal with a 50% duty cycle at the PC1 pin. We then configure Timer 1 in PWM MODE, with a prescaler of 1 and a reload value of 18432. To generate a 50% duty cycle PWM signal, we choose a PWM value of 9216 (that is, 18432/2). We also configure PC1/T1OUT pin as an output pin with the alternate function turned on. You may also use the following values to generate the 1 kHz, 50% duty cycle PWM signal: PRESCALER / PWM_VALUE / RELOAD_VALUE 2/4608/9216 4/2304/4608 8/1152/2304 16/576/1152 32/288/576 64/144/288 128/72/144 here is the sample code:

/* Aim: - Generate PWM signal using Timer 1 HARDWARE DESCRIPTION: - oscilloscope probe connected at PC1/T1OUT pin (pin 28) SOFTWARE DESCRIPTION: - TIMER 1 configured in PWM mode, with a reload period of 1ms (freq = 1khz) and 50% duty cycle */ #include <ez8.h> #define RELOAD_VALUE 18432 #define PWM_VALUE #define TMODE #define PRESCALER 9216 0x03 0x00 //Timer mode is PWM mode //prescaler = 1 //PWM period (sec.) = (RELOAD x Prescaler)/system //clock (Hz)

void init_PWM(void) //configure Timer 1 { DI(); T1CTL &= ~0x80; //Disable Timer 1 T1CTL |= (0x00 << 3)| 0x03; //Prescaler = 0, PWM mode T1CTL |= 0x40; //TPOL = 1. PC1/T1OUT output set to high PWM output //is initially high and toggle to low when //PWM_VALUE is reached T1H = 0x00; T1L = 0x01; //initial value is 1

T1PWMH = (PWM_VALUE >> 8); //PWM_VALUE loaded, set duty cycle T1PWML = (PWM_VALUE & ~0xFF00); T1RH = (RELOAD_VALUE >> 8); //RELOAD_VALUE loaded, set PWM period T1RL = (RELOAD_VALUE & ~0xFF00); IRQ0ENH &= ~0x40; IRQ0ENL &= ~0x40; PCDD |= 0x02; PCAF |= 0x02; T1CTL |= 0x80; //Timer 1 interrupt disabled //PCI/T1OUT pin is output //turn on alternate function of PC1, //Enable Timer1 //After the previous line of code, PWM will //automatically start. You may view the PWM //output at the PC1 pin using an oscilloscope. //PWM period is 1 kHz, with 50% duty cycle

EI(); } void main(void) { init_PWM(); while(1); }

//initialize timer1, PWM mode //infinite loop

the output PWM signal as viewed in an oscilloscope. Its a perfect square wave.

The duty cycle of the PWM signal can be changed anytime by software control. It can be done by simply writing a new PWM value to the T1PWMH and T1PWML (For timer 1) registers. In the sample program, 2 pushbuttons are connected to PD1 and PC2 pins. When the program is run, a 50% duty cycle PWM signal will be seen at PC1 pin. If Pushbutton1 (at PD1) is pressed, the duty cycle will be changed to 25%; if Pushbutton2 (at PC2) is pressed, duty cycle is now 75%. here is the sample program:
/* Aim: - Generate PWM signal using Timer 1 - Change PWM signal duty cycle via software control HARDWARE DESCRIPTION: - oscilloscope probe connected at PC1/T1OUT pin (pin 28) - pushbutton connected at PD1 - pushbutton connected at PC2 SOFTWARE DESCRIPTION: - TIMER 1 configured and 50% duty - if pushbutton1 (at - if pushbutton2 (at */ in PWM mode, with a reload period of 1ms (freq = 1khz) cycle PD1) is pressed, duty cycle is changed to 25% PC2) is pressed, duty cycle is changed to 75%

#include <ez8.h> #define #define #define #define RELOAD_VALUE PWM_VALUE1 PWM_VALUE2 PWM_VALUE3 18432 9216 4608 13824 0x03 0x00 //PWM //50% //25% //75% period (sec.) = (RELOAD x Prescaler)/system clock(HZ) duty cycle duty cycle duty cycle

#define TMODE #define PRESCALER

//Timer mode is PWM mode //prescaler = 1

void init_PWM(void) //configure Timer 1 { DI(); T1CTL &= ~0x80; //Disable Timer 1 T1CTL |= (0x00 << 3)| 0x03; //Prescaler = 0, PWM mode T1CTL |= 0x40; //TPOL = 1. PC1/T1OUT output set to high PWM output is initially // high and toggle to low when PWM_VALUE is reached T1H = 0x00; T1L = 0x01; //initial value is 1 //PWM_VALUE loaded, set duty cycle //RELOAD_VALUE loaded, set PWM period //Timer 1 interrupt disabled //PCI/T1OUT pin is output //turn on alternate function of PC1, //Enable Timer1

T1PWMH = (PWM_VALUE1 >> 8); T1PWML = (PWM_VALUE1 & ~0xFF00); T1RH = (RELOAD_VALUE >> 8); T1RL = (RELOAD_VALUE & ~0xFF00); IRQ0ENH &= ~0x40; IRQ0ENL &= ~0x40; PCDD |= 0x02; PCAF |= 0x02; T1CTL |= 0x80; EI(); } void init_pushbuttons(void) { PDDD |= 0x02; PDAF &= ~0x02; PCDD |= 0x04; PCAF &= ~0x04; } void main(void) { init_pushbuttons(); init_PWM();

//PD1 is input //PC2 is input

//initialize PC2 and PD1 as input pins //initialize timer1, PWM mode //if pushbutton1 is pressed //change to 25% duty cycle

while(1){ if((PDIN & 0x02) == 0x00) { T1PWMH = (PWM_VALUE2 >> 8); T1PWML = (PWM_VALUE2 & ~0xFF00); } if((PCIN & 0x04) == 0x00) { T1PWMH = (PWM_VALUE3 >> 8); T1PWML = (PWM_VALUE3 & ~0xFF00); } } }

//if pushbutton2 is pressed //change to 75% duty cycle

If Pushbutton1 is pressed:

if Pushbutton2 is pressed:

Press the reset button on the board to restart the program and display the PWM signal back to 50%

Lets modify the previous PWM example, this time, to control a DC servo motor. Refer to the schematic below.

I bought an E3003 dc servo motor from e-gizmo. Let's use this as an example. (pin connection: WHITE = signal, RED = +5v, BLACK = ground)

The board includes a +5v supply. We can use this to supply power to the servo motor.

Let's continue our discussion on servo motor control using Z8F MCU... To control a hobby dc servo motor, a PWM signal must be sent to the servo motor via the SIGNAL pin. To turn the servomotor shaft to full CCW position, a 2 ms pulse must be sent every 20 ms. Or in other words, send a 50 Hz (1/20 ms) square wave with a 10% duty cycle (2 ms/ 20 ms x 100%).

To turn the servomotor shaft to full CW position, a 1 ms pulse must be sent every 20 ms. Or in other words, send a 50 Hz (1/20 ms) square wave with a 5% duty cycle (1 ms/ 20 ms x 100%).

To move the shaft to center position, a 1.5 ms pulse must be sent every 20 ms. Or in other words, send a 50 Hz (1/20 ms) square wave with a 7.5% duty cycle (1.5 ms/ 20 ms x 100%).

here is the sample code to control a servo motor


/* Aim: - control a DC servo motor using a PWM signal generated from a Z8F MCU HARDWARE DESCRIPTION: - oscilloscope probe connected at PC1/T1OUT pin (optional) - DC servo motor signal pin connected to PC1/T1OUT - pushbutton connected at PD1 - pushbutton connected at PC2 SOFTWARE DESCRIPTION: - TIMER 1 configured in PWM mode, with a reload period of 20ms and an ON period of 1.5 ms (7.5% DC, servo motor shaft moves to center position) - if pushbutton1 (at PD1) is pressed, ON period of the PWM is changed to 2 ms (DC is 10%) and servomotor rotates to counter clock-wise full position - if pushbutton2 (at PC2) is pressed, ON period of the PWM is changed to 1 ms (DC is 5%) and servomotor rotates to clock-wise full position - if RESET button is pressed, duty cycle is back to 7.5%. Servo shaft moves to center position */ #include <ez8.h> #define #define #define #define RELOAD_VALUE PWM_VALUE1 PWM_VALUE2 PWM_VALUE3 23040 1728 2304 1152 //PWM period (sec.) = (RELOAD x Prescaler)/system clock (Hz) //7.5% duty cycle //10% duty cycle //5% duty cycle

#define TMODE #define PRESCALER

0x03 0x04

//Timer mode is PWM mode //prescaler = 4 //configure Timer 1 //Disable Timer 1 //Prescaler = 0, PWM mode //TPOL = 1. PC1/T1OUT output set to high //PWM output is initially high and toggle to low // when PWM_VALUE is reached //initial value is 1 //PWM_VALUE loaded, set duty cycle //RELOAD_VALUE loaded, set PWM period //Timer 1 interrupt disabled //PCI/T1OUT pin is output //turn on alternate function of PC1, //Enable Timer1

void init_PWM(void) { DI(); T1CTL &= ~0x80; T1CTL |= (PRESCALER << 3)| TMODE; T1CTL |= 0x40;

T1H = 0x00; T1L = 0x01; T1PWMH = (PWM_VALUE1 >> 8); T1PWML = (PWM_VALUE1 & ~0xFF00); T1RH = (RELOAD_VALUE >> 8); T1RL = (RELOAD_VALUE & ~0xFF00); IRQ0ENH &= ~0x40; IRQ0ENL &= ~0x40; PCDD |= 0x02; PCAF |= 0x02; T1CTL |= 0x80; EI(); } void init_pushbuttons(void) { PDDD |= 0x02; PDAF &= ~0x02; PCDD |= 0x04; PCAF &= ~0x04; } void main(void) { init_pushbuttons(); init_PWM();

//PD1 is input //PC2 is input

//initialize PC2 and PD1 as input pins //initialize timer1, PWM mode

while(1){ if((PDIN & 0x02) == 0x00) //if pushbutton1 is pressed { T1PWMH = (PWM_VALUE2 >> 8); //change to 10% duty cycle T1PWML = (PWM_VALUE2 & ~0xFF00); //servomotor turn counter //clock-wise (CCW) } if((PCIN & 0x04) == 0x00) //if pushbutton2 is pressed { T1PWMH = (PWM_VALUE3 >> 8); //change to 5% duty cycle T1PWML = (PWM_VALUE3 & ~0xFF00); //servomotor turn clock-wise (CW) } } }

To generate a 50 Hz square wave signal, Time 1 is configured in PWM mode with a reload value of 23040 and a prescaler of 4.

If Pushbutton1 is pressed, the Timer1 reload value is changed (to 2304), the PWM output at PC1 pin changed its duty cycle to 10% (corresponding to 2 ms/20 ms square wave), and servomotor rotates to full CCW position If Pushbutton2 is pressed, the Timer1 reload value is changed (to 1152), the PWM output at PC1 pin changed its duty cycle to 5% (corresponding to 1 ms/20 ms square wave) , and servomotor rotates to full CW position.
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

Serial Communications
the part of the DIY board relevant to serial communication is shown below, highlighted in red. In consists mainly with the Z8F, the MAX232 IC, the DB9 connector (J1), plus a serial data cable (not shown).

In our next sample program, we will transmit a text (a series of characters) from Z8F mcu to the PC (via serial port) for display using the Hyperterminal program. But before we make the program, first, we need to build the system below

To understand the 2nd figure above, we need a short discussion (sorry i cant avoid it

The RS-232 protocol (the 1st popular serial comm. protocol) is an old data communication standard (circa early 1960's) used to enable communication between two digital device (like a PC and a modem, or, in our example, a PC and a UARTenabled Z8F MCU ). Between the two devices, they will send logic 1 (logic 1 is any pulse signal with an amplitude between -3 to -27V) and logic 0 (+3 to +27V) to exchange data in full-duplex communication. As you can notice the 2 logic levels (1 and 0) and their corresponding voltage levels are inverted! The two digital device must each include a special circuitry that will implement the actual serial communication. That is the role of the UART. Note that the UART circuitry is included inside the MCU. Since many digital devices operates at 0-5V (or 0-3.3V for the Z8F MCU), another specialized circuit is needed to convert the (0/5V) logic signals to RS-232 signals. That is the role of the MAX232 chip. A MAX232 chip is mainly a voltage doubler + inverter. It converts a logic 1 (5V) to -10V and logic 0 (0V) to +10V, thereby satisfying the RS-232 recommended voltage levels. The DB9 connector is ,well, a connector used for standardization. It has no electrical relevance, except mainly for pin compatibility and mechanical support. to complete the circuit above, Connect PA4/RXD0 (pin 38) to MAX232 pin 12 and PA5/TXD0 to MAX232 pin 11. Use a solid wire. Using a serial data cable, connect also DB9 (J1) to a vacant serial port in your PC. This will complete the ckt.

In the picture above, i used COM1 (the name of the available serial port) for programming/debugging and COM14 for the serial communication. I used two serial data cables. (My PC have 3 serial ports, COM1, COM14, and COM15).

Here is the sample program. In the program, the UART0 peripheral of the Z8F MCU is initialized with a baud rate of 57600. Then it will send the text "I love Electronicslab.ph" to the PC. Build and download the program to the Z8F. Don't run it in the meantime.
/* AIM: - learn to interface the Z8F MCU to the PC using serial port - display transmitted data from Z8F MCU to HYPERTERMINAL program HARDWARE: - PA4/RXD0 (pin 38) connected to pin 12 of MAX232 - PA5/TXD0 (pin 37) connected to pin 11 of MAX232 - serial data cable connected to DB9 (J1) and PC's serial port PROGRAM: - PA4 and PA5 alternate functions are ON (as UART0 receive & transmit pin, respectively) - UART0 initialized with 57600 baud rate with 18.432 Mhz crystal - transmit a text to PC for display to Hyperterminal program */ #include <ez8.h> #include <sio.h> void delay(void) { int i,j; for (i=0;i<=0xFF;i++) for (j=0;j<=0xFF;j++) ; } void main(void) { init_uart(_UART0, _DEFFREQ, _DEFBAUD); /* Initialize UART0. The constants _UART1, _DEFFREQ,& _DEFBAUD are defined in sio.h. The INIT_UART() function is also defined in sio.h */ select_port(_UART0); //enable UART0 //software delay

//do nothing

while(1) { printf("i love electronicslab.ph\n"); //display text delay(); //invoke delay() 3 times delay(); delay(); } }

Before we run the Z8F sample program, first we need to run the Hyperterminal application program on Windows. Click Start > Programs > Accessories > Communications > Hyperterminal.

NOTE: If you are not using WinXP OS, chances are Hyperterminal is not yet installed. Download and install the program. Its a free program. (http://hyperterminal-private-edition-htpe.en.softonic.com/download) Type any desired name then click OK. Select COM1 (or any COM in your PC that corresponds to an actual physical serial port, where your DIY board is currently connected).

If you are not sure what is the COM port number of your PCs serial port, open Device Manager and click Ports to view available serial (as well as parallel) ports. Below is my own Device Manager window. I have 4 serial ports (COM1, COM14, COM15, and COM8). COM8 correspond to my USB-to-Serial converter cable that I bought from e-gizmo.

If you are using the latest laptop models, it may not have any serial ports. Your only solution then is to use the USB-toserial converter cable. In the COM1 Properties window, select the following parameters shown below. Click OK. Note that COM1 must have 57600 baud, same as the Z8F.

The Hyperterminal is now ready to display messages (texts or characters) receive at COM1.

In the ZDS IDE, run the Z8F program (click the Go button). The Hyperterminal will now start displaying the sent message. The text I love Electronicslab.ph will be displayed repeatedly.

Lets dissect the sample code. The very first line is the usual preprocessor directive. It tells the compiler to include the ez8.h file where the register names of the microcontroller are declared. Always include this line when using Z8F MCUs.
#include <ez8.h>

The next preprocessor directive tell the compiler to include the serial input/output library so that we can use the init_uart( ) and select_port( ) in our code to initialize and enable the UART peripheral and the printf( ) function for sending formatted character/string messages.
#include <sio.h>

This is the now familiar function definition of the delay( ) function that implements a simple software-based delay.
void delay(void) { int i,j; for (i=0;i<=0xFF;i++) for (j=0;j<=0xFF;j++) ; } //software delay

//do nothing

Inside the main( ) function, the first line will initialize UART0. The init_uart( ) function will enable the alternate functions of PA4/RXD0 (pin 38) and PA5/TXD0 (pin 37). These pins are the receive and transmit pins of UART0, respectively, when the alternate function is on. UART0 will also be configured to transmit data at 57600 bps (_DEFBAUD = 57600, the default baudrate).
init_uart(_UART0, _DEFFREQ, _DEFBAUD);

The 3 constants above are defined in sio.h with _DEFFREQ = 18432000, _DEFBAUD = 57600, and _UART0 = 0. You can open (using notepad) sio.h which is located in the ZiLOG\ZDSII_Z8Encore!_4.9.6\src\rtl\common folder. A quick glance at the code will tell you what internal register are modified by the init_uart( ) function. Next line, this will select the UART0 port as the current port, which means that any printf() (or similar function call) called later in the program will send data to this via UART0.
select_port(_UART0); //enable UART0 as current port

This code will transmit a text, repeated, to UART0. The delay() is called 3 times for slower repetition.
while(1) { printf("i love electronicslab.ph\n"); delay(); delay(); delay(); }

//display text //invoke delay() 3 times

Since UART0 communicates with the PC's serial port, you can view the transmitted data using Hyperterminal..

Our previous sample program is RS232 transmission via UART0, by displaying a text at the Hyperterminal program This next example is reception + transmission via UART0. In the program, if character 'a' or 'A' is received by the Z8F MCU at UART0, an LED connected at PD0 pin will toggle. If wrong character is received, LED is off. Here is the code:

/* hardware: - PA4/RXD0 (pin 38) connected to MAX232 pin 12 - PA5/TXD0 (pin 37) connected to MAX232 pin 11 - DB9 (J1) connected to PC (at COMx) using a serial cable - LED connected to PD0 pin program: - PD0 is output - UART0 is initialized with baudrate of 57600 - Z8F MCU toggles LED at PD0 when character 'a' or 'A' is receive ar UART0 - If wrong character is received at UART0, LED is off */ #include <ez8.h> #include <sio.h> //PD0 pin is output void init_LED(void) { PDDD &= ~0x01; PDOC &= ~0x01; PDHDE |= 0x01; PDOUT &= 0x01; } void main() { unsigned char temp; init_LED(); init_uart(_UART0, _DEFFREQ, _DEFBAUD); select_port(_UART0); while(1) { temp = getch();

//8-bit variable //initialize PD0 as output //initialize UART0 //enable UART0

//read receive character, if no character is // available, getch() will wait for character //check if receive character // is 'a' or 'A' //then toggle LED //transmit back receive letter //if character received is not 'a' or 'A' //LED will turn off

if (temp == 'a' || temp == 'A') { PDOUT ^= 0x01; printf("%c", temp); } else PDOUT &= ~0x01; } }

Build and download the code. Open Hyperterminal (refer to previous example for the steps) and choose the corresponding COM port with a baudrate of 57600. Press the 'A' key repeatedly and the LED on the DIY board will toggle on and off. The received character (a/A) will also be displayed at the Hyperterminal window since it is sent back by the MCU. (i.e. printf("%c", temp); )

Tips for learning how to program the zilog encore Make sure you have your own DIY board (This is a must!) Read the 64k-series Zilog Encore datasheet Read this in-forum tutorial thoroughly Ask questions. Give feedbacks. Write and test the sample programs (1 program a day should suffice, the more the better) Modify the sample programs Combine the sample programs (i.e. multitask the MCU) Print the Control Register Summary from the datasheet. This is your quick handy reference for all the internal registers of the Z8F MCU Share what you have learn Practice, practice, practice....at home or at school (the DIY board is handy, plus the serial data cable and a 9V battery. It takes up a small space in your school bag so you can take it anywhere Refer to other sources (i.e. Doc Sison's book, appnotes at zilog.com, etc) Make electronics projects using the Z8f mcu. Start simple and progress to difficult ones Join the MADC. This should motivate you furtherr Think critically
Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

I connected the zilog encore board to a dial-up modem, using an RS232 cable. Using AT commands, the MCU tells the modem to dial the local number 301-1234.
#include <ez8.h> #include <sio.h> void main() { init_uart(_UART0, _DEFFREQ, _DEFBAUD); select_port(_UART0); printf("ATD3011234\r");

//Initialize UART0 //Enable UART0 //command the modem //to dial the local number //301-1234

while(1); }

ADC Soon!!! Salamat Sir FRANZ !!!

Electronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslab.phElectronicslphElectronicslab.phElectronicslab.phphElectronicslab.p

JERZCLANX

Vous aimerez peut-être aussi