基于Linux kernel 4.9.123,利用的是kernel自带的drivers/pwm/pwm-imx.c
来实现。
Linux 2.6以后的内核所支持的sysfs文件系统被映射到/sys
目录上。Linux设备驱动模型中出现的总线、驱动和设备都可以在sysfs文件系统中找到对应的节点。当内核检测到在系统中出现了新设备后,内核会在sysfs文件系统中为该新设备生成一项新的记录。
1, 修改menuconfig
Symbol: PWM_IMX [=y]
│ Type : tristate
│ Prompt: i.MX PWM support
│ Location:
│ -> Device Drivers
│ -> Pulse-Width Modulation (PWM) Support (PWM [=y])
│
│ Defined at drivers/pwm/Kconfig:191
│ Depends on: PWM [=y] && (ARCH_MXC || ARCH_MXC_ARM64 [=y])
2, 修改设备树dts
添加引用pwm4节点的新节点pwm4
/&pwm4 {
compatible = "fsl,imx27-pwm";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm4>;
};
3, 添加pinctrl_pwm4
pinctrl_pwm4: pwm4grp {
fsl,pins = <
MX8MM_IOMUXC_GPIO1_IO15_PWM4_OUT 0x06
>;
};
4, 检查dts的其他device是否用到相同的复用端口,如果有用到,暂时注释掉
5, 编译内核拷贝到SD卡,保证内核正常启动
6, 通过用户层接口查看现象
1.echo 0 > /sys/class/pwm/pwmchip0/export
/*设置PWM4输出,调出pwm0目录下设备节点,用于以下配置 */
2.echo 1000000 >/sys/class/pwm/pwmchip0/pwm0/period
/*设置PWM4一个周期的持续时间,单位为ns,即1K Hz */
3.echo 500000 >/sys/class/pwm/pwmchip0/pwm0/duty_cycle
/*设置一个周期中的”ON”时间,单位为ns,即占空比=duty_cycle/period=50% */
4.echo 1 >/sys/class/pwm/pwmchip0/pwm0/enable
/*设置PWM4使能 */
7, 通过文件IO操作这些接口
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#define dbmsg(fmt, args ...) printf("%s[%d]: "fmt"\n", __FUNCTION__, __LINE__,##args) //__FILE__,
#define DUTY "duty"
#define PERIOD "1000000"
#define DUTYCYCLE "500000"
#define LENGTH 100
int fd_period = 0,fd_duty = 0,fd_enable = 0,duty_m = 0;
int usage()
{
printf("usage:\n");
printf("./pwm-sysfs-test duty <0/1> : 0-->static; 1-->dynamic \n");
return 0;
}
int pwm_setup()
{
int fd,ret;
fd = open("/sys/class/pwm/pwmchip0/export", O_WRONLY);
if(fd < 0)
{
dbmsg("open export error\n");
return -1;
}
ret = write(fd, "0", strlen("0"));
if(ret < 0)
{
dbmsg("creat pwm0 error\n");
return -1;
}else
dbmsg("export pwm0 ok\n");
fd_period = open("/sys/class/pwm/pwmchip0/pwm0/period", O_RDWR);
fd_duty = open("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", O_RDWR);
fd_enable = open("/sys/class/pwm/pwmchip0/pwm0/enable", O_RDWR);
if((fd_period < 0)||(fd_duty < 0)||(fd_enable < 0))
{
dbmsg("open error\n");
return -1;
}
ret = write(fd_period, PERIOD,strlen(PERIOD));
if(ret < 0)
{
dbmsg("change period error\n");
return -1;
}else
dbmsg("change period ok\n");
ret = write(fd_duty, DUTYCYCLE, strlen(DUTYCYCLE));
if(ret < 0)
{
dbmsg("change duty_cycle error\n");
return -1;
}else
dbmsg("change duty_cycle ok\n");
ret = write(fd_enable, "1", strlen("1"));
if(ret < 0)
{
dbmsg("enable pwm0 error\n");
return -1;
}else
dbmsg("enable pwm0 ok\n");
duty_m = atoi(DUTYCYCLE)/2;
printf("duty_m: %d \n",duty_m);
return 0;
}
int main ( int argc, char *argv[] )
{
int ret;
int num;
if(argc < 2)
{
usage();
return -1;
}
if(strncmp(argv[1],DUTY, sizeof(DUTY)) == 0)
{
dbmsg("%s", DUTY);
if(argc != 3)
{
usage();
return -1;
}
pwm_setup();
}
return 0;
}
交叉编译一下
aarch64-linux-gnu-gcc pwm-sysfs-test.c -o pwm-sysfs-test
把交叉编译好的可执行程序pwm-sysfs-test
拷贝到开发板上,运行一下
./pwm-sysfs-test duty 0
这个程序有两点需要注意,
-
open的几个文件的读写权限不全是O_RDWR
注意export的节点只有write权限,即O_WRONLY,其他的都是O_RDWR没有问题。扫描二维码关注公众号,回复: 5650706 查看本文章 -
如果想实现动态改变占空比的方式需要注意,因为我们read,write的都是字符串,所以需要用到
itoa()
和atoi()
。比如程序里我留了一个变量duty_m
,然后用printf
打印%d
,可以看到这种方式是可行的。
算是写完了,相比于传统的ioctl方式,这种方式充分利用了Linux系统一切皆文件的特点,更加灵活。