3.23-3.25笔记

这期实现温湿度采集、光照强度监测、智能设备控制(加湿器、PWM 调光 LED、PWM 调速风扇)

确定引脚,根据原理图找出可以使用的引脚开关。根据手册信息

PWM口GPIO0_D0和GPIO0_C6,把设备树GPIO0_D0做5G的复位disable,再加入

&pwm5 {

pinctrl-0 = <&pwm5m1_pins>;

status = "okay";

};

&pwm7 {

pinctrl-0 = <&pwm7m1_pins>;

status = "okay";

};

因为pinctrl.dtsi都定义好了io口做PWM,是PWM几。所以只要打开 status = "okay";。系统就会自动的在/sys/class/pwm/下去生成pwmchip5,直接去写入设置PWM。

echo 0 > /sys/class/pwm/pwmchip2/export echo 50000 > /sys/class/pwm/pwmchip2/pwm0/period echo 10000 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle echo normal > /sys/class/pwm/pwmchip2/pwm0/polarity echo 1 > /sys/class/pwm/pwmchip2/pwm0/enable

pwmchip2是PWM5,pwmchip5是PWM7。通过示波器正常显示PWM波形图

LED 亮度调节GPIO0_C6,PWM5
风扇调速 GPIO1_D0 PWM7
加湿器(开关)GPIO1_D3
DHT11 温湿度GPIO1_D2
光敏传感器SARADC_VIN4

需要GPIO,PWM,ADC驱动。所以设备树加入以下代码

复制代码
	humidifier {
    compatible = "rzroomi,humidifier";
    gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
    status = "okay";
	};
	dht11 {
    compatible = "rzroomi,dht11";
    gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
    status = "okay";
	};


&pwm5 {
	pinctrl-0 = <&pwm5m1_pins>;
	status = "okay";
};
&pwm6 {
	pinctrl-0 = <&pwm6m1_pins>;
	status = "okay";
};
&pwm7 {
	pinctrl-0 = <&pwm7m1_pins>;
	status = "okay";
};

经过编译,编译是成功了,烧写到开发版,结果usb识别不了设备了。

把之前的代码烧录进去,加入PWM和GPIO正常,可能是ADC设备驱动的原因把。

在驱动代码使用platform + miscdevice + gpiod + devm + ioctl进行编写驱动,代码会更精简。与之前的驱动代码相比有以下优点

1使用miscdevice,替代了下面的代码

alloc_chrdev_region(...) cdev_init(...) cdev_add(...) class_create(...) device_create(...)

  • 申请主设备号(固定 10)
  • 自动分配次设备号
  • 自动创建 /dev/xxx
  • 自动绑定 fops
  • 自动实现 open/close

2使用platform,替代了下面的代码

dev->nd = of_find_node_by_path(...); of_property_read_string(...); of_get_named_gpio_flags(...); gpio_request(...); gpio_direction_output(...);

  • 找设备树节点
  • 检查 compatible
  • 解析 gpios 属性
  • 申请 GPIO
  • 初始化输出模式
  • 默认输出低电平

3用了 ioctl 而不是 write,ioctl可传入更多的常数来控制设备

以下就是humidifier_gpio_driver.c

复制代码
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>

/*
_IOW = 写命令
'H' = 幻数(随便写)
0 = 命令号
int = 要传递的数据类型
用途:应用程序发这个命令,就能控制 GPIO 高低电平。
*/
#define IOCTL_HUMI_SET _IOW('H', 0, int)

static struct gpio_desc *humi_gpio;

//ioctl 函数(应用层控制硬件的入口)
//把应用层数据传入内核,并操作io
static long humi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int value;

    if (copy_from_user(&value, (int __user *)arg, sizeof(int)))
        return -EFAULT;

    gpiod_set_value(humi_gpio, !!value);
    return 0;
}
/*
unlocked_ioctl 
compat_ioctl
*/
static const struct file_operations humi_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = humi_ioctl,
    .compat_ioctl = humi_ioctl,
};



static struct miscdevice humi_misc = {
    .minor = MISC_DYNAMIC_MINOR,            // 自动分配次设备号
    .name = "humidifier",                   // 设备名 /dev/humidifier
    .fops = &humi_fops,                     // 关联操作函数
};

//Platform 驱动的探测函数
static int humi_probe(struct platform_device *pdev)
{
    humi_gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
    if (IS_ERR(humi_gpio))
        return PTR_ERR(humi_gpio);

    misc_register(&humi_misc);
    dev_info(&pdev->dev, "HUMI probe ok\n");
    return 0;
}


//设备树匹配表
static const struct of_device_id humi_of_match[] = {
    { .compatible = "rzroomi,humidifier" },
    {},
};
//告诉内核:这个驱动支持的设备。
MODULE_DEVICE_TABLE(of, humi_of_match);

//把 probe 函数和匹配表绑定到 Platform 驱动。
static struct platform_driver humi_driver = {
    .probe = humi_probe,
    .driver = {
        .name = "humidifier",
        .of_match_table = humi_of_match,
    },
};

// 给 main.c 调用
int humi_probe_register(void)
{
    return platform_driver_register(&humi_driver);
}

void humi_probe_unregister(void)
{
    platform_driver_unregister(&humi_driver);
}

另外两个打开PWM的代码,自动创建设备了,似乎也不需要写驱动了。

以下就是main.c

复制代码
#include <linux/module.h>

extern int led_probe_register(void);
extern void led_probe_unregister(void);

extern int fan_probe_register(void);
extern void fan_probe_unregister(void);

extern int humi_probe_register(void);
extern void humi_probe_unregister(void);

static int __init smart_driver_init(void)
{
    led_probe_register();
    fan_probe_register();
    humi_probe_register();
    return 0;
}

// 总出口(唯一)
static void __exit smart_driver_exit(void)
{
    humi_probe_unregister();
    fan_probe_unregister();
    led_probe_unregister();
}

module_init(smart_driver_init);
module_exit(smart_driver_exit);

MODULE_LICENSE("GPL");

这次的代码使用了四个.c文件,主程序是main.c。然后通过Makefile结合在一起进行编译生成ko文件。代码的思路是这样的:

insmod main.ko后,驱动代码里先会去匹配设备,比如先匹配humi_probe_register,Platform 驱动根据所给的name,compatible对应设备树内容,获取gpios然后就会执行humi_probe函数,在里面执行devm_gpiod_get申请gpio的使用权,然后设置默认默认输出低电平,然后执行misc_register(&humi_misc);作用就是创建字符设备,也就是上期代码里的cdevinit,class_create,device_create.当应用对其控制就可以执行命令iocrt('H', 1, int),设置gpio为高,就可以打开这个加湿器,比上一期的wirte更加灵活,因为可以修改参数。

三天的笔记到这了,如有错误请大佬指正

相关推荐
GGMM7896 小时前
VX4B600100 控制板全解析:施耐德 ATV630 变频器核心控制单元详解
笔记·变频器·变频器维修
kdxiaojie7 小时前
Linux USB驱动阅读笔记(1)
linux·笔记·学习·usb
半生过往8 小时前
前端学 Java 课程笔记
java·前端·笔记
九硕智慧建筑一体化厂家8 小时前
楼宇自控|智慧建筑核心引擎!楼宇自控系统,赋能建筑高效低碳运维
运维·人工智能·笔记·智慧城市
GGMM7899 小时前
施耐德 VX5A1HC90110(VX5A1HD9011)ATV61/ATV71 90‑110KW 电源驱动板详解
笔记·变频器·变频器维修
Wang's Blog10 小时前
PostgreSQL笔记48: 全文检索与向量检索的对比与混合检索实践
笔记·postgresql·全文检索
liuyicenysabel10 小时前
Flask 个人站 iProfile 上线笔记(k3s + Nginx + Let‘s Encrypt)
笔记·nginx·flask
深蓝海拓10 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(十五)后续完善和改进:PLC端数据解析的防粘包、残包、废包、拆包
笔记·学习·plc
小黄蚁11 小时前
LVGL学习笔记(六)
笔记·学习
Wang's Blog11 小时前
PostgreSQL笔记49:向量检索核心算法、索引调优与过滤策略深度解析
笔记·算法·postgresql