Kód pro 4x TPIC6B595N
Pravidla fóra
Toto subfórum slouží k řešení obecných otázek kolem programování (konstrukce, knihovny, alokace paměti, ...)
Toto subfórum slouží k řešení obecných otázek kolem programování (konstrukce, knihovny, alokace paměti, ...)
Kód pro 4x TPIC6B595N
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 = ®isterState;
//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 = ®isterState; - 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
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 = ®isterState;
//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 = ®isterState; - 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
Re: Kód pro 4x TPIC6B595N
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.
Re: Kód pro 4x TPIC6B595N
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 = ®isterState; - 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 = ®isterState; 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ě.
Re: Kód pro 4x TPIC6B595N
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.
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.
Re: Kód pro 4x TPIC6B595N
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...
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...
Re: Kód pro 4x TPIC6B595N
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
Mockrát děkuju, Dave
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
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..ondraN píše: ↑12 črc 2020, 20:26Ješ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.
Mockrát děkuju, Dave
Re: Kód pro 4x TPIC6B595N
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ů:
nebo určíš velikost výčtem prvků:
ad 2)
Ano. Ale můžeš to projet i v tom cyklu:
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...
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];
Kód: Vybrat vše
byte registerState[] = {0, 0, 0, 0};
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;
}
Re: Kód pro 4x TPIC6B595N
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.
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.
Re: Kód pro 4x TPIC6B595N
Ahoj Ondro, Kamile,
Chtel bych podekovat za vycerpavajici odpovedi. Zkusim vse ted zrealizovat v praxi, tak se uvidi, nakolik jsem vse pochopil
Dave
Chtel bych podekovat za vycerpavajici odpovedi. Zkusim vse ted zrealizovat v praxi, tak se uvidi, nakolik jsem vse pochopil
Dave
Kdo je online
Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 25 hostů