Vraag:
Kan een functie automatisch worden aangeroepen wanneer een invoer verandert?
Peter Bloomfield
2014-02-20 06:42:53 UTC
view on stackexchange narkive permalink

Momenteel controleert mijn schets elke keer een invoerpin in de hoofdlus. Als het een wijziging detecteert, roept het een aangepaste functie aan om erop te reageren. Hier is de code (ingekort tot de essentie):

  int pinValue = LOW; void pinChanged () {//...}void setup () {pinMode (2, INPUT);} void loop () {// Lees huidige invoer int newValue = digitalRead (2); // Is de invoer veranderd? if (newValue! = pinValue) {pinValue = newValue; pinChanged (); }}  

Helaas werkt dit niet altijd goed voor zeer korte wijzigingen aan de invoer (bijv. korte pulsen), vooral als loop () een beetje langzaam.

Is er een manier om de Arduino de invoerwijziging te laten detecteren en mijn functie automatisch aan te roepen?

Wat u zoekt, is een externe onderbreking
Drie antwoorden:
#1
+26
Peter Bloomfield
2014-02-20 06:42:53 UTC
view on stackexchange narkive permalink

U kunt dit doen met externe interrupts. De meeste Arduino's ondersteunen dit echter alleen op een beperkt aantal pinnen. Zie de documentatie op attachInterrupt () voor volledige details.

Aangenomen dat je een Uno gebruikt, zou je het als volgt kunnen doen:

  void pinChanged () {//...}void setup () {pinMode (2, INPUT); attachInterrupt (0, pinChanged, CHANGE);} void loop () {}  

Dit zal pinChanged () aanroepen wanneer er een verandering wordt gedetecteerd op externe interrupt 0. Op de Uno komt dat overeen met GPIO-pin 2. De externe interruptnummering is anders op andere kaarten, dus het is belangrijk om de relevante documentatie te raadplegen.

Er zijn echter beperkingen aan deze benadering. De aangepaste pinChanged () -functie wordt gebruikt als een Interrupt Service Routine (ISR). Dat betekent dat de rest van de code (alles in loop () ) tijdelijk wordt gestopt terwijl de oproep wordt uitgevoerd. Om te voorkomen dat belangrijke timing wordt verstoord, moet u ernaar streven om ISR's zo snel mogelijk te maken.

Het is ook belangrijk op te merken dat er geen andere interrupts worden uitgevoerd tijdens uw ISR. Dat betekent dat alles dat afhankelijk is van interrupts (zoals de kernfuncties delay () en millis () ) er mogelijk niet goed in werkt.

Ten slotte, als je ISR globale variabelen in de sketch moet veranderen, dan moeten ze meestal gedeclareerd worden als vluchtig , bijvoorbeeld:

  vluchtig int someNumber;  

Dat is belangrijk omdat het de compiler vertelt dat de waarde onverwachts kan veranderen, dus let op dat je geen verouderde kopieën / caches ervan gebruikt.

met betrekking tot de "korte pulsen" die in de vraag worden genoemd, is er een minimale tijd dat de pin in een staat moet zijn om de interrupt te activeren? (het zal duidelijk veel minder zijn dan pollen, wat afhangt van wat er nog meer in de lus gebeurt)
@sachleen Dat werkt zolang het niet gebeurt tijdens het uitvoeren van een ISR-functie (zoals uitgelegd in het antwoord); daarom moet `pinChanged ()` zo kort mogelijk zijn. Daarom moet de minimumtijd doorgaans de tijd zijn om de functie `pinChanged ()` zelf uit te voeren.
+1 voor dit zeer gedetailleerde antwoord dat alle belangrijke dingen bevat waar je op moet letten bij het gebruik van interrupts!
Naast het declareren van gedeelde globals "vluchtig", als de globale variabele breder is dan 1 byte, zoals someNumber is, moet u beschermen tegen de pin-change interrupt die optreedt tussen byte-toegangen door het programma. Een statement als `someNumber + = 5;` omvat het toevoegen van de lage bytes en het toevoegen van de hoge bytes inclusief carry. Deze twee (meer, voor bredere variabelen) mogen niet worden gedeeld door een interrupt. Het is voldoende om de interrupts uit te schakelen en ze voor en na de operatie te herstellen.
@sachleen - met betrekking tot de minimale pulsgrootte. Het is moeilijk om een ​​definitief antwoord te vinden in de datasheet, maar te oordelen naar de timing voor pin-change interrupts, worden ze binnen een halve klokcyclus vergrendeld. Als de onderbreking eenmaal is "onthouden", blijft deze onthouden totdat de ISR in werking treedt en deze afhandelt.
#2
+5
mpflaga
2014-02-24 20:36:06 UTC
view on stackexchange narkive permalink

Elke staat van verandering op een pin die is geconfigureerd als digitale invoer kan een onderbreking veroorzaken. In tegenstelling tot de unieke vectoren voor de interrupts die door INT1 of INT2 worden veroorzaakt, gebruikt de PinChangeInt-functie een gemeenschappelijke vector en vervolgens moet de Interrupt Service Routine (ook bekend als ISR) voor deze vector bepalen welke pin is gewijzigd.

Gelukkig maakt PinChangeInt Library dit gemakkelijk.

  PCintPort :: attachInterrupt (PIN, burpcount, RISING); // bevestig een PinChange Interrupt aan onze pin op de stijgende flank // (RISING, FALLING en CHANGE werken allemaal met deze bibliotheek) // en voer de functie burpcount uit wanneer die pin verandert  
#3
  0
Nick Gammon
2015-07-01 07:27:55 UTC
view on stackexchange narkive permalink

In het geval dat u een spanning wilt detecteren die een drempel overschrijdt, in plaats van alleen HOOG of LAAG te zijn, kunt u de analoge comparator gebruiken. Voorbeeldschets:

  vluchtige boolean geactiveerd; ISR (ANALOG_COMP_vect) {triggered = true; } void setup () {Serial.begin (115200); Serial.println ("Gestart."); ADCSRB = 0; // (Uitschakelen) ACME: Analoge comparator-multiplexer ACSR = bit inschakelen (ACI) // (Wissen) Analoge comparator-onderbrekingsvlag | bit (ACIE) // Analog Comparator Interrupt Enable | bit (ACIS1); // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger bij dalende flank)} // einde setupvoid loop () {if (geactiveerd) {Serial.println ("Triggered!"); triggered = false; }} // einde lus  

Dit kan handig zijn voor zaken als lichtdetectoren, waar je misschien een verandering van (bijvoorbeeld) 1V naar 2V op een ingang moet detecteren.

Voorbeeldcircuit:

enter image description here

Je kunt ook de Input Capture Unit op de processor gebruiken, die de exacte tijd van bepaalde inputs onthoudt, door de huidige telling van Timer / Teller 1. Hiermee kunt u het exacte (nou ja, bijna exacte) moment opslaan waarop de gewenste gebeurtenis plaatsvond, in plaats van de vertraging (van waarschijnlijk een paar microseconden) in te voeren voordat een ISR kan worden gebruikt om de huidige tijd.

Voor timingkritische toepassingen kan dit een iets grotere nauwkeurigheid opleveren.

Voorbeeldtoepassing: Verander uw Arduino in een condensatortester



Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 3.0-licentie waaronder het wordt gedistribueerd.
Loading...