Use a general purpose input/output interface on Linux computers and laptops.
GPIO Class
The GPIO
class (GPIO.cpp
and GPIO.hpp
) does the work of the program. Its member functions count up in a variety of number systems, count down in a variety of number systems, and display 12- and 24-hour clocks. The main()
function, which is not a member of the GPIO
class, instantiates FT232H
and GPIO
objects and starts a separate thread of execution to run the readWrite()
method.
As stated before, GPIO
counts by using its member methods: countUp(base)
, countDown(base)
, and runClock(hours)
. The GPIO
non-member thread method readWrite()
employs the FT232H
object to write the counts and time to the FT232H GPIO ports. GPIO
also contains some member helper methods for readWrite()
: bool GPIO::interrupted()
, void GPIO::resetInterrupt()
, and byte GPIO::getPattern(int digit)
.
Non-member functions implement time delays. The FT232H
and GPIO
classes have static helper functions, FT232H::getFT232H()
and GPIO::getGPIO()
that return pointers to each class. Wherever you are in the program, you can call these functions and access each classes' members. For example, GPIO.cpp
has the code shown in Listing 1. Note that the static functions FT232H::getFT232H()
and GPIO::GetGPIO()
are called by a fully declared name and not by a preexisting object. You use the pointers returned by these static functions to access the member methods of the class. The file gpio.asm
is the assembly language equivalent of GPIO.hpp
and GPIO.cpp
.
Listing 1
Write to GPIO
01 void* readWrite(void* pArg) // thread function - writes digits to GPIO 02 { 03 GPIO* pGPIO = GPIO::getGPIO(); 04 FT232H* pFT = FT232H::getFT232H(); 05 int nCnt = DELAY_COUNT; 06 07 while (!pGPIO->getExitFlag()) 08 { 09 if (--nCnt == 0) // is it time to check for an interrupt? 10 { 11 if (pGPIO->interrupted()) // yes - check for interrupt 12 { 13 pGPIO->setInterruptFlag(true); // respond to interrupt 14 pGPIO->resetInterrupt(); 15 } 16 nCnt = DELAY_COUNT; // repopulate nCnt 17 } 18 19 pFT->writeACByte(0); // clear digits - turn off display 20 pFT->writeADByte(pGPIO->getPattern(3)); // write digit3 segments 21 pFT->writeACByte(DRIVE_DIGIT_3); // turn on digit3 22 sleep2_5ms(); 23 ... 24 } 25 ... 26 }
Now look at a C++ member function (Listing 2) next to its assembly language equivalent (Listing 3). The C++ version has a member function of the GPIO
class that takes no arguments and returns a boolean value. It declares a local (or automatic) variable, byte byteToRead
, that lives on the stack.
Listing 2
GPIO.cpp Snippet
bool GPIO::interrupted() // check for interrupt (GPIO.cpp line 55) { byte byteToRead FT232H::getFT232H()->readACByte(&byteToRead); // readACByte into byteToRead return (byteToRead & INTERRUPT_MASK) != 0; }
Listing 3
gpio.asm Snippet
interrupted: ; check for interrupt (gpio.asm line 343) prologue sub rsp, VAR_SIZE * NUM_VAR ; make space for local var on stack lea rdi, byte nParam ; pass addr of nParam to readACByte call readACByte ; read a byte from AC into nParam mov al, byte nParam ; get the byte we just read test al, INTERRUPT_MASK ; is the INTERRUPT_MASK bit set? setnz al ; al = 1 if yes, al = 0 if no .fin: epilogue
The static member of the FT232H
class, FT232H::getFT232H(),
gets a pointer that you can use to call FT232H::readACByte()
and then pass it the address of byteToRead
. The question is: When you AND byteToRead
with INTERRUPT_MASK
(0x80), is the result not equal to zero? Remember that AC7 is an input pin that gets the Q output pin of the S-R latch. The answer to this question is returned to the calling method.
gpio.asm
Now, it's a little hard to believe, but the assembly language function does exactly the same thing, starting with the macro definition, prologue
. All Intel assembly language functions that call other subroutines, need
%macro prologue 0 push rbp mov rbp, rsp %endmacro
to prepare the stack. (The epilogue
macro at the end of the function,
%macro epilogue 0 leave ret %endmacro
restores the stack to the way it was before prologue
was invoked.) After invoking prologue
, the line
sub rsp, VAR_SIZE * NUM_VAR
subtracts the product (8x2=16 because NUM_VAR
is rounded up to an even number) from the stack pointer to make room on the stack for the local 8-bit variable nParam
. Next, the code
lea rdi, byte nParam readACByte call readACByte nParam mov al, byte nParam test al, INTERRUPT_MASK ; is the INTERRUPT_MASK bit set? setnz al ; al = 1 if yes, al = 0 if no epilogue
loads the effective address lea
of byte nParam
into the rdi
register. In the C calling convention, the first argument to a function is always passed in the rdi
register.
The lines that follow call readACByte
, get the byte just read into the 1-byte al
register, and ANDs al
with INTERRUPT_MASK
to clear the processor's zero flag if bit 7 of the byte read is a <1>1<1>. The setnz al
instruction sets the al
register to 1
if the zero flag is cleared, or it sets al
to
if the zero flag is set. By the C calling convention, you always return the result in the rax
register (of which al
is the least significant byte). After invoking the ret
(in epilogue
), the al
register will contain 1
if there was an interrupt, or
otherwise.
The calling function (in this case, readWrite
) can then invoke test al, al
to find out whether the interrupted function found an interrupt. The test
instruction does an AND operation, which affects the flags but doesn't save the result. The relevant piece of the calling function is:
readWrite: ... call interrupted ; check for interrupt test al, al ; interrupted? jz .continue0 ; jump if no call resetInterrupt ; handle interrupt .continue0: ...
Why are label names preceded by a period? It's a YASM and NASM assembler trick that lets you reuse label names from method to method. The label .continue0
is really known to the assembler as readWrite.continue0
. Assembly language contains lots and lots of jumps (equivalent to the dreaded goto
statement). To avoid calling the resetInterrupt
method, gpio.asm
performs jz .continue0
to jump around the call to resetInterrupt
if al == 0
. Every conditional (jz .next
, i.e., jump if zero flag is 1) or unconditional (jmp .next
) jump needs a target label. Reuse of label names is a real time and energy saver.
Infos
- FT232H : https://www.adafruit.com/product/2264
- "Access Raspberry Pi GPIO with ARM64 assembly" by John Schwartzman, Linux Magazine, issue 247, June 2021, pg. 56, https://www.linuxpromagazine.com/Issues/2021/247/ARM64-Assembly-and-GPIO
- Code for this article: ftp://ftp.linux-magazine.com/pub/listings/linux-magazine.com/258/
« Previous 1 2
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Fedora 41 Beta Available with Some Interesting Additions
If you're a Fedora fan, you'll be excited to hear the beta version of the latest release is now available for testing and includes plenty of updates.
-
AlmaLinux Unveils New Hardware Certification Process
The AlmaLinux Hardware Certification Program run by the Certification Special Interest Group (SIG) aims to ensure seamless compatibility between AlmaLinux and a wide range of hardware configurations.
-
Wind River Introduces eLxr Pro Linux Solution
eLxr Pro offers an end-to-end Linux solution backed by expert commercial support.
-
Juno Tab 3 Launches with Ubuntu 24.04
Anyone looking for a full-blown Linux tablet need look no further. Juno has released the Tab 3.
-
New KDE Slimbook Plasma Available for Preorder
Powered by an AMD Ryzen CPU, the latest KDE Slimbook laptop is powerful enough for local AI tasks.
-
Rhino Linux Announces Latest "Quick Update"
If you prefer your Linux distribution to be of the rolling type, Rhino Linux delivers a beautiful and reliable experience.
-
Plasma Desktop Will Soon Ask for Donations
The next iteration of Plasma has reached the soft feature freeze for the 6.2 version and includes a feature that could be divisive.
-
Linux Market Share Hits New High
For the first time, the Linux market share has reached a new high for desktops, and the trend looks like it will continue.
-
LibreOffice 24.8 Delivers New Features
LibreOffice is often considered the de facto standard office suite for the Linux operating system.
-
Deepin 23 Offers Wayland Support and New AI Tool
Deepin has been considered one of the most beautiful desktop operating systems for a long time and the arrival of version 23 has bolstered that reputation.