Je travaille sur un projet relativement "simple" où je dois mesurer la fréquence d'une onde sinusoïdale qui varie en amplitude et en fréquence. Pour simplifier les choses, pour l'instant, je n'ai qu'une entrée à onde sinusoïdale à fréquence fixe (27Hz) (entrée négative du comparateur) qui ne peut varier qu'en amplitude (à l'aide d'un potentiomètre). L'entrée positive du comparateur est réglée sur Vcc / 2. La sortie du comparateur est ensuite introduite dans le registre de capture d'entrée du microcontrôleur atmega2560 pour mesurer la fréquence.
Le problème est qu'à certaines amplitudes du signal d'entrée, j'obtiens un basculement assez intense (ou parfois des bandes mortes) sur la sortie qui ressemble à ceci:
Où la sortie attendue devrait ressembler à ceci:
Ce que j'ai essayé jusqu'à présent:
Utilisation du comparateur interne de l'atmega2560 interne. Utilisation d'un comparateur externe. Présentation de l'hystérésis à l'aide du logiciel et du circuit de déclenchement de Schmitt. J'ai essayé diverses configurations d'entrée, y compris la configuration de référence fixe et la configuration du trancheur de données. Essayer différents atmega2560. Essayer différentes vitesses d'horloge.
Certaines solutions étaient plus stables que d'autres, mais aucune d'entre elles n'était loin d'être acceptable. Je me suis installé avec la configuration la plus stable à ce jour:
Avec cette configuration, certaines choses améliorent / modifient la stabilité, mais toujours loin d'être parfaites:
Modification de la valeur de R5 pour augmenter l'hystérésis. Suppression complète de C2 (aucune idée pourquoi). Toucher des fils sur la planche à pain (un certain nombre d'entre eux côte à côte). Commutation des alimentations de l'extérieur vers l'USB et vice versa.
À ce stade, c'est soit du bruit, mon DAC avec lequel je génère l'onde sinusoïdale, soit je fais quelque chose de très fondamental de manière incorrecte. Ce circuit a fonctionné pour d'autres personnes sans aucun problème, donc quelque chose ne va pas avec ma configuration ou mon environnement.
Si quelqu'un a des suggestions, j'apprécierais grandement votre temps.
Voici ma source minimale:
#include <avr/io.h>
void init(void);
void init(void) {
/* Setup comparator */
ACSR = (1 << ACIE) | (1 << ACIS1);
/* Initialize PORTD for PIND5 */
DDRD = 0x00;
PORTD = 0x00;
/* Enable global interrupts */
sei();
}
int main(void) {
init();
while (1) {}
}
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACIS0))) { //comparator falling edge
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
ACSR |= (1<<ACIS0); //set next comparator detection on rising edge
}
else {
ACSR &= ~(1<<ACIS0); //set next comparator detection on falling edge
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
En outre, voici le lien vers le schéma de circuit et la bibliothèque elle-même:
http://interface.khm.de/index.php/lab/interfaces-advanced/frequency-measurement-library/
MISE À JOUR:
J'ai essayé toutes vos suggestions, aucune n'a fonctionné sauf une. La suppression des indicateurs d'interruption ou la désactivation des interruptions à l'intérieur ou à l'extérieur de l'ISR n'a pas vraiment eu d'effet. Je semble mal comprendre comment fonctionne réellement le registre comparateur de la puce.
Comme je l'avais mentionné initialement, j'allais utiliser la capture d'entrée pour mesurer la fréquence d'une onde carrée dérivée d'une onde sinusoïdale. La sortie du comparateur est introduite dans la broche de capture d'entrée, puis utilisez des minuteries pour mesurer la période, simplement.
Voici le diagramme de comparaison analogique de atmega2560 http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf , page 265:
Comme vous pouvez le voir, le comparateur a deux sorties, ACO et ACIS0 + ACIS1. ACO est défini lorsque + entrée> - entrée, effacé lorsque + entrée <- entrée. ACIS0 + ACIS1 sont des bits de sélection de front.
Au départ, je vérifiais le type de bord dans mon ISR. J'ai changé l'ISR à la place:
ISR(ANALOG_COMP_vect) {
if (!(ACSR & (1<<ACO))) { // + < -
/* Set PIND5 to 0V */
PORTD &= ~(1 << PIND5);
}
else {
/* Set PIND5 to 5V */
PORTD |= (1 << PIND5);
}
}
Et la sortie s'est comportée parfaitement (comme dans la deuxième image). Ensuite, j'ai commencé à mesurer la largeur des impulsions, mais les résultats n'étaient pas excellents. Basculement intense sur mon écran LCD, les nombres sautant à des valeurs aléatoires ou restant à 0, malgré un signal propre. J'ai réécrit mon code plusieurs fois en utilisant des conditions différentes, la seule solution semi-stable que j'ai eu jusqu'à présent est la suivante:
#include <avr/io.h>
#include <util/delay.h>
#include "UART.h"
void init(void);
volatile uint16_t y = 0;
volatile uint16_t x = 0;
volatile uint16_t current_value = 0;
volatile uint16_t previous_value = 0;
volatile uint16_t total = 0;
void init(void) {
/* Normal mode, 64 prescaler, Rising Edge trigger, Input Capture */
TCCR1A = 0;
TCCR1B = (1 << CS10) | (1 << CS11) | (1 << ICES1);
TIMSK1 = (1 << ICIE1);
ACSR = (1 << ACIC);
ADCSRB = 0x00;
/* This port is used for simulating comparator's output */
DDRC = 0xFF;
PORTC = 0xFF;
DDRD = 0x00;
PORTD = 0x00;
USART_Init(UBRR_VALUE);
sei();
}
int main(void) {
init();
while (1) {
if (TCNT1 == 60000) {
/* Display the values on the LCD */
USART_Transmit(0xFE);
USART_Transmit(0x01);
USART_Transmit_Double(x+y);
}
}
}
ISR(TIMER1_CAPT_vect) {
//ACSR &= ~(1<<ACIC);
if (!(ACSR & (1 << ACO))) {
if (!(TCCR1B & (1 << ICES1))) { // check for falling edge
PORTD |= (1 << PIND5);
PORTC &= ~(1 << PINC1);
TCCR1B |= (1 << ICES1);
current_value = ICR1;
x = current_value - previous_value;
previous_value = current_value;
}
}
else {
if (TCCR1B & (1 << ICES1)) { // check for rising edge
PORTD &= ~(1 << PIND5);
PORTC |= (1 << PINC1);
TCCR1B &= ~(1 << ICES1);
current_value = ICR1;
y = current_value - previous_value;
previous_value = current_value;
}
}
//ACSR |= (1<<ACIC);
}
Par semi-stable, je veux dire, j'obtiens la valeur correcte 1/3 des fois. Les autres fois, 2/3 des fois, c'est soit la moitié de la valeur correcte, soit une valeur aléatoire. J'ai essayé d'utiliser les bits de registre du temporisateur pour les instructions conditionnelles ainsi que les bits de registre du comparateur dans mon ISR, c'est la seule configuration qui fonctionne.
Ce que j'ai fait plus tard dans la journée a été d'utiliser un comparateur externe à la place avec la même configuration et la même source (à l'exclusion de toutes les lignes liées au comparateur). Sa sortie a été introduite dans la broche de capture d'entrée et elle a fonctionné comme prévu (n'a même pas eu besoin d'hystérésis).
À ce stade, je peux dire que je l'ai résolu en utilisant un comparateur externe, mais je ne sais pas pourquoi celui interne ne se comporte pas lui-même. J'ai lu de nombreux articles et guides à ce sujet, lu différentes bibliothèques, essayé de les imiter sans aucun résultat acceptable. La fiche technique ne contient que 5 pages sur l'ensemble du comparateur, je l'ai relu plusieurs fois et je ne vois pas ce que je fais mal.
Je voudrais savoir comment l'utiliser correctement mais si cela échoue, j'ai une sauvegarde. Si vous avez d'autres commentaires, c'est grandement apprécié.