P112

Table of Content


Content


P112 is a general purpose GPIO board based on PIC16F873 micro-controller.

The board provides twelve GPIO circuits that can be configured either as input or output depending of the component mounted. Inputs are opto isolated, outputs are reed relay contact closures.

The board also provides a serial port with a RS485/422 driver. This may be useful if you plan to connect more that one P112 boards in a RS485 bus, or a single board controlled by a computer via RS422.

To help you in the development of your Firmware, a "framework" is provided. This is an skeleton program written in assembly that takes care of GPI reading and de-bouncing, interrupts handling and more, so you can focus on your application-specific code. See section "Firmware" for details.

An "ICSP" connector (In-Chip-Self-Programming) is available in the board to upload your object code to the MCU memory.

Assembling the board

The printed circuit board is designed so you can chose the desired configuration of GPIOs by mounting the appropriate components for each circuit. For GPI, mount the opto isolator and the two associated resistors. For GPO, mount the relay and the associated diode. Never mount the two of these options in the same GPIO circuit.

You are not forced, though, to use opto isolators for inputs or relays for output. If a direct connection is desired, replace those components with jump wires (pins 2,4 of the opto isolator footprint). If it is an input, you may want to also mount the 10K pull up resistor.

The RS485/422 driver (U2) is optional. If you don't plan to make use of the serial port, you can leave the footprint empty as well as that of the header connector J2. Same with the ADDR header (S1) if you have no plans for configuration jumpers.

Firmware

Toolchain

Firmware development was done in Linux using the following tools:

Editor: vim
Assembler: GNU gpasm
Programmer: pk2cmd

To upload object code (Intel Hex) to the MCU, we utilize the USB-to-ICSP PIC programmer device "iCP02", available for purchase from http://www.piccircuit.com

Firmware simulation was done in Windows using "Real PIC Simulator".

Installation

Framework

What we call "framework" is a skeleton written in assembly that you must complete with your application specific code. The framework takes care of common tasks such as debouncing GPI readings and handling hardware interrupts. This section explains some details you must understand to make effective use of the framework.

The source is made of the following files:

    macros.h  (framework)
    p112.h    (framework)
    p112.asm  (framework)
    user.h    (User)
    init.asm  (User)
    user.asm  (User)
    timers.inc(User, optional)
    uart.inc  (User, optional)

Header files (.h) contains definitions only while .asm and .inc files implement the logic. As a user, you will only concern with the files indicated as (User) in the list above. Those marked as (User, optional) may or may not interest you depending on your application.

More on this in section "Writing your code".

FLAGS variable

Defined in p112.h is a variable named FLAGS. As the name suggests, this variable is used bitwise to indicate various status. The framework uses only two of these flags (also defined in p112.h):

   F_GPI_READY --> bit 0
   F_VT0_BUSY  --> bit 1

As a user, you are free to use the remaining bits for your own purposes.

MACROS

File macros.h contains all macros used by the framework. Your code will also make use of those macros so it worth to take a look to that file.

Some macros are generic and serve no purpose other than facilitate coding; this is the case of macro SELECT_BANK, for example. Also, a series of "traditional branch" macros are defined to get around the cumbersome PIC instructions btfsc, btfss. Those are: JZ, JNZ, JC, JNC.

There also are framework specific macros such as VTIMER and START_VTIMER. We will cover those later in this manual.

Interrupts Handling

Interrupts occurs when the Real Timer Tmr0 times out and when the UART has a complete byte available for reading. The framework handles those interrupts for you as following:

Registers STATUS and W are saved to the stack and global interrupts are disabled. Then, the source of the interrupt is identified. If it came from the Real Timer, subroutine ISR_Tmr0 is called; if it came from the UART, subroutine ISR_UART is called. These routines in turn make they ways to call user subroutines. More on this in section "Writing your code".

On return, registers STATUS and W are restored from the stack and global interrupts are enabled back.

Virtual Timers

The framework initializes the microcontroller's real timer Tmr0 to interrupt every one millisecond. Based on this "tick", various "virtual timers" are updated (decremented) by the interrupt service routine.

Unlike real timers, virtual timers are implemented in firmware. Each virtual timer consists of:

   COUNT variable
   VALUE literal
   BUSY flag

The BUSY flag resides in variable FLAGS; it is set while the virtual timer is counting, cleared after timeout. The VALUE literal expresses the count in milliseconds. The COUNT variable is decremented by the Tmr0 interrupt service routine until it reaches zero.

To start the virtual timer, the COUNT variable is loaded with the VALUE constant and the BUSY flag is set. This is done by macro START_VTIMER defined in macros.h. The BUSY flag is automatically cleared by the framework on timeout.

Using virtual timers in user code is as simple as running the START_TIMER macro to start it, then keep watching the BUSY flag for time out.

The framework implements one virtual timer (VT0) used for GPI debouncing. As a user, you can create and manage your own virtual timers. More on this in section "Writing your code".

GPI reading and debouncing

Reading and debouncing GPIs is handled by routine ReadGPI which is called continuously in the Main Loop. As a user, your only concern about whether flag F_GPI_READY (bit 0 of variable FLAGS) is set indicating that GPI reading is stable (debounced).

The ReadGPI routine works as following:

All GPIs are read at once from ports PORTA and PORTB. Literals GPIO_CFG_A and GPIO_CFG_B are used to mask GPOs. This is important to know because those literals are defined in user code (file user.h).

Values read from the ports are stored in temporary variables and virtual timer VT0 is started. As a consequence of this, flag F_VT0_BUSY (bit 1 of variable FLAGS) is set indicating that VT0 is counting.

When the ReadGPI routine is called again in the Main Loop, it returns immediately as this routine checks the F_VT0_BUSY flag. If set, it does nothing, just returns.

VT0 time out occurs 10 milliseconds later causing the F_VT0_BUSY flag to clear. The next time ReadGPI is called from the Main Loop, GPIs are read again from PORTA and PORTB, then compared with previos values. If they match, flag F_GPI_READY is set completing the job. If not, the new values are stored in temporary variables again and VT0 is started to give it another try.

This process is tried three times. If no two consecutives reading can be obtained, the alarm LED is turned on and reading is aborted all together.

Alarm codes and the ALM LED

The ADDR configuration jumpers

Serial Port (RS485/422)

Writing your code

Assembling and Uploading your code