Heng30的博客
搜索 分类 关于 订阅

Linux驱动如何使用pwm设备?

2025-03-26

在Linux内核中,struct pwm_device是用于描述和管理PWM(脉冲宽度调制)设备的核心数据结构。它通常定义在include/linux/pwm.h中,主要作用是为PWM控制器(硬件)和消费者(驱动)之间提供统一的抽象接口。

设备树

// test.dts

/dts-v1/;

/ {
    #address-cells = <1>;
    #size-cells = <1>;

    pwm1: timer@44000000 {
        #address-celss = <0x1>;
        #size-cells = <0x0>;
        compatible = "st,stm32-timers";
        reg = <0x44000000 0x400>;
        clocks = <0xb 0x85>;
        resets = <0xb 0x4c49>;
        dams = <0xe 0x53 0x400 0x1 0xe 0x54 0x400 0x1>;
        dma-name = "rx", "tx";
        power-domains = <0x10>;
        status = "disabled";

        pwm {
            compatible = "st,stm32-pwm";
            #pwm-cells = <3>;
            status = "disabled";
        };
    };

    pwm1_pins_1: pwm1_pins {
        status = "okay";
    };

    pwm1_sleep_pins_1: pwm1_sleep_pins {
        status = "okay";
    };

    fan_pwm {
        compatible = "pwm-fan";
        status = "okay";

        pwm = <&fan_pwm1 0 1000 0>;
    };
};

// 覆写原有的设备树节点
&pwm1 {
    /delete-property/dmas;
    /delete-property/dma-name;

    status = "okay";

    fan_pwm1: pwm {
        compatible = "st,stm32-timer";
        status = "okay";
        #pwm-cells = <0x3>;

        pinctl-names = "default", "sleep", "idle", "active";
        pinctl-0 = <&pwm1_pins_1>;
        pinctl-1 = <&pwm1_sleep_pins_1>;
    };
};

驱动代码

// simple.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>

#define PLATFORM_DEVICE_BASENAME "my_device"
#define PLATFORM_DEVICE_NAME_PWM "pwm-fan"

static u32 duty_ns;   // 占用比
static u32 period_ns; // 周期
static struct pwm_device *pwm;

static int _probe(struct platform_device *device) {
    pr_info("platform_driver_probe\n");

    pwm = devm_pwm_get(&device->dev, NULL);

    if (IS_ERR(pwm)) {
        pr_info("devm_of_pwm_get failed\n");
        return PTR_ERR(pwm);
    }

    // 占空比30%
    int precent = 30;
    duty_ns = precent * 10000;
    period_ns = 1000000; // 1毫秒
    pwm_config(pwm, duty_ns, period_ns);
    pwm_enable(pwm);
    return 0;
}

static int _remove(struct platform_device *device) {
    pr_info("platform_driver_remove\n");
    pwm_disable(pwm);
    return 0;
}

// 会与设备树安装的platform_device进行匹配(一般用在嵌入式linux开发中)
struct of_device_id of_node_match_table[] = {
    [0] = {.compatible = PLATFORM_DEVICE_NAME_PWM}, [1] = {}, // 空元素代表结束
};

struct platform_driver my_platform_driver = {
    .probe = _probe,
    .remove = _remove,

    // 通过id_table匹配, 实现一个驱动对多个设备
    .driver =
        {
            .name = PLATFORM_DEVICE_BASENAME,

            // 设备树的匹配方式
            .of_match_table = of_node_match_table,
        },
};

static int __init pwm_driver_init(void) {
    pr_info("pwm_driver_init\n");

    int ret = platform_driver_register(&my_platform_driver);
    if (ret < 0) {
        pr_err("platform_driver_register_v6 failed\n");
        return -1;
    }
    return 0;
}

static void __exit pwm_driver_exit(void) {
    pr_info("pwm_driver_exit\n");
    platform_driver_unregister(&my_platform_driver);
}

module_init(pwm_driver_init);
module_exit(pwm_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("heng30");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple pwm_driver kernel module");

编译脚本

#!/bin/sh

top-dir = $(shell pwd)
kernel-version = $(shell uname -r)
kernel-dir ?= /lib/modules/$(kernel-version)/build

obj-m += simple.o

all: dt driver

driver:
        make -C $(kernel-dir) modules M=$(top-dir)

dt:
        dtc -@ -I dts -O dtb -o test.dtbo test.dts

clean:
        rm -f *.o *.ko *.mod *.mod.c *.order *.symvers *.dtbo
        make -C $(kernel-dir) clean m=$(top-dir)