PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : A/D Werte ausgeben



toraX
14.02.2006, 15:49
Hallo Leute,

ich hab ein riesen Problem. Ich hock schon seit Tagen vor meinem Rechner und versuch die A/D Werte die mein Atmega32 ins ADC Register schreibt über UART auf meinem PC auszugeben, aber leider vergeblich. Das einzige was auf meinem Bildschirm erscheint sind Unmengen an Buchstaben und Ziffern, also muss doch irgendwas mit itoa oder der UART-Übertragung nicht stimmen. In meinem Programm versuch ich durch drücken einer Taste ein bestimmtes LED zum leuchten zu bringen und zugleich die A/D Werte auszuspucken, die Funktion um die LEDs zum leuchten zu bringen funzt einwandfrei aber bei der UART-Übertragung ist der Wurm drin. Also ich wäre über jede Hilfe dankbar, ist bestimmt ein Witz für euch.

Hier ist mein Code

#include <avr/io.h>
#include "wchar.h"
#include <stdlib.h>

#define SYSCLK 14756000

void init(void);
unsigned int get_key(void);

/*
* Hauptprogramm
*/
int main(void)
{
unsigned int i=0;
char buffer[100];

init();

while(1)
{
/* Ergebnis auslesen und anzeigen */

if(i != 0xff)
{
itoa(i,buffer,2);
write(buffer);
}

i = get_key();


if (i != 0xff)
PORTC = ~(1<<i) & 0x7f;
else
PORTC = 0x7f;
}

return 0;
}

/*
* Initialisierung des µC
*/
void init(void)
{
DDRA = 0x00; /* Port als Eingang deklarieren ( A/D-Wandler Eingänge ) */
PORTA = 0x80;

DDRB = 0xff; /* Port als Ausgang deklarieren */
PORTB = 0x00;

DDRC = 0xff; /* Port als Ausgang deklarieren */
PORTC = 0x00;

DDRD = 0xff; /* Port als Ausgang deklarieren */
PORTD = 0x00;

/* Analog zu Digital Wandler aktivieren ( Prescaler = 128, interne +5V Referenz, "8-Bit" Ergebnis ) */
ADMUX = (1<<REFS0)|(1<<ADLAR);
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

/**
* Fragt die Tasten an AD-Port 7 ab, kann nicht erkennen ob zwei Tasten gleichzeitig gedrückt werden
*
* 255 keine Taste gedrückt
0 Taste 1 gedrückt
1 Taste 2 gedrückt
2 Taste 3 gedrückt
3 Taste 4 gedrückt
4 Taste 5 gedrückt
*/
unsigned int get_key(void)
{
unsigned int i, taste = 0xff;

/* ADC7 mit Vcc als Referenzspannung */
ADMUX = (1<<REFS0)|(1<<ADLAR)|0x07;

/* AD Wandler starten */
ADCSRA |= (1<<ADSC);

/* Warten bis das Ergebniss zur Verfügung steht */
while(!(ADCSRA & (1<<ADIF)));

/* Ergebnis auslesen und anzeigen */

i = ADCH;


if (i < 36)
taste = 4;
else if (i < 57)
taste = 3;
else if (i < 76)
taste = 2;
else if (i < 94)
taste = 1;
else if (i < 150)
taste = 0;

return taste;
}



und noch der Code der Verantwortlich ist für die Ausgabe



#include <avr/io.h>
#include <string.h>
#include <inttypes.h>

void uart_init(void);

int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))); /* warten bis Senden moeglich */
UDR = c; /* sende Zeichen */
return 0;
}


/* puts ist unabhaengig vom Controllertyp */

void uart_puts (char text[])
{
char *s;
s=&(text[0]);
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}

void write(char text[])
{
/*Den UART initialisieren*/
uart_init();
uart_puts(text);

}

void uart_init(void)
{
/* USART-Init 19200 Baud bei 16MHz für Mega32 */
UCSRB |= ( 1 << TXEN ); // UART TX einschalten
UCSRC |= ( 1 << URSEL )|( 3<<UCSZ0 ); // Asynchron 8N1
UBRRH = 0; // Highbyte ist 0
UBRRL = 51; // Lowbyte ist 51 ( dezimal )
}


Vielen Dank schon mal im voraus für eure Bemühungen
Gruss
Torsten

SprinterSB
14.02.2006, 16:35
-- Dein Kommentar zur Clock passt nicht zu dem Define. Sind es nun 16MHz oder 14.***? Das Ausrechnen solltest du dem Compiler überlassen, sonst musst du immer die Quelle nachpinseln, wenn sich die CPU-Frequqnz ändert.
-- Du wandelst den ADC-Wert nach binär um, zu erwarten ist also eine Folge wie 00001010101111001



http://people.freenet.de/gjl/helferlein/avr-uart-rechner.html

toraX
14.02.2006, 16:53
Danke schon mal für deine rasche Antwort.

-was muss ich dafür angeben, dass der Compiler die Clock selbst berechnet. Ich hab bei define jetzt mal 16000000 angegeben.

-ich habs bei itoa auch schon mit 10 ausprobiert aber bei der Ausgabe kommen immer unendlich viele Zeichen.

super_castle
14.02.2006, 17:13
du musst die mfile für die make richtig benutzen...

Castle

SprinterSB
14.02.2006, 17:15
Im Wiki-Beispiel UART (https://www.roboternetz.de/wissen/index.php/UART_mit_avr-gcc) wird der UBRR-Wert von gcc berechnet. Dazu muss er natürlich zur Compilezeit bekannt sein.

Ansonsten kann ich mit meinem Debugger Brain v0.9 nix schlimmes erkennen, nur daß ich den Text nicht als char text[] übergeben würde, sonden als char *text.

super_castle
14.02.2006, 18:02
mal eine zusammengestellte hilfe.

winavr-c-programm:



#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <stdint.h>
#include <string.h>
#include <avr/pgmspace.h>
#include "adc.h"

#define delay_us_(us) _delayFourCycles_( ( ( 1*(F_CPU/4000) )*us)/2005 )

static inline void _delayFourCycles_(uint16_t z)
{
uint16_t i;

for (i=0; i<z; i++)
asm volatile("nop");
}

static inline void delay_ms_(uint16_t z)
{
uint16_t i;

for (i=0; i<z; i++)
delay_us_(999);
}

int main(void)
{

uint16_t x=0;
char buffer[7];

ADCinit;
ADCprescaler_16;
//Aktivierung des ADC, festlegen eines Prescalers von 16

while (1)
{
ADCchannel_1;
//Aktivierung des Pins , an ihm soll die zu messende Spannung liegen

ADCstart;
//Start einer Konvertierung

x=getadc();
itoa( x , buffer, 10);
//routine zum senden an ein display oder rs232
delay_ms_(200);
}
}



die adc.h :



#define ADCchannel_init DDRA=0x00 // ADC Port als Eingang deklarieren
#define ADCinit ADCSRA|=_BV(ADEN) // Teilt dem Board mit das der jeweilige Port für ADC verwendet wird
#define ADCdisable ADCSRA &=~_BV(ADEN) // machs das vorherige wieder rückgänig
#define ADCstart ADCSRA|=_BV(ADSC) // startet eine konvertierung auf dem gewünschten Kannal/Pin
#define ADCfree ADCSRA|=_BV(ADATE) // schaltet den freilaufenden Modus ein
#define ADCvintern ADMUX|=_BV(REFS0) // interne Spannungsversorgung
#define ADCinterrupt_on ADCSRA|=_BV(ADIE) // ADC interrupt wird freigeschalten
#define ADCprescaler_2 ADCSRA |=_BV(ADPS0) // gewünschter Teilungsfaktor/Prescaler
#define ADCprescaler_4 ADCSRA|=_BV(ADPS1)
#define ADCprescaler_8 ADCSRA=_BV(ADPS1) | _BV(ADPS0)
#define ADCprescaler_16 ADCSRA|=_BV(ADPS2)
#define ADCprescaler_32 ADCSRA=_BV(ADPS2) | _BV(ADPS0)
#define ADCprescaler_64 ADCSRA=_BV(ADPS2) | _BV(ADPS1)
#define ADCprescaler_128 ADCSRA=_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0)
#define ADCprescaler_reset ADCSRA = ~_BV(ADPS2) & ~_BV(ADPS1) & ~_BV(ADPS0)
#define ADCchannel_1 //gewünschter Kannal z.B bei ATmega32 PINA0 - PINA7
#define ADCchannel_2 ADMUX|=_BV(MUX0) // bei nicht freilaufen muss ADCchannel_x vor
#define ADCchannel_3 ADMUX|=_BV(MUX1) // ADCstart kommen dann kann man mit getadc() der
#define ADCchannel_4 ADMUX= _BV(MUX1) | _BV(MUX0) // Adcwert des gewählten Kannals auslesen
#define ADCchannel_5 ADMUX|=_BV(MUX2)
#define ADCchannel_6 ADMUX= _BV(MUX2) | _BV(MUX0)
#define ADCchannel_7 ADMUX= _BV(MUX2) | _BV(MUX1)
#define ADCchannel_8 ADMUX= _BV(MUX2) | _BV(MUX1) | _BV(MUX0)
#define ADCchannel_reset ADMUX= ~_BV(MUX2) & ~_BV(MUX1) & ~_BV(MUX0)

uint16_t getadc(void)
{
while (ADCSRA & _BV(ADSC)) {}
return ADC;
}

toraX
14.02.2006, 18:14
Vielen Dank super_castle,

bevor ich mit meinem Programm weitermach werd ich das lieber mal gründlich studieren.

Gruss
Torsten

Sternthaler
14.02.2006, 23:37
Hallo toraX,
wenn ich das richtig sehe, dann holst du in der Funktion get_key() ja den ADC-Wert in die Variable i.
Diese Variable hast du in der Funktion LOKAL definiert. So weit ist es noch OK.
In deiner main()-Funktion hast du eine WEITERE LOKALE i-Variable angelegt.
Es sieht so aus, dass du erwartest, dass i aus get_key() in main() den gleichen Inhalt hat.
In main() bekommst du aber den Wert, den du in get_key() über 'return taste' zurückgibst. Da taste in get_key() aber nur die Werte 0xff, 4, 3, 2, 1, und 0 annehmen kann, sollte dein write() immer nur diese Werte senden.

Um den ADC-Wert aus get_key() nach main() zu bekommen kannst du eine GLOBALE Variable ausserhalb aller Funktion anlegen.
Z.B.:

unsigned int adc_wert;

int main (void)
{
to was;

atoi (adc_wert, buffer, 2);
write (buffer);

to was anderes;
}

unsigned int get_key(void)
{
tu hier auch was;

adc_wert = ADCH;

if (adc_wert < 36)
taste = 4;
...

return taste;
}

Sternthaler
14.02.2006, 23:50
Noch ein Hinweis zu:

Das einzige was auf meinem Bildschirm erscheint sind Unmengen an Buchstaben und Ziffern, ...

Da der Kontroller recht flott ist, und dein main() natürlich in einer Schleife läuft, sendest du ununterbrochen. Ist das tatsächlich gewünscht?
Wenn nein, solltest du evl. nur bei einer Änderung des ADC-Wertes senden, und dann evl. mit einem Trennzeichen zwischen den einzelnen Werten.
Beispielhaft:

unsigned int adc_wert;

int main (void)
{
unsigned int adc_wert_last = 0;

while ()
{
tu was;

if (adc_wert != adc_wert_last)
{
atoi (adc_wert, buffer, 2);
write (buffer);
write ("\n\r");
adc_wert_last = adc_wert;
}

mach hier weiter;
}
return 0;
}
[highlight=red:37fc6e945f][/highlight:37fc6e945f]

toraX
15.02.2006, 01:30
Oh man, auf das Problem mit der lokalen Variable hätte ich auch selber kommen können. ](*,)
Vielen Dank Sternthaler!!!

Ich hab nun eine globale Variable eingeführt, die auch nur ausgegeben wird wenn eine Taste gedrückt wird, aber leider hab ich immer noch ein kleines Problem mit der Ausgabe.
Es scheint aber zumindestens, dass jetzt 2 Tasten richtig ausgegeben werden und ich hab zum Glück lauter neue Anregungen und Tipps um weiterzumachen.

Nochmals vielen Dank für eure Hilfe.

clupus
15.02.2006, 22:43
Kannst du mal versuchen,

a) einige Werte, die der µC so sendet abspeichern und hier posten? (möglichst Hex-Format)
b) Etwa abschätzen, was rauskommen müsste. Weiter oben sind einige Beispiele gewesen, die zeigen sollten, was man da rechnen muss.

MfG
Christian

toraX
16.02.2006, 00:50
Hallo clupus,

ich hab vor ner halben Stunde gemerkt warum in meinem terminal so viele Zeichen produziert wurden.
Meine Ausgabe war ja in einer Schleife die andauernd Werte an den PC schickte falls ich eine Taste gedrückt halte. Nun hab ich, nach dem Vorschlag von Sternthaler, eine if-Bedingung eingebaut die mir nur Werte ausgibt wenn eine andere Taste gedrückt wird und jetzt funktionierts.

Kann es sein, dass wenn man zu schnell Werte über UART schickt, diese nicht mehr richtig angezeigt werden?

Hab mal ein anderes Prog geschrieben indem auch Werte in einer Schleife über den UART gesendet werden und da kam natürlich wieder die Überfüllung von Zeichen. Ich hab dann ein Verzögerung eingebaut um zu schauen wie schnell ich senden kann. Bei 20ms kommen die Ziffern zwar noch korrekt an, aber leicht verzögert.


Gruss Torsten

Gruss
Torsten

ruediw
16.02.2006, 03:11
Hallo clupus,

Kann es sein, dass wenn man zu schnell Werte über UART schickt, diese nicht mehr richtig angezeigt werden?

Gruss Torsten

Gruss
Torsten

Ja, selbstverständlich ist das so. Stell dir vor, du sendest die Zeichen
schneller als der Empfänger sie abholen kann. Dann holt der Empfänger nicht das ursprüngliche sondern das später eingetroffene ab.

Deshalb sollte man:
1. Die Zeichen in einer ISR abholen.
2. Diese sofort in einen ausreichend dimensionierten Zwischenspeicher
kopieren (Global !).
3.Möglichst kurz in der ISR verbringen.
4. Dafür sorgen dass die Baudraten von Sender und Empfänger möglichst perfekt übereinstimmen.

Die RS232 schnittstelle ist asynchron.Es gibt Mittel um Übertragungsfehler
möglichst auszuschliessen:
1. Hardware- oder Software Handshake.
2. Checksumme

Sternthaler
17.02.2006, 02:20
Freut mich zu hören, dass mein Vorschlag nun funktioniert.