Beste Forumleden,

Ik vond dit lapje code bij een instructable, waarbij men 2 nixiebuisjes van 0 tot 99 laat tellen, aangestuurd door een arduino die de data doorschuift naar een 74HC595 waarop 2 stuks 74141 aanhangen.

Wat ik niet begrijp aan deze code, is hoe de getallen vertaald worden naar de byte die naar de 595 gestuurd wordt.

Ik vermoed dat het gedaan wordt in het stukje waar

int charTable gedeclareerd wordt, maar ik zie het verband niet.... Is er iemand die mij hierbij wat meer uitleg wil verschaffen?

de code:


/*  a sketch to drive two nixie tubes using two SN74141 BCD chips and a single SN74HC595N shift register
    developed from the tutorials on Adafruit.com and arduino.cc
    the sketch will cause two nixie tubes to count from 0 to 99 but you can change it to create any two-digit number and have the nixie tube display it
    Jeff Glans 2013
    Released into the public domain
   
*/
//set up the pins for communication with the shift register
int latchPin = 8;
int clockPin = 12;
int dataPin = 11;
int x; //create a counting variable


// create an array that translates decimal numbers into an appropriate byte for sending to the shift register
int charTable[] = {0,128,64,192,32,160,96,224,16,144,8,136,72,200,40,168,104,232,24,152,4,132,68,196,36,164,100,228,20,148,12,140,76,204,44,
172,108,236,28,156,2,130,66,194,34,162,98,226,
18,146,10,138,74,202,42,170,106,234,26,154,6,134,70,198,38,166,102,230,22,150,14,142,78,206,46,174,110,238,30,158,1,129,
65,193,33,161,97,225,17,145,9,137,73,201,41,169,105,233,25,153};


byte nixies = 255; //initiate the byte to be sent to the shift register and set it to blank the nixies

void setup(){
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  nixies = 255; // create a blank byte
  updateShiftRegister(); // send the blank byte to the shift register
  delay(500);
 

  for (x = 0; x<100; x++){ // count from 0 to 99
    nixies = charTable[x]; // translate into a byte to send to the shift register
    
    updateShiftRegister(); //send to the shift register
    delay(500);
 
  Serial.print("x = ");
  Serial.println(x);
  Serial.print("nixies = ");
  Serial.println(nixies);}
}
  //the process of sending a byte to the shift register
void updateShiftRegister(){
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, nixies);
  digitalWrite(latchPin, HIGH);
}

Je moet je even inlezen hoe een 74141 werkt, met name het BCD to Decimal gedeelte. Dat is het stuk wat van vier ingangen (de halve output van een 595) tien uitgangen maakt om een nixie aan te sturen.

Als je het schema erbij hebt, dan kun je zien wat er naar je drivers wordt gestuurd. Dan is het opeens wel zinnig.
Zo kun je zien dat '00' gepresenteerd wordt door '0'. '01' door 128. Blijkbaar zit de 'eenheden' buis op de most significant nibble, waarbij die waarschijnlijk ook nog een keer 'gespiegeld' moet worden.
Dan de tweede nibble zit waarschijnlijk op dezelfde manier aan de drivers.

De reden om het zo te doen is waarschijnlijk dat het met pinning en PCB layout leuk uit kwam. Het maakt die AVR tenslotte niet zo heel veel uit of-ie 0x01 of 0x80 naar buiten klokt...

Maar handig is anders. De Arduino kan zelf gewoon in BCD tellen, dus door de decoders 1-op-1 aan te sluiten ben je van die vreselijke tabel af.

Dat moet maar net uit komen met je PCB layout he.
Het is niet 'raar' om dit soort dingen te doen. Bij multiplexed 7-segment doe ik het meestal ook zo. En er zijn zat boards met kleine processors met extern RAM waar data en address ook gewoon zo zitten als het uit kwam - zolang ze bij READ en WRITE maar op dezelfde manier zitten, maakt het geen moer uit. Voor ROM is het iets lastiger - die wordt meestal ergens anders geprogrammeerd (guess what... ook daar ben ik al eens een coversion tool tegen gekomen met de PCB layout als oorzaak...).

En zeg nou zelf... Zo vreselijk is die tabel niet. En waarschijnlijk is-ie door een extern stukkie code gegenereerd. Copy-paste, 3 regels functie eromheen en klaar.
Het lijkt erop dat beide drivers op dezelfde manier anagesloten zijn. Ik had dan denk ik alleen 0-9 gemaakt en zelf voor beide digits de byte samengesteld. Maakt het wellicht iets overzichtelijker. Iets compacter. Maar niet sneller.

even voor de duidelijkheid het schema:

hoe de 74141 werkt, daar ben ik al wel uit, wat ik hier niet begrijp, is hoe die byte samengesteld wordt...

EricP weet direct hoe 00 door 0 en 01 door 128 gemaakt wordt...
Ik zie dat niet, en ook het verband niet in die tabel..

Links en rechts/MSB en LSB verwisselen:


00 => 0b0000_0000 => 0b0000_0000 => 0
01 => 0b0000_0001 => 0b1000_0000 => 128
09 => 0b0000_1001 => 0b1001_0000 => 144
10 => 0b0001_0000 => 0b0000_1000 => 8

ze hebben gewoon 1 tabel gemaakt waar ze de positie als nummer gebruiken.

wil je getal 16 zetten, dan nement ze de waarde die in de tabel op plaats 16 staat en sturen ze dit naar de output.

stel bv dat je van 0-8 binair wil. nu maak je ofwel een functie die van getal naar binair rekent. maar wat ze hier doen is een tabel maken met de 9 mogelijke waardes in.

bv
int binair[] = {0000,0001,0010,0011,0100,0101,0110,0111,1000}

binair[3] zal dus 0011 als output geven
de berekening is vlot aan te passen dat je van 0-1024 kan gaan omzetten,
maar met die tabel mag je 1024 elementen manueel gaan ingeven.

Bedankt voor de uitleg, nu begin ik het te snappen...het getal tussen [] verwijst naar de positie in het array...

Op 18 januari 2017 08:47:27 schreef EricP:

Het lijkt erop dat beide drivers op dezelfde manier anagesloten zijn. Ik had dan denk ik alleen 0-9 gemaakt en zelf voor beide digits de byte samengesteld. Maakt het wellicht iets overzichtelijker. Iets compacter. Maar niet sneller.

je hebt me nieuwsgierig gemaakt :)

Zou je me willen uitleggen hoe je dat dan doet? of wat hints geven...

yep, en als je dus 1 positie misrekent, of een getal vergeet, is al de volgende rest van de array ook fout.
heel die tabel hardcoden... brrrrrr. daar huiver ik al van

[Bericht gewijzigd door fcapri op (22%)]

Het kan ook in code:


// convert x to BCD
uint8_t bcd = (x/10)<<4+(x%10);

//swap bitorder
nixies = (bcd & 1);
for (uint8_t i = 0; i<7;i++) {
     nixies <<= 1;
     bcd >>= 1;
     nixies += (bcd & 1);
}

Het kan wel met een veel kortere tabel. (weet niet of de syntax klopt):


int charTable[] = {0,128,64,192,32,160,96,224,16,144};
 
... 
...
... 
  for (x = 0; x<100; x++){ // count from 0 to 99
    nixies = (charTable[x\10] << 4) + charTable[x mod 10]
 
    updateShiftRegister(); //send to the shift register
    delay(500);
...
...
...

@arco: Je verwisseld de volgorde ( dertien komt eruit als 31) en je raakt de tientallen kwijt (omdat je 128 vier keer left-shift). En nog een paar kleine C-dingetjes (modulus en divide operators)

Beter:


int charTable[] = {0,8,4,12,2,10,6,14,1,9};
 
... 
...
... 
  for (x = 0; x<100; x++){ // count from 0 to 99
    nixies = charTable[x % 10] + (charTable[x/10] << 4)
 
    updateShiftRegister(); //send to the shift register
    delay(500);
...

Zoiets als wat blurp post dus...

In het kort: 1 array die 1 conversie doet. Die gebruik je voor de eenheden en voor de tientallen. Dat plak je in de juiste volgorde aan elkaar.

Hartelijk bedankt allemaal, het is me duidelijk nu...
Ik ga er wat mee experimenteren ;)

Ik was toch nieuwsgierig of het ook niet volledig zonder tabel kon. Na een zoektochtje op het internet heb ik dit gevonden. :)


unsigned char reverse(unsigned char b) {
   b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
   b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
   b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
   return b;
}

Heb ik hier gevonden. Eerste worden de nibbles omgedraaid. Dan worden de paren gespiegeld, en vervolgens worden de bits gespiegeld.

[Bericht gewijzigd door Shock6805 op (12%)]

En dan zou het zomaar kunnen dat een lookup in een tabel veel sneller gaat dan een berekening. Een perfectionist zoekt dat natuurlijk even uit, een programmeur die gewoon snel klaar wil zijn pakt zijn favoriete methode en gaat ervan uit dat het niet op een milliseconde aankomt. Is ook een mogelijke plek om rekenkracht tegen geheugen uit te ruilen.

Het is dus niet per definitie fout of slordig om tabellen te gebruiken. Soms is het zelfs simpelweg verplicht.

Wel, in de link gaan ze er van uit dat de tabel sneller is. Maar idd ruimte tegen snelheid en als het niet uitmaakt neem je je favoriete methode. Ik vind ze allebei moeilijk. De tabel lijkt random waarden te bevatten als je de printlayout niet kent of niet goed nadenkt. Maar dat shiftspelletje is ook niet direct doorzichtig.

Tabel is handig, omdat de pinnen dan niet op volgorde hoeven te zitten...

Dat moet voor het shiften ook niet. Je moet alleen elke keer een andere shift functie verzinnen als de pinout veranderd. Of de tabel herschrijven. Dan zijn we terug bij wat de programmeur het liefste doet. Hetzelfde zie je trouwens met goniometrie en wortels. Moet het snel dan gebruik je een tabel, eventueel aangevuld met interpolatie. Als er weinig ruimte meer over is, gebruik je een functie. (Vermoedelijk een of andere Taylor reeks.)

ik probeer altijd naar de toekomst te denken.
stel nu bv dat je 2 van die nixie buisjes maakt.

als ik een tabel zou gebruiken, zou ik daar 10 waardes in zetten (0-9) en geen 00-99.
stel nu dat je ineens zegt: ik wil er 4 van die buisjes op zetten om een klok en teller te maken. dan krijg je een aardig lange tabel (0000-9999 of 0000-2459).

als je een rekenformule hebt, maakt het niet uit hoeveel getallen je hebt. als je een tabel maakt per karakter, maakt het ook niet uit. je roept de functie gewoon x aantal keer op dat je een karakter hebt.

maar een tabel die de hele reeks opsomt, dat vind ik een minder goeie oplossing. 1 foutieve waarde en je mag die hele tabel gaan narekenen. t is ook een lang werk om al die waardes erin te stoppen.
de oplossing van arco vind ik dan ook één van de betere. toch een kleine tabel, en toch kort om meerdere karakters te gaan maken.

alle andere rekenmethodes zijn ook wel schoon, maar je zit toch ff na te denken wat het nu precies doet.
vroeger had je ook amper rekenkracht en moest je gaan overwegen welke oplossing het beste was (beperkte rekenkracht VS beperkt geheugen). nu deze tijd hebben we van beide in overvloed dat het bitter weinig uitmaakt.

Zo'n tabel genereer je - niet voor 10 entries, maar wel voor 50. Stukkie C code kloppen op de PC, eenmalig een schop geven, output copy-pasten.
Verander je de layout? Nou, dan pas je de 'bit mapping' aan, runt het ding nog een keer en klaar is Clara.

Wel, in de link gaan ze er van uit dat de tabel sneller is. Maar idd ruimte tegen snelheid

Voor die 10 entries... Is het zowel sneller als compacter. Immers, die tabel vreet 10 bytes. Zelfs als een opcode maar 1 byte is, dan kun je er maar 10 kwijt Wellicht dat de compiler (of eigenlijk: optimizer) nog wat slim combineert, maar anders is je hele simpele stukkie code al 12 bytes. Nog afgezien van wat op en neer schuiven van data naar variablen - dezelfde overhead dit je ook bij een lookup table hebt. Als het complexer wordt dan 'omdraaien', dan wordt je code ook complexer - en die table blijft hetzelfde.

de oplossing van arco vind ik dan ook één van de betere.

Dank voor de eer.

vroeger had je ook amper rekenkracht en moest je gaan overwegen welke oplossing het beste was (beperkte rekenkracht VS beperkt geheugen). nu deze tijd hebben we van beide in overvloed dat het bitter weinig uitmaakt.

En door die instelling zijn we oa. aan windows gekomen... Waarom efficiënt als de klant ook een snellere computer kan kopen? Tsja...

Op 18 januari 2017 15:09:09 schreef blurp:
@arco: Je verwisseld de volgorde ( dertien komt eruit als 31) en je raakt de tientallen kwijt (omdat je 128 vier keer left-shift). En nog een paar kleine C-dingetjes (modulus en divide operators)

Beter:


int charTable[] = {0,8,4,12,2,10,6,14,1,9};
 
... 
...
... 
  for (x = 0; x<100; x++){ // count from 0 to 99
    nixies = charTable[x % 10] + (charTable[x/10] << 4)
 
    updateShiftRegister(); //send to the shift register
    delay(500);
...

Ik heb dit net even geprobeerd, werkt perfect, buiten dat de eenheden en de tientallen omgewisseld zijn tegenover de originele lap code. inderdaad veel overzichtelijker.

Dan gewoon omdraaien:


   nixies = (charTable[x % 10] << 4) + charTable[x/10]