Område 2 · lektion 8
Digital ingång
Hittills har kortet bara sagt saker. Nu ska det lyssna. En knapp är den enklaste givaren som finns, och ändå gömmer den två fällor som fäller nästan alla första gången: pinnen som inte är kopplad någonstans, och kontakten som studsar.
Mål
- Du kan koppla en tryckknapp till en digital pinne utan externa komponenter, och rita schemat.
- Du kan förklara varför en opåverkad knapp med
INPUT_PULLUPläses somHIGHoch en nedtryckt somLOW. - Du kan skriva ett villkor som låter knappen styra en utgång.
- Du kan känna igen kontaktstuds på symtomen och lägga in en enkel spärr mot den.
Genomgång
En ingång som inte är kopplad någonstans läser skräp
Tänk dig att du gör pinne 2 till ingång och kopplar en knapp mellan pinnen
och GND. Trycker du ner knappen ligger pinnen på 0 V, alltså
LOW. Det är entydigt. Men när knappen är uppe – vad ligger
pinnen på då?
Ingenting. Den hänger löst, och en lös ingång plockar upp störningar från
lysrör, mobiltelefoner och din egen hand. digitalRead()
levererar då omväxlande HIGH och LOW utan att
någon rört knappen. Det kallas att ingången flyter, och
symtomet är ett bygge som gör saker helt av sig självt.
Lösningen är ett motstånd som drar pinnen till en känd nivå när inget annat gör det. Dras den uppåt mot 5 V heter det pull-up, nedåt mot GND pull-down. Vi använder pull-up, eftersom kortet redan har ett sådant motstånd inbyggt i varje pinne.
Kopplingen med intern pull-up
- Motståndet inuti den streckade rutan finns redan på kortet. Du kopplar det inte – du slår på det, med en rad kod. Rutan är chippet, och allt utanför är det du bygger på kopplingsdäcket.
- S1 är tryckknappen. Den enda sladden du behöver dra går från pinne D2 till knappen, och från knappen till GND. Två sladdar, noll komponenter utöver knappen.
- Pull-up-motståndet är på 20–50 kΩ enligt kortets datablad. Det är stort med flit: det ska räcka för att hålla pinnen uppe men vara svagt nog att knappen enkelt drar ner den.
- Jordsymbolen är samma nolla som i område 1. Knappens andra ben går dit.
Hur mycket ström går det genom motståndet när knappen är nedtryckt? Hela de 5 volten ligger då över det:
I = U / R = 5 V / 20 000 Ω = 0,00025 A = 0,25 mA
I = U / R = 5 V / 50 000 Ω = 0,0001 A = 0,10 mA
Alltså någonstans mellan 0,1 och 0,25 mA. Jämför med de 20 mA en lysdiod vill ha: det här är åttio till tvåhundra gånger mindre, och det är hela poängen. Ett pull-up-motstånd ska hålla en nivå, inte driva något.
digitalRead och INPUT_PULLUP – och inversionen
Två rader gör hela jobbet. Den ena står i setup():
pinMode(KNAPP_PIN, INPUT_PULLUP);
Den andra i loop():
int lage = digitalRead(KNAPP_PIN);
Och nu det som förvirrar alla första gången:
| Knappen | Pinnens spänning | digitalRead() ger | I seriemonitorn |
|---|---|---|---|
| Uppe, opåverkad | 5 V, dragen dit av pull-upen | HIGH | 1 |
| Nedtryckt | 0 V, kortsluten till GND | LOW | 0 |
Nedtryckt är alltså LOW, inte HIGH.
Det känns bakvänt, och det är det också – men bara språkligt. Elektriskt är
det raka motsatsen till bakvänt: knappen har ett enda jobb, och det är att
koppla pinnen till noll. Att den släppta knappen ger 5 V är motståndets
förtjänst, inte knappens.
Skriv det i koden, inte i huvudet
Skriv if (digitalRead(KNAPP_PIN) == LOW) och sätt en kommentar
efter: // LOW betyder nedtryckt. Om ett halvår läser du din
egen kod och minns inte inversionen. Kommentaren tar fem sekunder att
skriva och sparar tjugo minuters felsökning.
Ett villkor som styr en utgång
Nu sitter hela kedjan ihop: givare in, villkor i mitten, ställdon ut. Här är den med en lysdiod som ställdon, eftersom servot kommer i lektion 9.
const int KNAPP_PIN = 2;
const int LAMPA_PIN = 8;
void setup() {
pinMode(KNAPP_PIN, INPUT_PULLUP);
pinMode(LAMPA_PIN, OUTPUT);
}
void loop() {
if (digitalRead(KNAPP_PIN) == LOW) { // LOW betyder nedtryckt
digitalWrite(LAMPA_PIN, HIGH);
} else {
digitalWrite(LAMPA_PIN, LOW);
}
}
Lägg märke till att båda pinnarna får sin pinMode(), och att
de får olika. Den ena lyssnar, den andra driver. Glöms den ena bort blir
symtomen helt olika, och båda ser ut som kodfel.
Kontaktstuds
En tryckknapp är två metallbitar som slås ihop. De studsar mot varandra innan de får kontakt, precis som en boll mot ett bord, och under den tiden går kontakten till och från många gånger. Det pågår i storleksordningen 1 till 10 millisekunder.
- Signalen ligger på
HIGHså länge knappen är uppe – det är pull-upen som håller den där. - I ögonblicket knappen trycks ned faller den till
LOW, men studsar sedan upp och ner några gånger innan den lägger sig. - Klammern markerar studsen. Efter den är signalen stabil.
- Samma sak händer när du släpper knappen. Studsen är inte något som bara drabbar nedtryckningen.
Varför spelar några millisekunder roll? Därför att kortet är snabbt.
16 MHz betyder 16 miljoner klockcykler i sekunden, alltså 16 000 cykler per
millisekund. Ett varv i en loop() som bara läser en pinne och
sätter en utgång tar några mikrosekunder, så under en studs på 5 ms hinner
loopen varvas hundratals gånger – och den ser varje uppstuds som en ny
knapptryckning. Symtomet är att spaken rycker fram och tillbaka, eller att
räknaren hoppar från 3 till 7 på ett tryck.
Den enkla spärren är att läsa om efter en kort paus. Är knappen kvar i samma läge var det ett riktigt tryck och inte en studs:
const int KNAPP_PIN = 2;
const int LAMPA_PIN = 8;
int forra = HIGH; // knappens läge förra varvet
bool tand = false; // vad lampan ska vara
void setup() {
pinMode(KNAPP_PIN, INPUT_PULLUP);
pinMode(LAMPA_PIN, OUTPUT);
}
void loop() {
int nu = digitalRead(KNAPP_PIN);
// Flanken: ögonblicket knappen går från uppe till nedtryckt.
if (forra == HIGH && nu == LOW) {
delay(20); // vänta ut studsen
if (digitalRead(KNAPP_PIN) == LOW) { // ligger den kvar var trycket riktigt
tand = !tand;
digitalWrite(LAMPA_PIN, tand);
}
}
forra = nu;
}
Tre saker gör den här versionen annorlunda:
- Den reagerar på flanken, alltså på övergången från uppe till nedtryckt, inte på att knappen ligger nere. Därför räknas ett långt tryck som ett tryck.
delay(20)väntar ut studsen med marginal – 20 ms är dubbelt så långt som de 10 ms studsen som mest håller på.tand = !tand;vänder på värdet. Utropstecknet är C++:snot.
Den här spärren är inte den slutgiltiga
delay(20) stoppar allt annat i tjugo millisekunder. Med en
knapp och en lampa märks det inte. Med två knappar och något som ska blinka
samtidigt gör det systemet trögt och opålitligt. Den riktiga lösningen
mäter tid utan att stanna, med millis(), och den hör hemma i
område 3. Här nöjer vi oss med att veta vad problemet heter och att
spärren ovan duger till bygge 1.
I industrin och i vardagen
I industrin: en nödstoppsknapp på en maskin kopplas alltid med en brytande kontakt, betecknad NC i datablad – den är sluten när allt är normalt, och trycket bryter kretsen. Motsatsen, en slutande kontakt (NO), är den vanliga tryckknappen: öppen i viloläge. Skillnaden är ett ord i ett datablad och avgör om maskinen stannar eller inte. Valet låter bakvänt av samma skäl som pull-upen gör det, och skälet är detsamma: den nivå som betyder "allt är bra" ska vara den som kräver att kretsen är hel. Går en sladd av, lossnar en skruv eller korroderar en kontakt får styrsystemet exakt samma signal som ett nödstopp och stannar maskinen. Ett fel som gör systemet säkrare i stället för farligare kallas felsäkert, och det är den principen som skiljer en industrikoppling från en hobbykoppling. Kretsarna arbetar dessutom oftast på 24 V i stället för våra 5 V, eftersom den högre spänningen tål längre kablar och mer störningar.
I vardagen: tangentbordet du skriver på studsar också. Varje tangent är en mekanisk kontakt med samma studs på några millisekunder, och skulle datorn läsa den rakt av skulle ett tryck på a ge tre eller fyra a:n. Avstudsningen sitter i tangentbordets egen lilla styrkrets, som väntar ut studsen precis som koden ovan gör. Riktningen är den motsatta mot nödstoppet: här är målet inte säkerhet utan att ett tryck ska bli precis ett tecken, hundratals gånger i minuten, utan att någon tänker på det.
Prova själv
- Koppla en tryckknapp mellan pinne D2 och GND. Rita schemat först – det är två sladdar, och det tar trettio sekunder.
- Skriv en sketch som läser pinnen med
INPUT_PULLUPoch skriver ut värdet till seriemonitorn två gånger i sekunden. Kontrollera att du får 1 när knappen är uppe och 0 när den är nere. - Ta bort
_PULLUPså det bara stårINPUT. Ladda upp och titta på monitorn utan att röra knappen. Så ser en flytande ingång ut. - Bygg vidare till lysdiodsexemplet ovan, så att knappen tänder lampan.
- Lägg till en räknare som skriver ut hur många gånger knappen tryckts. Tryck tio gånger. Blev det tio? Lägg sedan in spärren mot kontaktstuds och räkna om.
Kontrollfrågor
Varför blir en opåverkad knapp HIGH och inte
LOW?
Därför att pull-up-motståndet inuti chippet drar pinnen upp mot 5 V så länge ingenting annat drar den någon annanstans. Knappen kopplar pinnen till GND när den trycks ned, och 0 V vinner över motståndet, eftersom vägen till GND går genom knappens nästan obefintliga resistans medan vägen till 5 V går genom tiotusentals ohm.
Du har glömt INPUT_PULLUP och skrivit
INPUT. Vad ser du?
Värdet hoppar mellan 0 och 1 utan att någon rör knappen, och det påverkas av om du håller handen nära. Ingången flyter. Fungerar det ibland och inte andra gånger är det nästan alltid det här.
Om det interna motståndet är 30 kΩ, hur stor ström går genom det när knappen hålls nedtryckt?
I = U / R = 5 / 30 000 = 0,000167 A, alltså ungefär 0,17 mA. Effekten blir P = U · I = 5 · 0,000167 = 0,00083 W, alltså under en milliwatt. Det är därför du kan hålla knappen nedtryckt hur länge du vill utan att något blir varmt.
Din räknare hoppar från 4 till 9 på ett enda tryck. Vad är fel, och vad är det inte?
Det är kontaktstuds. Det är inte ett trasigt kort, en dålig knapp eller ett fel i räknarlogiken. Koden ser varje studs som en ny tryckning eftersom loopen hinner varvas hundratals gånger under de millisekunder studsen pågår. Lägg in flankdetektering och en paus på cirka 20 ms.
Varför räcker det inte att skriva
if (digitalRead(KNAPP_PIN) == LOW) om du vill räkna
tryckningar?
Därför att villkoret är sant hela tiden knappen hålls nere, inte bara i ögonblicket den trycks. Ett tryck på en halv sekund räknas då tusentals gånger. Du behöver jämföra med förra varvets värde och bara reagera när det ändrats – det är vad flankdetektering betyder.
Kan du använda pinne D2 till både knappen och en lysdiod?
Nej. En pinne är antingen ingång eller utgång, och
pinMode() ställer in vilket. Kortet har fjorton digitala
pinnar, varav tolv är fria att använda – det finns ingen anledning att
tränga ihop sig.
Vanliga fel
INPUTi stället förINPUT_PULLUP. Ingången flyter och värdet hoppar av sig självt. Det ser ut som en trasig knapp och är ett teckens skillnad i koden.- Knappen kopplad mellan pinnen och 5 V. Med intern pull-up ska knappen gå till GND. Går den till 5 V ligger pinnen på 5 V både med och utan tryck, och ingenting händer någonsin.
- Villkoret testar
HIGHnär det borde testaLOW. Allt fungerar tvärtom: lampan lyser tills du trycker. Det är inversionen, och det är ett ord i koden. - Fel ben på knappen. En fyrbent tryckknapp har benen parvis ihopkopplade redan inuti. Sitter dina två sladdar på ett par som alltid hänger ihop är kretsen sluten hela tiden. Vrid knappen ett kvarts varv och prova igen, eller mät med multimeterns genomgångsläge.
- Knappen sitter över mittspåret på kopplingsdäcket. Fyrbenta knappar är gjorda för att grensla spåret. Sitter den på ena sidan är benen kortslutna av däckets egna femhålsgrupper.
- Räknaren rusar. Villkoret reagerar på att knappen är nere i stället för att den trycktes ned. Jämför med förra varvet.
- Lampan blinkar till i stället för att växla. Ett enda tryck hinner räknas flera gånger, så utfallet blir slumpmässigt – ibland tänds den, ibland står den kvar. Det är kontaktstuds, inte en trasig knapp och inte ett fel i villkoret.
pinMode()saknas för knappen. Pinnen står kvar som ingång utan pull-up, vilket är samma symtom som det första felet i listan.
Vidare
Kapitlen Villkor och Typer och variabler i språkreferensen hör till den här guiden.
Nästa guide är servot och den mekaniska strukturen. Där byts lysdioden mot något som rör sig, och därmed blir bygget ett mekatroniskt system i stället för en lampa med en knapp.