Je ne fais pas beaucoup de c. Donc, je m'adresse à ceux qui connaissent bien.
Je fais tourner un bout de code dans une arduino uno à une fréquence critique par rapport au processeur. Y a-t-il un moyen d'économiser des cycles d'horloge dans ce petit bout de code ?
ISR(TIMER2_COMPA_vect)
{
if (++period_count_1 > tone_1)
{
PORTB = PORTB ^ 0b0000001 ; //inversion de la sortie digital 8
period_count_1 = 0;
}
if (++period_count_2 > tone_2)
{
PORTB = PORTB ^ 0b0000010 ; //inversion de la sortie digital 9
period_count_2 = 0;
}
if (++period_count_3 > tone_3)
{
PORTB = PORTB ^ 0b0000100 ; //inversion de la sortie digital 10
period_count_3 = 0;
}
if (++period_count_4 > tone_4)
{
PORTB = PORTB ^ 0b00001000 ; //inversion de la sortie digital 11
period_count_4 = 0;
}
}
il naus faudrait un peut plus d'info et surtout savoir comment sont géré les differentes varible dans le code.
Une optimisation, si c'est pour quelques tours de cycles, tu peux rarremrnt faire mieux que le compilateur. Si tu veux vraiement optimiser quelque chose, il faut souvent regarder coté de l'algo (c'est pour ça que je te demande un peut plus d'info).
la connaissance est une chose qui ne nous appauvrit pas quand on la partage.
Le but est de faire jouer une partition à 4 voix par 4 buzzers différents. Le code est prévu pour pouvoir générer les 88 fréquences correspondant aux 88 touches d'un piano. Voici le code complet :
//Chaque valeur doit valoir la le nombre de débordement à attendre pour générer la bonne fréquence
for (int i = 1; i < 89; ++i)
{
clavier[i] = clavier[i] / periode_timer;
}
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
TCCR2A = 0b00000010 ;
TCCR2B = 0b00000001 ; // prescaler = 1
TIMSK2 = 0b00000010 ; // interruption locale autorisée
OCR2A = TOP_TIMER ; // TOP du compteur
}
// Routine d'interruption
ISR(TIMER2_COMPA_vect)
{
if (++period_count_1 > tone_1)
{
PORTB = PORTB ^ 0b0000001 ; //inversion de la sortie digital 8
period_count_1 = 0;
}
if (++period_count_2 > tone_2)
{
PORTB = PORTB ^ 0b0000010 ; //inversion de la sortie digital 9
period_count_2 = 0;
}
if (++period_count_3 > tone_3)
{
PORTB = PORTB ^ 0b0000100 ; //inversion de la sortie digital 10
period_count_3 = 0;
}
if (++period_count_4 > tone_4)
{
PORTB = PORTB ^ 0b00001000 ; //inversion de la sortie digital 11
period_count_4 = 0;
}
}
La fonction tourne bien si je l'appelle tous les 119 ticks d'horloge, soit à 134 kHz (le processeur de l'arduino tourne à 16MHz). Si je descends à 118 ticks, cela ne fonctionne plus. Le problème est qu'actuellement, toutes les notes sont un peu fausses. Si je peux augmenter la fréquence d'appel à la fonction, je peux obtenir une meilleure définition.
J'y ai pensé, mais il n'y a que 3 timers sur une Arduino Uno Chacun peut générer 2 PMW, mais à la même fréquence, seul l'angle peut être différent (pour faire une note il doit forcément être à 50% pour faire des ondes carrées je pense ? ). Je pourrais donc gérer 3 voix, mais pas 4. Si j'ai bien compris. Mon programme marche bien au final, j'arrive à jouer des mélodies, des accords. Mais quelques ticks d'économisés n'auraient pas été superflus pour gagner en justesse... Aucun moyen d'économiser dans l'interruption ?
Ce qui m'embête aussi c'est que toutes les notes sont un peu plus basse (peut-être un quart de ton plus ou moins), que prévu. Si c'était juste une histoire de précision, elles devraient être plus hautes ou plus basses aléatoirement. C'est comme si le timer tournait un peu plus lentement que ce que je prévois par calcul. A moins que ce soit une erreur de conception dans mon code ?
J'ai utilisé la fréquence la plus élevée possible sans que sa plante.
La note la plus aigüe d'un piano (le C7 dans mon tableau) a une fréquence de 4186 HZ. pour la générer je dois basculer la sortie 2 fois plus rapidement pour générer une onde carrée, soit à 8372 HZ. Si je ne contrôle le temps écoulé qu'à une fréquence de 44KHz seulement , ça ne me fait pas une grande précision. La note sera fausse. Evidement, plus la note à générer est grave, plus elle sera juste, la période à générer étant beaucoup plus longue...
Avec ton exemple de 44 KHz, pour faire le C7, je devrai compter jusqu'à 44000/8372 = 5 avant de basculer le pin. Pour la note juste en dessous, le B6 sur un piano, qui sonne à 3951 Hz, je devrais basculer le pin tous les 44000 / (3951*2) = 5 également. On ne pourra donc pas différencier ces 2 notes voisines, elles sonneront exactement pareil !! (Dons mon code, je ne calcule pas en fréquence mais en période. Mais le principe est le même. )
Si je pouvais aller au delà de 134 kHz, je le ferais, pour gagner en justesse. Seulement le programme ne fonctionne plus si j'augmente la fréquence : l'interruption est trop longue...
Salut potterman ! Je compile avec l'IDE Arduino. Je ne sais pas du tout ce qu'il fait. Il y a des options de compilation dedans ? Je ne sais même pas si il respecte vraiment la norme du C. (j'imagine que oui)...
Je ne sais pas si il y a des options de compilation. ça dépend du compilateur qu'ils utilisent. Si tu regardes dans la doc ou si tu fais des recherches sur internet tu trouveras peut-être..
Pour prendre l'exemple du compilateur GCC sur un processeur x86 (genre le processeur Intel de ta machine perso), tu peux lui spécifier -O0 qui signifie de ne faire aucune optimisation, -O1 qui signifie de faire les optims les plus simples, -O2 où il va faire du réordonnancement d'instruction en plus avec d'autres optims un peu plus fortes, et -O3 où il va se mettre à faire du déroulage de boucle et des trucs du genre..
Selon le code source, le processeur, et le compilateur, la différence entre -O0, -O1 et -O2 peut être énorme. De l'ordre d'un x2 sur la performance.
Peut-être que Arduino IDE utilise déjà ces options de compilations de base. Ou peut-être qu'il n'en utilise pas.
La phrase "c'est pour de l'embarqué, je pense qu'ils ont tout fait pour optimiser le code" est loin d'être vraie - parce que quand tu optimises, l'assembleur devient moins lisible. Sans optimisation, tu as une correspondance assez évidente entre ton code C et l'assembleur généré, tu peux dire avec précision "cette ligne de l'assembleur vient de telle ligne du C". Quand tu utilises des optimisations, en général ça casse cette propriété là, et donc certains fabricants de systèmes critiques (par exemple : fusée, avion..) vont compiler leur code sans aucune optimisation afin de pouvoir vérifier que le code assembleur correspond bien au code source, et que le compilateur n'a pas fait n'importe quoi entre temps (un compilo peut avoir des bugs).
ça peut donc valoir le coup de vérifier si les options d'optimisation sont activées - et si elles ne le sont pas, de les activer.
Par contre, si ton code repose sur des temps d'exécution - peut-être que faire exécuter des parties du code plus vite, ça va en fait fausser ton programme. Par exemple si telle fonction fait maintenant 20 cycles au lieu de 40 après optimisation, peut-être que ça va te fausser le reste de ton code, vu que tout sera décalé de 20 cycles.
En fait, le bout de code que je voudrais optimiser est une interruption. C'est à dire une fonction appelée automatiquement tous les n ticks d'horloge. Je définis n moi même. C'est le rôle du#define TIMER_TOP au début du code. Si le processeur fait autre chose au moment où l'interruption est déclenchée il s'arrête, exécute l'interruption et reprend sa tâche après. Donc non, améliorer cette fonction ne va pas fausser mon code. Je pourrais juste l'appeler plus souvent et donc gagner en précision, en diminuant la valeur de TIMER_TOP. Le reste de mon programme s'adapte tout seul à l'augmentation de fréquence.
Pour l'optimisation par le compilateur, on parle d'Arduino, c'est prévu pour faire des petites choses à la maison, pas des fusées ou des avions je pense
Il faut savoir que quand tu fais une interruption, le passage du code normal à l'interruption n'est pas instantané, et tu perds des cycles quoi qu'il arrive à faire ça. Si ça se trouve, le nombre de cycles que tu perds à switcher à l'interruption, est plus grand que le nombre de cycles que tu mets à faire ton code d'interruption.
Une simple interruption peut prendre jusqu'à 100 cycles ! Je pense que ça dépasse largement le nombre de cycles que met ta fonction à s'exécuter.
Le lien donne d'ailleurs des moyens de faire baisser ce chiffre là.. Mais je ne sais pas si c'est applicable à ton Arduino en particulier.. Et peut-être que chez toi une interruption ne prend pas aussi longtemps. Le seul moyen de savoir c'est de mesurer le temps en utilisant des fonctions de mesures de temps : tu enregistres le nombre de cycles, tu fais une interruption sur une fonction qui ne fait rien, tu enregistre le nombre de cycles, et tu regardes la différence entre les deux nombres (sachant qu'une fonction "qui ne fait rien" va quand meme mettre 1-4 cycles à s'exécuter mais ça donne une ordre d'idée du coût d'une interruption).
---
Pour les optims d'Arduino, oui peut être, mais ça ne coute rien de vérifier ! C'est pas évident qu'elles y soient par défaut.
Merci pour ces infos très intéressantes. Les chiffres que tu donnes sont cohérents avec ce que j'ai observé. En effet, j'arrive à exécuter mon interruption tous les 119 cycles, dès que je passe à 118 ca donne n'importe quoi. Une centaine pour la lancer, et quelques-uns pour ma fonction. Les options de compilation de la fonction elle-même donneront donc un gain négligeable.
Je vais regarder ce que propose le lien. Merci bcp.
Bon je laisse les choses en l'état. Ca fonctionne suffisamment bien. Merci à tous pour votre aide. Voici le code fini. Il permet tout simplement de faire jouer à l'Arduino un morceau à 4 voix, avec en demo un Choral de Bach si jamais ca amusait quelqu'un, voici le code complet :
la connaissance est une chose qui ne nous appauvrit pas quand on la partage.
Mon GitHub
la connaissance est une chose qui ne nous appauvrit pas quand on la partage.
Mon GitHub
la connaissance est une chose qui ne nous appauvrit pas quand on la partage.
Mon GitHub