环境先决条件
交叉编译器选择
- 虚拟机的交叉编译要和硬件交叉编译的版本适配
- 各个版本交叉编译器链接mirrors.tuna.tsinghua.edu.cn/armbian-rel…
- 下载以下版本gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz
虚拟机环境构建
- 拖到vscode一个文件夹然后进行解压
sudo tar -xvf gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf.tar.xz - 将工具链路径添加到系统环境变量中,
vim ~/.bashrc - 路径一定是你解压的位置,到你解压位置pwd,然后把路径替换为你的路径
export PATH=$PATH:/opt/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/bin - 让环境变量立即生效:
source ~/.bashrc - 验证交叉编译器是否配置成功:
arm-none-linux-gnueabihf-gcc -v
上述还包括Qt配置,请参考以下内容链接:ubuntu22.04上添加STM32MP157、Qt5开发环境 - CodeBuug
LED的使用与查看
-
再MobaXterm上检查系统LED类
ls -l /sys/class/leds//sys/class/leds 下的目录 对应的 LED 灯设备 heartbeat 开发板的心跳灯 red Pro 开发板 RGB 灯的红色 (STM32MP157 开发板为用户灯) green Pro 开发板 RGB 灯的绿色 (STM32MP157 开发板为用户灯) Pro 开发板 RGB 灯的蓝色 (STM32MP157 开发板为用户灯) 注:不能操控系统灯
-
在系统中何控制它
-
进入它的目录
ls -l /sys/class/leds/注:trigger决定了这个LED由谁来控制。brightness控制LED的亮灭。
-
具体操纵
# 点亮LED echo 1 > /sys/class/leds/green/brightness echo 1 > /sys/class/leds/blue/brightness echo 1 > /sys/class/leds/red/brightness # 熄灭LED echo 0 > /sys/class/leds/green/brightness echo 0 > /sys/class/leds/blue/brightness echo 0 > /sys/class/leds/red/brightness -
传统方式 (sysfs)通过文件操纵操作LED灯
// Standard Input/Output (标准输入/输出库) #include <stdio.h> // Standard Library (标准库) #include <stdlib.h> // Unix Standard (Unix标准库) 访问操作系统内核功能的接口 // write(): 向文件描述符写入数据。 // read(): 从文件描述符读取数据。 // close(): 关闭一个文件描述符。 // sleep(): 让程序暂停指定的秒数。 #include <unistd.h> // File Control (文件控制) #include <fcntl.h> // Signal Handling (信号处理) #include <signal.h> #define GLED_DEV_PATH "/sys/class/leds/green/brightness" #define BLED_DEV_PATH "/sys/class/leds/blue/brightness" #define RLED_DEV_PATH "/sys/class/leds/red/brightness" // 全局标志位,控制循环是否继续 // volatile 关键字告诉编译器,这个变量的值可能在程序意想不到的时候被改变 volatile int keep_running = 1; // 定义信号处理函数。当程序收到 SIGINT 信号 (Ctrl+C) 时,这个函数会被调用 void int_handler(int sig) { printf("\nCaught signal %d, stopping...\n", sig); keep_running = 0; // 将标志位设为0,通知主循环退出 } // 定义开灯函数 void led_on(int fd) { // 尝试写入"255",如果失败则打印错误并退出程序 if (write(fd, "255", 3) < 0) { perror("Failed to turn LED on"); exit(1); } } // 定义关灯函数 void led_off(int fd) { // 尝试写入"0",如果失败则打印错误并退出程序 if (write(fd, "0", 1) < 0) { perror("Failed to turn LED off"); exit(1); } } int main(int argc, char *argv[]) { int r_fd, g_fd, b_fd; // 注册信号处理函数:告诉系统,当收到 SIGINT 信号时,不要执行默认操作,而是去调用我指定的函数,请调用 int_handler 函数 signal(SIGINT, int_handler); // O_WRONLY 是一个在 fcntl.h 中定义的宏,是 "Open for Write Only" 的缩写,意为“以只写模式打开”。 r_fd = open(RLED_DEV_PATH, O_WRONLY); g_fd = open(GLED_DEV_PATH, O_WRONLY); b_fd = open(BLED_DEV_PATH, O_WRONLY); if (r_fd && g_fd && b_fd < 0) { perror(RLED_DEV_PATH); perror(GLED_DEV_PATH); perror(BLED_DEV_PATH); exit(1); } printf("LED blinking... Press Ctrl+C to stop.\n"); int i = 0; while (keep_running) { i+=1; if(i%4==1){ led_on(r_fd); sleep(1); led_off(r_fd); } else if(i%4==2){ led_on(g_fd); sleep(1); led_off(g_fd); } else if(i%4==3){ led_on(b_fd); sleep(1); led_off(b_fd); } else{ led_on(r_fd); led_on(g_fd); led_on(b_fd); sleep(1); led_off(r_fd); led_off(g_fd); led_off(b_fd); } // 增加一个检查,如果在点亮期间收到了退出信号,就没必要再执行熄灭和等待了 if (!keep_running){ write(r_fd, "0", 1); // 确保LED熄灭 write(g_fd, "0", 1); // 确保LED熄灭 write(b_fd, "0", 1); // 确保LED熄 break; } } // 6. 当循环结束后,程序会继续向下执行 printf("Cleaning up and closing file...\n"); close(r_fd); // 关闭红色LED的文件描述符 close(g_fd); // 关闭绿色LED的文件描述符 close(b_fd); // 关闭蓝色LED的文件描述符 printf("Done.\n"); return 0; } -
input 子系统: 按键检测
/dev/input/event /dev/input/by-path目录查看具体的硬件设备
使用命令行工具快速测试
#安装evtest:
sudo apt update
sudo apt install evtest
#运行并查看事件:
sudo evtest /dev/input/event0
使用C语言编程实现
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h> // 包含了 input_event 结构体和事件类型/代码的宏定义
// 定义设备文件路径
#define INPUT_DEVICE "/dev/input/event0"
int main() {
int fd;
struct input_event event;
ssize_t n;
// 打开输入设备文件
// O_RDONLY 表示只读
fd = open(INPUT_DEVICE, O_RDONLY);
if (fd == -1) {
// 如果打开失败,打印错误信息并退出
perror("Error opening device");
exit(EXIT_FAILURE);
}
printf("Input device opened successfully. Waiting for key press...\n");
printf("----------------------------------------------------------\n");
// 无限循环,持续读取事件
while (1) {
// read() 会阻塞,直到有新的事件发生
n = read(fd, &event, sizeof(struct input_event));
if (n == sizeof(struct input_event)) {
// 我们只关心按键事件 (type == EV_KEY)
// 并且只在按键被“按下”(value == 1)时作出反应
if (event.type == EV_KEY && event.value == 1) {
printf("Key Press Event! Type: %d, Code: %d, Value: %d\n",
event.type, event.code, event.value);
// 使用 switch 语句根据 event.code 判断是哪个按键
// 请将下面的 KEY_XXX 替换为您通过 evtest 找到的实际宏或数字
switch (event.code) {
case KEY_ENTER: // 比如 code 是 28
printf("=> You pressed the ENTER key.\n");
break;
case KEY_VOLUMEUP: // 比如 code 是 115
printf("=> You pressed the VOLUME UP key.\n");
break;
case KEY_VOLUMEDOWN: // 比如 code 是 114
printf("=> You pressed the VOLUME DOWN key.\n");
break;
// 在这里添加更多 case 来处理您板子上的其他按键
// case 102: // 假设有一个 HOME 键的 code 是 102
// printf("=> You pressed the HOME key.\n");
// break;
default:
printf("=> An unhandled key (code %d) was pressed.\n", event.code);
break;
}
printf("----------------------------------------------------------\n");
}
} else {
// 如果读取错误或中断,打印警告
perror("Error reading from device");
break;
}
}
// 虽然在无限循环中这行代码不会执行,但好习惯是加上它
close(fd);
return 0;
}
- 在虚拟机上进行单个文件编译
arm-none-linux-gnueabihf-gcc -o led led.c - STM32MP157相应文件夹执行./led即可