PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : ATMEGA32 ADC Beschaltung für diff. +20dB Mesung



WarChild
28.04.2010, 00:26
Hi,

Für die Beleuchtung meines Bots wollt ich ne schöne Cree mit konstantstrom betreiben, aber ich habe ein Problem:
Die Spannung über meinem 0,51Ohm Messwiderstand welcher sich oberhalb des PWM gesteuerten Mosfets befindet wird von max. 1A durchflossen. Es ist also eine minimal wellige Spannung zwischen 0 und 0,5V über dem Widerstand zu messen. Problem ist nur, dass der Mosfet gegen masse schaltet und demzufolge das Potential am Messwiderstand im 20kHz takt um ca 7,4V angehoben wird. Einfach ADC0 und ADC1 über den Widerstand anzuschließen funzt nicht, da lösen die Überspannungsschuzdioden aus.
Muss ich also zwei Spannungsteiler (vor und hinter dem Messwiderstand gegen masse) verwenden, um die Spannungen zunächst in das richtige Potential zu verschieben und um dann zwischen den zwei Spannungsteilern differentiell zu messen?
Oder geht das auch einfacher?
Wo müssen Schutz oder Ausgleichskondensatoren bzw Antialiasingfilter hin, wenn ich differentiall messen möchte?

Vielen Dank für eure Tips!
mfg Sirvivor

WarChild
28.04.2010, 01:02
Also mein oben beschriebener Löungsvorschlag sieht so aus, funktioniert das so, wenn ich nastelle des kanal 2 meine ADC0 und ADC1 eingänge anschließe, stimmen die dimensionen der Bauteile so in etwa?

mfg WarChild alias "Sirvivor" *g*

WarChild
28.04.2010, 20:45
hmmm...

Eigentlich wollte ich keinen Monolog halten, aber nun denn:

Ich hab die schaltung heute getestet und alles funktioniert wunderbar. Ich verliere durch den zusätzlichen Spannungsteiler die halbe ADC Auflösung, weil ich aufgrund anderer Messungen, die ich nebenher mit AREF mache nicht die Vergleichsquelle wechseln möchte.

Nun aber zu meinem jetzt auftretendem Problem: Wenn ich das PWM auf 0 setze, und somit die Lampen eigentlich ausschalte, fließt durch die insgesammt vier Spannungsteiler, weil ich mit zwei Konstantstromquellen zwei Crees betreibe ein nicht unerheblicher Strom. Die haben einen Ersatzwiderstand von (2*10kOhm)/4=5kOhm. Bei 8,4V fließen also knapp 2mA "Leckstrom". Das ist sehr hübsch sichtbar. Wie groß kann ich die Widerstände maximal wählen, ohne dass das ADC Messergebnis verfälscht wird? am liebsten würde ich mit 100kOhm Teilen, aber im Tutorial steht, dass die zu mesende Quelle eine Impedanz von weinger als 20kOhm besitzen sollte. Da bleibt also nicht viel spiel. Wenn ich dann das signal noch mehr Puffern muss, damit der ADC genug Ladung bekommt, dann fürchte ich, dass es vor allem im niedrigen Strommessbereich zu hässlichen schwingungen kommt.
Also das ADC signal korrigiert das PWM Signal der Strom ändert sich, die Spannung am ADC hängt aber aufgrund der hohen Impedanz und großen Kapazität hinterher und wenn die änderung erkannt wird muss stark gegengeregelt werden un dann schwingt es halt.

bin für tipps offen:

mfg WarChild

Besserwessi
28.04.2010, 22:03
Bei der Differentiellen Messung darf der Eingang noch deutlich hochohmiger sein. Da sollten die 100 K schon noch gehen. Nach dem Datenblatt aber auch nicht viel mehr, vermutlich wegen Leckströmen am Eingang. Der Eingang des ADs selber sollte als CMOS Verstärker sehr hochohmig sein.

WarChild
29.04.2010, 10:52
vielen Dank!

Allmählich habe ich alle Probleme im Griff, nur sieht meine Schaltung mittlerweile recht spektakulär aus. Von ursprünglich 40 patzierten Bauteilen habe ich 5 entfernt und 18 hinzugefügt (Luftferdrahtung *g*) sowie 8 zusätzliche Leitungen. Ich denke jetzt ist es Zeit für eine Version 2 des Boards... 8-)

mfg WarChild

WarChild
09.11.2010, 23:32
Hallo Zusammen,

lang ist es her, da habt Ihr mir bei meinen ADC Problemen bei der Bike Control V1.0 geholfen, und jetzt bei Version 2.1 steh ich schon wieder auf dem Schlauch.

Folgendes Problem:
Ich erzeuge ein 15,6kHz PWM und versorge über zwei Step Down Wandler mit P-FET zweiHochleistungs-LEDs.

Es Funktioniert alles bis auf dass die gegenkopplung des PWMs duch den ADC mysteriöse Verhaltensmuster aufweist. Solange der Sollstrom unter 500mA (=512 ADC) liegt, wird dieser nur minimal von 10 bis 30 mA erhöht. überschreite ich diesen Punkt wird stark aufgedreht bis zu dem hier abgebildeten Betriebspunkt. Eigenartigerweise stecken in der ADC-Eingangsspannung hohe Peaks obwohl da ein 1,5kHz Tiefpass vorgeschaltet ist. Vielleicht gibt es da ein Masseproblem? habe ich Irgendwas im Code falsch gemacht, weil gerade 512 eine so charakteristische Zahl ist. 0-1A durch den 0,5 Ohm Widerstand entsprechen 0-0.5V mit 10-facher verstärkung und 10 Bit Auflösung ergibt dies bei 5V Referenz genau eine Proportionalität zwischen ADC und Strom von 1 zu 1mA.


Außerdem erreiche ich bei 16 MHz Controllertakt und 128 prescaler einen 125kHz internen ADC-Takt und bei 3 kanälen mit 8samples und einem verworfenem Messwert eine Frequenz von 320Hz je Kanal. Geht das nicht auch schneller? Derzeit starte ich nach jedem ADC interrupt den ADC neu, geht das nicht auch direkt fortlaufend?





// Programm for the Bike-Control V2.0 of S.Heidkamp//

//**************************************************//
//Includes: //
#include <avr/io.h> //
#include <avr/interrupt.h> //
#include <math.h> //
#include <stdint.h> //
#include <avr/eeprom.h> //
//
#define F_CPU 16000000UL //
#include <util/delay.h> //
// //
//**************************************************//

//INPUTS: A7=ADCBAT;A1=CUR1;A2=CUR2;D2=DOWN(INT0);D3=UP(INT1 );C3=LOCKED;B2=ALERT(INT2); D1=POWEROFF

volatile uint8_t SOUNDRISE=1;
volatile uint8_t WARNINGS=0;
volatile uint16_t COUNTDOWN1=0;
volatile uint8_t STATUS;

volatile uint16_t UBAT=740;
volatile uint16_t I1=100;
volatile uint16_t I2=100;
volatile uint16_t I_SET=100;

volatile uint8_t ADC_CHANNEL=0;
volatile uint8_t ADC_COUNT=0;
volatile uint16_t ADC_TMP=0;


volatile uint8_t GREEN_ENABLE=0;
volatile uint8_t GREEN_TIMER=0;
volatile uint8_t BACK_ENABLE=0;
volatile uint8_t BACK_TIMER=0;
volatile uint8_t SIDES_ENABLE=0;
volatile uint8_t SIDES_TIMER=0;

//**************************************************//
//Defines //
#define TAKT 16000000UL //
#define EEPROM __attribute__((section(".eeprom")))// new symbol
//INPUTS //
#define LOCKED PINC & (1 << PC3) // highactive
#define OFFSWITCH !(PIND & (1 << PD1)) // lowactive
#define VIBRATION PINB & (1 << PB2) // highactive
//OUTPUTS //
#define REDON PORTC &= ~(1<<PC7); // lowactive
#define REDOFF PORTC |= (1<<PC7); //
#define REDISON !(PINC & (1<<PC7)) //
#define GREENON PORTC &= ~(1<<PC6); // lowactive
#define GREENOFF PORTC |= (1<<PC6); //
#define GREENISON !(PINC & (1<<PC6)) //
#define BACKOFF PORTB &= ~(1<<PB1); // highactive
#define BACKON PORTB |= (1<<PB1); //
#define BACKISON PINB & (1<<PB1) //
#define SIDESOFF PORTC &= ~(1<<PC4); // highactive
#define SIDESON PORTC |= (1<<PC4); //
#define SIDESAREON PINC & (1<<PC4) //
#define POWEROFF PORTD &= ~(1<<PD0); // highactive
#define POWERON PORTD |= (1<<PD0); //
#define SIRENOFF TCCR2 &= ~(1<<COM20); //(OC2)
#define SIRENON TCCR2 |= (1<<COM20); //
#define LIGHTSOFF TCCR1A&= ~((1<<COM1A1)|(1<<COM1B1));//
#define LIGHTSON TCCR1A |= (1 << COM1A1)|(1 << COM1B1);//
#define NEARON TCCR1A |= (1 << COM1A1); //(OC1A) PD5
#define NEAROFF TCCR1A &=~(1<<COM1A1); //
#define FARON TCCR1A |= (1 << COM1B1); //(OC1B) PD4
#define FAROFF TCCR1A &=~(1<<COM1B1); //
#define VIBSENSOFF GICR &=~(1<<INT2); //
#define VIBSENSON GIFR |= (1<<INTF2);GICR |=(1<<INT2);//
//STATUS //
#define LIGHT 0 //
#define ALLCLEAR 1 //
#define NOISE 2 //
#define ATTENTION 3 //
#define ALERT 4 //
#define LOWBAT 5 //
#define SLEEP 6 //
//BRIGHTNESS
#define ULOW 0 // 50 mA
#define LOW 1 // 100 mA
#define MEDIUM 2 // 350 mA
#define HIGH 3 // 700 mA
#define PULSE 4 //1000 mA
//ADJUSTMENTS //
#define ADC_ADJUST -24 //
//**************************************************//

void tasks(void)
{
}


int main(void)
{
//Initiator

DDRA = 0b00000000; // Output Select
DDRB = 0b00000010; // Back
DDRC = 0b11010000; // RD GN SIDES
DDRD = 0b10110001;

PORTD |= (1<<PD5); // NEAR- & FAR-Lights are Lowactive so PD5&4
PORTD |= (1<<PD4); // should be set to Prevent LEDs from Damage

MCUCR |= (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00);//External Interrupts at rising edge
MCUCSR |= (1<<ISC2); // External Interrupt at rising edge
GICR |=(1<<INT0)|(1<<INT1); // Enable Buttons

POWERON;


cli();
//Timer 0
TCCR0 |= (1 << CS00)|(1 << CS02); // normal mode, prescaler 1024
TCNT0 = 256 - 156; // Timer0 mit 156 neu vorladen: 156*1024/4MHz = 9,984ms ~ 10ms
TIMSK |= (1 << TOIE0); // Timer0 Interrupt freigegeben

//Timer 1
TCCR1A |= (1 << WGM11)|(1 << WGM10); // 10-Bit PWM mode
TCCR1B |= (1 << CS10)|(1 << WGM12); // prescaler 1, freq. fixed, 16MHz/2^10=15,625kHz
OCR1A = 900; // Standard 10% as start Value
OCR1B = 900; //
TIMSK |= (1 << TOIE1); // enable overflow interrupt

//Timer 2
TCCR2 |= (1 << CS21)|(1 << WGM21); // Prescaler 8 ,PWM 1:1, Freq.=16Mhz/(2*OC2+1*Prescaler)
OCR2 = 50;

//ADC
ADCSRA|= 0b11101111; // ON/start/cont./Int.en./prescaler 128 ->125kHz
ADMUX |= 0b01000111; // AREF/right/8 unipol./ADC7
sei();

GREENOFF
REDOFF
//Mainprogramm
while(1)
{

if(UBAT==0) REDOFF;
while(OFFSWITCH)
{
LIGHTSOFF;
GREEN_ENABLE = 0;
BACK_ENABLE = 0;
SIDES_ENABLE = 0;
BACKOFF;
SIDESOFF;
SIRENOFF;
POWEROFF;}

//SIRENON;
//GREEN_ENABLE = 1;
BACK_ENABLE = 1;
SIDES_ENABLE = 1;
//NEARON;
//FARON;
//VIBSENSON;
LIGHTSON;
}

return 1;
}

SIGNAL(SIG_INTERRUPT0) //DOWN Button
{
if(I_SET >60) I_SET-=50;

GIFR |=(1<<INTF0);
}

SIGNAL(SIG_INTERRUPT1) //UP Button
{
if(I_SET <950) I_SET+=50;

GIFR |=(1<<INTF1);
}

SIGNAL(SIG_INTERRUPT2) //ALERT signal
{
GIFR |=(1<<INTF2);
}


SIGNAL(SIG_OVERFLOW0) //0,01s Timer Unit
{
TCNT0 = 256 - 156; //new Preload

if(SOUNDRISE){ //Alternating Noise
if(OCR2<180){
OCR2+=10;}
else{
SOUNDRISE = 0;}}
else
{
if(OCR2>30){
OCR2-=10;}
else{
SOUNDRISE = 1;}
}



if(GREEN_ENABLE) //Green LED Control
{
GREEN_TIMER++;
if(GREEN_TIMER == 0)
GREENON;
if(GREEN_TIMER == (UBAT>>2))
GREENOFF;

}

if(BACK_ENABLE) //Back LED Control
{
BACK_TIMER++;
if(BACK_TIMER == 25)
BACKON;
if(BACK_TIMER>=50){
BACKOFF;
BACK_TIMER = 0;}
}

if(SIDES_ENABLE) //Side LEDs Control
{
SIDES_TIMER++;
if(SIDES_TIMER == 20)
SIDESON;
if(SIDES_TIMER>=40){
SIDESOFF;
SIDES_TIMER = 0;}
}
}

SIGNAL(SIG_OVERFLOW1)
{

}

SIGNAL(SIG_ADC)
{

if(ADC_COUNT)
ADC_TMP+=ADC; //akkumulate by dropping first measurement

ADC_COUNT++; //increase measurement counter
if(ADC_COUNT > 8) //every 8 measurements
{
switch(ADC_CHANNEL) //store value and change channel
{
case (0):
UBAT=(ADC_TMP>>3);
ADMUX|= 0b01001001; // AREF/right/2-CH amp./10*ADC1->0
ADC_CHANNEL++;
break;
case (1):
I1=(ADC_TMP>>3);
ADMUX|= 0b01001101; // AREF/right/2-CH amp./10*ADC3->2
ADC_CHANNEL++;
if((I_SET+10>I1) && (OCR1A>500)){
REDOFF
GREENON
OCR1A--;}
else if((I_SET-10<I1) && (OCR1A<1022)){
REDON
GREENOFF
OCR1A++;}
break;
case (2):
I2=(ADC_TMP>>3);
ADMUX|= 0b01000111; // AREF/right/8 unipol./ADC7
ADC_CHANNEL=0;
if((I_SET+10>I2) && (OCR1B>500)){
OCR1B--;}
else if((I_SET-10<I2) && (OCR1B<1022)){
OCR1B++;}
break;
}
ADC_COUNT=0;
ADC_TMP=0;
}

ADCSRA|= (1<<ADSC); //start next Conversion
}








Hier ein Paar Dokumente:

Danke für eure Hilfe!

Besserwessi
10.11.2010, 18:48
Man kann den ADC auch durchlaufen lassen. Das bringt aber nur relativ wenig Geschwindigkeitsvorteil. Dafür wird das zuordnen der Kanäle schwieriger. Bei etwas Verzögerung vor der ISR besteht auch die Gefahr, das man mit dem Umschalten des MUX genau zur kritischen Zeit macht, dann wenn die nächste Konversion gerade gestartet wurde.

Wenn es schneller werden soll, könnte man ggf. die Zahl der Mittlungen reduzieren oder den ADC-Takt auf 250 kHz erhöhen. Wenn man darauf verzichten will, noch Werte zu verwerfen, müsste man ggf. den Kondensator des Tiefpass noch vergrößern ( > 15 nF). Ob das mit verstärkung auch geht, weiss ich aber nicht. Ohne Verstärkung geht es jedenfalls auch ohne das man Werte verwirft.

Ein Fehler ist mir nicht aufgefallen, aber es fehlt auch der Teil wo der ADC wert im Hauptprogramm weiter benutzt wird.

WarChild
10.11.2010, 21:03
@Besserwessi
Der ADC Wert wird nur gespeichert und direkt im Interupt verwendet um das Tastverhältnis des Timer 2 anzupassen.
Hier liegt auch mein Problem:

Ich konnte den Fehler jetzt stark eingrenzen. Wenn ich nur einen Kanal ausführen lasse, dann funkioniert es. Abgesehen von einem Faktor 2 den ich mir nicht erklären kann. Wenn 500mV am Shunt anliegen erhalte ich anscheinend 512 als ADC wert. Bei 10 facher Verstärkung und 5V referenz sollten es aber 1024 sein. Aber ansonsten funktioniert jeder Kanal für sich. Erst wenn ich im Interupt den Kanal wechseln will, klappt nichts mehr.

Das Problem liegt in den drei ADMUX Befehlen im ADC Interrupt.
Darf ich vielleicht nicht das ganze admus neu schreiben sondern nur die entsprechenden Bits toggeln lassen? Ich kann mir das Problem nicht erklären.



SIGNAL(SIG_ADC)
{
if(ADC_COUNT) //drop first Measurement
ADC_TMP+=(ADC<<1); //eigenartigerweise Anpassung der ADC WERTE

ADC_COUNT++;

if(ADC_COUNT>1)
{
switch(ADC_CHANNEL)
{
case(0): // BATTERY
UBAT=ADC_TMP;
ADC_CHANNEL=1;
ADMUX|= 0b01001001; // AREF/right/2-CH amp./10*ADC1->0
break;

case(1): // NEAR LED
I1=ADC_TMP;
if((I_SET>I1) && (OCR1A>400)){
OCR1A--;}
else if((I_SET<I1) && (OCR1A<1022)){
OCR1A++;}
ADC_CHANNEL=2;
ADMUX|= 0b01001101; // AREF/right/2-CH amp./10*ADC3->2
break;

case(2): // FAR LED
I2=ADC_TMP;
if((I_SET>I2) && (OCR1B>400)){
REDOFF
GREENON
OCR1B--;}
else if((I_SET<I2) && (OCR1B<1022)){
REDON
GREENOFF
OCR1B++;}
ADC_CHANNEL=0;
ADMUX|= 0b01000111; // AREF/right/8 unipol./ADC7
break;
}
ADC_COUNT=0;
ADC_TMP=0;
}

ADCSRA|= (1<<ADSC); //start next Conversion*/
}

WarChild
15.11.2010, 14:47
Fehler gefunden:
Ich habe :


ADMUX|= 0b01001001; // AREF/right/2-CH amp./10*ADC1->0

geschrieben.
muss aber kein odergleich sondern ein istgleich sein. Also



ADMUX= 0b01001001; // AREF/right/2-CH amp./10*ADC1->0


mfg WarChild