The Pico In MicroPython: Direct To The Hardware
Written by Harry Fairhead & Mike James   
Monday, 13 December 2021
Article Index
The Pico In MicroPython: Direct To The Hardware
Single-Cycle IO Block

Just occasionally you need to work directly with the hardware and MicroPython has the commands to let you do this fairly easily but you need to know more about the hardware than usual. This is an extract from our latest book all about the Raspberry Pi Pico in MicroPython.

Programming the Raspberry Pi Pico/W In MicroPython Second Edition

By Harry Fairhead & Mike James

picopython2e360

Buy from Amazon.

Contents

  • Preface
  • Chapter 1 The Raspberry Pi Pico – Before We Begin
  • Chapter 2 Getting Started
  • Chapter 3 Getting Started With The GPIO
  • Chapter 4 Simple Output
  • Chapter 5 Some Electronics
  • Chapter 6 Simple Input
             Extract: Simple Input 
  • Chapter 7 Advanced Input – Events and Interrupts
  • Chapter 8 Pulse Width Modulation
             Extract: PWM 
  • Chapter 9 Controlling Motors And Servos
             Extract: DC Motors
  • Chapter 10 Getting Started With The SPI Bus
  • Chapter 11 A-To-D and The SPI Bus 
  • Chapter 12 Using The I2C Bus
  • Chapter 13 Using The PIO   
  • Chapter 14 The DHT22 Sensor Implementing A Custom Protocol
             Extract: A PIO Driver For The DHT22  
  • Chapter 15 The 1‑Wire Bus And The DS1820
  • Chapter 16 The Serial Port
  • Chapter 17 Using The Pico W - WiFi
             Extract: HTTP Client 
             Extract: Sockets***NEW!
  • Chapter 18 Asyncio And Servers
  • Chapter 19 Direct To The Hardware
             Extract: Direct To The Hardware

Also of interest:

Raspberry Pico File System

<ASIN:B0BR8LWYMZ>

<ASIN:B0BL1HS3QD>

MicroPython provides classes and methods to let you access most of the major hardware features of the Pico. They are very simple wrappers around the basic mechanism of working with the hardware – memory-mapped registers. Unfortunately at the time of writing there are many hardware features which are simply not exposed via MicroPython. In most cases it is possible to extend what you access using lower-level interactions with the hardware – still staying in MicroPython, but writing and reading the low-level register based hardware.

The obvious reason for knowing how to use memory-mapped registers is that if MicroPython doesn’t provide a function that does what you want, create it! Perhaps a better reason is just to know how things work. In this chapter we take a look at how the Pico presents its hardware for you to use and how to access it via basic software.

Registers

Some processors have special ways of connecting devices, but the Pico’s processor uses the more common memory-mapping approach. In this, each external device is represented by a set of memory locations or “registers” that control it. Each bit in the register controls some aspect of the way the device behaves. Groups of bits also can be interpreted as short integers which set operating values or modes.

How do you access a register? MicroPython provides a number of ways of doing this but the simplest is to make use of the mem functions in the machine module:

machine.mem32[address] Returns or sets a 32-bit value at the address

machine.mem16[address] Returns or sets a 16-bit value at the address

machine.mem8[address] Returns or sets an 8-bit value at the address

The only difficult part is in working out the address you need to use and the value that sets or resets the bits you need to modify.

For example, if you look in the documentation you will find that the GPIO registers start at address 0x40014000. The registers are defined by their offset from this starting address. So for example, the table of GPIO registers is:

Offset

Register Name

Description

0x000

GPIO0_STATUS

GPIO status

0x004

GPIO0_CTRL

GPIO control including function select and overrides

0X008

GPIO1_STATUS

GPIO status

0x00c

GPIO1_CTRL

GPIO control including function select and overrides

and so on down to

0x0ec

GPIO29_CTRL

GPIO control including function select and overrides

 

You can see that there are two registers for each GPIO line from GP0 to GP29, one control register and one status register.

Each register has the same format for each GPIO line. For example, the status register is:

Bits

Name

Description

Type

Reset

31:27

Reserved

 

-

-

26

IRQTOPROC

Interrupt to processors, after override applied

RO

0x0

25

Reserved

 

-

-

24

IRQFROMPAD

Interrupt from pad, before override applied

RO

0x0

23:20

Reserved

 

-

-

19

INTOPERI

Input signal to peripheral, after override applied

RO

0x0

18

Reserved

 

-

-

17

INFROMPAD

Input signal from pad, before override applied

RO

0x0

16:14

Reserved

 

-

-

13

OETOPAD

Output enable to pad, after override applied

RO

0x0

12

OEFROMPERI

Output enable from selected peripheral, before override applied

RO

0x0

11:10

Reserved

 

-

-

9

OUTTOPAD

Output signal to pad after override applied

RO

0x0

8

OUTFROMPERI

Output signal from selected peripheral, before override applied

RO

0x0

7:0

Reserved

 

-

-

You can see that many of the 32 bits in the register are not used, but bit 9 is OUTTOPAD which is the final state of the GPIO line after register overrides have been applied. You can read its current value using:

from machine import mem32
addrGP0Status= 0x40014000
value=mem32[addrGP0Status]
print(bin(value)) 

This prints the current status of GP0 in binary. If you want to find the status of GPn you need to use address 0x40014000+2n. Usually addresses are specified as a base address, i.e. where things start, and an offset that has to be added to the base to get the address of a specific device.

This is the general way you work with peripheral devices such as the PWM units or I2C hardware, but the GPIO is special in that it has another set of registers that control it.



Last Updated ( Tuesday, 06 August 2024 )