Tu oublies le FPGA et tu regardes ce qu'on trouve dans un µC, par exemple pour la famille 16 bit -> http://ww1.microchip.com/downloads/e.../30010109D.pdf
-----
Tu oublies le FPGA et tu regardes ce qu'on trouve dans un µC, par exemple pour la famille 16 bit -> http://ww1.microchip.com/downloads/e.../30010109D.pdf
A priori, l'uart n'a rien à voir avec les FPGA, sauf à vouloir en reconstituer un.
L'uart est simplement le module permettant à un microprocesseur / microcontrôleur de communiquer en liaison série asynchrone.
Tous les microcontrôleurs ou presque en implémentent au moins un de manière matérielle.
Dernière modification par Jack ; 27/02/2018 à 12h34.
C'est bien ce que j'avais compris.
(NB : je passe sur le fait qu'il n'existe pas un, mais plusieurs standards pour le langage C.)
Le problème, c'est que cette dissociation n'a pas beaucoup de sens dans le cas des petits micro-contrôleurs. Ces circuits ne disposant pas des ressources suffisantes pour implémenter la totalité d'un C standard, on est contraint de faire des choix entre :
- supprimer certaines fonctions standards,
- amputer les fonctions standards de certaines des possibilités normalement prévues,
- garder toutes les fonctions standards complètes mais ne pas pouvoir les utiliser dans la grande majorité des programmes à réaliser.
Par ailleurs, afin d'accéder à certains aspects de très bas niveau que le langage ne prend pas en charge, quelques extensions doivent être ajoutées de base au langage.
Au bout du compte, même s'il est originellement bâti sur un standard, pour être un tant soit peu utilisable un langage C destiné à de petits micro-contrôleurs ne peut plus être 100% conforme à ce standard.
Pour les micro-contrôleurs AVR, le C/C++ utilisé par Arduino (AVR-GCC) découle bien d'un standard, mais est affecté d'un certain nombre de compromis pour les raisons que je viens d'évoquer. Notamment :
- les fonctions print et scanf existent, mais ne sont connectées par défaut à aucun périphérique (s'il le souhaite, le développeur peut réaliser ce type de connexion « à la main » dans son programme), et ont été largement amputées au niveau des conversions de formats,
- en l'absence d'un système de fichiers, les fonctions relatives à l'accès aux fichiers (fopen, fread, ...) n'ont pas été implémentées,
- aucun traitement des nombres flottants en double précision n'a été prévu (le type double est équivalent au type float).
(De plus, les versions du compilateur et des bibliothèques de base qui sont fournies commencent un peu à dater.)
Quel que soit la chaîne de compilation choisie, il y aura forcément des compromis dès lors que sa cible sera un petit micro-contrôleur.
Si c'est la mise en œuvre des petits micro-contrôleurs qui t'intéresse, tu pourrais par exemple commencer par utiliser l'IDE Arduino juste pour te faire la main sur du C/C++ (pratiquement standard quoi qu'on en dise) tout en gardant à l'esprit les limitations que j'ai évoquées, puis passer à des outils professionnels plus sérieux et plus complets, qui présentent d'autres compromis (et donc des limitations différentes) adaptés à tes besoins spécifiques.
Si ton but est plutôt de mettre à fond en pratique la programmation en C/C++, une plus grosse machine dotée d'un système d'exploitation (PC ou carte à « gros » micro-contrôleur) serait toute indiquée pour pouvoir en expérimenter tous les aspects.
Cela dit, rien ne t'interdit de faire les deux en même temps, mais séparément.
Dernière modification par PA5CAL ; 27/02/2018 à 14h00.
(Pour être plus précis, concernant fread, la fonction n'est pas implémentée pour lire des fichiers, mais existe néanmoins pour lire l'entrée standard qui pourrait être créée par programme.)
Il me semble que c'est conforme au standard C-ANSI, qui ne précise pas la taille des types (seulement une taille minimale, et le fait qu'un double ne peut pas être moins précis qu'un float)- aucun traitement des nombres flottants en double précision n'a été prévu (le type double est équivalent au type float).
On peut ajouter que la gestion dynamique de la mémoire a généralement disparu également lorsque les ressources en RAM sont faibles.
Bonjour,
Merci beaucoup pour toutes vos réponses et vos apports apport en connaissance!
Dans ces cas là, quelqu'un pourrait m'indiquer ce que je dois faire si je veux alors une partie d'écriture avec un int main en corps principal par exemple au lieu d'un loop?
Merci!
Même si je sais que de base c'est déjà inclut dans le void loop. Mais j'aimerais au moins avoir ce bout de syntaxe la ^^
Je ne comprends pas ce que tu entends par là. De ton côté, le point d'entré reste le "main", avec la particularité que c'est le seul du système et qu'il ne sort jamais. La chaîne de compilation à fait le boulot de remplir ce qui se passe avant le main.
(ceci n'est vrai qu'en "bare-metal", pas avec un "vrai" OS comme GNU/Linux)
Je vais passer sous atmel studio, ajouter les librairies dont j'ai besoin et faire ma petite vie, je crois que c'est la seule chose répondant à ma question au final x)
Je ne comprends pas ce que tu entends par là. De ton côté, le point d'entré reste le "main", avec la particularité que c'est le seul du système et qu'il ne sort jamais. La chaîne de compilation à fait le boulot de remplir ce qui se passe avant le main.
(ceci n'est vrai qu'en "bare-metal", pas avec un "vrai" OS comme GNU/Linux)
Hm.. Que je veux pas de la surcouche pour débutant :/
Bonsoir,
Je suis content, après pas mal temps passer sur la config, j'ai réussi à programmer en C avec mon arduino sur Atmel studio, même si ça a pas été facile.
Merci a tous pour votre aide !![]()
Dernière modification par PA5CAL ; 28/02/2018 à 21h05.
Oui, c'est vrai, je me souviens ! Excuse moi
Par contre, j'aurais un autre tout petit problème.
J'ai fais quelques tests, allumage d'une led, chenillard etc tout c'est bien passé. Mais maintenant que je mets un bouton en entrée, cela ne veut pas fonctionner.
J'ai mis la LED sur le PORTD (registre DDRD) et le bouton sur le PORTB (registre DDRB) pour bien séparer mais rien ne fonctionne. J'ai ensuite remplacer le bouton direct par le 5V mais rien non plus, je n'arrive pas à lire la valeur de la pin 8 (donc ici PINB0). J'ai l'impression que c'est une erreur de synthaxe mais je comprends vraiment pas .. J'ai pourtant tout essayé en terme de synthaxe (PINB0, PB0, DDRB0 ...) mais rien y fait ..
Voici la pin mapping de la carte : https://www.arduino.cc/en/Hacking/PinMapping168
Le code est envoyé en pièce jointe.
Dans l'attente d'une réponse, merci d'avance
***
Dernière modification par Antoane ; 01/03/2018 à 06h37. Motif: Suppression lien vers PJ externe
Si l'on note x la lettre désignant un port d'entrée-sortie :
• chaque bit des registres suivants correspond à une broche d'entrée-sortie
• le registre DDRx définit pour chaque broche si celle-ci est configurée en entrée (bit à 0) ou en sortie (bit à 1)
• le registre PORTx définit :
- - pour chaque broche configurée en sortie, si celle-ci est au niveau bas (bit à 0) ou au niveau haut (bit à 1) lorsqu'elle n'est pas déjà utilisée par une fonction interne
- - pour chaque broche configurée en entrée, si sa résistance de rappel interne (« pull up ») est désactivée (bit à 0) ou activée (bit à 1) lorsque le bit PUD (désactivation générale des résistances de rappel) est à 0 (valeur par défaut)
• le registre PINx permet :
- - de lire le niveau logique sur chaque broche (avec un léger retard dû à la synchronisation)
- - lorsqu'on écrit un bit à 1, de changer l'état du bit correspondant du registre PORTx (« toggle »)
(source : datasheet de l'ATmega328P)
Voici ce que pourrait donner ton programme, avec le bouton sur le port D et la led sur le port B (correspondant à la configuration d'une carte Arduino Uno, Nano ou Mini) :
Code:// Sur l'Arduino Uno/Nano/Mini : // PD2 = pin #3 // PB5 = pin #13 + led L DDRB = 0x20; // pin PB5 configurée en sortie pour la led DDRD = 0x00; // pin PD2 configurée en entrée // (inutile car valeur par défaut) PORTD = 0x04; // activation de la résistance de rappel sur PD2 // brancher le bouton entre l'entrée PD2 et GND // bouton appuyé = 0, bouton relâché = 1 while (1) { if (PIND & 0x04) // test de la pin PD2 { // bouton relâché --> led éteinte PORTB = 0x00; } else { // bouton appuyé --> led L clignotante PORTB = 0x20; _delay_ms(100); PORTB = 0x00; _delay_ms(100); } }
En toute rigueur, lorsqu'on écrit un bit dans un registre, il ne faudrait pas modifier les autres bits qui correspondent à d'autres entrées-sorties.
Par ailleurs, pour rendre le code plus lisible, on peut expliciter les valeurs à écrire dans les registres à partir du nom des bits (définis dans le fichier avr/iom328p.h pour l'ATmega328P). La calculs qui apparaissent dans le code sont supprimés par l'optimisation du compilateur.
Code:DDRB |= 1<<DDB5; // pin PB5 configurée en sortie pour la led DDRD &= ~(1<<DDD2); // pin PD2 configurée en entrée PORTD |= 1<<PORTD2; // activation de la résistance de rappel sur PD2 while (1) { if (PIND & (1<<PIND2)) // test de la pin PD2 { // bouton relâché --> led éteinte PORTB &= ~(1<<PORTB5); } else { // bouton appuyé --> led L clignotante PORTB |= (1<<PORTB5); _delay_ms(100); PORTB &= ~(1<<PORTB5); _delay_ms(100); } }
Dernière modification par PA5CAL ; 01/03/2018 à 09h08.
Oups ! Dans ce qui précède, il faut lire :
PD2 = pin #2
Pour simplifier la gestion des bits, il faudrait pouvoir écrire des trucs du genre DDRB_5 = 1; à la place de DDRB |= 1<<DDB5;
Ca doit sûrement se trouver dans un fichier d'entête (je ne connais pas ATMEL).
Sinon, ça se fait à l'aide de bitfield.
De plus, afin de gérer le port au niveau bit, groupe de bits, de l'octet ou éventuellement du mot de 16 bits, on peut grouper le tout dans une union.
Atmel fournit juste les définitions que j'ai utilisées ci-dessus.
Afin d'alléger l'écriture, la chaîne AVR-GCC fournit des redéfinitions de constantes comme par exemple PC5 pour remplacer PORTC5, ou encore des constantes génériques comme DD2 pour remplacer DDB2, DDC2 ou DDD2.
Mais s'agissant de la désignation des bits, il n'est proposé que la macro _BV() qui fait la même opération que ci-dessus tout en ayant l'inconvénient d'être moins explicite :
Code:#define _BV(bit) (1 << (bit))
Concernant les bitfields, leur utilisation soulève un problème de portabilité (rien ne garantit que tous les compilateurs utilisés rangent les bits de la même manière), et provoque le risque de ne pas profiter de certaines optimisations du code machine.
Le rangement des bits dans un bitfield peut également dépendre d'options de compilation, qu'on peut généralement forcer à l'aide de directives #pragma. Malheureusement ces dernières sont souvent spécifiques à la chaîne de compilation utilisée.
Quoi qu'il en soit, Atmel se limite à fournir un fichier de définition passe-partout, en C standard, qui ne facilite pas beaucoup le travail du développeur.
D'autre part, le code généré lorsqu'on utilise les bitfields avec AVR-GCC n'est clairement pas optimisé.
Par exemple, le code machine généré par :réclame 7 mots de mémoire programme et 9 cycles machine et utilise 4 registres généraux :Code:typedef struct { char b0 : 1; char b1 : 1; char b2 : 1; char b3 : 1; char b4 : 1; char b5 : 1; char b6 : 1; char b7 : 1; } bits; ... ((bits *)PORTC)->b5 = 1;Code:in r30,0x08 ldi r31,0x00 ld r24,Z ldi r25,0xFF bst r25,0 bld r24,5 st Z,r24
En comparaison, si l'on crée sa propre définition des masques de bit, le code :ne réclame plus que 1 mot de mémoire programme et 2 cycles machine et n'utilise aucun registre général :Code:const char b0 = 1<<0; const char b1 = 1<<1; const char b2 = 1<<2; const char b3 = 1<<3; const char b4 = 1<<4; const char b5 = 1<<5; const char b6 = 1<<6; const char b7 = 1<<7; ... PORTC |= b5;(NB: l'utilisation d'une constante au lieu d'un #define évite d'éventuelles modifications inappropriées du code par le préprocesseur, et au final ne consomme pas de mémoire supplémentaire du fait de l'optimisation réalisée).Code:sbi 0x08,5
La définition des masques de bit est aussi plus intéressante lorsqu'il s'agit de positionner plusieurs bits simultanément. Le code :ne réclame que 3 mots de mémoire programme et 3 cycles machine et n'utilise qu'un registre général :Code:const char b0 = 1<<0; const char b1 = 1<<1; const char b2 = 1<<2; const char b3 = 1<<3; const char b4 = 1<<4; const char b5 = 1<<5; const char b6 = 1<<6; const char b7 = 1<<7; ... PORTC |= b7 | b4 | b1 | b0;Code:in r24,0x08 ori r24,0x93 out 0x08,r24
Dernière modification par PA5CAL ; 01/03/2018 à 14h17.
C'est très clair.
Du coup, je viens de tester avec un petit µC freescale (j'ai remis la main sur une vielle machine virtuelle XP car je n'ai plus touché depuis des lustres à ces petites choses). Voici le code généré dans un fichier listing pour mettre un bit à 1:Peut-être est-ce dû au fait que les 9S08 soient des cisc, mais on ne peut faire mieux comme résultat.Code:43: PTAD_PTAD0 = 1; 0006 1000 [5] BSET 0,_PTAD
Un aperçu du fichier d'entête pour le port D en question:
Petite remarque en passant: c'est du C. En C++, l'usage des unions anonymes simplifierait un peu les choses, mais c'est un détail syntaxique.Code:/**************** registers I/O map ****************/ /*** PTAD - Port A Data Register; 0x00000000 ***/ typedef union { byte Byte; struct { byte PTAD0 :1; /* Port A Data Register Bit 0 */ byte PTAD1 :1; /* Port A Data Register Bit 1 */ byte PTAD2 :1; /* Port A Data Register Bit 2 */ byte PTAD3 :1; /* Port A Data Register Bit 3 */ byte PTAD4 :1; /* Port A Data Register Bit 4 */ byte PTAD5 :1; /* Port A Data Register Bit 5 */ byte :1; byte :1; } Bits; struct { byte grpPTAD :6; byte :1; byte :1; } MergedBits; } PTADSTR; extern volatile PTADSTR _PTAD @0x00000000; #define PTAD _PTAD.Byte #define PTAD_PTAD0 _PTAD.Bits.PTAD0 #define PTAD_PTAD1 _PTAD.Bits.PTAD1 #define PTAD_PTAD2 _PTAD.Bits.PTAD2 #define PTAD_PTAD3 _PTAD.Bits.PTAD3 #define PTAD_PTAD4 _PTAD.Bits.PTAD4 #define PTAD_PTAD5 _PTAD.Bits.PTAD5 #define PTAD_PTAD _PTAD.MergedBits.grpPTAD #define PTAD_PTAD0_MASK 1 #define PTAD_PTAD1_MASK 2 #define PTAD_PTAD2_MASK 4 #define PTAD_PTAD3_MASK 8 #define PTAD_PTAD4_MASK 16 #define PTAD_PTAD5_MASK 32 #define PTAD_PTAD_MASK 63 #define PTAD_PTAD_BITNUM 0
Bonjour, aucun des deux programme ne fonctionne.
A la limite le 2ème lors du téléversement, mais après plus rien.
Je comprends pas, moi j'ai fais exactement l'inverse dans la déclaration des entrées sorties et ça fonctionnait. Je pensais que 0 c'était pour mettre en sortie et 1 pour mettre en entrée
Au final le code fonctionne bien. Excusez moi
Donc je ne comprends rien du tout. Chaque uC à une logique propre à lui ? Je pensais que c'était standard les I/O
Car voici mon code qui est fait à l'inverse du tient et qui fonctionne ..
Le début d'une résistance est branchée sur une sortie digitale et la sortie de la led sur le GND. Et cela pour les 4 "mini-circuits"int main(void)
{
/* Replace with your application code */
DDRD=0xC3;
while (1)
{
PORTD=0x3C;
_delay_ms(1000);
PORTD=0x1C;
_delay_ms(1000);
PORTD=0x0C;
_delay_ms(1000);
PORTD=0x04;
_delay_ms(1000);
}
}
Globalement, les micro-contrôleurs partagent les mêmes principes, mais ils peuvent présenter certaines particularités propres au constructeur ou à la gamme de circuit.
Quoi qu'il en soit, la première chose à faire est de lire la datasheet du micro-contrôleur qu'on utilise pour en connaître le fonctionnement, ou a minima, s'assurer qu'il fonctionne bien comme on le pense.
Dans le cas présent, d'après tes indications du post #45, il n'y a pas de doute quant au fait qu'on parle bien du même type de micro-contrôleur (ATmega88/168/328/P).
Concernant ton code, je l'analyse comme suit :
Compte tenu de ce que tu obtiens, j'en déduis que tes leds s'allument et clignotent parce qu'elles sont alimentées par les résistances de rappel internes des pins que tu as configurées en entrées.Code:DDRD=0xC3; // PD0, PD1, PD6 et PD7 configurées en sorties // PD2, PD3, PD4 et PD5 configurées en entrées while (1) { PORTD=0x3C; // sorties PD0, PD1, PD6 et PD7 à 0 // activation des résistances de pull-up sur les entrées _delay_ms(1000); PORTD=0x1C; // désactivation de la résistance sur l'entrée PD5 _delay_ms(1000); PORTD=0x0C; // désactivation de la résistance sur l'entrée PD4 _delay_ms(1000); PORTD=0x04; // désactivation de la résistance sur l'entrée PD3 _delay_ms(1000); }
Si elles parviennent à s'allumer, c'est juste parce qu'elles présentent un bon rendement. Mais :
- si tu mesures la tension sur les pins par rapport à GND, tu constateras qu'elle ne monte pas jusqu'à 5V,
- si tu vérifies l'intensité du courant qui traverse les leds (par exemple en mesurant la tension aux bornes de la résistance de limitation et en appliquant la loi d'Ohm I=U/R), tu constateras qu'elle est beaucoup plus faible que celle attendue. Tu pourrais par exemple obtenir dans les 0,1 mA (luminosité d'un voyant) au lieu de 20 mA (luminosité d'un petit éclairage).
Oui, tu as bien raison, elle ne vont pas jusqu'a 5V et le courant est bien plus faible.
Alors je souhaiterais quelques explications en fonction du schéma pour bien comprendre si tu as du temps à m'accorder. Car apparemment les sorties et entrées serait inversé par rapport à ma logique.
Ce serait peut-être plus à sa place en Électronique qu’ici ? Si oui je peux déplacer la discussion.
Rien ne sert de penser, il faut réfléchir avant - Pierre Dac
Parfois il faut se taper des pages de datasheet.Oui, tu as bien raison, elle ne vont pas jusqu'a 5V et le courant est bien plus faible.
Alors je souhaiterais quelques explications en fonction du schéma pour bien comprendre si tu as du temps à m'accorder. Car apparemment les sorties et entrées serait inversé par rapport à ma logique.
On y trouve la tension de sortie des I/O en fonction du courant, l'état des registres pour réaliser un paramétrage, etc