C/C++ GPIO Development
lgpio Introduction
lgpio is a GPIO control library for Linux systems, providing a simple and easy-to-use C language interface to operate GPIO, I2C, SPI and other peripherals.
Main Features
- High Performance: C language implementation with high execution efficiency
- Cross-platform Compatibility: Supports various Linux single board computers
- Rich Features: Supports GPIO, I2C, SPI, PWM and other peripherals
- Easy Integration: Provides standard C library interface
Supported Features
| Feature | Description |
|---|---|
| GPIO | General purpose input/output control |
| I2C | I²C bus communication |
| SPI | SPI bus communication |
| PWM | Pulse width modulation output |
| Notify | GPIO interrupts and event notifications |
Installing lgpio
Install from Source
# Install dependency tools
apt update
apt install -y git build-essential
# Clone lgpio source code
git clone https://github.com/joan2937/lg.git
cd lg
# Compile and install
make
make install
# Update dynamic library cache
ldconfig
Verify Installation
After installation, you can verify with the following commands:
# Check dynamic library
ldconfig -p | grep lgpio
You should see output similar to liblgpio.so.
Other Verification Methods:
# Check if library files exist
ls -l /usr/local/lib/liblgpio.*
# Check if header files exist
ls -l /usr/local/include/lgpio.h
GPIO Control
Hardware Preparation
- Main board
- Jumper wires
- Multimeter (optional, for measuring voltage)
GPIO Output Control
The following example demonstrates how to use lgpio to control GPIO pin output high and low levels.
Example Code
Create gpio_output.c file:
#include <stdio.h>
#include <stdlib.h>
#include <lgpio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc < 3) {
printf("Usage: %s <gpio_num> <pin_level>\n", argv[0]);
printf("Example: %s 36 1 # Set GPIO36 to HIGH\n", argv[0]);
return 1;
}
int gpio_num = atoi(argv[1]);
int pin_level = atoi(argv[2]);
int h;
// Open GPIO chip 4
h = lgGpiochipOpen(4);
if (h < 0) {
printf("ERROR: %s (%d)\n", lguErrorText(h), h);
return 1;
}
// Set GPIO to output mode and set initial level
int e = lgGpioClaimOutput(h, 0, gpio_num, pin_level);
if (e < 0) {
printf("ERROR: %s (%d)\n", lguErrorText(e), e);
lgGpiochipClose(h);
return 1;
}
printf("GPIO %d set to %s\n", gpio_num, pin_level ? "HIGH (3.3V)" : "LOW (0V)");
// Maintain state for 5 seconds
sleep(5);
// Release GPIO resources
lgGpioFree(h, gpio_num);
lgGpiochipClose(h);
return 0;
}
Compile Program
gcc -o gpio_output gpio_output.c -llgpio
Run Test
Test Low Level:
sudo ./gpio_output 36 0
Output: GPIO 36 set to LOW (0V)
Test High Level:
sudo ./gpio_output 36 1
Output: GPIO 36 set to HIGH (3.3V)
Tip: GPIO36 corresponds to Pin3 of 40Pin.
GPIO Input Reading
The following example demonstrates how to read GPIO pin status.
Example Code
Create gpio_input.c file:
#include <stdio.h>
#include <stdlib.h>
#include <lgpio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Usage: %s <gpio_num>\n", argv[0]);
printf("Example: %s 36 # Read GPIO36 state\n", argv[0]);
return 1;
}
int gpio_num = atoi(argv[1]);
int h, e, value;
// Open GPIO chip 4
h = lgGpiochipOpen(4);
if (h < 0) {
printf("ERROR: %s (%d)\n", lguErrorText(h), h);
return 1;
}
// Set GPIO to input mode
e = lgGpioClaimInput(h, 0, gpio_num);
if (e < 0) {
printf("ERROR: %s (%d)\n", lguErrorText(e), e);
lgGpiochipClose(h);
return 1;
}
printf("Reading GPIO %d state (press Ctrl+C to exit)...\n", gpio_num);
// Loop to read GPIO status
while (1) {
value = lgGpioRead(h, gpio_num);
if (value < 0) {
printf("ERROR: %s (%d)\n", lguErrorText(value), value);
break;
}
printf("GPIO %d: %s\n", gpio_num, value ? "HIGH" : "LOW");
sleep(1);
}
// Release resources
lgGpioFree(h, gpio_num);
lgGpiochipClose(h);
return 0;
}
Compile and Run
# Compile
gcc -o gpio_input gpio_input.c -llgpio
# Run
sudo ./gpio_input 36
GPIO Output Feedback Test
The following example demonstrates how to verify functionality by outputting from one pin and reading from another pin.
Example Code
Create gpio_feedback.c file:
#include <stdio.h>
#include <lgpio.h>
#include <unistd.h>
int main()
{
int h;
int gpio_out = 36; // Pin3 - GPIO36
int gpio_in = 37; // Pin5 - GPIO37
// Open GPIO chip 4
h = lgGpiochipOpen(4);
if (h < 0) {
printf("ERROR: %s (%d)\n", lguErrorText(h), h);
return 1;
}
// Set GPIO36 as output, GPIO37 as input
int e1 = lgGpioClaimOutput(h, 0, gpio_out, 0);
int e2 = lgGpioClaimInput(h, 0, gpio_in);
if (e1 < 0 || e2 < 0) {
printf("ERROR: Failed to claim GPIO\n");
lgGpiochipClose(h);
return 1;
}
printf("=== GPIO Feedback Test ===\n");
printf("Output: GPIO%d (Pin3)\n", gpio_out);
printf("Input: GPIO%d (Pin5)\n", gpio_in);
printf("Please connect Pin3 and Pin5 together\n\n");
for (int i = 0; i < 5; i++) {
// Set high level
lgGpioWrite(h, gpio_out, 1);
usleep(100000); // 100ms
int val_high = lgGpioRead(h, gpio_in);
printf("Output: HIGH | Input: %s\n", val_high ? "HIGH" : "LOW");
sleep(1);
// Set low level
lgGpioWrite(h, gpio_out, 0);
usleep(100000);
int val_low = lgGpioRead(h, gpio_in);
printf("Output: LOW | Input: %s\n", val_low ? "HIGH" : "LOW");
sleep(1);
}
// Release resources
lgGpioFree(h, gpio_out);
lgGpioFree(h, gpio_in);
lgGpiochipClose(h);
printf("\nTest completed\n");
return 0;
}
Compile and Run
# Compile
gcc -o gpio_feedback gpio_feedback.c -llgpio
# Run (need to short Pin3 and Pin5 first)
sudo ./gpio_feedback
Expected Output:
=== GPIO Feedback Test ===
Output: GPIO36 (Pin3)
Input: GPIO37 (Pin5)
Please connect Pin3 and Pin5 together
Output: HIGH | Input: HIGH
Output: LOW | Input: LOW
Output: HIGH | Input: HIGH
Output: LOW | Input: LOW
...
GPIO Pin Mapping
Quectel Pi H1 primarily uses /dev/gpiochip4 for GPIO control.
Common Pin Mapping
| Physical Pin | Pin Name | GPIO Number | GPIO Chip | Default Function |
|---|---|---|---|---|
| Pin3 | NFC_I2C_SDA | 36 | /dev/gpiochip4 | I2C09_SDA |
| Pin5 | NFC_I2C_SCL | 37 | /dev/gpiochip4 | I2C09_SCL |
| Pin7 | CAM2_RST | 77 | /dev/gpiochip4 | GPIO |
| Pin11 | GPIO_16 | 16 | /dev/gpiochip4 | GPIO/SPI/UART/I2C |
| Pin13 | GPIO_17 | 17 | /dev/gpiochip4 | GPIO/SPI/UART/I2C |
Note: Pin3 and Pin5 are configured as I2C9 interface by default. If you need to use them as GPIO, please ensure no other devices are occupying them.
For complete pin mapping, please refer to: 40Pin Function Testing
lgpio API Reference
GPIO Chip Operations
// Open GPIO chip
int lgGpiochipOpen(int chip_num);
// Close GPIO chip
int lgGpiochipClose(int handle);
GPIO Configuration
// Claim GPIO as output mode
int lgGpioClaimOutput(int handle, int flags, int gpio, int level);
// Claim GPIO as input mode
int lgGpioClaimInput(int handle, int flags, int gpio);
// Release GPIO
int lgGpioFree(int handle, int gpio);
GPIO Read/Write
// Read GPIO level
int lgGpioRead(int handle, int gpio);
// Write GPIO level
int lgGpioWrite(int handle, int gpio, int level);
PWM Control
// Output PWM signal
int lgTxPwm(int handle, int gpio, float freq, float duty, int offset, int cycles);
Common Issues
Compilation Errors
Issue: Cannot find lgpio.h
fatal error: lgpio.h: No such file or directory
Solution:
- Confirm lgpio library is installed
- Check header file path:
ls /usr/local/include/lgpio.h - If in other path, specify during compilation:
gcc -I/path/to/include ...
Runtime Errors
Issue: Permission denied
Solution:
Run program with sudo:
sudo ./gpio_output 36 1
Issue: Cannot find shared library
error while loading shared libraries: liblgpio.so
Solution:
# Update dynamic library cache
sudo ldconfig
# Or set LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
Pin Occupied Issue
If you encounter GPIO already occupied error, use the following command to check:
# View GPIO status
gpioinfo | grep -A 180 "gpiochip4" | grep -E "(line 36|line 37|consumer)"
If the pin is occupied, you need to release it first or select another unoccupied pin.