PWM子系统

文章目录

  • 一、电路连接
    • (一)蜂鸣器
    • (二)风扇
    • (三)马达
  • 二、配置内核
  • 三、设备树
    • (一)timer定时器
    • (二)PWM
    • (三)设备树节点
  • 四、驱动
    • (一)相关API
      • 1. 分配对象pwm_device
      • 2. 配置PWM设备
      • 3. 打开或者关闭PWM设备
      • 4. 注销PWM设备
    • (二)代码示例

实现用PWM子系统控制风扇、蜂鸣器、马达的速率

一、电路连接

(一)蜂鸣器

在这里插入图片描述
由图可知,使用了定时器4的通道1,该引脚连到了主控板的PB6引脚

在这里插入图片描述

(二)风扇

在这里插入图片描述
使用了定时器1的通道1,连接了PE9引脚
在这里插入图片描述

(三)马达

在这里插入图片描述
连接引脚PF6

在这里插入图片描述

二、配置内核

  1. 开启STM32定时器

     Device Drivers --->
     	Multifunction device drivers --->
     		{*} Support for STM32 Timers 
    
  2. 开启STM32 PWM控制器

     Device Drivers --->
     	[*] Pulse-Width Modulation (PWM) Support --->
     		<*> STMicroelectronics STM32 PWM
     		<*> STMicroelectronics STM32 PWM LP
    
  3. 开启基于PWM⼦系统的pwm-beeper驱动
    如果不使用内核自带的pwm-beeper驱动,该配置不要选配,选n

     Device Drivers --->
     	Input device support --->
     		[*] Miscellaneous devices --->
     			<*> PWM beeper support 
    

三、设备树

(一)timer定时器

打开stm32mp151.dtsi文件,找到timers4节点

timers4: timer@40002000 {
			#address-cells = <1>;
			#size-cells = <0>;
			compatible = "st,stm32-timers";
			reg = <0x40002000 0x400>;
			clocks = <&rcc TIM4_K>;
			clock-names = "int";
			dmas = <&dmamux1 29 0x400 0x80000001>,
			       <&dmamux1 30 0x400 0x80000001>,
			       <&dmamux1 31 0x400 0x80000001>,
			       <&dmamux1 32 0x400 0x80000001>;
			dma-names = "ch1", "ch2", "ch3", "ch4";
			status = "disabled";
		};

打开内核帮助文档,找到相关示例
st,stm32-timer.yaml

36 examples:
 37   - |
 38     #include <dt-bindings/interrupt-controller/arm-gic.h>
 39     #include <dt-bindings/clock/stm32mp1-clks.h>
 40     timer: timer@40000c00 {
 41         compatible = "st,stm32-timer";
 42         reg = <0x40000c00 0x400>;
 43         interrupts = <50>;                                                       
 44         clocks = <&clk_pmtr1>;
 45     };

对比可见,定时器基本配置已经配置完毕。因此只需要配置相关的引脚控制。
定时器4节点

&timers4 {
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
	pwm4: pwm {
		pinctrl-0 = <&pwm4_pins_c>;
		pinctrl-1 = <&pwm4_sleep_pins_c>;
		pinctrl-names = "default", "sleep";
		#pwm-cells = <2>;
		status = "okay";
	};
	timer@3 {
		status = "disabled";
	};
};

引脚配置

&pinctrl {
	pwm4_pins_c: pwm4-0 {
		pins {
			pinmux = <STM32_PINMUX('B', 6, AF2)>;
			/* TIM4_CH1 */
			bias-pull-down;  	//下拉
			drive-push-pull;	//推挽
			slew-rate = <0>;
		};
	};
	pwm4_sleep_pins_c: pwm4-sleep-0 {
		pins {
		pinmux = <STM32_PINMUX('B', 6, ANALOG)>; /* TIM4_CH1 */
		};
	};
};

(二)PWM

然后打开PWM的帮助文档
pwm.txt ---- 介绍用户节点
pwm.yaml ---- 介绍控制器节点

在pwm.txt中可以获得关于pwm用户节点的介绍:
#pwm-cells = <2>;
使用两个元素来描述PWM设备
第一个元素描述PWM通道号,0表示使用PWM控制器的第一个通道。
第二个元素描述PWM频率,单位是赫兹(Hz)

pwms:这是一个必需的属性,用于列出设备将要使用的PWM设备。它的值是一个或多个pwm-list结构
pwm-list属性的列表来指定想要使用的PWM设备

蜂鸣器节点

/{
	//蜂鸣器
	beeper {
		compatible = "pwm-beeper";
		pwms = <&pwm4 0 4000000>;
	};
};

(三)设备树节点

由上述类比,可得

\{
	//蜂鸣器
	beeper {
		compatible = "zyx,pwm-beep";
		pwms = <&pwm4 0 4000000>;
	};
	//风扇
	fun {
		compatible = "zyx,pwm-fun";
		pwms = <&pwm1 0 4000000>;
	};
	//马达
	motor {
		compatible = "zyx,pwm-motor";
		pwms = <&pwm16 0 4000000>;
	};
};

/***蜂鸣器***/
&timers4 {
	/* spare dmas for other usage */
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
	pwm4: pwm {
		pinctrl-0 = <&pwm4_pins_c>;
		pinctrl-1 = <&pwm4_sleep_pins_c>;
		pinctrl-names = "default", "sleep";
		#pwm-cells = <2>;
		status = "okay";
	};
	timer@3 {
		status = "disabled";
	};
};

&pinctrl {
	pwm4_pins_c: pwm4-0 {
		pins {
			pinmux = <STM32_PINMUX('B', 6, AF2)>;
			/* TIM4_CH1 */
			bias-pull-down;
			drive-push-pull;
			slew-rate = <0>;
		};
	};
	pwm4_sleep_pins_c: pwm4-sleep-0 {
		pins {
		pinmux = <STM32_PINMUX('B', 6, ANALOG)>; /* TIM4_CH1 */
		};
	};
};

/***风扇***/
&timers1{
	/* spare dmas for other usage */
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
	pwm1: pwm {
		pinctrl-0 = <&pwm1_pins_b>;
		pinctrl-1 = <&pwm1_sleep_pins_b>;
		pinctrl-names = "default", "sleep";
		#pwm-cells = <2>;
		status = "okay";
	};
	
};
//pinctl文件中提供的pwm1引脚会包含其他引脚,因此此处仍然自己实现引脚
&pinctrl{
	pwm1_pins_b: pwm1-0 {
		pins {
			pinmux = <STM32_PINMUX('E', 9, AF1)>;/* TIM1_CH1 */
			bias-pull-down;
			drive-push-pull;
			slew-rate = <0>;
		};
	};

	pwm1_sleep_pins_b: pwm1-sleep-0 {
		pins {
			pinmux = <STM32_PINMUX('E', 9, ANALOG)>; /* TIM1_CH1 */
		};
	};
};

/***马达***/
&timers16{
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
	pwm16: pwm {
		pinctrl-0 = <&pwm16_pins_a>;
		pinctrl-1 = <&pwm16_sleep_pins_a>;
		pinctrl-names = "default", "sleep";
		#pwm-cells = <2>;
		status = "okay";
	};
};

&pinctrl{
	pwm16_pins_a: pwm16-0 {
		pins {
			pinmux = <STM32_PINMUX('F', 6, AF1)>;/* TIM1_CH1 */
			bias-pull-down;
			drive-push-pull;
			slew-rate = <0>;
		};
	};

	pwm16_sleep_pins_a: pwm16-sleep-0 {
		pins {
			pinmux = <STM32_PINMUX('F', 6, ANALOG)>; /* TIM1_CH1 */
		};
	};
};

四、驱动

(一)相关API

1. 分配对象pwm_device

 //PWM通道对象
struct pwm_device { 
	const char *label;		//PWM设备名
	unsigned long flags;	//标志位
	unsigned int hwpwm;		//PWM设备索引
	unsigned int pwm;		//全局索引
	struct pwm_chip *chip;	//PWM设备的PWM芯片
	void *chip_data;		//芯片的私有数据

	struct pwm_args args;	//PWM参数
	struct pwm_state state;	//PWM当前状态
	struct pwm_state last;	//最后一次尝试实现的状态,用于调试
};

申请对象内存空间:

struct pwm_device *pwm;
pwm = kzalloc(sizeof(*pwm),GFP_KERNEL);

分配对象:

struct pwm_device *pwm_get(struct device *dev, const char *con_id);
功能:
	请求一个PWM设备
参数:
	@dev:device结构体指针,父类
	@con_id:指向一个字符串,用作连接标识符或别名来指定所需的PWM设备
返回值:
	成功,返回pwm_device结构体指针
	失败,返回错误码指针
		使用IS_ERR()判断
		使用PTR_ERR()打印错误码

2. 配置PWM设备

static inline int pwm_config(struct pwm_device *pwm, int duty_ns,int period_ns);
功能:
	改变PWM设备的配置
参数:
	@pwm: 		PWM设备
	@duty_ns: 	高电平的时长(单位:ns)
	@period_ns: 周期时长(单位:ns)
返回值:
	成功,返回0;
	失败,返回错误码;

3. 打开或者关闭PWM设备

static inline int pwm_enable(struct pwm_device *pwm);
功能:开始PWM输出
参数:
	@pwm: PWM设备
返回值:
	成功,返回0
	失败,返回错误码

static inline void pwm_disable(struct pwm_device *pwm);
功能:停止PWM输出
参数:	
	@pwm: PWM设备
返回值:	无

4. 注销PWM设备

void pwm_free(struct pwm_device *pwm);
功能:
	注销PWM设备
参数:
	@pwm:PWM设备
返回值: 无

(二)代码示例

注意:风扇的占空比需要相对较高,否则可能无法转动
本次尝试设置百分之五十的占空比,结果无法转动

此处以风扇为例

#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/pwm.h>
#include <linux/kernel.h>
#include <linux/input-event-codes.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define CNAME "fun_driver"
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
//1s = 1000000000ns

struct cdev *fun_cdev;    //字符设备指针
int major=0;
int minor=0;
dev_t devno;    //设备号
struct class *cls;

struct pwm_fun {
    struct pwm_device *pwm;
    unsigned long period;
};

struct pwm_fun *fun;

int fun_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

ssize_t fun_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
    int ret;
    int value;
    //从用户空间读取频率
    ret = copy_from_user(&value,ubuf,sizeof(value));
    if(ret < 0)
    {
        printk("copy from user error\n");
        return -EIO;
    }
    //配置频率
    if(value == 0)
    {
        //第五步:关闭PWM设备
        pwm_disable(fun->pwm); 
    }else{
        //第三步:配置pwm设备
        fun->period = HZ_TO_NANOSECONDS(value); 
        ret = pwm_config(fun->pwm,fun->period,fun->period); 
        if(ret){
            printk("pwm config error\n");
            return ret;
        }
        //第四步:使能pwm设备
        ret = pwm_enable(fun->pwm); 
        if(ret){
            printk("pwm enable error\n");
            return ret;
        }
    }
    return size;
}

int fun_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    pwm_disable(fun->pwm);
    return 0;
}

const struct file_operations fops = {
    .open = fun_open,
    .write = fun_write,
    .release = fun_close,
};

//设备树匹配成功后会进入probe函数
int fun_probe(struct platform_device *pdev)
{
    int ret;
    struct device *dev = &pdev->dev;
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    
    //第一步:分配PWM对象
    fun = kzalloc(sizeof(*fun),GFP_KERNEL);
    if(!fun)
    {
        printk("fun kzalloc error\n");
        ret = -ENOMEM;
        goto err0;
    }
    //第二步:申请PWM对象
    fun->pwm = pwm_get(dev,NULL);
    if(IS_ERR(fun->pwm))
    {
        printk("failed to request pwm device:%ld\n",PTR_ERR(fun->pwm));
        ret = PTR_ERR(fun->pwm);
        goto err1;
    }
    
    /***注册字符设备***/
    //1.分配对象
    fun_cdev = cdev_alloc();
    if(NULL == fun_cdev){
        printk("cdev alloc error\n");
        ret = -ENOMEM;
        goto err2;
    }
    //2. 对象部分初始化
    cdev_init(fun_cdev,&fops);
    //3. 申请设备号
    if(major > 0){ //静态申请主设备号
        ret = register_chrdev_region(MKDEV(major,minor),1,CNAME);
        if(ret < 0){
            pr_err("register_chrdev_region failed\n");
            goto err3;
        }
    }else{//动态分配设备号
        ret = alloc_chrdev_region(&devno,0,1,CNAME);
        if(ret < 0){
            pr_err("alloc_chrdev_region failed\n");
            goto err3;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    //4. 注册字符设备
    ret = cdev_add(fun_cdev,devno,1);
    if(ret < 0){
        printk("cdev add error\n");
        goto err4;
    }
    /***自动创建设备节点***/
    cls = class_create(THIS_MODULE,CNAME);
    if(IS_ERR(cls))
    {
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto err5;
    }
    dev = device_create(cls,NULL,MKDEV(major,0),NULL,CNAME);
    if(IS_ERR(dev))
    {
        printk("device create error\n");
        ret = PTR_ERR(dev);
        goto err6;
    }

    return 0;
err6:
    class_destroy(cls);
err5:
    cdev_del(fun_cdev);
err4:
    unregister_chrdev_region(MKDEV(major,minor),1);
err3:
    kfree(fun_cdev);
err2:
    pwm_free(fun->pwm);
err1:
    kfree(fun);
err0:
    return ret;
}

//卸载驱动会调用remove函数
int fun_remove(struct platform_device *pdev)
{
    device_destroy(cls,MKDEV(major,minor));
    class_destroy(cls);
    cdev_del(fun_cdev);
    unregister_chrdev_region(MKDEV(major,minor),1);
    kfree(fun_cdev);
    //第六步:关闭PWM设备,释放PWM设备
    pwm_disable(fun->pwm);
    pwm_free(fun->pwm);
    kfree(fun);
    return 0;
}

//使用设备树匹配
const struct of_device_id ofmatch[] = {
    {.compatible = "zyx,pwm-fun",},
    {}
};

struct platform_driver fun_pwm = {
    .probe = fun_probe,
    .remove = fun_remove,
    .driver = {
        .name = "fun",
        .of_match_table = ofmatch,
    }
};

MODULE_DEVICE_TABLE(of,ofmatch);
module_platform_driver(fun_pwm);
MODULE_LICENSE("GPL");
根据提供的引用内容,我无法找到关于Linux PWM子系统的reload操作的具体信息。但是,我可以为您提供一些关于Linux PWM子系统的基本介绍和操作方法。 Linux PWM子系统是用于控制嵌入式系统中的脉冲宽度调制(PWM)信号的一个子系统。它允许用户通过软件来生成和控制PWM信号,用于控制各种外设,如LED灯、电机等。 要使用Linux PWM子系统,您需要进行以下步骤: 1. 确保您的系统已经加载了PWM驱动程序。您可以通过运行以下命令来检查: ```shell ls /sys/class/pwm/ ``` 如果该目录存在,则表示PWM驱动程序已加载。 2. 找到您想要使用的PWM通道。PWM通道通常在/sys/class/pwm/目录下以pwmchipX的形式表示,其中X是通道的编号。您可以运行以下命令来查看可用的PWM通道: ```shell ls /sys/class/pwm/ ``` 3. 配置PWM通道的属性。您可以通过修改/sys/class/pwm/pwmchipX/pwmY/目录下的相应属性文件来配置PWM通道的参数,其中X是PWM通道的编号,Y是具体通道的编号。例如,您可以通过修改/sys/class/pwm/pwmchip0/pwm0/目录下的duty_cycle文件来设置PWM信号的占空比。 4. 启动PWM信号。您可以通过将1写入/sys/class/pwm/pwmchipX/pwmY/enable文件来启动PWM信号,其中X是PWM通道的编号,Y是具体通道的编号。例如,您可以运行以下命令来启动PWM信号: ```shell echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable ``` 请注意,具体的PWM子系统操作可能因不同的硬件平台和Linux内核版本而有所差异。因此,我建议您查阅相关的文档和资料以获取更详细和准确的信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值