EUR | USD

Het argument voor Direct Port Manipulation

Programmeren wordt toegankelijk voor steeds meer mensen. Maar in dat proces van 'democratisering' kunnen basale maar waardevolle stukjes code aandacht tekort komen of zelfs volledig worden vergeten. Een goed voorbeeld is de DPM, direct port manipulation. Sommige programmeurs geven als argument dat deze opdrachten moeilijk leesbaar zijn en dat ze nieuwelingen in verwarring kunnen brengen. En daarom zouden ze niet mogen worden gecombineerd met de meer bekende structuren. Maar waarom zouden we niet alle tools inzetten die beschikbaar zijn om code overzichtelijk en efficiënt te houden?

Een voorbeeld: voor iedere poort van een 8-bit microcontroller zijn drie registers van 8 bit nodig om de activiteit van de poort aan te sturen. Het gaat hierbij om de DDRx voor de ingangs-/uitgangsrichting, de PORTx voor de aansturing van het logisch niveau van de pins en de PINx, waar de huidige pinstatussen van de poort in staan. Laten we de C-poort bekijken om hier wat dieper op in te gaan. Tijdens de configuratie wil een programmeur wellicht de pins van poort C instellen als outputs met een initiële LOW-status. Vaak wordt er dan een keurige "for-loop" geschreven, met 3 of 4 regels herhalende code die de pins van nul tot zeven doorloopt: in totaal 32 regels geschreven code die wordt uitgevoerd. Als je bibliotheekfuncties toepast in de loop, zoals pinMode() en digitalWrite(), wordt er meer activiteit gegenereerd op de achtergrond, zodat er een veelvoud aan extra code wordt uitgevoerd.

Uiteindelijk worden de acht bits van het richtingsregister voor poort C (DDRC) op HIGH gezet en de acht bits van het logisch niveau voor poort C (PORTC) op LOW. Precies diezelfde actie kan ook worden bereikt door de bits op HIGH of LOW te zetten met de volgende directe opdrachten voor de poortregisters:

DDRC = 0xFF; //Pins van poort C instellen als OUTPUTS (binair: DDRD = 0b11111111;)

PORTC = 0x00; //Pins van poort C instellen als LOW (binair: PORTC = 0b00000000;)

En hoe zit het met de instelling van poort A als ingang?

DDRA = 0x00; //Pins van poort A instellen als INPUTS (binair: DDRA = 0b00000000;)

En tot slot, poort D als tweerichtingsverkeer:

DDRD = 0x0 F; //De bovenste vier bits van poort D instellen als INPUTS en de onderste als OUTPUTS (in binair: DDRD = 0b00001111);

Als we een hexadecimale waarde gebruiken als 0xFF, krijgen we een interessant inzicht in de toewijzing van de bitwaarde in het register. "0x" is de hexadecimale indicator. De eerste 'F' vertegenwoordigt de bovenste vier bits van een 8-bits register en de tweede 'F' komt overeen met de onderste vier bits.

Alle mogelijke combinaties van bits ingesteld op HIGH of LOW kunnen worden weergegeven door een hexadecimaal of binaire integer (cijfer). Het werken met binaire integers is visueler, omdat je alle acht bits kan zien tijdens het schrijven van de code. Voor de compiler maakt het niet uit, die ondersteunt allebei de varianten. Maar hexadecimaal is korter – en ook gewoon cool.

Opmerking: Het gebruik van binaire integers is geen universele standaard in C/C++.

Nog een stap verder

Poorten A en C zijn geconfigureerd in de set-up, dus er zijn maar een paar regels extra code nodig om met DPM in de hoofdlus digitale apparaten af te lezen en andere digitale apparaten aan te sturen. In de praktijk is het eenvoudig om twee stappenmotoren met eindschakelaars voor de verplaatsing, bestuurd met joystick, te programmeren met een beperkt aantal registeropdrachten en een look-up table. Het hardwareschema voor dit scenario is te zien in afbeelding 1.

Afbeelding 1: Hardwareschema van joystick/stappenmotoren met microcontroller-poorten A en C vergroot. (Bron afbeelding: Digi-Key Electronics)

Hardware:

Een digitale joystick met vier normaal open (NO) contacten, aangesloten op VCC en pinnen 0 tot 3 van poort A. De outputs worden bij de microcontroller op LOW getrokken. Als de contacten sluiten, gaan de pinnen van de poort naar HIGH. De contacten vertegenwoordigen de besturing OMHOOG, OMLAAG, LINKS en RECHTS. Ze zijn zo geconfigureerd dat twee naast elkaar gelegen contacten tegelijkertijd kunnen activeren, zodat er acht output-combinaties van de schakelaar mogelijk zijn. Een negende output vertegenwoordigt de opdracht ALL STOP, die optreedt als de joystick in het midden staat en alle contacten dus open zijn.

De vier eindschakelaars met hun NO-contacten aangesloten op de aarde zijn ook verbonden met de overige pinnen van poort A, die overeenkomen met de joystick-configuratie OMHOOG, OMLAAG, LINKS, RECHTS. De outputs van de schakelaar worden bij de microcontroller naar HIGH getrokken. Als de contacten sluiten, gaan de pinnen naar LOW.

Driverborden voor de stappenmotoren creëren een mechanische beweging door de drie besturingspinnen omhoog en omlaag te zetten, om de functies stap, richting en pauze aan te sturen. In dit voorbeeld zijn alle acht bits van poort C bestemd voor de besturing van de twee drivers, hoewel twee pinnen niet worden gebruikt.

Programmering:

Om de juiste output richting de drivers te genereren, wordt een look-up table gebruikt om de vier joystick-bits te vertalen naar de acht driverbits. Eén regel code in de hoofdlus roept de functie 'get_output()' aan en geeft de inhoud van het PINA-register door aan de functie. De waarde die wordt teruggegeven door de functie wordt direct naar het PORTC-register geschreven;

PORTC = get_output(PINA);

Binnen de functie wordt de look-up table 'lookup_output[ ]' gebruikt. De return is een geïndexeerde waarde. Maar er gebeurt nog iets anders binnen deze functie, waar ook de eindschakelaars bij betrokken zijn. De indexwaarde van de look-up table, die tussen de vierkante haken staat in 'lookup_output[ ]', wordt vertegenwoordig door een expressie voor bitverschuiving en -maskering. De indexvariabele die hieruit komt, is de bitsgewijze AND van de bovenste 4 bits van het ingangsregister (de eindschakelaarwaarden) en de onderste vier bits (de joystickwaarden). Als één van de contacten van de eindschakelaars wordt gesloten en er in één van de vier bovenste bits een nulwaarde optreedt, wordt de bijbehorende onderste bit gewist.

Opmerking: Met vier bits zijn 16 unieke combinaties mogelijk, dus de zeven posities van de look-up table die niet worden gebruikt, worden vertaald naar 0x00 om fouten te voorkomen.

Copyuint8_t get_output(uint8_t porta_val) { return lookup_output[(porta_val >> 4) & (porta_val & 0x0F)]; } 

Voorbeeld:

Als de joystick naar boven en rechts staat met alle eindschakelaars open, is de binaire PINA-registerwaarde die wordt doorgegeven aan de functie 0b11111001 (of 0xF9 hex). De functie zet de bovenste vier bits op nul met een bitsgewijze AND van 0b00001111 (0x0 F), wat een look-up table indexwaarde oplevert van 0b00001001 (0x09). Als deze waarde via nog een bitsgewijze AND wordt vergeleken met de verschoven statuswaarde van de schakelaar 0b00001111 (0x0F), blijft de eerdere waarde ongewijzigd, zodat 0b00001001 (0x09) wordt teruggegeven als de definitieve indexwaarde, die 0b00100001 (0x21) aanwijst in de look-up table. Zo wordt BOVEN en RECHTS dus vertaald voor de driver van de stappenmotor. Zie afbeelding 2.

Afbeelding 2: Interpretatie door de microcontroller van de inputs van poorten A en C, als de joystick in de positie BOVEN en RECHTS staat. (Bron afbeelding: Digi-Key Electronics)

Als de eindschakelaar voor BOVEN zou zijn bereikt, met een nulbit als resultaat, zou de verschoven waarde 0b00000111 (0x07) zijn in plaats van 0b00001111 (0x0F). Dat zou de overeenkomstige joystickwaarde voor BOVEN teniet doen, zodat de uiteindelijke indexwaarde 0b00000001 (0x01) zou worden in plaats van 0b00001001 (0x09). De vertaalde waarde voor 0b00000001 (0x01) in de look-up table is 0x26. Voor de driver van de stappenmotor is dat de vertaling van RECHTS (zonder BOVEN). Zie afbeelding 3.

Afbeelding 3: Interpretatie door de microcontroller van de inputs van poorten A en C, als de joystick in de positie BOVEN en RECHTS staat, terwijl de eindschakelaar voor BOVEN is geactiveerd. (Bron afbeelding: Digi-Key Electronics)

Conclusie

Of een programmeur nu wel of niet met DPM werkt als een goede tool voor het configureren, lezen of schrijven van poortgegevens, het is een goed idee om de code significant te reduceren. Voor dezelfde operaties is met standaard bibliotheekfuncties veel meer code nodig, en ze kunnen een fors beslag leggen op het beschikbare programmageheugen. De code hieronder vereist minder dan 1% van het geheugen van de microcontroller ATMEGA328P die wordt gebruikt voor bench-tests. Om de DPM-functionaliteit in geschreven code te kunnen begrijpen, is het belangrijk dat de code correct is becommentarieerd. Dat maakt het ook eenvoudiger voor programmeurs van ieder niveau om te debuggen.

Hardware-voorbeelden van Digi-Key:

Stappenmotoren - https://www.digikey.com/short/pdnfp4

Stappencontrollers - https://www.digikey.com/short/pdnf4r

Joystick - https://www.digikey.com/short/pdnf57

Eindschakelaars - https://www.digikey.com/short/pdnfwm

Voorbeeldcode:

Copyconst uint8_t lookup_output[16] = { 0x09, //Index 0 All Stop. Apply hold current 0x26, // Index 1 Right 0x34, // Index 2 Left 0x00, // Index 3 Unused 0x36, // Index 4 Down 0x0C, // Index 5 Down/Right 0x31, // Index 6 Down/Left 0x00, // Index 7 Unused 0x24, // Index 8 Up 0x21, // Index 9 Up/Right 0x0E, // Index 10 Up/Left 0x00, // Index 11 Unused 0x00, // Index 12 Unused 0x00, // Index 13 Unused 0x00, // Index 14 Unused 0x00 // Index 15 Unused }; void setup() { // Set all bits in port A direction register as INPUTs; // Limits (up, down, left, right) Joystick (up, down, left, right) DDRA = 0x00; // Set all bits of port C direction register as OUTPUTs; // Motor control (Not Used, Mot_1, Dir_1, En_1, Not Used, Mot_2, Dir_2, En_2 DDRC = 0xFF; } void loop() { //Send the port A values to the function. Write the return value to port C. PORTC = get_output(PINA); } /***** Input Value Translation Function *******/ uint8_t get_output(uint8_t porta_val) { // Compare the limit switch and joystick values. Retrieve and return the translated value.
    return lookup_output[(porta_val >> 4) & (porta_val & 0x0F)]; } 

Achtergrondinformatie over deze auteur

Image of Don Johanneck

Don Johanneck is Technische Contentontwikkelaar bij Digi-Key Electronics. Sinds 2014 is hij actief binnen het bedrijf. Nu hij onlangs zijn huidige functie opnam, is hij verantwoordelijk voor het schrijven van videobeschrijvingen en productinhoud. Don behaalde zijn diploma als geaggregeerde in toegepaste wetenschappen aan het Electronics Technology & Automated Systems from Northland Community & Technical College via het studiebeursprogramma van Digi-Key. Hij houdt van modelbouw met afstandsbediening, restauratie van vintage machines en knutselen.

More posts by Don Johanneck