Cet article est en continuité avec le précédent post PWM. Apprenez à programmer les minuteries pour qu’elles fonctionnent en mode PWM ! Alors commençons!
Bonjour les gens! Ça fait longtemps qu’on ne voit pas! 🙂
Dans mon post précédent, nous avons discuté des concepts de base de PWM. Résumons-le d’abord:
- PWM signifie Modulation de largeur d’impulsion.
- Il peut être généré en comparant une forme d’onde prédéterminée avec un niveau de tension de référence ou en réalisant de simples circuits analogiques.
- Le rapport cyclique d’une forme d’onde PWM est donné par la relation suivante.
- Il existe trois modes de fonctionnement PWM – PWM rapide, PWM Correct en phase et PWM Correct en fréquence et en phase
- Comment choisir la minuterie, le mode de fonctionnement et comparer le mode de sortie pour générer le PWM souhaité.
Prenons un énoncé de problème. Nous devons générer un signal PWM de 50 Hz ayant un rapport cyclique de 45%.
Analyse
Étant donné que
Frequency = 50 Hz
En d’autres termes, la période de temps, T
T = T(on) + T(off) = 1/50 = 0.02 s = 20 ms
Également, étant donné que
Duty Cycle = 45%
Ainsi, en résolvant selon l’équation donnée ci-dessus, on obtient
T(on) = 9 msT(off) = 11 ms
Maintenant, cela peut être réalisé de deux manières:
- Utilisez la minuterie en mode CTC
- Utilisez la minuterie en mode PWM
Méthodologie – Mode CTC
D’accord, donc je n’écrirai aucun code ici (juste le pseudo-code). Je suppose qu’après avoir lu mes articles précédents, vous êtes assez intelligent pour en écrire un vous-même! Nous ne discuterons que des concepts.
Tout d’abord, choisissez une minuterie appropriée. Pour cette application, nous pouvons choisir l’une des trois minuteries disponibles dans ATMEGA32. Choisissez un pré-scellant approprié. Ensuite, configurez la minuterie et procédez comme d’habitude. Le hic ici est que vous devez mettre à jour la valeur de comparaison du registre OCRx à chaque fois. Une telle façon est discutée dans le pseudo-code donné ci-dessous.
Ceci est analogue au clignotant LED traditionnel, sauf le fait que les heures de marche et d’arrêt sont différentes.
Pseudo Code
#include <avr/io.h>#include <avr/interrupt.h> uint8_t count = 0; // global counter // initialize timer, interrupt and variablevoid timerX_init(){ // set up timerX with suitable prescaler and CTC mode // initialize counter // initialize compare value // enable compare interrupt // enable global interrupts} // process the ISR that is firedISR (TIMERx_COMPA_vect){ // do whatever you want to do here // say, increment the global counter count++; // check for the global counter // if count == odd, delay required = 11 ms // if count == even, delay required = 9 ms // thus, the value of the OCRx should be constantly updated if (count % 2 == 0) OCRx = 9999; // calculate and substitute appropriate value else OCRx = 10999; // calculate and substitute appropriate value} int main(void){ // initialize the output pin, say PC0 DDRC |= (1 << 0); // initialize timerX timerX_init(); // loop forever while(1) { // do nothing }}
Maintenant, c’est une méthode. Et c’est très inefficace. Vous pouvez augmenter son efficacité en écrivant un meilleur code C (au niveau de la syntaxe), mais le concept reste le même. Si vous avez une autre méthode / concept, vous êtes les bienvenus pour le partager ici! 🙂
MISE À JOUR: L’un des lecteurs de maxEmbedded, « coolpales » a écrit ce code, et cela a fonctionné pour lui.
Veuillez noter que ce code n’est pas encore testé! Donc, si l’un d’entre vous l’essaie, publiez vos résultats ici, je serais heureux de les voir! 🙂
Méthodologie – Mode PWM
D’accord, voyons maintenant le mode PWM. Le mode PWM dans AVR est contrôlé par le matériel. Cela signifie que tout, par tout ce que je veux dire « tout », est fait par le processeur AVR. Tout ce que vous avez à faire est d’initialiser et de démarrer la minuterie, et de régler le cycle de service! Cool, hein ?! Apprenons comment!
Ici, j’ai utilisé Timer0 d’ATMEGA32 pour la démonstration. Vous pouvez également choisir n’importe quelle autre minuterie ou microcontrôleur AVR. Voyons maintenant les registres.
TCCR0 – Registre de contrôle Timer / Counter0
Nous avons rencontré ce registre dans mon tutoriel Timer0. Ici, nous allons apprendre à définir les bits appropriés pour exécuter la minuterie en mode PWM.
Registre TCCR0
Nous ne discuterons que des bits qui nous intéressent maintenant.
- Bit 6,3-WGM01: 0 – Mode de génération de forme d’onde – Ces bits peuvent être réglés sur « 00 » ou « 01 » en fonction du type de PWM que vous souhaitez générer. Voici la table de recherche.
Description du Bit du Mode de Génération de Forme d’Onde
- Bit 5,4 – COM01:0 – Mode de sortie de correspondance de comparaison – Ces bits sont définis afin de contrôler le comportement de la broche de comparaison de sortie (OC0, broche 4 dans ATMEGA32) conformément aux bits WGM01: 0. La table de recherche suivante détermine les opérations de la broche OC0 pour le mode PWM rapide.
Comparer le Mode de sortie, le Mode PWM Rapide
Voyons maintenant les formes d’onde PWM rapides. Des explications détaillées peuvent être trouvées dans mon tutoriel précédent.
PWM rapide
Maintenant, permettez-moi de vous rappeler que le PWM AVR est entièrement contrôlé par le matériel, ce qui signifie que même l’opération de comparaison de minuterie est effectuée par le processeur AVR. Tout ce que nous devons faire est de dire au CPU quoi faire une fois qu’une correspondance se produit. Les broches COM01:0 entrent en jeu ici. On voit qu’en le mettant sur « 10 » ou « 11 », la broche de sortie OC0 est soit réglée, soit effacée (autrement dit, elle détermine si le PWM est en mode inversé, ou en mode non inversé).
De même pour une PWM à phase correcte, la table de recherche et les formes d’onde vont comme ceci.
Comparer Le Mode De Sortie, Mode PWM Correct En Phase
PWM Correct de phase
Même ici, le réglage de COM01:0 sur « 10 » ou « 11 » détermine le comportement de la broche OC0. Comme indiqué dans les formes d’onde, il y a deux instances – l’une pendant le comptage ascendant et l’autre pendant le comptage descendant. Le comportement est clairement décrit dans le tableau de recherche.
Veuillez noter que OC0 est une broche de sortie. Ainsi, les effets de WGM et COM n’entreront en jeu que si le registre DDRx est correctement défini. Reportez-vous à ce tutoriel pour plus d’informations.
- Bit 2:0 – CS02:0 – Bits de sélection d’horloge – Ces bits sont déjà discutés dans le tutoriel Timer0.
OCR0 – Registre de comparaison de sortie
Nous avons rencontré même ce registre dans mon tutoriel Timer0. Nous utilisons ce registre pour stocker la valeur de comparaison. Mais lorsque nous utilisons Timer0 en mode PWM, la valeur qui y est stockée agit comme le rapport cyclique (évidemment!). Dans l’énoncé du problème, il est donné que le rapport cyclique est de 45%, ce qui signifie
OCR0 = 45% of 255 = 114.75 = 115
Et c’est tout! Maintenant, nous sommes prêts à écrire un code pour cela! 🙂
Edit: Remarque
Le code suivant explique comment créer un signal PWM d’un cycle de service souhaité. Si vous souhaitez modifier sa fréquence, vous devez modifier la valeur SUPÉRIEURE, ce qui peut être fait en utilisant le registre ICRx (qui n’est pas pris en charge par les minuteries 8 bits). Pour 16 bits Timer1, il peut être varié en utilisant ICR1A. J’en discuterai bientôt lorsque nous discuterons de la servocommande.
Code
Alors voici le code. Pour en savoir plus sur les opérations de port d’E/S dans AVR, consultez ceci. Pour connaître les manipulations de bits, consultez ceci. Pour apprendre à utiliser AVR Studio 5, consultez ceci. Pour savoir comment ce code est structuré, consultez le précédent article TIMER0.
#include <avr/io.h>#include <util/delay.h>void pwm_init(){ // initialize TCCR0 as per requirement, say as follows TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS00); // make sure to make OC0 pin (pin PB3 for atmega32) as output pin DDRB |= (1<<PB3);} void main(){ uint8_t duty; duty = 115; // duty cycle = 45% of 255 = 114.75 = 115 // initialize timer in PWM mode pwm_init(); // run forever while(1) { OCR0 = duty; }}
Énoncé du problème
Alors maintenant, prenons un autre énoncé du problème. Celui-ci va être plus pratique que le précédent!
Prenons le clignotant LED traditionnel où nous devons clignoter une LED à une fréquence particulière. Mais bon, attendez, n’en avons-nous pas discuté longtemps dans ce post (faites défiler vers le bas vers la fin)? Hmm, alors modifions-le de manière à intégrer PWM. Contrairement au clignotant LED traditionnel (où les LED sont ALLUMÉES ou ÉTEINTES), permet de la faire briller à la luminosité maximale, puis de diminuer lentement sa luminosité jusqu’à ce qu’elle atteigne zéro, puis d’augmenter à nouveau sa luminosité lentement jusqu’à ce qu’elle devienne maximale.
Analyse et code
Alors, comment le faisons-nous? Oui, vous l’avez bien deviné! Diminuez lentement le rapport cyclique de 255 à zéro, puis augmentez-le de zéro à 255. Selon le rapport cyclique, la tension appliquée à la LED varie, et donc la luminosité. La formule suivante donne la relation entre la tension et le rapport cyclique.
Alors voici le code. Je ne l’expliquerai pas, vous pouvez le décoder vous-même. Pour en savoir plus sur les opérations de port d’E/S dans AVR, consultez ceci. Pour connaître les manipulations de bits, consultez ceci. Pour apprendre à utiliser AVR Studio 5, consultez ceci. Pour savoir comment ce code est structuré, consultez le précédent article TIMER0.
// program to change brightness of an LED// demonstration of PWM #include <avr/io.h>#include <util/delay.h> // initialize PWMvoid pwm_init(){ // initialize timer0 in PWM mode TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS00); // make sure to make OC0 pin (pin PB3 for atmega32) as output pin DDRB |= (1<<PB3);} void main(){ uint8_t brightness; // initialize timer0 in PWM mode pwm_init(); // run forever while(1) { // increasing brightness for (brightness = 0; brightness < 255; ++brightness) { // set the brightness as duty cycle OCR0 = brightness; // delay so as to make the user "see" the change in brightness _delay_ms(10); } // decreasing brightness for (brightness = 255; brightness > 0; --brightness) { // set the brightness as duty cycle OCR0 = brightness; // delay so as to make the user "see" the change in brightness _delay_ms(10); } // repeat this forever }}
Voici donc mon autre tutoriel très attendu et long! Ensuite.. Communication série! À bientôt !! 🙂
Et oui, si vous avez des suggestions, des doutes, des critiques constructives, etc., vous êtes les bienvenus pour déposer une note ci-dessous! Abonnez-vous à mon blog ou prenez les flux RSS pour rester à jour!