C语言控制树莓派硬件

平台: raspbian-bookworm-arm64。

1. 安装工具

以下方式任选一种(我使用的是第一种,在树莓派上本地编译):

1.1 本地编译(树莓派):

$ sudo apt install build-essential bison flex gawk texinfo file git ssh libc6-dev libssl-dev libncurses-dev
$ sudo apt install raspberrypi-kernel-headers

1.2 交叉编译(如Ubuntu):

$ sudo apt install build-essential bison flex gawk texinfo file git ssh libc6-dev libssl-dev libncurses-dev
$ sudo apt install crossbuild-essential-arm64

交叉编译需要下载树莓派linux kernel源代码:

$ git clone --depth=1 https://github.com/raspberrypi/linux.git

编译驱动程序的时候有可能会用到源码中的文件。

2. Blink

C语言,代码折叠,点击打开:

/**
 * LED blink example.
 *
 * License - MIT.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gpiod.h>


#define GPIO_CHIP       "/dev/gpiochip0"
#define GPIO_LED        3


int main()
{
    int ret = 0;
    struct gpiod_chip *gpiochip;
    struct gpiod_line *gpioline;

    // Open driver.
    gpiochip = gpiod_chip_open(GPIO_CHIP);

    if (NULL == gpiochip) {
        printf("Error in gpiod_chip_open.\n");
        ret = -1;
        goto out1;
    }

    // Get gpio.
    gpioline = gpiod_chip_get_line(gpiochip, GPIO_LED);

    if (NULL == gpioline) {
        printf("Error in gpiod_chip_get_line.\n");
        ret = -1;
        goto out2;
    }

    // Set gpio direction.
    ret = gpiod_line_request_output(gpioline, "gpio", 0);

    if (ret != 0) {
        printf("Error in gpiod_line_request_output.\n");
        ret = -1;
        goto out2;
    }

    // Blink.
    for (int i = 0; i < 10; i++) {
        printf("%d times.\n", i);

        gpiod_line_set_value(gpioline, 1);
        sleep(1);

        gpiod_line_set_value(gpioline, 0);
        sleep(1);
    }

    // Release.
    gpiod_line_release(gpioline);

out2:
    gpiod_chip_close(gpiochip);

out1:
    return ret;
}
mian.c

Makefile:

CC = gcc
CFLAGS = -Wall -g -O0
LDLIBS = -lgpiod

OBJ = blink

SRC = main.c

$(OBJ): $(SRC)
    $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

clean:
    $(RM) $(OBJ) *.o
Makefile

3. PWM

代码: (待补充).

Makefile: (待补充).

4. Button

按键代码:

/**
 * Button example.
 *
 * License - MIT.
 */

#include <stdio.h>
#include <stdlib.h>
#include <gpiod.h>
#include <unistd.h>


#define GPIO_CHIP               "gpiochip0" // Default gpio chip.
#define BUTTON_PIN              17          // GPIO 17.


int main()
{
    int ret;
    struct gpiod_chip *chip;
    struct gpiod_line *button_line;

    // 1. Open gpio chip.
    chip = gpiod_chip_open_by_name(GPIO_CHIP);

    if (!chip) {
        perror("Error in open the gpio chip.");

        return EXIT_FAILURE;
    }

    // 2. Get the GPIO line (button input).
    button_line = gpiod_chip_get_line(chip, BUTTON_PIN);

    if (!button_line) {
        perror("Error in get goio line.");
        gpiod_chip_close(chip);

        return EXIT_FAILURE;
    }

    // 3.Config gpio direction to input, enable pull up mode (Prevent key shaking).
    ret = gpiod_line_request_input_flags(button_line, "button-demo",
                                         GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);

    if (ret < 0) {
        perror("Error in input direction.");
        gpiod_line_release(button_line);
        gpiod_chip_close(chip);

        return EXIT_FAILURE;
    }

    // 4. Loop for get gpio value.
    while (1)
    {
        int val = gpiod_line_get_value(button_line);
        if (val < 0) {
            perror("Error in read gpio value.");
            break;
        }

        // Low value is key pressed.
        if (val == 0) {
            printf("Key pressed !\n");
            // Optional: delay for prevent key shaking.
            usleep(50000);

            // Loop for wait key release.
            while (!gpiod_line_get_value(button_line))
                usleep(10000);
        }

        // 10 ms apart.
        usleep(10000);
    }

    // 5. Release resource.
    gpiod_line_release(button_line);
    gpiod_chip_close(chip);

    return EXIT_SUCCESS;
}
main.c

Makefile:

CC = gcc
CFLAGS = -Wall -g -O0
LDLIBS = -lgpiod

OBJ = button

SRC = main.c

$(OBJ): $(SRC)
    $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

clean:
    $(RM) $(OBJ) *.o
Makefile

5. Interrupt

按键中断代码:

/**
 * Button interrupt example.
 *
 * License - MIT.
*/

#include <stdio.h>
#include <stdlib.h>
#include <gpiod.h>
#include <unistd.h>
#include <signal.h>


#define GPIO_CHIP               "gpiochip0" // Default gpio chip.
#define BUTTON_PIN              17          // GPIO 17.


volatile sig_atomic_t stop = 0;

/**
 * Signal function.
*/
void signal_handler(int sig)
{
    stop = 1;
}

int main()
{
    struct gpiod_chip *chip;
    struct gpiod_line *button_line;
    int ret;

    // Register signal function (Ctrl + C to quit).
    signal(SIGINT, signal_handler);

    // 1. Open the gpio chip.
    chip = gpiod_chip_open_by_name(GPIO_CHIP);

    if (!chip) {
        perror("Error in open gpio chip.");

        return EXIT_FAILURE;
    }

    // 2. Get gpio line.
    button_line = gpiod_chip_get_line(chip, BUTTON_PIN);

    if (!button_line) {
        perror("Error in get gpio line.");
        gpiod_chip_close(chip);

        return EXIT_FAILURE;
    }

    // 3. Configure gpio to input direction, enable pull up mode, Falling trigger.
    ret = gpiod_line_request_falling_edge_events(button_line, "button-interrupt");
    if (ret < 0) {
        perror("Error in config interrupt.");
        gpiod_line_release(button_line);
        gpiod_chip_close(chip);

        return EXIT_FAILURE;
    }

    // 4. Loop for interrupt events.
    while (!stop)
    {
        // Timeout waiting for 1 sec (Non-blocking).
        struct timespec ts = {1, 0};
        ret = gpiod_line_event_wait(button_line, &ts);

        if (ret < 0) {
            perror("Error in wait events.");
            break;
        }
        else if (ret == 1) {
            // Read event.
            struct gpiod_line_event event;
            ret = gpiod_line_event_read(button_line, &event);
            if (ret < 0) {
                perror("Error in read events.");
                continue;
            }

            // Key anti-shake: wait after event is detected 50 ms reconfirmation status.
            usleep(50000);
            int val = gpiod_line_get_value(button_line);

            // Confirm button still pressed.
            if (val == 0) {
                printf("Key pressed (time: %ld.%09ld Second(s))\n",
                       event.ts.tv_sec, event.ts.tv_nsec);
            }
        }
    }

    // 5. Release resource.
    gpiod_line_release(button_line);
    gpiod_chip_close(chip);

    return EXIT_SUCCESS;
}
main.c

Makefile:

CC = gcc
CFLAGS = -Wall -g -O0
LDLIBS = -lgpiod

OBJ = interrupt

SRC = main.c

$(OBJ): $(SRC)
    $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)

clean:
    $(RM) $(OBJ) *.o
Makefile

6. I2C

示例 AT24 EEPROM 代码:

/**
 * AT24C02 eeprom i2c example.
 *
 * License - MIT.
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <unistd.h>


#define I2C_DEV                 "/dev/i2c-1"
#define EEPROM_ADDR             0x50


/**
 * eeprom_write_byte - Write a byte to EEPROM. 
*/
int eeprom_write_byte(int fd, unsigned char addr, unsigned char data)
{
    unsigned char buf[2] = {addr, data};
    if (write(fd, buf, 2) != 2) {
        perror("Write failed");
        return -1;
    }

    // Need delay for writing.
    usleep(5000);

    return 0;
}

/**
 * eeprom_read_byte - Read a byte from EEPROM.
*/
int eeprom_read_byte(int fd, unsigned char addr, unsigned char *data)
{
    if (write(fd, &addr, 1) != 1) {
        perror("Set read address failed");
        return -1;
    }

    if (read(fd, data, 1) != 1) {
        perror("Read failed");
        return -1;
    }

    return 0;
}

int main()
{
    int fd = open(I2C_DEV, O_RDWR);
    if (fd < 0) {
        perror("Open I2C device failed");
        return 1;
    }

    if (ioctl(fd, I2C_SLAVE, EEPROM_ADDR) < 0) {
        perror("Set I2C address failed");
        close(fd);
        return 1;
    }

    // Write data.
    if (eeprom_write_byte(fd, 0x00, 0x66) == 0) {
        printf("Write success\n");
    }

    // Read data.
    unsigned char value;
    if (eeprom_read_byte(fd, 0x00, &value) == 0) {
        printf("Read value: 0x%02X\n", value);
    }

    close(fd);

    return 0;
}
mian.c

Makefile:

CC = gcc
CFLAGS = -Wall -g -O0

OBJ = at24c02

SRC = main.c

$(OBJ): $(SRC)
    $(CC) $(CFLAGS) -o $@ $^

clean:
    $(RM) $(OBJ) *.o
Makefile

7. SPI

示例 W25Q64 Flash 代码:

/**
 * W25Q64 spi flash example.
 *
 * License - MIT.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>


#define SPI_DEVICE              "/dev/spidev0.0"
#define SPI_SPEED               500000
#define PAGE_SIZE               256
#define SECTOR_SIZE             4096


/**
 * spi_transfer - Send command.
*/
int spi_transfer(int fd, uint8_t *tx, uint8_t *rx, size_t len)
{
    struct spi_ioc_transfer tr = {
        .tx_buf   = (unsigned long)tx,
        .rx_buf   = (unsigned long)rx,
        .len      = len,
        .speed_hz = SPI_SPEED,
        .bits_per_word = 8,
    };

    return ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
}

/**
 * w25q64_write_enable - Write enable.
*/
void w25q64_write_enable(int fd)
{
    uint8_t cmd = 0x06;
    spi_transfer(fd, &cmd, NULL, 1);
}

/**
 * w25q64_erase_sector - erase flash (4KiB).
*/
void w25q64_erase_sector(int fd, uint32_t addr)
{
    uint8_t cmd[4] = {0x20, addr >> 16, addr >> 8, addr};

    w25q64_write_enable(fd);
    spi_transfer(fd, cmd, NULL, 4);

    // Wait for erase complete (also can read status register).
    sleep(1);
}

/**
 * w25q64_page_program - Write a page (max 256 byte).
*/
void w25q64_page_program(int fd, uint32_t addr, uint8_t *data, size_t len)
{
    uint8_t cmd[4 + PAGE_SIZE] = {0x02, addr >> 16, addr >> 8, addr};

    w25q64_write_enable(fd);
    memcpy(&cmd[4], data, len);
    spi_transfer(fd, cmd, NULL, 4 + len);

    // Wait for write complete.
    usleep(5000);
}

/**
 * w25q64_read_data - Read data.
*/
void w25q64_read_data(int fd, uint32_t addr, uint8_t *buf, size_t len)
{
    uint8_t cmd[4] = {0x03, addr >> 16, addr >> 8, addr};
    uint8_t dummy[len + 4];

    memset(dummy, 0, sizeof(dummy));
    memcpy(dummy, cmd, 4);

    spi_transfer(fd, dummy, buf, len + 4);

    // Remove first 4 byte command response.
    memmove(buf, buf + 4, len);
}

int main()
{
    uint8_t  mode  = SPI_MODE_0;
    uint32_t speed = SPI_SPEED;
    uint32_t addr  = 0x000000;
    uint8_t write_data[PAGE_SIZE] = "Hello, W25Q64!";
    uint8_t read_data[PAGE_SIZE]  = {0};

    int fd = open(SPI_DEVICE, O_RDWR);
    if (fd < 0) {
        perror("Open SPI device failed");
        return 1;
    }

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

    printf("Erasing sector...\n");
    w25q64_erase_sector(fd, addr);

    printf("Writing data...\n");
    w25q64_page_program(fd, addr, write_data, strlen((char *) write_data));

    printf("Reading back...\n");
    w25q64_read_data(fd, addr, read_data, strlen((char *) write_data));

    printf("Read result: %s\n", read_data);

    close(fd);

    return 0;
}
main.c

Makefile:

CC = gcc
CFLAGS = -Wall -g -O0

OBJ = w25q64

SRC = main.c

$(OBJ): $(SRC)
    $(CC) $(CFLAGS) -o $@ $^

clean:
    $(RM) $(OBJ) *.o
Makefile

8. Uart

自发自收代码(RX/TX短接):

/**
 * Uart send and receive example.
 *
 * License - MIT.
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <errno.h>


#define UART_DEV                "/dev/serial0" // default uart dev node (soft link).
#define BAUDRATE                B115200        // baudrate (defaukt 115200 bps).


/**
 * uart_init - Initialize uart.
*/
int uart_init()
{
    int uart_fd = open(UART_DEV, O_RDWR | O_NOCTTY);

    if (uart_fd < 0) {
        perror("Error in open uart.");

        return -1;
    }

    struct termios options;
    tcgetattr(uart_fd, &options);
    cfsetispeed(&options, BAUDRATE);
    cfsetospeed(&options, BAUDRATE);

    options.c_cflag |= (CLOCAL | CREAD);        // Local mode, enable receive.
    options.c_cflag &= ~PARENB;                 // No parity.
    options.c_cflag &= ~CSTOPB;                 // 1 bit stop.
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;                     // 8 bit data.
    options.c_lflag &= ~(ICANON | ECHO | ISIG); // Raw input mode.
    options.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control.
    options.c_oflag &= ~OPOST;                  // Raw output mode.

    tcflush(uart_fd, TCIOFLUSH);

    if (tcsetattr(uart_fd, TCSANOW, &options) != 0) {
        perror("Error in configure uart.");
        close(uart_fd);

        return -1;
    }

    return uart_fd;
}

/**
 * uart_send - Send data.
*/
void uart_send(int fd, const char *data)
{
    ssize_t len = write(fd, data, strlen(data));
    if (len < 0) {
        perror("Error in send data.");
    }
    else {
        printf("Sent %zd Bytes: %s\n", len, data);
    }

    // Wait for data to be completely sent.
    tcdrain(fd);
}

/**
 * uart_receive - Receive data (Blocking mode).
*/
void uart_receive(int fd)
{
    char buffer[256];
    ssize_t len = read(fd, buffer, sizeof(buffer) - 1);

    if (len < 0) {
        perror("Error in receive data.");
    }
    else if (len == 0) {
        printf("No data arrived.\n");
    }
    else {
        buffer[len] = '\0';
        printf("Receive %zd Bytes: %s\n", len, buffer);
    }
}

int main()
{
    int uart_fd = uart_init();

    if (uart_fd < 0)
        return EXIT_FAILURE;

    // Self sending self receiving test (TXD and RXD short circuit).
    const char *test_data = "Hello World!\n";
    uart_send(uart_fd, test_data);

    // Data stabilize
    usleep(100000);

    uart_receive(uart_fd);

    close(uart_fd);

    return EXIT_SUCCESS;
}
main.c

Makefile:

CC = gcc
CFLAGS = -Wall -g -O0

OBJ = uart-self

SRC = main.c

$(OBJ): $(SRC)
    $(CC) $(CFLAGS) -o $@ $^

clean:
    $(RM) $(OBJ) *.o
Makefile

9. 1-Wire

代码: (待补充).

Makefile: (待补充).

posted @ 2025-06-06 23:33  this毛豆  阅读(45)  评论(0)    收藏  举报