Sometimes you might be overthinking somethings...
With all things like this, sometimes easier to look at the sources.
The main Arduino sources are up at:
arduino/ArduinoCore-zephyr: Arduino Core based on Zephyr+llext
They are also already on your machine, under where Arduino15 is on your machine
On mine right now:
C:\Users\kurte\AppData\Local\Arduino15\packages\arduino\hardware\zephyr\0.56.0
In the cores\arduino\wiring_digital.cpp you see:
void digitalWrite(pin_size_t pinNumber, PinStatus status) {
RETURN_ON_INVALID_PIN(pinNumber);
gpio_pin_set_dt(&arduino_pins[pinNumber], status);
}
So it is taking the pin number passed in and looking it up in a table to find the
PORT and PIN of it...
This table is created by Zehpyr magic from the device tree, in wiring_private.h
constexpr struct gpio_dt_spec arduino_pins[] = {
DT_FOREACH_PROP_ELEM_SEP(
DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))};
If you wish to know how the gpio_pin_set_dt works, you can see reference to it in the documentation:
General-Purpose Input/Output (GPIO) β Zephyr Project Documentation
Which if you go to the actual API:
Zephyr API Documentation: GPIO
It shows that the call:
gpio_pin_set_dt(&arduino_pins[pinNumber], status);
is the same as:
gpio_pin_set(&arduino_pins[pinNumber].port, &arduino_pins[pinNumber].pin, status);
The main header file for Zephyr GPIO is included in the Arduino15 code base under the variant for each board: like:
...Arduino15\packages\arduino\hardware\zephyr\0.56.0\variants\arduino_uno_q_stm32u585xx\llext-edk\include\zephyr\include\zephyr\drivers\gpio.h
Where you will find that it does a few checks and then calls off to the gpio_pin_set_raw
...
Where it finally calls off to the STM32 HAL (Hardware Abstraction Layer) which sets or clears the appropriate bit in the correct register for the GPIO port...
Note: I have/had code that I did it directly on UNOQ, but I may not have had it stored on computer when I fried the one Q...
Note: post 3 has some code for allowing you to directly set which mode each pin is used by using Port/Pin number...
But I have a header file I was using with Arduino GIGA and Portenta H7 on MBED, that I could easily adapt:
Arduino_GIGA-stuff/libraries/GIGA_digitalWriteFast at main Β· KurtE/Arduino_GIGA-stuff
For example has:
#ifndef _GIGA_DIGITALWRITEFAST_H_
#define _GIGA_DIGITALWRITEFAST_H_
#include <Arduino.h>
#include "pinDefinitions.h"
#if !defined(ARDUINO_GIGA) && !defined(ARDUINO_PORTENTA_H7_M7)
#error "Only works on Arduino GIGA or Portenta H7 boards"
#endif
static GPIO_TypeDef * const port_table[] = { GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK };
static const uint16_t mask_table[] = { 1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7,
1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15 };
// quick and dirty digitalWriteFast
// Sets the state of an IO pin
// Two versions - this version you takes in a pin number as pin_size_t type - uint8_t
static inline void digitalWriteFast(pin_size_t pin, PinStatus val) __attribute__((always_inline, unused));
static inline void digitalWriteFast(pin_size_t pin, PinStatus val) {
PinName pin_name = g_APinDescription[pin].name;
uint16_t mask = mask_table[pin_name & 0xf];
GPIO_TypeDef * const port = port_table[pin_name >> 4];
if (val) port->BSRR = mask;
else port->BSRR = (uint32_t)(mask << 16);
}
// This version you takes in a pin name (PinName) like LED_RED
static inline void digitalWriteFast(PinName pin_name, PinStatus val) __attribute__((always_inline, unused));
static inline void digitalWriteFast(PinName pin_name, PinStatus val) {
uint16_t mask = mask_table[pin_name & 0xf];
GPIO_TypeDef * const port = port_table[pin_name >> 4];
if (val) port->BSRR = mask;
else port->BSRR = (uint32_t)(mask << 16);
}
...
The first one would require work, to actually pass in the pin number and map to PORT/Pin. I had code that simply duplicated that zephyr magic to create the same table...
or simply use it... Before it was marked private so duplicated.
For names, I duplicated a table like that was used in MBED, like:
typedef enum {
PA_0=0x00, PA_1, PA_2, PA_3, PA_4, PA_5, PA_6, PA_7, PA_8, PA_9, PA_10, PA_11, PA_12, PA_13, PA_14, PA_15,
PB_0=0x10, PB_1, PB_2, PB_3, PB_4, PB_5, PB_6, PB_7, PB_8, PB_9, PB_10, PB_11, PB_12, PB_13, PB_14, PB_15,
PC_0=0x20, PC_1, PC_2, PC_3, PC_4, PC_5, PC_6, PC_7, PC_8, PC_9, PC_10, PC_11, PC_12, PC_13, PC_14, PC_15,
PD_0=0x30, PD_1, PD_2, PD_3, PD_4, PD_5, PD_6, PD_7, PD_8, PD_9, PD_10, PD_11, PD_12, PD_13, PD_14, PD_15,
PE_0=0x40, PE_1, PE_2, PE_3, PE_4, PE_5, PE_6, PE_7, PE_8, PE_9, PE_10, PE_11, PE_12, PE_13, PE_14, PE_15,
PF_0=0x50, PF_1, PF_2, PF_3, PF_4, PF_5, PF_6, PF_7, PF_8, PF_9, PF_10, PF_11, PF_12, PF_13, PF_14, PF_15,
PG_0=0x60, PG_1, PG_2, PG_3, PG_4, PG_5, PG_6, PG_7, PG_8, PG_9, PG_10, PG_11, PG_12, PG_13, PG_14, PG_15,
PH_0=0x70, PH_1, PH_2, PH_3, PH_4, PH_5, PH_6, PH_7, PH_8, PH_9, PH_10, PH_11, PH_12, PH_13, PH_14, PH_15,
PI_0=0x80, PI_1, PI_2, PI_3, PI_4, PI_5, PI_6, PI_7, PI_8, PI_9, PI_10, PI_11, PI_12, PI_13, PI_14, PI_15,
PJ_0=0x90, PJ_1, PJ_2, PJ_3, PJ_4, PJ_5, PJ_6, PJ_7, PJ_8, PJ_9, PJ_10, PJ_11, PJ_12, PJ_13, PJ_14, PJ_15,
PK_0=0xA0, PK_1, PK_2, PK_3, PK_4, PK_5, PK_6, PK_7
} PinNames;
I don't remember if PinStatus is defined or not, but not hard to define or simply
use defines and the like.
Was also pointed out you don't need the mask table, it should reduce to about the same code like:
static inline void digitalWriteFast(PinName pin_name, PinStatus val) {
uint16_t mask = 1 << (pin_name & 0xf);
GPIO_TypeDef * const port = port_table[pin_name >> 4];
if (val) port->BSRR = mask;
else port->BSRR = (uint32_t)(mask << 16);
}
As you probably can see in the PinNames, the port/Pin are encoded into the number...
As for how to do delay()...
Several options depending on things like accuracy...
Like a for loop that simply does nops...
On some boards there is a cycle counter register often part of debug... But I don't think this one has one...
You could setup some timer, that interrupts every millisecond and keeps a counter...
and then delay simply waits until the counter gets to where you need it...
Probably enough for one message.