现在记录一下rk3399下pwm的驱动编写,下面是内核pwm的API,从开源论坛复制(firefly的开源论坛里面的Wiki教程)
(1)、在要使用 PWM 控制的设备驱动文件中包含以下头文件:
#include
该头文件主要包含 PWM 的函数接口。
(2)、申请 PWM使用
struct pwm_device *pwm_request(int pwm_id, const char *label);
函数申请 PWM。 例如:
struct pwm_device * pwm1 = NULL;pwm0 = pwm_request(1, “firefly-pwm”);
(3)、配置 PWM使用
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
配置 PWM 的占空比, 例如:
pwm_config(pwm0, 500000, 1000000);
(4)、使能PWM 函数
int pwm_enable(struct pwm_device *pwm);
用于使能 PWM,例如:
pwm_enable(pwm0);
(5)控制 PWM 输出主要使用以下接口函数:
struct pwm_device *pwm_request(int pwm_id, const char *label);
功能:用于申请 pwm
void pwm_free(struct pwm_device *pwm);
功能:用于释放所申请的 pwm
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
功能:用于配置 pwm 的占空比
int pwm_enable(struct pwm_device *pwm);
功能:使能 pwm
void pwm_disable(struct pwm_device *pwm);
功能:禁止 pwm
编写该驱动程序的几个流程:
1.在设备树下添加相应内容,内容如下:
pwm_demo{
status = "okay";
compatible = "pwm_test";
buzzer-gpio = <&gpio4 18 GPIO_ACTIVE_LOW>;
};
经测试,gpio一项完全可以不要,compatible为匹配条件,另外在/kernel/arch/arm64/boot/dts/rockchip目录下使用grep "pwm" * -nR命令,搜索结果如下:(关键的内容)
Binary file g3399-development-board.dtb matches
g3399-development-board.dts:234:&pwm3 {
g3399-development-board.dts:237: compatible = "rockchip,remotectl-pwm";
g3399-development-board.dts:238: remote_pwm_id = <3>;
rk3399.dtsi:1234: pwm0: pwm@ff420000 {
rk3399.dtsi:1235: compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
rk3399.dtsi:1237: #pwm-cells = <3>;
rk3399.dtsi:1239: pinctrl-0 = <&pwm0_pin>;
rk3399.dtsi:1241: clock-names = "pwm";
rk3399.dtsi:1245: pwm1: pwm@ff420010 {
rk3399.dtsi:1246: compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
rk3399.dtsi:1248: #pwm-cells = <3>;
rk3399.dtsi:1250: pinctrl-0 = <&pwm1_pin>;
rk3399.dtsi:1252: clock-names = "pwm";
rk3399.dtsi:1256: pwm2: pwm@ff420020 {
rk3399.dtsi:1257: compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
rk3399.dtsi:1259: #pwm-cells = <3>;
rk3399.dtsi:1261: pinctrl-0 = <&pwm2_pin>;
rk3399.dtsi:1263: clock-names = "pwm";
rk3399.dtsi:1267: pwm3: pwm@ff420030 {
rk3399.dtsi:1268: compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
rk3399.dtsi:1270: #pwm-cells = <3>;
rk3399.dtsi:1272: pinctrl-0 = <&pwm3a_pin>;
rk3399.dtsi:1274: clock-names = "pwm";
rk3399.dtsi:1790: vop1_pwm: voppwm@ff8f01a0 {
rk3399.dtsi:1791: compatible = "rockchip,vop-pwm";
rk3399.dtsi:1793: #pwm-cells = <3>;
rk3399.dtsi:1795: pinctrl-0 = <&vop1_pwm_pin>;
rk3399.dtsi:1797: clock-names = "pwm";
rk3399.dtsi:1859: vop0_pwm: voppwm@ff9001a0 {
rk3399.dtsi:1860: compatible = "rockchip,vop-pwm";
rk3399.dtsi:1862: #pwm-cells = <3>;
rk3399.dtsi:1864: pinctrl-0 = <&vop0_pwm_pin>;
rk3399.dtsi:1866: clock-names = "pwm";
rk3399.dtsi:2673: pwm0 {
rk3399.dtsi:2674: pwm0_pin: pwm0-pin {
rk3399.dtsi:2679: vop0_pwm_pin: vop0-pwm-pin {
rk3399.dtsi:2685: pwm1 {
rk3399.dtsi:2686: pwm1_pin: pwm1-pin {
rk3399.dtsi:2691: vop1_pwm_pin: vop1-pwm-pin {
rk3399.dtsi:2697: pwm2 {
rk3399.dtsi:2698: pwm2_pin: pwm2-pin {
rk3399.dtsi:2704: pwm3a {
rk3399.dtsi:2705: pwm3a_pin: pwm3a-pin {
rk3399.dtsi:2711: pwm3b {
rk3399.dtsi:2712: pwm3b_pin: pwm3b-pin {
然后打开rk3399.dtsi,内容如下:(sudo vim rk3399.dtsi)然后搜索,在终端输入 :/pwm
pwm0: pwm@ff420000 {
compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
reg = <0x0 0xff420000 0x0 0x10>;
#pwm-cells = <3>;
pinctrl-names = "default";
pinctrl-0 = <&pwm0_pin>;
clocks = <&pmucru PCLK_RKPWM_PMU>;
clock-names = "pwm";
status = "disabled";
};
pwm1: pwm@ff420010 {
compatible = "rockchip,rk3399-pwm", "rockchip,rk3288-pwm";
reg = <0x0 0xff420010 0x0 0x10>;
#pwm-cells = <3>;
pinctrl-names = "default";
pinctrl-0 = <&pwm1_pin>;
clocks = <&pmucru PCLK_RKPWM_PMU>;
clock-names = "pwm";
status = "disabled";
};
.......................
此处省略n行代码
可以看到:status = "disabled";
这里我使用的pwm1 所以pwm1下面的status = "disabled";改为status = "okay";同时保证其他的驱动程序没有使用到该pwm通道
现在是添加自己的内容了,我在该目录下的g3399-baseboard.dtsi文件中添加如下内容:
pwm_demo{
status = "okay";
compatible = "pwm_test";
buzzer-gpio = <&gpio4 18 GPIO_ACTIVE_LOW>;
};
另外建议使用一下方式去打开pwm,这种方式就不用添加上面的信息以及进行上面的操作,多注意设备树里边的&pwm1这样的地方,如果被其他驱动占用,要么整段注释掉,重新写,要么注释掉其他驱动的代码,紧接着在下面写自己的代码 ,如下:
&pwm1 {
status = "okay";
compatible = "pwm_1";
};
拿pwm3为例,描述怎么修改,如下,曾今踩过的坑,就是以为把下面的status = "okay" 改为status = "disable"就ok了,没领悟到真谛,还是没对设备树进行系统的了解,status = "disable",实际上是关闭pwm3
&pwm3 {
/* status = "okay";
interrupts = ;
compatible = "rockchip,remotectl-pwm";
remote_pwm_id = <3>;
handle_cpu_id = <0>;
ir_key0 {
rockchip,usercode = <0xff40>;
rockchip,key_table =
<0xb2 KEY_POWER>,
<0xe5 KEY_HOME>,
<0xbd KEY_BACK>,
<0xba KEY_MENU>,
<0xf4 KEY_UP>,
<0xf1 KEY_DOWN>,
<0xef KEY_LEFT>,
<0xee KEY_RIGHT>,
<0xf2 KEY_ENTER>,
<0xf0 KEY_REPLY>,
<0xea KEY_VOLUMEUP>,
<0xe3 KEY_VOLUMEDOWN>,
<0xbc KEY_MUTE>,
<0xfe KEY_1>,
<0xfd KEY_2>,
<0xfc KEY_3>,
<0xfb KEY_4>,
<0xfa KEY_5>,
<0xf9 KEY_6>,
<0xf8 KEY_7>,
<0xf7 KEY_8>,
<0xb6 KEY_9>,
<0xff KEY_0>,
<0xed KEY_BACKSPACE>,
<0xaf KEY_POWER>,
<0x8b KEY_VOLUMEUP>,
<0xb9 KEY_VOLUMEDOWN>;
};*/
//上面是红外接收的驱动使用信息
status = "okay";//打开pwm3
compatible = "pwm_3";//匹配条件
pwm_id = <3>;//id
};
另外wiki上面提供的查看pwm注册情况的命令, cat /sys/kernel/debug/pwm 可以查看注册情况,如果相应的pwm通道没打开,则不会显示,如下,没有打开pwm3,下面这里边的信息有周期以及占空比的相关信息,以及打开的通道的注册情况
platform/ff420020.pwm, 1 PWM device
pwm-0 (vdd-log ): requested enabled period: 24997 ns duty: 8243 ns polarity: inverse
platform/ff420010.pwm, 1 PWM device
pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: inverse
platform/ff420000.pwm, 1 PWM device
pwm-0 (backlight ): requested enabled period: 24997 ns duty: 10003 ns polarity: normal
另外,遇到错误代码-517的情况,请参考pwm3错误代码-517
现在第一步就结束了
2.编写设备驱动及Makefile:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIO_LOW 0
#define GPIO_HIGH 1
struct pwm_device *pwm = NULL;
int major;
int gpio;
static struct class *cls;
static ulong arg1;
static int arg2=0;
module_param(arg1,ulong,S_IRUSR);
module_param(arg2,int,S_IRUSR);
static int pwm_open(struct inode *inode, struct file *file)
{
printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);
return 0;
}
static struct file_operations pwm_fops = {
.owner = THIS_MODULE,
.open = pwm_open,
};
static int pwm_probe(struct platform_device *pdev)
{
int ret;
enum of_gpio_flags flag;
struct device_node *led_node = pdev->dev.of_node;
major = register_chrdev(0, "pwm_test", &pwm_fops);
cls = class_create(THIS_MODULE, "pwm_test");
device_create(cls, NULL, MKDEV(major, 0), NULL, "pwm_buzzer");
gpio = of_get_named_gpio_flags(led_node,"buzzer-gpio", 0,&flag);
if (!gpio_is_valid(gpio)){
printk(KERN_INFO "hello: invalid gpio : %d\n",gpio);
return -1;
}
ret = gpio_request(gpio, "buzzer");
if (ret) {
gpio_free(gpio);
return -EIO;
}
gpio_direction_output(gpio, GPIO_HIGH);
gpio_set_value(gpio,arg2);
pwm = pwm_request(1,"pwm");
if(IS_ERR(pwm)){
dev_err(&pdev->dev,"unable to request pwm\n");
printk("pwm err %ld\n",PTR_ERR(pwm));
}
pwm_config(pwm,arg1,100000);
pwm_enable(pwm);
printk(KERN_INFO "pwm sucess\n");
return 0;
}
static struct of_device_id pwm_of_match[] = {
{ .compatible = "pwm_test"},
{}
};
static int pwm_remove(struct platform_device *pdev)
{
gpio_free(gpio);
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "pwm_test");
pwm_free(pwm);
return 0;
}
static struct platform_driver pwm_driver={
.driver = {
.name ="pwm",
.owner =THIS_MODULE,
.of_match_table = pwm_of_match,
},
.probe = pwm_probe,
.remove = pwm_remove,
};
static int __init pwm_init(void)
{
printk(KERN_INFO "Enter %s\n", __FUNCTION__);
return platform_driver_register(&pwm_driver);
return 0;
}
static void __exit pwm_exit(void)
{
platform_driver_unregister(&pwm_driver);
printk(KERN_INFO "Exit pwm_driver\n");
}
module_init(pwm_init);
module_exit(pwm_exit);
MODULE_LICENSE("GPL");
#!/bin/bash
obj-m += pwm.o
KDIR := /rk3399/source/g3399-v7-1-2-20180529/kernel
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko
到此,结束。
使用ls /dev 命令可以看到自己生成的设备结点,另附上本驱动加载命令: insmod pwm.ko arg1=80000 arg2=0