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

Single-Cycle IO Block

At this point you might think that we are ready to access the state of the GPIO lines for general input and output. This isn’t quite the whole story. To accommodate the fact that the processor has two cores, and to make access faster to important devices, there is a special connection, the SIO or Single-cycle IO Block, between the cores and, among other things, the GPIO. The SIO connects directly to the two cores and they can perform single-cycle 32‑bit reads and writes to any register in the SIO. Notice that the SIO is not connected via the general address bus. You can see the general structure of the SIO in the diagram below. You can find out about the other devices it connects to from the documentation - our focus is on the GPIO lines.

 SIO

Notice that the GPIO lines are multipurpose and the SIO only has control when they are being used as GPIO lines. In this sense the SIO is just another peripheral that can take control of a GPIO line.

The SIO provides a set of registers that makes using the GPIO much faster and much easier. The basic registers are:

GPIO_OUT Sets all GPIO lines to high or low

GPIO_IN Reads all GPIO lines

GPIO_OE Sets any GPIO line to output driver or high impedance

There are also three registers – SET, CLR and XOR - that make working with GPIO_OUT and GPIO_OE easier. Each of these can be thought of as a mask that sets, clears or XORs bits in the corresponding register.

For example, GPIO_OUT_SET can be used to set just those bits in GPIO_OUT that correspond to the positions that are set high.

The locations of these registers are as offsets from 0xd0000000:

Offset

Name

Description

0x004

GPIO_IN

GPIO Input value

0x010

GPIO_OUT

GPIO output value

0x014

GPIO_OUT_SET

GPIO output value set

0x018

GPIO_OUT_CLR

GPIO output value clear

0x01c

GPIO_OUT_XOR

GPIO output value XOR

0x020

GPIO_OE

GPIO output enable

0x024

GPIO_OE_SET

GPIO output enable set

0x028

GPIO_OE_CLR

GPIO output enable clear

0x02c

GPIO_OE_XOR

GPIO output enable XOR

 

Blinky Revisited

Now we can re-write Blinky yet again, but this time using direct access to the SIO GPIO registers.

from machine import mem32,Pin
from time import sleep_ms 
led=Pin(25,mode=Pin.OUT)  
addrSIO = 0xd0000000
while True:
    mem32[addrSIO + 0x014] = 1 << 25
    sleep_ms(500)
    mem32[addrSIO + 0x018] = 1 << 25
    sleep_ms(500)

This program uses the standard MicroPython class to set the GPIO line to SIO control and output. If you think that this is cheating, it is an exercise to set the line correctly using the GPIO control register and the SIO.

This example is a demonstration rather than being useful, but there are some very useful functions we can write using our knowledge of how the GPIO lines are controlled. MicroPython is limited to controlling a single GPIO line at a time, but the hardware can change or read multiple GPIO lines in a single register operation. For example:

def gpio_get():
    return mem32[0xd0000000+0x010]

Here the get function simply reads the GPIO_OUT register which has a single bit for the output state of each GPIO line. Notice that GPIO lines set to output reflect their last written-to state.

A set function simply writes the mask to the GPIO_OUT_SET register

def gpio_set(mask):
	mem32[0xd0000000+0x014] = mask

A clear function is just as easy and this just writes to the GPIO_OUT_CLR register:

def gpio_clear(mask):
 	mem32[0xd0000000+0x18C] = mask

You can easily create functions for reset and other logical operations on all of the GPIO lines in one operation, but a single mask value function is usually sufficient:

def gpio_set(value,mask):  
	mem32[0xd0000000+0x01C] = 
(machine.mem32[0xd0000000+0x010])^value & mask

This writes to the GPIO_OUT_XOR register, but it writes a combination of a mask and a value. The mask gives the GPIO lines that need to be changed and the value gives the state they are to be set to. For example, if mask is 0111 and value is 0100 then value & mask is 0100. If this is XORed with the current state of the lines – e.g. 0101, in this case the result is 0001, which changes the state of only GP0 to a zero. Thus we have set lines GP2, GP1 and GP0 as specified in the mask to the corresponding bits in the value, i.e. 0100. Notice that this process sets the lines selected in the mask to either a zero or a one as determined by the bits in value.

As demonstrated in Chapter 4, the value,mask function can be used to set GPIO lines simultaneously:

from machine import Pin
import machine
def gpio_get():
    return machine.mem32[0xd0000000+0x010]
def gpio_set(value,mask):
     machine.mem32[0xd0000000+0x01C]=
(machine.mem32[0xd0000000+0x010])^value & mask
pin=Pin(22,Pin.OUT) pin=Pin(21,Pin.OUT) value1=1<<22 | 0<<21 value2=0<<22 | 1<<21 mask=1<<22 | 1<<21 while True: gpio_set(value1,mask) gpio_set(value2,mask)

This sets lines GP21 and GP22 to 01 and 10 on each pass through the loop:

SIOlogic

 

In Chapter But Not In This Extract

  • Example I - Events
  • Example II PAD - Pull, Drive and Schmitt
  • Digging Deeper 

Summary

  • All of the peripherals, including the GPIO lines, are controlled by registers – special memory locations that you write and read to configure and use the hardware.

  • Exactly where the registers are positioned in address space is given in the documentation as a base address used for all of the similar registers and an offset that has to be added to the base to get the address of a particular register.

  • The SIO block provides a more convenient way to access the GPIO lines and it has a different set of addresses and registers to the GPIO lines.

  • With knowledge of how things work, you can add functions that are missing from MicroPython such as events and PAD control.

  • Each GPIO line connects to the outside world via a PAD which has a number of configurable elements such as pull-up, slew rate and so on.

 SIO

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 ***NEW!
  • 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 
  • Chapter 18 Asyncio And Servers
  • Chapter 19 Direct To The Hardware
             Extract: Direct To The Hardware

Also of interest:

Raspberry Pico File System

<ASIN:1871962803>

<ASIN:B0BR8LWYMZ>

<ASIN:187196279X>

<ASIN:B0BL1HS3QD>

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Explore SyncFusion's Blazor Playground
16/04/2024

Syncfusion has provided an in-browser environment where you can write, compile and run code that uses Blazor components and get it previewed live.



Pure Virtual C++ 2024 Sessions Announced
19/04/2024

Microsoft has announced the sessions for Pure Virtual C++ 2024, which is taking place on April 30th 15:00 UTC. People who sign up will get access to five sessions happening on the day, alongside a ran [ ... ]


More News

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info

Chapter 18

Direct To The Hardware

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.

Single-Cycle IO Block

At this point you might think that we are ready to access the state of the GPIO lines for general input and output. This isn’t quite the whole story. To accommodate the fact that the processor has two cores, and to make access faster to important devices, there is a special connection, the SIO or Single-cycle IO Block, between the cores and, among other things, the GPIO. The SIO connects directly to the two cores and they can perform single-cycle 32‑bit reads and writes to any register in the SIO. Notice that the SIO is not connected via the general address bus.

Yo
u can see the general structure of the SIO in the diagram below. You can find out about the other devices it connects to from the documentation - our focus is on the GPIO lines.

Notice that the GPIO lines are multipurpose and the SIO only has control when they are being used as GPIO lines. In this sense the SIO is just another peripheral that can take control of a GPIO line.

The SIO provides a set of registers that makes using the GPIO much faster and much easier. The basic registers are:

GPIO_OUT Sets all GPIO lines to high or low

GPIO_IN Reads all GPIO lines

GPIO_OE Sets any GPIO line to output driver or high impedance

There are also three registers – SET, CLR and XOR - that make working with GPIO_OUT and GPIO_OE easier. Each of these can be thought of as a mask that sets, clears or XORs bits in the corresponding register.

For example, GPIO_OUT_SET can be used to set just those bits in GPIO_OUT that correspond to the positions that are set high.

The locations of these registers are as offsets from 0xd0000000:

Offset

Name

Description

0x004

GPIO_IN

GPIO Input value

0x010

GPIO_OUT

GPIO output value

0x014

GPIO_OUT_SET

GPIO output value set

0x018

GPIO_OUT_CLR

GPIO output value clear

0x01c

GPIO_OUT_XOR

GPIO output value XOR

0x020

GPIO_OE

GPIO output enable

0x024

GPIO_OE_SET

GPIO output enable set

0x028

GPIO_OE_CLR

GPIO output enable clear

0x02c

GPIO_OE_XOR

GPIO output enable XOR

Blinky Revisited

Now we can re-write Blinky yet again, but this time using direct access to the SIO GPIO registers.

from machine import mem32,Pin
from time import sleep_ms 
led=Pin(25,mode=Pin.OUT)  
addrSIO = 0xd0000000
while True:
    mem32[addrSIO + 0x014] = 1 << 25
    sleep_ms(500)
    mem32[addrSIO + 0x018] = 1 << 25
    sleep_ms(500)

This program uses the standard MicroPython class to set the GPIO line to SIO control and output. If you think that this is cheating, it is an exercise to set the line correctly using the GPIO control register and the SIO.

This example is a demonstration rather than being useful, but there are some very useful functions we can write using our knowledge of how the GPIO lines are controlled. MicroPython is limited to controlling a single GPIO line at a time, but the hardware can change or read multiple GPIO lines in a single register operation. For example:

def gpio_get():
    return mem32[0xd0000000+0x010]

Here the get function simply reads the GPIO_OUT register which has a single bit for the output state of each GPIO line. Notice that GPIO lines set to output reflect their last written-to state.

A set function simply writes the mask to the GPIO_OUT_SET register

def gpio_set(mask):
	mem32[0xd0000000+0x014] = mask

A clear function is just as easy and this just writes to the GPIO_OUT_CLR register:

def gpio_clear(mask):
 	mem32[0xd0000000+0x18C] = mask

You can easily create functions for reset and other logical operations on all of the GPIO lines in one operation, but a single mask value function is usually sufficient:

def gpio_set(value,mask):  
	mem32[0xd0000000+0x01C] = (machine.mem32[0xd0000000+0x010])^value & mask

This writes to the GPIO_OUT_XOR register, but it writes a combination of a mask and a value. The mask gives the GPIO lines that need to be changed and the value gives the state they are to be set to. For example, if mask is 0111 and value is 0100 then value & mask is 0100. If this is XORed with the current state of the lines – e.g. 0101, in this case the result is 0001, which changes the state of only GP0 to a zero. Thus we have set lines GP2, GP1 and GP0 as specified in the mask to the corresponding bits in the value, i.e. 0100. Notice that this process sets the lines selected in the mask to either a zero or a one as determined by the bits in value.

 

As demonstrated in Chapter 4, the value,mask function can be used to set GPIO lines simultaneously:

from machine import Pin
import machine
def gpio_get():
    return machine.mem32[0xd0000000+0x010]
def gpio_set(value,mask):
machine.mem32[0xd0000000+0x01C]=
(machine.mem32[0xd0000000+0x010])^value & mask
pin=Pin(22,Pin.OUT) pin=Pin(21,Pin.OUT) value1=1<<22 | 0<<21 value2=0<<22 | 1<<21 mask=1<<22 | 1<<21 while True: gpio_set(value1,mask) gpio_set(value2,mask)

This sets lines GP21 and GP22 to 01 and 10 on each pass through the loop:


 

 

 

Example I - Events

In Chapter 7 the idea of events was introduced, but MicroPython doesn’t provide any access to events. The solution is to add our own function that accesses the GPIO register that records interrupts. This is a register in the GPIO set of registers rather than in the SIO as GPIO interrupts aren’t specific to what is controlling the GPIO line.

The base address of the GPIO registers is 0x40014000. After the end of the set of status and control registers, starting at offset 0x0f0, there is a block of four interrupt registers that record the status of the GPIO lines. Each group of four bits gives the status of the various level and edge events:

Bits

Name

Type

Reset

31

GPIO7_EDGE_HIGH

WC

0x0

30

GPIO7_EDGE_LOW

WC

0x0

29

GPIO7_LEVEL_HIGH

RO

0x0

28

GPIO7_LEVEL_LOW

RO

0x0

27

GPIO6_EDGE_HIGH

WC

0x0

26

GPIO6_EDGE_LOW

WC

0x0

25

GPIO6_LEVEL_HIGH

RO

0x0

24

GPIO6_LEVEL_LOW

RO

0x0

 

. . .

 

 

8

GPIO2_LEVEL_LOW

RW

0x0

7

GPIO1_EDGE_HIGH

RW

0x0

6

GPIO1_EDGE_LOW

RW

0x0

5

GPIO1_LEVEL_HIGH

RW

0x0

4

GPIO1_LEVEL_LOW

RW

0x0

3

GPIO0_EDGE_HIGH

RW

0x0

2

GPIO0_EDGE_LOW

RW

0x0

1

GPIO0_LEVEL_HIGH

RW

0x0

0

GPIO0_LEVEL_LOW

RW

0x0

 

The format of the raw interrupt registers is more complicated than the previous one bit to one GPIO line arrangement we have encountered before. In this case there are four bits per GPIO line and they record different interrupt types. The first four bits of the first register record interrupts on GP0:

3

GPIO0_EDGE_HIGH

WC

0x0

2

GPIO0_EDGE_LOW

WC

0x0

1

GPIO0_LEVEL_HIGH

RO

0x0

0

GPIO0_LEVEL_LOW

RO

0x0

This pattern is repeated for each of the GPIO lines and, when all of the bits in the first register have been used, the pattern continues in the second register with GP8 and so on. Each register records the event data for eight GPIO lines. Notice that each of the bits is set if the event that would cause the interrupt occurs – the interrupt itself only occurs if it is enabled. What this means is that the level bits track the current level of the GPIO line and the edge bits are set if an edge of that type has occurred. The WC in the third column indicates that the bit is cleared if you write to it and this is how the event is cleared.

The new problem here is that we have to work out which register is concerned with which GPIO line – each register looks after eight GPIO lines – and which group of four bits in the register gives the events for that GPIO line.

The following function accepts the GPIO number and works out which register and group of bits it corresponds to:

def gpio_get_events(pinNo):
    mask = 0xF << 4 * (pinNo % 8)
    intrAddr = 0x40014000 + 0x0f0 + (pinNo // 8)*4
    return (machine.mem32[intrAddr] & mask) >> (4 * (pinNo % 8))

The calculation for the address of the register needed is just the base address plus the offset of the first interrupt register and then +(pinNo//8)*4. As each register deals with a group of 8, pinNo//8 (integer division) gives the number of the register needed and *4 converts this to a byte address, i.e. each register is four bytes. The mask is constructed using a similar technique. The first GPIO line uses four bits starting at 0, the second uses four bits starting at 4 the third at 8 and so on, i.e. (4*(pinNo % 8), and this is used to create a mask.

Once you have the basic way of accessing the bits you need, you can reuse it to create a clear events function:

def gpio_clear_events(pinNo, events):
    intrAddr = 0x40014000 + 0x0f0 + (pinNo // 8)*4
    machine.mem32[intrAddr] = events << (4 * (pinNo % 8))

To clear the raw interrupt bit you simply have to write a zero to it.

Example II PAD - Pull, Drive and Schmitt

Each GPIO line has an identical input output stage, called a PAD, which is the connection to the outside world, no matter what mode the GPIO line is being used in. This is fundamental to the workings of the GPIO line and you might be wondering why it is being introduced so late? The answer is that MicroPython doesn’t fully support it and the aspects of the PAD that it does support, Pull Up and Pull Down, are simple enough. If you are interested in the finer details then you are going to have to implement functions that work with them.

The structure of the PAD can be seen below:

You can see that under program control you can set the pull up/pull down configuration and enable the input/output. The input also has a Schmitt trigger that can be enabled to clean up noisy inputs. The output can be customized by slew rate, how fast it changes and drive strength.

Before moving on to the software, it is worth explaining the basic ideas of the options. The Schmitt trigger adds hysteresis to the input line. This means that before the state changes from high to low it has to cross a threshold, but to change back to a low state it has to cross a lower threshold. This acts like a limited debounce mechanism in that it stops the line from going low because the input drops a little after going high. The Pico’s Schmitt trigger uses thresholds of a 0.2V difference if the processor supply voltage is 2.5V to 3.3V and 0.18V if the voltage is 1.8V. What this means is that at 3.3V the input has to be greater than 2V to be a one, but after this the voltage has to fall to 1.8V before it changes back to a zero. For a zero, the thresholds are 0.8V to change a zero and 2V to change back to a one.

The output drive strength isn’t to do with how much current the GPIO line can source, it is about the voltage output at different currents. It is the effective output resistance. Each time the drive current is increased by 2mA another transistor is used in the drive, so lowering the output resistance. This has the effect of increasing or decreasing the voltage at the pin. For example, if you set the drive to 1 then, if you want to keep the output voltage at or above 2.62V, i.e. a logic 1, you can’t draw more than 2mA. Put more simply, if you want the output to maintain voltages that are within the threshold for a logic 1 or 0 then you can only draw 2, 4, 8 or 12mA from the GPIO line depending on the setting of the drive strength. If you draw more current, this is fine, but the voltage will fall below the standards for 3.3V logic.

The final option is to change the slew rate. There is no information on this in the datasheet, but slowing the rate of change of the GPIO line can be useful when driving long lines or reactive loads. How useful this is in practice is a matter of experimentation.

If you want to work with the PAD directly then you need to know the location and format of the PAD control registers. These start at 0x4001c000 and the first, controlling the GP0 PAD is at offset 0x04 and in general the register for GPn PAD is at offset 4(n+1). The format of the PAD register is:

Bits

Name

Description

Type

Reset

31:8

Reserved

-

-

-

7

OD

Output disable. Has priority over output enable from peripherals

RW

0x0

6

IE

Input enable

RW

0x1

5:4

DRIVE

Drive strength.

0x02mA

0x1 → 4mA

0x2 → 8mA

0x3 → 12mA

RW

0x1

3

PUE

Pull up enable

RW

0x0

2

PDE

Pull down enable

RW

0x1

1

SCHMITT

Enable Schmitt trigger

RW

0x1

0

SLEWFAST

Slew rate control. 1 = Fast, 0 = Slow

RW

0x0

Using this information it is fairly easy to write functions to set each of the characteristics of the PAD. For example, to read the slew rate:

def pad_get_slew(gpio):
    return mem32[0x4001c000+(gpio+1)*4] & 0x01

To set the skew rate:

def pad_set_slew(gpio,value):
    if value:
        mem32[0x4001c000+(gpio+1)*4]=
mem32[0x4001c000+(gpio+1)*4] | 0x1
else: mem32[0x4001c000+(gpio+1)*4]=
mem32[0x4001c000+(gpio+1)*4] & 0xFFFFFFFE

Getting and setting the Schmitt trigger is just as easy, but now we set or clear the second bit not the first:

def pad_set_schmitt(gpio,value):
    if value:
        mem32[0x4001c000+(gpio+1)*4]= 
mem32[0x4001c000+(gpio+1)*4] | 0x2
else: mem32[0x4001c000+(gpio+1)*4]=
mem32[0x4001c000+(gpio+1)*4] & 0xFFFFFFFD

Setting the drive is slightly more complicated as it is a three-bit value:

def pad_get_drive(gpio):
    return (mem32[0x4001c000+(gpio+1)*4] & 0xE0) >> 5
def pad_set_drive(gpio, value):
    mem32[0x4001c000+(gpio+1)*4]=
mem32[0x4001c000+(gpio+1)*4] ^ (0xE0 & (value<< 5))

The set function uses the same logic as the value mask function given earlier to only change the bits we are interesting in changing.

Of course we, don’t need to deal with the pull-ups or pull-downs as there are methods that do the job.

Digging Deeper

There is so much more to explore, but now we have covered the details that make things difficult to get started. From here you can read the datasheet to find out how the registers control things and implement MicroPython functions to extend what you can do. The biggest difficult is finding the register that contains the bits that reflect the status or control whatever it is you are interested in. Once you have found this out, the only remaining difficulty is in working out how to set or clear the bits you need to work with without changing other bits. It also has to be said that hardware documentation at this level is often incomplete due to assumptions the writer makes about what you should already know. In such a circumstance your best approach is the experimental method. Work out the simplest program you can think of to verify that you understand what the hardware does – and if you are wrong always check the addresses and bits you are changing before concluding that things work differently to the manual.

 

Summary

  • All of the peripherals, including the GPIO lines, are controlled by registers – special memory locations that you write and read to configure and use the hardware.

  • Exactly where the registers are positioned in address space is given in the documentation as a base address used for all of the similar registers and an offset that has to be added to the base to get the address of a particular register.

  • The SIO block provides a more convenient way to access the GPIO lines and it has a different set of addresses and registers to the GPIO lines.

  • With knowledge of how things work, you can add functions that are missing from MicroPython such as events and PAD control.

  • Each GPIO line connects to the outside world via a PAD which has a number of configurable elements such as pull-up, slew rate and so on.

 

 



Last Updated ( Monday, 13 December 2021 )