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; }
Makefile:
CC = gcc CFLAGS = -Wall -g -O0 LDLIBS = -lgpiod OBJ = blink SRC = main.c $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) clean: $(RM) $(OBJ) *.o
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; }
Makefile:
CC = gcc CFLAGS = -Wall -g -O0 LDLIBS = -lgpiod OBJ = button SRC = main.c $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) clean: $(RM) $(OBJ) *.o
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; }
Makefile:
CC = gcc CFLAGS = -Wall -g -O0 LDLIBS = -lgpiod OBJ = interrupt SRC = main.c $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) clean: $(RM) $(OBJ) *.o
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; }
Makefile:
CC = gcc CFLAGS = -Wall -g -O0 OBJ = at24c02 SRC = main.c $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $@ $^ clean: $(RM) $(OBJ) *.o
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; }
Makefile:
CC = gcc CFLAGS = -Wall -g -O0 OBJ = w25q64 SRC = main.c $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $@ $^ clean: $(RM) $(OBJ) *.o
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; }
Makefile:
CC = gcc CFLAGS = -Wall -g -O0 OBJ = uart-self SRC = main.c $(OBJ): $(SRC) $(CC) $(CFLAGS) -o $@ $^ clean: $(RM) $(OBJ) *.o
9. 1-Wire
代码: (待补充).
Makefile: (待补充).

浙公网安备 33010602011771号