Langage C sur ARDUINO

Bonjour à tous
Je souhaite programmer en C sur arduino, je trouve des tutoriels par millier sur le C , mais strictement rien sur les code à utiliser pour manipuler les registres du contrôleur de l'Arduino ( ici un Atmega 328p) , par exemple comment définir les ports en sortie en entrée, comment accéder à un bit particulier d'un registre.
De façon sporadique je trouve ici et la des infios mais aucun document de référence .....
J'ai trouvé dan AVR_LIBC des info mais j'y comprends rien, exemple

#define
stdout (__iob[1]) ca veut dire quoi

Alors si quelqu'un peut m'aiguiller je lui serais très reconnaissant

je trouve des tutoriels par millier sur le C , mais strictement rien sur les code à utiliser pour manipuler les registres du contrôleur de l'Arduino

Très étonnant !

La data sheet des microcontroleurs Atmel fourmille d'exemple en assembleur et en C justement.
J'ai toujours pensé que pour un produit électronique, un microcontroleur en est un, la datasheet est le premier document à consulter.

Merci pour ta réponse
Je connais très bien la programmation en assembleur sur les PIC (dsPic4013 par ex) ,les datasheet sont excellentes , ainsi que la doc sur l’assembleur , c’est bien simple on ne peut pas faire mieux ,chaque instruction fait l’objet de plusieurs exemples en mode 8 bit puis en 16 bit ,très précieux en cas de doute sur une instruction…Bon faire des gros calcul en 32bit ,des fonctions alambiquées en assembleur c’est passionnant un certain temps , alors le C serait mieux enfin , car le langage ARDUINO me fait perdre la compréhension que j’avais en assembleur ,je savais exactement tous ce que se passait à quelques nanoseconde prés.
Donc un peu dérouté pour passer en C sur Arduino , les exemples que tu cites précédemment ne m’éclairent pas beaucoup…par exemple l’affectation de valeur à un port, je comprends
PORTB = 0x10010100 facile mais les décalages (? PBx ) sont un peu mystérieux et compliqué
Avant de refaire ce poste j’ai tenté toujours sans succès de trouver les syntaxes , pas de source ,ca reste nébuleux…
Alors si quelqu’un peur éclairer ma lanterne pour suivre le CAS DU C…il me guérirait bien de mon tourment.!!!

hello
ici, toute la page

J’ai des fichiers qui m’ont pas mal intéressés et qui sont en pièces jointes.

Maintenant j’ai programmé il y a longtemps en assembleur car je dépassais la capacité mémoire, et l’assembleur m’a permis de faire rentrer le code dans un AVR. Maintenant c’était pour des élèves qui devaient modifier le code
et contrairement à ce que tout le mode affirme, l’assembleur bien écrit était plus facile à comprendre et à modifier que le C. On avait un assembler/compilateurC.

Pour moi, Arduino est fait pour écrire en C++, mas en Asm. On peut bien entendu y mettre des routines en assembleur, je n’ai utilisé que es fonctions inline assembleur.

La limite entre le C++ et les extensions d’Arduino et l’assembleur est assez floue. Quand on écrit _NOP, cli(), PORTB=B00001111; c’est du C, ou de l’assembleur?

Maintenant si tu as un problème particulier, dire “j’arrive pas à faire tout un tas de trucs” ne permet pas une aide. PBx n’est pas assez précis.

AVR Assembler.pdf (312 KB)

Micro Atmel-0856-AVR-Instruction-Set-Manual.pdf (1.41 MB)

Mix Asm et C.pdf (94.3 KB)

Dernier fichier qui ne passait pas dans le post précédent.

Micro AVR assembleur (assembleur et directives).pdf (489 KB)

ne m'éclairent pas beaucoup....par exemple l'affectation de valeur à un port, je comprends
PORTB = 0x10010100 facile mais les décalages (? PBx ) sont un peu mystérieux et compliqué
Avant de refaire ce poste j'ai tenté toujours sans succès de trouver les syntaxes , pas de source ,ca reste nébuleux.....
Alors si quelqu'un peur éclairer ma lanterne pour suivre le CAS DU C....il me guérirait bien de mon tourment.!!!!

Programmer en arduino avec les fonctions WIRING/Arduino est une chose.
Programmer sans passer par ces fonctions en est une autre.

La bible reste la datasheet où tu trouvera les adresses des registres par exemple PORTB possède une adresse et on peut accéder à son contenu par cette adresse .

Les fonction Wiring/Arduino masque à l'utilisateur tout ce travail. La principale préoccupation de leur auteurs n'est pas l'optimisation, qui n'est pas terrible, mais la facilité d'emploi.

Si tu veux l'efficacité il ne reste que la datasheet . Il y a aussi le forum "avrfreaks" maintenu par Atmel.
https://www.avrfreaks.net/
Attention sur ce forum majoritairement fréquenté par des professionnels il n'est pas trop recommandé de parler Arduino :grin:
Perso moi qui ne suis pas un professionnel de la programmation je ne peux pas en dire plus.

vileroi:
Quand on écrit PORTB=B00001111; c'est du C, ou de l'assembleur?

Ni l'un ni l'autre :grin:

La notation "B00011001" c'est "un truc arduino"

En C ou C++ on écrit PORTB = 0b00011001 tout simplement en notation binaire, pourquoi ajouter des intermédiaires inutiles ?

C'est équivalent à écrire

uint8_t truc _muche = 0b00011001

Les adresses des registres comme PORTB sont dans la datasheet mais "le terme PORTB" est défini dans le fichier arduino-1.8.13/hardware/tools/avr/avr/include/avr/iom328p.h pour l'ATMEGA328p.

#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

Dans la datasheet on trouve :

C'est un peu petit mais c'est extrait du §36 "Registre summary" page 612 de mon édition Rev. 8271A – 12/09

Si l'on prend comme définition pour l'assembleur:
"Un langage d'assemblage ou langage assembleur est, en programmation informatique, le langage de plus bas niveau qui représente le langage machine sous une forme lisible par un humain."
"Langage de programmation, spécifique d'un ordinateur ou d'une gamme d'ordinateurs, qui utilise des instructions symboliques représentant des suites d'instructions en langage machine."
"− Emploi subst. Un assembleur. ,,Programme écrit spécialement pour un ordinateur déterminé et qui permet de traduire les instructions mnémoniques en instructions numériques et aussi de les ordonner`` (Guilh. 1969).
− Emploi adj. Langage assembleur. Instruction mnémonique pour écrire ce programme."

Pour moi _NOP, cli(), PORTB=0b00001111; correspond bien à la définition, du langage assembleur.

Je n'entrerai pas dans cette discussion.

Je completerai juste mes propos :
Une variable dans un programme a une adresse en mémoire qui est définie au moment de la compilation.
Cette variable est placée en flash à l'endroit choisi par le compilateur.

Un registre c'est une variable dont l'adresse est fixe afin qu'elle soit accessible par les modules électroniques du microcontrôleur (UART, SPI, I2C, Timers, etc...).
Son emplacement a été imposée par un homme, le concepteur du microcontroleur.
Cette adresse est dans une zone de la flash où le compilateur n'a pas le droit d'écrire.

J'ai du mal à voir ce que l'assembleur vient faire dans ce sujet, mais bon ce n'est pas mon domaine de compétence donc je n'entrerai pas dans une discussion interminable dans un domaine de compétence qui n'est pas le mien.

Une variable dans un programme a une adresse en mémoire qui est définie au moment de la compilation.
Cette variable est placée en flash à l'endroit choisi par le compilateur.

Non, en général placée en RAM en général à l'endroit choisi par le compilateur.
Toutes mes variables sont en RAM, mais certains utilisent ce type de variable:

int bouton = 2;
...
pinMode(bouton, OUTPUT)

bouton est une variable (définie comme telle), mais le compilateur la met comme une constante dans le programme. Elle est donc en flash.
Si une variable n'est jamais lue, il est possible que le compilateur la range dans les oubliettes.
En général c'est le compilateur qui choisit la place de la variable (une vraie, qui est modifiée et lue), mais cela m'est arrivée que je choisisse sa place:

uint8_t &maVariable(monAutreVariable);

Ci-dessus j'ai choisi comme place de la variable celle de monAutreVariable.
Et quand on définit un pointeur, c'est définir une variable ou l'on choisira sa place à l'eécution.

Un registre c'est une variable dont l'adresse est fixe afin qu'elle soit accessible par les modules électroniques du microcontrôleur (UART, SPI, I2C, Timers, etc...).
Son emplacement a été imposée par un homme, le concepteur du microcontroleur.
Cette adresse est dans une zone de la flash où le compilateur n'a pas le droit d'écrire.

Pas en flash non plus, c'est de la RAM ou dans un espace Input/output ou les deux suivant le micro. L'espace IO est un espace RAM, mais accessible avec des instructions assembleur à part, différentes des instructions d'accès à la mémoire "normale"

Bonjour

Cet article répond peut être à la demande initiale avec un petit chapître sur la gestion des registres (en utilisant les en-têtes GCC):

https://fr.wikiversity.org/wiki/Micro_contr%C3%B4leurs_AVR/Le_langage_C_pour_AVR

J’apprécie aussi.

Hello tout le monde, super !!

Merci pour toutes ses réponses que je vais approfondir....bien que le sujet initial a un peu dérivé !
La tentative de passer d'un dsPIC4013 16bit programmé en assembleur à un arduino avec l'IDE d'ARDUINO, est comme passer du labour à la cuillere au machine agricole de maintenant
Sur le forum je vois beaucoup de questions posées qui ont (pour moi) comme origine le coté nébuleux d'une fonction on ne sait pas comment elle agit, donc on s'étonne que dans certaines circonstances il y a des conflits qui plantent le programme ou qui donnent un résultat innatendu.
Par exemple il me parait difficile de savoir ce qui ce passe avec plusieurs interruptions ,matérielles ou logiciel (mon mardi)...en programmation en langage ARDUINO, c'est pourquoi je souhaite mixer du C et de l'ARDUINO, voire de l'assembleur pour savoir ce qui se passe.....et aussi maitriser le temps....ARDUINO est super facile en apparence d'utilisation, le revers de la médaille c'est qu'on ne maitrise plus grand chose, enfin c'est mon sentiment de néophyte (frite?)
Merci encore pour toutes ces réponses......

pour savoir ce qui se passe 'intimement' il est toujours possible de désassembler le fichier HEX (ou mieux ELF) issu de la compilation

Par ailleurs le code source des fonctions Wiring (reprises par Arduino) est accessible.

Opinion d’un vieux développeur C en embarqué : ce n’est pas parce que l’on écrit en assembleur que l’on maîtrise mieux ce qui se passe.
Ecrire en assembleur revient souvent à développer des fonctions void func(void) avec des tas de variables globales, ce qui n’améliore ni la lisibilité ni la modularité.
Avancer des arguments du style compacité du code ne tient pas non plus, car un compilateur C générera du meilleur code machine que n’importe quel développeur en assembleur, sauf surdoué.

Enfin le langage C est portable, ce qui n’est pas le cas de l’assembleur, forcément lié à une famille de processeurs.
J’ai personnellement porté sur MSP430 les “kernel timers” de Linux, et plus récemment sur AVR.
A part le gestion des timers hardware le code est strictement identique dans les deux cas.

Hola on ne s'affole pas....
Une question simple au début dont je pensais que la réponse aurait été immédiate , jusqu'à toutes ces considérations....
Alors non qu'importe qu'il soit portable ou , qu'il soit plus ci ou mi , non je bidouille je veux comprendre et écrire un programme de blaireau en C , pas pour la Nasa ,sur mon PC AZUS....
Pour certaine appli le temps est important le recours à une partie en C ou en assembleur est nécessaire, par exemple pour positionne un bit en sortie peut etre très utile!!
un digitalwrite est 100 fois plus lent que l'affectation direct du PORT (63.5ns)...et voili

Merci pour ce débat(d'Helene)....

hbachetti:
un compilateur C générera du meilleur code machine que n'importe quel développeur en assembleur, sauf surdoué.

je confirme.
Le langage C a été conçu pour développer des systèmes d'exploitation, donc pour être proche de la machine. Il permet d'accéder facilement à la mémoire (RAM ou registres hardware mappés)

un digitalwrite est 100 fois plus lent que l'affectation direct du PORT (63.5ns)...et voili

On peut le faire aussi en C, sans digitalwrite().

ATOS_VQ:
un digitalwrite est 100 fois plus lent que l'affectation direct du PORT (63.5ns)...et voili

Rien ne t'empêche d'affecter directement ton port ... en C.

*port = valeur;

que le compilateur traduira en assembleur de façon très efficace.
Tu te fais des noeuds avec l'utilisation d'une fonction, qui est plus lente non pas parce que c'est du C, mais parce qu'elle fait des vérifications à tours de bras (j'imagine). Pas une question de langage.

Je crois qu’il faut expliquer en détail. Voici un code qui fait clignoter la LED sur D13 :

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  DDRB = 0b00100000;
}

void loop() {
  PORTB |= 0b00100000;
  delay(500);                       // wait for a second
  PORTB &= ~0b00100000;
  delay(500);                       // wait for a second
}

Si l’on demande le listing assembleur du .elf généré:

avr-objdump -S DirectPort.ino.elf > list.txt

On obtient ceci :

void loop() {
  PORTB |= 0b00100000;
 26c: 2d 9a       sbi 0x05, 5 ; 5
  delay(500);                       // wait for a second
 26e: 0e 94 6d 00 call 0xda ; 0xda <delay.constprop.0>
  PORTB &= ~0b00100000;
 272: 2d 98       cbi 0x05, 5 ; 5
  delay(500);                       // wait for a second
 274: 0e 94 6d 00 call 0xda ; 0xda <delay.constprop.0>
 278: 20 97       sbiw r28, 0x00 ; 0
 27a: c1 f3       breq .-16     ; 0x26c <main+0x8e>
 27c: 0e 94 00 00 call 0 ; 0x0 <__vectors>
 280: f5 cf       rjmp .-22     ; 0x26c <main+0x8e>

Le compilateur traduit la ligne PORTB |= 0b00100000; par une seule instruction assembleur sbi (comme l’aurait deviné d’ailleurs n’importe quel développeur C confirmé).

Ceci dit il faut tout de même aller visiter la datasheet de l’ATMEGA328P pur savoir que l’adresse du registre DATA du PORTB est 0x05, et la doc ARDUINO pour savoir que la LED de la NANO est sur PB5.

A mon humble avis, il est plus simple d’écrire digitalWrite(13, HIGH);

Et il existe une version digitalWriteFast().

Ce n’est qu’une question de métier, et de connaissances …