I am attempting to write a Hardware Abstraction Layer (HAL) for a PIC32 (PIC32MX664F128H to be precise), in which I can use generic defines for the ports in question without having to use the exact port names and port registers. Ideally I would like to only use these defines for the various pins on the PIC, and have a "wrapper" module in which I can specify which pins the defines refer to.
I have read through this forum post, which basically explains exactly what I am trying to achieve. Post #4 (reply #3) is of particular interest here. The basic idea is to use a struct (GPIO_TypeDef
) to define a type that contains the 4 registers (TRIS, PORT, LAT and ODC) for each port (note that the forum post was for a PIC24 (16-bit processor), and that I've adapted the code for the PIC32 (32-bit processor)):
typedef struct {
volatile unsigned int TRIS; //direction register - offset 0x0000
volatile unsigned int PORT; //input data register
volatile unsigned int LAT; //output data register
volatile unsigned int ODC; //open drain register
} GPIO_TypeDef; //gpio type definitions
The entire port is then cast and bound to a define which is used to point to the port address in memory:
#define GPIOA ((GPIO_TypeDef *) &TRISA)
#define GPIOB ((GPIO_TypeDef *) &TRISB)
etc...
The TRIS register's address is used for the pointer binding, since it is the first of the four register to occur in memory for each port. This can be seen from the PIC's datasheet (using PORTB as an example):
Using PORTB for the rest of this post, the define GPIOB
thus should point to the TRISB register (with base address 0xBF886040), or rather, GPIOB
should point to the address of the first element of the TRISB register.
A set of macros is then defined for the port operations:
#define PIN_SET(port, pins) port->LAT |= (pins) //set pins on port
#define PIN_CLR(port, pins) port->LAT &=~(pins) //clear pins on port
#define PIN_FLP(port, pins) port->LAT ^= (pins) //flip pins on port
#define PIN_GET(port, pins) ((port->PORT) & (pins)) //get pins
#define PIN_OUT(port, pins) port->TRIS &=~(pins) //pins as output
In the user code, one would define e.g. a pin that controls an LED as follows (using PORTB instead of PORTC as in the forum post):
#define LED_PORT GPIOB
#define LED (1<<4) //led on PORTB.4
The LED can then be turned on/off as follows:
PIN_SET(LED_PORT, LED); //turn LED on
PIN_CLR(LED_PORT, LED); //turn LED off
However, when I use the above code, the corresponding pin in the TRISB register is toggled, instead of the LATB register. Furthermore, attempting to turn the LED off, the corresponding bit that was set in the TRISB register doesn't toggle back to 0. Why is this not working?
What is interesting is if I define seperate structs for each of the four port registers as follows:
typedef struct
{
volatile unsigned int LAT;
} IO_LAT;
typedef struct
{
volatile unsigned int PORT;
} IO_PORT;
typedef struct
{
volatile unsigned int TRIS;
} IO_TRIS;
typedef struct
{
volatile unsigned int ODC;
} IO_ODC;
#define IO_LATB ((IO_LAT *)&LATB)
and toggle the corresponding bit (bit 4) in each of these registers as follows:
PIN_SET(IO_LATB, LED);
PIN_CLR(IO_LATB, LED);
it works perfectly. The same goes for when I do the same with the TRIS, PORT and ODC registers (and of course modifying the macros accordingly). Why does this work, and not the first method?