2017-09-17 5 views
0

Utilisation de l'Arduino IDE pour un Arduino Uno ...asm vers AVR port à partir de variable

je peux envoyer avec succès à un port en utilisant des macros telles que PORTB mais je ne peux pas comprendre comment envoyer des données à un port est défini dans une variable comme ceci:

uint8_t pin = 0; // any value... 
uint8_t port = digitalPinToPort(pin); 
uint8_t *portreg = portModeRegister(port); 
uint8_t portsfr = _SFR_IO_ADDR(port); 

asm volatile 
(
    // other asm instructions... 

    "out %[port], %[masklo] \n\t" 
    :: 
    [port]  "I" (_SFR_IO_ADDR(PORTB)) // works 

    // [port] "I" (_SFR_IO_ADDR(port)) // doesn't compile 
    // [port] "I" (portreg) // doesn't compile 
    // [port] "I" (portsfr) // doesn't compile 
); 

Je trouve l'article suivant qui coud connexes, mais ne montre pas un exemple comment: avr gcc inline asm variable input operand

+0

Comme toute référence du jeu d'instructions vous dira, 'out' ne prend pas une variable. Voyez si vous pouvez accéder à votre port en utilisant l'adresse mappée mémoire à la place. – Jester

Répondre

0

le jeu d'instructions AVR ne supporte pas d'arguments non immédiats aux instructions IN et OUT. Donc, il n'y a simplement aucune instruction machine pour faire exactement ce que vous voulez. Donc, sans faire référence au code auto-modifiable qui s'interdit naturellement sur un système fonctionnant à partir de flash, vous ne pouvez pas atteindre ce que vous voulez.

Le nombre de ports possibles sur un AVR est cependant limitée, généralement 3 - Il serait facile et relativement pas cher à mettre en œuvre au moyen d'un interrupteur cas et quelques énumérations:

typedef enum {PortA, PortB, PortC} portNo; 

void out (portNo, value) { 
    switch (portNo) { 
     case PortA: 
     out (PORTA, value); 
     break; 
     ... 
0

in de l'AVR, Les instructions out, sbi et cbi prennent uniquement des adresses d'E/S littérales, de sorte que le registre auquel elles accèdent doit être connu au moment de la compilation. La bibliothèque pololu-led-strip-arduino est un exemple de bibliothèque qui utilise des modèles pour contourner cela: un argument template spécifie un numéro de broche Arduino et le compilateur recherche les adresses d'E/S appropriées au moment de la compilation et le place dans le code d'assemblage de la fonction .

Si cette approche ne fonctionne pas pour vous, une approche plus simple est d'utiliser une déclaration switch pour convertir le nombre de broches (ou une autre spécification de ce que la broche à utiliser) de quelque chose d'inconnu au moment de la compilation à quelque chose qui est:

switch (pin_number) 
{ 
case 1: 
    // some assembly using pin "1" 
    break; 
case 2 
    // some assembly using pin "2" 
    break; 
// ... 
} 

une troisième approche: le jeu d'instructions AVR n'a pas le support pour les pointeurs, et je crois que vous pouvez mettre en place un pointeur pour pointer sur un registre d'E/S, puis lire et écrire du registre par le pointeur. Donc, vous devriez essayer de compiler un code comme ceci et regardez la liste de démontage fourni par votre toolchain pour voir comment il se fait dans l'assemblage:

void write_to_reg_through_pointer(uint16_t value) { 
    volatile unsigned char * volatile ptr = &PORTB; 
    *ptr = value; 
} 
+0

Ce ne seraient pas les "broches" que vous seriez en train d'adresser dans la structure 'switch ... case', mais plutôt les ports (PORTA, PORTB, ...). Les broches seraient adressées par la valeur de bit que vous auriez mise dans les registres. – tofro

+0

C'est juste une façon de le faire, c'est au développeur. –

0

Notez que tout registre E/S peut être abordé à travers son emplacement de mémoire . Donc, tout ce dont vous avez besoin est d'utiliser le pointeur, qui pointe vers cet emplacement de mémoire. portModeRegister(port) renvoie un tel pointeur vers un registre DDRx ou le port spécifié. Il y a aussi portOutputRegister(port) qui retourne un pointeur pour un registre PORTx et pour un PINx.

volatile uint8_t * p_port = portOutputRegister(port); 

*p_port = xxx; // writing to the PORTx by a pointer 
uint8_t yyy = *p_port; // reading the PORTx value 

aussi, vous pouvez attribuer le pointeur sur le registre directement, en utilisant des macros PortX:

volatile uint8_t * p_port = &PORTB; 

Lifehack

Puisque dans ATmega328P (qui est Arduino), ainsi que dans beaucoup d'autres AVR, registres de port sont tous placés dans des emplacements conséquents dans le même ordre, généralement dans ce: PIXx, DDRx, PORTx. Ainsi, au lieu de stocker trois pointeurs, vous pouvez utiliser uniquement l'inférieur (c.-à-pinx), puis accéder au pointeur sous forme de tableau:

volatile uint8_t * p_base = portInputRegister(port); // obtaining pointer to PINx 

p_base[1] = ddr_val; // writing the DDRx value: offset + 1 
p_base[2] = port_val; // writing the PORTx value: offset + 2 
uint8_t pin_val pin_val = p_base[0]; // reading the PINx value: offset 0