[Arm Linux]PWM驱动——sysfs方式

版权声明:本文为博主原创文章,欢迎转载,转载请注明出处。 https://blog.csdn.net/wangyijieonline/article/details/88710547

基于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

这个程序有两点需要注意,

  1. open的几个文件的读写权限不全是O_RDWR
    在这里插入图片描述在这里插入图片描述
    注意export的节点只有write权限,即O_WRONLY,其他的都是O_RDWR没有问题。

  2. 如果想实现动态改变占空比的方式需要注意,因为我们read,write的都是字符串,所以需要用到
    itoa()atoi()。比如程序里我留了一个变量duty_m,然后用printf打印%d,可以看到这种方式是可行的。
    在这里插入图片描述


算是写完了,相比于传统的ioctl方式,这种方式充分利用了Linux系统一切皆文件的特点,更加灵活。

猜你喜欢

转载自blog.csdn.net/wangyijieonline/article/details/88710547