Vstupy a výstupy AVR: Rozdiel medzi revíziami
Zo stránky SensorWiki
Vytvorená stránka „ == Digitálne výstupy (LED) == == Digitálne vstupy (Tlačidlá) == === '''Ošetrenie zákmitov (''debouncing'')''' === '''Literatúra:''' * Jack G. Ganssle: '…“ |
Bez shrnutí editace |
||
(26 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 2: | Riadok 2: | ||
== Digitálne výstupy (LED) == | == Digitálne výstupy (LED) == | ||
Teraz už vieme celkom dobre ovládať výstupy, takže by sme mohli skúsiť ten obligátny príkaz na blikanie LED diódou, potrebujeme už len nejaké oneskorenie. Vedeli by sme to robiť pomocou funkcie napísanej v assembleri, ako sme to robili na prvom cvičení. Trocha (ale len trocha) si to zjednodušíme a využijeme už zadefinovanú funkciu <code> _delay_ms() </code> | |||
=== Využívanie funkcie <code>_delay_ms()</code> === | |||
<tabs> | |||
<tab name="AVR C"><source lang="cpp"> | |||
#define F_CPU 16000000UL // toto je lepsie vlozit do parametrov pre kompilator | |||
#include <avr/io.h> | |||
#include <util/delay.h> | |||
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) | |||
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) | |||
#define LED1 PB5 // zabudovana dioda | |||
void delay(int delay) // vlastna funkcia pre dlhsie casy | |||
{ | |||
for (int i=1; i<=delay; i++) | |||
_delay_ms(1); | |||
} | |||
int main(void) | |||
{ | |||
set_bit(DDRB,LED1); // set pin LED1 as output | |||
while(1) | |||
{ | |||
delay(250); // 250 ms delay | |||
set_bit(PORTB,LED1); // LED1 = log.1 | |||
delay(250); // 250 ms delay | |||
clear_bit(PORTB,LED1); // LED1 = log.0 | |||
} | |||
return(0); // sem nikdy neprideme | |||
} | |||
</source></tab> | |||
<tab name="Arduino"> | |||
<source lang="arduino"> | |||
void setup() | |||
{ | |||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output. | |||
} | |||
void loop() | |||
{ | |||
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) | |||
delay(250); // wait for a quarter second | |||
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW | |||
delay(250); // wait for a quarter second | |||
} | |||
</source></tab> | |||
</tabs> | |||
Prečo je tento program napísaný práve takto, s pomocnou funkciou <code>delay</code> a prečo nepoužijeme rovno <code>_delay_ms()</code> sa dočítate v podrobnejšom texte [[Oneskorenia_s_AVR]]. | |||
== Digitálne vstupy (Tlačidlá) == | == Digitálne vstupy (Tlačidlá) == | ||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_TlacitkaFoto01.jpg]]<BR> | |||
''Rozličné typy tlačidiel vhodných pre mikropočítač.'' | |||
</div> | |||
Chceli by sme ďalej naprogramovať zapínanie a vypínanie jedným tlačítkom. Na predmete Základy počítačov sme tento problém riešili pomocou stavového diagramu. | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_StavovyDiagram01.png|MIPS_StavovyDiagram01.png]]<BR> | |||
''Stavový diagram pre tlačítko.'' | |||
</div> | |||
K nemu zodpovedajúci program môže vyzerať napríklad nasledovne. Na ukladanie aktuálneho stavu {On, Off} sme použili špeciálny datový typ Enumerate, ktorý má práve dve hodnoty. | |||
Okrem toho si všimnite, že testujeme, či je vstup v log. 0 (tlačítko spína k zemi). | |||
<tabs><tab name="program03-1.c"> | |||
<source lang="c++"> | |||
#include <avr/io.h> | |||
#include <util/delay.h> | |||
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) | |||
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) | |||
#define LED1 PB5 // zabudovana dioda | |||
#define SW1 PD6 // tlacitko | |||
enum states { On, Off }; | |||
int main(void) | |||
{ | |||
enum states LedState = Off; | |||
set_bit(DDRB,LED1); // set pin LED1 as output | |||
set_bit(PORTD,SW1); // pull-up resistor ON | |||
while(1) | |||
{ | |||
if ( (LedState == Off) && bit_is_clear(PIND,SW1) ) | |||
{ | |||
LedState = On; | |||
set_bit(PORTB,LED1); // LED1 = log.1 | |||
} | |||
else if ( (LedState == On) && bit_is_clear(PIND,SW1) ) | |||
{ | |||
LedState = Off; | |||
clear_bit(PORTB,LED1); // LED1 = log.0 | |||
} | |||
} | |||
return(0); // sem nikdy neprideme | |||
} | |||
</source></tab></tabs> | |||
Ak si to skúsite, zistíte, že to ako tak funguje, ale viacmenej len náhodne. Je to spôsobené tým, že mirkopočítač je strašne rýchly, a kým držíme tlačítko stlačené, zmení stav aj niekoľkotisíc krát, takže výsledný stav je potom viacmenej náhodný. Aby to fungovalo správne, musíme rozlišovať až štyri rozličné stavy tlačítka: Vypnuté (Off), Práve stlačené (Down), Zapnuté (On) a Práve pustené (Up). | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_PushButon4States.png]]<BR> | |||
''Stavový diagram tlačítka s rozpoznaním zmeny stavov.'' | |||
</div> | |||
Keď máme nakreslený stavový diagram, ľahko podľa neho preklopíme vyššieuvedený program. | |||
<tabs><tab name="program03-2.c"> | |||
<source lang="c++"> | |||
#include <avr/io.h> | |||
#include <util/delay.h> | |||
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) | |||
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) | |||
#define LED1 PB5 // zabudovana dioda | |||
#define SW1 PD6 // tlacitko | |||
enum states { Off, Down, On, Up }; | |||
int main(void) | |||
{ | |||
enum states LedState = Off; | |||
enum states Tlacitko = Off; | |||
set_bit(DDRB,LED1); // set pin LED1 as output | |||
set_bit(PORTD,SW1); // pull-up resistor ON | |||
while(1) | |||
{ | |||
/* Oproti predoslemu programu sem pridame druhy * | |||
* stavovy automat len pre tlacitko s jeho styrmi * | |||
* stavmi Off, Down, On a Up */ | |||
if ( (Tlacitko == Off) && bit_is_clear(PIND,SW1) ) | |||
{ | |||
Tlacitko = Down; | |||
} | |||
else if ( (Tlacitko == Down) && bit_is_clear(PIND,SW1) ) | |||
{ | |||
Tlacitko = On; | |||
} | |||
else if ( (Tlacitko == On) && bit_is_set(PIND,SW1) ) | |||
{ | |||
Tlacitko = Up; | |||
} | |||
else if ( (Tlacitko == Up) && bit_is_set(PIND,SW1) ) | |||
{ | |||
Tlacitko = Off; | |||
} | |||
/* Toto je povodny stavovy automat pre LEDku * | |||
* upraveny tak, ze menime stav len ak je tlacitko * | |||
* v stave DOWN, cize ked zmenilo stav. */ | |||
if ( (LedState == Off) && (Tlacitko==Down) ) | |||
{ | |||
LedState = On; | |||
set_bit(PORTB,LED1); | |||
} | |||
else | |||
if ( (LedState == On ) && (Tlacitko==Down) ) | |||
{ | |||
LedState = Off; | |||
clear_bit(PORTB,LED1); | |||
} | |||
} /* end of while loop */ | |||
return(0); // sem nikdy neprideme | |||
} | |||
</source></tab></tabs> | |||
Takto by to už malo fungovať, ale napriek tomu môžete občas pozorovať ako keby náhodné prepnutie stavu. Je to spôsobené javom, kedy sa z mechanických príčin nezmení stav na tlačítku okamžite a trvale, ale kontakt niekoľkokrát preskočí, vzniknú tzv. ''zákmity''. | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_PushButonBouncesScope.png]]<BR> | |||
''Zákmity zachytené osciloskopom.'' | |||
</div> | |||
Aby nám všetko naozaj správne fungovalo, musíme aj tieto zákmity ošetriť (angl. termín ''debouncing''). | |||
=== '''Ošetrenie zákmitov (''debouncing'')''' === | === '''Ošetrenie zákmitov (''debouncing'')''' === | ||
==== '''a) Hardvérové''' ==== | |||
Jeden spôsob, ako sa vysporiadať so zákmitmi je pridať okolo tlačítka niekoľko ďalších súčiastok, ktoré zákmity eliminujú. | |||
<div style='text-align: center;'> | |||
[[Súbor:MIPS_DebouncingSchematicDiagram.png]]<BR> | |||
''Niekoľko príkladov hardvérového odstránenia zákmitov.'' | |||
</div> | |||
==== '''b) Softvérové''' ==== | |||
Jednoduchší a lacnejší spôsob je ošetriť zákmity softvérovo, ale za cenu zdržania programu. Princíp je jednoduchý, ak zistíme, že sa stav tlačítka zmenil, počkáme nejaký čas na ustálenie a potom overíme, či je tento stav stabilný, alebo to bol len náhodný impulz. Realizácia je možná napríklad takto (uvádzame len jedno z miest, kde sa program zmenil o doplnkovú kontrolu stavu | |||
<tabs><tab name="program03-3.c"> | |||
<source lang="c++"> | |||
/* Test s osetrenim zakmitov */ | |||
if ( (Tlacitko == Off) && bit_is_clear(PIND,SW1) ) | |||
{ // Ak je tlacitko stlacene, | |||
_delay_ms(10); // potom chvilu pockajme | |||
if ( bit_is_clear(PIND,SW1) ) // a skontrolujme, ci naozaj je | |||
Tlacitko = Down; // a ak ano, tak zmenime stav. | |||
} | |||
</source></tab></tabs> | |||
== Úlohy == | |||
Ak ste úspešne prišli až sem, pozrite si zadanie v Classroome a vyplňte formulár s otázkami. K zadaniu potom nahrajte aj zdrojový kód <code>program03.c</code> a fotografiu alebo video fungujúceho programu s so zapínaním a vypínaním aspoň jednej LED diódy na vašom procesore. | |||
== Literatúra == | |||
<references group="REF" /> | |||
* Jack G. Ganssle: ''[https://cseweb.ucsd.edu/classes/sp07/cse140L/debounce.pdf A Guide to Debouncing]''. The Ganssle Group, Rev 2: April, 2007. | * Jack G. Ganssle: ''[https://cseweb.ucsd.edu/classes/sp07/cse140L/debounce.pdf A Guide to Debouncing]''. The Ganssle Group, Rev 2: April, 2007. | ||
* Elliot Williams: ''Embed With Elliot:Debounce Your Noisy Buttons'', [https://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-i/ Part I]. Hackaday, 2015. | * Elliot Williams: ''Embed With Elliot:Debounce Your Noisy Buttons'', [https://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-i/ Part I]. Hackaday, 2015. | ||
* Elliot Williams: ''Embed With Elliot:Debounce Your Noisy Buttons'', [https://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-ii/ Part II]. Hackaday, 2015. | * Elliot Williams: ''Embed With Elliot:Debounce Your Noisy Buttons'', [https://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-ii/ Part II]. Hackaday, 2015. | ||
* Mike Szczys: ''[https://hackaday.com/2010/11/09/debounce-code-one-post-to-rule-them-all/ Debounce Code – One Post To Rule Them All]''. Hackaday, 2010. | |||
+ Naviac: | |||
* https://www.ladyada.net/learn/arduino/lesson5.html | |||
* https://raw.githubusercontent.com/j-bellavance/Tutorials/master/State%20machines%20Tutorial/Part%201/State%20machine%20with%20Arduino%20Part%201.pdf | |||
* https://www.avrfreaks.net/sites/default/files/forum_attachments/debounce.pdf | |||
+ Stavové automaty | |||
* https://hackaday.com/2018/04/06/state-your-intentions-more-clearly-with-state-machines/ | |||
* https://gist.github.com/NormalUniverse/f807ca9f267b861365c688193ddfdd68 | |||
* http://johnsantic.com/comp/state.html | |||
* Simulacia krok za krokom https://graphit.web.app/app | |||
* FSM v LaTeX https://hayesall.com/blog/latex-automata/ | |||
* https://ivanzuzak.info/noam/webapps/fsm_simulator/ | |||
<BR><BR>'''Navigácia:''' | |||
[https://senzor.robotika.sk/sensorwiki/index.php/Programovanie_AVR_v_jazyku_C 🡄 Predošlé cvičenie] | [[Mikropočítačové systémy (MIPS)#Cvičenia| 🡅 Zoznam cvičení]] | [[Sériové komunikačné rozhranie|Nasledujúce cvičenie 🡆]] | |||
[[Category:AVR]] [[Category:MIPS]] |
Aktuálna revízia z 07:23, 28. február 2024
Digitálne výstupy (LED)
Teraz už vieme celkom dobre ovládať výstupy, takže by sme mohli skúsiť ten obligátny príkaz na blikanie LED diódou, potrebujeme už len nejaké oneskorenie. Vedeli by sme to robiť pomocou funkcie napísanej v assembleri, ako sme to robili na prvom cvičení. Trocha (ale len trocha) si to zjednodušíme a využijeme už zadefinovanú funkciu _delay_ms()
Využívanie funkcie _delay_ms()
#define F_CPU 16000000UL // toto je lepsie vlozit do parametrov pre kompilator
#include <avr/io.h>
#include <util/delay.h>
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define LED1 PB5 // zabudovana dioda
void delay(int delay) // vlastna funkcia pre dlhsie casy
{
for (int i=1; i<=delay; i++)
_delay_ms(1);
}
int main(void)
{
set_bit(DDRB,LED1); // set pin LED1 as output
while(1)
{
delay(250); // 250 ms delay
set_bit(PORTB,LED1); // LED1 = log.1
delay(250); // 250 ms delay
clear_bit(PORTB,LED1); // LED1 = log.0
}
return(0); // sem nikdy neprideme
}
void setup()
{
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output.
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(250); // wait for a quarter second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(250); // wait for a quarter second
}
Prečo je tento program napísaný práve takto, s pomocnou funkciou delay
a prečo nepoužijeme rovno _delay_ms()
sa dočítate v podrobnejšom texte Oneskorenia_s_AVR.
Digitálne vstupy (Tlačidlá)
Chceli by sme ďalej naprogramovať zapínanie a vypínanie jedným tlačítkom. Na predmete Základy počítačov sme tento problém riešili pomocou stavového diagramu.
K nemu zodpovedajúci program môže vyzerať napríklad nasledovne. Na ukladanie aktuálneho stavu {On, Off} sme použili špeciálny datový typ Enumerate, ktorý má práve dve hodnoty. Okrem toho si všimnite, že testujeme, či je vstup v log. 0 (tlačítko spína k zemi).
#include <avr/io.h>
#include <util/delay.h>
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define LED1 PB5 // zabudovana dioda
#define SW1 PD6 // tlacitko
enum states { On, Off };
int main(void)
{
enum states LedState = Off;
set_bit(DDRB,LED1); // set pin LED1 as output
set_bit(PORTD,SW1); // pull-up resistor ON
while(1)
{
if ( (LedState == Off) && bit_is_clear(PIND,SW1) )
{
LedState = On;
set_bit(PORTB,LED1); // LED1 = log.1
}
else if ( (LedState == On) && bit_is_clear(PIND,SW1) )
{
LedState = Off;
clear_bit(PORTB,LED1); // LED1 = log.0
}
}
return(0); // sem nikdy neprideme
}
Ak si to skúsite, zistíte, že to ako tak funguje, ale viacmenej len náhodne. Je to spôsobené tým, že mirkopočítač je strašne rýchly, a kým držíme tlačítko stlačené, zmení stav aj niekoľkotisíc krát, takže výsledný stav je potom viacmenej náhodný. Aby to fungovalo správne, musíme rozlišovať až štyri rozličné stavy tlačítka: Vypnuté (Off), Práve stlačené (Down), Zapnuté (On) a Práve pustené (Up).
Keď máme nakreslený stavový diagram, ľahko podľa neho preklopíme vyššieuvedený program.
#include <avr/io.h>
#include <util/delay.h>
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define LED1 PB5 // zabudovana dioda
#define SW1 PD6 // tlacitko
enum states { Off, Down, On, Up };
int main(void)
{
enum states LedState = Off;
enum states Tlacitko = Off;
set_bit(DDRB,LED1); // set pin LED1 as output
set_bit(PORTD,SW1); // pull-up resistor ON
while(1)
{
/* Oproti predoslemu programu sem pridame druhy *
* stavovy automat len pre tlacitko s jeho styrmi *
* stavmi Off, Down, On a Up */
if ( (Tlacitko == Off) && bit_is_clear(PIND,SW1) )
{
Tlacitko = Down;
}
else if ( (Tlacitko == Down) && bit_is_clear(PIND,SW1) )
{
Tlacitko = On;
}
else if ( (Tlacitko == On) && bit_is_set(PIND,SW1) )
{
Tlacitko = Up;
}
else if ( (Tlacitko == Up) && bit_is_set(PIND,SW1) )
{
Tlacitko = Off;
}
/* Toto je povodny stavovy automat pre LEDku *
* upraveny tak, ze menime stav len ak je tlacitko *
* v stave DOWN, cize ked zmenilo stav. */
if ( (LedState == Off) && (Tlacitko==Down) )
{
LedState = On;
set_bit(PORTB,LED1);
}
else
if ( (LedState == On ) && (Tlacitko==Down) )
{
LedState = Off;
clear_bit(PORTB,LED1);
}
} /* end of while loop */
return(0); // sem nikdy neprideme
}
Takto by to už malo fungovať, ale napriek tomu môžete občas pozorovať ako keby náhodné prepnutie stavu. Je to spôsobené javom, kedy sa z mechanických príčin nezmení stav na tlačítku okamžite a trvale, ale kontakt niekoľkokrát preskočí, vzniknú tzv. zákmity.
Aby nám všetko naozaj správne fungovalo, musíme aj tieto zákmity ošetriť (angl. termín debouncing).
Ošetrenie zákmitov (debouncing)
a) Hardvérové
Jeden spôsob, ako sa vysporiadať so zákmitmi je pridať okolo tlačítka niekoľko ďalších súčiastok, ktoré zákmity eliminujú.
b) Softvérové
Jednoduchší a lacnejší spôsob je ošetriť zákmity softvérovo, ale za cenu zdržania programu. Princíp je jednoduchý, ak zistíme, že sa stav tlačítka zmenil, počkáme nejaký čas na ustálenie a potom overíme, či je tento stav stabilný, alebo to bol len náhodný impulz. Realizácia je možná napríklad takto (uvádzame len jedno z miest, kde sa program zmenil o doplnkovú kontrolu stavu
/* Test s osetrenim zakmitov */
if ( (Tlacitko == Off) && bit_is_clear(PIND,SW1) )
{ // Ak je tlacitko stlacene,
_delay_ms(10); // potom chvilu pockajme
if ( bit_is_clear(PIND,SW1) ) // a skontrolujme, ci naozaj je
Tlacitko = Down; // a ak ano, tak zmenime stav.
}
Úlohy
Ak ste úspešne prišli až sem, pozrite si zadanie v Classroome a vyplňte formulár s otázkami. K zadaniu potom nahrajte aj zdrojový kód program03.c
a fotografiu alebo video fungujúceho programu s so zapínaním a vypínaním aspoň jednej LED diódy na vašom procesore.
Literatúra
- Jack G. Ganssle: A Guide to Debouncing. The Ganssle Group, Rev 2: April, 2007.
- Elliot Williams: Embed With Elliot:Debounce Your Noisy Buttons, Part I. Hackaday, 2015.
- Elliot Williams: Embed With Elliot:Debounce Your Noisy Buttons, Part II. Hackaday, 2015.
- Mike Szczys: Debounce Code – One Post To Rule Them All. Hackaday, 2010.
+ Naviac:
- https://www.ladyada.net/learn/arduino/lesson5.html
- https://raw.githubusercontent.com/j-bellavance/Tutorials/master/State%20machines%20Tutorial/Part%201/State%20machine%20with%20Arduino%20Part%201.pdf
- https://www.avrfreaks.net/sites/default/files/forum_attachments/debounce.pdf
+ Stavové automaty
- https://hackaday.com/2018/04/06/state-your-intentions-more-clearly-with-state-machines/
- https://gist.github.com/NormalUniverse/f807ca9f267b861365c688193ddfdd68
- http://johnsantic.com/comp/state.html
- Simulacia krok za krokom https://graphit.web.app/app
- FSM v LaTeX https://hayesall.com/blog/latex-automata/
- https://ivanzuzak.info/noam/webapps/fsm_simulator/
Navigácia:
🡄 Predošlé cvičenie | 🡅 Zoznam cvičení | Nasledujúce cvičenie 🡆