How to Interface GPIO Using MicroPython?
The General-Purpose Input/Output GPIO pins are present on an integrated circuit or board that can be used to perform digital input or output functions. Although these pins have no pre-defined purpose and can be used by developers to perform any desired function(s). Usually, they are used for
- reading switches
- controlling LEDs
- Read/Write various types of sensors.
- Soft SPI/UART/SPI implementation using Bit-Banging.
- Generate clocks.
Basically, GPIO pins let you interface an external board to MCUs(Microcontrollers).
- GPIO pins shouldn’t be used to power and drive other IC’s.
- It shouldn’t be connected to Vcc and GND.
- It shouldn’t be used to draw current for driving motors, Display devices etc.
How To Handle Interrupts Using GPIO Pins?
Now let us have a look at how to program GPIO pins of Raspberry Pi Pico, ESP32, and ESP8266 using MicroPython.
How to Configure a Pin as ADC?
Now let’s understand how to configure a pin as an ADC. Follow the steps given below.
- Using the PINSEL register, configure the GPIO pin for ADC operation.
- Turn on the Clock to the ADC module.
- Deselect all channels and turn on the internal ADC module by adjusting the ADCR.PDN bit.
- Set the appropriate bits in ADCR.SEL to choose the specific channel for A/D conversion.
- Set the ADCR.START bit is used to initiate the A/D conversion for the specified channel.
- Please wait for the conversion to finish; once the conversion is complete, the ADGR.DONE bit will be set.
- ADGR.RESULT 12-bit A/D value is read.
- Use it for further processing or just to show on an LCD.
Let’s have a look at the Micropython code.
from machine import Pin, ADC from time import sleep pot = ADC(0) while True: pot_value = pot.read() print(pot_value) sleep(0.1)
The pin numbers which can be configured as ADC are – (31, 32, 33, 34, and 35).
|Pico||GPIO28, GPIO27, GPIO26|
|ESP32||GPIO36, GPIO39, GPIO34, GPIO35, GPIO32, GPIO33, GPIO25, GPIO26, GPIO27, GPIO14, GPIO12, GPIO13, GPIO4, GPIO2, GPIO15|
|PYBOARD||GPIOX1, GPIOX2, GPIOX3, GPIOX4, GPIOX5, GPIOX6, GPIOX7, GPIOX8, GPIOX11, GPIOX12, GPIOY11, GPIOY12|
If you want to learn more about how to program and interface ADC using Micropython, then read our in-depth blog.
How to Configure a Pin as UART?
The various UART signals RXD, CTS (Clear To Send, active low), RTS (Request To Send, active low), and TXD are mapped to physical pins according to the configuration provided in the
- PSELRXD registers
- PSELCTS registers
- PSELRTS registers
- PSELTXD registers
If any of these registers is set to 0xFFFFFFFF, the accompanying UART signal is not linked to any physical pin. The PSELRXD, PSELCTS, PSELRTS, and PSELTXD registers and their settings are only utilized when the UART is active and are only kept while the device is in ON mode. PSELRXD, PSELCTS, PSELRTS, and PSELTXD must be set only while the UART is turned off.
When the system is in OFF mode, the pins must be set in the GPIO peripheral as specified in Pin configuration to ensure accurate signal levels on the pins by the UART.
At any one moment, only one peripheral can be assigned to operate a certain GPIO pin. Failure to do so may result in erratic behavior.
Let’s have a look at the Micropython code.
from machine import UART uart = UART(1, 9600) # init with given baud rate uart.init(9600, bits=8, parity=None, stop=1) # init with given parameters
The pin numbers which can be configured as ADC in Raspberry Pi Pico are – (1, 2, 6, 7, 11, 12, 16, 17, 21, and 22).
How to Configure a Pin as DAC?
Connect a digital device to a pin that supports digital output if you wish to control it, such as turning on or off a street lamp. Eight digital outputs are available on XBee 802.15.4 devices (from D0 to D7). In addition, depending on the desired default state, set that pin as Digital Output Low (Digital Out, Low ) or Digital Output High (Digital Out, High ).
Connect an analog device to a pin that supports analog output if you wish to control it, such as changing the brightness of an LED (PWM). PWM outputs are available on XBee 802.15.4 RF Modules (P0 and P1). Set the pin to PWM Output .
Let’s have a look at the Micropython code.
from pyb import DAC dac = DAC(1) # create DAC 1 on pin X5 dac.write(128) # write a value to the DAC (makes X5 1.65V) dac = DAC(1, bits=12) # use 12 bit resolution dac.write(4095) # output maximum value, 3.3V
Requirements To Interface GPIO Using MicroPython
Now, let’s talk about the requirements to interface GPIO using MicroPython. So, the requirements are
- Raspberry Pi Pico Microcontroller with MicroPython installed.
- A computer with Python 3.6+ installed in it.
- A USB to micro USB cable
Programming The Pico
To program the Raspberry Pi Pico, you will need to issue read/write requests to any of its I/O pins. Only some pins are available for connecting external devices, whereas the rest are connected internally. Raspberry Pi Pico has 40 pins for interacting with the real world. The following section will tell us about GPIO pins and how to operate them.
The Raspberry Pi Pico provides a strong mechanism to interact with the real world through General Purpose Input Output Pins (GPIO) on one side of the board. Raspberry Pi Pico has forty (40) GPIO pins. The previous Raspberry Pi models had 26 GPIO pins, which have been raised by 40 in all of the newest models.
The beauty of Raspberry Pi Pico GPIO is that any pin can be assigned as Input or Output and utilized by any developer for IO purposes. In this approach, Raspberry Pi Pico has given developers the ability to build their own hardware and software. With this freedom, the developer can also design and modify hardware and create a variety of IoT-based projects. The Raspberry Pi Pico’s key feature, GPIO, is the main driver behind the hardware’s rapid growth as well as popularity among the developer community.
Before we begin working with GPIO, let us first understand the electronics behind the GPIO.
Voltage For Programming Raspberry Pi Pico
In this world, all electronics work at two voltage levels – 5v and 3.3v. Raspberry Pi Pico features a total of four pins on the board, two for each voltage, to give those voltages to connected sensors. When we have positive (+) voltage, we must always provide ground (-) voltage. Also, the Raspberry Pi Pico contains a number of ground (-) voltage pins that are easily configurable. The remaining GPIO pins, both input, and output, are set to 3.3v.
Input For Programming Raspberry Pi Pico
Pull Up and Pull Down resistors are a concept that allows developers to offer input via GPIO pins. All of the INPUT pins may read either 3.3v or 0v. It is determined by the above-mentioned concept of Pull Up or Pull Down. There are just two pins (GPIO2 and GPIO3) that have the Pull Up resistor feature built in. Therefore, the remaining GPIO pins use the Pull-Down resistor concept.
Output For Programming Raspberry Pi Pico
The remaining GPIO pins are configured for OUTPUT, which means they can generate 3.3v or 0v upon activation. Developers can change the values of the pins to suit their needs and project requirements.
Aside from the GPIO classifications listed above, the Raspberry Pi Pico has a multitude of extra pins that may be used for external analog and digital communications. It also assists the developer in providing serial input, PWM communication, and so forth. The information below pertains to a few more dedicated pins on the Raspberry Pi Pico Board.
How to Configure GPIO Pins as Input Pins?
Understanding What is Pin-Class and Its Attributes
A pin object is used to control I/O pins (also known as GPIO, which stands for general-purpose input/output). Pin objects are frequently connected with a physical pin that may drive an output voltage as well as read input voltages.
The pin class has methods for setting the pin’s mode (IN, OUT, etc.) as well as methods for getting and setting the digital logic level. See the ADC class for analog control of a pin.
A pin object is created by using an identifier that clearly defines an I/O pin. The permitted identifier forms and the physical pin to which the identifier translates are port-specific. Therefore, the identifier might be an integer, a string, or a tuple, including the port and pin number.
Let’s look at the code.
from machine import Pin # create an output pin on pin #0 p0 = Pin(0, Pin.OUT) # set the value low then high p0.value(0) p0.value(1) # create an input pin on pin #2, with a pull up resistor p2 = Pin(2, Pin.IN, Pin.PULL_UP) # read and print the pin value print(p2.value()) # reconfigure pin #0 in input mode with a pull down resistor p0.init(p0.IN, p0.PULL_DOWN) # configure an irq callback p0.irq(lambda p:print(p))
class machine.Pin(id, mode=- 1, pull=- 1, *, value, drive, alt)
Access the GPIO pin peripheral linked with the given id. Additional arguments passed to the constructor are used to initialize the pin. Any parameters that are not provided will also be left unchanged.
The Pin class lets you define an alternate function for a specific pin, but it does not describe any additional operations on that pin. Pins in alternate-function mode are often not utilized as GPIO but are instead driven by other hardware peripherals.
The only operation supported on such a pin is re-initialization, which may be accomplished by calling the constructor or the Pin.init() method. Also, if an alternate-function-configured pin is re-initialized with Pin.IN, Pin.OUT, or Pin.OPEN_DRAIN, the alternate function is removed from the pin.
Pin.init(mode=- 1, pull=- 1, *, value, drive, alt)
Re-initialize the pin with the parameters provided. Therefore, only the parameters that are explicitly given will be set. The rest of the pin’s peripheral state will be left unchanged.
How to Configure GPIO Pins as Output Pins?
Now let us see a program to access the onboard LED on the pin labeled as GP25 at the top of the diagram.
GPIO Program 1: Turning On The Onboard LED
The onboard LED is at the pin GP25. It is one of the several General-Purpose Input/Output pins that are present on the Pico. So, these pins can be easily distinguished from other pins as they are labeled as GP followed by the number.
Micropython includes machine.Pin class and a machine module that is specifically dedicated to access and work with GPIO pins.
Now enter the MicroPython REPL and enter the following command:
from machine import Pin led = Pin(25, Pin.OUT) led.on()
This code will turn on the onboard LED. In the above command, the Pin object created specifies that the GPIO pin 25 is attached to it and also will be used as an output pin. The on() method activates the onboard LED to light up by setting a “high” value on the pin25.
A Pin set as OUT can only be used to write the value 0/1 by the MCU.
—[PIN-OUT-BIT] ←-write-only by MCU. So 0 means 0v, and 1means 3.3/5V.
While a Pin Configured as PIN.IN will read the pin-value, and it will also be updated in the PIN-register-bit. Then value will be readonly by MCU.
—[PIN-IN-BIT]—> Read-only by the MCU.
The LED can be switched off by calling the led.off() method. This method will also replace the “high” value with a “low” value on the GPIO pin25. You can also try led.toggle() to blink the onboard LED.
GPIO Pull-Up And Pull-Down
Anyone who develops software for microcontrollers must configure and manage general-purpose input/output (GPIO) pins. On the surface, GPIO configuration appears simple: pins may be input or output, and they can be high or low.
What is Pin Pull-Up?
Pull-ups are the resistors that are used to link a signal to VCC. When the signal is floating, pull-ups are employed to set a default state.
Remember that an input pin is floating at a residual voltage level while it is in high-impedance mode and not driven by external sources. Pull-up resistors keep the pin from floating by pushing it to VCC while not actively operated. When another source pushes the signal low (connects to ground), the pull-up is skipped, and the input pin reads a ‘0’.
Many microcontrollers have options for configuring internal pull-ups. Sometimes a certain pull-up resistor value is required, necessitating the use of an external pull-up rather than a chip’s internal pull-up.
What is Pin Pull-Down?
Pull-downs are the resistors that are used to link a signal to the ground. When the signal is floating, pull-downs are utilized to specify a default state. When another source drives the signal high (through VCC), the pull-down is bypassed, and the input pin reads a ‘1′.
Internal pull-down configuration options are available on several microcontrollers. When a certain pull-down resistor value is required, an external pull-down is used instead of a chip’s internal pull-down.
button_red = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_DOWN) button_black = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
NOTE: Pins that are configured as input pins only can have pull-up & pull-down options.
For out-pin configurations, the pin is in a high impedance or totem pole state, and a very low current can be drawn.
If any input is applied to the out-pin, it will not change its state.
E.g., if the out-pin value = 5V, then the connecting pin can use 0v/5V.
For 0V, it will be in a high impedance state and will allow a very low current to flow, something like a reverse diode.
If the voltage is 5V, then both the pins are in the same state.
Important Things To Consider
If the pin is configured as input, pull up or pull down can be set (machine.Pin.PULL_UP or machine.Pin.PULL_DOWN).
The input pin can be explicitly set as FLOAT (high impedance). Therefore, there is no defined constant; you can enter the value: pull=3.
Both pull-up and pull-down can also be enabled (pull=2)
Input-only GPIOs 34~39 do not have pull-up or pull-down circuitry
Following are the values for all possible gpio modes (2nd argument in machine.Pin()):
GPIO mode : disable input and output 0 GPIO mode : input only 1 GPIO mode : output only mode 2 GPIO mode : output only with open-drain mode 3 GPIO mode : output and input with open-drain mode 4 GPIO mode : output and input mode 5
In normal output mode, the pin has two MOSFETS, one linking the pin to 0V and the other linking it to 3.3V. At any time, one is ON, and the other is OFF. So the pin is actively pulled low or high. This is what you usually want.
In open-drain mode, the one linking it to 3.3V is always OFF. When the pin state is 0, the 0V one is ON; when the pin state is 1, it is OFF. This leaves the pin floating, and you normally have a pullup resistor to produce a logical 1 state. Therefore, the purpose of this is that it allows you to connect multiple such pins together (usually on different devices). Then the pin goes to 1 only if all connected devices are OFF. Therefore, the I2C bus uses this feature to implement clock stretching.
How much current/power you can draw from GPIO Pins?
The GPIO pins have an operational voltage of 3.3v and maximum current consumption of 16mA. This simply means that we may safely power one or two LEDs (Light Emitting Diodes) through a resistor from a single GPIO pin. However, for anything requiring higher current, such as a DC motor, we will need to utilize external components to avoid damaging the GPIO.
How to Configure PIO as Input Pin?
To give the PIO controller (Parallel input/output) control of a GPIO pin, we must set a bit in the PIO enable register to the appropriate pin. To utilize PA11 as an example, we would set bit 11 in the PIO Enable Register (page 588). When a bit is set to 1, the PIO controller is given control. Setting a bit to 0 has no effect. To turn off the PIO controller for that pin, put a bit in the PIO Disable Register.
GPIO Program 2: Temperature Reading via On-chip Temperature Sensor
The built-in temperature sensor of the Raspberry Pi Pico is connected to a special ADC(Analogue-to-Digital Converter) pin. ADC pins are different from standard GPIO pins in such a way that an ADC pin supports a wide range of values depending on the input voltage applied to the pin, whereas the GPIO pins support only two states(value), low and high.<<Add a link: for how to interface ADC/DAC>>
Run the following code to read the temperature sensor from the MicroPython shell:
from machine import ADC temp_sensor = ADC(4) temperature = temp_sensor.read_u16()
Also, the machine module provides ADC() class to work with the ADC pins. Now since it is a thermometer, you will need to convert the integer value of the temperature value to either Fahrenheit or Celsius degree scale.
How the Temperature Sensor Works?
The temperature sensor works by receiving a voltage in the ADC4 pin that is proportionate to temperature. According to the sensor, a temperature of 27 degrees Celsius delivers a voltage of 0.706 V, and with each additional degree, the voltage reduces by 1.721 mV or 0.001721 V. Therefore, each degree below 27 degrees Celsius increases the voltage by the same amount. So, the first step is to convert the 16-bit temperature back to volts based on the 3.3 V maximum voltage. The code for the following:
to_volts = 3.3 / 65535 temperature = temperature * to_volts
Now the temperature value stores a value between 0 to 3.3. Here a send conversion is necessary so as to display the celsius value of the temperature. The code to bring the temperature to Celsius scale is the following:
celsius_degrees = 27 – (temperature – 0.706) / 0.001721
The following is the additional code required if you want to use the Fahrenheit scale:
fahrenheit_degrees = celsius_degrees * 9 / 5 + 32
GPIO Program 3: Blinking The Onboard LED 5 Times
A complete MicroPython application can also be run on the Raspberry Pi Pico at the time it boots. The following is a code to run a short MicroPython application that blinks the onboard LED 5 times. You can also copy this into a new file on your computer with the name led_blink.py:
from machine import Pin from utime import sleep led = Pin(25, Pin.OUT) for i in range(5): led.on() sleep(1) led.off() sleep(1)
Wrapping It Up
These were some very basic applications of the GPIO pins on a Raspberry Pi Pico. In addition, there are multiple external devices such as buttons, sensors, lights, and displays that are compatible with this microcontroller through its GPIO pins and can be used for various applications.
I hope this article helped you in understanding the basics of how to interface GPIO pins using MicroPython.