PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Problem der ADC Wandlung bei AtMega16



bmtil
26.09.2011, 08:04
Hallo,
ich bin recht neu in der Welt der µC'er und braeuchte etwas Starthilfe.
Bin gerade dabei einen "intelligenten" Thermometer zu basteln, indem ich die Temperatur ueber einen LM35 auslese, die Werte an den ADC des AtMega16 uebergebe, und dieser mir dann die Werte ueber die UART an den Rechner sendet (an Hterm).
Folgendes Problem gibt es dabei: ich kann nur eine 1023 rauslesen auf dem Hterm.
Die Hardware ist voellig richtig, wenn ich mit dem Multimeter an den GND und Output des LM35 geh, mess ich vernuenftige 230mV, die sich auch erhoehen wenn ich den Sensor erwaerme.
Der UART teil der Hardware ist auch richtig, hab davor um die UART zu testen ein Programm geschrieben mit dem ich ueber die UART die LED auf der Platine ein und ausschalten konnte und das Programm mir den "Status" der LED auf Hterm rausgegeben hat.
Das Problem muss also an dem ADC liegene, respective an meinem Code.
Ich hab mal meinen Code als .txt angehangen und hoffe auf zahlreiche Tipps.
Schonmal vielen Dank und Grüße.

Ceos
26.09.2011, 10:28
in deiner Schleife rufst du zwar adc_read auf, aber du weist das Ergenis deiner Variablen adc_wert nicht zu, der Wert wird nur VOR der Schleife einmal belegt und bleibt dann unberührt! Da du außerdem beim ersten adc_read die interrupts noch abgeschaltet hast, wird vermutlich auch kein Messergebnis entstehen, was die 0 erklären würde!

wofür zum Geier brauchst du das "overflowzahl" ? Der dürfte dir theoretisch niemals antworten! Da du die Bedingung if (overflowzahl==1) nie erfüllen kannst!!!

Außerdem kannst du dir das lesen des ADC-Wertes in deiner Init sparen!!! Der wert wird nur DANN nicht aktualisiert, wenn du das High- ohne das Lowbyte liest! Also erst ADCH dann ADCL und nicht andersrum! Wenn du garnichts liest intressiert das nicht, nur durch das lesen des Highbytes wird das Lowbyte gesperrt und das Lowbyte durch lesen desselbigen wieder freigegeben!

Das ADCSRA |=(1<<ADEN); im adc_read ist auch überflüssig, wenn der ADC einmal an ist, geht er nicht wieder aus bis du das Flag explizit löschst.
und dein Kanalwählen ist so auch nciht richtig: ADMUX = (ADMUX & 0b11000000) | (channel & 0b00111111);
denn Bit 7 und 6 bestimmen deine Analog-Referenz und dürfen nicht durch die Kanalwahl verändert werden!!!! Deswegen erst die Bits 0 bis 5 löschen und dann die channel Variable maskieren und abschließend ver-oder-n

bmtil
26.09.2011, 10:58
@Ceos:
Vielen Dank fuer die Antwort.
Also hab jetzt folgendes gemacht:
erstmal die Zuweisung "adc_wert = adc_read(0);" in die Mainloop gepackt. Dann in der main(void) die Interrupts als allererstes aktiviert. Das Maskieren des ADMUX hab ich jetzt aus deinem Post uebernohmen.
"overflowzahl" stammt eigentlich aus meinem Timer (ich benutz den ADs ohne Interrupts), der µC soll nur beim auftreten eines Overflows mir auf den Hterm den AD-Wert senden (bei meinem Quarz ist ca. alle 2 Sekunden, also gut lesbar).
Mit den ganzen Veränderungen funktioniert es trotzdem nicht, ich bekomm jetzt wieder nur noch Nullen, und der Wert veraendert sich natuerlich immer noch nicht wenn ich den Sensor erwaerme.
Wie wuerdest du das Auslesen realisieren?
Grueße.

Nachtrag: der Sensor ist natuerlich am PA0 des AtMegas angeschlossen.

Ceos
26.09.2011, 11:02
das sei() erst nach den inits aber noch vor dem read_ADC so früh darfst du den natürlich auch nicht raushauen ^^

hast du denn mal z.B. mit einem Voltmeter überprüft ob da überhaupt eine Spannung messbar ist ?!

EDIT: Ei Ei Ei ... das erste read darf außerdem NICHT vor dem init stehen!!!!! erst die inits, dann sei() dann das erste read

PS: ich schau nochmal eben ins DAtenblatt ob alles richtig konfiguriert iss

okay Folgendes:
mit REFS0 REFS1 nimmst du die interne 2.56V Referenz richtig?
mit ADPS1 ADPS0 ist dein Teiler 8 nicht 128 wie im Kommentar richtig?

Wie hast du den Sensor angeschlossen?! Was erwartest du für eine Spannung?! Wie ist der AREF-Pin beschaltet !?

PPS: Hast du überprüft ob er den Wert mehr als nur einmal ausgibt?!

bmtil
26.09.2011, 11:08
Sicherlich, mit dem Voltmeter mness ich eine Spannung, diese veraendert sich auch wenn ich mit den Fingern den Sensor erwaerme. Die Hardware ist zu 100% richtig.
Meine Zuweisung adc_wert=adc-read(0); steht jetzt in der if-Bedienung der main-loop drin, damit halt beim jedem Overflow der Wert neu ausgelesen wird.

Interne 2,56V Spannung nehme ich.
Wegen dem Teiler: hab vergessen in den Kommentaren zu aendern, ist aber auf 8.

Sensorbeschaltung:
Ist ein LM35, Vout (mittlerer Pin) geht an PA0, GND sicherlich an GND und Vin sicherlich an Vcc.
ich erwarte eine Spannung zwischen 200 und 250mV (dieselbe mess ich zwischen Vin und Vout des LM35).
AREF PIN ist ueber einen Abblockkondensator (100nF) mit GND verbunden.

Nachtrag: die Null kommt kontinuirlich alle 2 Sekunden (wie auch erwuenscht, nur eben ist der Wert falsch und nicht veraenderbar -.-)

Ceos
26.09.2011, 11:20
ich erwarte eine Spannung zwischen 200 und 250mV (dieselbe mess ich zwischen Vin und Vout des LM35).

Schreibfehler ?!

Denn sonst wären das ja 4.8V am PA0 und das wäre ein wenig über der Referenz ^^ ... bisher ergibt sich mir der Fehler nicht, vll. bin ich auch gerade etwas zu blind dafür -.- die Kaffee-Maschine ist Kaputt

Poste den code einfach nochmal fix (PS versuchs mit den[ C O D E] dein code [ / C O D E] -Tags, macht es einfacher als Dateien hochzuladen, lass nur die Leerzeichen zwischen den Buchstaben weg)

bmtil
26.09.2011, 11:30
Ja, ich meinte sicherlich zwischen Vout des Sensors und der Ground.
Zwischen Vout des Sensors und Vin des Sensors kommen auch die richtigen 4.8V

hier nochmal der code in "sauber":



#include <avr/io.h>
#include <stdlib.h>
#include <string.h>
#include <avr/interrupt.h>
#include "uart_komplett.h" //meine uart
#include "timer1.h" //meine timer funktion, sorgt dafür dass die uart ausgabe nur alle 2 sekunden erfolgt (overflows halt)

//Dinge definieren
#define LED_DDR DDRC //definieren der LED DDR
#define LED_PORT PORTC //defintion des genauen ports für die DDR
#define LED_PORTPIN1 PC1 //definition der roten LED
#define LED_PORTPIN0 PC0 //definition des gruenen LED

//Variablen definieren

uint16_t adc_wert = 0; //Wert des ADC
volatile uint8_t overflowzahl = 0; //zaehlt die overflows, wichtig für den timer und die uart ausgabe (zwei sekunden takt halt)

//Funktionen definieren
void adc_init()
{
uint16_t result; //ergebnis variable festlegen, uint16 wegen 10 bit ergebnis

ADMUX = (1<<REFS1)|(1<<REFS0); //die interne Referenzspannung nehmen
ADCSRA = (1<<ADPS1)|(1<<ADPS0); // Frequenzvorteiler bei 8
ADCSRA |= (1<<ADEN); //ADEN "enabled" ADC
ADCSRA |=(1<<ADSC); //analog zu digital wandlung aktivieren


while (ADCSRA & (1<<ADSC)) // eine "sinnlos" Wandlung durchfuehren um den eventuellen Mist
{ // aus dem Wandler rauszuschmeissen

}
result = ADCW; // das ergebnis der wandlung steht in ADCL und ADCH (sind die Result Register)
//dabei wird immer von ADCL nach ADCH gelesen/
//muss einmal gelesen werden, sonst wird das ergebnis der naechsten wandlung nicht gelesen
}

//Einzelne ADC Messung und "Lesung" des Wertes

uint16_t adc_read (uint8_t channel)
{
ADMUX = (ADMUX & 0b11000000) | (channel & 0b00111111); //kanal waehlen

ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) )
{
} // s.o.
return ADCW;
} //geb mir das ergebnis, ADCW is das selbe wie result = ADCL, zusaetzlich zu result += (ADCH<<8)

int main(void)
{

LED_DDR |= (1<<LED_PORTPIN1)|(1<<LED_PORTPIN0); //PC1 und PC0 Pins als ausgang festlegen und einschalten



LED_PORT = 0x01; //rote LED an

DDRA = 0x00; //explizit den PortA als Eingang deklarieren
uint16_t adc_wert;
char buffer[20];


usart_init();
timer1_init();
adc_init(0);

sei(); //interrupts aktivieren

adc_wert = adc_read(0); //zuweisung der Wert - Variable der "Auslese-Funktion"

while(1)
{

adc_wert = adc_read(0); //zuweisung der Wert - Variable der "Auslese-Funktion"

if (overflowzahl==1) //UART Uasgabe nur wenn ein Overflow stattfinden
{

sprintf ( buffer, "ADC: %d\r\n", (int)adc_wert ); //string rausgeben, hier leider nur "ADC: 0 "endzeichen""

uart_puts( buffer ); //puts fuer den string

overflowzahl=0; //die zählung der overflows wieder auf 0 setzen, in der ISR fuer den Timer steht drin: "overflowzahl++;", damit die
// if-Bedienung dieser main-loop erfuellt wird



}

}
}

Ceos
26.09.2011, 11:50
okay, das sieht aufegräumter aus, aber ich finde dein besagtes

adc_wert = adc_read(0);

nicht innerhalb der while(1)-schleife wieder, was allerdings wichtiger ist, du hast wahrescheinlich ebenfalls einen tippfehler begangen udn das erste adc_read(0) VOR dem sei sollte richtig ein adc_init() sein ?

bmtil
26.09.2011, 11:56
Ja genau, hab jetzt nochmal oben (und sicherlich in meinem AVR Studio) die adc_wert=adc_read(0); auch in die while(1) gepackt. Und das adc_read(0) vor dem seio ist sicherlich ein adc_init.
nun kommt kontinuirlich eine 767, diese verändert sich immer noch nicht und hat auch nichts mit dem Ergebniss zu tun. Nach der Rechnung ADWert= Vout*1024/Vref sollten naemlich ca. 90 rauskommen.

bmtil
27.09.2011, 10:38
So, Problem geloest.
hab den AtMega getauscht, und es ging mit einem neuen AtMega16. Hab tatsaechlich einen defekten erwischt.
PS, sorry fuer den Doppelpost, dachte nur das waer interresant.

Ceos
27.09.2011, 18:08
sorry deine Antwort war mir entgangen, für das "erledigt" gibts irgendwo nen button wenn du im topic bist, und ich denke ein doppelpost so von wegen "problem gelöst" wird dir niemand übel nehmen ... geht eher um solche doppelpostst wie "keiner da der ahnung hat" ^^