Vraag:
Kan iemand deze vreemd uitziende code uitleggen, die wordt gebruikt om timers in te stellen?
The Guy with The Hat
2014-02-24 08:06:06 UTC
view on stackexchange narkive permalink

Terwijl ik naar schetsen kijk die andere mensen hebben geschreven, kom ik af en toe code tegen die er ongeveer zo uitziet:

  TCCR1A = 0; TCCR1B = 0; TCNT1 = 34286; TCCR1B | = (1 << CS12); TIMSK1 | = (1 << TOIE1);  

Ik weet alleen dat het iets te maken heeft met timing / timers (denk ik). Hoe kan ik code op deze manier ontcijferen - en creëren -? Wat zijn TCCR1A , TCCR1B , TCNT1 , CS12 , TIMSK1 en TOIE1 ?

Weet niet genoeg om te antwoorden, maar: http://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b, http://forum.arduino.cc/index .php? topic = 134602.0, en http://stackoverflow.com/questions/9475482/pulse-width-modulation-pwm-on-avr-studio. Weet niet of je deze al hebt gezien.
Download de "complete" datasheet voor uw apparaat van de [Atmel-website] (http://atmel.com/) en lees de hoofdstukken over timers. De datasheet is naar mijn mening verrassend goed om te lezen.
Drie antwoorden:
#1
+15
Connor Wolf
2014-02-24 09:54:06 UTC
view on stackexchange narkive permalink

Dit ziet er niet raar uit. Het is hoe normale MCU-code er in werkelijkheid uitziet.

Wat je hier hebt, is een voorbeeld van het concept van geheugen toegewezen randapparatuur . Kortom, de MCU-hardware heeft speciale locaties in de SRAM-adresruimte van de MCU die eraan is toegewezen. Als u naar deze adressen schrijft, bepalen de bits van de byte die naar het adres n worden geschreven, het gedrag van randapparatuur m .

In wezen hebben bepaalde geheugenbanken letterlijk kleine draden die van de SRAM-cel naar de hardware lopen. Als je een "1" naar dit bit in die byte schrijft, wordt deze SRAM-cel op een logische hoogte gezet, waardoor een deel van de hardware wordt ingeschakeld.

Als je in de headers voor de MCU kijkt , er zijn grote tabellen met keyword<-> adrestoewijzingen. Dit is hoe zaken als TCCR1B enz ... worden opgelost tijdens het compileren.

Dit geheugen-mappingmechanisme wordt extreem algemeen gebruikt in MCU's. De ATmega MCU in de Arduino gebruiken het, evenals de PIC-, ARM-, MSP430-, STM32- en STM8 MCU-series, evenals veel MCU's die ik niet meteen ken.


Arduino -code is het rare, met functies die indirect toegang hebben tot de MCU-besturingsregisters. Hoewel dit er ietwat "mooier" uitziet, is het ook veel langzamer en neemt het veel meer programmaruimte in beslag.

De mysterieuze constanten worden allemaal in detail beschreven in de ATmega328P-datasheet, die je echt zou moeten lezen als je meer wilt doen dan af en toe pinnen op een Arduino wisselen.

Selecteer fragmenten uit de datasheet die hierboven is gelinkt:

enter image description here enter image description here enter image description here

Dus, bijvoorbeeld TIMSK1 | = (1 << TOIE1); stelt de bit TOIE1 in TIMSK1 . Dit wordt bereikt door binair 1 ( 0b00000001 ) naar links te verschuiven met TOIE1 bits, waarbij TOIE1 in een headerbestand wordt gedefinieerd als 0. Dit wordt dan bitsgewijs OR-omgezet in de huidige waarde van TIMSK1 , waardoor deze effectief een bit hoog wordt ingesteld.

Kijkend naar de documentatie voor bit 0 van TIMSK1 , we kunnen zien dat het wordt beschreven als

Wanneer deze bit naar een bit wordt geschreven en de I-vlag in het statusregister is ingesteld (interrupts algemeen ingeschakeld), is de timer / teller1 overlooponderbreking ingeschakeld . De corresponderende Interrupt Vector (Zie ”Interrupts” op pagina 57) wordt uitgevoerd wanneer de TOV1-vlag, die zich in TIFR1 bevindt, is ingesteld.

Alle andere regels moeten op dezelfde manier worden geïnterpreteerd.


Enkele opmerkingen:

Mogelijk ziet u ook zaken als TIMSK1 | = _BV (TOIE1); . _BV () is een veelgebruikte macro die oorspronkelijk afkomstig is van de AVR libc-implementatie. _BV (TOIE1) is functioneel identiek aan (1 << TOIE1) , met het voordeel van betere leesbaarheid.

Ook kunt u ook regels zien zoals als: TIMSK1 & = ~ (1 << TOIE1); of TIMSK1 & = ~ _BV (TOIE1); . Dit heeft de tegenovergestelde functie van TIMSK1 | = _BV (TOIE1); , in die zin dat het de bit TOIE1 in TIMSK1 uitschakelt code>. Dit wordt bereikt door het bitmasker geproduceerd door _BV (TOIE1) te nemen, er een bitsgewijze NOT-bewerking op uit te voeren ( ~ ), en vervolgens TIMSK1 door deze genoteerde waarde (die 0b11111110 is).

Merk op dat in al deze gevallen de waarde van zaken als (1 << TOIE1) of _BV (TOIE1) volledig zijn opgelost op compilatietijd , zodat ze functioneel worden gereduceerd tot een eenvoudige constante, en daarom geen uitvoeringstijd nodig hebben om tijdens runtime te berekenen.


Goed geschreven code zal over het algemeen opmerkingen hebben in lijn met de code die aangeeft wat de registers moeten doen. Hier is een vrij eenvoudige soft-SPI-routine die ik onlangs heb geschreven:

  uint8_t transactByteADC (uint8_t outByte) {// Brengt één byte over naar de ADC en ontvangt tegelijkertijd één byte // doet niets met de chip-select // MSB eerst, data geklokt op de stijgende flank uint8_t loopCnt; uint8_t retDat = 0; for (loopCnt = 0; loopCnt < 8; loopCnt ++) {if (outByte & 0x80) // als het huidige bit hoog is PORTC | = _BV (ADC_MOSI); // stel datalijn anders in PORTC & = ~ (_BV (ADC_MOSI)); // zet het anders uitByte << = 1; // en verschuif de uitvoergegevens voor de volgende iteratie retDat << = 1; // verschuiven over de teruggelezen gegevens PORTC | = _BV (ADC_SCK); // Zet de klok hoog if (PINC & _BV (ADC_MISO)) // sample de invoerregel retDat | = 0x01; // en stel het bit in de retval in als de invoer hoog is PORTC & = ~ (_BV (ADC_SCK)); // set clock low} return retDat;}  

PORTC is het register dat de waarde van outputpinnen binnen PORTC van de ATmega328P regelt. PINC is het register waar de input waarden van PORTC beschikbaar zijn. In wezen gebeuren dit soort dingen intern als u de functies digitalWrite of digitalRead gebruikt. Er is echter een opzoekbewerking die de "pincodes" van de Arduino omzet in daadwerkelijke hardware-pincodes, wat ergens in de orde van 50 klokcycli ligt. Zoals je waarschijnlijk wel kunt raden, als je snel probeert te gaan, is het een beetje belachelijk om 50 klokcycli te verspillen aan een bewerking die er maar 1 nodig zou hebben.

De bovenstaande functie duurt waarschijnlijk ergens in het rijk van 100 -200 klokcycli om 8 bits over te dragen. Dit omvat 24 pin-writes en 8 reads. Dit is vele, vele malen sneller dan het gebruik van de digitale {stuff} functies.

Merk op dat deze code ook zou moeten werken met Atmega32u4 (gebruikt in Leonardo) aangezien deze meer timers bevat dan ATmega328P.
+1 Uitstekend antwoord !! Kun je alsjeblieft wat toelichten over wat je bedoelt met 'Arduino-code is het rare'? Wat is er precies uniek aan Arduino-code, die je meestal niet op andere platforms vindt?
@Ricardo - Waarschijnlijk gebruikt meer dan 90% van de kleine MCU-embedded code directe registermanipulatie. Dingen doen met indirecte nutsfuncties is niet de gebruikelijke manier om IO / randapparatuur te manipuleren. Er zijn enkele toolkits voor het abstraheren van hardwarebesturing (bijvoorbeeld The Atmel ASF), maar die zijn over het algemeen geschreven om zoveel mogelijk te compileren om de runtime-overhead te verminderen, en vereist bijna altijd dat je de randapparatuur echt begrijpt door de datasheets te lezen.
Kortom, arduino-dingen, door te zeggen "hier zijn functies die X doen", zonder echt de moeite te nemen om te verwijzen naar de feitelijke documentatie of hoe de hardware * doet * de dingen die het doet, is niet normaal. Ik begrijp dat het waardevol is als inleidende tool, maar behalve voor snelle prototyping, wordt het nooit echt gedaan in echte professionele omgevingen.
Om duidelijk te zijn, het ding dat arduino-code ongebruikelijk maakt voor embedded MCU-firmware is niet * uniek * voor arduino-code, het is een functie van de algehele benadering. Kortom, als je eenmaal een goed begrip hebt van de * daadwerkelijke * MCU, kost het goed doen (bijvoorbeeld door hardwareregisters rechtstreeks te gebruiken) weinig tot geen extra tijd. Als je dus echte MCU-ontwikkelaars wilt leren, is het veel beter om gewoon te gaan zitten en te begrijpen wat je MCU * eigenlijk * doet, in plaats van te vertrouwen op de abstractie van iemand * anders *, die meestal lek is.
Kortom, het hele Arduino-systeem is min of meer ontworpen om een ​​lage toegangsdrempel voor * niet * -programmeurs mogelijk te maken. Het doet dit goed, maar tegelijkertijd doet het er niet goed aan om eindgebruikers te dwingen de hardware echt * te begrijpen *, simpelweg omdat dat in tegenspraak is met de lage instapdrempel. Ik erken dat dit een waardevolle opstap kan (en is) voor mensen die op weg zijn naar het leren van software / MCU-ontwikkelaars, maar iedereen die het gebruikt, zou geen illusies moeten hebben dat de Arduino-manier noodzakelijkerwijs de beste is, of soms zelfs een goede manier om te doen wat het doet.
Merk op dat ik hier misschien een beetje cynisch ben, maar veel van het gedrag dat ik in de arduino-gemeenschap zie, is het programmeren van antipatronen. Ik zie veel "copy-paste" -programmering, het behandelen van bibliotheken als zwarte dozen, en gewoon algemene slechte ontwerppraktijken in de gemeenschap in het algemeen. Natuurlijk ben ik redelijk actief op EE.stackexchange, dus ik heb misschien een ietwat scheef beeld, aangezien ik een aantal moderatortools heb en als zodanig veel van de gesloten vragen zie. Er is zeker een vooroordeel in de Arduino-vragen die ik daar heb gezien in de richting van "vertel me wat ik met C&P moet repareren", in plaats van "waarom werkt dit niet".
Het is waarschijnlijk ook vermeldenswaard dat het gebruik van C ++ * enigszins * ongebruikelijk is. Het meeste kleine MCU-werk wordt alleen in pure-C gedaan.
Begrepen! Het was me niet duidelijk wat je indirecte toegang tot registers in Arduino noemde, maar nu weet ik dat het de `digitalWrite () 'soort abstracties zijn. Het verlaagt de toetredingsdrempel, maar wordt later een drempel om vooruit te gaan. Maar je antwoord is een grote stap om dat obstakel te verwijderen. Tenminste voor mij.
@Ricardo - Ja. Een van de dingen die ik echt * doe * leuk vind aan arduino als leermiddel, is dat het je een werkomgeving biedt om direct met de hardware te spelen, zonder dat je te maken hebt met het vaak meest irritante deel: de eerste introductie. Ik heb op platforms gewerkt waar ik alleen een internetprovider had en geen werkende voorbeelden. Ik moet proberen op te lossen waarom de seriële debug-interface die ik probeerde aan het werk te krijgen zonder een echte debug-interface is ... betrokken. Het hebben van een platform met eenvoudig te gebruiken seriële libs is erg fijn.
#2
+3
TheDoctor
2014-02-24 09:07:30 UTC
view on stackexchange narkive permalink

TCCR1A is timer / teller 1 controleregister A

TCCR1B is timer / teller 1 controleregister B

TCNT1 is de tellerwaarde van timer / teller 1

CS12 is het 3e klokselectiebit voor timer / teller 1

TIMSK1 is het onderbrekingsmaskerregister van timer / teller 1

TOIE1 is de timer / teller 1 overlooponderbreking inschakelen

Dus de code schakelt timer in / counter 1 op 62,5 kHz en stelt de waarde in op 34286. Vervolgens wordt de overlooponderbreking ingeschakeld, dus wanneer het 65535 bereikt, wordt de interruptfunctie geactiveerd, hoogstwaarschijnlijk aangeduid als ISR (timer0_overflow_vect)

#3
+1
VENKATESAN
2020-01-18 22:44:35 UTC
view on stackexchange narkive permalink

CS12 heeft een waarde van 2 omdat het bit 2 van het TCCR1B-register vertegenwoordigt.

(1 << CS12) neemt de waarde 1 (0b00000001) en verschuift deze 2 keer naar links om (0b00000100) te krijgen. De volgorde van bewerkingen dicteert dat dingen in () eerst gebeuren, dus dit wordt gedaan voordat de "| =" wordt geëvalueerd.

(1 << CS10) neemt de waarde 1 (0b00000001) en verschuift deze 0 keer naar links om (0b00000001) te krijgen. De volgorde van bewerkingen dicteert dat dingen in () eerst gebeuren , dus dit wordt gedaan voordat de "| =" wordt geëvalueerd.

Dus nu krijgen we TCCR1B | = 0b00000101, wat hetzelfde is als TCCR1B = TCCR1B | 0b00000101.

Sinds "|" is "OF", alle bits behalve CS12 in TCCR1B blijven onaangetast.



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...