查看kernel的dts文件:
./build.sh kernel
在文件目录Z:\kernel\arch\arm64\boot\dts\rockchip下找到rk3308b-roc-cc-plus-amic_emmc然后在里面添加pwm配置,如下所示:配置了一个pwm10
pwm-roll {
status = "okay";
compatible = "pwm-roll";
pwms = <&pwm10 0 25000 0>;
};
在下面添加有&符号后面添加一个:
&pwm10 {
status = "okay";
pinctrl-names = "active";
pinctrl-0 = <&pwm10_pin_pull_down>;
};
在Z:\kernel\drivers(自己创建的文件夹):
创建一个如下的文件夹:
Z:\kernel\drivers的makefile最后添加:
obj-y += (自己文件夹名字)/pwm_roll/
切换到:
除了pwm_roll文件以外都可以在drivers文件中找到,复制过来后只用修改Makefile和Kconfig,将其串联进kernel中:
Makefile文件如下
obj-y += pwm_roll.o
Kconfig文件如下
config PWM_ROLL
bool "PWM_ROLL"
default y
depends on PWM_SYSFS
---help---
Serial Port Transmitter support
编写pwm_roll.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pwm.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/roll.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
static struct pwm_roll roll_pwm;
static const struct of_device_id pwm_roll_match[] = {
{ .compatible = "pwm-roll", },
{ },
};
MODULE_DEVICE_TABLE(of, pwm_roll_match);
static ssize_t pwm_roll_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
int tmp,ret;
void __user *argp = (void __user *)arg;
switch(cmd)
{
case ROLL_PWM_SET_PERIOD:
pwm_disable(roll_pwm.pwm);
printk("roll_pwm_SET_DUTY.");
ret =copy_from_user(&tmp, argp, sizeof(int));
if(ret)
{
printk("copy_to_user failed.");
return -EINVAL;
}
//频率= 1000,000,000 /time
roll_pwm.period_ns =(int)( ROLL_PWM_PERIOD/tmp);
roll_pwm.period = tmp;
pwm_set_period(roll_pwm.pwm,roll_pwm.period_ns);
pwm_enable(roll_pwm.pwm);
printk("ROLL_PWM_SET_PERIOD. =%d hz,period_ns = %dns\n",roll_pwm.period,roll_pwm.period_ns);
break;
case ROLL_PWM_SET_DUTY:
ret =copy_from_user(&tmp, argp, sizeof(int));
printk("ret%d\n",ret);
if(ret)
{
printk("copy_to_user failed.");
return -EINVAL;
}
if(abs(tmp) > roll_pwm.period_ns)
{printk("temp%d\n",tmp);
roll_pwm.duty_cycle = abs(tmp);
pwm_config(roll_pwm.pwm, roll_pwm.period_ns, roll_pwm.period_ns);
}
else
{printk("temp%d\n",tmp);
roll_pwm.duty_cycle = abs(tmp);
pwm_config(roll_pwm.pwm, roll_pwm.duty_cycle, roll_pwm.period_ns);
}
printk("ROLL_PWM_SET_PDUTY = %d ns.\n",roll_pwm.duty_cycle);
break;
case ROLL_PWM_GET_DUTY:
ret = copy_to_user(argp, &roll_pwm.duty_cycle, sizeof(unsigned int));
if(ret)
{
printk("copy_to_user failed.");
return -EINVAL;
}
printk("ROLL_PWM_GET_DUTY %d ns\n.",roll_pwm.duty_cycle);
break;
case ROLL_PWM_GET_PERIOD:
ret = copy_to_user(argp, &roll_pwm.period, sizeof(unsigned int));
if(ret)
{
printk("copy_to_user failed.\n");
return -EINVAL;
}
printk("ROLL_PWM_GET_PERIOD %d hz.\n",roll_pwm.period);
break;
case ROLL_PWM_ENABLE:
printk("ROLL_PWM_ENABLE.\n");
pwm_config(roll_pwm.pwm, 0, roll_pwm.period_ns);
pwm_enable(roll_pwm.pwm);
break;
case ROLL_PWM_DISABLE:
pwm_config(roll_pwm.pwm, 0, roll_pwm.period_ns);
pwm_disable(roll_pwm.pwm);
printk("ROLL_PWM_DISABLE.\n");
break;
}
return 0;
}
static int roll_pwm_open(struct inode *inode, struct file *filp)
{
printk("roll pwm open \n");
return 0;
}
static int roll_pwm_release(struct inode *inode, struct file *filp)
{
printk("roll pwm release \n");
return 0;
}
static ssize_t roll_pwm_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
{
printk("roll pwm read \n");
return 0;
}
static ssize_t roll_pwm_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
{
printk("roll pwm write len=%ld\n",len);
return 0;
}
static struct file_operations ROLL_PWM_fops = {
.owner = THIS_MODULE,
.open = roll_pwm_open,
.release = roll_pwm_release,
.write = roll_pwm_write,
.read = roll_pwm_read,
.unlocked_ioctl = pwm_roll_ioctl,
};
struct miscdevice ROLL_PWM_dev =
{
.minor = MISC_DYNAMIC_MINOR,
.fops = &ROLL_PWM_fops,
.name = "roll_pwm",
};
static int pwm_roll_probe(struct platform_device *pdev)
{
int ret =0;
struct pwm_device *pwm;
static struct pwm_roll *roll = &roll_pwm;
struct device *dev;
dev =&pdev->dev;
if(!dev->of_node)
{
dev_err(dev,"dev->of_node is not \n");
return -ENODEV;
}
pwm = devm_pwm_get(dev, NULL);
if(IS_ERR(pwm))
{
dev_err(dev,"get pwm error\n");
// goto ROLL_IRQ_ERROR;
}
ret = pwm_adjust_config(pwm);
if(ret)
{
misc_register(&ROLL_PWM_dev);
//goto ROLL_IRQ_ERROR;
}
pwm_set_period(pwm,10000);
pwm_enable(pwm);
roll->period = (int)(ROLL_PWM_PERIOD/10000);
roll->duty_cycle = 0;
roll->period_ns = 10000;
roll->pwm = pwm;
ret =misc_register(&ROLL_PWM_dev);
if(ret != 0)
{
dev_err(dev,"failed misc register\n");
//goto ROLL_MISC_ERROR;
}
return 0;
}
static struct platform_driver roll_driver = {
.probe = pwm_roll_probe,
.driver = {
.name = "pwm-roll",
.of_match_table = of_match_ptr(pwm_roll_match),
},
};
module_platform_driver(roll_driver);
MODULE_DESCRIPTION("PWM ROLL");
MODULE_AUTHOR("Y <sean@mess.org>");
MODULE_LICENSE("GPL");
roll.h如下:该文件放在Z:\kernel\include\linux下
#include <linux/pwm.h>
#define ROLL_PWM_SET_DUTY _IOW('Y', 0x11, unsigned int) //设置占空比
#define ROLL_PWM_SET_PERIOD _IOW('Y', 0x12, unsigned int) //设置频率
#define ROLL_PWM_GET_DUTY _IOR('Y', 0x13, unsigned int) //获取当前占空比
#define ROLL_PWM_GET_PERIOD _IOR('Y', 0x14, unsigned int) //获取频率
#define ROLL_PWM_ENABLE _IO('Y', 0x17) //开启pwm
#define ROLL_PWM_DISABLE _IO('Y', 0x18) //关闭pwm
#define ROLL_PWM_PERIOD 1000000000 //用于计算 频率 = 1000,000,000 /time
struct pwm_roll {
unsigned int period; //频率
unsigned int duty_cycle; //占空比
unsigned int period_ns; //占空比周期
struct pwm_device *pwm;
};
然后运行./build.sh重新进行编译,将我们得到的镜像烧录到RK3308的板子上。
编写运行程序demo.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#define ROLL_PWM_SET_DUTY _IOW('Y', 0x11, unsigned int) //设置占空比
#define ROLL_PWM_SET_PERIOD _IOW('Y', 0x12, unsigned int) //设置频率
#define ROLL_PWM_GET_DUTY _IOR('Y', 0x13, unsigned int) //获取当前占空比
#define ROLL_PWM_GET_PERIOD _IOR('Y', 0x14, unsigned int) //获取频率
#define ROLL_PWM_ENABLE _IO('Y', 0x15) //开启pwm
#define ROLL_PWM_DISABLE _IO('Y', 0x16) //关闭pwm
#define DEV_NAME "/dev/roll_pwm"
int main(int argc, char *argv[])
{
int fd;
int duty, period;
if(argc <2)
{
printf("no value\n");
return 1;
}
printf("argc =%d\n",argc);
fd = open(DEV_NAME,O_RDWR);
if(fd < 0)
{
printf("hello world\n");
}
switch (*(argv[1] +1))
{
case 'p':
period=atoi(argv[2]);
ioctl(fd, ROLL_PWM_SET_PERIOD, &period);
printf("period = %d\n",period);
break;
case 'd':
duty=atoi(argv[2]);
ioctl(fd, ROLL_PWM_SET_DUTY, &duty);
printf("duty =%d\n",duty);
break;
case 'a':
ioctl(fd, ROLL_PWM_GET_PERIOD, &period);
printf("get period =%d\n",period);
break;
case 'b':
ioctl(fd, ROLL_PWM_GET_DUTY, &duty);
printf("get duty =%d\n",duty);
break;
default:
break;
}
close(fd);
return 0;
}
这个的运行方法参照led的运行方法:https://blog.csdn.net/Wu996/article/details/120632573
使用demo -p 10000设置周期
使用demo -d 500000设置占空比
使用demo -a 查看周期
使用demo -b 查看设置的周期
最后用cat sys/kernel/debug/pwm就可以查看我们设置的pwm了
这样一个完成的pwm就设置好了,使用示波器会呈现一个方波