Raspberry Pi 5 IoT In C - Gpio5 |
Written by Harry Fairhead | |||||
Tuesday, 01 April 2025 | |||||
Page 2 of 4
Initializing GPIOBefore we can use a GPIO line it has to be initialized and connected to the RIO: void gpio_init(uint32_t gpio) { gpio_set_dir(gpio, GPIO_IN); gpio_put(gpio, 0); gpio_set_function(gpio, GPIO_FUNC_RIO); } This is almost identical to the corresponding function in the Pico SDK. The GPIO line is set to input and a zero output before connecting to the RIO, which is called SIO in Pico jargon. To make this work we need to define the functions called but, these are also standard Pico SDK functions and will be defined later. All of the error detection code included in the Pico SDK is omitted from Gpio5. The reason is that in nearly all cases the errors being tested for can be detected at compile time and hence there is little point in slowing down the code at runtime. For example the Pico SDK usually checks that the GPIO line specified is a valid line but the number of the line to be used is usually selected and set by the programmer and a runtime check is usually not required. If you do need to use a runtime check then make it before calling the Gpio5 function. The Pico SDK also provides a masked version of the function where you can set bits in the mask corresponding to lines that you want to set to RIO operation: void gpio_init_mask(uint32_t gpio_mask) { for (int i = 0; i < 29; i++) { if (gpio_mask & 1) { gpio_init(i); } gpio_mask >>= 1; } } The set_function function is: void gpio_set_function(uint32_t gpio, enum gpio_function_rp1 fn) { pad[gpio] = 0x50; GPIO[gpio].ctrl = fn; } This not only sets the function to RIO it also puts the line into input mode with output enabled so that other functions can change the direction. A deinit function now easy: void gpio_deinit(uint gpio) { gpio_set_function(gpio, GPIO_FUNC_NULL); } Next we need to set the line direction and the SDK has three functions to do the job. The first sets the direction of a single line by constructing a mask and then calling one of the two mask functions: void gpio_set_dir(uint32_t gpio, bool out) { uint32_t mask = 1ul << gpio; if (out) gpio_set_dir_out_masked(mask); else gpio_set_dir_in_masked(mask); }
The mask functions do the actual job of setting the direction by accessing the RIO registers: void gpio_set_dir_in_masked(uint32_t mask) { rioCLR->OE = mask; } void gpio_set_dir_out_masked(uint32_t mask) { rioSET->OE = mask; } The third function is a “bulk” setting function which only sets the direction of the lines specified in the mask to the direction specified by the corresponding bit in the value: void gpio_set_dir_masked(uint32_t mask, uint32_t value) { rioXOR->OE = (rio->OE ^ value) & mask; } This makes use of the XOR register and implements the well-known formula: data XOR ( (data XOR value) AND mask) to set the bits in data specified by mask to the values of the corresponding bits in value. An alternative expression that does the same job and is sometimes useful is: data & ~mask | value & mask Get and SetNow we have the GPIO lines of our choice initialized and set to a direction we can start using them as input or output lines. There are two output functions that simply set or clear the GPIO lines specified in the mask: void gpio_set_mask(uint32_t mask) { rioSET->Out = mask; } void gpio_clr_mask(uint32_t mask) { rioCLR->Out = mask; } An XOR function along the same lines is also often useful as this toggles the state of the lines specified in the mask: void gpio_xor_mask(uint32_t mask) { rioXOR->Out = mask; } |
|||||
Last Updated ( Tuesday, 01 April 2025 ) |