Trenutno koristim ovakav debounce, ja sam zadovoljan, mislim radi dobro za dosadašnje potrebe:
Code:
const int buttonPin1 = 10;
const int buttonPin2 = 13;
volatile byte state = LOW;
int buttonState1 = 0;
int buttonState2 = 0;
int buttonStateInk;
int lastButtonState1 = LOW;
int buttonStateDek;
int lastButtonState2 = LOW;
unsigned long lastDebounceTime = 0; // poslednje vreme kada je tater pritisnut
unsigned long debounceDelay = 50; // debounce vreme u milisekundama
01-03-2018, 08:28 AM (This post was last modified: 01-03-2018, 08:57 AM by mikikg.)
Da dodam malo komentara na taj code i par nekih programskih cakica koje od babe prave devojku tj primetno mogu da ubrzaju izvsavanje te loop() petlje samo kada se malo to sve prepakuje ; )
Ako bi se i millis() funkcija napisala kao inline code sa low-level operacijama onda bio dobio super-brzu petlju, ne moze brze od toga bez nekih tezih zahvata po ASM!
PS: Ukoliko te interesuje koliko je tacno vreme izvrsavanja petlje ili da uporedis razlicita resenja sta je brze ili sporije, recimo na liniji 50. stavi jednu instrukciju koja radi toggle nekog izlaza ili LED diode i onda prikljuci osciloskop ili frekvecmetar na taj pin i prati vreme polu-periode ili periode. Tako se najlakse moze direktno utvrditi vreme izvrsavanja nekog dela programa i vrlo je korisno kada se pise progam da se povremeno provere ta vremena i da se po potrebi odmah optimizuje neko sporno parce coda pa nastaviti dalje pisanje progama. Ides redom i pises dobar i optimizovan code.
@mikikg
Ako sam dobro shvatio, ovo:
int v1 = digitalRead(buttonPin1);
int v2 = digitalRead(buttonPin2);
da prebacim u "setup" tako da samo na početku programa pročita jednom da bi uštedio vrijeme, jedino mi ostaje mogućnost da ulaze definišem kao bitRead(), to bi bilo još brže?
A za millis() nisam baš skontao, trebao bih millis() zamjeniti sa nekom varijablom koja je dodjeljena, pa da ne upisujem millis() u svaki debounce, jer će ih biti blizu 20-tak (mislim toliko će biti tastera)?
01-05-2018, 11:38 PM (This post was last modified: 01-05-2018, 11:49 PM by me[R]a.)
Ne moze to u "setup" mora u "loop" jer u suprotnom bi stanje tih tastera ocitao samo prilikom pustanja u rad adrduina a promene se nebi registrovale. Zato sto tebi treba mozda i ne treba nista znacajno menjati...Jedino ako su ucestale i brze promene stanja tastera mozda bi trebala drugacija koncepcija.
Recimo ja ne bi na pocetku proveravao stanja svih pinova kao ti :
Code:
int v1 = digitalRead(buttonPin1);
int v2 = digitalRead(buttonPin2);
.
.
.
int vn = digitalRead(buttonPinn);
pa posle ispitivao sve sa if gde je promena.
Ja bi prvo uradio jedan digitaRead(buttonPin) pa odmah uradio if pa na kraju stavio return i onda ponovo krece loop... na taj nacin nebi gubio vreme provera svih tastera sem ako nije na zadnjem tasteru doslo do promene...
Ako ces imati istovremeno pritusnuto dva i vise tastera onda ovo gore ne vazi.
Imaću bar 2-3 tastera minimalno koji će biti aktivirana u istom trenutku, neće konstanto biti, često će se dešavati promjene ali će se ipak događati da budu minimalno 2-3 tastera istovrmeno aktivna.
Nije dobro da čitanje tastera radiš u loop jer loop, zavisno od potreba, može da naraste. Imaćeš situaciju da se stanje tastera neće osvežavati u tačno određenim trenucima vremena. U tu svrhu se koriste prekidne rutine. Dakle, ovaj kod je OK za sada ali moraš savladati i kako radi mehanizam prekida. Prvi prekid koji treba da pređeš jer prekid na prekoračenje tajmera.
ono sto je @gorankg rekao vezano za mehanizme prekida, govorio sam ti jos pre godinu dana. U Arduino kodove i funkcije se ne razumem, ali kod tebe ne vidim da si koristio prekidne rutine, pogotovo prekid nekog tajmera. Sam debounce algoritam je jednostavan. Ako uspes da pokrenes prekid nekog tajmera na overflow int, sam debounce je prost. Imas jednu promenljivu koju inkrementiras ako je na svaki prekid detektovana logicka nula npr., a resetujes je ako je detektovana jedinica. Ako 3-5 puta uzastopno dobijes logicku nulu, proglasavas da je ulaz stvarno aktivan. Vreme tmr-a je oko 1ms.
Ako zelis bilo kako da nastavis sa programiranjem mikrokontrolera, moras da savladas mehanizme mikrokontrolera. Glavna razlika izmedju funkcija koje koristis i mehanizama mikrokontrolera je u rasterecenju CPU-a i paralelizmu (kvazi) izvrsavanja programa. Npr. ti koristis softverski I2C, a MCU ima hardverski. Ako koristis softverski, tebi je CPU zakucan u toj rutini i nista drugo ne mozes da izvrsavas. Ako koristis hardverski, tvoj CPU ne radi nista. On samo kaze I2C modulu sta treba da uradi i slobodan je, a sam modul sve obavlja sam. Ako uz to jos povezes i prekid, onda ti CPU ide na spavanje, a budi kada se zavrsi transakcija. Zamisli sada jos 5-10 ovakvih radnji, a CPU rasterecen, jer sve rade periferije MCU-a. Tada u main-loop mozes da izvrsavas neke vremenski nekriticne zadatke. Ovako veliki broj prekida ti daje sliku paralelnog izvrsavanja vise radnji.
Debounce je pravi pokazatelj da je vreme da se krene ozbiljnije napred ili mozes da budes ogranicen upotrebom obicnog sekvencijalnog programiranja.
ISR(Timer1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
TCNT1 = 62437; // preload timer
if(digitalRead(10) = HIGH){
tastertest++
if(tasterttest > 3){
//poruka koja se šalje kao komanda
}
}
if(digitalRead(10) == LOW){
tastertest = 0;
}
ISR(Timer1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
TCNT1 = 62437; // preload timer
if(digitalRead(10) = HIGH){
tastertest++
if(tasterttest > 3){
//poruka koja se šalje kao komanda
}
}
else // !!!!if(digitalRead(10) == LOW){ !!!!
tastertest = 0;
}
}
void loop()
{
}
Makso,
to je interapt tmr debounce. Samo jedna mala ispravka. Pogleda u kodu. Prvo ispitujes da li je taster pritisnut i ako jeste, onda inkrementiras brojac. Posle toga ispitujes da li je taster otpusten preko if. Nemoj tako, nego samo ubaci else. I zaboravio si da inicijalizujes promenljivu tastertest na nulu.
Jos jedan savet. Posto sada prelazis na ozbiljniji nivo programiranja, evo jos par saveta koji mozda sada ovde nisu bitni, ali ce u kompleksnijim projektima svakako da ti pomognu.
Ti si definisao dve promenljive taster i tastertest. Obe promenljive su tipa int, tj. zauzimaju po dva bajta. Tebi je max vrednost promenljive tastertest 3, sto znaci da je dovoljno da promenljiva bude tipa unsigned char. Ista je prica i za promeljnivu taster.
Takodje si postavio da je promenljiva int, sto znaci da da njena vrednost moze da se krece od -32768 - +32767 (ove granice pisem iz glave. Zbir treba da bude 65535.). Problem moze da bude sledeci. Ako npr. definises promenljivu tipa int, kao npr int Brojac = 0. I u svakom interaptu je povecas za 1 i ispitujes da li je ona veca od 40000. Ako jeste onda nesto uradi. Evo primer:
int Brojac = 0;
isr TMR()
{
Brojac++;
if(Brojac > 40000)
{// Ovde se onda radi neka logika
}
}
Progrma nikada nece da izvrsi deo koda u if-u. To je zbog toga jer je promeljniva oznacena, tj. kada predje onih 32767, onda na sledeci inkrement ona dobija vrednost -1. Zbog toga se mora voditi racuna i o tome, tj. da li neka promeljniva treba da bude oznacena ili neoznacena.
ISR(TIMER1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
TCNT1 = 62437; // preload timer
if(digitalRead(8) == HIGH){
//digitalWrite(LED, HIGH);
taster1test++;
if(taster1test > 3){
mySerial.println(1111); //poruka koja se šalje kao komanda
}
}
else{
taster1test = 0;
mySerial.println(0000);
}
if(digitalRead(9) == HIGH){
//digitalWrite(LED, HIGH);
taster2test++;
if(taster2test > 3){
mySerial.println(2222); //poruka koja se šalje kao komanda
}
}
else{
taster2test = 0;
mySerial.println(3333);
}
}
void loop() {
}
testirao sam slanje poruka, to radi, e sad ne znam da li sam pogodio dobro vrijeme u interrupt-u a mislim da nisam jer imam kašnjenje koje se okom vidi (kasni palenje ledice nako pritiska tastera).
Morao sam dodati i poruku za gašenje ledica u ovom kodu jer mi ostaju ledice upaljene na drugom Arduinu (receiveru) i na njemu sam morao ostaviti i "if" uslov i za provjeru da li je komanda za palenje ili gašenje, kada sam zamjenio sa "else" onda sam dobio treperenje ledica kada su istovpremeno aktivne?
Evo i kod od receivera, šta bih tu trebao izmjeniti?
if(mySerial.available() > 1){
int input = mySerial.parseInt();//read serial input and convert to integer (-32,768 to 32,767)
if(input == 1111){//if on code is received
digitalWrite(ledPin, HIGH);//turn LED on
}
/*else{
digitalWrite(ledPin, LOW);
}*/
if(input == 0000){//if off code is received
digitalWrite(ledPin, LOW);//turn LED off
}
if(input == 2222){
digitalWrite(ledPin2, HIGH);
}
/*else{
digitalWrite(ledPin2, LOW);
}*/
if(input == 3333){
digitalWrite(ledPin2, LOW);
}
}
mySerial.flush();//clear the serial buffer for unwanted inputs
//delay(20);//delay little for better serial communication
evo par saveta. Interapt rutinu si lepo uradio. Objasnicu ti malo kako kako tajmer radi. Ono sto mi se ne svidja kod tebe u programu je to sto koristis softverski uart. Razumem da je to sada za potrebe provere rada i to je oprosteno. Ovako cemo. Idemo prvo na interapt tajmera, pa na hardverski uart pooling metodom, pa na uart interapte. Tada ce ti vec dosta stvari biti mnogo jasno i moci ces da pravis ozbiljnije programe koji ce maksimalno koristiti hardverske resurse mikrokontrolera. To i jeste poenta rada mikrokontrolera.
Da se vratim na tajmer i proracunavanje perioda od interesa.
Tajmer je hardverski modul mikrokontrolera koji ima jedan prost zadatak, a to je da broji sistemske taktove. Nista drugo pametno ne radi tajmer. On moze da se koristi kao osnova za generisanje PWM signala asimetricnih/simetricnih, jednofaznih/visefaznih itd. Ovde cemo da se fokusiramo na osnovnu i najbitniju funkciju rada tajmera.
Sta to znaci brojanje sistemskih taktova. Ako MCU radi npr. na 1MHz to znaci da mu je osnovna perioda takta 1us (1/1MHz). Tajmerski registar koji se zove TCNT ce da poveca svoju vrednost na svaku 1us. Ako je sirina ovog registra 16-bit-a, to znaci da tajmer moze da broji od 0 - 65535, tj. moze maksimalno da odbroji 65536 odbiraka, sto odgovara vremenu od 65536us. Ako ovaj vremenski interval nije dovoljan, onda ukoliko MCU to dozvoljava, mozemo da tajmeru dodelimo preskaler koji ima zadatak da nam poveca opseg brojanja na racun deljenja sistemskog takta koji ulazi u nas tajmer. Ako npr. na postojecem primeru aktiviramo preskaler koji deli takt 1:4, onda dobijamo sledecu situaciju: Ttmr = 4 / (1MHz) = 4us, tj. osnovni inkrement tajmera je sada 4us. Maksimalno odbrojavanje tajmera ce sada biti 4us * 65536 = 262.166ms. Ako tajmeru dodelimo i prekid (interapt) onda imamo precizno generisanje vremenskih intervala. Prekid se desava (uglavnom, ali to zavisi od arhitekrure, a kod nekih MCU-ova je programabilan) pri prelasku sa MAX vrednosti TMR-a na 0, tj. sa 65535 na 0 (ako je TMR 16-bitni).
Npr. ako zelis da generises interapt na svaku 1ms sa 16-bitnim tajmerom pri frekvenciji MCU-a od 16MHz, onda to racunas na sledeci naci.
Fclk = 16MHz => Tclk = 62.5ns
1ms / 62.5ns = 16000. To znaci da TMR treba da odbroji 16000 taktova. To postizemo tako sto u TMR registar upisujemo TMR = 65536 - 16000 = 49536.
TMR = 0xC180
Ovo je 16-bitna vrednost. Moras da vodis i racuna kako upisujes 16-bitnu vrednost na 8-bitnom MCU. To se radi tako sto se prvo u H registar upise vrednost, pa onda u L. Na konktretnom primeru:
TCNT1H = 0xC1;
TCNT1L = 0x80;
Ti si u kodu napisao TCNT1. To je 16-bitna promenljiva koja se fizicki sastoji od dva registra, TCNT1H i TCNT1L.
To za softverski uart, nigdje nisam mogao naći na netu da je neko koristio hardverski, to me je malo bunilo a ujedno nisam znao kako da uradim hardverski uart i onda je ostalo tako, vjerujem da mi i to malo pravi probleme.?
Nije mi jasan TMR registar, jer ga ne mogu naći za ATmega328P, ni u datasheet-u ni u konkretnim primjerima, samo u TCNT1 se upisuje vrijednost, da li je to samo slučaj kod ovog MCU-a ili ja griješim?
(01-19-2018, 06:38 PM)Makso Wrote: To za softverski uart, nigdje nisam mogao naći na netu da je neko koristio hardverski, to me je malo bunilo a ujedno nisam znao kako da uradim hardverski uart i onda je ostalo tako, vjerujem da mi i to malo pravi probleme.?
Nije mi jasan TMR registar, jer ga ne mogu naći za ATmega328P, ni u datasheet-u ni u konkretnim primjerima, samo u TCNT1 se upisuje vrijednost, da li je to samo slučaj kod ovog MCU-a ili ja griješim?
TCNT1 je tajmerski registar tajmera 1. Ovaj registar je 16-bitni i sastoji se fizicki (posto je MCU 8-bitni) iz dva registra: TCNT1H i TCNT1L. Sve pise u PDF-u: http://ww1.microchip.com/downloads/en/De...asheet.pdf
Krece od strane 49. Samo treba sesti i procitati.
Sto se tice hardverskog uart-a, nemoj ni da trazis nigde. Sve ti pise u gore pomenutom PDF-u. Nista ti drugo od literature ne treba. Krenes redom i podesavas registre.
01-19-2018, 11:19 PM (This post was last modified: 01-19-2018, 11:20 PM by Makso.)
Izbacio sam onaj SoftwareSerial.h, umjesto onih linija mySerial.println zamjenio sa Serial.println, prebacio HC-12 na pinove RX i TX, to trenutno radi kao i prije, da li je to sada hardverski uart?
Usput sam opet dodao konrolnu ledicu kada aktiviram taster da znam kad je taster stvarno aktivan i tek onda skontam da imam kašnjenje u komunikaciji između dva Arduina, kada je taster aktivan nakon 1-2s se aktivira ledica na drugom Arduinu, da li je tu problem do interrupt-a ili uart-a?
01-21-2018, 01:26 PM (This post was last modified: 01-21-2018, 01:45 PM by Makso.)
@YuMERA
Ne moraju biti poruke u obliku brojeva.
Probao sam sa tom prepravkom, ništa se nije promjenilo.
Svrha ovog sklopa je da koristim kao univerzalni džojstik (bežični naravno), po potrebi ja ću sebi proširivati koliko mi treba komandi, tako da to mogu ugraditi gdje hoću (ne u industrijske uslove), možda nekad bude upgrade da imam nekoliko PWM izlaza, ali to mi sada nije bitno.
if(Serial.available() > 0){
delay(10);
int input = Serial.parseInt();//read serial input and convert to integer (-32,768 to 32,767)
if(input == 1111){//if on code is received
digitalWrite(ledPin, HIGH);//turn LED on
}
if(input == 0000){//if off code is received
digitalWrite(ledPin, LOW);//turn LED off
}
if(input == 2222){
digitalWrite(ledPin2, HIGH);
}
if(input == 3333){
digitalWrite(ledPin2, LOW);
}
}
Serial.flush();//clear the serial buffer for unwanted inputs
}
void loop() {
while (Serial.available()) { // Ako HC-12 ima podatke
inByte = Serial.read(); // citamo svaki bajt primljen od HC-12
readBuffer += char(inByte); // dodajemo svaki bajt u readBuffer string variable
}
delay(10);
if(readBuffer == "GO"){ //ako je primljena komanda "GO" (gore)
digitalWrite(ledPin, HIGH);//turn LED on
}else if (readBuffer == "DO"){ //ako je primljena komanda "DO" (dole)
digitalWrite(ledPin, LOW);//turn LED off
}else if (readBuffer == "LE"){ //ako je primljena komanda "LE" (levo)
digitalWrite(ledPin2, HIGH);
}else if (readBuffer == "DE"){ //ako je primljena komanda "DE" (desno)
digitalWrite(ledPin2, LOW);
}
Serial.flush();//clear the serial buffer for unwanted inputs
}
Evo ovako se to obicno citaju serijski podaci na prijemnoj strani. Sto znaci da ove komande moras ispostovati u kodu i sa transmit strane.
Ovo sto sad koristis je Hardverski uart i ne bi smeo da imas spojen USB kabl sa PC-a na arduino (ni zbog napajanja) jer ces imati nepravilno funkcionisanje.
Ako hoces da koristis i Serial monitor radi monitoringa podataka onda moras koristiti i softverski uart.
Nadam se da ta dva HC-12 modula komuniciraju na istom kanalu (po defaultu je tako ako nisi nista menjao) mislim da kanali idu od 1 -100 i da su pomereni za 400kHz