ESP32 In MicroPython: Using Hardware Registers |
Written by Harry Fairhead & Mike James | ||||
Monday, 19 June 2023 | ||||
Page 2 of 3
Blinky RevisitedNow we can re-write Blinky yet again, but this time using direct access to the GPIO registers. from machine import mem32,Pin from time import sleep_ms led = Pin(2,mode=Pin.OUT) GPIOSet = 0x3FF44008 GPIOClear = 0x3FF4400C mask = 1<<2 while True: mem32[GPIOSet] = mask sleep_ms(500) mem32[GPIOClear] = mask sleep_ms(500) This program uses the standard MicroPython class to set the GPIO line to output. If you think that this is cheating, it is an exercise in setting the line correctly using the GPIO control register, but if you do this you risk getting out of sync with MicroPython’s internal state. That is, if you set a GPIO line to output don’t expect MicroPython to know anything about it. To toggle GPIO2 we make use of the set and clear registers and a mask that has bit 2 set to 1. Notice that 1<<n is a bit pattern with bit n set to 1. Alternatively you could use: mask = 0x02 Once we have the mask, the loop simply stores it in the set and clear register alternately. Notice that as only bit 2 is a 1 this only changes the state of GPIO2. This raises the question of how fast is this direct manipulation of the GPIO line’s state? Using the same optimizations we used in Chapter 4 gives: from machine import mem32,Pin from time import sleep_ms @micropython.native def blink(): GPIOSet = 0x3FF44008 GPIOClear = 0x3FF4400C mask = 1<<2 while True: mem32[GPIOSet] = mask mem32[GPIOClear] = mask led = Pin(2,mode=Pin.OUT) blink() If you try this out you will find that the pulses are slightly faster at 1.8µs compared to 2.7µs. 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. For example, 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. This was introduced in Chapter 4 but without explanation. For example: def gpio_get(): return mem32[0x3FF44004] 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 that are set to output reflect their last written-to state – this is not a way of reading the line’s current state. A set function simply writes the mask to the GPIO_OUT_W1TS_REG register: def gpio_set(mask): mem32[0x3FF44008] = mask A clear function is just as easy and writes to the GPIO_OUT_W1TC_REG register: def gpio_clear(mask): mem32[0x3FF4400C] = mask As before, only the set bits in the mask are affected. Example 1 - Simultaneous Setting of GPIO LinesYou use these two functions to set or clear any GPIO lines, but you often want to select a set of bits and set or clear them in one operation. For example, if you want to change two or more GPIO lines in phase, i.e. all high or all low, then you can use clear and set. For example; gpio_set(0x3) gpio_clear(0x3) sets the bottom 2 bits and so it toggles the GPIO0 and GPIO1. Both turn on and off at exactly the same time. Now consider how you do the same thing but setting GPIO0 high when GPIO1 is low? The best you can do is: gpio_set(0x1) gpio_clear(0x2) gpio_set(0x2) gpio_clear(0x1) and, while this does set the GPIO lines correctly, the changes don’t happen at the same time. What we need is a function that will set any group of GPIO lines to 0 or 1 at the same time: def gpio_setgroup(value, mask): reg = machine.mem32[0x3FF44004] reg = reg & ~mask value = value & mask reg = reg | value machine.mem32[0x3FF44004] = reg The mask gives the GPIO lines that need to be changed, i.e. it determines the group and the value gives the state they are to be set to. For example, if mask is 0111 and value is 0100 and the low four bits of the register are 1010 then reg & ~mask is 1000, value & mask is 0100 and finally reg | value is 1100. You can see that bits 0 to 3 have been set to 100 and bit 4 has been unchanged. 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_setgroup(value, mask): machine.mem32[0x3FF44004] = machine.mem32[0x3FF44004] & ~mask | value & mask pin = Pin(2, Pin.OUT) pin = Pin(4, Pin.OUT) value1 = 1 << 2 | 0 << 4 value2 = 0 << 2 | 1 << 4 mask = 1 << 2 | 1 << 4 while True: gpio_setgroup(value1, mask) gpio_setgroup(value2, mask)
|
||||
Last Updated ( Tuesday, 20 June 2023 ) |