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更加灵活,因为可以修改参数。

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

相关推荐
hanlin033 小时前
刷题笔记:力扣第43、67题(字符串计算)
笔记·算法·leetcode
多看书少吃饭4 小时前
Vue + Java + Python 打造企业级 AI 知识库与任务分发系统(RAG架构全解析)
java·vue.js·笔记
了一梨4 小时前
[T113] 交叉编译 OpenCV 4.5.2 + face 模块
linux·笔记·opencv
困死,根本不会4 小时前
VMware Ubuntu 显示有线连接却无法上网|完整排查与解决笔记
linux·笔记·ubuntu
左左右右左右摇晃5 小时前
数据结构——栈
数据结构·笔记
左左右右左右摇晃5 小时前
数据结构——树
数据结构·笔记
chudonghao6 小时前
[UE学习笔记][基于源码] 理解 Gameplay
c++·笔记·学习·ue5
左左右右左右摇晃7 小时前
数据结构——数组
数据结构·笔记·算法
nainaire8 小时前
速通LeetCode hot100——(1~9 哈希,双指针,滑动窗口)
c++·笔记·算法·leetcode