Problem med SPI samt ISR på arduino uno

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Enkan
Inlägg: 11
Blev medlem: 24 november 2012, 12:42:33

Problem med SPI samt ISR på arduino uno

Inlägg av Enkan »

Hej!

Först och främst vill jag säga att jag är nybörjare inom området :), och ursäkta för WoT. Jag har slitit håret av mig i en vecka och ville ge så mycket information som möjligt. (Samt att jag var osäker på om tråden hörde hemma här eller i programmering :humm: )

Jag håller på och bygger en binärklocka med hjälp utav en arduino(UNO, 328), tre stycken 74HC595N skiftregister och lysdioder. Jag har fått min krets att fungera och visa tiden korrekt, men nu försöker jag slipa lite på det rent estetiska och går bet. För tillfället använder jag mig av shiftOut() för att sända över dem tre bytes som representerar tiden till mina register. Det fungerar bra, men jag kan bara välja att ha lysdioderna på eller av. Jag vill kunna dimma dom! Dels för att få snyggare övergångar, men dels för att kunna bestämma ljusstyrkan efter hur ljust det är i rummet.

Så, lite efterforskningar visade att det verkar vara BAM(Bit Angle Modulation) som är den stiligaste lösningen på mitt problem. Jag hittade lite exempelkod, från en snubbe som använder denna metod för att styra en 8*8*8 led-kub(http://www.kevindarrah.com/download/8x8 ... _speed.ino ) och har tittat på denna för att försöka förstå hur jag ska bära mig åt.


Jag har pin10 på SR(Master reset, active LOW) kopplad till 5V
Jag har pin11 på SR(shift register clock) kopplad till pin13 på arduino
Jag har pin12 på SR( storage register clock, latch) kopplad till pin12 på arduino ///Här låg problemet: pin12 reserverad av SPI. Hade även en felaktig skrivning till denna pinne
Jag har pin13 på SR(Output enable, active LOW) kopplad till pin10 på arduino(blank_pin)
Jag har pin14 på SR(Serial Data) kopplad till pin11 på arduino.
(Här bör det inte vara något felaktig, då det fungerar utmärkt om jag kör med min ursprungliga kod.)
(För tillfället har jag bara ett skiftregister inkopplat för att minska felkällorna)

Kod: Markera allt

#include <SPI.h>

#define blank_pin 10
#define data_pin 11 
#define latch_pin 12
#define clock_pin 13


int counter = 0;
int bam_bit = 0;

byte test = 0;
byte test0 = B00000000;    //Trots att jag har nollor överallt
byte test1 = B00000000;    //Så lyser alla lysdioder
byte test2 = B00000000;
byte test3 = B00000000;

void setup(){
  SPI.setBitOrder(LSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  
  noInterrupts();
  TCCR1A = B00000000;    //Dessa delar har jag inte riktigt förstått
  TCCR1B = B00001011;    //vad dom innebär. Kan det vara här något galet ligger?
                                         //kopierat från Kevin Darrahs kod. 
  TIMSK1 = B00000010;
  OCR1A = 30;
  
  pinMode(latch_pin, OUTPUT);
  pinMode(clock_pin, OUTPUT);
  pinMode(data_pin, OUTPUT);
  SPI.begin();
  delay(500);
  interrupts();
  
}

void loop(){
    //delay(20);
}

ISR(TIMER1_COMPA_vect){
  PORTB |= 1 << blank_pin; //Hög signal till Output enable på SR för att släcka LED
   
  if (counter == 8){  //Logik för att justera bam_bit
    bam_bit++;
  }
  else if (counter == 24){  
    bam_bit++;
  }
  else if (counter == 56){
    bam_bit++;
  } 
  
  counter++;
  //Beroende på var i vår cykel vi är, så använder vi olika bytes. 
  switch (bam_bit){
  case 0:
    SPI.transfer(test0);
    break;
  case 1:
    SPI.transfer(test1);
    break;
  case 2:
    SPI.transfer(test2);
    break;
  case 3: 
    SPI.transfer(test3);
    if(counter == 120){ //Om vi är i slutet på vår cykel börja om från början 
    bam_bit = 0;
    counter = 0; 
    }
    break;
  }
  
  PORTB |= 1 << latch_pin; //latch on
  PORTB &= ~(1 << latch_pin); //latch off
  
  
  PORTB &= ~(1 << blank_pin);//output enable låg för att visa ny data
  pinMode(blank_pin, OUTPUT);
}
Jag lutar åt att det är något med min ISR som är knas, resultatet blir liknande om jag river ut all logik och bara skickar data i den.. Men å andra sidan, om jag skiter helt i den och kör en SPI.transfer() direkt i loop() så blir resultatet liknande. Så det kan även vara så att jag missförstått hur SPI fungerar, men av dom exemplen jag tittat på så ser jag inte att dessa gör något annorlunda än vad jag gör.
Det är även så att även om jag gör koden helt statisk, dvs inte ändrar några variabler någonstans. Så tänds och släcks det lite hur som om man låter det stå och ticka en stund.

Snälla hjälp! :D
Senast redigerad av Enkan 10 februari 2014, 19:09:37, redigerad totalt 2 gånger.
sodjan
EF Sponsor
Inlägg: 43231
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Problem med SPI samt ISR på arduino uno

Inlägg av sodjan »

Jag har inte kollat alla detaljer, men jag ser inte varför själva kopplingen
ska behöva ändras. Enda skillnaden är väl att du ska ha lysdioderna tända
lika olika länge. Men visst blir det myket mer trafik, så att säga. Med
enbart on/off så beöver du ju bara skicka något då utgångarna ska
ändras, med PWM så behöver du kontinuerligt skicka data för att
få önskad effekt.

En annan lösning som du absolut ska kolla på är att ersätta dina
enkla 74HC595N med t.ex någon TLC-modell från TI som har hela
PWM delen inbyggd. Du skiver bara önskad en ljusstyrka per utgång
och sedan sköter kretsen om allt.

> TCCR1A = B00000000; //Dessa delar har jag inte riktigt förstått
> TCCR1B = B00001011; //vad dom innebär. Kan det vara här något galet ligger?
> //kopierat från Kevin Darrahs kod.

Det är lite svårt att svara på när du inte beskriver *vad* du inte har förstått.
Vad är det mer konkret som är oklart i dessa kapitel i databladet?
15.11.1 TCCR1A – Timer/Counter1 Control Register A
15.11.2 TCCR1B – Timer/Counter1 Control Register B
Användarvisningsbild
Icecap
Inlägg: 26611
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med SPI samt ISR på arduino uno

Inlägg av Icecap »

Jag jobbar med LED-skärmar och vet hur de styr intensiteten.

För att få fler nivåer måste du skriva fler "bilder" inom scanningstiden.

I ditt fall hade jag reserverat ett antal arrays om 3 bytes som motsvarar de olika nivåer. Säg att du vill ha 16 nivåer, då reserverar man 16 st arrays om 3 bytes vardera.

I en timer-ISR "spelar" man sedan upp dessa, en i sände. Med 16 nivåer bör uppdateringshastigheten vara 1120Hz+.

Vid att köra det i en timer-ISR kan du sedan i main-loop nöja dig med att styra bitsen i detta minne. Är alla likvärdiga bits i alla 16 nivåer på lyser den LED intensivt, är det bara en av 16 som är på lyser den svagt.

Självklart kan man "trimma" lite på tiderna: tiden för lägsta nivå kan vara signifikant kortare än för längsta nivå, ögat är inte linjärt och det vore en fördel att få en logaritmisk(aktig) skala för tiderna.

Men principen är: varje bit i ett 3-bytes array motsvarar en LED. I ju fler av de 16 "kopior" som den bit är '1', ju intensivare lyser den. Så när man skriver data till bitmönstret ska den rutin avgöra hur många av de 16 array som bit'en ska sättas i.

Har man en olika tid för varje nivå kan man nöja sig med färre nivåer och då sätta bit i "mitten" av skalan, detta ger fler steg för färre uppdateringar.
Enkan
Inlägg: 11
Blev medlem: 24 november 2012, 12:42:33

Re: Problem med SPI samt ISR på arduino uno

Inlägg av Enkan »

sodjan skrev:Jag har inte kollat alla detaljer, men jag ser inte varför själva kopplingen
ska behöva ändras. Enda skillnaden är väl att du ska ha lysdioderna tända
lika olika länge. Men visst blir det myket mer trafik, så att säga. Med
enbart on/off så beöver du ju bara skicka något då utgångarna ska
ändras, med PWM så behöver du kontinuerligt skicka data för att
få önskad effekt.
Jag tror jag var lite otydlig, min tanke är inte att ändra kopplingen, den ska fungera utmärkt! :D Men i och med att jag har haft problem att få det att fungera, så har jag tagit bort så många komponenter som möjligt. Jag är nu nere på ett skiftregister och får ändå inte SPI-kommunikationen att fungera som tänkt.

Trots att jag skickar en enskild byte, och den är statisk genom hela koden, får jag varierande resultat. Har precis provat att skicka B10000000 (LSBFIRST), första försöket så gick det (nästan) som det skulle: Q7 gav hög signal och resten låg(Borde varit Q0 som gav hög p.g.a. LSBFIRST, men det var åtminstone bara en pinne som blev hög). Flyttade ettan och skickade istället B00100000, vilket resulterade i att Q3 och Q1 gav hög. Flyttade tillbaka och skickade B10000000 vilket resulterade i att alla blev höga. Detta alltså utan att ändra i övriga delar av koden.(Medan jag har skrivit resterande delen av svaret, så har den stått och tickat bredvid datorn, och titt som tätt så tänds en lysdiod och släcks efter uppskattningsvis 100ms Därav antar jag att något i kommunikationen är skevt, och att jag förmodligen missförstått/inte förstått någon del i den.
sodjan skrev: En annan lösning som du absolut ska kolla på är att ersätta dina
enkla 74HC595N med t.ex någon TLC-modell från TI som har hela
PWM delen inbyggd. Du skiver bara önskad en ljusstyrka per utgång
och sedan sköter kretsen om allt.

Jag har köpt ett par TLC5940 men tyvärr inte fått hem dom än. Det skulle visserligen lösa mitt problem, genom att inte lösa det så att säga. I och med att det ska gå att lösa med dom medel jag har för tillfället, så vill jag gärna göra det. Om inte annat för att lära mig :)
sodjan skrev: > TCCR1A = B00000000; //Dessa delar har jag inte riktigt förstått
> TCCR1B = B00001011; //vad dom innebär. Kan det vara här något galet ligger?
> //kopierat från Kevin Darrahs kod.

Det är lite svårt att svara på när du inte beskriver *vad* du inte har förstått.
Vad är det mer konkret som är oklart i dessa kapitel i databladet?
15.11.1 TCCR1A – Timer/Counter1 Control Register A
15.11.2 TCCR1B – Timer/Counter1 Control Register B
Jag har förstått så långt att TCCR1A respektive B är inställningsregister och att TCCR1A i detta fall har sina "default-inställningar", då ingen av bitarna skiljer sig från sina initiala tillstånd. Även att TCCR1Bs 1:a på bit3 innebär CTC-mode(Nollställ timern när värdet överensstämmer med OCR1A. Jag förstår dock inte vad detta innebär i praktiken) och att bit0 och bit1 bestämmer vilken klocka som timern använder sig av.
TIMSK1 möjliggör en användandet av en ISR. (Men detta bör väl inte påverka hur SPI fungerar? Såvida inte inte timer1 är något som används av SPI-biblioteket. Men i så fall, bör ju inte herr Darrahs kod till kuben fungera heller, då jag gör samma ändringar till registren som han gör. )
Hm, det blev något klarare av att gå igenom databladet igen, men är fortfarande väldigt förvirrad. Det är första gången jag behövt gå igenom datablad, så jag har nog inte riktigt vanan inne. :humm:
Icecap skrev: Jag jobbar med LED-skärmar och vet hur de styr intensiteten.

För att få fler nivåer måste du skriva fler "bilder" inom scanningstiden.

I ditt fall hade jag reserverat ett antal arrays om 3 bytes som motsvarar de olika nivåer. Säg att du vill ha 16 nivåer, då reserverar man 16 st arrays om 3 bytes vardera.

I en timer-ISR "spelar" man sedan upp dessa, en i sände. Med 16 nivåer bör uppdateringshastigheten vara 1120Hz+.
Detta låter som samma sak man försöker uppnå m.h.a. BAM. Det jag försöker återskapa är 4-bitars BAM, för varje bit finns en motsvarande array med bytes som bestämmer vilka lysdioder som ska lysa. Sedan loopar man igenom dessa ungefär "logaritmiskt" som du pratar om. bit1 får 8 "på-vändor", bit2 får 16st, bit3 32st och bit4 64st "på-vändor". Jag känner att jag förklarar rörigt:
här förklaras det hur det fungerar. Men principen är densamma som ni verkar jobba med. :)
Icecap skrev: Vid att köra det i en timer-ISR kan du sedan i main-loop nöja dig med att styra bitsen i detta minne. Är alla likvärdiga bits i alla 16 nivåer på lyser den LED intensivt, är det bara en av 16 som är på lyser den svagt.

Självklart kan man "trimma" lite på tiderna: tiden för lägsta nivå kan vara signifikant kortare än för längsta nivå, ögat är inte linjärt och det vore en fördel att få en logaritmisk(aktig) skala för tiderna.

Men principen är: varje bit i ett 3-bytes array motsvarar en LED. I ju fler av de 16 "kopior" som den bit är '1', ju intensivare lyser den. Så när man skriver data till bitmönstret ska den rutin avgöra hur många av de 16 array som bit'en ska sättas i.

Har man en olika tid för varje nivå kan man nöja sig med färre nivåer och då sätta bit i "mitten" av skalan, detta ger fler steg för färre uppdateringar.
Precis, min tanke är att ha funktioner för att hålla reda på klockan, konvertera detta till bytes och efter det sätta ljusstyrkan och låta interrupten sköta 'uppritandet'.

Ursäkta att svaret dröjde.
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4736
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Problem med SPI samt ISR på arduino uno

Inlägg av Swech »

Kod: Markera allt

  case 3: 
    SPI.transfer(test3);
    if(counter == 120){ //Om vi är i slutet på vår cykel börja om från början 
    bam_bit = 0;
    counter = 0; 
    }
    break;
  }
  
Denna del

Kod: Markera allt

 if(counter == 120){ //Om vi är i slutet på vår cykel börja om från början 
    bam_bit = 0;
    counter = 0; 
borde väl ligga utanför CASE delen, efter sista break

Swech
Enkan
Inlägg: 11
Blev medlem: 24 november 2012, 12:42:33

Re: Problem med SPI samt ISR på arduino uno

Inlägg av Enkan »

Swech: Nja, i och med att bam_bit beror på counter, och switch-satsen beror på bam-bit så bör man väl minska antalet gånger som if-satsen behöver köras om den ligger under case3. Vi vet ju att den bara behöver köras under case3 i och med att det är enda gången den kan vara 120. Eller? Nu blev jag osäker.

Har för övrigt upptäckt åtminstone ett fel. Uttrycket för att sätta latch hög var:

Kod: Markera allt

PORTB |= 1 << latch_pin;
Men i och med att latch_pin är satt till 12, hamnar jag utanför registret. Korrekt ska vara:

Kod: Markera allt

PORTB |= 1 << (latch_pin - 8);
:tumupp:
Detta har löst vissa problem, men det är fortfarande väldigt märkliga saker som händer. Om det inte vore för att min andra kod går som en klocka( :roll:) så hade jag misstänkt hårdvarufel.
Just nu kör jag denna kod:

Kod: Markera allt

#include <SPI.h>

#define blank_pin 10
#define data_pin 11 
#define latch_pin 12
#define clock_pin 13
int i=0;
void setup(){
  SPI.setBitOrder(LSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  
  pinMode(latch_pin, OUTPUT);
  pinMode(clock_pin, OUTPUT);
  pinMode(data_pin, OUTPUT);
  SPI.begin();
  Serial.begin(9600);
  delay(500);  
}

void loop(){
  Serial.println(i);
  Serial.println(i, BIN);
  SPI.transfer(i);
  PORTB |= 1 << (latch_pin - 8); //latch on
  PORTB &= ~(1 << (latch_pin - 8)); //latch off
  i++;
  delay(1000);
}
Och det går bra, en bit. Vid ungefär femte vändan så blir det konstigheter. Först räknas det fel en eller två gånger, 6 visas som 128 och 7 som 192 (Ganska godtyckligt, varierar från gång till gång). Och sedan stannar allt, oftast på 192. Om jag väljer mode2 så räknas det korrekt till 12, sedan kommer helt plötsligt 17, 19 och så stannar den(En sväng räknade den rätt upp till 29 sen kom 80 för att sedan frysa 30s och fortsätta med slumpmässiga tal.). :humm:

Nu måste jag sova, återkommer imorgon. Tack för all hjälp hittills! :)

EDIT - dagen efter:
Det slog mig under dagen, att jag använde pin12 till latch. Denna pinne är 'reserverad' som MISO av SPI biblioteket. Den går utmärkt att använda så länge inte spi är inkluderat, men är det det så blir det knas! Flytt av denna till pin8 gjorde susen. Nu ska det BAM:as!

Återkommer säkert när det strular nästa gång ;)
Skriv svar