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

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 #45 Dato: August 18, 2011, 08:03:44 »
Inde i sig_overflow0, som trigger på en countdown) kalder jeg så rutinen repeats hvor gentagelser bliver defineret og brugt i ADC.c

ADC.c skal sådan set kun aflæse ADC'en. Der vil være en rutine, der hedder getADC3Value(), som returnerer værdien af ADC'en til den kaldende rutine. Noget a'la...
Kode:
/*
 * File   : ADC.c
 *
 */

#include <avr/io.h>

#include "ADC.h"

static uint8_t adcIndex = 0;
static uint16_t adc3Value[2];
static uint16_t adc2Value[2];

uint16_t getADC3Value()
{
    return(adc3Value[adcIndex]);
}

void initADC(uint8_t aBits)
{
    /* not yet implemented */
}

-Ovenstående er stort set tomt, og det er fordi jeg ikke er gået i dybden med den endnu.
For at den skal aflevere et rimeligt stabilt tal til dig, bør den aflæse - lad os sige - 4 gange, og aflevere gennemsnittet af disse 4 værdier til dig. Jeg vil kigge lidt videre på dette.

Citér
aMinuteCountdown bliver defineret i dosing's set_dosing fouktionen og brugt i sig_overflow0,  og det hele med en 8 bit timer/counter så tror sq jeg har forstået det nu - næsten

-Ja, aMinuteCountdown er en parameter (et argument), som bliver givet, når setMinuteCountdown funktionen kaldes. a'et i aMinuteCountdown står for "argument", hvilket vil sige, at man så nemt kan se, at det er en variabel der kommer som argument.

Citér
Lavede selv main.h udfra hvordan jeg kunne se at du havde lavet de andre header filer

initDosing er defineret i Dosing.h, initTimer0 er defineret i Timer0.h, så main.h burde se således ud:

Kode:
#ifndef __main_h__
#define __main_h__


#endif /* _main_h_ */

-Altså ganske tom indtil videre.

Citér
Men her ville jeg jo også gerne have haft void initADC( 8 ); med men det kan jeg jo ikke, da den allerede er som void initADC(uint8_t aBits) i ADC.h
Gør jeg det alligevel bliver de conflicting. Gør jeg det ikke bliver initADC( 8 ) undefined.

Grunden til at initADC er undefined, er at du ikke har en ADC.c. Prøv at lægge ovennævnte ADC.c ind (som test), så tror jeg fejlen skulle forsvinde.
Bemærk: Det er linkeren der brokker sig over at initADC mangler.

...Interrupts er smarte; de kan spare mange CPU-kræfter, det er derfor jeg gerne vil have dig til at lære dem. Nogle ting kan drille ved interrupts, men det vil du nok ikke opleve lige med det første, og på en microcontroller bliver den slags fejl ikke så katastrofale som på en computer.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #46 Dato: August 18, 2011, 15:31:24 »
Citér
Grunden til at initADC er undefined, er at du ikke har en ADC.c. Prøv at lægge ovennævnte ADC.c ind (som test), så tror jeg fejlen skulle forsvinde.
Bemærk: Det er linkeren der brokker sig over at initADC mangler.

Ganske rigtigt nu siger den bare at den er defined but not used og det er jo ganske rigtigt.

Læser lidt videre på koden for at få banked metoden helt fast og begynder at se lidt på PWM delen.  ;D


 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #47 Dato: August 19, 2011, 01:07:22 »
Vil det her virke som repeats ?
(en del af koden i dosing.c

Kode:
void timer0Elapsed()
{
   
for(a = 0; a <= repeats; a++)
{
for(Is looking for a way to implement seconds from the timer rutine to make the for last for 10 seconds befor continue)
{
rotation = adc3Value; /*TOP, (20ms) load*/
ServoMove()
}
for(Is looking for a way to implement seconds from the timer rutine to make the for last for 10 seconds befor continue)
{
rotation = adc2Value; /*TOP, (20ms) unload*/
ServoMove()
}
}
}

void ServoMove()
{
TCCR1A = 0; /* disable all PWM on Timer1 whilst we set it up*/
ICR1 = 20000; /*frequency is every 20ms/*

/*sets  fast PWM (mode 14) and no prescaler*/
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); 

DDRB |= (1<<PA7); /*Sets OC0B as output*/
OCR1A = ICR1 * rotation /20; /*pulse to load or unload servo on pa7 depending on the variable rotation*/
}

Jeg er lidt i tvivl om der vil blive pumpet signal ud i 20 ms cycler så længe funktionen ServoMove() bliver kaldt i "for" løkken i timer0elapsed eller om det kun sker 1 gang for hvert ++ der vil være, for det skal helst være kontinuerligt

 

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 #48 Dato: August 19, 2011, 10:32:15 »
Citér
Jeg er lidt i tvivl om der vil blive pumpet signal ud i 20 ms cycler så længe funktionen ServoMove() bliver kaldt i "for" løkken i timer0elapsed eller om det kun sker 1 gang for hvert ++ der vil være, for det skal helst være kontinuerligt

Mit gæt er at servoen vil køre, indtil du slukker den, dvs. jeg tror at ServoMove tænder servoen, som så vil blive ved med at køre, indtil du kalder den uskrevne funktion "ServoStop".

Men jeg ville foretrække at lave et servo-timer-interrupt, og lade dette trigge på sluk-pulsen; muligvis kan denne stump kode være til nytte...

Kode:
/*
 * File: Timer1.h
 *
 * (8-bit timer with simple event handling)
 *
 */

#ifndef __Timer1_h__
#define __Timer1_h__

uint8_t isServoRunning();
void stopServo();
void startServo(uint16_t aServoCountdown, uint16_t aRotation);
void initTimer1();

#endif /* __Timer1_h__ */

Kode:
/*
 * File: Timer1.c
 *
 * (8-bit timer with simple event handling)
 *
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#include "Timer1.h"

static volatile uint16_t    servoCountdown = 0;                 /* variables that are changed by an interrupt, *MUST, MUST* be volatile! */
const uint16_t              servoFrequency = F_CPU / 50;        /* frequency is every 20ms = 50 times per second (50Hz) */

uint8_t isServoRunning()
{
    return(servoCountdown != 0);
}

SIGNAL (SIG_OUTPUT_COMPARE1B)
{
    if(servoCountdown)
    {
        if(0 == --servoCountdown)
        {
            stopServo();                                        /* turn off servo-timer (including interrupt) */
        }
    }
}

void stopServo()
{
    TCCR1B = 0;                                                 /* CS12 : CS11 : CS10 = 0, no clock source (Timer/Counter stopped) */
    TCCR1A = 0;
    TCNT1 = 0;
    OCR1A = 0;
    OCR1B = 0;
    ICR1 = 0;
    TIMSK1 = 0;
    TIFR1 = 0xff;
}

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 */
    TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);      /* enable output COMpare 1 A, output COMpare 1 B, set WGM to Fast PWM  */
    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 */

    /* ATtiny24/44/84 Preliminary, page 111; section 12.11.8 */
    TIMSK1 |= (1 << OCIE1B);                                    /* enable Output Compare Interrupt 1 B */
}

void initTimer1()
{
    cli();                                                      /* disable interrupts if not already done */

    DDRB |= (1 << PA7);                                         /* set OC0B as output */

    stopServo();                                                /* initially, the servo is not running */
}

Du kan så (efter min bedste overbevisning) #include "Timer1.h" fra fx. Dosing.c, og kalde
Kode:
startServo(50, getADC3Value());
-Da vil servoen køre i 50 pulser (i alt 1 sekund), og så slukke.

Jeg har inkluderet en funktion, så du kan se om servoen er igang med at køre.
isServoRunning() vil returnere true (1), hvis servoen kører, ellers false (0).

Du kan evt. lægge følgende ind i Timer0.h (efter #define, før #endif)...

Kode:
void waitSeconds(uint8_t aSeconds);

...og følgende i Timer0.c (fx. lige over setMinuteCountdown)...

Kode:
void waitSeconds(uint8_t aSeconds)
{
aSeconds = (aSeconds + seconds);                    /* add current second value to aSeconds */
if(aSeconds >= 60)                                  /* we can't wait more than 59 seconds, so if the result is larger than 59... */
{
aSeconds -= 60;                             /* ...then subtract 60 */
}
while(aSeconds != seconds)                          /* wait until seconds has the same value as aSeconds */
{
}
}

I Dosing.c burde du så kunne #include "Timer0.h" og så få muligheden for at vente x antal sekunder (max. 59 sekunder!)

Følgende er lidt omskrivning af din tidligere kode...

Kode:
void timer0Elapsed()
{
    uint8_t a;
    uint16_t adc2Value;
    uint16_t adc3Value;

    adc2Value = getADC2Value();
    adc3Value = getADC3Value();

    for(a = 0; a < repeats; a++)
    {
        /* load: */
        waitSeconds(10);
        startServo(50, adc3Value);

        /* unload: */
        waitSeconds(10);
        startServo(50, adc2Value);
    }
}

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #49 Dato: August 20, 2011, 23:49:34 »
Hvad skulle jeg dog gøre uden din hjælp.
Er ved at forstå at interrupts er vejen frem.  :o

implementere det i eksisterende kode.
Takker og bukker.

ville sådan ønske at jeg kunne gøre et eller andet til gengæld  :-[
HVis du nogensinde starter et saltvandsakvarie op så må du sige til he he.

Har lige et spørgsmål. Hvorfor ikke bruge CTC? kræver det ikke mindre regnekraft. Ved godt at lige i dette tilfælde her skal jeg bruge 16 bit timeren til PWM men generelt er det så ikke bedst at komme så tæt på ren hardware counter/timers og så bruge flags? (det er vel også en form for interrupts) ???
« Senest Redigeret: August 20, 2011, 23:51:29 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 #50 Dato: August 21, 2011, 04:12:53 »
Er ved at forstå at interrupts er vejen frem.  :o

Ja, interrupts er ret gode, for de bruger et minimum af CPU-tid, og er ret præcise.

Citér
ville sådan ønske at jeg kunne gøre et eller andet til gengæld  :-[
HVis du nogensinde starter et saltvandsakvarie op så må du sige til he he.

Tja, det kan være, når jeg en gang skal flytte i hus; men dér hvor jeg bor i øjeblikket, er der slet ikke plads. ;)

Citér
Har lige et spørgsmål. Hvorfor ikke bruge CTC? kræver det ikke mindre regnekraft.

"polling" (dvs. hvor man hele tiden kigger på en værdi, som ændrer sig) har en del ulemper:
  • Der går lang tid (i alt) med at se på værdien.
  • Nogle værdier når man ikke at se, fordi CTC ændrer sig.

Citér
Ved godt at lige i dette tilfælde her skal jeg bruge 16 bit timeren til PWM men generelt er det så ikke bedst at komme så tæt på ren hardware counter/timers og så bruge flags? (det er vel også en form for interrupts) ???

Du er tæt på. Timeren sætter nogle flags, og interrupts bliver udført når disse flags sættes. Dvs. du kan faktisk (hvor det er muligt) trigge et interrupt, ved at sætte det flag som timeren sætter.

Men igen... hvis man laver et loop, hvor man bliver ved med at kigge på én ting, kan det være man går glip af en anden, plus at man bruger meget CPU-tid på at kigge på den ene ting.
eks:
Kode:
while(TCNT <= 19998)
{
    /* here we're only waiting, no code. */
}
/* do something here */

Ovenstående har et par fejl...
1: Vi spenderer op til 1 sekund med at vente på at TCNT kommer op på 19999.
2: Er vi uheldige, er TCNT lig med 19998 lige når den aflæses, hvilket vil sige at while-løkken udføres igen. Når while så hopper tilbage, bruges 2 clock-cycles, hvilket vil sige at TCNT skifter 2 gange, og den kommer op på 20000, hvilket vil sige at den nul-stilles, så den er 0, når while igen tester værdien. Derved bruger vi mere end 1 sekund, hvis vi er uheldige, dvs. vi skipper en aflæsning, hvis vi er uheldige.
-Endnu værre havde det været, hvis vi skulle lave noget imens vi ventede på at TCNT nåede op på 19999; der ville være flere muligheder for at TCNT 'kappede over' og røg på 0, før vi kunne aflæse den.

 

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 #51 Dato: August 23, 2011, 00:12:45 »
...ADC.h, ADC.c...

Nu har jeg kigget lidt på ATtiny44's ADC håndtering.
Den er næsten helt magen til de ADC'er jeg har brugt tidligere.
Der er dog en forskel; dens reference spænding er 1.1V eller VCC.
De andre's reference-spændinger er normalt 2.56V.

Men det, at ATtiny44's reference-spænding er VCC, gør tingene lettere for dig! :)
1: Du behøver ikke at have en modstand i serie med dit 10K potmeter!!
2: Din omregning bliver en lille smule simplere end ellers.

Her er først en header-fil, som kan bruges til begge de efterfølgende kode-stumper...
Kode:
/*
 * File: ADC.h
 *
 * (Controls when the dosing occurs, but also holds the code to do the actual dosing)
 *
 */

#ifndef __ADC_h__
#define __ADC_h__

void waitUntilADCStable();
uint16_t getADC2Value();                                    /* this routine returns the low 10 bits of the ADC2's value, no matter if you use 8-bit or 10-bit ADC */
uint16_t getADC3Value();                                    /* this routine returns the low 10 bits of the ADC3's value, no matter if you use 8-bit or 10-bit ADC */
void initADC(uint8_t aBits);

#endif /* __ADC_h__ */

I din main() rutine, bør du kalde initADC(8); eller initADC(10); og lige efter din 'sei();' instruktion (før 'while(1)'), bør du kalde...

Kode:
waitUntilADCStable();

-Denne rutine venter på at ADC'en afgiver fornuftige resultater du kan stole nogenlunde på.


OK nu til selve koden.. Først en forholdsvis enkel stump kode. Dette er ikke koden du bør implementere, men denne kode er primært fordi du så lettere kan overskue hvad der sker.

Koden her aflæser et par ADC'ers værdi (ADC2 og ADC3), og gemmer dem til senere videregivelse.
Der gemmes kun en enkelt værdi. Det, som er specielt ved denne kode, er at den ud over at køre flere gange i sekundet (prescaler 128), skriver et andet sted end den læser.
Du undgår således at læsning kommer i kambolage med skrivningen.

Kode:
/*
 * File: ADC.c - simple
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#include "ADC.h"

#ifndef inb
#define inb(sfr) _SFR_BYTE(sfr)
#endif

#define ADC_STABLE  4                                                                   /* ADC should be stable after 4 complete read cycles. You may need to adjust this number */

#define ADC_FIRST   2                                                                   /* first ADC channel to read */
#define ADC_LAST    3                                                                   /* last ADC channel to read */


static volatile uint16_t    adcValue[ADC_LAST][2];                                      /* this buffer holds our conversion results. Write to one place, read from another. */
static volatile uint8_t     adcIndex = 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 */

void waitUntilADCStable()
{
    while(adcCounter < ADC_STABLE)                                                      /* keep waiting, until ADC is reliable */
    {
    }
}

uint16_t getADC2Value()
{
    return(adcValue[2][adcIndex]);
}

uint16_t getADC3Value()
{
return(adcValue[3][adcIndex]);
}

SIGNAL (SIG_ADC)
{
    uint8_t        adLo;
    uint8_t        adHi;

    adLo = inb(ADCL);                                                                   /* read lowbyte before highbyte! */
    adHi = inb(ADCH);                                                                   /* read lowbyte before highbyte! */

    adcValue[adcChannel][adcIndex ^ 1] = (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 */
        adcIndex ^= 1;                                                                  /* swap index, so we write to one place and read from another */
        adcCounter++;                                                                   /* increment counter */
    }
    ADMUX = (0 << REFS1) | (0 << REFS0) | (adcChannel & 0x07);                          /* set which channel to read next time */
}

void initADC(uint8_t aBits)                                                             /* in this simple example, we ignore the precision bits; we always use 10-bit conversion */
{
    adcCounter = 0;                                                                     /* (initialization of adcCounter is not really needed) */
    adcIndex = 0;                                                                       /* read at index 0, write at index 1 */
    adcChannel = ADC_FIRST;                                                             /* initialize ADC channel number to read */

    ADMUX = (0 << REFS1) | (0 << REFS0) | (adcChannel & 0x07);                          /* VCC is 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);                                /* free running mode */
    ADCSRA |= (1 << ADSC);                                                              /* start conversion */
}

Og nu den mere komplicerede kode. Denne stump kode aflæser nogle af ADC'ens kanaler, og gemmer de aflæste resultater i en buffer. Når du så vil have værdierne ud, tages gennemsnittet af de sidste (4) alæsninger...

Kode:
/*
 * File: ADC.c - complex version
 */

#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   2                                                                   /* first ADC channel to read */
#define ADC_LAST    3                                                                   /* last ADC channel to read */


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 getADC2Value()
{
    return(getADCValue(2));
}

uint16_t getADC3Value()
{
    return(getADCValue(3));
}


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                                                                                /* using 10-bit precision */
    {
        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 */
}

Hvorvidt koden fungerer, kan jeg ikke sige med sikkerhed.
Men her lægger jeg mere vægt på at du forstår systemet, end at det faktisk virker.
Hvis det virker, så har vi det som bonus. ;)

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #52 Dato: August 23, 2011, 11:41:21 »
Det var en ordentlig omgang  :P
det vil jeg lige tygge igennem og forstå hvordan det fungere mens jeg venter spændt på pakken  ;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 #53 Dato: August 24, 2011, 01:51:49 »
...mens jeg venter spændt på pakken  ;D

Det ser ud til at de har 'opdaget' pakken nu. Dens tracking nr. er registreret; den venter nok stadig på afhentningen fra indleverings-stedet.
I mellemtiden har jeg fået IDC-stik og kappen til det 25-polede stik, så i dag, onsdag, vil jeg så sende resten.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #54 Dato: August 24, 2011, 02:07:53 »
Det lyder super glæder mig helt vildt til at komme igang med lidt praktisk he he
Den sidste kode syntes jeg ikke lige lå til højrebenet he he men tror jeg er ved at fange den.

Hvor store er udsvingene på en ADC value?
Jeg skal faktisk kun bruge en udsvingsmargien på lige over 100o ud af servoens 180.
Opløsningen skal være på ca 2 us i PWM. det må give lidt større spillerum i aflæsningspræcisionen når værdier går fra -30o til 45o og ikke alle 180o fordelt over pot værdierne.

Jeg overvejer stadig at droppe det ene pot og så kalibrere nulpunktet software mæssigt vha. en fast værdi, det spare da en del regnekræft som så måske kan bruges på at lave et mere præcist gennemsnit for det andet pot.

Jeg håber at det bliver sådan at der ikke er et større udsving end 0,2 ml +- pr dosering da saltvand kan være rimeligt hysterisk over for under og overdosering. men selvfølgelig fordelt over et vist antal doser
« Senest Redigeret: August 24, 2011, 02:12:44 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 #55 Dato: August 24, 2011, 02:52:14 »
Det lyder super glæder mig helt vildt til at komme igang med lidt praktisk he he

Ja, det er rart, når man kan holde på resultatet, i stedet for kun, at forestille sig hvordan det eventuelt kunne være/se ud.

Citér
Den sidste kode syntes jeg ikke lige lå til højrebenet he he men tror jeg er ved at fange den.

Det er også årsagen til at jeg forsøgte at forklare princippet bag; for det er måske en uventet måde at gøre tingene på, men den skulle gerne give mening, når du har det hele på plads.

ADC'en har sit eget interrupt. I den tidligere nævnte kode (det 'simple' eksempel), ser du at jeg aflæser en værdi fra ADC'ens data registre, og lagrer den i RAM'en.
Lad os antage at vi har kaldt rutinen til at oplyse os om den sidst lagrede ADC værdi, nemlig getADC2Value().
På det tidspunkt har vores adcIndex fx. værdien 1.
Vi aflæser adcValue[2][adcIndex], hvilket vil sige adcValue[2][1].
Imens vi er gået igang med at aflæse værdien, bliver vores ADC-interrupt måske aktiveret.
ADC-interruptet kører helt færdigt, før der returneres til vores aflæsnings-rutine, så derfor ville det være kedeligt, hvis vores ADC-værdi blev overskrevet imens (især hvis den består af 2 bytes, og vi allerede havde aflæst den ene). I ADC-interruptet skriver vi ADC-værdien i RAM-adressen adcValue[2][adcIndex ^ 1], hvilket betyder at den bliver skrevet et andet sted end vi er igang med at aflæse. Lidt forklaring: adcIndex ^ 1 giver samme resultat som ((adcIndex + 1) & 1). Altså vil værdien blive skrevet til adcValue[2][0].

Når ADC-interruptet er færdigt, kommer vi (som nævnt tidligere) tilbage til hvor vi blev afbrudt, og programmet fortsætter kørslen fra hvor vi slap.


En ADC giver dig nemlig ikke det samme resultat hver gang du aflæser den.
Ofte svinger den +/- 3, fx. hvis vi har en 10-bit ADC værdi, og vi læser værdien 252 første gang, kan det være vi læser 249 anden gang, 250 tredje gang og måske 249 fjerde gang.

Lægger vi dem sammen... 252+249+250+249, får vi værdien 1000, denne dividerer vi så med 4, og har et 'stabilt' resultat på 250.

Citér
Hvor store er udsvingene på en ADC value?

Nogle gange 2 bits, nogle gange +/- 3..4 stykker (cirka). Det kommer helt an på hvilken ADC vi har fat i, og hvad vej månen skinner i forhold til østenvinden.

Citér
Jeg skal faktisk kun bruge en udsvingsmargien på lige over 100o ud af servoens 180.
Opløsningen skal være på ca 2 us i PWM. det må give lidt større spillerum i aflæsningspræcisionen når værdier går fra -30o til 45o og ikke alle 180o fordelt over pot værdierne.

Burde nok kunne lade sig gøre. Hvis vi bruger en 10-bit ADC, har vi værdierne fra 0 til 1023, hvilket vil sige en inddeling på 5/1023 = 4.887mV indeling.
Hvis vi måler i grader, så har vi... 180/1023 = 0.176o indeling.

Skal du kun bruge 105o, får du 105/1023 = 0.103o indeling, hvilket jeg vil påstå er vældig fint. ;)

Uhm, PWM opløsningen burde kunne nå helt op på 1/2 af clock-frekvensen, dvs. 500kHz, hvilket vil sige 2μS. Skal du bruge mere, kan du bare sætte den interne clock-frekvens til 8MHz; jeg mener ikke at der skulle være bivirkninger ved dette.

Citér
Jeg overvejer stadig at droppe det ene pot og så kalibrere nulpunktet software mæssigt vha. en fast værdi, det spare da en del regnekræft som så måske kan bruges på at lave et mere præcist gennemsnit for det andet pot.

Det må du vurdere mens du sidder med det. Det kan sagtens være du kan undvære det ene potmeter, og derved komme et godt stykke ned i den endelige pris.
Når vi snakker produktion, begynder én enkelt komponent til 0.05 øre at blive 'dyr'.
Oftest tænker man på... "jamen... i forhold til alt det andet, er det jo ingenting".
Det er også rigtigt.
Men der er flere ting der spiller ind...
1: Prisen for at sætte en komponent på (med maskine), er 1 kr, så hvis komponenten koster 0.05 øre, bliver prisen pludselig 1.0005 kr.
2: Jo mindre printet er, desto billigere bliver produktet. Printet er normalt meget dyrt i forhold til komponenterne.
3: Fjerner vi en komponent, kan det være vi får en lettere kredsløbsvej, eller et simplere print-udlæg.

Dertil skal det også siges, at i modsætning til nummer 3, kan det i visse tilfælde blive lettere at tegne kredsløbet med flere komponenter. Fx. 2 baner krydser hinanden. Den ene bane kan gå midt under en SMD-modstand.

Og jo, jeg ved at i starten har vi med leadede komponenter at gøre, og muligvis vil potmetrene forblive leadede. Men det er godt at have i tankerne under hele forløbet.

 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #56 Dato: August 24, 2011, 20:52:24 »
Ved ikke lige om det giver nogen mening det her  :o
Regner ikke med at det gør noget at baner krydser under komponenter her på hul printet. men sidder og prøver at tegne en hvor det undgåes men det er jo stort set umu... nej meget svært gg


 

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 #57 Dato: August 24, 2011, 22:36:21 »
Regner ikke med at det gør noget at baner krydser under komponenter her på hul printet.

Det gør ingenting.

Citér
men sidder og prøver at tegne en hvor det undgåes men det er jo stort set umu... nej meget svært gg

R6 (499K) skulle være 499 ohm, vil jeg tro.
R7 (200K) skulle nok være 250 ohm (hvilket vil sige 2 stk 499 ohm i serie), ellers kan det være koden vil drille lidt (det skulle dog kunne kompenseres for alligevel).

Forresten, i pakken lagde jeg 10 stk 0 ohm (dvs. lus). De kan være ganske praktiske hvis du selv ætser enkeltsidede print.
De kan også være praktiske, hvis du laver 2 krydsninger på oversiden af dit hulprint, så lægger du først ledningen, og når den er loddet, sætter du 0 ohm modstanden hen over; ellers kan det være du kommer til at brænde hul i isoleringen under lodningen.

Hmm... Måske jeg skulle have givet dig nogle 2k med; men hvis du sætter 2 stk 1k i serie, får du en 2k.
hvis du ikke har 499 ohm, kan du sætte 2 stk 1k i parallel, så giver det 500 ohm.
(4 stk 1k i parallel giver 250 ohm)

Din SCK ser ud til at være glemt. Den skulle nok have været forbundet til ben 9. :)

Ellers ser det ganske tilforladeligt ud. Det burde ikke gå hiel gal. ;)

R10 kan forresten være 0K (erstattes af lus), fordi ATtiny44's ADC måler i forhold til VCC (dvs. 1023 = 5V, 511 = 2.5V)

Husk at du kan sætte modstandene på højkant (dvs. bukke det ene ben 180o rundt, så får du et mellemrum på 1U (et unit, dvs. 2.54mm).


 

Offline jascore

  • Jordet Basic
  • **
  • Indlæg: 157
  • Antal brugbare Indlæg: 0
    • Vis profil
Sv: hjælp til servo kontrol med timer
« Svar #58 Dato: August 25, 2011, 04:00:39 »
Oki har lavet lidt ændringer og ellers sat modstande i serier og paralelt.
Havde ikke til at lave en 100 ohm så satte en 4k7 på R8 plads, men ved ikke om den er for stor.

har fjernet det ene pot (det til at stille nulpunkt) og sat en trykknap på den ledige ADC (startknap)



Kønt blev det ikke, må nok ud og invistere i noget værktøj der er lidt finere end alm elektrikker grej  og en ordentlig kolbe :-[


 

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 #59 Dato: August 25, 2011, 14:37:51 »
Oki har lavet lidt ændringer og ellers sat modstande i serier og paralelt.
Havde ikke til at lave en 100 ohm så satte en 4k7 på R8 plads, men ved ikke om den er for stor.

100K var korrekt (ikke 100 ohm). 4K7 kan bruges, men det vil være lettere at lave aflæsningen, hvis den er 4K præcis - ellers brug 100K.
-Sidder 4K7 der allerede, så prøv at lade den sidde, og se hvordan det går med aflæsnings-koden.

Idéen er at man "halverer spændingen" for hver dip-switch der er "slået til".
På denne måde kan man lave alle typer kombinationer, og stadig finde ud af hvordan kontakterne er indstillet uden for megen bøvl.

Bruges 'halveringer', bliver det forholdsvis nemt:

Hvis R8 har værdien 4K, 8K, 16K, 32K eller 64K, vil jeg gætte på at værdierne nedenfor skulle være rimelig tætte på den angivne værdi...
Kontakt 1 (R7) har værdi 512
Kontakt 2 (R6) har værdi 256
Kontakt 3 (R5) har værdi 128
Kontakt 4 (R4) har værdi 64

Er kontakt 2 og 4 tændt mens 1 og 3 er slukket, skulle ADC'en give dig en værdi meget tæt på 320.

Aflæsningen bør så foregå på nogenlunde denne måde:

Kode:
switches = (getADC1Value() + 3) & ~7;  /* '3' er for tolerance +/- 3. ~7 ignorerer de 3 laveste bits. */

Citér
har fjernet det ene pot (det til at stille nulpunkt) og sat en trykknap på den ledige ADC (startknap)

Start-knappen har en fejl. Den vil enten blive aflæst som værende 'trykket ned hele tiden', eller også vil den stå og flik-flakke mellem tændt/slukket, fordi den ikke har en pull-up eller pull-down modstand.

Oftest sætter man en pull-up modstand til +5V og selve knappen 'kortslutter' så til GND når den trykkes ned.

Hvis du hellere vil have det omvendt, så i/o-værdien er 1, når knappen trykkes ned, så behold det som det er nu og sæt en pulldown modstand fra ben 10 til GND med værdien 4K7...10K.

Så længe du ikke sætter en 'stærk' modstand på, kan du sagtens sætte start-knappen på fx. MISO, MOSI eller SCK benet, så har du stadig et ledigt ben til ADC. (Stærk modstand = lav værdi, fx. 100 ohm, svag modstand = fx. 10K..100K) 10K vil nok være godt i dette tilfælde.


Citér
Kønt blev det ikke

-Det er ligegyldigt når det er en prototype. :)

Citér
, må nok ud og invistere i noget værktøj der er lidt finere end alm elektrikker grej  og en ordentlig kolbe :-[

Det vigtigste du har brug for, er et multimeter...
Jeg vil anbefale et, som kan måle så meget af følgende, som muligt, højeste prioritet nævnt først:
1: Modstand (ohm)
2: Gennemgang (gerne med bip)
3: Jævnspænding
4: Kapacitet (kondensator-værdi)
5: Vekselspænding
6: Frekvens (Hz)
7: hFE (transistor)
8: Spoler (Henry, faktisk microHenry)
9: Ampere (ja, de fleste multimetre har Ampere sammen med spænding)

For ca. 200 kr, bør du kunne finde noget der kan klare fra 1 til 6 + ampere.
Et Branford fra Harald Nybørge er faktisk ikke helt tosset. Det har gennemgangstester med bip, ohmmeter, vekselspændings voltmeter, jævnspædnings voltmeter, hFE, jævnstrøms amperemeter og vekselstrøms amperemeter (ja, hvorfor de er separate forstår jeg ikke!)

Desværre har Nybørge's multimeter ikke frekvens-måling, hvilket er meget rart at have, når man arbejder med microcontrollere. Der er et rimelig godt multimeter hos El-Supply. det kan også måle kapacitans, men det koster også over 3 gange så meget.

Derudover, vil jeg anbefale Weller's WS81 loddestation. Det er en professionel station, som er værd at investere i.
Har du ikke råd til denne, er det laveste du bør gå ned på, nok en Unavngiven loddestation, den kan findes forskellige steder i landet, også hos Biltema; men den laveste pris finder du uden tvivl hos Hallenslev Radio/TV.
De fleste sætter deres eget navn på loddestationen, men det er samme producent.
"For alt i verden:" undgå 'loddekolbe på ledning'; de er intet værd.

Hvis du skal lave professionelle lodninger (senere hen), vil jeg anbefale WDC Tørrens. Loddespidsernes levetid forlænges markant, plus lodningerne bliver mange gange flottere, end hvis du bruger 'svamp med vand'. Bruger du svamp med vand, bør du bruge vand, der har været kogt, hvis du ikke har demineraliseret vand.

Før jeg fik en modstands-bukkelære, brugte jeg ikke den slags. Men nu jeg har den, bruger jeg den rimelig ofte.
(Søg hos PC-Elektronik efter "Bukkeklods", han har også en udemærket billig elektronik-skævbider, den hedder "TANG 570" og koster 20 kr.)
Skævbideren kan klippe 'fladt ned til printet', hvilket jeg selv har fundet nyttigt mange gange.

En Olfa-kniv er også blandt mit favorit-værktøj, jeg bruger den nærmest hver eneste dag; jeg bruger også denne, når jeg skal "knække hulprint", da skærer jeg lige et par gange på hver side af printet før jeg knækker det, for at få et nogenlunde pænt knæk.

Og så selvfølgelig et finmekanisk værktøjssæt. Har du et sæt med 6 skruetrækkere allerede, så burde det være tilstrækkeligt. Disse skruetrækkere er også gode til at "løfte IC'er ud af sokler" med.
« Senest Redigeret: August 25, 2011, 14:46:46 af pacman »