PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] ADC Sharp Ir Problem



Martinius11
07.10.2011, 16:34
Hi,

ich arbeite immer noch sehr fleisig an meinen Smartinius und versuche nun schon seit einiger
Zeit an der Sensor auslesung via ADC aber das will nicht recht klappen.
Ich glaube es liegt daran das ich mit Kommerzahlen rechen.
Zur der Brechnung kurz
Die Entfernung ist proprtional zur ausgegebenen Spannung =>

D = A/(X-B)

D ist die Entfernung X ist der Ausgabewert des Sensors A ist die Steigung der Kurve A/X B ist der Offset der Kurve
Da ich bei beiden Sensoren leichte Unterschiede messe habe ich B auf links und rechts aufgeteilt.
BL und BR.

Für das ADC benutzte ich AVCC und einen Vorteiler von 64 ich hoffe
der Rest ist aus dem leicht kommentierten Code zu erkennen



/*
* SMARTINIUS.c
*
* Created: 26.07.2011 00:17:22
* Author: Martinius
*/
#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

const ON = 1; //andere Werte
const OFF = 0;
const Left = 2;
const Right = 1;
const Enable = 1;
const Disable = 0;
const Reset = 2;

#define A 11.925
#define BL 0.660
#define BR 0.610
#define ADCconst 00.04883

void Setup(void){ // Stellt den Controller für das nachfolgende Programm ein
// muss immer als erstes aufgerufen werden.
DDRC |= (1<<PC7); // Status LED
DDRB |= (1<<PB0); // IR_Eable
DDRA &= ~(1<<PA0); // IR_Left
DDRA &= ~(1<<PA1); // IR_Right
ADMUX |= (1<<REFS0);//Reverrenzspannungsquelle des ADC wird festgelegt
ADCSRA = (1<<ADPS1) | (1<<ADPS2); // Frequenzvorteiler
ADCSRA |= (1<<ADEN); // ADC aktivieren
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
}

void Status_LED(uint8_t Status){
if(Status == 1){
PORTC |= (1<<PC7);
}
if(Status == 0){
PORTC &= ~(1<<PC7);
}
}
uint16_t get_Ir_Distance (uint8_t Dir){ // Distance in mm
if(Dir==1){//Right
uint16_t x;
ADMUX = (ADMUX & ~(0x1F)) | (1 & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
x = ADCW;
x = x*ADCconst;
x = x-BL;
x = A/x;
x = x*10
return x; // ADC auslesen und zurückgeben
}

if(Dir==2){//Left
uint16_t x;
ADMUX = (ADMUX & ~(0x1F)) | (0 & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
x = ADCW;
x = x*ADCconst;
x = x-BL;
x = A/x;
x = x*10;
return x;

}
}
void Ir_Enable(uint8_t Status){
if (Status == 1){
PORTB |= (1<<PB0);
}
if(Status == 0){
PORTB &= ~(1<<PB0);
}
}
int main(void)
{
Setup();

Ir_Enable(ON);

while(1){
drive_ahead();
if(get_Ir_Distance(Right)<200){
Status_LED(ON);
}
if(get_Ir_Distance(Left)< 200){
Status_LED(ON);
}
}
}

mfG _Martinius_

BMS
07.10.2011, 17:34
Hallo,
Also die Multiplikation von x mit einer (kleinen) Fließkommazahl wird sicher keine schönen Werte liefern.
Die Variable x sollte dann schon vom Typ float sein.



const ON = 1; //andere Werte
const OFF = 0;
const Left = 2;
const Right = 1;
const Enable = 1;
const Disable = 0;
const Reset = 2;

Da fehlt doch der Typ der Variablen, also z.B. int ???

Und get_Ir_Distance kann im Moment auch mal keinen return-Wert haben, wenn Dir!=1 && Dir!!=2
Liefert der Compiler da keine Warnung ?

Bei ein paar Methoden kannst du die Parameter noch auf bool umstellen und ggf als inline-Methode
deklarieren, das spart etwas Programmspeicher und Ausführungszeit (Status_Led, IR_Enable).

Grüße,
Bernhard

Martinius11
07.10.2011, 18:01
const ON = 1; //andere Werte
const OFF = 0;
const Left = 2;
const Right = 1;
const Enable = 1;
const Disable = 0;
const Reset = 2;

Da fehlt doch der Typ der Variablen, also z.B. int ???

Und get_Ir_Distance kann im Moment auch mal keinen return-Wert haben, wenn Dir!=1 && Dir!!=2
Liefert der Compiler da keine Warnung ?

Bei ein paar Methoden kannst du die Parameter noch auf bool umstellen und ggf als inline-Methode
deklarieren, das spart etwas Programmspeicher und Ausführungszeit (Status_Led, IR_Enable).

Bernhard

Also ich hab bis jetzt immer const so gesetzt hat auch bis jetzt immer funktioniert
aber ich lasse mich gerne aufklären wie man es richtig macht

Und zu get_Ir_Distance also der Complier liefert keine warnungen aber es ist auch nicht forgesehn etwas anderes zu übergeben auser 1 und 2 aber ich werde ein return ergänzen.

Martinius11
08.10.2011, 13:35
Ich habe jetzt das Program editiert aber es will immer noch nicht klappen jetzt leuchtet die LED dauerhaft:


/*
* SMARTINIUS.c
*
* Created: 26.07.2011 00:17:22
* Author: Martinius
*/
#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

const int FWD = 1; // Definition der Richtungswerte
const int STOP = 0;
const int BWD = 2;
const int ON = 1; //andere Werte
const int OFF = 0;
const int Left = 2;
const int Right = 1;
const int Enable = 1;
const int Disable = 0;
const int Reset = 2;
const float A = 11.925;
const float BL = 0.660;
const float BR = 0.610;
const float ADCconst = 00.04883;

void Setup(void){ // Stellt den Controller für das nachfolgende Programm ein
// muss immer als erstes aufgerufen werden.
DDRC |= (1<<PC7); // Status LED
DDRB |= (1<<PB0); // IR_Eable
DDRA &= ~(1<<PA0); // IR_Left
DDRA &= ~(1<<PA1); // IR_Right
ADMUX |= (1<<REFS0);//Reverrenzspannungsquelle des ADC wird festgelegt
ADCSRA = (1<<ADPS1) | (1<<ADPS2); // Frequenzvorteiler
ADCSRA |= (1<<ADEN); // ADC aktivieren
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
}

void Status_LED(uint8_t Status){
if(Status == 1){
PORTC |= (1<<PC7);
}
if(Status == 0){
PORTC &= ~(1<<PC7);
}
}
uint16_t get_Ir_Distance (uint8_t Dir){ // Distance in mm
if(Dir==1){//Right
float x;
ADMUX = (ADMUX & ~(0x1F)) | (1 & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
x = ADCW;
x = x*ADCconst;
x = x-BL;
x = A/x;
x = x*10
return x; // ADC auslesen und zurückgeben
}

if(Dir==2){//Left
float x;
ADMUX = (ADMUX & ~(0x1F)) | (0 & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
x = ADCW;
x = x*ADCconst;
x = x-BL;
x = A/x;
x = x*10;
return x;

}
return 0;
}
void Ir_Enable(uint8_t Status){
if (Status == 1){
PORTB |= (1<<PB0);
}
if(Status == 0){
PORTB &= ~(1<<PB0);
}
}
int main(void)
{
Setup();

Ir_Enable(ON);

while(1){
drive_ahead();
if(get_Ir_Distance(Right)<2000){
Status_LED(ON);
}
if(get_Ir_Distance(Left)< 2000){
Status_LED(ON);
}
}
}

BMS
08.10.2011, 14:14
Hallo,
du schaltest die LED auch nirgends im Programm wieder aus, deswegen leuchtet sie dauerhaft ;)
Also irgendwo sollte da noch ein Status_LED(OFF); in die while-Schleife.
So z.B.

while(1)
{
drive_ahead();
if(get_Ir_Distance(Right)<2000)
{
Status_LED(ON);
}
else if(get_Ir_Distance(Left)< 2000)
{
Status_LED(ON);
}
else
{
Status_LED(OFF);
}
}
Grüße,
Bernhard

EDIT:
Und in uint16_t get_Ir_Distance solltest du die return-Werte noch in einen int umwandeln, d.h. return int(x);

Martinius11
08.10.2011, 14:15
ja das weis ich schon ich meinte ja selbst wenn ich den roboter von mir weg halte einen reset auslöse leuchtet die
LED auch sofort obwohl kein hinderniss da ist und sogar wenn ich die sensoren ausstecke

BMS
08.10.2011, 14:24
Kannst du die "Rohwerte" vom AD-Wandler irgendwie ausgeben?
Also z.B. über Display anzeigen oder über serielle Schnittstelle an PC übertragen?
Wenn du die Sensoren aussteckst, liegen die Analogeingänge in der Luft,
der kann dann irgendwelche Werte ausgeben.
Aber die LED an sich lässt sich ein- und ausschalten?
Also z.B. per Programm blinken lassen geht?

Ansonsten: Bist du dir sicher, dass deine Konstanten passen?
Grüße, Bernhard

Martinius11
08.10.2011, 14:30
Also das Uart hat schon ne schnittstelle aber die software ist noch nicht fertig
die LED kann ich ein und ausschalten
Und die sensoren sind dann komplett abgetrennt
Und die Konstanten stimmen ich habe mal via multimeter nach gerechnet und es würde stimmen.

Martinius11
08.10.2011, 17:09
Hat den niemand eine idee was verkehrt sein könnt?

BMS
08.10.2011, 18:16
Wie gesagt, schau mal, dass du irgendwie die Werte angezeigt kriegst. UART geht relativ zügig.
Auf dem PC braucht man dann nur ein Terminal und sieht alles was der AVR geschickt hat.
Sonst ist das nicht nachvollziehbar was da genau läuft, da fischt man zu lange im Trüben.
Notfalls die LED blinken lassen und die Blinkfrequenz abhängig vom gemessenen Abstand machen.
Dann sieht man überhaupt mal, ob sich die Werte auch ändern.
Die Analogeingänge an sich funktionieren, oder?
Schon mal etwas anderes angeschlossen als einen Sharp-Sensor?
Grüße, Bernhard

oberallgeier
08.10.2011, 18:48
... dass du irgendwie die Werte angezeigt ...In ähnlichen Fällen, aber GENAU bei meinen ersten Sharp-Distanzsensor-Erfahrungen, hatte ich nen Servo als "Zeigerinstrument" vom Sharp aus gesteuert. Wenn ich ne 8-Bit-Wandlung nehme, kann ich den Servo ziemlich easy direkt bedienen. Ich messe allerdings nicht selten mit dem DMM oder Oskar nach, ob die analoge Ausgabe des Sharps glaubhaft ist . . . .

Martinius11
08.10.2011, 19:16
Ich bekomme das UART einfach nicht zum laufen hier ist mal der Code:



/*
* SMARTINIUS.c
*
* Created: 26.07.2011 00:17:22
* Author: Martinius
*/
#define F_CPU 16000000
#define BAUD 9600UL // Baudrate
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // clever runden
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.

#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
char s[7];
const int FWD = 1; // Definition der Richtungswerte
const int STOP = 0;
const int BWD = 2;
const int ON = 1; //andere Werte
const int OFF = 0;
const int Left = 2;
const int Right = 1;
const int Enable = 1;
const int Disable = 0;
const int Reset = 2;
const float A = 11.925;
const float BL = 0.660;
const float BR = 0.610;
const float ADCconst = 00.04883;

void Setup(void){ // Stellt den Controller für das nachfolgende Programm ein
// muss immer als erstes aufgerufen werden.
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;

UCSRB |= (1<<TXEN); // UART TX einschalten
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // Asynchron 8N1

DDRC |= (1<<PC7); // Status LED
DDRB |= (1<<PB0); // IR_Eable
DDRA &= ~(1<<PA0); // IR_Left
DDRA &= ~(1<<PA1); // IR_Right
ADMUX |= (1<<REFS0);//Reverrenzspannungsquelle des ADC wird festgelegt
ADCSRA = (1<<ADPS1) | (1<<ADPS2); // Frequenzvorteiler
ADCSRA |= (1<<ADEN); // ADC aktivieren
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
}

void Status_LED(uint8_t Status){
if(Status == 1){
PORTC |= (1<<PC7);
}
if(Status == 0){
PORTC &= ~(1<<PC7);
}
}
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}

UDR = c; /* sende Zeichen */
return 0;
}
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen(Terminator)" */
uart_putc(*s);
s++;
}
}
uint16_t get_Ir_Distance (uint8_t Dir){ // Distance in mm
if(Dir==1){//Right
float x;
ADMUX = (ADMUX & ~(0x1F)) | (1 & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
x = ADCW;
x = x*ADCconst;
x = x-BL;
x = A/x;
x = x*10
uart_puts( itoa( x, s, 10 ) );
return x; // ADC auslesen und zurückgeben
}

if(Dir==2){//Left
float x;
ADMUX = (ADMUX & ~(0x1F)) | (0 & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten
x = ADCW;
x = x*ADCconst;
x = x-BL;
x = A/x;
x = x*10;
uart_puts( itoa( x, s, 10 ) );
return x;

}
return 0;
}
void Ir_Enable(uint8_t Status){
if (Status == 1){
PORTB |= (1<<PB0);
}
if(Status == 0){
PORTB &= ~(1<<PB0);
}
}
int main(void)
{
Setup();

Ir_Enable(ON);

while(1){
drive_ahead();
if(get_Ir_Distance(Right)<2000){
Status_LED(ON);
}
if(get_Ir_Distance(Left)< 2000){
Status_LED(ON);
}
}
}

Martinius11
08.10.2011, 21:25
Ich habe das Problem gefunden da ich den Sensor immer erst eingeschaltet habe beim reset gab es immer einen sehr kleine spannungs einbruch den
das Programm nätürlich sofort erkant hat.