Langage C de bas niveau - INPUT_PULLUP - ATmega328p

Bonjour,
Je savais qu'il était possible de programmer en assembleur avec l'IDE arduino avec le mot clé: asm.
Exemple : soustraction de deux variables v1 et v2 en assembleur :

volatile uint8_t value1 asm("v1") __attribute__ ((used)) = 65;
volatile uint8_t value2 asm("v2") __attribute__ ((used)) = 15;


void setup() {
  Serial.begin(115200);
  asm
  (
    "lds r18, v1  \n" // lds : charge un octet de l’espace de données vers un registre
    "lds r19, v2  \n"
    "sub r18, r19 \n" // sub : soustrait deux registres
    "sts v1, r18  \n"// sts : Ecrit le résultat à la même adresse qu’il a été extrait
  );
  Serial.println(value1);
}

void loop() {}

Je viens de constater qu'il était possible de programmer les registres qui gèrent les pins de l'ATmega328P directement et ceci sans passer par le mot clé asm. C'est ainsi que j'ai programmer la pin D2 d'une UNO afin de faire clignoter une LED :

void setup() {
  DDRD |= B00000100; // Sélection de D2 dans le registre DDRD
  //PORTD |= B00000100; // Met D2 à HIGH - l'instruction n'est pas nécessaire
}
void loop() {
  PORTD |= B00000100; // Met D2 à HIGH
  delay(200);
  PORTD ^= B00000100; //PORTD &= ~B00000100; // met D2 à low
  delay(1000);
}

J'ai trouvé ce schéma concernant la gestion de ces registres :


C'est ainsi que :

  • les broches D0, D1, D2, D3, D4, D5, D6 et D7 font partie du groupe D ;
  • les broches D8, D9, D10, D11, D12 et D13 font partie du groupe B ;
  • les broches analogiques font partie du groupe C.

Du coup il y a 9 registres utilisables :

  • PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;

  • PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;

  • PORTC, PINC et DDRC pour les broches analogiques.

  • les registres DDR (Data Direction Register) permettent de positionner le sens d'un port (INPUT, OUTPUT);

  • les registres PORT permettent de positionner l'état d'un port (HIGH ou LOW);

  • les registres PIN permettent de lire l'état d'un port.

Là où je coince c'est au niveau de la sélection d'une pin en entrée et en INPUT_PULLUP.
J'ai bien compris que pour paramétrer en entrée par exemple la pin D7, il va falloir que je positionne à 0 le bit de poids fort (b7) dans le registre DDRD. A l'inverse si je souhaite programmer cette pin en sortie, il me faut armer ce bit : DDRD |= B10000000;

Donc pour paramétrer une pin en entrée, il faut mettre à 0 le bit du registre DDR qui lui correspond. Mais alors comment fait on pour paramétrer une pin en entrée avec l'utilisation du pullup interne ?
J'ai pensé qu'il fallait peut-être mettre le bit à 1 dans le registre DDR et utiliser le registre PIN correspondant ?

Merci par avance.

L'idéal pour trouver ce type d'information est la datasheet du micro. Il est dit:

If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated

Si on veut une broche en entrée pullup, c'est bien en entrée qu'il faut la mettre. D'où un bit de DDRD à 0. Maintenant, il ont choisi d'écrire en sortie la valeur 1 (dans PORTD) pour activer la pullup. Cela fait un peu bizarre au début de demander de mettre 1 en sortie sur une entrée.

Une autre façon de voir est quand on voit les anciens programmes du temps ou INPUT_PULLUP n'existait pas encore. Dans le setup on voyait:

pinMode(2, INPUT); // Broche en entrée
digitalWrite(2, HIGH); // Activation du pullup

Ce qui fonctionne d'ailleurs encore.
 


 

Une autre chose amusante quand on travaille avec les registres:

14.2.2 Toggling the Pin
Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of DDRxn. Note that the SBI instruction can be used to toggle one single bit in a port.

On peut donc changer une valeur en sortie. Blink devient:

 void setup() {
  DDRD |= B00000100; // Sélection de D2 dans le registre DDRD
  //PORTD |= B00000100; // Met D2 à HIGH - l'instruction n'est pas nécessaire
}
void loop() {
  PIND |= B00000100; // Change D2
  delay(500);
}

En utilisant un peu d'assembleur, on fait clignoter la led 13 d'une uno par:

void setup()
{
  asm("sbi 4,5\n");
}

void loop()
{
  asm("sbi 3,5\n");
  delay(500);
}

Ca à l'avantage de ne pas être compréhensible!

Merci @vileroi disons que c'est l'usine à gaz mais j'ai compris.
Par contre le langage C de bas niveau ne permet que de manipuler les registres DDR PORT et PIN ?

Merci c'est sympa de m'avoir répondu.
Bonne journée.

C’est un peu spécial, il faut effectivemet ecrire un "1" alors que la pin est une entrée.
Les avr ne sont que des micros 8 bits avec peu de memoire il faut que leur concepteur soit astucieux.

Comme toujours il faut lire les datasheets et celle d’Atmel ont l’avantage d’etre accompagnées de schémas de principe clairs et d’exemples assembleur et C.

L’action de mettre des pull up est décorrélée du mode entrée ou sortie.
Rien n’empêche d’actionner les pull up en mode mesure analogique (atmega 328p port C)
Les fonctions arduino ne le permettent probablement pas, avec les registres c’est possible.

Avec les registres il est possible de faire des mesures analogiques a un rythme plus élevé, de faire de la PWM a unefrequence plus élevée, etc .

Merci @68tjs,
Mais du coup en C de bas niveau, on ne peut manipuler que les registres qui gèrent les pins de l'ATmega328p ?

Si je peux me permettre, même si cela ne répond pas au sujet de ton post, ce n'est pas vraiment du langage C bas niveau à la différence d'utiliser de l'assembleur dans ton code C.
Tu cour-circuite simplement le framework Arduino, mais le langage reste le même.
En gros comme dans n'importe quel IDE, tu peux utiliser tout ce que te permet le langage, qui lui dépend du compilateur que tu utilise.

Il doit y avoir des cas ou cela peut être intéressant de by-passer la surcouche Arduino, mais en générale mieux vaut reste avec la surcouche et ne pas mélanger les genres.
Si vraiment tu veux explorer cette voie, le mieux serait de ne pas utiliser la surcouche Arduino et de programmer directement on micro-controller avec la chaine de compilation Atmel ou carrément le Atmel studio.
Tu peux accéder normalement à tout ce qui est donnée dans cette documentation, il me semblais qu'il y avait une autre plus simple, mais je ne l'a retrouve pas.

Non tu peux gérer tout le micro-contrôleur et tout les registres de celui-ci.

Merci pour ta réponse @terwal.
Je m'intéresse au bas niveau car ça me permet de mieux comprendre le fonctionnement d'un microcontrôleur et c'est important pour moi (j'en ferai de même pour les processeurs X86 mais ce n'est pas le sujet ici) Maintenant je prends note du fait qu'il ne faut pas mélanger les genres
J'ai téléchargé Microchip Studio, je l'installerai plus tard.
Merci
bonne journée.

Oui, c'est ce que j'avais compris et tu fais bien, cela ne peut t'être que bénéfique.
Après au contraire, cela te permettra de pouvoir mélanger les genres en connaissances de cause.
Comme par exemple écrire de l'assembleur dans ton code C, parce que tu sais que le compilateur ne fait pas ce que tu veux.
Mais d'une façon générale lorsque l'on ne veut justement ne pas descendre dans les différentes couches de classes ou de Framework, on reste sur une seul couche.

C'est bien noté.

Merci.

Avec un pointeur on peut lire et écrire partout.
Les PINx, DDRx, etc ne sont que des pointeurs définis dans des include des librairies d'avr-gcc. Tu devrais les trouver dans le répertoire arduino15.
Il y a un fichier ioxxxx.h pour chaque processeur par exemple pour l'ATmega328p c'est iom328p.h.
Dans le même répertoire voir aussi io.h et sfr_defs.h pour voir comment tout ça fonctionne.

Merci @fdufnews
C'est vraiment très intéressant.

Bonne journée.

Il n'y a pas que la couche arduino, avant il y a la couche Atmel.
Regardes dans l'IDE Arduino : Arduino intègre des pans entiers du travail d'Atmel qui n'est pas en C++ mais en C.

Dans le répertoire : arduino-1.8.19/hardware/tools/avr/avr/include/avr
Il y a le fichier iom328p.h écrit par Atmel
Dans lequel tu peuxlire des choses comme celle ci :

#define PINB _SFR_IO8(0x03)
#define PINB0 0
#define PINB1 1
#define PINB2 2
#define PINB3 3
#define PINB4 4
#define PINB5 5
#define PINB6 6
#define PINB7 7


#define DDRB _SFR_IO8(0x04)
#define DDB0 0
#define DDB1 1
#define DDB2 2
#define DDB3 3
#define DDB4 4
#define DDB5 5
#define DDB6 6
#define DDB7 7

#define PORTB _SFR_IO8(0x05)
#define PORTB0 0
#define PORTB1 1
#define PORTB2 2
#define PORTB3 3
#define PORTB4 4
#define PORTB5 5
#define PORTB6 6
#define PORTB7 7

Ce n'est qu'un extrait, le fichier fait 950 lignes.

Merci @68tjs c'est l'objet du post #10 de @fdufnews sauf que lui désigne iom328p.h du répertoire arduino15. Par contre, j'apprends que les fichiers sont écrits par Atmel.
Merci à vous deux pour l'info.

Je regrette d'avoir négligé mes cours d'anglais dans ma jeunesse.
Les documentations d'Atmel sur l'ATmega328p sont en PDF et en anglais !
Je ne peux pas traduire. Mon apprentissage est très lourdement ralenti ...

J'ai un message pour les jeunes :
Il ne faut pas négliger vos cours d'anglais, c'est une langue indispensable !!!!!!!!!!!

Google translate fait ça très bien. Il suffit de coller les paragraphes à traduire.
On ne peut pas faire traduire un doc de cette taille directement.

C'est ce que je fais, c'est long et fastidieux mais je n'ai pas le choix. Je paye cash mes erreurs de jeunesse...
Dans la vie on a ce qu'on mérite même si parfois le hasard intervient. J'aurai du bosser mes cours d'anglais.

Solution pour les nuls en anglais (comme moi) :

Aller à :

Cliquer sur l'onglet Document
et c'est parti.

Si le fichier est trop gros, utiliser un logiciel de découpage de pdf.

Exemple de document original et de document traduit :
VL53L1X-api-user_manuel.pdf (911,5 Ko)
VL53L1X-api-user_manuel_FR.pdf (1,3 Mo)

Les atmega se programmaient bien avant qu' Hernando Barragán n'écrive l'IDE Wiring qui a été copiée par Arduino.
https://arduinohistory.github.io/

Malheureusement l'informatique et encore plus la programmation est dominé par la langue anglaise.

Il y a surtout qu'avec la mondialisation, la documentation est devenue un centre de coût important.
Dans l'exemple que j'ai joint, il ne s'agit pas de programmation, il s'agit de mode d'emploi.
S'il y a des notions de programmation, elles sont anecdotiques.

Il est plus simple de généraliser l'anglais qui, sauf pour certains gaulois réfractaires dont je fais partie, est la langue la plus simple à apprendre et qui est bien répandue grâce à l'empire colonial britannique.

Comme pour le foot qui a des règles ultra-simples par rapport au rugby.
[] ok, je sors.

Avant de sortir, laissez moi vous remercier car le mode d'emploi est parfait...
A la mi-temps du match j'ai pu traduire ma documentation...
Merci mille fois @68tjs
Très Bonne soirée à vous

PS : j'aime tous les sports y compris le rugby :rugby_football: peut-être même plus que le foot car les engagements sont très vigoureux. Un match de rugby c'est un combat du début à la fin ...