We talked earlier about how this approach could be used to mcus whose port register mapping is discontinuous, but consistently discontinuous. We mentioned PIC32 being such a case. I was asked how this approach could be implemented on PIC32.
If you look at the datasheet for say PIC32MX1xx/2xx, here is the mapping of port registers:
offset 0x6010, TRISA
offset 0x6020, PORTA
offset 0x6030, LATA
offset 0x6040, ODCA
...
offset 0x6110, TRISB
offset 0x6120, PORTB
offset 0x6130, LATB
offset 0x6140, ODCB
...
So the gap between each (named) register is 12 bytes, or ***3*** uint32_t types.
The number '3' should immediately ring a bell for those of us who know the chip well. Yes, they are the xCLR, xSET and xINV registers.
So we have an option in defining the GPIO_TypeDef struct: we can name those registers or just pad up the struct to fill the void between the named registers.
Here is what GPIO_TypeDef for a PIC32 may look like:
//gpio definitions
typedef struct {
volatile uint32_t TRIS; //output direction register
//volatile uint32_t reserved1[3]; //padding
volatile uint32_t TRISCLR;
volatile uint32_t TRISSET;
volatile uint32_t TRISINV;
volatile uint32_t PORT; //input data register
volatile uint32_t reserved2[3]; //padding
volatile uint32_t LAT; //output data register
//volatile uint32_t reserved3[3]; //padding
volatile uint32_t LATCLR;
volatile uint32_t LATSET;
volatile uint32_t LATINV;
volatile uint32_t ODC; //opendrain register
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *) &TRISA)
#define GPIOB ((GPIO_TypeDef *) &TRISB)
...
As you can see, I implemented TRIS, PORT, LAT and ODC for a port, as well as the corresponding xCLR, xSET and xINV for TRIS and LAT registers - if you wish, you could implement those for PORT and ODC as well.
The PIN_SET(), PIN_CLR() and other macros would remain the same.
However, the introduction of xCLR, xSET and xINV registers introduce a new way of doing things. The PIN_SET() and etc. macros are read-modify-write operations. With xCLR, xSET and xINV registers, you can make them into simple write operations, by introducing a set of fast PIN_xxx() macros:
//fast port operations
#define FPIN_OUT(port, pins) port->TRISCLR = (pins)
#define FPIN_IN(port, pins) port->TRISSET = (pins)
#define FPIN_SET(port, pins) port->LATSET = (pins)
#define FPIN_CLR(port, pins) port->LATCLR = (pins)
#define FPIN_FLP(port, pins) port->LATINV = (pins)
#define FPIN_GET(port, pins) IO_GET(port, pins) //no fast operations for PORT as we did not implement xCLR/xSET/xINV for PORT
By swapping out PIN_xxx() with FPIN_xxx() macros, you would have significantly increased the speed of port operations.
Disclaimer:
1) the xCLR/xSET/xINV registers' offsets are not documented in any PIC32 documents that I have seen so you are running the risk that Microchip may not have implemented them consistently from chip to chip;
2) I don't have a real PIC32 chip to test but the above simulates as expected in C32/XC32.
A side note: the concept of FPIN_xxx() can be implemented on other chips, like STM32F via BSRR and BRR registers. Its performance gain will depend on both the compiler and hardware - BME for example offers better performance than FPIN_xxx() macros.