Lite frågor ang. AVR

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

förstog inte riktigt.. aja programeraren fick frejo skicka :roll: hehe.. fick den på posten.. har inte kunnat hitta några skilnader..

aja provade koden nu:D den funkar utmärkt..

om BV är pb0 vad är då pb1?
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Konstanterna och deras värdet i decimal form:
PB0 = 0
PB1 = 1
PB2 = 2
PB3 = 3
PB4 = 4
PB5 = 5
PB6 = 6
PB7 = 7

...och i binär form:
PB0 = 00000000
PB1 = 00000001
PB2 = 00000010
PB3 = 00000011
PB4 = 00000100
PB5 = 00000101
PB6 = 00000110
PB7 = 00000111

Om vi skulle vilja ha PB3 som utgång kanske man skulle tycka att man kunde skriva:
DDRB = PB3;
Men kollar du vad PB3 motsvarar binärt här ovan så ser du att det blir helt galet. Resultatet kommer nämligen bli att PB0 och PB1 blir utgångar.

Kollar vi istället vilka värden vi får om vi kör dom igenom _BV() så ser vi att det blir som vi vill. Studera listan nedan:

_BV(PB0) = 00000001
_BV(PB1) = 00000010
_BV(PB2) = 00000100
_BV(PB3) = 00001000
_BV(PB4) = 00010000
_BV(PB5) = 00100000
_BV(PB6) = 01000000
_BV(PB7) = 10000000

Som du ser så blir bit nummer 3 (man börjar räkna från 0) satt om vi kör _BV(PB3). Och det betyder att bara PB3 kommer fungera som en utgång.

Nu fick du ett mycket bättre förklaring. Hoppas det duger, annars får du fråga igen. :)
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

jasså så man skriver bara de i mellan paranteserna..

var de så lätt:D ska kolla om jag kan göra något trevligt nu:D har ju en tre färgs led ikopplad:D weee..

TACK!
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

okej.. nu har jag lyckats med att blinka min diod i tre färger 8)

men nu vill jag blinka den röd grön blå..

i takt..
men hur pausar jag?

wait(1000)? eller något i den i stilen?
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Du kommer nog inte ha så roligt innan du lär dig lite bit-manipulering. Du vill ju kunna ändra bitarna i PORTB för att kunna tända och släcka led:sen.

En liten lathund över de vanligaste bitoperationerna:
---------------------------------------
PORTB = _BV(PB3); //Gör så att enbart PB3 blir hög. Alla andra bitar i PORTB kommer sättas till 0 avsett vad de var innan.

PORTB |= _BV(PB3); //Gör PB3 hög utan att påverka tillståndet på de andra bitarna i PORTB

PORTB &= ~_BV(PB3); //Gör PB3 låg utan att påverka tillståndet på de andra bitarna i PORTB

PORTB ^= _BV(PB3); //Växlar tillstånd på PB3

Med dom borde du kunna göra allt du vill. Det går även att ändra på fler än en bit åt gången. Här är några vanliga exempel:

PORTB = _BV(PB3) | _BV(PB4); //Gör enbart PB3 och PB4 höga. Alla andra kommer sättas till 0.

PORTB |= _BV(PB3) | _BV(PB4); //Gör PB3 och PB4 höga, utan att påverka de andra bitarna i PORTB

PORTB &= ~(_BV(PB3) | _BV(PB4)); //Gör PB3 och PB4 låga utan att påverka de andra bitarna i PORTB.

PORTB ^= (_BV(PB3) | _BV(PB4)); //Växlar tillståndet på både PB3 och PB4

Dumma Telia-DNS! Gick inte att komma in på någonting igår kväll, och därmed inte att skicka det här inlägget heller.

edit: Lade till en parentes på sista raden. Tror det kan behövas.
Senast redigerad av cykze 9 september 2004, 13:55:49, redigerad totalt 1 gång.
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

hehe.. har samma problem me telia :evil:
ok.. ska prova de sen när ja kommer hem från skolan:P

trevlig lathund den kommer jag att ha mycke nytta av..
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Virr3 skrev:okej.. nu har jag lyckats med att blinka min diod i tre färger 8)

men nu vill jag blinka den röd grön blå..

i takt..
men hur pausar jag?

wait(1000)? eller något i den i stilen?
Funktionen _delay_loop_1(x) "pausar" 3*x klockcykler. OBS! x måste vara mellan 0 och 255
Funktionen _delay_loop_2(x) "pausar" 4*x klockcykler. OBS! x måste vara mellan 0 och 65535

Tiden en klockcykel tar beror på vilken frekvens du kör AVR:en i.

4 MHz: 1/4000000 s = 0.25 µs
8 MHz: 1/8000000 s = 0.125 µs
12 MHz: 1/12000000 s = ca 0.8333 µs

Så vill du ha en paus på 1µs vid 4 MHz så kör du:
_delay_loop_2(1); // Därför att 1/(0.25*4) är 1

...och vid 8 MHz:
_delay_loop_2(2); // Därför att 1/(0.125*4) är 2

Räkna på samma sätt för andra frekvenser.

Eftersom _delay_loop_2() som max kan ta 65535 som argument, så kan du som mest göra en paus på 65535 µs vid 4 MHz (32767.5 µs vid 8 MHz osv). För att komma upp i runt en sekund så får du anropa _delay_loop_2() flera gånger.
Antingen genom att helt enkelt lägga fler på rad:
_delay_loop_2(60000); //Väntar 60 ms vid 4 MHz
_delay_loop_2(40000); //Väntar 40 ms vid 4 MHz
Totalt blir det alltså 100 ms (0.1 s)

Eller lägga dom i en for-loop som i exemplet jag visade först.

Som du märker blir det lite jobbigt att "pausa" i t ex en sekund. Men det är egentligen inget hinder, eftersom man aldrig brukar behöva ha sådana jättelånga pausar. Istället löser man sådana långa uppehåll med hjälp av timers och interrupts.

Nu blir det lite avancerat här. Förstår du inte så är det bara att titta tillbaka på koden när du har lärt dig lite mer om AVR och C.

Kod: Markera allt

// Koden växlar värdet på PB0 varje sekund. Kopplar du en lysdiod till PB0 kommer den alltså växla en gång per sekund.

#include <inttypes.h> // Innehåller lite variabeltyper. Används inte i det här exemplet men brukar ändå vara bra att ha

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

SIGNAL (SIG_OUTPUT_COMPARE1A) //Interrupt för "Compare Output Match"
{
    PORTB ^= _BV(PB0); // Växlar värdet på PB0
}

int main()
{
    DDRB = _BV(PB0); //Sätter PB0 till utgång.
    
    OCR1 = 62499;
    TCCR1A = 0;

    TCCR1B = _BV(CS10)|_BV(CS11) | _BV(WGM12); // Kombinationen CS10 och CS11 ger en prescaler på 64. WGM12 betyder att TCCR1A nollställs när interrupten inträffar. (WGM12 heter CTC1 i äldre modeller)

    TIMSK = _BV(OCIE1A); //Säger till att vi vill ha en interrupt för "Output Compare Match"

    sei(); //Aktiverar interrupts
    
    while(1) //Loopar i en evighet. Kommer avbrytas varje sekund.
    {
    }

    return 1;
}
Nu till förklaringen. Jag antar att oscillatorn är på 4 MHz. Använder du någon annan hastighet, så är det bara att räkna om det till det.

För att nå upp till en sekund så går det åt 4000000 klockcykler. Det värdet är för stort för ett 16-bitsregister (två 8-bitars). Därför behöver vi dela ner det värdet så att det blir max 65535. De "prescalers" som finns är /1, /8, /64, /256 och /1024.

4000000 / 1 = 4000000 Större än 65535, går inte!
4000000 / 8 = 500000 Större än 65535, går inte!
4000000 / 64 = 62500 Mindre än 65535, Går bra!

Vi bestämmer oss därför för att använda en prescaler på 64 och räkna från 0 upp till 62500-1. TCNT1 kommer öka med 1 var 64:e klockcykel och kommer hela tiden jämföras med värdet i OCR1. När TCNT1 når upp till värdet i OCR1 kommer en interrupt inträffa. Då kommer programexeveringen avbrytas och hoppa till "Compare Match"-rutinen (SIGNAL-raden i exemplet). Samtidigt kommer TCNT1 nollställas och fortsätta räkna uppåt medan interrupten körs.

Lätt som en plätt! :P

Det var visst något stort DNS-fel hos Telia såg jag.

edit: Felrättning: Ändrat TCCR1A till TCNT1 (bättre sent än aldrig ;))
Senast redigerad av cykze 25 december 2004, 18:57:43, redigerad totalt 1 gång.
Användarvisningsbild
rickeboy
Inlägg: 678
Blev medlem: 13 augusti 2003, 09:12:17
Ort: Göteborg / Karlskrona
Kontakt:

Inlägg av rickeboy »

cykze>> Intressant läsning... tackar man för ;)

Frågan är bara vart man hittar all denna informationen om vad registrena heter etc och vilka kommandon man ska använda... har en känsla av att det är manualen för avr-libc men dessvärre är ju den väldigt dåligt upplagd...

Inte bara telia har haft problem med DNS:erna... hela skanovasnäts alla DNS:er hade tydligen gått ner... funkar dock nu igen...
Användarvisningsbild
erixon
Inlägg: 380
Blev medlem: 27 augusti 2003, 10:21:58

Inlägg av erixon »

Den informationen hittar man i databladet för AVR, och där står det lite C kode exempel (dock endast i dom lite nyare databladen)
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

I databladet finns allt! Där står det vad registrena och bitarna heter och hur man använder dom.

Finns även lite lättare beskrivet på www.avrbeginners.net

Skulle nog rekommendera att kolla i databladet för någon mindre AVR, som t ex AT90S2313, för att inte drunkna i text.
http://www.atmel.com/dyn/resources/prod ... OC0839.PDF

Förutom att större AVR:er har fler funktioner skiljer de sig inte så mycket åt.

I manualen för avr-libc står det inte så mycket om sådant här, utan mer om det som är specifikt för AVR-GCC.
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

förstog inte sä jätte mycke av koden du skrev nyss men..


jag gjorde ett litet test men det blev sådär..

Kod: Markera allt

#include <avr/io.h>
#include <avr/delay.h>
#include <inttypes.h>

// Inkluderar lite header-filer som behövs
// io.h innehåller bl a värdet för PORTB
// delay.h innehåller funktionen _delay_loop_2()
// inttypes.h innehåller olika typer. T ex:
//      uint8_t - unsigned char 8-bit  (0 - 255)
//      int8_t  - signed char 8-bit    (-128 - 127)
//      uint16_t - unsigned int 16-bit (0 - 65535)

int main()
{
   uint16_t i; // Deklarerar en variabel för loopen nedan. 16-bitars eftersom
            // 8 bitar blir för litet för värdet 500 i for-loopen.

   DDRB = _BV(PB0); // Sätter bit nummer PD0 i DDRB till 1 vilket gör så att den blir utgång
   
   
   while (1) // Startar en evighetsloop. Programmet kommer loopa genom den
           // den här loopen så länge AVR:en är igång
   {
      PORTB ^= _BV(PB0); // Växlar värdet på PB0. Dvs ett värde på 1
                  // på PB0 ger 0 på PB0. Och 0 ger 1.
				 	
     
      for (i=0;i<500;i++)
      {
         _delay_loop_2(10000); // 1000 motsvarar 1000*4 klockcykler. Vid
                              // 4 MHz motsvarar det 1000 µs. Loopar vi det
                         // 500 gånger så motsvarar det 1/2 sekund.
        PORTB &= ~_BV(PB0);  
		PORTB^=_BV(PB1);
		_delay_loop_2(10000);
 }
   } // Går tillbaka till while (1) igen...

   return 1; // Kommer aldrig att köras eftersom programexecveringen aldrig
           // lämnar while-loopen.
}
så gjorde jag.. men den lyser blått i ca tre sek:( och blinkar rött i kanske en halv sek..

meningen är att den ska blinka blått sedan rött..
vad har lilla jag gjort för fel :?:
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Hur är lysdioden inkopplad?
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

detär en trefärgs diod som är kopplad på pin 1, 2 och 3
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Du har missat att sätta även PB1 och PB2 som utgångar. Sen har jag slängt ihop en liten kod som borde göra det du vill. Koden kan göras på andra, snyggare sätt. Men på det här sättet är tydligt och bra.

Kod: Markera allt

#include <avr/io.h>
#include <avr/delay.h>
#include <inttypes.h>

int main()
{
	uint16_t i, j;

	DDRB = _BV(PB0) | _BV(PB1) | _BV(PB2);
	
	while (1)
	{
		PORTB |=  _BV(PB0);
		PORTB &= ~_BV(PB1);
		for (i=0;i<2;i++)
		{
			for (j=0;j<1000;j++)
				_delay_loop_2(1000);

			PORTB ^= _BV(PB0);
		}

		PORTB &= ~_BV(PB0);
		PORTB |=  _BV(PB1);
		for (i=0;i<2;i++)
		{
			for (j=0;j<1000;j++)
				_delay_loop_2(1000);

			PORTB ^= _BV(PB1);
		}
	}

	return 1;
}
Har inte provat att kompilera det ens, men det borde kunna fungera. Se om du kan klura ut själv vad koden gör.
Virr3
Inlägg: 840
Blev medlem: 25 juli 2004, 23:05:59
Ort: Göteborg

Inlägg av Virr3 »

yeehh nice 8) den funkar utmärkt..

nu ska vi se om jag förstår något av koden:(jag har markerat med // i koden för det jag förstår/inte förstår:P)


Kod: Markera allt

 #include <avr/io.h>
#include <avr/delay.h>
#include <inttypes.h>

int main()
{
   uint16_t i, j; //vad gör den?

   DDRB = _BV(PB0) | _BV(PB1) | _BV(PB2); //gör PB0 PB1 PB2 till utgångar
   
   while (1)
   {
      PORTB |=  _BV(PB0); //gör PB0 hög
      PORTB &= ~_BV(PB1); // gör PB1 låg(varför?)
      for (i=0;i<2;i++) //vad?
      {
         for (j=0;j<1000;j++) //??
            _delay_loop_2(1000);//pausar

         PORTB ^= _BV(PB0); //gör PB0 låg
      }

      PORTB &= ~_BV(PB0); //gör PB0 låg
      PORTB |=  _BV(PB1); //gör PB1 hög
      for (i=0;i<2;i++) //??
      {
         for (j=0;j<1000;j++) //??
            _delay_loop_2(1000);

         PORTB ^= _BV(PB1); //gör PBlåg
      }
   }

   return 1;
}
dät är vad jag förstår och inte förstår..
Skriv svar