hjælp til servo kontrol med timer (Læst 129975x)

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #150 Dato: Oktober 10, 2011, 03:10:00 »
Advarsel: Meget langt indlæg.

Citér
Giver det bedre mening nu ? :)

Jeg forstår godt metoden med at finde værdien (eks 71), men aner ikke hvordan jeg skal kode det, og får jeg ikke en masse floating med alle de divisioner?
Hvis du får floating point kode med de viste divisioner, har du en dårlig compiler, for den skal for det første selv reducere konstante tal, dvs. tal, som den kan regne værdien ud af på forhånd.
Compileren kan fx. reducere udtrykket
Kode:
uint16_t plads;

plads = ((4 + 12) / 2) * 3.
Dette kan du nemlig også selv reducere.

Så uanset om du skriver ovenstående, eller...
Kode:
plads = 24;
...vil resultat-koden være nøjagtig den samme.

Men compileren kan ikke reducere udtrykket
Kode:
uint16_t plads;

plads = ((kaniner + elefanter) / marsvin) * salater.
Dog kan intelligente compilere reducere følgende:
Kode:
uint16_t kaniner;
uint16_t elefanter;
uint16_t marsvin;
uint16_t salater;
uint16_t plads;

plads = ((kaniner + elefanter) / marsvin) * salater;

Det kan være svært i starten, at gennemskue hvad der foregår i en compiler, men efterhånden som du bruger den, vil du få en bedre fornemmelse af 'hvordan de tænker'. ;)

Bemærk: Koden til at finde disse konstante tal er kun beregnet til dip-switches og knapper med modstande, ikke potmetrene.
Citér

værdien i nedenstående er så konstanten #define ADC2_CALIBRATION; eller hva?   

Denne kode er til kalibrering af eks adc2
værdien value hvor kommer den fra? for hvis getADCvalue er en aflæsning af spænding (som du skrev for nogle posts siden) så skal jeg jo have en omregning til 10 bit værdien et eller andet sted.

Inden nævnte kode, skal du bare aflæse ADC'en på denne måde...
Kode:
value = getADC#value();
if(value >= 0 && value < ((0 + 127) / 2))
{
  /* setting 1 */
}
else ...


Citér
Skal det så gøres ved en beregning der f.eks ser således ud
Kode:
 
void calculation (int16_t value)
{
calc = 0,2048 * getADC2Value;
return(calc);
}
(de 0,204 er en omregning af 1024 / 5000 til en konstant, for at undgå divisioner i programmet til gengæld er det et komma tal som jo også er noget skidt)

I ovenstående kode returnerer du void (ingenting), hvilket vil sige at du kan ikke returnere et tal.
calc er ikke defineret. Hvis calc er en float, så vil du få floating-point kode, hvilket du ikke ønsker.
value bliver ikke brugt til noget, den kan udelades fra argumenterne.
getADC2Value skal have paranteser på, for ellers får du adressen på funktionen (dvs. du får at vide hvor i hukommelsen funktionen ligger).
Derimod, hvis calc er et heltal, vil du altid få resultatet 0, da 0.2048 vil blive rundet ned af compileren før der ganges med getADC2Value().

Denne stump burde kunne virke:
Kode:
 
uint16_t calculation()
{
    uint32_t calc;
    /* simpel: */
    calc = (5000 * getADC2Value()) >> 10;
/* eller med op/nedrunding: */
/*  calc = (((5000 * getADC2Value()) >> 9) + 1) >> 1; */
    return(calc);
}

Desværre kommer vi helt op på 32 bits midlertidigt, men det må så være det, det er.
Her får du så resultatet i millivolt.

Men du kunne også gøre det omvendte, nemlig regne millivolt om til ADC-værdi:

Kode:
if(value > (1023 * 1500 / 5000))
{
    /* value is 1.5V */
}

På den måde vil din kode blive mindre og hurtigere, fordi compileren kan reducere de konstante tal.
Bemærk: Vi ganger før vi dividerer, for at vores resultat skal blive så præcist som muligt.
Dette gør vi pga. at vi regner med heltal.
1023 / 5000 = 0.
1023 * 1500 = 1534500
1534500 / 5000 = 306

Man kan lave et hjælpeværktøj til dette...
#define MILLIVOLT2ADC(a) (1023 * (a) / 5000)

Eller endnu bedre, et, som kan runde op eller ned til nærmeste heltal:

#define MILLIVOLT2ADC(a) (((1023 * (a) / 2500) + 1) / 2)

Her lægger jeg 1 til, og dividerer med 2; det er det samme som hvis man kunne lægge 0.5 til det færdige resultat.
Man kan bruge bitskift til at dividere med 2:

#define MILLIVOLT2ADC(a) (((1023 * (a) / 2500) + 1) >> 1)

...Så de sidste 2 MILLIVOLT2ADC vil give de samme resultater.

Citér
og i tilfælde af at det skulle være så heldigt at være denne måde hvorpå jeg skal gøre det. bør det så gøres i min adc.c eller i min dosing.c

Jeg anbefaler at lave det i dosing.c, for adc.c er stort set kun aflæsning. Prøv at forestil dig det som om adc.c bruges i 10 andre programmer der har med ADC at gøre.
-På den måde vil det være lettere for dig at se hvor du bør lægge tingene.


Citér
Jeg beklager og er ked af hvis jeg er lidt tung i det her lige nu (hader at føle mig så tabt bag vognen)
Helt iorden. ;)

Citér
og lige et spørgsmål mere, jeg er ikke sikker på om du så det jeg skrev i min tidligere post så tillader mig lige at gentage spørgsmålet her
kan jeg ikke splitte dippen op i 2 og så køre antal doseringer pr dag ind på pa1 og repeats ind på pa4 hvis jeg så bruger 300 k og 150k samt en 150k fra pb2 og en fra pa1 til gnd, så får jeg da en foreskel i værdier fra 1.6 volt til 3 og kan nøjes med 4 cases for hver funktion istedet for 16 cases på 1 funktion ??

Det kan du godt, men modstandenes værdier bør ikke ændres. 150K og 300K er nok for store.
Du bør normalt bruge modstande under 10K, da støj ellers kan påvirke dit kredsløb.
Jeg anbefaler stadig at køre med de nævnte værdier: 0.5K, 1K, 2K, 4K.

Jeg vil tro at hvis du sætter 1K på den ene switch og 2K på den anden switch og 2K på output (istedet for 3k9), så ville det kunne fungere. -Og så lave to stk af dem i alt.

Jeg tror du har fejl i dine værdier (0 + 127), (128 + 191), osv.
Grunden er at vores modstands-netværk ikke leverer linære værdier.
Derfor kan vi ikke bare 'dividere med 2' hele vejen; ellers havde der været en simplere metode. ;)

Men i stedet for den lange 'if/else if' kode, kan du gøre koden kortere, ved at aflæse en tabel.
Udførslen tager et par ekstra clock-cycles (hvilket ikke betyder noget i dette tilfælde).
Mit skud er at denne kode vil fylde lidt over 48 bytes, hvilket er en hel del kortere end tidligere nævnte kode:

Følgende #include bør indsættes i toppen af dosing.c, fx. nede under #include <avr/io.h>:
Kode:
#include <avr/pgmspace.h>

Dernæst, et eller andet sted, du synes er passende:

Kode:
uint8_t calculateSetting(uint16_t aValue)
{
    const uint16_t convTab[] PROGMEM = { ((1023 + 523) / 2), ((523 + 349) / 2), ((349 + 262) / 2), ((262 + 209) / 2),
        ((209 + 174) / 2), ((174 + 149) / 2), ((149 + 131) / 2), ((131 + 116) / 2), ((116 + 105) / 2), ((105 + 95) / 2),
        ((95 + 87) / 2), ((87 + 80) / 2), ((80 + 75) / 2), ((75 + 70) / 2), ((70 + 65) / 2), 0 }; /* 3K9, 5% */
    uint8_t i;

    i = 0;
    while(aValue <= pgm_read_word(convTab[i]))
    {
        i++;
    }
    return(i);
}

Tabellen er beregnet efter VCC=5.00V, men den er checket op ad VCC=5.05V med en 3K9, 5% modstand (3k86), og værdierne er ikke langt fra hinanden.

Include-filen pgmspace.h indeholder macroer, som gør det nemt at læse tabeller der ligger i Flash-memory, frem for at de først lægges over i RAM. Dette sparer både RAM og reducerer kode-størrelsen.

... Koden er afprøvet som program på min Mac, og giver korrekte resultater.
Tabellen er baseret på en 3K9 modstand.

Her er en tabel, som er baseret på en 4K/0% modstand. Du kan fx. bruge 2 stk. 2K modstande på 1% ved udgangen:
Kode:
    const uint16_t convTab[] PROGMEM = { ((1023 + 512) / 2), ((512 + 341) / 2), ((341 + 256) / 2), ((256 + 205) / 2), 
        ((205 + 171) / 2), ((171 + 146) / 2), ((146 + 128) / 2), ((128 + 114) / 2), ((114 + 102) / 2), ((102 + 93) / 2),
        ((93 + 85) / 2), ((85 + 79) / 2), ((79 + 73) / 2), ((73 + 68) / 2), ((68 + 65) / 2), 0 }; /* 4K, 0% */

Tabellen er beregnet efter VCC=5.00V, men den er checket op ad en 4K, 1% modstand.


Det er bedst at bruge de beregnede tabeller, frem for en tabel baseret på målte værdier, fordi hvis du har en modstand der ligger på -5%, og du baserer værdien på denne, vil du få fejl, den dag du bruger en modstand på +5%. Er tabellen baseret på +0%, vil du undgå fejlen.

Her er et par små formler, som du kan bruge til at beregne tabellerne:

voltage = VCC * 1000 / ((r1 + r2) / 8) / (setting + 1)

* 1000 er for at lave volt om til millivolt
r1 er din udgangs-modstand i ohm, fx. 3900.
r2 er værdien på den største modstand der sidder til en switch i ohm, fx. 4000.
setting er et tal mellem 0 og 15.
Her går jeg selvfølgelig ud fra, at modstandsværdierne bliver halveret efterhånden som der sættes flere switches på.

Formlen kan skrives om til en lidt mere lommeregner-venlig udgave:
voltage = VCC * 1000 * 8 / (r1 + r2) / (setting + 1)

Du kan så omregne voltage til ADC værdi:
adcValue = 1023 * voltage / 5

Formlerne kan reduceres yderligere og de kan også kombineres til én formel der giver dig adcValue.

Citér
og tusind tak for arbejdet med dip readings

Velbekomme. :)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #151 Dato: Oktober 10, 2011, 16:19:06 »
oki går igang med det samme  :P.
Citér
Hvis du får floating point kode med de viste divisioner, har du en dårlig compiler
Den ene sætning gjorde mig meget klogere på compilers ha ha. troede at compileren bare lavede c om til maskinkode og skænkede ikke det en tanke at den også går ind og reducerer og udregner konstanter så når jeg skrev 200/11 eks så troede jeg at den smed samme regnestykke ind i maskinkoden og ikke resultatet afrundet til heltal  ;D

Jeg har lavet dips som du foreslog i diagrammet på forrige side. har dog brugt 2k modstande til at lave 4k fra +5 til dips og det har jeg også der hvor der skal være 4k fra dip. havde også en 500 ohm så det skulle være på plads nu. nu vil jeg så lige måle igennem. Min strømforsyning leverer 5.28V så det skal der jo også tages højde for, altså at det ikke er 5v rent. skal jeg så ikke regne med 5280 mv istedet for 5000 rent ?
 
Hov hvor skal ADC benet ind i det diagram du har lavet? før havde jeg fra dip til adc og adc til jord via en modstand. den er nu sløjfet og sat over på +5v siden men jeg kan jo ikke kortslutte adc til jord med en lus, så kan jeg hvertfald ikke måle fra adc til jord
Tror jeg fandt du af det adc benet skal på efter 3k9 modstanden fra +5v og så skal adc benet ikke føres til jord. er det ikke sådan ? gg var det ikke noget med pull up eller pull down modstand ?

« Senest Redigeret: Oktober 10, 2011, 17:14:59 af jascore »

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #152 Dato: Oktober 10, 2011, 23:14:55 »
oki går igang med det samme  :P.
Citér
Hvis du får floating point kode med de viste divisioner, har du en dårlig compiler
Den ene sætning gjorde mig meget klogere på compilers ha ha. troede at compileren bare lavede c om til maskinkode og skænkede ikke det en tanke at den også går ind og reducerer og udregner konstanter så når jeg skrev 200/11 eks så troede jeg at den smed samme regnestykke ind i maskinkoden og ikke resultatet afrundet til heltal  ;D
Det gjorde C-compilere engang, så du er faktisk ikke helt forkert på den.
Frem til 1988 lavede man C-compilere, så de lavede rigtig god klodset kode, hvor man kunne disassemble koden og derefter nemt fjerne 1/4 af alle de ligegyldige ting. Men i dag er det vældig svært at slå compilernes optimeringer, fordi der netop er programmører der har siddet og lavet avancerede optimerings-algoritmer og kigget på resultatet. Ofte kan det ikke betale sig at optimere en compiler's output i dag.

Men nogle gange får man en skør idé, til hvordan man kan gøre lave et bestemt stykke kode i maskinkode; og netop dér kan C-compileren muligvis ikke bruges. Dette gælder fx. noget som har præcis timing (her snakker vi nanosekunder).

Citér
Jeg har lavet dips som du foreslog i diagrammet på forrige side. har dog brugt 2k modstande til at lave 4k fra +5 til dips og det har jeg også der hvor der skal være 4k fra dip. havde også en 500 ohm så det skulle være på plads nu. nu vil jeg så lige måle igennem. Min strømforsyning leverer 5.28V så det skal der jo også tages højde for, altså at det ikke er 5v rent. skal jeg så ikke regne med 5280 mv istedet for 5000 rent ?

Det bedste ville være at sætte din egen spændings-regulator på, så du véd at du har en fast 5V spænding, som ikke ændrer sig.
Men da denne Atmel chip måler i forhold til VCC, burde du ikke få noget vrøvl. :)
Husk at Atmel chippen ikke kan tåle mere end 5.5V (dette står beskrevet i databladet - husk at downloade dette, hvis du ikke allerede har det).

Citér
Hov hvor skal ADC benet ind i det diagram du har lavet? før havde jeg fra dip til adc og adc til jord via en modstand. den er nu sløjfet og sat over på +5v siden men jeg kan jo ikke kortslutte adc til jord med en lus, så kan jeg hvertfald ikke måle fra adc til jord
Tror jeg fandt du af det adc benet skal på efter 3k9 modstanden fra +5v og så skal adc benet ikke føres til jord. er det ikke sådan ? gg var det ikke noget med pull up eller pull down modstand ?

Lige præcist. Den 3k9 modstand (som bør være så tæt på 4k00 som muligt), kaldes en pull-up, fordi den er sat til VCC. Var den sat til GND, blev det til en pull-down. :)
Og jo, det er netop på ikke-VCC siden af denne pull-up modstand du bør sætte dit ADC-ben. =)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #153 Dato: Oktober 11, 2011, 01:29:26 »
Jeg ved ikke hvorfor jeg ikke kan få metoden ind i hovedet.
Jeg forstår godt de enkelte elementer men kan ikke se hvordan jeg får dem til at arbejde sammen.
skriver lige et eksempel som jeg vil have det til at hænge sammen i mit hoved

Først laver jeg en linje der hedder
Kode:
#define ADC1_CALIBRATION;
jeg går ud fra at der skal stå et eller andet efter ADC1_CALIBRATION der på en eller anden måde skal kædes en værdi mellem 0 og 1023 til dette
denne værdi kan jeg finde ved eks den value der beregnes på nedenstående linje.

Kode:
int16_t value = ((1023 * getADC1Value()) / 5000)

meeen så giver det ikke mening for mig at skulle bruge #define ADC1_CALIBRATION for den er vel ikke mere konstant end Int16_t value

nå men videre med mine tanker nu har jeg så fundet en værdi mellem 0 og 1023 og så mener jeg at kunne forstå at den så skal kædes til en case
som eks
Kode:
uint8_t calculateSetting(uint16_t aValue)
{
    const uint16_t convTab[] PROGMEM = { ((1023 + 523) / 2), ((523 + 349) / 2), ((349 + 262) / 2), ((262 + 209) / 2),
        ((209 + 174) / 2), ((174 + 149) / 2), ((149 + 131) / 2), ((131 + 116) / 2), ((116 + 105) / 2), ((105 + 95) / 2),
        ((95 + 87) / 2), ((87 + 80) / 2), ((80 + 75) / 2), ((75 + 70) / 2), ((70 + 65) / 2), 0 }; /* 3K9, 5% */
    uint8_t i;

    i = 0;
    while(aValue <= pgm_read_word(convTab[i]))
    {
        i++;
    }
    return(i);
}
uden at væer sikker på det skulle jeg så mene at uint16_t aValue er et tal mellem 0 og 15 (har husket #include <avr/pgmspace.h> i starten )

her efter må jeg så skulle have nogle if/else sætninger der forbinder  uint16_t aValue til antal doseringer og antal repeats og dem kan jeg godt finde ud af at lave
er det helt ude i skoven ?? jeg har svært ved at se hvad jeg skal bruge linjen #define ADC1_CALIBRATION; til

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #154 Dato: Oktober 11, 2011, 01:40:39 »
du må godt bande ad mig he he  :-[

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #155 Dato: Oktober 11, 2011, 01:57:33 »
Jeg ved ikke hvorfor jeg ikke kan få metoden ind i hovedet.

Vi prøver da lige at kigge på det. :)

Først får du lige to #defines
Kode:
#define ADC_TO_MILLIVOLT(a) ((5000 * (a)) / 1023)
#define MILLIVOLT_TO_ADC(a) ((1023 * (a)) / 5000)

Den første tager en værdi du får fra ADC'en og konverterer den til millivolt.
Nummer to tager en millivolt-værdi og konverterer den til en ADC-værdi (denne kan fx. bruges til sammenligninger).

Fx. hvis du vil se om ADC'en måler over 2500 mV:
Kode:
if(getADC#Value() >= MILLIVOLT_TO_ADC(2500))
{
  /* 2500 mV or more */
}
else
{
  /* less than 2500 mV */
}

Citér
Jeg forstår godt de enkelte elementer men kan ikke se hvordan jeg får dem til at arbejde sammen.
skriver lige et eksempel som jeg vil have det til at hænge sammen i mit hoved

Jeg tror du er kommet ind i lidt forvirring, derfor springer jeg lidt frem.

Citér
nå men videre med mine tanker nu har jeg så fundet en værdi mellem 0 og 1023 og så mener jeg at kunne forstå at den så skal kædes til en case

getADC#Value() giver dig en værdi mellem 0 og 1023. Denne kan du overføre direkte til calculateSetting uden at skulle regne den om.

Citér
som eks
Kode:
uint8_t calculateSetting(uint16_t aValue)
{
    const uint16_t convTab[] PROGMEM = { ((1023 + 523) / 2), ((523 + 349) / 2), ((349 + 262) / 2), ((262 + 209) / 2),
        ((209 + 174) / 2), ((174 + 149) / 2), ((149 + 131) / 2), ((131 + 116) / 2), ((116 + 105) / 2), ((105 + 95) / 2),
        ((95 + 87) / 2), ((87 + 80) / 2), ((80 + 75) / 2), ((75 + 70) / 2), ((70 + 65) / 2), 0 }; /* 3K9, 5% */
    uint8_t i;

    i = 0;
    while(aValue <= pgm_read_word(convTab[i]))
    {
        i++;
    }
    return(i);
}
uden at væer sikker på det skulle jeg så mene at uint16_t aValue er et tal mellem 0 og 15 (har husket #include <avr/pgmspace.h> i starten )
Rettelse:
Du giver et tal til calculateSetting, som er mellem 0 og 1023. Den giver dig et stabilt resultat som ligger mellem 0 og 15.
Citér

her efter må jeg så skulle have nogle if/else sætninger der forbinder  uint16_t aValue til antal doseringer og antal repeats og dem kan jeg godt finde ud af at lave

Fint. :)

Citér
er det helt ude i skoven ?? jeg har svært ved at se hvad jeg skal bruge linjen #define ADC1_CALIBRATION; til

ADC1_CALIBRATION har jeg erstattet med MILLIVOLT_TO_ADC og ADC_TO_MILLIVOLT, i tilfælde at du vil bruge dem til potmetret.
-Men faktisk vil det nok være en god idé, at lave en funktion der ligner calculateSetting, bare med andre værdier, som passer til potmetret, så nej, du behøver ikke de to macroer.

Hvis du har sat 2 switches på ADC1, burde du kunne gøre noget i stil med:
Kode:
    uint8_t setting;

    setting = calculateSetting(getADC1Value());
    switch(setting)
    {
      case 0:
        ...
        break;
      case 1:
        ...
        break;
      case 2:
        ...
        break;
      case 3:
        ...
        break;
    }


Hvis du har de 4 switches på en og samme ADC, burde du kunne gøre følgende:

Kode:
    uint8_t setting;
    uint8_t repeats;
    uint8_t dosingCount;

    setting = calculateSetting(getADC1Value());
    dosingCount = (setting >> 2) % 0x03;
    repeats = setting & 0x03;

    ...
    ...


 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #156 Dato: Oktober 11, 2011, 15:42:39 »
Oki så prøver jeg.
kan jeg gøre det på denne måde (kun lavet for dips og StartDosint skal også laves om) ?
Kode:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "Dosing.h"
#include "Timer0.h"
#include "ADC.h"
#include "Timer1.h"
#define ADC_TO_MILLIVOLT(a) ((5000 * (a)) / 1023)
#define MILLIVOLT_TO_ADC(a) ((1023 * (a)) / 5000)
int8_t a;
int8_t repeats=1; /*Must be altered to represent the dip for repeats*/
uint8_t setting;
int16_t starting = getADC3Value(); /* controlls if startbutton is pushed*/
uint16_t aValue = getADC1Value();

/*ADC TABELS START*/
uint8_t calculateSettingDip(uint16_t aValue)
{
    const uint16_t convTab[] PROGMEM = { ((1023 + 512) / 2), ((512 + 341) / 2), ((341 + 256) / 2), ((256 + 205) / 2),
        ((205 + 171) / 2), ((171 + 146) / 2), ((146 + 128) / 2), ((128 + 114) / 2), ((114 + 102) / 2), ((102 + 93) / 2),
        ((93 + 85) / 2), ((85 + 79) / 2), ((79 + 73) / 2), ((73 + 68) / 2), ((68 + 65) / 2), 0 }; /* 4K, 0% */
    uint8_t i;

    i = 0;
    while(aValue <= pgm_read_word(convTab[i]))
    {
        i++;
    }
    return(i);
}
/*ADC TABELS END*/


/* CONDITIONS START*/
setting = calculateSetting(getADC1Value());
/* REPEATS */
if (setting == 1)||(setting == 5)||(setting == 9)||(setting == 13)
{
repeats = 10
}
else if (setting == 2)||(setting == 6)||(setting == 10)||(setting == 14)
{
repeats = 4
}
else if (setting == 3)||(setting == 7)||(setting == 11)||(setting == 15)
{
repeats = 2
}
else if (setting == 4)||(setting == 8)||(setting == 12)
{
repeats = 1
}

/* DOSINGS */
if (setting == 4)||(setting == 5)||(setting == 6)||(setting == 7)
{
setMinuteCountdown(24 * 60);
}
else if (setting == 8)||(setting == 9)||(setting == 10)||(setting == 11)
{
setMinuteCountdown(12 * 60);
}
else if (setting == 12)||(setting == 13)||(setting == 14)||(setting == 15)
{
setMinuteCountdown(6 * 60);
}
else if (setting == 1)||(setting == 2)||(setting == 3)
{
setMinuteCountdown(2 * 60);
}
/* CONDITIONS END*/

/*FUNCTIONS START*/
void startDosing() /* this routine should be called when the 'start' button is pressed */
{
   int16_t adcValue;
    int8_t  dosing;
    adcValue = getADC2Value(); /* read value of ADC channel 2 */
    adcValue = adcValue + ADC2_CALIBRATION; /* add calibration value */
    dosing = ((adcValue * 4) >> 10) + 1; /* calculate the dosing (we'll get a value from 1 to 4) */
    setDosing(dosing); /* when this routine is called, the dosing countdown starts */
}
void timer0Elapsed()
{
    uint8_t a;
    uint16_t adc2Value;
    uint8_t Unload;
    Unload =0; /* Value 0 - 256 since the pot2 is removed and unload is defined by constant*/
    for(a = 0; a < repeats; a++)
{
        /* load: */
        waitSeconds(10);
        startServo(200, adc2Value);
        /* unload: */
        waitSeconds(10);
        startServo(200, Unload);
}
}
void initDosing()
{
    startDosing(); /* in case of power failure, automatically restart with the current value */
}

Citér
Men faktisk vil det nok være en god idé, at lave en funktion der ligner calculateSetting
også når opløsningen skal være minimum 256, så bliver det jo noget af en tabel

(jeg ved godt at kommentarene ikke er alignet, men det er de i avrstudio)  :P
« Senest Redigeret: Oktober 11, 2011, 15:47:00 af jascore »

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #157 Dato: Oktober 11, 2011, 16:08:58 »
Oki så prøver jeg.
kan jeg gøre det på denne måde (kun lavet for dips og StartDosint skal også laves om) ?

Det ser rimelig fornuftigt ud, du er på rette kurs.
Du har dog ikke 'if(setting == 0)' med, hvilket du nok gerne vil have, så du er sikker på at have en indstilling for alle de mulige dip-switch kombinationer der er.

Selvfølgelig skal du også have conditions lagt ind i de rigtige funktioner, men det regner jeg med at du har styr på.

Hvis du vil have variabler liggende udenfor funktioner, vil jeg anbefale at du skriver 'volatile' foran dem, for ellers kan du få bøvl med at 'de ikke altid virker'. Dette har med at gøre, at de er cached, og hvis de bliver ændret af et interrupt, så vil cachen ikke blive opdateret.

Så...
Kode:
int8_t a;
int8_t repeats=1;
uint8_t setting;
int16_t starting = getADC3Value();
uint16_t aValue = getADC1Value();
bør så ændres til...
Kode:
volatile int8_t a;
volatile int8_t repeats=1;
volatile uint8_t setting;
volatile int16_t starting = getADC3Value();
volatile uint16_t aValue = getADC1Value();


Men jeg regner med, at du egentlig ikke ønsker at have disse som globale variabler.

Grunden til at jeg et sted har kaldt en variable for 'aValue' i stedet for 'value', er at A'et står for "Argument".
Dette er en kode-standard, som jeg lærte da jeg arbejdede på samme kode som omkring 20 andre programmører, så jeg vil anbefale at du starter alle argumenter med lille 'a' og derefter laver 'kamel-pukler' med uppercase for hver gang der starter et nyt ord, fx.
Kode:
int16_t myFunction(uint8_t *aMessageText, uint16_t aMessageLength)
{
...
}

Og derfor også undgår at variabler, der ikke er argumenter, ikke starter med lille a efterfulgt af stort bogstav, fx...
Kode:
int16_t myFunction(uint8_t *aMessageText, uint16_t aMessageLength)
{
    uint8_t apples = 5;       /* good naming convention */
    uint8_t aLotOfTrees = 1;  /* bad naming convention */
}

Citér
Citér
Men faktisk vil det nok være en god idé, at lave en funktion der ligner calculateSetting
også når opløsningen skal være minimum 256, så bliver det jo noget af en tabel

Uhm, jeg regnede ikke med at dit potmeter skulle have så fin indstilling. Men det er muligt at det stadig vil være en god idé.
Du vil finde ud af om det er nødvendigt eller ej, for jeg husker ikke om potmetret er linært eller logaritmisk.

Citér
(jeg ved godt at kommentarene ikke er alignet, men det er de i avrstudio)  :P

Gør ikke mig noget. Jeg alignede dem i mine eksempler, så du kan se at det bliver pænt læsbart, når de er det. :)

Husk: Hvis du får compile-fejl, så er du nødt til at rette den første fejl, som bliver rapporteret først. For fejl, som følger efter første fejl, kan være forårsaget af netop den første fejl.
Det kan være en god idé, at ændre 5-10 linier ad gangen, for ikke at blive overfuset af fejl-meddelelser.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #158 Dato: Oktober 11, 2011, 20:57:45 »
Lige et hurtigt spørgsmål
kan lokale variabler godt erklæres ude i starten så?
eks int8_t a;
virker den så godt nok i den rutine hvor den skal bruges (og der hvor værdien tillægges)?

eller med andre ord er det kun når værdien af variabler skal bruges i rutiner at det er et problem
eks int8_t a = 10;
så bør det være volatile  int8_t a = 10;

Prøver lige igen  :P
Kode:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "Dosing.h"
#include "Timer0.h"
#include "ADC.h"
#include "Timer1.h"
/* #define ADC_TO_MILLIVOLT(a) ((5000 * (a)) / 1023)*/ /* maby use tabel instead*/
/* #define MILLIVOLT_TO_ADC(a) ((1023 * (a)) / 5000)*/ /* maby use tabel instead*/
int8_t a;
int16_t starting = getADC3Value();      /* controlls if startbutton is pushed*/
volatile uint16_t aValue = getADC1Value();    /* value assigned to aValue for use in calculateSettingDip*/
volatile int8_t setting = calculateSetting(getADC1Value()); /*Declare the global var setting for use in start dosing and timer0elapsed*/

if (starting > 0)           /* if start button is pressed the system starts by calling startDosing*/
{
startDosing();
}
/*ADC TABELS START*/
uint8_t calculateSettingDip(uint16_t aValue)
{
    const uint16_t convTab[] PROGMEM = { ((1023 + 512) / 2), ((512 + 341) / 2), ((341 + 256) / 2), ((256 + 205) / 2),
        ((205 + 171) / 2), ((171 + 146) / 2), ((146 + 128) / 2), ((128 + 114) / 2), ((114 + 102) / 2), ((102 + 93) / 2),
        ((93 + 85) / 2), ((85 + 79) / 2), ((79 + 73) / 2), ((73 + 68) / 2), ((68 + 65) / 2), 0 }; /* 4K, 0% */
    uint8_t i;

    i = 0;
    while(aValue <= pgm_read_word(convTab[i]))
    {
        i++;
    }
    return(i);
}
/*ADC TABELS END*/

/*FUNCTIONS START*/
void startDosing() /* this routine starts when the 'start' button is pressed */
{      /* setMinuteCountdown initiated corrosponding to dip setting*/
if (setting == 4)||(setting == 5)||(setting == 6)||(setting == 7)
{
setMinuteCountdown(24 * 60);
}
else if (setting == 8)||(setting == 9)||(setting == 10)||(setting == 11)
{
setMinuteCountdown(12 * 60);
}
else if (setting == 12)||(setting == 13)||(setting == 14)||(setting == 15)
{
setMinuteCountdown(6 * 60);
}
else if (setting == 1)||(setting == 2)||(setting == 3)
{
setMinuteCountdown(2 * 60);
}
timer0Elapsed(); /* Calls timer0Elapsed to perform first dosing */
    setDosing(dosing); /* when this routine is called, the dosing countdown starts */
}

void timer0Elapsed()   /* the dosing rutine*/
{
if (setting == 1)||(setting == 5)||(setting == 9)||(setting == 13)
{
repeats = 10
}
else if (setting == 2)||(setting == 6)||(setting == 10)||(setting == 14)
{
repeats = 4
}
else if (setting == 3)||(setting == 7)||(setting == 11)||(setting == 15)
{
repeats = 2
}
else if (setting == 4)||(setting == 8)||(setting == 12)||(setting == 0)
{
repeats = 1
}
    uint8_t a;
    uint16_t adc2Value;
    uint8_t Unload;
    Unload =0; /* Value 0 - 256 since the pot2 is removed and unload is defined by constant*/
    for(a = 0; a < repeats; a++)
{
        /* load: */
        waitSeconds(10);
        startServo(200, adc2Value);
        /* unload: */
        waitSeconds(10);
        startServo(200, Unload);
}
}

void initDosing()
{
    startDosing(); /* in case of power failure, automatically restart with the current value */
}
/*FUNCTIONS END*/
er i tvivl om værdien setMinuteCountdown bliver sendt til Timer0.c
og så mangler jeg self lige pot reading men den tygger jeg lige på for 256 er måske nok i overkanten.
skal maks dosere 5 ml så det må være riligt med en opløsning på 20 svarende til ca 0,25 ml og med en tabel er det jo let at sætte range (de ms svarende til at rykke servoen) med andre ord så kan jeg sætte range på potten til kun at inkluderer værdier der giver mening i forhold til de grader der er til rådighed for den begrænset bevægelse af servoen i forhold til fuldt udsving på 180o jeg skal jo kun bruge en bevægelse på max 90o Håber det gav mening  :P

« Senest Redigeret: Oktober 12, 2011, 01:50:57 af jascore »

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #159 Dato: Oktober 12, 2011, 04:45:02 »
Lige et hurtigt spørgsmål
kan lokale variabler godt erklæres ude i starten så?
eks int8_t a;
virker den så godt nok i den rutine hvor den skal bruges (og der hvor værdien tillægges)?

Lokale variabler erklæres inde i den funktion de skal bruges.
Erklæres variabler udenfor en funktion er de altid globale.

Citér
eller med andre ord er det kun når værdien af variabler skal bruges i rutiner at det er et problem
eks int8_t a = 10;
så bør det være volatile  int8_t a = 10;

Hvis en variabel er erklæret global, og den bruges af 2 forskellige rutiner, er det en god idé at have den volatile.
Hvis den kun bruges af én rutine, er der normalt ikke grund til at have den som global.

Men hvis du vil have den tekniske forklaring, vil jeg bruge dette program som eksempel:

Kode:
uint8_t seconds;
uint8_t minutes;
uint8_t hours;

void clock()
{
    if(seconds++ >= 60)
    {
        seconds = 0;
        if(minutes++ >= 60)
        {
            hours++;
        }
    }
}

void wait()
{
    uint8_t oldSeconds;

    oldSeconds = seconds;
    while(oldSeconds == seconds)
    {
    }
}

Vores rutine 'clock' er kaldt under udførsel af interrupt. Om den er kaldt direkte af interruptet, eller af interrupt-funktionen er ligegyldigt, resultatet vil blive det samme.
Vores 'wait' rutine læser værdien 'seconds' og gemmer værdien i 'oldSeconds', for at vi kan se hvornår der er sket en ændring.
Næste ting, er at vi venter til seconds har ændret sig.
Compileren er så smart, at den optimerer vores program.
Da den kan se, at vi aflæser 'seconds' to gange lige efter hinanden, vil den for det første sige: Fint, dette tager jeg som én aflæsning, så det vil være det samme som:
Kode:
    uint8_t temp;

    temp = seconds;
    oldSeconds = temp;
    while(oldSeconds != temp)
    {
    }

-Fordi compileren ved ikke at der nu og da sker et interrupt, som ændre i seconds.
Men hvis vi nu ovenover erklærer seconds som volatile, vil koden virke som forventet.

Dvs. hvis en variabel ændrer værdi udenfor den funktion, som den aflæses i, imens denne funktion køres, bør den være volatile.
Dette sker normalt kun i interrupts, men der er faktisk et andet eksempel.
Alle microcontrollerens porte er erklæret volatile, fordi ellers ville første aflæsning blive cached, og så har vi et input-ben som ikke ser ud til at virke. ;)

Sidebemærkning:
Du har sikkert set at der står...
Kode:
    if(seconds++ >= 60)

Og måske undret dig lidt over hvornår 'seconds' bliver sammenlignet med 60.
Når ++ står efter variablen, hedder det post-increment.
Når -- står efter variablen, hedder det post-decrement.
Når ++ står før variablen, hedder det pre-increment.
Når -- står før variablen, hedder det pre-decrement.

(increment = tælle opad, decrement = tælle nedad)

I while og for-løkker bruger man normal post-increment eller post-decrement.
Ved pointere bruger man normal post-increment og pre-decrement; det er sjældent at det er logisk/smart at gøre andet; men der kan komme situationer, hvor det er praktisk at bruge pre-increment eller post-decrement.

Konklusion. Vi kunne have lavet koden ovenover anderledes, fx.
Kode:
    if(++seconds >= 61)
eller
Kode:
    if(++seconds > 60)

...men personligt synes jeg det andet giver bedre læsbarhed/pænere kode.

Et eksempel på post-decrement og pre-decrement i en while-løkke:
Kode:
i = 10;
while(i--) /* post-decrement */
{
    /* Here, i has the values 9, 8, 7, 6, 5, 4, 3, 2, 1, 0. */
}

i = 10;
while(--i) /* pre-decrement */
{
    /* Here, i has the values 9, 8, 7, 6, 5, 4, 3, 2, 1. */
}

Citér
Prøver lige igen  :P
Kode:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "Dosing.h"
#include "Timer0.h"
#include "ADC.h"
#include "Timer1.h"
/* #define ADC_TO_MILLIVOLT(a) ((5000 * (a)) / 1023)*/ /* maby use tabel instead*/
/* #define MILLIVOLT_TO_ADC(a) ((1023 * (a)) / 5000)*/ /* maby use tabel instead*/
int8_t a;
int16_t starting = getADC3Value();      /* controlls if startbutton is pushed*/
volatile uint16_t aValue = getADC1Value();    /* value assigned to aValue for use in calculateSettingDip*/
volatile int8_t setting = calculateSetting(getADC1Value()); /*Declare the global var setting for use in start dosing and timer0elapsed*/

if (starting > 0)           /* if start button is pressed the system starts by calling startDosing*/
{
startDosing();
}

Her er et par ting, som du måske ikke har lagt mærke til:

1: Hvornår skal 'starting' aflæses ?
2: Hvornår skal 'aValue' aflæses ?
3: Hvornår skal koden 'if(starting > 0)' bruges ?

Hvis du kunne få lov til at lave ovenstående kode, ville starting blive indstillet én gang, aValue ville blive indstillet én gang, og disse ville så begge være indstillet i starten af dit program, og derefter vil de kun ændre værdi, hvis du har en funktion, som ændrer værdien.
Koden 'if(starting > 0)' ville (igen, hvis du fik lov til det), kun udføres én gang, nemlig under opstart.

Hvis du har globale variabler, som skal tilskrives en startværdi (initialiseres), bør det være en konstant, for at du ikke kommer ud i noget skidt.
Altså, en god idé ville være enten:
Kode:
uint16_t gStarting = 0;         /* uint16_t, since getADC1Value() returns an uint16_t */
uint16_t gValue = 0;

Læg mærke til at jeg har ændret 'prefix', dvs. starten på 'starting' og 'aValue'. g står for 'global'.
Fordi: Hvis du nu havde en funktion, fx. 'calculateSettingDip(uint16_t aValue)', og du skulle bruge ovenstående 'aValue', ville du ikke kunne 'få fat i den', da argumentet har samme navn som din globale variabel.

Citér
Kode:
void timer0Elapsed()												  /* the dosing rutine*/
{
    if (setting == 1)||(setting == 5)||(setting == 9)||(setting == 13)
    {
        repeats = 10
    }
    else if (setting == 2)||(setting == 6)||(setting == 10)||(setting == 14)
    {
        repeats = 4
    }
    else if (setting == 3)||(setting == 7)||(setting == 11)||(setting == 15)
    {
        repeats = 2
    }
    else if (setting == 4)||(setting == 8)||(setting == 12)||(setting == 0)
    {
        repeats = 1
    }
    uint8_t a;
    uint16_t adc2Value;
    uint8_t Unload;
    Unload =0; /* Value 0 - 256 since the pot2 is removed and unload is defined by constant*/
    for(a = 0; a < repeats; a++)
    {
        /* load: */
        waitSeconds(10);
        startServo(200, adc2Value);
        /* unload: */
        waitSeconds(10);
        startServo(200, Unload);
    }
}

Compileren vil sikkert brokke sig her.
Prøv at holde alle erklæringer i toppen af dine rutiner/funktioner.
Fx.

Kode:
void timer0Elapsed()												  /* the dosing rutine*/
{
    uint8_t     a;
    uint16_t    adc2Value;
    uint8_t     Unload;

    if (setting == 1)||(setting == 5)||(setting == 9)||(setting == 13)
    {
        repeats = 10
    }
    else if (setting == 2)||(setting == 6)||(setting == 10)||(setting == 14)
    {
        repeats = 4
    }
    else if (setting == 3)||(setting == 7)||(setting == 11)||(setting == 15)
    {
        repeats = 2
    }
    else if (setting == 4)||(setting == 8)||(setting == 12)||(setting == 0)
    {
        repeats = 1
    }
    Unload =0; /* Value 0 - 256 since the pot2 is removed and unload is defined by constant*/
    for(a = 0; a < repeats; a++)
    {
        /* load: */
        waitSeconds(10);
        startServo(200, adc2Value);
        /* unload: */
        waitSeconds(10);
        startServo(200, Unload);
    }
}

-For så er du sikker på at det vil virke med alle compilere.
Dette er fordi der gøres plads til variabler når funktionen starter.
Enkelte compilere tillader dette som standard, men de fleste compilere skal have særlige indstillinger til at tillade dette, hvis de er i stand til at håndtere det.


Citér
er i tvivl om værdien setMinuteCountdown bliver sendt til Timer0.c

Tarveligt spørgsmål: Hvorfor er du i tvivl om dette ? :)

Citér
og så mangler jeg self lige pot reading men den tygger jeg lige på for 256 er måske nok i overkanten.
skal maks dosere 5 ml så det må være riligt med en opløsning på 20 svarende til ca 0,25 ml og med en tabel er det jo let at sætte range (de ms svarende til at rykke servoen) med andre ord så kan jeg sætte range på potten til kun at inkluderer værdier der giver mening i forhold til de grader der er til rådighed for den begrænset bevægelse af servoen i forhold til fuldt udsving på 180o jeg skal jo kun bruge en bevægelse på max 90o Håber det gav mening  :P

Jep. :)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #160 Dato: Oktober 12, 2011, 13:15:07 »
Og her troede jeg jeg stillede et simpelt og hurtigt spørgsmål ha ha. tak for det fyldestgørende svar ;D

Citér
1: Hvornår skal 'starting' aflæses ?
2: Hvornår skal 'aValue' aflæses ?
3: Hvornår skal koden 'if(starting > 0)' bruges ?
Citér
kun udføres én gang, nemlig under opstart.
min ide var faktisk at atarting bliver aflæst og udført når jeg trykker på start knappen. værdien af denne er 0 når den ikke er trykket ned, så jeg sænkte at en if sætning der kontrolerer for en vrdi større end 0 ville være nok til at registrerer dette. aValue var min mening at denne kun skal sttes ved opstart. systemet skal ikke ændre sig med mindre at dips bliver stillet anderledes og der på ny trykkes på startknappen.
Den eneste værdi der løbende skal kunne opdateres er faktisk potmeteret. alle de andre der har med dosering og repeats at gøre skal sættes ved tryk på start og derefter forblive konstante. Det troede jeg faktisk at jeg havde opnået ved den kode jeg havde skrevet, men lurer lige på den for at se  om jeg kan finde Holger  :P

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #161 Dato: Oktober 12, 2011, 13:42:04 »
Og her troede jeg jeg stillede et simpelt og hurtigt spørgsmål ha ha. tak for det fyldestgørende svar ;D

Jeg kunne nok have valgt at give et kort svar, men du ville nok bedre kunne bruge at have baggrunden med.

Citér
Citér
1: Hvornår skal 'starting' aflæses ?
2: Hvornår skal 'aValue' aflæses ?
3: Hvornår skal koden 'if(starting > 0)' bruges ?
Citér
kun udføres én gang, nemlig under opstart.
min ide var faktisk at atarting bliver aflæst og udført når jeg trykker på start knappen.
værdien af denne er 0 når den ikke er trykket ned, så jeg sænkte at en if sætning der kontrolerer for en vrdi større end 0 ville være nok til at registrerer dette.

Det vil være bedst, at lave benet digitalt, hvis du kun har 1 knap på.
Så vil du kunne lave et pin-change interrupt.
Således vil denne pin-change interrupt rutine blive kaldt, så snart der er en ændring på start-knappen.
Bemærk: Jeg skriver ændring. :)
I dit pin-change interrupt bør du så finde ud af om knappen lige er blevet trykket ned, eller om den er sluppet.

Jeg vil vende tilbage til disse ting senere, for der indgår sikring mod prel i denne lille del af programmet. Det kan laves på flere måder.

Citér
aValue var min mening at denne kun skal sttes ved opstart. systemet skal ikke ændre sig med mindre at dips bliver stillet anderledes og der på ny trykkes på startknappen.

OK, så vil jeg bestemt anbefale at du ikke kalder den 'aValue', men noget som passer bedre, fx. gDipSwitches.

Men du bør gøre disse ting i din 'main' rutine, efter du har lavet alle initialiseringer, ellers får du slet ikke noget brugbart resultat, idét fx. ADC'en ikke er initialiseret, i/o-benet er sat til at være et digitalt input-ben, mm.

Desuden skal du lige have en forskrækkelse: 8)
Når du sætter strøm på microcontrolleren, starter den med at udføre noget tilfældigt (af din) kode, som du slet ikke har kontrol over.
Først efter nogle millisekunder, hoppes til starten af dit program.
Og hvad endnu værre er: Før starten af dit program udføres, kan alle variabler have fuldstændig tilfældige tossede værdier, selvom du har sagt at de skal starte med at være 0.
Men så snart den første linie i main() udføres, som er dét sted, dit program starter, så er der styr på tingene.

Citér
Den eneste værdi der løbende skal kunne opdateres er faktisk potmeteret. alle de andre der har med dosering og repeats at gøre skal sættes ved tryk på start og derefter forblive konstante. Det troede jeg faktisk at jeg havde opnået ved den kode jeg havde skrevet, men lurer lige på den for at se  om jeg kan finde Holger  :P

Dét er en ganske god måde at gøre det på. Godt design.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #162 Dato: Oktober 12, 2011, 14:25:13 »
Citér
Men du bør gøre disse ting i din 'main' rutine

Ok så rykker jeg alle dip rutinerne samt startknap og hvad dertil hører over i main, efter initialiseringen. resultatet af repeats gør jeg så til en global variabel der kan bruges i dosing.c
Potmeter rutinen lader jeg blive i dosing.c

Citér
Desuden skal du lige have en forskrækkelse
havde regnet med noget i den stil men var ikke klar over om den gik ind i og udførte rutiner også. regnede bare med at den holdt sig uden for disse for det går jo ikke at den laver en eller anden tilfældig dosering eller for den sags skyld trækker et par tilfældige antal ml op i sprøjten, inden programmet "starter"

Citér
så vil jeg bestemt anbefale at du ikke kalder den 'aValue', men noget som passer bedre, fx. gDipSwitches.
Yup  :D

Citér
Jeg kunne nok have valgt at give et kort svar, men du ville nok bedre kunne bruge at have baggrunden med.
helt sikkert. tak  ;D

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #163 Dato: Oktober 12, 2011, 17:09:31 »
Citér
Desuden skal du lige have en forskrækkelse
havde regnet med noget i den stil men var ikke klar over om den gik ind i og udførte rutiner også. regnede bare med at den holdt sig uden for disse for det går jo ikke at den laver en eller anden tilfældig dosering eller for den sags skyld trækker et par tilfældige antal ml op i sprøjten, inden programmet "starter"

Du kan selvfølgelig også være heldig at den nogle gange vil starte op i noget junk-kode som ikke har med din kode at gøre. :)
(dvs. resten af Flash-hukommelsen, hvor din kode ikke ligger), men hver gang microcontrolleren startes, udføres kode et tilfældigt sted i Flash-hukommelsen.


Chancen for at den laver tilfældige doseringer er nu ikke så stor.

Men der er en løsning, en rigtig god og sikker løsning, endda.

I dokumentationen for ATtiny24/44/84 står at det aller første som sker, når en ATtiny starter/resettes, er at alle I/O-registre bliver indstillet til en fast start-værdi. (Fx. alle i/o-porte bliver sat til input, så de ikke pludselig 'lægger arm' med noget andet hardware).

Kigger vi i ATtiny24/44/84's I/O-register liste, finder vi lidt forskellige registre, som ikke skal bruges i dit tilfælde.
Dem kunne vi selvfølgelig bruge, men der er noget endnu bedre, for Atmel har nemlig reserveret 3 registre helt til dig.

Disse 3 registre hedder GPIOR0, GPIOR1 og GPIOR2. Det er således dig selv, som bestemmer hvad disse skal bruges til.

Til at starte med, sættes disse til værdien 0 af microcontrolleren, før noget kode køres.

I Servo.h indsætter du følgende #define:
Kode:
#define SERVO_ENABLED  0xa8  /* our lucky number or something else, just not zero! */

I main.c, lige under #include "main.h", bør du så have...
Kode:
#include "Servo.h"

Kode:
void startButtonPushed()
{
    gSettings = calculateSettings(getADC1Value());  /* update our settings */
    /* ... perform other needed actions as needed ... */
}

int main()
{
    /* ... declared variables ... */

    GPIOR2 = SERVO_ENABLED;     /* This must be the first code executed; no code goes before it, and GPIOR2 is not to be changed from now on. */

    /* initialize global variables after the above line, but before the main loop, and ofcourse, before they're used. */

    /* ... initialization code: initADC(); initServo(); initTimer0(); etc... */

    /* Now we can use the ADC to get our initial settings: */
    startButtonPushed();    /* this line must be placed after the ADC is initialized, otherwise you won't get the right value. */

    /* ...main loop... */
}

Og så i rutinen startServo (som jeg regner med ligger i Servo.c), finder du den linie, som er årsag til at servoen starter (servoen må ikke kunne starte uden den linie), og sætter følgende kode rundt om:

Kode:
    if(GPIOR2 == SERVO_ENABLED)
    {
        /* the line of code that starts the servo, and nothing else. */
    }

Lidt forklaring:
Lad os antage at tilfældig kode bliver udført.
Rammer vi ind i den allerførste linie, som udføres, kan skaden ikke blive ret stor, fordi så bliver alt initialiseret som det skal, og alting kører som vi forventer.
Det kan være en rigtig god idé, at undgå at tilegne globale variabler en start-værdi, og så i stedet indstille denne start-værdi *efter* GPIOR2 = SERVO_ENABLED; - derved er du 100% sikker på at intet går galt.

Men hvis nu GPIOR2 = SERVO_ENABLED; står efter al initialisering, så er der mulighed for at kun denne linie bliver udført, og så har vi faktisk ikke fået initialiseret vores program, men i stedet har vi fået 'fortalt en løgn'. Derfor er det nødvendigt at der aldrig skrives noget kode før GPIOR2 = SERVO_ENABLED; linien.

Det samme med if(GPIOR == SERVO_ENABLED) - inde i denne if-sætning, bør der være så lidt kode som overhovedet muligt. Helst kun lige den linie, som er årsagen til at servoen starter, dvs. hvis du bruger PWM, så den enkelte kommando der skriver til I/O-registret der starter PWM'en.

Du kunne sådan set sætte if(GPIOR == SERVO_ENABLED) omkring hver eneste linie, som ændrer ved et I/O-register der har med servo'en at gøre.
« Senest Redigeret: Oktober 12, 2011, 17:13:28 af pacman »

 

Offline pacman

  • Højpas filter
  • *****
  • Indlæg: 311
  • Antal brugbare Indlæg: 8
  • Jens Bauer (Forsøgs-person)
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #164 Dato: Oktober 12, 2011, 17:15:25 »
Lige en ting til... GPIOR2 må ikke ændres andre steder i programmet; den må kun indstilles én gang, nemlig i starten af main. ;)
-Ellers bliver virkningen ødelagt.