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
-
Red Hat Migrates RHEL from Xorg to Wayland
If you've been wondering when Xorg will finally be a thing of the past, wonder no more, as Red Hat has made it clear.
-
PipeWire 1.0 Officially Released
PipeWire was created to take the place of the oft-troubled PulseAudio and has finally reached the 1.0 status as a major update with plenty of improvements and the usual bug fixes.
-
Rocky Linux 9.3 Available for Download
The latest version of the RHEL alternative is now available and brings back cloud and container images for ppc64le along with plenty of new features and fixes.
-
Ubuntu Budgie Shifts How to Tackle Wayland
Ubuntu Budgie has yet to make the switch to Wayland but with a change in approaches, they're finally on track to making it happen.
-
TUXEDO's New Ultraportable Linux Workstation Released
The TUXEDO Pulse 14 blends portability with power, thanks to the AMD Ryzen 7 7840HS CPU.
-
AlmaLinux Will No Longer Be "Just Another RHEL Clone"
With the release of AlmaLinux 9.3, the distribution will be built entirely from upstream sources.
-
elementary OS 8 Has a Big Surprise in Store
When elementary OS 8 finally arrives, it will not only be based on Ubuntu 24.04 but it will also default to Wayland for better performance and security.
-
OpenELA Releases Enterprise Linux Source Code
With Red Hat restricting the source for RHEL, it was only a matter of time before those who depended on that source struck out on their own.
-
StripedFly Malware Hiding in Plain Sight as a Cryptocurrency Miner
A rather deceptive piece of malware has infected 1 million Windows and Linux hosts since 2017.
-
Experimental Wayland Support Planned for Linux Mint 21.3
As with most Linux distributions, the migration to Wayland is in full force. While some distributions have already made the move, Linux Mint has been a bit slower to do so.