AVR Xmega - ¿Puedo simplificar este código de inicio de puerto / periférico?

1

Asumamos una función que obtiene un puerto como parámetro, configura el módulo SPI en ese puerto y también establece los pines necesarios para un estado de salida.

La declaración se parece a esto:

void SPIInit(PORT_t *portname);

Sin embargo, dentro de la función también necesitaré usar SPIx, siendo x el puerto específico. Por ejemplo, si se llama a la función con PORTD, usaré SPID para el módulo SPI. ¿Hay alguna forma de obtener SPIx de PORTx sin usar declaraciones 'if'?

    
pregunta user34920

3 respuestas

1

No estoy familiarizado con los XMega, pero aquí hay algo que he usado antes. Puede parecer complicado si no ha utilizado los punteros de esta manera.

Para este ejemplo, digamos que tiene tres controladores SPI y cada uno tiene tres registros asociados con ellos. Asumamos que estos nombres y direcciones están definidos en los archivos de encabezado de XMega:

SPI1_CR1 0x4100
SPI1_CR2 0x4102
SPI1_CR3 0x4104

SPI2_CR1 0x4200
SPI2_CR2 0x4202
SPI2_CR3 0x4204

SPI3_CR1 0x4300
SPI3_CR2 0x4302
SPI3_CR3 0x4304

Este ejemplo asume que los registros de configuración de cada registro SPI siguen un esquema de dirección similar. Es posible que esta técnica no funcione con XMega.

  1. Cree una estructura que se utilizará como punteros de dirección:

    typedef struct
    {
        volatile uint16_t CR1; // assumes 16-bit registers
        volatile uint16_t CR2;
        volatile uint16_t CR3;
    } SPI_Regs_t
    

    Si las direcciones de registro no son contiguas, deberá ajustarlo en su estructura agregando miembros de estructura inútiles:

    typedef struct
    {
        volatile uint16_t CR1; // assumes 32 bits of space between addresses
        uint16_t USELESS1;
        volatile uint16_t CR2;
        uint16_t USELESS2;
        volatile uint16_t CR3;
    } SPI_Regs_t
    
  2. Defina punteros a sus direcciones de registro SPI:

    #define SPI1 ((SPI_Regs_t *) SPI1_CR1)
    #define SPI2 ((SPI_Regs_t *) SPI2_CR1)
    #define SPI3 ((SPI_Regs_t *) SPI3_CR1)
    
  3. Ahora, puedes crear funciones así:

    void SPIInit(SPI_Regs_t* SPIx)
    {
        uint16_t myvar;
    
        SPIx->Reg1 = 0xFF;
        uint16_t myvar = SPIx->Reg2;
    }
    

Por supuesto, probablemente tendrás que elegir nombres diferentes. Espero que "SPI1" ya se utilice en los encabezados XMega. :)

Entonces, ahora, la pregunta es: ¿Vale la pena?

    
respondido por el bitsmack
1

Es un poco cuestionable si algo como esto es una buena idea desde el principio, pero sí, puedes hacerlo. Comienza con el supuesto de que las direcciones para los bloques de registros PORTx están espaciadas uniformemente y lo mismo es cierto para los bloques SPIx. Esto parece ser cierto en las hojas de datos, pero tenga en cuenta que no está garantizado oficialmente.

De todos modos, a partir de este supuesto, puedes hacer algo como:

uint8_t n;
SPI_t *spi;

n = ((void*)portname - (void*)&PORTC) / ((void*)&PORTD - (void*)&PORTC);
spi = (SPI_t *) ((void *)&SPIC + n*((void *)&SPID - (void *)&SPIC));
...
spi->DATA = ...;

Con algunas macros puedes dejar que el preprocesador se encargue de toda esta aritmética, lo que sería mejor. Sin embargo, este fragmento de código no comprueba que la dirección resultante sea realmente válida y el módulo periférico existente. Y no puede utilizar este truco para obtener, por ejemplo, el número de vector ISR.

Calcularlo de otra manera: desde la dirección del módulo SPI hasta la dirección PORT, sería un poco más seguro, ya que para el módulo SPI válido siempre hay un puerto correspondiente. Tenga en cuenta también que para hacer que el código anterior sea más universal, se necesitaría al menos un #ifdef de todos modos, para verificar si se define SPID (XMEGA-E solo tiene un módulo SPI).

    
respondido por el Martin
0

Como recuerdo de días pasados trabajando con la familia AVR, no hay manera de hacer lo que quieres sin una selección condicional.

Lo más directo es usar "if / else if / ..... / else structure.

Una alternativa es crear algunas enumeraciones que tengan elementos con nombres similares a los nombres de bloques periféricos de PORT y SPI. Luego, cuando pasa un argumento a la subrutina de configuración, pase el valor de enumeración si es el puerto en su lugar y luego use una instrucción switch dentro de la subrutina para seleccionar configuraciones de puerto SPI específicas.

    
respondido por el Michael Karas

Lea otras preguntas en las etiquetas