【嵌入式Linux应用开发】泰山派RK3566+PWM(脉宽调制)

一、前言

嵌入式Linux学习交流群:1005210698
欢迎各位大佬和萌新进来一起学习交流
本文基于泰山派RK3566开发板为例


二. 脉宽调制

1. 什么是PWM

  • PWM(Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术, 广泛应用在测量、通信、工控等方面。

2. PWM的频率

  • 是指在1秒钟内,信号从高电平到低电平再回到高电平的次数,也就是说一秒钟PWM有多少个周期,单位Hz。

3. PWM的周期

  • T=1/f,T是周期,f 是频率。
  • 如果频率为50Hz ,也就是说一个周期是20ms,那么一秒钟就有50个PWM周期。

4. 占空比

  • 是一个脉冲周期内,高电平的时间与整个周期时间的比例,单位是% (0%-100%)

  • 一个周期的长度,如下图所示
    在这里插入图片描述

  • 其中,周期是一个脉冲信号的时间,1s内的周期T次数等于频率f,脉宽时间是指高电平时间。

  • 上图中,脉宽时间占总周期时间的比例,就是占空比。

  • 比方说,周期的时间是10ms,脉宽时间是8ms,那么占空比是8/10= 80%,这就是占空比为80%的脉冲信号。

  • PWM就是脉冲宽度调制,通过调节占空比就可以调节脉冲宽度。

5. PWM原理

  • 假设高电平为5V、低电平则为0V,那么要输出不同的模拟电压就要用到PWM。
  • 通过改变IO口输出的方波的占空比,从而获得使用数字信号模拟成的模拟电压信号。
  • 电压是以一种脉冲序列被加到模拟负载上去的,接通时是高电平1,断开时是低电平0。
  • 接通时直流供电输出,断开时直流供电断开。通过对接通和断开时间的控制,理论上来讲,可以输出任意不大于最大电压值5V的模拟电压。
  • 比方说,占空比为50%那就是高电平时间一半,低电平时间一半。在一定的频率下,就可以得到模拟的2.5V输出电压。那么75%的占空比,得到的电压就是3.75V,如下图所示。

在这里插入图片描述


三. pwm引脚

  • 泰山派RK3566板卡上集成了四个具有pwm功能的GPIO

在这里插入图片描述

  • 对应设备树(/kernel/arch/arm64/boot/dts/rockchip/tspi-rk3566-user-v10-linux.dts),默认全部开启
&pwm8 {
    status = "okay";
};

&pwm9 {
    status = "okay";
};

&pwm14 {
    status = "okay";
};

//pwd 15遥控器
&pwm15 {
	status = "okay";
	compatible = "rockchip,remotectl-pwm";
	remote_pwm_id = <3>;
	handle_cpu_id = <1>;
	remote_support_psci = <0>;
	pinctrl-names = "default";
	pinctrl-0 = <&pwm15m0_pins>;

	//用户自定方法:adb设置输出日志并通过dmesg确定usercode=address与key_table=command
	//echo 1 > sys/module/rockchip_pwm_remotectl/parameters/code_print
	//键值可在 include/dt-bindings/input/linux-event-codes.h 中查找
	ir_key1 {
		rockchip,usercode = <0xff00>;
		rockchip,key_table =
			<0xf2	KEY_MENU>,
			<0xe9	KEY_BACK>,
			<0xe3	KEY_ENTER>,
			<0xe7	KEY_UP>,
			<0xad	KEY_DOWN>,
			<0xf7	KEY_LEFT>,
			<0xa5	KEY_RIGHT>,
			<0xba	KEY_1>,
			<0xb9	KEY_2>,
			<0xb8	KEY_3>,
			<0xbb	KEY_4>,
			<0xbf	KEY_5>,
			<0xbc	KEY_6>,
			<0xf8	KEY_7>,
			<0xea	KEY_8>,
			<0xf6	KEY_9>,
			<0xe6	KEY_0>;
	};
};

四、检查PWM设备

  • 在给板子上点后,可以用以下命令查看pwm信息
ls /sys/class/pwm/

cat /sys/kernel/debug/pwm

在这里插入图片描述

// kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi
pwm5: pwm@fe6e0010 {
		compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
		reg = <0x0 0xfe6e0010 0x0 0x10>;
		#pwm-cells = <3>;
		pinctrl-names = "active";
		pinctrl-0 = <&pwm5_pins>;
		clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;
		clock-names = "pwm", "pclk";
		status = "disabled";
};

pwm8: pwm@fe6f0000 {
		compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
		reg = <0x0 0xfe6f0000 0x0 0x10>;
		#pwm-cells = <3>;
		pinctrl-names = "active";
		pinctrl-0 = <&pwm8m0_pins>;
		clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>;
		clock-names = "pwm", "pclk";
		status = "disabled";
};

pwm9: pwm@fe6f0010 {
		compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
		reg = <0x0 0xfe6f0010 0x0 0x10>;
		#pwm-cells = <3>;
		pinctrl-names = "active";
		pinctrl-0 = <&pwm9m0_pins>;
		clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>;
		clock-names = "pwm", "pclk";
		status = "disabled";
};

pwm14: pwm@fe700020 {
		compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
		reg = <0x0 0xfe700020 0x0 0x10>;
		#pwm-cells = <3>;
		pinctrl-names = "active";
		pinctrl-0 = <&pwm14m0_pins>;
		clocks = <&cru CLK_PWM3>, <&cru PCLK_PWM3>;
		clock-names = "pwm", "pclk";
		status = "disabled";
};
  • pwmchip0为屏幕的背光对应pwm5,可以在 kernel/arch/arm64/boot/dts/rockchip/tspi-rk3566-dsi-v10.dtsi查看
  • 当开启多个pwm设备树插件时,pwm控制器值越小,系统分配的pwmchip越小。

五、pwm控制方式

1. 在板卡上通过shell控制
  • 下面操作以pwm8为例
#将pwm3导出到用户空间
echo 0 > /sys/class/pwm/pwmchip1/export

#设置pwm周期 单位为ns
echo 1000000 > /sys/class/pwm/pwmchip1/pwm0/period

#设置占空比
echo 500000 > /sys/class/pwm/pwmchip1/pwm0/duty_cycle

#设置pwm极性
echo "normal" > /sys/class/pwm/pwmchip1/pwm0/polarity

#使能pwm
echo 1 > /sys/class/pwm/pwmchip1/pwm0/enable

#失能pwm
echo 0 > /sys/class/pwm/pwmchip1/pwm0/enable

#如果需要关闭PWM则将pwm3导出到用户空间
echo 0 > /sys/class/pwm/pwmchip1/unexport

2. 通过代码系统调用
  • 下面操作以pwm8为例
/******************************************************************
 * 个人博客:https://blog.csdn.net/2302_80277720?type=blog
 * 嵌入式Linux学习交流群:1005210698
 * 欢迎各位大佬和萌新来加入交流学习
 * Change Logs:
 * Date           Author       Notes
 * 2025-03-01     喝呜昂黄    first version
 ******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

static char pwm_path[75];

static int pwm_config(const char *attr, const char *val){
    char file_path[100];
    int len;
    int fd;
    int res;

    sprintf(file_path, "%s/%s", pwm_path, attr);
    fd = open(file_path, O_WRONLY);
    if(0 > fd){
        perror("open error");
        return fd;
    }

    len = strlen(val);
    res = write(fd, val, len);
    if(len != res){
        perror("write error");
        close(fd);
        return -1;
    }

    close(fd);
    return 0;
}

static void pwm_export(int fd, char *id){
    char temp[100];
    int res;

    // 如果 pwm0 目录不存在, 则导出
    if(access(pwm_path, F_OK)){ 
        sprintf(temp, "/sys/class/pwm/pwmchip%s/export", id);
        
        fd = open(temp, O_WRONLY);
        if(fd < 0){
            perror("open error");
            exit(-1);
        }
        
        // 导出 pwm
        res = write(fd, "0", 1);
        if(1 != res){
            perror("write error");
            close(fd);
            exit(-1);
        }
    }
}

static void pwm_unexport(int fd, char *id){
    char temp[100];
    int res;

    sprintf(temp, "/sys/class/pwm/pwmchip%s/unexport", id);
    fd = open(temp, O_WRONLY);
    if(fd < 0){
        perror("open error");
        exit(-1);
    }

    // 导出 pwm
    res = write(fd, "0", 1);
    if(1 != res){
        perror("write error");
        close(fd);
        exit(-1);
    }
}

// ./pwm_test 1 1000000 500000
int main(int argc, char **argv){
    int fd;
    int res;

    if(argc != 4){
        fprintf(stderr, "Usage: %s <id> <period> <duty>\n", argv[0]);
        exit(-1);
    }

    printf("PWM config: id<%s>, period<%s>, duty<%s>\n",argv[1], argv[2],argv[3]);
    // pwm 路径
    sprintf(pwm_path, "/sys/class/pwm/pwmchip%s/pwm0", argv[1]);
    // 导出 pwm
    pwm_export(fd, argv[1]);
    
    // 配置 PWM 周期
    res = pwm_config("period", argv[2]);
    if(res)
        exit(-1);
        
    // 配置占空比
    res = pwm_config("duty_cycle", argv[3]);
    if(res)
        exit(-1);
    // 使能 pwm
    pwm_config("enable", "1");
    
    printf("按任意键终止程序\n");
    getchar();
    // 失能 pwm
    pwm_config("enable", "0");
    
    // 放回 pwm
    pwm_unexport(fd, argv[1]);
    
    return 0;
}

六、参考

本片文章参考野火的《pwm(脉宽调制)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喝呜昂_黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值