PIC32 GPIO pines Pregunta de capa de abstracción de hardware

3

Estoy intentando escribir una capa de abstracción de hardware (HAL) para un PIC32 (PIC32MX664F128H para ser preciso), en el que puedo usar definiciones genéricas para los puertos en cuestión sin tener que usar los nombres de puertos y los registros de puertos exactos. Idealmente, me gustaría usar solo estas definiciones para los diferentes pines en el PIC, y tener un módulo "wrapper" en el que puedo especificar a qué pines se refieren las definiciones.

He leído a través de esta publicación en el foro , que básicamente explica exactamente lo que estoy tratando de lograr. La publicación # 4 (respuesta # 3) es de particular interés aquí. La idea básica es usar una estructura ( GPIO_TypeDef ) para definir un tipo que contenga los 4 registros (TRIS, PORT, LAT y ODC) para cada puerto (tenga en cuenta que la publicación del foro fue para un PIC24 (procesador de 16 bits) , y que he adaptado el código para el PIC32 (procesador de 32 bits)):

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

Luego, todo el puerto se convierte y se vincula a una definición que se usa para apuntar a la dirección del puerto en la memoria:

#define GPIOA  ((GPIO_TypeDef *) &TRISA)
#define GPIOB  ((GPIO_TypeDef *) &TRISB)
etc...

La dirección del registro TRIS se usa para el enlace del puntero, ya que es el primero de los cuatro registros que se producen en la memoria para cada puerto. Esto se puede ver en la hoja de datos del PIC (usando PORTB como ejemplo):

Usando PORTB para el resto de esta publicación, la definición GPIOB debería apuntar al registro TRISB (con la dirección base 0xBF886040), o más bien, GPIOB debería apuntar a la dirección del primer elemento del registro TRISB .

Luego se define un conjunto de macros para las operaciones de puerto:

#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

En el código de usuario, uno definiría, por ejemplo, un pin que controla un LED de la siguiente manera (usando PORTB en lugar de PORTC como en la publicación del foro):

#define LED_PORT  GPIOB
#define LED       (1<<4) //led on PORTB.4

El LED se puede encender / apagar de la siguiente manera:

PIN_SET(LED_PORT, LED); //turn LED on
PIN_CLR(LED_PORT, LED); //turn LED off

Sin embargo, cuando uso el código anterior, el pin correspondiente en el registro TRISB se alterna, en lugar del registro LATB. Además, al intentar apagar el LED, el bit correspondiente que se estableció en el registro TRISB no vuelve a cambiar a 0. ¿Por qué esto no funciona?

Lo que es interesante es si defino estructuras separadas para cada uno de los cuatro registros de puertos de la siguiente manera:

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)

y alterne el bit correspondiente (bit 4) en cada uno de estos registros de la siguiente manera:

PIN_SET(IO_LATB, LED);
PIN_CLR(IO_LATB, LED);

funciona perfectamente. Lo mismo ocurre cuando hago lo mismo con los registros TRIS, PORT y ODC (y, por supuesto, modificando las macros en consecuencia). ¿Por qué funciona esto, y no el primer método?

    
pregunta wave.jaco

3 respuestas

2

Creo que el verdadero problema puede estar en que no tienes tu estructura correctamente diseñada para incluir la instrucción atómica de cada sfr sfr. En MplabX puede presionar ctrl en un sfr como PORTA para ir al encabezado. Por ejemplo, para una serie PIC32MM, obtengo esto

extern volatile __LATAbits_t LATAbits __asm__ ("LATA") __attribute__((section("sfrs"), address(0xBF802630)));
extern volatile unsigned int        LATACLR __attribute__((section("sfrs"),address(0xBF802634)));
extern volatile unsigned int        LATASET __attribute__((section("sfrs"),address(0xBF802638)));
extern volatile unsigned int        LATAINV __attribute__((section("sfrs"),address(0xBF80263C)));

Para hacer esto correctamente, la definición de la estructura debería ser algo como

typedef struct {
     volatile unsigned int TRIS; //direction register - offset 0x0000
     volatile unsigned int TRISCLR;
     volatile unsigned int TRISSET;
     volatile unsigned int TRISINV;
     volatile unsigned int PORT; //input data register
     volatile unsigned int PORTCLR;
     volatile unsigned int PORTSET;
     volatile unsigned int PORTINV;
     volatile unsigned int LAT;  //output data register
     volatile unsigned int LATCLR;
     volatile unsigned int LATSET;
     volatile unsigned int LATINV;
     volatile unsigned int ODC;  //open drain register
     volatile unsigned int ODCCLR;
     volatile unsigned int ODCSET;
     volatile unsigned int ODCINV;
} GPIO_TypeDef;    //gpio type definitions

también como una respuesta adicional para extender mi comentario, debe usar las instrucciones atómicas con pic32 o las escrituras de interrupción cruzada en los cierres se corromperán debido a las escrituras de modificación de lectura. Así que para corregir tus ejemplos, deberían ser algo así como

#define PIN_SET(port, pin) port->LATSET = (1UL << pin) //set pins on port
#define PIN_CLR(port, pin) port->LATCLR = (1UL << pin) //clear pins on port
#define PIN_FLP(port, pin) port->LATINV = (1UL << pin) //flip pins on port
La respuesta de

kkrambo merece alguna mención aquí, ya que tiene matices de la respuesta correcta.

    
respondido por el Erik Friesen
1

Eche un vistazo al diagrama de un puerto de E / S típico.

Observe que WR tris anula todas las demás operaciones de escritura, por lo que debe controlar el puerto en la secuencia correcta. WR TRIS debe ser primero, luego WR LAT, luego WR ODC. WR LAT y WR PORT van a una puerta OR, por lo que cualquiera de los dos pone los datos en el pin.

Funciona manualmente porque usted define el orden implícitamente, pero como un comando global, las cosas no pueden ocurrir en el orden correcto, especialmente si obtiene un efecto de alternancia.

Considere volver a escribir la definición de su tipo para incluir un orden específico, de modo que los datos no vuelvan a sí mismos (alternar). Como parte de la HAL, también debe contener la secuencia adecuada.

    
respondido por el Sparky256
1

No estoy familiarizado con este microcontrolador. Pero las direcciones virtuales en esa tabla (6040, 6050, 6060 y 6070) parecen estar 16 bytes aparte. Si un int sin signo es de 32 bits, entonces su estructura los coloca a solo 4 bytes.

¿Funciona su primer método si usa la siguiente definición de estructura?

typedef struct {
 volatile unsigned int TRIS; //direction register - offset 0x0000
 volatile unsigned int reserved1[3];
 volatile unsigned int PORT; //input data register
 volatile unsigned int reserved2[3];
 volatile unsigned int LAT;  //output data register
 volatile unsigned int reserved3[3];
 volatile unsigned int ODC;  //open drain register
} GPIO_TypeDef;    //gpio type definitions

Si es así, entonces quizás todos los bytes del alias 6044 a 604F se encuentren en el registro TRIS y es por eso que vio el cambio del registro TRIS cuando configuró involuntariamente 6048 a 604B.

    
respondido por el kkrambo

Lea otras preguntas en las etiquetas