Hempl/PIO
Introduction
[edit | edit source]PIO means Programmable Input/Output and this is the simplest way of controlling and measuring digital voltage levels on the pins of the AVR32 processor pins connected to the bus connectors.
To use a GPIO pin as a PIO, you must first set the pin to be an input or an output. If you set it as an input, you can then check the input voltage to see whether it has a low or a high value coming into it, for example to check the position of a switch. If you set it to be an output, you can program it to output a low voltage or a high voltage to control lights, motors or other circuits.
For PIO pins that are inputs, you can also ask that, when the voltage on that pin changes from 0 to 1 or from 1 to 0, this will generate an interrupt. When this happens, the processor will stop what it is doing, run a special piece of code called an interrupt routine, and when it has finished doing that it will go back and continue what it was doing when the interrupt happened.
Lastly, each pin has an optional pull-up resistor that can be enabled so that, if nothing is connected to a pin that is an input, it will float up to logic "1" instead of waving up and down at random. This is the usual way to connect switches or pushbuttons: you program a pull-up resistor on the pin, then connect the switch between the pin and zero volts so that when contact is closed you will read a value of 0, and when the contact is open you will read a value of 1.
Hardware view
[edit | edit source]Any of the AT32UC3A chip's peripheral pins can be read as a digital input or set as an output and programmed to 0 or 1 logic level.
When a pin is set to be an output, a 0 logic output connects the pin to 0 volts, while a logic 1 ("high") value puts 3.3 volts on it with a maximum current supply or drain of 4 milliamperes for both states.
When they are set to be inputs, a voltage level from 0.0 to 0.8 volts reads as a "0" (low) input, and a voltage level from 2.0 volts to 5.0 volts reads as a "1" (high) input. Values from 0.8 to 2.0 volts may read as high or low and are not certain.
Some pins of the Mizar32 can only be used as programmable I/O pins because they are not used for anything else. Others carry signals to various peripheral devices but, if those devices are not being used, the pins can be used as PIO pins instead.
Other pins are critical to the correct functioning of the processor, for example those used to access the SDRAM, oscillators and other on-board circuitry; if you use those as PIO pins the board will probably crash and need its reset button pressing.
Dedicated PIO pins
[edit | edit source]Pin | Name | Bus pin | PicoLisp |
---|---|---|---|
PA2 | GPIO2 | BUS5 pin 11 | 'PA_2
|
PA7 | GPIO7 | BUS5 pin 12 | 'PA_7
|
PB17 | GPIO49 | BUS5 pin 8 | 'PB_17
|
PB18 | GPIO50 | BUS5 pin 9 | 'PB_18
|
PB29 | GPIO61 | On-board LED | 'PB_29
|
PB30 | GPIO62 | BUS6 pin 9 | 'PB_30
|
PB31 | GPIO63 | BUS6 pin 10 | 'PB_31
|
PX16 | GPIO88 | User button | 'PX_16
|
PX19 | GPIO85 | BUS6 pin 12 | 'PX_19
|
PX22 | GPIO82 | BUS6 pin 11 | 'PX_22
|
PX33 | GPIO71 | BUS5 pin 10 | 'PX_33
|
Optional PIO pins
[edit | edit source]Unused ADC, PWM, SPI or UART pins can also be used as PIO pins. See those sections for the relevant pin names. This brings the total number of usable PIOs to 66.
Software view
[edit | edit source]The PIO
Hempl module allows you to set any pin as a logic input or to set it as an output and put 0v or 3.3V on it and you can enable a pull-up resistor on any pin.
Driving a pin as an output
[edit | edit source]This example lights the on-board LED.
From PicoLisp:
(setq led 'PB_29) (pio-pin-setdir *pio-output* led) (pio-pin-setlow led)
Note that, straight after a reset, the LED lights up at line 2 because the reset state is that all pins are low and the onboard LED lights when the signal is low. To set a pin as an output whose value starts high, you need to call pio.pin.sethigh()
before calling pio.pin.setdir()
.
Reading the voltage on a pin as an input
[edit | edit source]Here is an example of reading one PIO pin and driving another in response to it. We will read the pin connected to the onboard user button and, as long as it is pressed down, we will make the on-board LED flicker. In PicoLisp, here is how to do it.
# A simple program which demonstrates # the usage of user-buttons. # declare pins (setq led 'PB_29 button 'PX_16) # a simple delay function (de delay (t) (tmr-delay 0 t) ) # make sure the LED starts in # the "off" position and enable # input/output pins (de init-pins () (pio-pin-sethigh led) (pio-pin-setdir *pio-output* led) (pio-pin-setdir *pio-input* button) ) # And now, the main loop (de prog-loop () (init-pins) (loop (if (= 0 (pio-pin-getval button)) (prog (pio-pin-setlow led) (delay 100000) (pio-pin-sethigh led) (delay 100000) ) ) ) ) (prog-loop)
Programmable pull-up resistors
[edit | edit source]The user button's electrical circuit is quite simple: when the button is pressed, it connects its PIO pin to zero volts, giving a low input value, and there is a resistor connected between the PIO pin and 3.3 volts so that if the button is not pressed the PIO pin is gently pulled up to 3.3V and reads as a high value.
Other PIO pins that are not connected to anything, if you program them as inputs, will pick up random noise from the surrounding environment and give values that are sometimes high and sometimes low. The programmable pull-up resistors are a way to ensure that, if no signal is connected to an input pin, it will gently be pulled up to a high value instead of floating randomly.
This example turns one of the unused GPIO pins into a PIO input, but ensures that, if nothing is physically connected to it, it will always return a high value of 1
.
If you remove the (pio-pin-setpull)
line from the following code (on my test board, at least), it prints mostly zeroes but if you touch the underside of the Mizar32 board the value flickers between 0 and 1. With the (pio-pin-setpull)
line included, the input value is always 1 unless you connect the pin it to a GND pin (e.g. BUS5 pin 14) with a piece of wire.
In PicoLisp:
(setq pin 'PA_2) # Stabilize GPIO2 (connector BUS5 pin 11) (pio-pin-setdir *pio-input* pin) (pio-pin-setpull *pio-pullup* pin) (loop (prinl (pio-pin-getval pin)) )
Although Hempl also has a similar primitive PULLDOWN
, the AVR32 chip used in the Mizar32 does not have programmable pull-down resistors in its hardware, so using this will provoke an error message.
To disable the pull-up resistor again, you use:
(pio-pin-setpull pin *pio-nopull*)
Open-collector outputs
[edit | edit source]Another use for the programmable pull-up resistors is to implement "open-collector outputs". In this scheme of things, a pin can be in one of two states: either it is being driven as a 0 output or it being read as an input. The pull-up resistor ensures that, if no one is driving it as an output, everyone will read it as a high value. This is used when several computers need to communicate over a single signal wire in such a way that any computer can talk with any other one without needing a master-slave relationship or a way to negotiate who is controlling the bus. Using this system, any computer can read the wire to see if its value is high or low, and any computer can drive the wire to a low value, to be read by all the others. This is different from driving a wire as a high or low output because if one computer is driving it high and another is driving it low at the same time, that could damage the computers in question and would certainly result in garbled communication.
A simple example would be a system to turn a house light on and off in a way that can be activated by any one of several switch units. The I2C bus, instead, is an advanced example that uses open-collector outputs on its signal wires so that any one of the computers on an I2C bus to talk with any other one without ever causing conflicting signals on the bus wires.
The following code implements an open-collector output on a PIO pin, giving one function to configure it as an OC pin, one function to drive it as a low output and one to set it as an input and tell you what value is on the wire at the moment:
In PicoLisp:
# Turn a PIO pin into an open-collector output. # Call this function once before using the other # two to drive and read the pin. (de oc-setup (pin) # OC output starts as an input, not driving # the bus wire (pio-pin-setdir *pio-input* pin) # Arrange that, when it is an input and no # one is driving it, it will float high (pio-pin-setpull *pio-pullup* pin) # and that when we set it as an output, it will drive a low value (pio-pin-setlow pin) ) # Drive a low output value onto the pin. # The low output value is already programmed during setup() # so we only need to enable it as an output. (de oc-drive-low (pin) (pio-pin-setdir *pio-output* pin) ) # Make the pin an input and return the value on the bus wire (de oc-read (pin) (pio-pin-setdir *pio-input* pin) (pio-pin-getval pin) )
Interrupts
[edit | edit source]Please note: There is currently no support for interrupt handling in PicoLisp. See issue #2.
Decoding pin numbers
[edit | edit source]Symbols like 'PB_29
return numbers that are used internally in Hempl to identify the GPIO pins. You do not need to know anything about the values of these numbers except in one circumstance: when you have several edge-triggered interrupts enabled on different GPIO pins at the same time from Hempl.
If you wish to decode symbols like 'PB_29
into a port number and a pin number, you can do so using
(pio-decode 'PB_29)
The result is a list. Its car
gives the port number and its cadr
gives the pin number.
For pins on Port A, port
will be 0
and pin
will be from 0 to 31.
For pins on Port B, port
will be 1
and pin
will be from 0 to 31.
For pins on Port C, port
will be 2
and pin
will be from 0 to 5.
For pins on Port X, port
will be 23
and pin
will be from 0 to 39.
Further reading
[edit | edit source]- The Open collector article on Wikipedia
- The Atmel AT32UC3A datasheet Chapter 22: General-Purpose Input/Output Controller (GPIO)