[Arm]PWM驱动——sysfs方式

基于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系统一切皆文件的特点,更加灵活。

### 实现RK3588平台上的IR_OUT功能 #### 设备树配置 为了使能 IR_OUT 功能,在设备树 (DTS) 中需要定义红外发射器的相关节点。通常这涉及到 GPIO 和 PWM 控制器的设置,因为大多数红外发射电路通过这些接口控制。 对于 Rockchip RK3588 而言,具体的 DTS 文件路径可能是 `arch/arm64/boot/dts/rockchip/rk3588.dtsi` 或者特定板级文件如 `rk3588-evb.dts`。下面是一个假设性的例子来展示如何在 DTS 中添加 IR_OUT 支持: ```dts &gpio { ir_out_gpio: irout { compatible = "gpio-leds"; pinctrl-names = "default"; pinctrl-0 = <&ir_out_pins>; status = "okay"; ir_out { label = "ir-out"; gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>; /* 假设GPIO编号 */ }; }; }; &pwm { ir_pwm: pwm@0 { compatible = "pwm-rockchip"; pwms = <&pwm0 0 50000 0>; /* 设置PWM频率和极性 */ status = "okay"; }; }; ``` 上述代码片段展示了如何利用 GPIO 来驱动简单的开/关型红外发射管以及使用 PWM 进行更复杂的调制信号输出[^1]。 #### 用户空间程序开发 一旦硬件层面准备就绪,则可以在应用程序层面上编写相应的逻辑去操作这个外设。可以采用 Linux 下的标准方法——sysfs 接口或者直接读写 `/dev/pwm*` 字符设备来进行编程控制。 这里给出一段 Python 示例脚本用于发送简单脉冲序列给连接到指定引脚上的红外LED: ```python import time from periphery import GPIO, PWM def send_ir_signal(gpio_pin=19, frequency_hz=38e3, duty_cycle_percent=25): try: gpio = GPIO("/dev/gpiochip0", gpio_pin, "out") # 初始化GPIO对象 with PWM(0, 0) as pwm: # 使用第一个PWM通道 pwm.frequency = frequency_hz # 设置载波频率为38kHz while True: pwm.enable() # 开启PWM输出 for _ in range(10): # 发送多个周期 gpio.write(True) time.sleep(duty_cycle_percent / 100 * (1/frequency_hz)) gpio.write(False) time.sleep((1-duty_cycle_percent)/100*(1/frequency_hz)) pwm.disable() break # 只执行一次循环后退出 finally: if 'gpio' in locals(): del gpio # 清理资源 send_ir_signal() ``` 这段Python代码依赖于第三方库`periphery`来简化对底层硬件的操作。请注意实际应用中可能还需要考虑更多细节比如错误处理机制等[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

山猫Show

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

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

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

打赏作者

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

抵扣说明:

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

余额充值