Kód pro 4x TPIC6B595N

Wiring, C++, C, Java, ...
Pravidla fóra
Toto subfórum slouží k řešení obecných otázek kolem programování (konstrukce, knihovny, alokace paměti, ...)
Odpovědět
x86
Příspěvky: 3
Registrován: 12 črc 2020, 08:54
Reputation: 0

Kód pro 4x TPIC6B595N

Příspěvek od x86 » 12 črc 2020, 09:51

Ahoj,

chtěl bych poprosit o pomoc s vysvětlením funkce kódu. Mám čtyři osmisegmentové číslicové displeje. Každý displej ovládá jeden TPIC6B595N - ty jsou zapojeny v sérii za sebou. Desku s displeji ovládám přes Arduino UNO, které mám v ruce poprvé v životě :). Nějaké základy programování mám, přesto mi není jasné, jak program posílá signály pro led diody 8 až 31. Svými znalostmi dokážu ovládat stavy pouze diod 0 až 7.
Kód, který jsem našel na internetu a který mou desku s číslicovými displeji plnohodnotně ovládá, je následující:

int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

int numOfRegisters = 4;
byte* registerState;

void setup() {

//Vytvoří pole (z počtu numOfRegisters všech TPIC6B595N) a každému přiřadí hodnotu 0
registerState = new byte[numOfRegisters];
for (size_t i = 0; i < numOfRegisters; i++) {

registerState = 0;
}

//set pins to output so you can control the shift register
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}

void loop() {

for (int i = 0; i < 32; i++){
for (int k = i; k < 32; k++){

regWrite(k, HIGH);
delay(200);
regWrite(k, LOW);
}
regWrite(i, HIGH);
}
}

void regWrite(int pin, bool state){
//Determines register
int reg = pin / 8;

//Determines pin for actual register
int actualPin = pin - (8 * reg);

//Begin session
digitalWrite(latchPin, LOW);

for (int i = 0; i < numOfRegisters; i++){
//Get actual states for register
byte* states = &registerState;

//Update state
if (i == reg){
bitWrite(*states, actualPin, state);
}

//Write
shiftOut(dataPin, clockPin, MSBFIRST, *states);
}

//End session
digitalWrite(latchPin, HIGH);
}


Tučně označený kód je ta část kódu, kde přesně nevím, jaké informace respektive jaké hodnoty bitů se kam posílají.
byte* states = &registerState; - tímto se vytvoří nějaké pole stavů jednotlivých TPIC6B595N
bitWrite(*states, actualPin, state); - tady se zapisují bity jednotlivých stavů TPIC6B595N - jak si zobrazím jednotlivé hodnoty pres Serial.print ?
shiftOut(dataPin, clockPin, MSBFIRST, *states); - a tady už nevím vůbec, co se posílá

Mohl by mi to někdo, prosím, detailně vysvětlit ?


Když totiž pro každou diodu napíšu např. toto:

bitWrite(states, 0, LOW);
bitWrite(states, 1, HIGH);
bitWrite(states, 2, HIGH);
bitWrite(states, 3, LOW);
.
.
bitWrite(states, 7, HIGH);
bitWrite(states, 8, LOW);
.
.

A pak to odešlu tímto:

//Write
shiftOut(dataPin, clockPin, MSBFIRST, states);

..tak se nastaví pouze první číslicový displej dle bitWrites pro diody 0-7 a zbylé tři displeje se nastaví dle toho prvního - tedy všechny displeje ukazují to samé...

Snad jsem to popsal srozumitelně. Předem díky za odpověď.
Dave

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Kód pro 4x TPIC6B595N

Příspěvek od ondraN » 12 črc 2020, 16:08

Ty registy jsou zapojené v sérii za sebou (výstup prvního je zapojen na vstup dalšího atd, hodiny jsou paralelně). První řídí výstupy 0-7, další 8-15 další 16-23 .....atd. Můžeš jich dát za sebou kolik chceš. Každý registr má vstup nulování a vstup pro zápis z posuvného registru do registru výstupů. Ty jsou zapojeny paralelně, takže jedním výstupem můžeš všechny registry snulovat a druhým zase zapsat všechny hodnoty z posuvných registrů do výstupů. Musíš tedy vědět, kolik je registrů a mít v paměti všechny hodnoty jejich stavů. To je třeba pro to, když potřeguješ třeba změnit hodnotu na bitu posledního registru, tak musíš zároveň poslat i všechny stavy těch předchozích. Kdybys to neudělal a poslal jen poslední bit, tak vy se po impulsu zapsání do výstupního registru na všech ostatních výstupech objevily nuly. Takže se VŽDY musí poslat kompletní sekvence všech bitů. To řeší ta knihovna za tebe. Pokud tohle víš, není už žádný problém se podívat přímo do kódu knihovny, co a jak se tam dělá. Soubor .h je vlastně deklarace všech prototypů, soubor .cpp obsahuje kód ke všem prototypům a může obsahovat i nějaký privátní kód, který pak není vidět z hlavního .ino souboru.

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Kód pro 4x TPIC6B595N

Příspěvek od ondraN » 12 črc 2020, 16:48

x86 píše:
12 črc 2020, 09:51

Tučně označený kód je ta část kódu, kde přesně nevím, jaké informace respektive jaké hodnoty bitů se kam posílají.
byte* states = &registerState; - tímto se vytvoří nějaké pole stavů jednotlivých TPIC6B595N
bitWrite(*states, actualPin, state); - tady se zapisují bity jednotlivých stavů TPIC6B595N - jak si zobrazím jednotlivé hodnoty pres Serial.print ?
shiftOut(dataPin, clockPin, MSBFIRST, *states); - a tady už nevím vůbec, co se posílá




Proměnná registerState je ukazatel na pole byte s počtem numOfRegister byte, které je vytvořené zde

//Vytvoří pole (z počtu numOfRegisters všech TPIC6B595N) a každému přiřadí hodnotu 0
registerState = new byte[numOfRegisters];

v registerState je ukazatel na začátek dynamického pole

byte* states = &registerState; do proměnné states typu ukazatel na byte se dá ukazatel na ukazatel začátku dynamického pole
bitWrite(*states, actualPin, state); tady je jako první parametr dereference states, tedy vlastně zase ukazatel na začátek dynamického pole
shiftOut(dataPin, clockPin, MSBFIRST, *states); tady je zase jako poslední parametr dereference states


A pak to odešlu tímto:

//Write
shiftOut(dataPin, clockPin, MSBFIRST, states); tady máš použito states přímo, takže tomu předáváš úplně jinou hodnotu než je v příkladu
bitWrite(states, 0, LOW); tady máš použito states přímo, takže tomu předáváš úplně jinou hodnotu než je v příkladu
Překladač ti neřve, protože jat states tak *states jsou pointery
Pokud máš ve states opravdu přímo pointer na dynamické pole, tak to je ale správně.

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Kód pro 4x TPIC6B595N

Příspěvek od ondraN » 12 črc 2020, 20:26

Ještě jsem trochu dumal, proč to je v tom kódu tak podivně řešené, ale nic mě nenapadlo. Úplně by stačilo aby
byte* states = registerState;
a pak by se vůbec nemusela používat v parametru funkce dereference. registerState je zde pointer na byte a může se rovnou přiřadit. Tu rošádu s ukazatelem na ukazatel považuji za úplně zbytečnou.

KamilV
Příspěvky: 479
Registrován: 03 dub 2018, 15:27
Reputation: 0
Bydliště: Olomouc

Re: Kód pro 4x TPIC6B595N

Příspěvek od KamilV » 12 črc 2020, 20:42

Já ani moc nechápu, proč to není fixně deklarované statické pole.
U pole o 4 indexech by to bylo to nejpřehlednější, co se dá vymyslet.

Pochopil bych, kdyby se velikost pole mallocem zvětšovala, ale pokud dopředu vím, že rozměr je numOfRegisters, není moc co řešit...

x86
Příspěvky: 3
Registrován: 12 črc 2020, 08:54
Reputation: 0

Re: Kód pro 4x TPIC6B595N

Příspěvek od x86 » 12 črc 2020, 21:23

Ahoj Ondro,

děkuji za odpověď. Pomaličku mi to začíná být jasné. Ale pár otázek tu ještě je :)
Názorně:
1)
byte* registerState;
mohu zapsat také takto ?
byte registerState[];

2)

registerState = new byte[numOfRegisters];
for (size_t i = 0; i < numOfRegisters; i++) {
registerState = 0;
}

Toto sice tvoří pole dynamicky, ale pro konkrétní počet registrů to lze zapsat napřímo?

registerState[0] = 0;
registerState[1] = 0;
registerState[2] = 0;
registerState[3] = 0;

anebo to lze vytvořit bez použití polí ?

byte registerState0 = 0;
byte registerState1 = 0;
byte registerState2 = 0;
byte registerState3 = 0;

3)

bitWrite(*states, actualPin, state);
lze to tedy "nedynamicky" napsat takto ?

bitWrite(registerState0, actualPin, state); // zapisuji bity pro první TPIC6B595N
bitWrite(registerState1, actualPin, state); // zapisuji bity pro druhý TPIC6B595N
bitWrite(registerState2, actualPin, state); // zapisuji bity pro třetí TPIC6B595N
bitWrite(registerState3, actualPin, state); // zapisuji bity pro čtvrtý TPIC6B595N

...díky nějaké knihovně se bity zapisují do patřičných registrů


to samé se pak děje zde...

//Write
shiftOut(dataPin, clockPin, MSBFIRST, *states);

"nedynamicky" to píšu takto..
shiftOut(dataPin, clockPin, MSBFIRST, registerState0);
shiftOut(dataPin, clockPin, MSBFIRST, registerState1);
shiftOut(dataPin, clockPin, MSBFIRST, registerState2);
shiftOut(dataPin, clockPin, MSBFIRST, registerState3);

musím dodržet správné pořadí??

JDE TEDY O TO POSLAT INFORMACE VŽDY PRO VŠECHNY REGISTRY
ondraN píše:
12 črc 2020, 20:26
Ještě jsem trochu dumal, proč to je v tom kódu tak podivně řešené, ale nic mě nenapadlo. Úplně by stačilo aby
byte* states = registerState;
a pak by se vůbec nemusela používat v parametru funkce dereference. registerState je zde pointer na byte a může se rovnou přiřadit. Tu rošádu s ukazatelem na ukazatel považuji za úplně zbytečnou.
Tak jak jsi mi to vysvětlil, tak to chápu tak, že si aktualizuje stavy jednotlivých registrů a pak přepíše jen ten, kde se mění stav..

Mockrát děkuju, Dave

KamilV
Příspěvky: 479
Registrován: 03 dub 2018, 15:27
Reputation: 0
Bydliště: Olomouc

Re: Kód pro 4x TPIC6B595N

Příspěvek od KamilV » 12 črc 2020, 21:38

ad 1)
Ne. Bylo by to bezrozměrné pole, což kompilátor nepustí. Buď uděláš pointer do paměti, kde pole začíná a pak mu alokuješ potřebnou velikost paměti, nebo definuješ pole staticky. A to buď tak, že určíš počet indexů:

Kód: Vybrat vše

byte registerState[numOfRegisters];
nebo určíš velikost výčtem prvků:

Kód: Vybrat vše

byte registerState[] = {0, 0, 0, 0};
ad 2)
Ano. Ale můžeš to projet i v tom cyklu:

Kód: Vybrat vše

for (size_t i = 0; i < numOfRegisters; i++) {
registerState[i] = 0;
}
Ta varianta bez polí je jen teoretická. Ano, šlo by to, ale není to vůbec dobrý nápad. Nebudeš moct to procházet cyklem, nijak rozumně tomu nebudeš moct měnit rozměr...

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: Kód pro 4x TPIC6B595N

Příspěvek od ondraN » 13 črc 2020, 07:46

Myslím, že KamilV to shrnul celkem jasně.
Já bych dodal něco jen k tomu dynamickému poli.
Běžné pole má svůj identifikátor ve formě jména. To jméno, je vlasně ukazatel na první prvek a typ pole (byte, int ...) nastavuje správné měřítko pro pointerovou aritmetiku (kolik byte zabírá jeden prvek). Z toho je vidět, že se jménem pole se pracuje jako s pointerem, protože to pointer je. Dále je nutné, aby statické pole mělo danou velikost v okamžiku překladu. Pak už nelze velikost pole měnit.
Dynamické pole nemá jmenný identifikátor, ale pouze adresu a k jeho členům mohu přistupovat pouze přes pointer (nelze použít přístup přeshranaté závorky [] ). Jeho adresu dostanu v okamžiku jeho vytvoření. Vrátí ji funkce new, která pole vytváří. Tuhle adresu nesmím ztratit, jinak ztratím možnost s tím polem pracovat nebo ho zrušit. Dynamické pole mohu po použití zase zrušit funkcí delete. Ta uvolní místo v paměti pro další použití.
Pokud si píšu vlastní program, určitě vím, kolik registrů použiju a určitě si zvolím statické pole, protože se s ním komfortně pracuje. Pokud budu dělat knihovnu, je to úplně jiná situace, protože nevím, kolik registrů bude chtít uživatel použít, takže musím použít dynamické pole a pak se vyrovnat s méně komfortní práci s ním. Nebo mohu nechat na uživateli, ať vytvoří pole požadované délky a předá mi jeho adresu (jméno) jako parametr funkce.
Doporučuji problematiku pointerů důkladně prostudovat, protože je hodně používaná. Hlavně se používá v parametrech funkcí, a pokud chci aby funkce měnila nějaké hodnoty proměnných v mém programu, tak to v C/C++ nejde jinak, než přes pointery.

x86
Příspěvky: 3
Registrován: 12 črc 2020, 08:54
Reputation: 0

Re: Kód pro 4x TPIC6B595N

Příspěvek od x86 » 13 črc 2020, 11:53

Ahoj Ondro, Kamile,

Chtel bych podekovat za vycerpavajici odpovedi. Zkusim vse ted zrealizovat v praxi, tak se uvidi, nakolik jsem vse pochopil 😉

Dave

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 20 hostů