PWM
PWM简介⭕
**PWM(Pulse Width Modulation,脉冲宽度调制)**是一种利用微处理器的数字输出对模拟电路进行控制的技术。通过改变脉冲的占空比,可以控制模拟电路的输出电压或电流。PWM技术广泛应用于电机控制、灯光调节、音频信号生成等领域。
PWM频率和占空比⭕
周期:PWM信号的重复周期,即一个PWM信号从高电平到低电平再到高电平的时间间隔。
频率:PWM信号的重复频率,即每秒钟PWM信号的重复次数。频率与周期成反比,频率 = 1 / 周期。
占空比:PWM信号的脉冲宽度与周期的比值,即高电平时间占整个周期的比例。占空比可以取0到1之间的任意值。
PWM的应用⭕
面积等效原理:
冲量相等而形状不同的窄宽脉冲加在具有惯性的环节上,其效果基本相同
- 冲量相等而形状不同是指面积相等
- 惯性环节在电路和系统分析中,当输入信号发生变化时,其输出不会立即跟随变化,而是需要经过一段时间后才能逐渐达到新的稳态值。
通俗的说:电压不同,时间不同的俩个信号,当他们的电压和时间的乘积相等的时候,输出的波形信号是相同的
pwm的子系统框架图
两种驱动方法:
- 直接在应用层操作
sys/class/pwm
- 编写驱动程序后在应用层调用
PWM驱动编写:⭕
Linux内描述一个PWM控制器的结构体:
struct pwm_chip {
struct device *dev;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
/* only used internally by the PWM framework */
struct list_head list;
struct pwm_device *pwms;
ANDROID_KABI_RESERVE(1);
};
PWM常有API:
pwm_config
函数
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) //改变pwm配置
参数 | 作用 |
---|---|
*pwm | pwm_device |
duty_ns | 占空比 |
period_ns | 周期 |
成功返回0,失败返回负数
pwm_set_polarity
函数
int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) //设置pwm极性
参数 | 作用 |
---|---|
*pwm | pwm_device |
polarity | pwm极性 |
成功返回0,失败返回负数
pwm_enable
函数
int pwm_enable(struct pwm_device *pwm) //使能pwm
参数 | 作用 |
---|---|
*pwm | pwm_device |
成功返回0,失败返回负数
pwm_disable
函数
int pwm_disable(struct pwm_device *pwm) //禁止pwm
参数 | 作用 |
---|---|
*pwm | pwm_device |
成功返回0,失败返回负数
pwm_request
函数
struct pwm_device *pwm_request(int pwm, const char *label) //申请pwm
参数 | 作用 |
---|---|
pwm | pwm号 |
label | pwm标签 |
成功返回pwm_device,失败返回NULL
pwm_free
函数
void pwm_free(struct pwm_device *pwm) //释放pwm
参数 | 作用 |
---|---|
*pwm | pwm_device |
无返回
devm_pwm_get
函数
int devm_pwm_get(struct device *dev, const char *con_id)//获取PWM设备句柄
参数 | 作用 |
---|---|
*dev | 设备 |
con_id | pwm标签 |
成功返回pwm_device句柄,失败返回负数
driver层的使用:⭕
使用platform_driver
的实现驱动注册,匹配设备树节点即可。
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/pwm.h>
struct pwm_device *pwm_dev;
dev_t dev_num; // 设备号
static int major = 0; /* 主设备号, 0 表示由系统分配 */
struct class *class; // 类和对象
static int pwm_driver_open(struct inode *, struct file *)
{
pwm_config(pwm_dev, 500000, 2000000); // 周期2000000ns,占空比500000ns
pwm_set_polarity(pwm_dev, PWM_POLARITY_NORMAL); // 设置极性
pwm_enable(pwm_dev); // 启动PWM
return 0;
}
static int pwm_driver_release(struct inode *, struct file *)
{
pwm_free(pwm_dev);
return 0;
}
static struct file_operations pwm_fops = {
.owner = THIS_MODULE,
.open = pwm_driver_open,
.release = pwm_driver_release};
static int pwm_driver_probe(struct platform_device *pdev)
{
int ret = 0;
pwm_dev = devm_of_pwm_get(&pdev->dev, dev->dev.of_node, NULL);
if (IS_ERR(pwm_dev))
{
printk("get pwm device failed\n");
return -1;
}
// 添加字符设备节点
int err;
major = register_chrdev(0, "hello", &pwm_fops);
class = class_create(THIS_MODULE, "hello_class");
err = PTR_ERR(class);
if (IS_ERR(class))
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "hello");
return -1;
}
device_create(class, NULL, MKDEV(major, 0), NULL, "hello"); /* 设备节点/dev/hello创建 */
return 0;
}
static int pwm_remove(struct platform_device *pdev)
{
unregister_chrdev(major, "hello");
class_destroy(class);
if (!pwm_dev)
{
pwm_free(pwm_dev);
}
return 0;
}
static const struct of_device_id pwm_of_match[] = {
{.compatible = "pwm_test"},
{},
};
MODULE_DEVICE_TABLE(of, pwm_of_match);
static struct platform_driver pwm_driver = {
.driver = {
.name = "pwm_test",
.of_match_table = pwm_of_match,
},
.probe = pwm_driver_probe,
.remove = pwm_remove,
};
static int __init pwm_init(void)
{
return platform_driver_register(&pwm_driver);
}
static void __exit pwm_exit(void)
{
platform_driver_unregister(&pwm_driver);
}
module_init(pwm_init);
module_exit(pwm_exit);
MODULE_LICENSE("GPL");
模拟PWM
使用模拟PWM,即使用定时器来模拟PWM信号。给GPIO配置为输出模式,然后通过定时器来控制GPIO的电平变化,从而实现PWM信号的产生。
配置设备树,指定一个LED的GPIO引脚
led {
compatible = "gpio-led";
gpios = <&gpio0 9 GPIO_ACTIVE_LOW>;
};
在驱动中,通过与设备树probe获取GPIO引脚,然后配置为输出模式,并使用定时器来控制GPIO的电平变化,从而实现PWM信号的产生。
高精度定时器
普通定时器的时钟频率可以设置在 100Hz 到 1000Hz 之间,所以精度只能限制在毫秒级别。所以无法满足精度较高的场景当中,为此 Linux 提供了高精度定时器,可以提供纳秒级别的精度。
struct hrtimer结构体
// include/linux/hrtimer.h高精度定时器
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;//定时时间
enum hrtimer_restart (*function)(struct hrtimer *);//超时服务函数
struct hrtimer_clock_base *base;
u8 state;
u8 is_rel;
u8 is_soft;
u8 is_hard;
ANDROID_KABI_RESERVE(1);
};
// include/linux/timer.h普通定时器
struct timer_list {
struct list_head entry;
unsigned long expires; //定时时间
void (*function)(unsigned long);//超时服务函数
unsigned long data;
unsigned int flags;
int slack;
};
hrtimer_init函数
//初始化一个定时器
void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
enum hrtimer_mode mode);
参数 | 作用 |
---|---|
timer | 要初始化的定时器 |
which_clock | 定时器所使用的时钟类型,比如 CLOCK_REALTIME、CLOCK_MONOTONIC 等 |
hrtimer_mode | 定时器模式,比如 HRTIMER_MODE_REL、HRTIMER_MODE_ABS 等 |
ktime_set函数
//设置定时时间
ktime_t ktime_set(const <error-type> secs, const unsigned long nsecs);
参数 | 作用 |
---|---|
secs | 秒 |
nsecs | 纳秒 |
hrtimer_start函数
//启动定时器
int hrtimer_start(struct hrtimer *timer, ktime_t time, const enum hrtimer_mode mode);
参数 | 作用 |
---|---|
timer | 要启动的定时器 |
time | 定时时间 |
mode | 定时器模式 |
hrtimer_forward函数
//定时器延时
void hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
参数 | 作用 |
---|---|
timer | 要延时的定时器 |
now | 当前时间 |
interval | 延时时间 |
hrtimer_cancel函数
//取消定时器
int hrtimer_cancel(struct hrtimer *timer);
参数 | 作用 |
---|---|
timer | 要取消的定时器 |
Source_code
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/pwm.h>
struct pwm_data{
int sum_conut; // 总计数
int high_count; // 高电平计数
struct gpio_desc *gpio; // GPIO
struct hrtimer pwm_timer; // 定时器
int time; // 定时时间
};
struct pwm_data *data;
struct pwm_device *pwm_dev;
dev_t dev_num; // 设备号
static int major = 0; /* 主设备号, 0 表示由系统分配 */
struct class *class; // 类和对象
enum hrtimer_restart pwm_timer_func(struct hrtimer *timer){
//container_of 宏来从一个结构体成员的指针中获取包含它的结构体指针
/*
timer 是指向 pwm_timer 成员的指针。
struct pwm_data 是包含 pwm_timer 成员的结构体类型。
pwm_timer 是 struct pwm_data 结构体中的一个成员。
*/
static int timer_count = 0;
struct pwm_data *mydata = container_of(timer, struct pwm_data, pwm_timer);
if(timer_count == mydata->sum_conut){
gpiod_set_value(mydata->gpio, 1);
timer_count = 0;
}
if(timer_count == mydata->high_count){
gpiod_set_value(mydata->gpio, 0);
}
timer_count++
if(mydata->high_count == 0){
timer_count = 0;
}
hrtimer_forward(timer, timer->_softexpires, mydata->time); // 定时器重新启动, 调整定时器的到期时间为当前时间加上mydata->time
return HRTIMER_RESTART; // 重启定时器
}
static int pwm_driver_open(struct inode *, struct file *)
{
return 0;
}
static int pwm_driver_release(struct inode *, struct file *)
{
return 0;
}
static long pwm_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
}
static ssize_t pwm_driver_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){
int ret = 0;
int kbuf[2];
ret = copy_from_user(kbuf, buf, count);
data->sum_conut = kbuf[0]; //总计数,周期
data->high_count = kbuf[1];//高电平计数,占空比
return ret;
}
static struct file_operations pwm_fops = {
.owner = THIS_MODULE,
.open = pwm_driver_open,
.release = pwm_driver_release,
.unlocked_ioctl = pwm_driver_ioctl,
.write = pwm_driver_write,
};
static int pwm_driver_probe(struct platform_device *pdev)
{
data = kmalloc(sizeof(struct pwm_data), GFP_KERNEL);
data->sum_conut = 20;
data->high_count = 10;
// 添加字符设备节点
int err;
major = register_chrdev(0, "hello", &pwm_fops);
class = class_create(THIS_MODULE, "hello_class");
err = PTR_ERR(class);
if (IS_ERR(class))
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "hello");
return -1;
}
device_create(class, NULL, MKDEV(major, 0), NULL, "hello"); /* 设备节点/dev/hello创建 */
data->gpio = gpiod_get(&pdev->dev,"gpio-led",GPIOF_OUT_INIT_HIGH) //获取GPIO
gpiod_set_value(data->gpio, 1);//设置GPIO高电平
data->time = ktime_set(0,1000000); //定时器时间1ms,返回总时间
hrtimer_init(&data->pwm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化定时器,CLOCK_MONOTONIC表示定时器从系统启动开始计时,HRTIMER_MODE_REL表示定时器从当前时间开始计时
data->pwm_timer.function = pwm_timer_func;//定时器回调函数
hrtimer_start(&data->pwm_timer, data->time, HRTIMER_MODE_REL);//启动定时器
return 0;
}
static int pwm_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id pwm_of_match[] = {
{.compatible = "pwm_test"},
{},
};
MODULE_DEVICE_TABLE(of, pwm_of_match);
static struct platform_driver pwm_driver = {
.driver = {
.name = "pwm_test",
.of_match_table = pwm_of_match,
},
.probe = pwm_driver_probe,
.remove = pwm_remove,
};
static int __init pwm_init(void)
{
return platform_driver_register(&pwm_driver);
}
static void __exit pwm_exit(void)
{
hrtimer_cancel(&data->pwm_timer);
kfree(data);
platform_driver_unregister(&pwm_driver);
device_destroy(class, MKDEV(major, 0));
class_destroy(class);
unregister_chrdev(major, "hello");
}
module_init(pwm_init);
module_exit(pwm_exit);
MODULE_LICENSE("GPL");