Arduino: Varför 0 till 1023 vid analog ?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Arduino: Varför 0 till 1023 vid analog ?

Inlägg av jesse »

Hoppsan, tråden flöt iväg till att handla om potentiometrar och spänningsdelare...
Men eftersom jag skrev ett inlägg i början så ska jag följa upp det:
jesse skrev:om Aref = 5.0 volt, ska man då räkna med att Aref motsvaras av 1024 eller 1023 ?

dvs. ska det vara:

Kod: Markera allt

float volt;
int analog;
...
analog = ADC; // läs in från ADC
volt = analog * 5.00 / 1024;
eller ska det vara

Kod: Markera allt

float volt;
int analog;
...
analog = ADC; // läs in från ADC
volt = analog * 5.00 / 1023;
Divisorn ska vara 1024, annars blir det fel!

Ta exempelvis en tvåbitars AD-omvandlare som exempel, så är det lättare att se vad som händer:

Det binära talet, "rådatan" från omvandlaren kan då anta fyra värden, från 0 till 3.
(De båda bitarna kan bara anta dessa fyra värden: 00, 01, 10 och 11)

om U är referensspänningen så får vi dessa värden ut:

vid spänning mellan 0 och U/4 volt in får vi 00 ut.
vid spänning mellan U/4 och U*2/4 volt in får vi 01 ut.
vid spänning mellan U*2/4 och U*3/4 volt in får vi 10 ut.
vid spänning mellan U*3/4 och U volt in får vi 11 ut.

Då U = 5.0 får vi enligt formeln volt = analog * 5.00 / 4 :

00 --> 0.00 volt
01 --> 1.25 volt
10 --> 2.50 volt
11 --> 3.75 volt

Detta kan se väldigt fel ut i första anblicken, eftersom resultatet bara kan variera mellan 0 och 3.75 volt. Orsaken är att ADC:n alltid avrundar resultatet neråt. Hade vi dividerat med 3 istället så hade vi fått följande resultat:

00 --> 0.00 volt
01 --> 1.667 volt
10 --> 3.333 volt
11 --> 5.000 volt

Vilket ger en mer korrekt fördelning av resultatet, men ändå inte riktigt bra.
Jag anser alltså att det är division med fyra som gäller, men att man istället avrundar till närmaste heltal i stället för att avrunda neråt.

Så resultatet 00 , som alltså motsvarar en verklig inspänning nånstans mellan 0.00-1.25 volt borde ge svaret 0.625 volt - det är medeltalet mellan 0 och 1.25 och är det tal som bäst motsvarar den verkliga spänningen. Formeln blir då:

volt = (analog + 0.5) * 5.0 / 4

och resultaten blir:

00 --> 0.625 volt
01 --> 1.875 volt
10 --> 3.125 volt
11 --> 4.375 volt

På motsvarande sätt, för att få ett så korrekt resultat som möjligt från en 10-bitars ADC, så blir formeln:

volt = (analog + 0.5) * 5.0 / 1024

Man kan göra motsvarande beräkningar med enbart heltal (vilket rekommenderas) istället för med flyttal, men då måste man flytta decimalpunkten några steg, och t.ex. låta resultatet redovisas i millivolt istället för volt. Heltalsoperationer kräver att man tänker till några gånger extra, då alla delresutat avrundas neråt och man kan råka ut för owerflow, så det gäller att ha tungan rätt i mun här.

Det skulle kunna se ut så här:

int analog;
int volt;
...
analog = ADC;
volt = (((uint32_t)analog * 5000 + 2500) / 1024



-------------------------------------------------------
kommentar:
(uint32_t) är en så kallad "typecast"... Då 1023 * 5000 > 5.1 miljoner så får det inte plats i ett 16-bitars heltal (int). Därför omvandlas "analog" till ett 32-bitars heltal innan man utför multiplikationen. Annars skulle man få owerflow och det skulle bli fel. uint32_t är ett sätt att skriva att det är ett 32-bitars heltal och inget annat, och det används bl.a. när man programmerar AVR-processorer. För att förtydliga att det är 16-bitars heltal man arbetar med annars, kan man skriva uint16_t istället för int.
Användarvisningsbild
kimmen
Inlägg: 2042
Blev medlem: 25 augusti 2007, 16:53:51
Ort: Stockholm (Kista)

Re: Arduino: Varför 0 till 1023 vid analog ?

Inlägg av kimmen »

Normalt är väl snarare den här sortens överföringsfunktion för ADC:er:

http://www.eetasia.com/STATIC/ARTICLE_I ... 01Fig1.gif

Första omslaget sker alltså vid +1/2 LSB, och ett digitalt värde på 0 betyder att spänningen in ligger på 0 V +- 1/2 LSB.

Med 2 bitar och 5 V referens skulle trösklarna alltså bli:

Kod: Markera allt

      < 0.625V  -> 00 (under 0 V + 1/2 LSB)
0.625 - 1.875V  -> 01 (1.25 V +- 1/2 LSB)
1.875 - 3.125V  -> 10 (2.5 V +- 1/2 LSB)
> 3.125 V       -> 11 (över 3.75 V - 1/2 LSB)
Alltså vet man att den verkliga spänningen ligger inom +- 1/2 LSB från (avläst värde / upplösning) * referensspänning, så länge man ligger under en inspänning på 4.375 V (3 LSB + 1/2 LSB).

Detta passar bra ihop med DAC:ar som normalt ger en utspänning på \(V_{o} = V_{ref}\cdot\frac{digitalt~värde}{upplösning}\). Det ADC:n gör är att avrunda till närmaste kod enligt samma formel. Om man kopplar utgången från en DAC till en ADC med samma upplösning får man alltså samma kod ut ur ADC:n som man matar in i DAC:en, med en marginal på +-1/2 LSB på den analoga sidan.

Fast när upplösningen börjar bli hög så har det ju ingen större betydelse och dränks helt i de analoga felen hos ADC:n.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Arduino: Varför 0 till 1023 vid analog ?

Inlägg av jesse »

Attans också, det visste jag inte. :oops:

Mina ADC-kunskaper kommer från 80-talets början... och då fanns inte avrundning uppåt i hårdvaran. Men du har rätt, ADC:n på en AVR gör så som du beskriver (Var tvungen att ta fram databladet och läsa). Det framgår inte tydligt av texten, men man kan se det i diagrammet (som ser likadant ut som de du länkade till).

Då blir det helt korrekt alltså, att skriva så här : volt = analog * 5.0 / 1024;
Användarvisningsbild
kimmen
Inlägg: 2042
Blev medlem: 25 augusti 2007, 16:53:51
Ort: Stockholm (Kista)

Re: Arduino: Varför 0 till 1023 vid analog ?

Inlägg av kimmen »

Men jag måste säga att jag är osäker på om det alltid är så. :) Det jag senast kollade på var ADC:n på Freescale K10-mikrokontrollern, och där var det också med avrundning till närmaste värde.

Jag minns också att det sades i någon kurs på universitetet att det var det "normala" eller åtminstone ofta önskvärda sättet, eftersom kvantiseringsfelet blir +-1/2 LSB istället för 0-1 LSB åt ena hållet.

Bäst är väl att kolla databladen. :)
Skriv svar