嵌入式Linux系统构建:为STM32MP157移植Buildroot并开发温湿度采集驱动

文章目录

    • 摘要
    • [1. 环境准备与工具安装](#1. 环境准备与工具安装)
      • [1.1 硬件准备](#1.1 硬件准备)
      • [1.2 软件环境搭建](#1.2 软件环境搭建)
      • [1.3 工具链配置](#1.3 工具链配置)
    • [2. Buildroot系统构建](#2. Buildroot系统构建)
      • [2.1 获取Buildroot源码](#2.1 获取Buildroot源码)
      • [2.2 配置Buildroot](#2.2 配置Buildroot)
      • [2.3 构建根文件系统](#2.3 构建根文件系统)
    • [3. Linux内核配置与编译](#3. Linux内核配置与编译)
      • [3.1 获取内核源码](#3.1 获取内核源码)
      • [3.2 内核配置选项](#3.2 内核配置选项)
      • [3.3 编译内核](#3.3 编译内核)
    • [4. DHT11温湿度传感器驱动开发](#4. DHT11温湿度传感器驱动开发)
      • [4.1 创建驱动文件](#4.1 创建驱动文件)
      • [4.2 创建Makefile](#4.2 创建Makefile)
      • [4.3 编译驱动](#4.3 编译驱动)
    • [5. 设备树配置](#5. 设备树配置)
      • [5.1 创建设备树 overlay](#5.1 创建设备树 overlay)
      • [5.2 编译设备树 overlay](#5.2 编译设备树 overlay)
    • [6. 系统集成与部署](#6. 系统集成与部署)
      • [6.1 创建启动脚本](#6.1 创建启动脚本)
      • [6.2 配置Buildroot overlay](#6.2 配置Buildroot overlay)
      • [6.3 创建测试应用程序](#6.3 创建测试应用程序)
      • [6.4 编译测试程序](#6.4 编译测试程序)
    • [7. 系统烧录与测试](#7. 系统烧录与测试)
      • [7.1 制作SD卡镜像](#7.1 制作SD卡镜像)
      • [7.2 启动系统并测试](#7.2 启动系统并测试)
      • [7.3 常见问题处理](#7.3 常见问题处理)
    • [8. 成果展示与验证](#8. 成果展示与验证)
      • [8.1 系统功能验证](#8.1 系统功能验证)
      • [8.2 性能测试结果](#8.2 性能测试结果)
    • 技术图谱
    • 总结

摘要

本教程详细介绍了如何为STM32MP157开发板构建嵌入式Linux系统,使用Buildroot制作根文件系统,并开发DHT11温湿度传感器驱动,最终实现数据采集与显示。

1. 环境准备与工具安装

1.1 硬件准备

  • STM32MP157开发板(推荐正点原子或野火系列)
  • DHT11温湿度传感器模块
  • 杜邦线若干
  • MicroSD卡(16GB以上)
  • USB转串口调试工具

1.2 软件环境搭建

bash 复制代码
# 更新系统
sudo apt update
sudo apt upgrade -y

# 安装必要工具
sudo apt install -y build-essential git libncurses5-dev libssl-dev \
    bison flex libelf-dev u-boot-tools device-tree-compiler \
    python3 python3-pip python3-venv gcc-arm-linux-gnueabihf

# 创建工作目录
mkdir -p ~/stm32mp157-project/{buildroot,linux,kernel,drivers}
cd ~/stm32mp157-project

1.3 工具链配置

bash 复制代码
# 下载ARM工具链
wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz

# 解压并设置环境变量
tar xf gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz
echo 'export PATH=$PATH:~/stm32mp157-project/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin' >> ~/.bashrc
source ~/.bashrc

2. Buildroot系统构建

2.1 获取Buildroot源码

bash 复制代码
cd ~/stm32mp157-project/buildroot

# 克隆Buildroot仓库
git clone https://git.buildroot.net/buildroot
cd buildroot
git checkout 2022.02.1  # 使用稳定版本

2.2 配置Buildroot

bash 复制代码
# 创建配置文件
make stm32mp157_defconfig
make menuconfig

配置选项说明:

  • Target options → ARM (little endian)
  • Toolchain → Custom toolchain
  • System configuration → Root filesystem overlay
  • Kernel → Linux kernel (使用外部自定义内核)

Buildroot配置
Target Options
Toolchain
System Configuration
Kernel
ARM little endian
STM32MP157
Custom toolchain
Kernel Headers 5.10
Root filesystem overlay
Dev management: Dynamic
Use external Linux
Custom kernel config

2.3 构建根文件系统

bash 复制代码
# 开始构建
make -j$(nproc)

# 构建完成后输出路径
echo "输出目录: $(pwd)/output/images/"

3. Linux内核配置与编译

3.1 获取内核源码

bash 复制代码
cd ~/stm32mp157-project/linux

# 获取ST官方内核
git clone https://github.com/STMicroelectronics/linux.git
cd linux
git checkout v5.10-stm32mp

# 配置内核
make ARCH=arm stm32mp157_defconfig
make ARCH=arm menuconfig

3.2 内核配置选项

确保以下选项启用:

复制代码
Device Drivers →
  → Hardware Monitoring support
  → DHT11 humidity/temperature sensor
  → Industrial I/O support →
      → Humidity sensors
      → Temperature sensors

File systems →
  → DOS/FAT/NT Filesystems →
      → VFAT (Windows-95) fs support
  → Pseudo filesystems →
      → /proc file system support

3.3 编译内核

bash 复制代码
# 编译内核和模块
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- -j$(nproc)

# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs

# 安装模块到临时目录
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- \
    INSTALL_MOD_PATH=../output modules_install

4. DHT11温湿度传感器驱动开发

4.1 创建驱动文件

创建文件:~/stm32mp157-project/drivers/dht11.c

c 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define DRIVER_NAME "dht11"
#define DHT11_DATA_BITS 40

struct dht11_data {
    struct device *dev;
    struct gpio_desc *gpio;
    int temperature;
    int humidity;
    struct timer_list timer;
    struct completion completion;
    int data[DHT11_DATA_BITS];
    int edge_counter;
};

static irqreturn_t dht11_irq_handler(int irq, void *dev_id)
{
    struct dht11_data *data = dev_id;
    static ktime_t last_time;
    ktime_t current_time = ktime_get();
    s64 delta;

    if (data->edge_counter < 2) {
        /* 忽略前两个边沿(启动信号) */
        data->edge_counter++;
        last_time = current_time;
        return IRQ_HANDLED;
    }

    delta = ktime_us_delta(current_time, last_time);
    last_time = current_time;

    if (data->edge_counter - 2 < DHT11_DATA_BITS) {
        int bit_index = data->edge_counter - 2;
        /* 超过40μs为1,否则为0 */
        data->data[bit_index] = delta > 40 ? 1 : 0;
    }

    data->edge_counter++;

    if (data->edge_counter >= DHT11_DATA_BITS + 2) {
        complete(&data->completion);
    }

    return IRQ_HANDLED;
}

static void dht11_timer_callback(struct timer_list *t)
{
    struct dht11_data *data = from_timer(data, t, timer);
    complete(&data->completion);
}

static int dht11_read_data(struct dht11_data *data)
{
    int i, humidity = 0, temperature = 0;
    unsigned long timeout;

    /* 发送开始信号 */
    gpiod_direction_output(data->gpio, 0);
    mdelay(18);
    gpiod_direction_input(data->gpio);

    /* 准备读取数据 */
    data->edge_counter = 0;
    reinit_completion(&data->completion);
    mod_timer(&data->timer, jiffies + msecs_to_jiffies(10));

    timeout = wait_for_completion_timeout(&data->completion,
                     msecs_to_jiffies(10));
    del_timer(&data->timer);

    if (!timeout) {
        dev_err(data->dev, "DHT11读取超时\n");
        return -ETIMEDOUT;
    }

    /* 解析数据 */
    for (i = 0; i < 16; i++) {
        humidity = (humidity << 1) | data->data[i];
    }
    for (i = 16; i < 32; i++) {
        temperature = (temperature << 1) | data->data[i];
    }

    /* 校验和验证 */
    if (((humidity + temperature) & 0xFF) != data->data[32]) {
        dev_err(data->dev, "DHT11校验和错误\n");
        return -EIO;
    }

    data->humidity = humidity;
    data->temperature = temperature;

    dev_info(data->dev, "温度: %d°C, 湿度: %d%%\n",
         temperature, humidity);

    return 0;
}

static ssize_t temperature_show(struct device *dev,
                struct device_attribute *attr, char *buf)
{
    struct dht11_data *data = dev_get_drvdata(dev);
    int ret = dht11_read_data(data);
    if (ret)
        return ret;
    return sprintf(buf, "%d\n", data->temperature);
}

static ssize_t humidity_show(struct device *dev,
               struct device_attribute *attr, char *buf)
{
    struct dht11_data *data = dev_get_drvdata(dev);
    int ret = dht11_read_data(data);
    if (ret)
        return ret;
    return sprintf(buf, "%d\n", data->humidity);
}

static SENSOR_DEVICE_ATTR_RO(temp1_input, temperature, 0);
static SENSOR_DEVICE_ATTR_RO(humidity1_input, humidity, 0);

static struct attribute *dht11_attrs[] = {
    &sensor_dev_attr_temp1_input.dev_attr.attr,
    &sensor_dev_attr_humidity1_input.dev_attr.attr,
    NULL
};

ATTRIBUTE_GROUPS(dht11);

static int dht11_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct dht11_data *data;
    int irq, ret;

    data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    data->dev = dev;
    platform_set_drvdata(pdev, data);

    /* 获取GPIO */
    data->gpio = devm_gpiod_get(dev, NULL, GPIOD_IN);
    if (IS_ERR(data->gpio)) {
        dev_err(dev, "无法获取GPIO\n");
        return PTR_ERR(data->gpio);
    }

    /* 获取中断 */
    irq = gpiod_to_irq(data->gpio);
    if (irq < 0) {
        dev_err(dev, "无法获取IRQ\n");
        return irq;
    }

    /* 初始化完成量和定时器 */
    init_completion(&data->completion);
    timer_setup(&data->timer, dht11_timer_callback, 0);

    /* 注册中断处理程序 */
    ret = devm_request_irq(dev, irq, dht11_irq_handler,
                   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                   DRIVER_NAME, data);
    if (ret) {
        dev_err(dev, "无法请求IRQ\n");
        return ret;
    }

    dev_info(dev, "DHT11驱动加载成功\n");
    return 0;
}

static int dht11_remove(struct platform_device *pdev)
{
    struct dht11_data *data = platform_get_drvdata(pdev);
    del_timer(&data->timer);
    return 0;
}

static const struct of_device_id dht11_of_match[] = {
    { .compatible = "dht11", },
    { }
};
MODULE_DEVICE_TABLE(of, dht11_of_match);

static struct platform_driver dht11_driver = {
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = dht11_of_match,
        .dev_groups = dht11_groups,
    },
    .probe = dht11_probe,
    .remove = dht11_remove,
};

module_platform_driver(dht11_driver);

MODULE_AUTHOR("Embedded Linux Developer");
MODULE_DESCRIPTION("DHT11 Temperature and Humidity Sensor Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");

4.2 创建Makefile

创建文件:~/stm32mp157-project/drivers/Makefile

makefile 复制代码
obj-m += dht11.o

KDIR ?= ../linux

all:
    make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf-

clean:
    make -C $(KDIR) M=$(PWD) clean

4.3 编译驱动

bash 复制代码
cd ~/stm32mp157-project/drivers
make

DHT11驱动工作流程
初始化
数据读取
数据处理
GPIO配置
中断注册
定时器初始化
发送开始信号
等待响应
边沿检测
超时处理
数据解析
校验和验证
单位转换
sysfs接口

5. 设备树配置

5.1 创建设备树 overlay

创建文件:~/stm32mp157-project/dts/dht11-overlay.dts

dts 复制代码
/dts-v1/;
/plugin/;

/ {
    compatible = "st,stm32mp157";

    fragment@0 {
        target = <&pinctrl>;
        __overlay__ {
            dht11_pins: dht11-pins {
                pins = "PA6";  /* 使用PA6引脚 */
                bias-pull-up;
            };
        };
    };

    fragment@1 {
        target-path = "/";
        __overlay__ {
            dht11: dht11@0 {
                compatible = "dht11";
                pinctrl-names = "default";
                pinctrl-0 = <&dht11_pins>;
                gpios = <&gpioa 6 GPIO_ACTIVE_HIGH>;
                status = "okay";
            };
        };
    };
};

5.2 编译设备树 overlay

bash 复制代码
# 编译设备树 overlay
dtc -@ -I dts -O dtb -o dht11-overlay.dtbo dht11-overlay.dts

# 将 overlay 复制到 Buildroot 的 overlay 目录
cp dht11-overlay.dtbo ~/stm32mp157-project/buildroot/output/overlay/

6. 系统集成与部署

6.1 创建启动脚本

创建文件:~/stm32mp157-project/scripts/init-dht11.sh

bash 复制代码
#!/bin/bash

# 加载DHT11驱动
modprobe industrialio
insmod /lib/modules/$(uname -r)/extra/dht11.ko

# 创建设备节点
mknod /dev/dht11 c 240 0

# 设置权限
chmod 666 /dev/dht11

echo "DHT11驱动初始化完成"

6.2 配置Buildroot overlay

bash 复制代码
cd ~/stm32mp157-project/buildroot/output/target

# 创建必要的目录
mkdir -p etc/init.d lib/modules/$(uname -r)/extra

# 复制驱动模块
cp ~/stm32mp157-project/drivers/dht11.ko lib/modules/$(uname -r)/extra/

# 复制启动脚本
cp ~/stm32mp157-project/scripts/init-dht11.sh etc/init.d/S90dht11
chmod +x etc/init.d/S90dht11

# 更新模块依赖
depmod -b . -a $(uname -r)

6.3 创建测试应用程序

创建文件:~/stm32mp157-project/apps/dht11-test.c

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define DEVICE_PATH "/sys/bus/platform/devices/dht11@0/"

int read_sysfs_value(const char *path) {
    int value;
    FILE *file = fopen(path, "r");
    if (file == NULL) {
        perror("无法打开文件");
        return -1;
    }
    if (fscanf(file, "%d", &value) != 1) {
        fclose(file);
        return -1;
    }
    fclose(file);
    return value;
}

int main() {
    int temperature, humidity;
    
    printf("DHT11温湿度传感器测试程序\n");
    
    while (1) {
        temperature = read_sysfs_value(DEVICE_PATH "temp1_input");
        humidity = read_sysfs_value(DEVICE_PATH "humidity1_input");
        
        if (temperature >= 0 && humidity >= 0) {
            printf("温度: %.1f°C, 湿度: %.1f%%\n", 
                   temperature / 10.0, humidity / 10.0);
        } else {
            printf("读取数据失败\n");
        }
        
        sleep(2);
    }
    
    return 0;
}

6.4 编译测试程序

bash 复制代码
arm-none-linux-gnueabihf-gcc -o dht11-test dht11-test.c -static
cp dht11-test ~/stm32mp157-project/buildroot/output/target/usr/bin/

7. 系统烧录与测试

7.1 制作SD卡镜像

bash 复制代码
cd ~/stm32mp157-project/buildroot/output/images/

# 使用dd命令烧录镜像
sudo dd if=sd-card.img of=/dev/sdX bs=4M status=progress
sync

7.2 启动系统并测试

bash 复制代码
# 通过串口连接开发板
minicom -D /dev/ttyUSB0 -b 115200

# 系统启动后,测试驱动
dmesg | grep dht11  # 查看驱动加载信息

# 运行测试程序
dht11-test

7.3 常见问题处理

问题1:驱动加载失败

复制代码
解决方法:检查GPIO配置和设备树设置

问题2:数据读取不稳定

复制代码
解决方法:增加去抖动处理,调整时序参数

问题3:权限问题

复制代码
解决方法:确保/dev节点权限正确

8. 成果展示与验证

8.1 系统功能验证

bash 复制代码
# 验证驱动加载
lsmod | grep dht11

# 验证sysfs接口
ls /sys/bus/platform/devices/dht11@0/

# 实时数据读取
cat /sys/bus/platform/devices/dht11@0/temp1_input
cat /sys/bus/platform/devices/dht11@0/humidity1_input

8.2 性能测试结果

复制代码
温度测量范围:0-50°C ±2°C
湿度测量范围:20-90%RH ±5%
响应时间:<2秒
采样间隔:≥2秒

技术图谱

STM32MP157 Buildroot系统
硬件层
系统层
应用层
STM32MP157芯片
DHT11传感器
GPIO接口
Linux 5.10内核
Buildroot根文件系统
设备树配置
驱动框架
温湿度采集驱动
sysfs接口
用户空间程序
数据展示

总结

本教程详细介绍了从零开始为STM32MP157构建嵌入式Linux系统的完整流程,包括:

  1. 环境搭建:交叉编译工具链和开发环境配置
  2. 系统构建:使用Buildroot制作定制根文件系统
  3. 内核移植:Linux内核配置、编译和设备树定制
  4. 驱动开发:DHT11温湿度传感器驱动完整实现
  5. 系统集成:驱动部署和应用程序开发
  6. 测试验证:系统功能验证和性能测试

通过本教程,读者可以掌握嵌入式Linux系统开发的完整技能栈,并能够将这些技术应用到其他嵌入式项目中。整个系统具有稳定性高、资源占用少、易于定制等优点,非常适合工业控制和物联网应用场景。

相关推荐
不爱学习的笨蛋2 小时前
ubuntu安装gitlab
linux·ubuntu·gitlab
QQ__17646198242 小时前
Ubuntu系统克隆Github仓库项目到本地
linux·ubuntu·github
坚持的小马2 小时前
启动NameServer集群
linux·运维·网络
一只大侠的侠2 小时前
Linux实战:动态进度条从零实现,多版本优化与缓冲区原理全解析
linux·运维·服务器
阿坤带你走近大数据2 小时前
Elasticsearch(ES)的基本概念、架构及基本使用介绍
大数据·elasticsearch
山人在山上2 小时前
ubuntu mysql 5.7安装
linux·mysql·ubuntu
catoop2 小时前
CentOS 7 重置root密码步骤
linux·运维·centos
小刘爱玩单片机2 小时前
【stm32简单外设篇】- LCD1602A
c语言·stm32·单片机·嵌入式硬件
意法半导体STM322 小时前
【官方原创】在H563上使用RTX5 RTOS LAT1584
stm32·单片机·嵌入式硬件·mcu
Elastic 中国社区官方博客2 小时前
使用 Elasticsearch 中的结构化输出创建可靠的 agents
大数据·人工智能·elk·elasticsearch·搜索引擎·ai·全文检索