Start Elkretssimulator


Koncept

En 7-segmentdisplay uppsatt i simulatorn. Klicka på knappen där det står räkna upp.


Beroende på vilka lysdioder i denna skapelse som vi tänder så får vi olika symboler.

Kombinationer av lysande dioder

Siffra Display gfedcba abcdefg a b c d e f g
0 0 0×3F 0×7E on on on on on on off
1 1 0×06 0×30 off on on off off off off
2 2 0×5B 0×6D on on off on on off on
3 3 0×4F 0×79 on on on on off off on
4 4 0×66 0×33 off on on off off on on
5 5 0×6D 0×5B on off on on off on on
6 6 0×7D 0×5F on off on on on on on
7 7 0×07 0×70 on on on off off off off
8 8 0×7F 0×7F on on on on on on on
9 9 0×6F 0×7B on on on on off on on
A A 0×77 0×77 on on on off on on on
b b 0×7C 0×1F off off on on on on on
C C 0×39 0×4E on off off on on on off
d d 0×5E 0×3D off on on on on off on
E E 0×79 0×4F on off off on on on on
F F 0×71 0×47 on off off off on on on


För att skapa siffran 6 tänder vi alltså samtliga lysdioder utom segment b.

Gemensam anod eller katod

Dioder har en anod (+) och en katod (-). Den ena varianten av 7-segmentdisplayer har gemensam katod (-). Den andra har gemensam anod (+). Om displayen har gemensam katod så kopplas den gemensamma katoden till minus. En posiv spänning på de olika segmenten kommer sedan tända dessa. Och vice versa.


Driva display med arduino

Uppkopplingen för att testa koden nedan.



Kod för att driva 1 display

När jag testat mitt exempel har jag använt en display med gemensam katod (-). Dvs hög signal på segmenten och låg signal på gemensam katod tänder displayen. Så nedan kod fungerar med displayer med gemensam katod. Har du en display med gemensam anod så ändrar du de översta defines till

#define _HIGH LOW
#define _LOW HIGH


Okej, lycka till. Här är koden.


// Beroende på om du anv. gemensam anod (+) eller katod (-) så
// vänder du på nedan. #define _HIGH HIGH ger gemensam katod.

#define _HIGH HIGH
#define _LOW LOW

// pinnar för segmenten a,b,c,d,e,f,g
int segPins[8] = {2,3,4,5,6,7,8,9};    
 
// pinne för gemensam anod/katod
int commonPin = 10;

// siffra som skall visas
int num = 7;

int segLEDs[11][7]=
{
{1,1,1,1,1,1,0},
{0,1,1,0,0,0,0},
{1,1,0,1,1,0,1},
{1,1,1,1,0,0,1},
{0,1,1,0,0,1,1},
{1,0,1,1,0,1,1},
{1,0,1,1,1,1,1},
{1,1,1,0,0,0,0},
{1,1,1,1,1,1,1},
{1,1,1,0,0,1,1},
{0,0,0,0,0,0,0}
};

void setup()
{  
  for (int i=0; i<8;i++)
    pinMode(segPins[i],OUTPUT);
    
  pinMode(commonPin,OUTPUT);   
  digitalWrite(commonPin, _LOW);
    
  clear();
  digit(num);
}

void loop() // används inte
{
}

void digit(int num)
{  
  for (int i=0; i<7; i++)
    digitalWrite(segPins[i],segLEDs[num][i]?_HIGH:_LOW);
}

void clear()
{
  for (int i=0; i<7; i++)
    digitalWrite(segPins[i],_LOW);       
}

Flera stycken displayer

Det saknas digitala utgångar på arduinon (och de flesta andra processorer också för den delen) för att koppla in ett helt gäng med 7-segmentdisplayer. Utgångarna är i princip slut redan efter att vi har kopplat in första displayen. Så hur gör vi? Vi måste trixa lite. Det absolut vanligaste är att man gör som beskrivet nedan. Nedan är uppkopplingen och sedan koncept och kod.









Koncept

Vi kan inte skicka signaler till alla displayer samtidigt eftersom signalerna för segmenten är parallellkopplade. Vad vi däremot kan göra, eftersom vi separat kontrollerar den gemensamma katoden (eller anoden beroende på displaytyp), det är att vi kan tända displayerna var för sig.



Vi skapar alltså en sluten krets mellan strömmen till segmenten och den gemensamma katoden (eller anoden beroende på displaytyp) för en specifik display. För att skapa kretsen sätter vi alltså de digitala utgångar så att kretsen uppstår. I vilken riktning strömmen skall flyta beror på om vi har en gemensam katod eller anod. Om vi t.ex. har en gemensam katod sätter vi de digitala utgångarna till segmenten till "1" (höga, HIGH) och den gemensamma anoden (eller katoden) till "0" (låg, LOW) när vi vill tända en display.



Vi gör detta i tur och ordning och låter displayen lysa en liten stund och därefter går vi vidare till nästa display.



Om vi gör detta tillräckligt snabbt så kommer det se ut som om alla displayer lyser samtidigt med respektiva siffra.



När alla displayer lyser med relevanta siffror är alltså detta bara en synvilla. Vi tänder dem var för sig och låter displayen lysa en stund och går sedan går vi vidare till nästa display. Ungefär som cylindrarna i en förbränningsmotor tänds en och en.



Timer-Interrupt

Rent tekniskt, för att åstadkomma ovanstående, så kan man använda ett timer-interrupt. Det finns lite olika lösningar där ute, men lösningen med ett timer -interrupt är den lösning jag lärt mig är snyggast och stabilast. På detta sätt får man kontroll över ljusstyrkan på displayen och displayen slocknar inte varje gång programmet skall göra något viktigt.

Ett interrupt (avbrott) innebär att man sätter upp hårdvaran (processorn) så att en funktion anropas om någonting definierat händer. Det kan handla om att en signal på en viss pinne blir hög eller låg, men det kan också sättas upp så att ett anrop sker med vissa tidsintervall. Processorn avbryter alltså pågående exekvering, för att istället hoppa till den funktion som är kopplad till avbrottet. Det är därför det heter avbrottsvektor (engelska "interrupt"), ett avbrott görs. Det finns lite olika sätt att konfigurera både signalstyrda avbrott och timer -styrda avbrott på arduino (och de flesta andra processorer också).

Vi måste sätta upp villkoren för interruptet. Varje gång vi ändrar villkoren måste interruptet disable:as (disaktiveras). När vi sedan är färdiga aktiverar vi interruptet igen. Därför inleder vi med funktionen cli() (clear interrupt) och avslutar med sei() (set interrupt).

För att få vårt interrupt måste vi sätta vissa flaggor i vissa register. Det är lite överkurs för denna text förklara allting men i stora drag gör vi följande.

Frekvensen för vårt avbrott är = Klockfrekvens / (prescaler x (OCR1A+1))

Genom att välja lämplig prescaler, dvs TCCR1B, samt lämpligt värde på OCR1A så får vi alltså våra timer -avbrott så ofta som vi önskar. Med en prescaler på 1024 och OCR1A på 100 får vi alltså en frekvens på 16000000 / (1024 * (100+1)) Hz = ca 155 Hz.

 cli();
TCCR1A = 0;
TCCR1B = 0;
TCNT1  = 0;                          
OCR1A =  100; 
TCCR1B |= (1 << WGM12); // CTC-mode=ON, dvs TCNT1=0 när TCNT1>=OCR1A
TCCR1B |= (1 << CS12) | (1 << CS10); // 1024 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei(); 


Kod för att driva 4 displayer

ISR(TIMER1_COMPA_vect) anropas 155 gånger per sekund och tänder upp displayerna i tur och ordning. 155 gånger per sekund verkar räcka för att skapa synvillan att alla segmenten lyser samtidigt, man vill inte göra detta med för hög frekvens eftersom det tar kraft från processorns övriga jobb. Värdet som visas på displayerna ligger i vektorn int num[DIGITS]={2,3,4,8};


// Beroende på om du anv. gemensam anod (+) eller katod (-) så
// vänder du på nedan. #define _HIGH HIGH ger gemensam katod.

#define _HIGH HIGH
#define _LOW LOW
#define DIGITS 4

// pinnar för segmenten a,b,c,d,e,f,g
int segPins[8] = {2,3,4,5,6,7,8,9};    
 
// pinnar för gemensam (common) anod/katod
int comPins[DIGITS] = {10,11,12,13};

// siffror som skall visas
int num[DIGITS]={2,3,4,8};

int segLEDs[11][7]=
{
{1,1,1,1,1,1,0},
{0,1,1,0,0,0,0},
{1,1,0,1,1,0,1},
{1,1,1,1,0,0,1},
{0,1,1,0,0,1,1},
{1,0,1,1,0,1,1},
{1,0,1,1,1,1,1},
{1,1,1,0,0,0,0},
{1,1,1,1,1,1,1},
{1,1,1,0,0,1,1},
{0,0,0,0,0,0,0}
};

void setup()
{  
  for (int i=0; i<8;i++)
    pinMode(segPins[i],OUTPUT);
    
  for (int i=0; i< DIGITS;i++)
    pinMode(comPins[i],OUTPUT);  
  
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A =  100;
  TCCR1B |= (1 << WGM12);  // CTC-mode=ON, dvs TCNT1=0 när TCNT1>=OCR1A
  TCCR1B |= (1 << CS12) | (1 << CS10); // 1024 prescaler
  TIMSK1 |= (1 << OCIE1A);   // enable timer compare interrupt
  sei(); 
}

void loop() // används inte
{
}

ISR(TIMER1_COMPA_vect)
{
  clear();
  digit(0,num[0]);
  digit(1,num[1]);
  digit(2,num[2]);
  digit(3,num[3]);
}

void digit(int d, int num)
{
  for (int d=0 ; d < DIGITS;d++)
    digitalWrite(comPins[d], _HIGH);

  digitalWrite(comPins[d], _LOW);
  for (int i=0; i<7; i++)
    digitalWrite(segPins[i],segLEDs[num][i]?_HIGH:_LOW);

  delay(50);
}

void clear()
{
  for (int d=0; d < DIGITS;d++)
  {
    digitalWrite(comPins[d], _HIGH);
      for (int i=0; i<7; i++)
        digitalWrite(segPins[i],_LOW);     
  }
}