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

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 #195 Dato: November 07, 2011, 22:28:19 »
Jeg fik en fejl på Prel funktionen så lagde følgende linje ind i starten af main.c
#include <util/delay.h>

Det gav mig så en warning (nogen gange) om at F_CPU bliver redefined, da den også bliver defined i <util/delay.h>
Ved ikke lige hvad jeg skal gøre ved den, for hvis jeg fjerner den fra main.c er der andre dele der brokker sig. Jeg kan self lave en #include <util/delay.h> i de andre .c som gør brug af F_CPU, men det virker som en lidt "beskidt" metode.

Det rigtige er at gå ind i din Makefile og definere den dér.
Godt råd:Lav en backup først!

Toppen af min Makefile ser nogenlunde sådan ud:

Kode:
TARGET     = LightControl-Test1
DEVICE     = atmega164p
CLOCK      = 8000000

Af disse linier skal du kun indsætte 'CLOCK = (frekvens)' i din Makefile.

Senere er der et sted, hvor compileren får sine parametre, linien kunne se nogenlunde således ud, men det er ikke sikkert...

Kode:
COMPILE = avr-gcc -Wall -Os -mmcu=$(DEVICE)

Hvis den gør det, så indsæt '-DF_CPU=$(CLOCK)' nogenlunde således...

Kode:
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)

Citér
Startkanp funktionen lavede jeg om til flg.
{snip}
Går ud fra at det løser problemet med at jeg bruger pull down og ikke pull up

Det ser fint ud og burde løse det..

Hvis du ikke kan få defineret F_CPU i din Makefile, så kan du definere den før du inkluderer <avr/delay.h>; men det er korrekt at definere den i din Makefile, for så kan alle dele af source-koden se den.

Her er lidt advarsel om Makefile...
Der er forskel på hvad space og tab betyder, så hvis der er mellemrum, må du ikke erstatte dem med tabuleringer og er der tabuleringer må de ikke erstattes af mellemrum!
-Vældig uintuitivt, men gutterne der engang for længe siden opfandt Make mente at det var smart på det tidspunkt (hvilket det sikkert også var, så jeg vil ikke slå dem oveni hovedet).

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #196 Dato: November 07, 2011, 22:39:39 »
Super.
jeg lagde #include <util/delay.h> ned under #include <main.h>
Det løste det, men ændre dette igen så det stemmer overens med det du siger om make filen

så tror jeg kun at jeg mangler pot til PWM funktionen til servoen :P
« Senest Redigeret: November 07, 2011, 22:45:57 af jascore »

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #197 Dato: November 10, 2011, 21:29:24 »
Kan kun finde assemble eksempler på hvordan den servo funktion skal laves, så er faktisk lidt blank syntes jeg  :-[
Har du et forslat til hvor jeg skal kigge ??

 

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 #198 Dato: November 10, 2011, 23:14:04 »
Kan kun finde assemble eksempler på hvordan den servo funktion skal laves, så er faktisk lidt blank syntes jeg  :-[
Har du et forslat til hvor jeg skal kigge ??

Prøv at sende mig nogle links til de assembler-ting du har fundet, måske kan jeg 'konvertere' noget af det til C.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #199 Dato: November 11, 2011, 16:07:54 »
Tusind tak. her er de links jeg har kigget på
http://winavr.scienceprog.com/comment/52
http://blog.roderickmann.org/2006/01/atmega32-pwm/
http://www.societyofrobots.com/member_tutorials/node/231

Den øverste kan jeg ikke helt greje om den er i c
De er alle 3 tutorials til en at mega men det er vel bare et spørgsmål om hvordan der addresseres


 

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 #200 Dato: November 11, 2011, 17:49:12 »
Advarsel: Forfærdeligt langt indlæg, uha-uha-uha...
Tusind tak. her er de links jeg har kigget på
http://winavr.scienceprog.com/comment/52
http://blog.roderickmann.org/2006/01/atmega32-pwm/
http://www.societyofrobots.com/member_tutorials/node/231

Den øverste kan jeg ikke helt greje om den er i c
De er alle 3 tutorials til en at mega men det er vel bare et spørgsmål om hvordan der addresseres

:) Sådan set er alle 3 i C, ikke assembler. =)

Jeg kan ikke selv afprøve noget kode, for jeg har ikke en servo-motor.

Men det, som er vigtigt, er at du finder ud af at regne frekvensen ud korrekt.
Derefter er det forholdsvis enkelt at sætte en timer op; så vidt jeg husker, valgte vi at have en 16-bit timer fri til PWM, hvilket vil sige at der er gode muligheder for rimelig præcise frekvenser.

Du skal altså have en rutine der regner frekvensen ud, når denne rutine får en eller anden form for input.
Du skal så også have en rutine, som starter PWM'en.

Hvis du kigger på det 3. eksempel du sendte, vil du se den nederste kode-blok.

Han har skrevet koden til ATmega8, så hvis vi kigger i ATmega8's datablad, vil vi kunne finde ud af hvad det er han sætter op.

Det første han gør, er at slå PWM fra, mens det sættes op, det gør han her...
TCCR1A = 0;

Med andre ord: Han sætter disse bits til 0 i TCCR1A:
COM1A1, COM1A0, COM1B1, COM1B0, FOC1A, FOC1B, WGM11, WGM10

At han sætter alle COM1xx bit til 0, betyder at COMpare- (sammenlignings-) registrene slåes fra.
Jeg mener der er en bedre måde at gøre dette på, nemlig at sætte prescaleren til off (timer stopped), men det kommer lidt an på om man ønsker at timeren kører hele tiden.

Det næste han gør, er:
Kode:
ICR1 = 19999;

Dette sætter i hans tilfælde TOP-grænsen hvis man bruger Fast PWM (mode 14).
Han sætter så sin timer op til at køre Fast PWM:
Kode:
TCCR1A = (1 << WGM11);  /* WGM10 er her samtidig blevet sat til 0 */
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);

Han sætter så samtidig CS10 til 1, og alle andre CSxx til 0, dvs...
CS12=0, CS11= 0, CS10=1: clk/1 (no prescaling)

Derefter sætter han PB1 og PB2 til at være outputs.
Kode:
DDRB |= _BV(1) | _BV(2);
...Dette kan skrives lidt mere forståeligt:

Kode:
DDRB |= (1 << DDB1) | (1 << DDB2);
(DDB1 betyder Data Direction for pin B1, DDB2 betyder så Data Direction for pin B2)

Efter det, enabler han COM1xx registrene:
Kode:
TCCR1A |= 2 << 6;
TCCR1A |= 2 << 4;

Dette kunne skrives mere forståeligt sådan:

Kode:
TCCR1A |= (1 << COM1A1) | (1 << COM1B1);

Så indstiller han sine Output Compare Registers...
Kode:
OCR1A = ICR1 * 2 / 20;
OCR1B = ICR1 * 2 / 20;

Det ene outputben (OC1A) får så resultatet af OCR1A's indstilling i forhold til ICR1
Det andet outputben (OC2A) får så resultatet af OCR2A's indstilling i forhold til ICR1

Efter det, så lader han sit program gå i loop med en
Kode:
while(1)
{
}

-Timeren ordner resten.

Man kan lave en slags grafisk illustration på denne måde:


+------------------------------------------------+
                    ^                            ^ ICR (top)
                    OCR1A (40% 'duty cycle' af ICR)


Så resultatet af ovenstående illustration kunne være...


____________________                               (on)
                    ______________________________ (off)




Se, det du så ønsker er...

1: At kunne beregne hvilken duty-cycle du vil have, altså forholdet mellem 'tændt' og 'slukket'.
Man kunne fx. sige du giver et tal i procent, så ville formlen være....
Kode:
OCRxx = (TOP * procent) / 100;

Eller hvis du hellere vil give en byte...
Kode:
OCRxx = (TOP * byte) / 256;
(kan skrives om til)...
Kode:
OCRxx = (TOP * byte) >> 8; /* at bitskifte til venstre 8 gange er det samme som at dividere med 256, bare hurtigere og kortere */

-Find eventuelt selv på flere. :)

2: At indstille Timer1. Prøv at se på databladet, side 85, "Sektion 12. 16-bit Timer/Counter1".
-Der står en hel masse som du ikke gider læse lige nu.
Bladrer vi frem til side 106, finder vi beskrivelse af TCCR1A. Her er et lille overblik over dens bits:

COM1A1, COM1A0, COM1B1, COM1B0, -, -, WGM11, WGM10

Godt at huske til om lidt.

TCCR1B bliver beskrevet på side 108. På denne side står også lidt omkring de forskellige modes. Det er lige det vi skal bruge.

Vi vil gerne have det ligesom ham gutten i den tutorial; vi sætter selv vores TOP-værdi, og vi bestemmer selv hvor vores PWM skal tænde og hvor den skal slukke.
Mode 14 ser ud til at være fin til det brug; det virker til at det er samme mode som han brugte i sin tutorial.

Så kigger vi på hvad vi skal indstille WGM tll. Det er i nabo-kolonnen til Mode. Der står WGM1[3:0] hvilket betyder at rækkefølgen er WGM13, WGM12, WGM11, WGM10.
Dvs. for at få mode 14, skal vi sætte , WGM13=1, WGM12=1, WGM11=1 og WGM10=0.

Godt. Vi skal også indstille ICR1 til at være top-værdi.

WGM11 og WGM10 indstilles i TCCR1A, mens WGM13 og WGM12 indstilles i TCCR1B (fjollet, men det kan vi ikke lave om på).

Vi skal også finde en passende prescaler værdi. Om du vil køre 8MHz eller 1MHz, det ved jeg ikke lige nu, men vi kan prøve at starte med 1MHz.
På side 109 finder vi en oversigt over prescaler værdierne:
CS12=0, CS11=0, CS10=1 giver en prescaler på clk/1, dette giver 8MHz.
CS12=0, CS11=1, CS10=0 giver en prescaler på cllk/8, dette giver 1MHz.

OK, så godt, så langt. ;)

På side 107, ser vi en oversigt over hvad Timer1 kan gøre med OC1A og OC1B benene.
Jeg fortalte dig vidstnok noget tidligere, med at det ville være praktisk at sætte servoen på et OCxx-ben, desværre tror jeg at jeg fik overbevist dig til at bruge OC0x og ikke OC1x.
Det kan derfor være du 'bliver nødt til' at flytte servoen til fx. ben 7 som er OC1A, eller ben 8, som er OC1B.
Det kan være at servo-motoren går amok, når du så programmerer chippen, men det må den så gøre.

Altså på side 107 er der 2 tabeller. Den første er "non-PWM", den vil vi ikke kigge på.
Den anden tabel, "Table 12-3. Compare Output Mode, Fast PWM" viser noget vi kan bruge.

Vi er nok mest interesseret i at tænde OC1A først, derefter slukke den når vi rammer sammenlignings-værdien som står i OCR1A. Det ser ud til at COM1A[1:0] så skal være 11.
(Det gør ikke noget, hvis vi samtidig sætter COM1B, så kan vi flytte servo-motoren derover hvis vi har lyst).

Det bliver nogenlunde til følgende kode, som kunne ligge i PWM.c:

Kode:
#define PWM_TOP_VALUE    65535  /* choose any value that suits you. */
void initPWM()
{
    /* First we stop our timer: */
    TCCR1B = 0;  /* CS12=0, CS11=0, CS10=0: No clock source (Timer/Counter stopped). */

    ICR1 = PWM_TOP_VALUE;

    if(SERVO_ENABLED == GPIOR2)
    {
        TCCR1A = (1 << WGM11) | (1 << WGM10) | (1 << COM1A1) | (COM1A0) | (1 << COM1B1) | (1 << COM1B0);
    }
    TCCR1B = 0;  /* safety, to ensure PWM is not started by accident during RESET */
    if(SERVO_ENABLED == GPIOR2)
    {
        TCCR1B = (0 << CS12) | (1 << CS11) | (0 << CS10) | (1 << WGM13) | (1 << WGM12);
    }
    /* PWM is now running! */

    DDRA |= (1 << DDA6) | (1 << DDA5); /* set both OC1A and OC1B to be output pins */
}

Et eller andet sted i programmet kan du så...
Kode:
    OCR1A = (PWM_TOP_VALUE * 17) / 100;   /* 17% on, 83% off */
    OCR1B = (PWM_TOP_VALUE * 61) / 100;   /* 61% on, 39% off */

Dette kan skrives om til noget lidt mere fleksibel kode, som kunne ligge i PWM.c:

Kode:
- (void)setDutyCyclePercentage(uint8_t aPercentage)
{
    OCR1A = (PWM_TOP_VALUE * aPercentage) / 100;
}

-Men bemærk: Dette er sikkert slet ikke en præcis nok beregning.
Du vil sikkert gerne have nogle forud-definerede (forud-beregnede) 16-bit værdier, fx. én værdi til 'load' og én værdi til 'unload'.

Jeg ved dette indlæg ikke er udtømmende, men det skulle kunne give dig en nogenlunde pejling på hvordan PWM sættes op.

Hvis du har datablad på servoen, der fortæller dig noget om hvordan duty-cycle/frekvenserne beregnes, så kan det være til hjælp.
Det kan være, at det ikke er nok, kun at ændre OCR1A, men at du også er nødt til at kunne ændre ICR1 når du styrer servoen.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #201 Dato: November 14, 2011, 17:41:44 »
men men men har vi ikke lavet det i servo.c, altså selve pwm så det bare er omregningen fra pot til % on time der mangler ?
Citér
/*
 * servo.c
 *
 * Created: 12-10-2011 19:12:27
 *  Author: jascore
 */
#include <avr/io.h>
#include "servo.h"
#include "Timer1.h"
void initServo()
{

}
void startServo(uint16_t aServoCountdown, uint16_t aRotation)
{
    /*servoCountdown = aServoCountdown;*/                  /* number of pulses to send to servo, before turning it off */
    TCCR1A = 0;                                                                  /* disable all PWM on Timer1 whilst we set it up */
    TCCR1B = 0;                                                                  /* (disable clock-source, so the timer is frozen) */
    TIMSK1 = 0;                                                                  /* disable all Timer1 interrupts */
    ICR1 = servoFrequency;                                               /* frequency is every 20ms, 50 times per second (50Hz) */
    /* select fast PWM (mode 14) and no prescaler */
    if(GPIOR2 == SERVO_ENABLED)
   {
      TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);      /* enable output COMpare 1 A, output COMpare 1 B, set WGM to Fast PWM  */
   }
   
    if(GPIOR2 == SERVO_ENABLED)
   {
      TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);               /* set WGM to fast PWM, choose clock source CS12:CS11:CS10 = 1, clk/1 (no prescaling) */   
   }
   OCR1A = servoFrequency * aRotation / 20;                                           /* pulse to load or unload servo on PA7 depending on the argument aRotation */
   TIMSK1 |= (1 << OCIE1B);                                                                     /* enable Output Compare Interrupt 1 B */
}

Og mange tak for linket til fritzing i en anden tråd. det var jo lige hvad en amatør havde brug for  :P
« Senest Redigeret: November 14, 2011, 17:45:17 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 #202 Dato: November 14, 2011, 20:52:06 »
men men men har vi ikke lavet det i servo.c, altså selve pwm så det bare er omregningen fra pot til % on time der mangler ?

Hmm, jouw, jeg kunne bare ikke huske at den var så komplet. =)

Citér
Og mange tak for linket til fritzing i en anden tråd. det var jo lige hvad en amatør havde brug for  :P

Velbekomme. Jeg håber den bliver til gavn. =)
Hvis du skal lave print-layout og diagram-tegning, er der 2 programmer som har fået fornuftig karakter på Windows:
Eagle og DesignSpark.
Hvis du kun skal lave print-layout og ikke vil lave diagram-tegning, kan du bruge Pad2Pad, men det kræver at du bruger pad2pad.com til at lave print for dig, så du skal betale import-afgift også (ofte ca. 5% oveni).
Det er nok mest praktisk at bruge Eagle (Gratis for print-plader på under 160 mm x 100 mm), hvis du kan vænne dig til den. Hvis du beslutter dig for Eagle, så følg nogle online-tutorials; det er en lidt langsom process, men det vil nok være til gavn i sidste ende.
DesignSpark fra RS Components er også gratis.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #203 Dato: November 15, 2011, 21:08:06 »
Perfect :P
Her har jeg så lige et par spørgsmål til det sidste.
(kan mærke målet er nært, men alligevel så langt væk hehe)

Citér
Det næste han gør, er:

ICR1 = 19999;
Dette sætter i hans tilfælde TOP-grænsen hvis man bruger Fast PWM (mode 14).

giver det samme resultat som det vi gør når vi siger
ICR1 = servoFrequency;
hvor
servoFrequency = F_CPU / 50;
eller bør vi sige
servoFrequency = (F_CPU / 50)-1;
for at få 19999 eller har det ingen betydning?

Citér
1: At kunne beregne hvilken duty-cycle du vil have, altså forholdet mellem 'tændt' og 'slukket'.
Man kunne fx. sige du giver et tal i procent, så ville formlen være....

OCRxx = (TOP * procent) / 100;
Eller hvis du hellere vil give en byte...

OCRxx = (TOP * byte) / 256;(kan skrives om til)...

OCRxx = (TOP * byte) >> 8; /* at bitskifte til venstre 8 gange er det samme som at dividere med 256, bare hurtigere og kortere */
-Find eventuelt selv på flere. :)

hvis potten retunerer en værdi mellem 0 og 255 ville det så ikke være en ide med en tabel der bare giver OCRxx en konstant? (ligesom dipswitch)
som sagt har jeg skal jeg ikke bruge det fulde udsving på servoen men kun ca 60 - 90 grader, så de 100% udsving der ligger over potten skal fordeles i en top på f.eks mellem 1.2-1.8 ms i duty cyclen.
men er en tabel på 256 muligheder for stor sådan rent byte mæssigt?

Citér
vi bestemmer selv hvor vores PWM skal tænde og hvor den skal slukke.
opnår vi det ved følgende (uddrag af kode) 10 sek til at køre en dutycycle der trækker servoen ud og efterfølgende 10 sek der køre servoen modsat?
 for(a = 0; a < repeats; a++)
    {
        /* load: */
        waitSeconds(10);
        aRotation = getADC2Value();
        startServo(200, aRotation);
        /* unload: */
        waitSeconds(10);
        startServo(200, unload);
    }
sammenlagt med
OCR1A = servoFrequency * aRotation / 20;            
   TIMSK1 |= (1 << OCIE1B);

Selvfølgelig skal getADC2Value() erstattes med en værdi fra tabellen (hvis det kan betale sig med en sådan jf fpørgsmålet længere oppe)

Citér
Jeg mener der er en bedre måde at gøre dette på, nemlig at sætte prescaleren til off (timer stopped), men det kommer lidt an på om man ønsker at timeren kører hele tiden.
Jeg tror det er en fordel at den kører hele tiden. servoer har det med at have en "støj" bevægelse hvis man slukker og tænder dem så det ville ikke gøre noget at servoen
hele tiden får af vide hvor den skal stå indtil næste dosering. Det belaster den ikke hvis den får samme input, med mindre den skal arbejde for at komme til den possition den får besked på.
« Senest Redigeret: November 15, 2011, 21:38:16 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 #204 Dato: November 15, 2011, 23:44:58 »
Citér
ICR1 = 19999;
Dette sætter i hans tilfælde TOP-grænsen hvis man bruger Fast PWM (mode 14).

giver det samme resultat som det vi gør når vi siger
ICR1 = servoFrequency;
hvor
servoFrequency = F_CPU / 50;
eller bør vi sige
servoFrequency = (F_CPU / 50)-1;
for at få 19999 eller har det ingen betydning?

Det er korrekt at trække 1 fra resultatet efter divisionen, men vores beregning skal være korrekt også.

Hvis vi har F_CPU / 50, og F_CPU er 8000000, så får vi et resultat der hedder 160000.
Dette tal kan ikke være i ICR1. Det kan tallet - 1 (159999( heller ikke.

Hvad er det så egentlig for en opløsning vi skal bruge ? -Dette giver du svar på her...

Citér
skal fordeles i en top på f.eks mellem 1.2-1.8 ms i duty cyclen

Da vi har 8000000 clock cycles per sekund, vil det sige at vores opløsning med en clk / 1 prescaler vil være 1 / 8000000 sekund = 0.000000125 sekunder = 125 nS (125 nanosekunder).
Vi vil maks. kunne sætte vores TOP til 65535.
65535 * 125 nS = 0.008191875 sekunder, altså 8.19 mS, hvilket er det vindue vi har til rådighed.
(minimum 125 nS, maksimum 8.19 mS - og både 1.2 mS og 1.8 mS ligger indenfor dette område)

Med andre ord:
Timeren vil køre 8000000 / 65536 = 122.07 gange i sekundet (cirka), hvilket vi kan regne efter med...
122.07 * 0.008191 sek = (cirka) 1. Vores beregning er god nok. =)

Altså, tilbage til vores indstilling af ICR1. Denne sætter vi på højest mulige værdi, for at få finest mulige opløsning, nemlig værdien 65535.

Lad os så se på hvad værdien 1.2 mS og 1.8 mS hedder, når de er skaleret op, så de passer til ovenstående...
1.2 mS = 0.0012 sekunder
1.8 mS = 0.0018 sekunder

Der går 8000000 clock-cycles på 1 sekund.
8000000 * 0.0012 = 9600
8000000 * 0.0018 = 14400

Altså bør din duty-cycle ligge mellem værdien (9600 - 1) og (14400 - 1)

OK. Nu har vi så det tal, som potmetret egentlig skal regnes om til.

Selve ADC-værdien er en værdi mellem 0 og 1023.

Vi kan så derfor gøre følgende:

Kode:
uint16_t freq
...
...
freq = (uint16_t) (((14400 - 9600) * ((uint32_t) adcValue)) >> 10) + (9600 - 1);

Denne værdi burde vi så kunne lægge direkte ind i vores OCR1x register.

Bemærk: Det er i dette tilfælde vigtigt at indsætte (uint32_t) typecasting; for ellers kan vores tal blive 'klippet', da 16 bits ikke vil være nok til beregningen:
(14400 - 9600) * 1023 = 4910400 (som er højere end 65535, det maksimale der kan være i en 16-bit variabel).

Du ser også at jeg bitskifter med 10. Dette er en måde at dividere med 1024 hurtigt.

Men ovenstående har for mange konstante værdier. Dette kan vi ikke lide, for hvad hvis vi en dag vil ændre på prescaleren eller på F_CPU ?

Derfor...


Kode:
#define LOWER_US (1200)    /* 1.2 mS in microseconds */
#define UPPER_US (1800)    /* 1.8 mS in microseconds */
#define LOWER_LIMIT (F_CPU * LOWER_US / 1000000)
#define UPPER_LIMIT (F_CPU * UPPER_US / 1000000)

uint16_t freq;
...
freq = (uint16_t) (((UPPER_LIMIT - LOWER_LIMIT) * ((uint32_t)adcValue)) >> 10) + (LOWER_LIMIT - 1);

OCR1x = freq;

Læg mærke til at vi stadig regner i heltal, men ganger vi før vi dividerer, taber vi ikke noget i disse beregninger.


Citér
hvis potten retunerer en værdi mellem 0 og 255 ville det så ikke være en ide med en tabel der bare giver OCRxx en konstant? (ligesom dipswitch)...
...men er en tabel på 256 muligheder for stor sådan rent byte mæssigt?

En tabel ville være temmelig stor til en ATtiny.
Altså ville en sådan tabel fylde 256 stk 16-bit værdier, hvilket er 512 bytes.
Beregner vi resultatet, vil det fylde meget mindre.

En fordel ved at bruge en beregnings-rutine, er at man nemt kan lave værdierne, så de tilpasses, hvis fx. F_CPU skal ændres på et tidspunkt.

Citér
Citér
vi bestemmer selv hvor vores PWM skal tænde og hvor den skal slukke.
opnår vi det ved følgende (uddrag af kode) 10 sek til at køre en dutycycle der trækker servoen ud og efterfølgende 10 sek der køre servoen modsat?

Æh, nej. Her mente jeg på frekvens-kurven.
For du sender nogle pulser mange gange i sekundet til din servo.
Hvad jeg forsøgte at sige, var noget i stil med at vi selv bestemmer om den skal starte med at sætte i/o-benet højt eller om den skal starte med at sætte i/o-benet lavt. :)

Citér

Citér
Jeg mener der er en bedre måde at gøre dette på, nemlig at sætte prescaleren til off (timer stopped), men det kommer lidt an på om man ønsker at timeren kører hele tiden.
Jeg tror det er en fordel at den kører hele tiden. servoer har det med at have en "støj" bevægelse hvis man slukker og tænder dem så det ville ikke gøre noget at servoen
hele tiden får af vide hvor den skal stå indtil næste dosering. Det belaster den ikke hvis den får samme input, med mindre den skal arbejde for at komme til den possition den får besked på.

OK, så skal den da selvfølgelig være tændt. :)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #205 Dato: November 16, 2011, 22:17:47 »
Ok så prøver jeg lige det her og hører hvad du siger

først sætter jeg PWM op i servo.c
Kode:
#include <avr/io.h>
#include "servo.h"
#include "Timer1.h"
#include "ADC.h"
#include "Dosing.h"
void initServo()
{

}
void startServo(uint16_t aServoCountdown, uint16_t aRotation)
{
    /*servoCountdown = aServoCountdown;*/ /* number of pulses to send to servo, before turning it off */
    TCCR1A = 0; /* disable all PWM on Timer1 whilst we set it up */
    TCCR1B = 0; /* (disable clock-source, so the timer is frozen) */
    TIMSK1 = 0; /* disable all Timer1 interrupts */
    ICR1 = servoFrequency; /* frequency is every 20ms, 50 times per second (50Hz) */
    /* select fast PWM (mode 14) and no prescaler */
    if(GPIOR2 == SERVO_ENABLED)
{
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); /* enable output COMpare 1 A, output COMpare 1 B, set WGM to Fast PWM  */
}

    if(GPIOR2 == SERVO_ENABLED)
{
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); /* set WGM to fast PWM, choose clock source CS12:CS11:CS10 = 1, clk/1 (no prescaling) */
}
OCR1A = freq; /* pulse to load or unload servo on PA7 depending on the argument aRotation */
TIMSK1 |= (1 << OCIE1B); /* enable Output Compare Interrupt 1 B */
}

OCR1A = freq; hvor freq bliver beregnet i min dosing.c som følger her

Kode:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "Dosing.h"
#include "Timer0.h"
#include "ADC.h"
#include "Timer1.h"
#include "servo.h"
#define LOWER_US (1200) /* 1.2 mS in microseconds */
#define UPPER_US (1800) /* 1.8 mS in microseconds */
#define LOWER_LIMIT (F_CPU * LOWER_US / 1000000)
#define UPPER_LIMIT (F_CPU * UPPER_US / 1000000)
/*a number that rep the pos zero when servo is unloaded*/
uint16_t freq;
volatile uint8_t gSettings;
uint8_t repeats =0;

/*ADC TABELS START*/
uint8_t calculateSettingsDip(uint16_t gDipSwitches)
{
    static 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(gDipSwitches <= 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*/
    static const uint16_t timeTable[] PROGMEM = { 2 * 60, 24 * 60, 12 * 60, 6 * 60 };

    setMinuteCountdown(pgm_read_word(&timeTable[(gSettings >> 2) & 0x03]));
    timer0Elapsed();
}

void timer0Elapsed()
{
    static const uint8_t repTable[] PROGMEM = { 1, 10, 4, 2 };
    uint8_t repeats;
    uint8_t a;
    uint16_t aRotation;
    uint8_t unload; /* Value 0 - 256 since the pot2 is removed and unload is defined by constant*/


    unload = 0;   
    repeats = pgm_read_byte(&repTable[gSettings & 0x03]);

    for(a = 0; a < repeats; a++)
    {
        /* load: */
freq = (uint16_t) (((UPPER_LIMIT - LOWER_LIMIT) * ((uint32_t)adcValue)) >> 10) + (LOWER_LIMIT - 1); /* sets how much to load, read from pot*/
        waitSeconds(10);

        /* unload: */
freq = unload;
waitSeconds(10);
    }
}

void initDosing()
{
    startDosing(); /* in case of power failure, automatically restart with the current value */
}
/*FUNCTIONS END*/

Her får jeg en compiler fejl på adcvalue undeclared when first used
adcvalue bruger jeg jo i adc.c
Kode:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "ADC.h"
#ifndef inb
#define inb(sfr)    _SFR_BYTE(sfr)
#endif
#define ADC_SIZE    5                                                                   /* number of reads to average the ADC channel (we write one, and average over last 4) */
#define ADC_STABLE  (ADC_SIZE + 3)
#define ADC_FIRST   1                                                                   /* first ADC channel to read (ADC1) */
#define ADC_LAST    3                                                                   /* last ADC channel to read (ADC4)*/
static volatile uint16_t    adcValue[ADC_LAST - ADC_FIRST][ADC_SIZE];                   /* this buffer holds a lot of conversion results, enough for us to average the values */
static volatile uint8_t     adcReadIndex = 0;                                           /* this is the index we start reading values from (up to, but excluding writeIndex) */
static volatile uint8_t     adcWriteIndex = 0;                                          /* this is the index we write values to */
static volatile uint8_t     adcCounter = 0;                                             /* just a counter, that counts how many times we've read all the ADC channels */
static volatile uint8_t     adcChannel = 0;                                             /* current channel we're reading the ADC value from */
static uint8_t              adcBits = 8;
void waitUntilADCStable()
{
    while(adcCounter < ADC_STABLE)                                                      /* keep waiting, until ADC is reliable */
{
}
}
uint16_t getADCValue(uint8_t aADC)
{
    uint16_t    result;
    uint8_t     i;
    uint8_t     idx;

    idx = adcReadIndex;                                                                 /* read from this index; avoiding reading at the 'write position' */
    i = ADC_SIZE - 1;                                                                   /* number of values to read (eg. if ADC_SIZE is 10, we only read 9 values) */
    result = 0;                                                                         /* zero our result */
    while(i--)
{
        result += adcValue[aADC - ADC_FIRST][idx];                                      /* add the one value we've just read from the index */
        idx = idx >= (ADC_SIZE - 1) ? 0 : idx + 1;                                      /* next index */
}
    return(result / (ADC_SIZE - 1));                                                    /* return the averaged result */
}

/* uint16_t getADC1Value()
{
    return(getADCValue(1)); /* ADC value of startbutton */
/*}*/

uint16_t getADC2Value()
{
    return(getADCValue(2)); /* ADC value of pot */
}

SIGNAL (SIG_ADC)
{
    uint8_t        adLo;
    uint8_t        adHi;

    if(adcBits <= 8)                                                                    /* using 8-bit precision */
{
        if(ADCSRB & (1 << ADLAR))                                                       /* check hardware alignment-configuration and act accordingly */
{
            adLo = 0;
            adHi = inb(ADCH);                                                           /* (read highbyte only) */
}
        else
{
            adHi = 0;
            adLo = inb(ADCH);                                                           /* (read highbyte only) */
}
}
    else
{
        adLo = inb(ADCL); /* read lowbyte before highbyte! */
        adHi = inb(ADCH); /* read lowbyte before highbyte! */
}
    adcValue[adcChannel - ADC_FIRST][adcWriteIndex] = (adHi << 8) | adLo; /* save the value we've read above */
    ADCSRA |= (1 << ADSC); /* start another conversion */

    if(adcChannel++ >= ADC_LAST) /* next channel. If channel reached last channel... */
{
        adcChannel = ADC_FIRST; /* ...start over */
        adcWriteIndex = adcWriteIndex >= (ADC_SIZE - 1) ? 0 : adcWriteIndex + 1; /* increment write position and wrap if necessary */
        adcReadIndex = adcReadIndex >= (ADC_SIZE - 1) ? 0 : adcReadIndex + 1; /* increment read position and wrap if necessary */
        if(adcCounter < ADC_STABLE) /* if we haven't reached the number of conversions required for the ADC to stabilize... */
{
            adcCounter++; /* increment counter */
}
}
    ADMUX = (0 << REFS1) | (0 << REFS0) | (adcChannel & 0x07); /* set which channel to read next time */
}
void initADC(uint8_t aBits)
{
    adcCounter = 0; /* this is so we can see when the ADC conversions are stable */
    adcWriteIndex = 0; /* start writing at index 0 */
    adcReadIndex = adcWriteIndex + 1; /* read right after the write-index */
    adcChannel = ADC_FIRST; /* initialize ADC channel number to read */
    adcBits = aBits;

    ADMUX = (0 << REFS1) | (0 << REFS0) | (adcChannel & 0x07); /* VCC used as analog reference, first channel */
    ADCSRA = (1 << ADEN) | (1 << ADIF) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
    ADCSRB = (0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0) | (0 << ADLAR); /* free running mode, result is right aligned */
    ADCSRA |= (1 << ADSC); /* start conversion */
}
da det er adc2 jeg læser fra, kan jeg så lave sætningen i dosing.c om til
req = (uint16_t) (((UPPER_LIMIT - LOWER_LIMIT) * ((uint32_t)getADC2Value())) >> 10) + (LOWER_LIMIT - 1);

desuden er jeg i tvivl om hvorvidt
waitSeconds(10);
gør at servoen har 10 sec til at bevæge sig fra freq unload til freq load, eller om jeg hellere skal have en for løkke ind



« Senest Redigeret: November 16, 2011, 22:33:10 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 #206 Dato: November 17, 2011, 01:29:30 »
Citér
da det er adc2 jeg læser fra, kan jeg så lave sætningen i dosing.c om til
req = (uint16_t) (((UPPER_LIMIT - LOWER_LIMIT) * ((uint32_t)getADC2Value())) >> 10) + (LOWER_LIMIT - 1);

Ja, det mener jeg er korrekt. :)

Citér
desuden er jeg i tvivl om hvorvidt
waitSeconds(10);
gør at servoen har 10 sec til at bevæge sig fra freq unload til freq load, eller om jeg hellere skal have en for løkke ind

Det burde fungere fint med waitSeconds(10);.
En for-løkke ville fungere på stort set samme måde, men ulempen ved en for-løkke er at hvis du skifter CPU-frekvens, vil for-løkken ikke længere vente 10 sekunder, men mindre, hvis CPU-frekvensen er sat op, eller mere, hvis CPU-frekvensen er sat ned. Det er bedre at bruge waitSeconds i dette tilfælde.


Jeg har et forslag til lidt ændringer i Servo.c og Dosing.c...

Servo.c:
Kode:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "servo.h"

void initServo()
{
    cli(); /* (disable all interrupts) */
    TCCR1A = 0; /* disable all PWM on Timer1 */
    TCCR1B = 0; /* disable clock-source, so the timer is frozen */
    TIMSK1 = 0; /* disable all Timer1 interrupts */

    DDRA |= (1 << DDA7); /* set OC1A as output */

    ICR1 = 65534; /* we want our top to be as large as possible, for best resolution, but we'll keep it, so there's one value the OCR1x registers cannot reach. */

    OCR1A = 65535; /* make sure this has a known value */
    OCR1B = 65535; /* make sure this has a known value */

    if(GPIOR2 == SERVO_ENABLED)
    {
        TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); /* set WGM to fast PWM, choose clock source CS12:CS11:CS10 = 1, clk/1 (no prescaling) */
    }
    /* select fast PWM (mode 14) and no prescaler */
    if(GPIOR2 == SERVO_ENABLED)
    {
        TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); /* enable output COMpare 1 A, output COMpare 1 B, set WGM to Fast PWM  */
    }
}

void startServo(uint16_t aRotation)
{
OCR1A = aRotation; /* pulse to load or unload servo on PA7 depending on the argument aRotation */
}

Jeg har ovenover flyttet alt, der kun skal gøres én gang op i initServo().
Jeg har sat initialiseringen af output-benet ind i initServo() også.
Jeg har fjernet initialiseringen af Timer1 interruptet, da det nok ikke skal bruges.
Så har jeg også initialiseret OCR1A og OCR1B til 65535, for at undgå at der bliver bøvl med dem mens vi arbejder med koden.

Derudover har jeg sat top-grænsen for PWM til 65534 (ikke 65535), fordi hvis vi gør dette, så kan vi 'fryse PWM' ved at sætte PWM-værdien til 65535! -Da vil benet ikke skifte værdi mellem høj/lav.

Jeg har også ændret OCR1A = freq; til OCR1A = aRotation;

-Så giver du nemlig denne parameter som argument til funktionen.
(Prøv så vidt muligt at undgå globale variabler).

Du vil sikkert også bemærke at jeg har fjernet alle referencer til andre filer, dvs. header-filer som Main.h, Dosing.h, osv.
Det er fordi, hvis man tænker på koden som selvstændige enheder der kan tages ud og sættes ind, bør man ikke lave snore 'baglæns' i systemet, men kun forlæns. Man kunne sige det er som en 'uro'. hver dims der hænger i en snor, kan ikke pludselig side over den dims som den hænger ned fra. ;)

Jeg har smidt Timer1.c helt ud, da der ikke rigtigt er noget brugbart i den længere; Servo.c har sådan set overtaget dens funktion. :)
-Så Timer1.h skal ikke længere inkluderes i andre filer (som fx. Main.h).

Du er nok også nødt til at ændre prototypen for startServo i Servo.h:
Kode:
void startServo(uint16_t aRotation);


Dosing.c:
Kode:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "Dosing.h"
#include "Timer0.h"
#include "ADC.h"
#include "servo.h"

#define LOWER_US (1200) /* 1.2 mS in microseconds */
#define UPPER_US (1800) /* 1.8 mS in microseconds */
#define LOWER_LIMIT (F_CPU * LOWER_US / 1000000)
#define UPPER_LIMIT (F_CPU * UPPER_US / 1000000)
#define UNLOAD 0 /*a number that rep the pos zero when servo is unloaded*/
#define LOAD(a) (uint16_t) (((UPPER_LIMIT - LOWER_LIMIT) * ((uint32_t) a)) >> 10) + (LOWER_LIMIT - 1)

volatile uint8_t gSettings;

/*ADC TABELS START*/
uint8_t calculateSettingsDip(uint16_t gDipSwitches)
{
    static 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(gDipSwitches <= 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*/
    static const uint16_t timeTable[] PROGMEM = { 2 * 60, 24 * 60, 12 * 60, 6 * 60 };

    setMinuteCountdown(pgm_read_word(&timeTable[(gSettings >> 2) & 0x03]));
    timer0Elapsed();
}

void timer0Elapsed()
{
    static const uint8_t repTable[] PROGMEM = { 1, 10, 4, 2 };
    uint8_t repeats;
    uint8_t a;


    repeats = pgm_read_byte(&repTable[gSettings & 0x03]);

    for(a = 0; a < repeats; a++)
    {
        /* load: */
        startServo(LOAD(getADC2Value())); /* sets how much to load, read from pot */
        waitSeconds(10);

        /* unload: */
        startServo(UNLOAD);
        waitSeconds(10);
    }
}

void initDosing()
{
    startDosing(); /* in case of power failure, automatically restart with the current value */
}
/*FUNCTIONS END*/

I toppen af filen...
Jeg har fjernet den globale variabel 'uint16_t freq;'
Jeg har fjernet den globale variabel 'uint8_t repeats =0;'
Jeg har indsat en #define der definerer en konstant værdi for unload (den hedder UNLOAD).
Jeg har indsat en macro, dvs. en #define der beregner værdien for load (den hedder LOAD).

I timer0Elapsed()...
Jeg har fjernet uint16_t aRotation;
Jeg har fjernet uint8_t unload;
Jeg har ændret freq = unload; til at være startServo(UNLOAD);
Jeg har ændret freq = ...; til at være startServo(LOAD(getADC2Value()));
Jeg har således i beregningen erstattet adcValue med getADC2Value()

Muligvis får du nogle compile-fejl pga. ændringerne, så må du bare sige til. :D

Jeg mener selv at koden skulle være blevet lidt mere overskuelig, da den en del steder er blevet kortere; men jeg kan selvfølgelig tage fejl her. ;)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #207 Dato: November 17, 2011, 23:54:42 »
Tusind tak.

så fik jeg den compilet
og nu laver den da hex filen men stadig lidt problemer med nogle warnings.
prøvede alligevel at smide den på avr men virkede self ikke  :P
Citér
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a -c Main.c -o Main.o
Main.c:36: warning: 'SIG_PCINT0' appears to be a misspelled signal handler
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a -c ADC.c -o ADC.o
ADC.c:54: warning: 'SIG_ADC' appears to be a misspelled signal handler
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a -c Dosing.c -o Dosing.o
Dosing.c: In function 'timer0Elapsed':
Dosing.c:67: warning: implicit declaration of function 'waitSeconds'
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a -c Timer0.c -o Timer0.o
Timer0.c:39: warning: 'SIG_OVERFLOW0' appears to be a misspelled signal handler
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a -c servo.c -o servo.o
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a  -o aquadose.elf Main.o ADC.o Dosing.o Timer0.o servo.o
rm -f aquadose.hex
avr-objcopy -j .text -j .data -O ihex                                              aquadose.elf aquadose.hex
if [ -e aquadose.elf ]; then { avr-size aquadose.elf; } elif [ -e aquadose.hex ]; then { avr-size aquadose.hex; } fi
   text      data       bss       dec       hex   filename
   1188         2        32      1222       4c6   aquadose.elf

Har sådan et multimeter
http://australianrobotics.com.au/products/dm830d-lcd-digital-multimeter
så kan ikke rigtigt måle frekvensen "tror jeg"

Desuden har jeg konstateret at servoen skal have strøm fra samme enhed som leverer til microcontroleren.
jeg troede at jeg kunne smide en anden strømforsyning på servoen og så bare få impulserne fra avr men det har jeg testet med en servotester og det kan tilsyneladende ikke lade sig gøre. ved ikke lige hvorfor.
Men skaber det problemer når servoen går igang? jeg mener vil det ikke lave en lille forstyrrelse på strømmen til avr, når servo lige pludselig trækker 5 volt?
med mindre jeg kan lave et eller andet trick med f.eks at samle alle gnd
« Senest Redigeret: November 18, 2011, 02:28:16 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 #208 Dato: November 18, 2011, 07:20:17 »
så fik jeg den compilet
og nu laver den da hex filen men stadig lidt problemer med nogle warnings.
prøvede alligevel at smide den på avr men virkede self ikke  :P

Når jeg programmerer en AVR, har jeg slet ikke nogle simulator-værktøjer.
Min fremgangsmåde er cirka følgende:
1: Skriv et lille program, som gør en simpel ting.
2: Få det til at compile.
3: Brænd det over på AVR'en.
4: Se om det fungerer som jeg forventede.
5: Hvis der er fejl, ret fejlene, tilbage til 2
6: Ingen fejl, udvid med næste funktion, tilbage til 2.

I andre tilfælde, når jeg har et større program, hvor jeg skal lave ny funktionalitet, skriver jeg et lille prøve-program, som kun gør den ene ting, og når det virker, fører jeg princippet over i det større program.

Citér
avr-gcc -Wall -Os -DF_CPU=1000000 -mmcu=attiny44a -c Main.c -o Main.o
Main.c:36: warning: 'SIG_PCINT0' appears to be a misspelled signal handler

Ups, min fejl; jeg fik blandet interrupt-navnene sammen, fordi hvis man bruger den ene type erklæring, skal de navngives i én stil, hvis man bruger en anden type erklæring, skal de navngives i en anden stil.
ISR( ... ) er den anbefalede type erklæring, så den vil jeg så lære fra mig... ;)

Prøv i Main.c, at ændre...
Kode:
SIGNAL (SIG_PCINT0)
...til...
Kode:
ISR(PCINT0_vect)

Citér
ADC.c:54: warning: 'SIG_ADC' appears to be a misspelled signal handler

Prøv i ADC.c, at ændre...
Kode:
SIGNAL (SIG_ADC)
...til...
Kode:
ISR(ADC_vect)

Citér
Dosing.c:67: warning: implicit declaration of function 'waitSeconds'

Hvor ligger funktionen 'waitSeconds' ?
-Det kunne være en god idé at lægge den ud i sin egen fil, fx. "Wait.c", og så lave prototypen på funktionen i "Wait.h"


Citér
Timer0.c:39: warning: 'SIG_OVERFLOW0' appears to be a misspelled signal handler

Prøv i Timer0.c at ændre...
Kode:
SIGNAL (SIG_INT0)
...til...
Kode:
ISR(INT0_vect)

Forklaring:
Da ingen af disse interrupt-navne er genkendt, vil de ikke køre.
Derfor vil programmet ikke kunne virke...
1: Timer0-interruptet kører ikke; dvs. sekunderne tæller ikke.
2: ADC-interruptet kører ikke, dvs. vi får ikke værdien fra ADC'en beregnet/opdateret
3: Interrupt0 er ikke installeret, hvilket vil sige at vores start-knap ikke bliver aflæst.

-Med de korrekte navne, skulle det dog have en chance for at blive lidt bedre. :)

Citér
   text      data       bss       dec       hex   filename
   1188         2        32      1222       4c6   aquadose.elf
[/quote]

TEXT + DATA = 1190 bytes: Selve programmet kan fint ligge i Flash hukommelsen.
DATA + BSS = 34 bytes: Der er SRAM nok på chippen til både data og stack.

TEXT bliver lagt kun i Flash-hukommelsen.
DATA bliver lagt i Flash-hukommelsen, men når programmet starter op, kopierer det DATA fra Flash over i SRAM'en.
BSS er værdier der starter på 0. Der bliver reserveret plads til dem i SRAM'en.

Citér
Har sådan et multimeter
http://australianrobotics.com.au/products/dm830d-lcd-digital-multimeter
så kan ikke rigtigt måle frekvensen "tror jeg"
Citér

Hvis du kigger mellem 'diode-symbolet' og 'hFE', er dette så ikke 'frekvens' ? - Det
Sjovt, jeg har selv et DT830B, jeg bruger en del, men det har ikke det symbol.
...Men jeg vil nok prøve at holde udkig og se om jeg kan få fat i et DM830D, da det jo er lidt bedre end mit nuværende (jo, jeg har en Fluke, men den fylder og vejer cirka det dobbelte).

Citér
Desuden har jeg konstateret at servoen skal have strøm fra samme enhed som leverer til microcontroleren.

Det kan være jeg tager fejl, men jeg mener det ikke burde være nødvendigt. Hvad der er vigtigt, er at du har sat microcontrolleren's GND til servoens GND. :)

Citér
jeg troede at jeg kunne smide en anden strømforsyning på servoen og så bare få impulserne fra avr men det har jeg testet med en servotester og det kan tilsyneladende ikke lade sig gøre. ved ikke lige hvorfor.

...Hvis man ikke kan, så kan man ikke, men det ville undre mig hvis det er sådan... ;)

Men skaber det problemer når servoen går igang? jeg mener vil det ikke lave en lille forstyrrelse på strømmen til avr, når servo lige pludselig trækker 5 volt?
med mindre jeg kan lave et eller andet trick med f.eks at samle alle gnd

Det kommer helt an på hvor mange Ampere servoen trækker.
Du bør dimensionere din strømforsyning, så den har ampere nok til at trække servoen plus microcontroller.
Et hurtigt gæt er det antal Ampere servoen trækker plus 50 mA til microcontrolleren, plus 50 mA ekstra til dårlige tider. ;)
-Og så skal du nok have en elektrolyt kondensator på før din spændings-regulator, og en efter din spændings-regulator.
Hvis jeg trækker 50 mA på mit board, plejer jeg at bruge 33uF før spændings-regulatoren, og 10uF efter.
Men det vil nok være en god idé at sætte nogle større på.
Hvad jeg ville gøre, er noget i stil med... Start med to stk. 10uF. Det burde ikke være nok til at trække apparatet.
Skift værdien på kondensatorerne op til 33uF, derefter 56 uF, så 82uF, 100uF, ... med lidt spring ind imellem.. Når den så begynder at kunne trække, så hop nogle værdier op, det gør ikke noget du dobler op, med mindre, selvfølgelig at kondensatorerne kommer til at fylde 10 cm i højden og vejer en bondegård. ;)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #209 Dato: November 18, 2011, 18:40:57 »
Fantastisk  :P
Citér
Timer0.c:39: warning: 'SIG_OVERFLOW0' appears to be a misspelled signal handler
Prøv i Timer0.c at ændre...

SIGNAL (SIG_INT0)...til...

ISR(INT0_vect)

Har ikke en SIGNAL (SIG_INT0), men en SIGNAL (SIG_OVERFLOW0) /*This interrupt occurs when TCNT0 wraps to 0*/
hvis jeg ændre den til ISR(INT0_vect) får jeg stadig samme fejl.
men hvis det er TCNT0 så er det vel timer/counter 0 der checkes for overflow. vil det så være rigtigt at bruge
ISR(TIM0_OVF_vect) istedet for SIGNAL (SIG_OVERFLOW0)?
TIM0_OVF er iflg data arket for 44'ern Timer/Counter0 Overflow

De andre fejl er rettet og fungerer fint.
lavede en void waitSeconds(uint8_t aSeconds); i timer0.h
waitSeconds er jo en del at timerfunktionen der holder øje med døgnet så syntes ikke den skulle fjernes fra timer0.c hvor den bruges.
og fejlen forsvandt da jeg lavede linjen i timer0.h
« Senest Redigeret: November 18, 2011, 19:09:22 af jascore »