PWM(Pulse Width Modulation),是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%。PWM是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。PWM信号一般下图所示:
经常见到的就是交流调光电路(手机充电的呼吸灯),也可以说是无级调速,高电平占多一点,也就是占空比大一点亮度就亮一点,占空比小一点亮度就没有那么亮,前提是PWM的频率要大于我们人眼识别频率(大约80Hz以上最好)。在电机驱动、无源蜂鸣器驱动、LCD屏幕背光调节、逆变电路等都会有应用。
PWM一共有三个重要的参数:频率、 周期、 占空比:
示例:
周期:代表一个脉冲信号的时间,1s周期的个数为频率
脉宽时间:高电平的时间,脉宽时间(也就是高电平的时间)占总周期的时间为占空比
我们可以知道开发板上有4路PWM分别为:
PWM1 ---> backlight //LCD背光
PWM2 ---> beep //蜂鸣器
PWM7,PWM8 ---> 40pin扩展 //需要使能开启
想要使能40pin扩展口的PWM7和8的话,需要修改/run/media/mmcblk1p1
路径下的config.txt文件如下:
# Enable PWM overlays, PWM8 conflict with UART8(NB-IoT/4G module)
dtoverlay_pwm=7 8
修改后重启系统,和gpio一样通过sysfs方式进行操作,在/sys/class/pwm
路径下存在着我们PWM控制器的文件pwmchipN,我们可以在文件夹中看到几个比较重要的属性文件:
echo 0 > export
导出后可以在pwmchipN文件中看见我们导出的一个pwm0的文件夹。
注意:echo导出的值必须小于我们npwm中看见的值
echo 0 > unexport
注意:export文件和unexport文件都是只写的、没有读权限
pwm_test.c
/*********************************************************************************
* Copyright: (C) 2023 Deng Yonghao
* All rights reserved.
*
* Filename: pwm_test.c
* Description: This file use to test pwm
*
* Version: 1.0.0(2023年03月17日)
* Author: Deng Yonghao
* ChangeLog: 1, Release initial version on "2023年03月17日 11时54分08秒"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include
#include
static char pwm_path[100];
static int pwm_config(const char *attr, const char *val);//pwm配置函数声明,attr:属性文件名字,val属性的值(字符串)
int main(int argc, char **argv)
{
char temp[100];
int fd = -1;
/*传参校验*/
if(4 != argc)
{
printf("usage: %s \n" , argv[0]);
exit(-1);//exit(0) 表示进程正常退出 exit(非0)表示异常退出
}
/*打印配置信息*/
printf("PWM config: id<%s>, period<%s>, duty<%s>\n", argv[1], argv[2], argv[3]);
/* 导出pwm 首先确定最终导出的文件夹路径*/
memset(pwm_path, 0, sizeof(pwm_path));
snprintf(pwm_path, sizeof(pwm_path), "/sys/class/pwm/pwmchip%s/pwm0", argv[1]);
//如果pwm0目录不存在, 则导出
memset(temp, 0, sizeof(temp));
if(access(pwm_path, F_OK))
{
snprintf(temp, sizeof(temp) , "/sys/class/pwm/pwmchip%s/export", argv[1]);
if(0 > (fd = open(temp, O_WRONLY)))
{
printf("open pwmchip%s error\n", argv[1]);
exit(-1);
}
//导出pwm0文件夹
if(1 != write(fd, "0", 1))
{
printf("write '0' to pwmchip%s/export error\n", argv[1]);
close(fd);
exit(-2);
}
close(fd);
}
/*配置PWM周期*/
if(pwm_config("period",argv[2]))
{
exit(-1);
}
/*配置占空比*/
if(pwm_config("duty_cycle", argv[3]))
{
exit(-1);
}
/*使能pwm*/
pwm_config("enable", "1");
return 0;
}
static int pwm_config(const char *attr, const char *val)
{
char file_path[200];
int len;
int fd =-1;
if(attr == NULL || val == NULL)
{
printf("[%s] argument error\n", __FUNCTION__);
return -1;
}
memset(file_path, 0, sizeof(file_path));
snprintf(file_path, sizeof(file_path), "%s/%s", pwm_path, attr);
if(0 > (fd = open(file_path, O_WRONLY)))
{
printf("[%s] open %s error\n", __FUNCTION__, file_path);
return fd;
}
len = strlen(val);
if(len != write(fd, val, len))
{
printf("[%s] write %s to %s error\n", __FUNCTION__, val, file_path);
close(fd);
return -2;
}
close(fd);
return 0;
}
代码如下(示例):
CC=arm-linux-gnueabihf-gcc
APP_NAME1=pwm_test
all:clean
@${CC} ${APP_NAME1}.c -o ${APP_NAME1}
clean:
@rm -f ${APP_NAME1}
首先,通过在通过make命令编译好我们的测试程序,然后通过如下命令把编译好的可执行程序下载到开发板上,并赋予可执行权限:
tftp -gr pwm_test 192.168.137.101
chmod a+x pwm_test
然后我们运行此测试程序来测试PWM8管脚:
root@igkboard:~# ./pwm_test 3 10000 1000
root@igkboard:~# ./pwm_test 3 10000 3000
root@igkboard:~# ./pwm_test 3 10000 7000
root@igkboard:~# ./pwm_test 3 10000 9000
执行时可以看见led灯会随着我们的参数不同而变化,参数第一个代表pwmchip3,第二个为周期,越大越暗,第三个为占空比,越大灯越亮。
测试视频可见:PWM管脚led测试视频
然后我们可以测试蜂鸣器:
参数第一个代表pwmchip1蜂鸣器,第二个周期越大越小声,第三个占空比越大灯越大声,测试成功。