Serious
31.10.2007, 18:59
Hallo,
ich hab mir vor einiger Zeit das Atmel Evaluationboard von Pollin (http://www.pollin.de/shop/shop.php?cf=detail.php&pg=OA==&a=MTY5OTgxOTk=&w=OTk4OTY4&ts=0) gekauft und mich jetzt mal mit dem ADC des Atmgega32 beschäftigt. Zum Messen und Ausgeben kam folgender Code zum Einsatz. (Man einer bemekrt vielleicht, dass ich den Code aus Beispielen zusammengeschustert habe)
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
// Sollte schon im Makefile definiert sein.
// In dem Fall hier einfach löschen.
#define F_CPU 16000000L
#define BAUD 19200UL
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)
void uart_init(void)
{
// Baudrate einstellen (Normaler Modus)
UBRRH = (unsigned char) (UBRR_BAUD>>8);
UBRRL = (unsigned char) (UBRR_BAUD & 0x0ff);
// Aktivieren des Empfängers, des Senders und des "Daten empfangen"-Interrupts
UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
// Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}
void uart_send(char c) {
while ( !( UCSRA & (1<<UDRE)) );
UDR = c;
}
void uart_send(char *s){
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_send(*s);
s++;
}
}
uint16_t ReadChannel(uint8_t mux)
{
uint8_t i;
uint16_t result;
ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); // Frequenzvorteiler
// setzen auf 8 (1) und ADC aktivieren (1)
ADMUX = mux; // Kanal waehlen
ADMUX |= (0<<REFS1) | (1<<REFS0); // VCC als Referenzspannung nutzen
/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while ( ADCSRA & (1<<ADSC) ) {
; // auf Abschluss der Konvertierung warten
}
result = ADCW; // ADCW muss einmal gelesen werden,
// sonst wird Ergebnis der nächsten Wandlung
// nicht übernommen.
/* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
result = 0;
for( i=0; i<4; i++ )
{
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while ( ADCSRA & (1<<ADSC) ) {
; // auf Abschluss der Konvertierung warten
}
result += ADCW; // Wandlungsergebnisse aufaddieren
}
ADCSRA &= ~(1<<ADEN); // ADC deaktivieren (2)
result /= 4; // Summe durch vier teilen = arithm. Mittelwert
return result;
}
int main(void)
{
// USART initialisieren
uart_init();
sei();
// Nichts tun. Die Interrupts erledigten den Rest
uint32_t i = 0;
while (1) {
i = 0;
while (i < 100000) i++;
uint16_t adcval;
adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -> Channel 0 */
char c[10];
itoa(adcval, c, 10);
uart_send(c);
uart_send("\n\r");
}
}
Nach dem Programmieren hab ich das ganze erst einmal unbeschaltet ausprobiert, das heißt an dem 40poligen Kabel, an dem die ganzen Ports herausgeführt sind war nichts dran gesteckt. Auffällig war ertwmal ein starkes Rauschen um den Wert 500. Sobald ich das Kabel dann mit in meinen Breadboard Adapter eingesteckt hate und ein loses Kabel mit Verbindung zum ADC port 0 mit meinen Fingern berührt habe, gab es plötzlich seltsame Schwingungnen. Nach der anfänglichen Überraschung(Ich wollte schn meinen Herzschlag dafür verantwortlich machen) hab ich dann entdeckt, dass das ganze auch mit größeren Metallansammlungen funktioniert(zange, langes Kabel, Metalltisch). Die Schwingungen sahen etwa so aus:
736
1023
1023
648
27
0
11
127
672
1023
1023
707
72
3
13
90
517
1023
1023
871
289
3
9
Mit etwas Rechnerei habe ich dann eine Schwingungsfrequenz von 49,987 Hz ausgetrechnet, das erinnert auch mich Elektronik Anfänger an die 50 Hz NNetzfrequenz. Ich hab dann mal den ADC pin über 5,6 Mohm (10 Widerstände hntereinander, soviele wie ich gefunden habe) mit VCC verbunden, da gab es dann gleich den Messswert 1023.
Wie kommt das, dass der ADC auf so geringe Einflüsse schon anspricht? Hab ich das Prinzip des ADC vielleicht falsch verstanden? Ein Aanalog Digital Wandler wandelt doch schließlich Spannungen in Messwerte um, oder?
Mr. Serious
ich hab mir vor einiger Zeit das Atmel Evaluationboard von Pollin (http://www.pollin.de/shop/shop.php?cf=detail.php&pg=OA==&a=MTY5OTgxOTk=&w=OTk4OTY4&ts=0) gekauft und mich jetzt mal mit dem ADC des Atmgega32 beschäftigt. Zum Messen und Ausgeben kam folgender Code zum Einsatz. (Man einer bemekrt vielleicht, dass ich den Code aus Beispielen zusammengeschustert habe)
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
// Sollte schon im Makefile definiert sein.
// In dem Fall hier einfach löschen.
#define F_CPU 16000000L
#define BAUD 19200UL
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)
void uart_init(void)
{
// Baudrate einstellen (Normaler Modus)
UBRRH = (unsigned char) (UBRR_BAUD>>8);
UBRRL = (unsigned char) (UBRR_BAUD & 0x0ff);
// Aktivieren des Empfängers, des Senders und des "Daten empfangen"-Interrupts
UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
// Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}
void uart_send(char c) {
while ( !( UCSRA & (1<<UDRE)) );
UDR = c;
}
void uart_send(char *s){
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_send(*s);
s++;
}
}
uint16_t ReadChannel(uint8_t mux)
{
uint8_t i;
uint16_t result;
ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); // Frequenzvorteiler
// setzen auf 8 (1) und ADC aktivieren (1)
ADMUX = mux; // Kanal waehlen
ADMUX |= (0<<REFS1) | (1<<REFS0); // VCC als Referenzspannung nutzen
/* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while ( ADCSRA & (1<<ADSC) ) {
; // auf Abschluss der Konvertierung warten
}
result = ADCW; // ADCW muss einmal gelesen werden,
// sonst wird Ergebnis der nächsten Wandlung
// nicht übernommen.
/* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
result = 0;
for( i=0; i<4; i++ )
{
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while ( ADCSRA & (1<<ADSC) ) {
; // auf Abschluss der Konvertierung warten
}
result += ADCW; // Wandlungsergebnisse aufaddieren
}
ADCSRA &= ~(1<<ADEN); // ADC deaktivieren (2)
result /= 4; // Summe durch vier teilen = arithm. Mittelwert
return result;
}
int main(void)
{
// USART initialisieren
uart_init();
sei();
// Nichts tun. Die Interrupts erledigten den Rest
uint32_t i = 0;
while (1) {
i = 0;
while (i < 100000) i++;
uint16_t adcval;
adcval = ReadChannel(0); /* MUX-Bits auf 0b0000 -> Channel 0 */
char c[10];
itoa(adcval, c, 10);
uart_send(c);
uart_send("\n\r");
}
}
Nach dem Programmieren hab ich das ganze erst einmal unbeschaltet ausprobiert, das heißt an dem 40poligen Kabel, an dem die ganzen Ports herausgeführt sind war nichts dran gesteckt. Auffällig war ertwmal ein starkes Rauschen um den Wert 500. Sobald ich das Kabel dann mit in meinen Breadboard Adapter eingesteckt hate und ein loses Kabel mit Verbindung zum ADC port 0 mit meinen Fingern berührt habe, gab es plötzlich seltsame Schwingungnen. Nach der anfänglichen Überraschung(Ich wollte schn meinen Herzschlag dafür verantwortlich machen) hab ich dann entdeckt, dass das ganze auch mit größeren Metallansammlungen funktioniert(zange, langes Kabel, Metalltisch). Die Schwingungen sahen etwa so aus:
736
1023
1023
648
27
0
11
127
672
1023
1023
707
72
3
13
90
517
1023
1023
871
289
3
9
Mit etwas Rechnerei habe ich dann eine Schwingungsfrequenz von 49,987 Hz ausgetrechnet, das erinnert auch mich Elektronik Anfänger an die 50 Hz NNetzfrequenz. Ich hab dann mal den ADC pin über 5,6 Mohm (10 Widerstände hntereinander, soviele wie ich gefunden habe) mit VCC verbunden, da gab es dann gleich den Messswert 1023.
Wie kommt das, dass der ADC auf so geringe Einflüsse schon anspricht? Hab ich das Prinzip des ADC vielleicht falsch verstanden? Ein Aanalog Digital Wandler wandelt doch schließlich Spannungen in Messwerte um, oder?
Mr. Serious