Linux应用开发-5-STM32MP157开发板上控制LED和KEY

102 阅读5分钟

环境先决条件

交叉编译器选择
  • 虚拟机的交叉编译要和硬件交叉编译的版本适配
  • 各个版本交叉编译器链接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 image.png

上述还包括Qt配置,请参考以下内容链接:ubuntu22.04上添加STM32MP157、Qt5开发环境 - CodeBuug

LED的使用与查看

  • 再MobaXterm上检查系统LED类ls -l /sys/class/leds/ image.png

    /sys/class/leds 下的目录对应的 LED 灯设备
    heartbeat开发板的心跳灯
    redPro 开发板 RGB 灯的红色 (STM32MP157 开发板为用户灯)
    greenPro 开发板 RGB 灯的绿色 (STM32MP157 开发板为用户灯)
    Pro开发板 RGB 灯的蓝色 (STM32MP157 开发板为用户灯)

    注:不能操控系统灯

  • 在系统中何控制它

    • 进入它的目录ls -l /sys/class/leds/

      image.png

      注: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即可