android设备中可以使用gpio去模拟pwm对外接led灯进行亮度控制。
下面是一个gpio模拟pwm的内核驱动:
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/clk.h>
// #include <linux/adc.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#include <linux/iio/consumer.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/input.h>
#include <linux/interrupt.h>
// #include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/pm_wakeirq.h>
#include <linux/reset.h>
struct gpio_pwm_dev {
int value;
unsigned gpio;
struct gpio_desc * gpiod;
struct mutex mtx;
const char* name;
int gpioled_pwm_level;
struct device *dev;
int high_time;
int low_time;
unsigned long flags;
unsigned active_low;
struct timer_list pwm_timer;
void __iomem *reg_base;
};
static int gpioled_gpio = 0;
static int frequency = 1000; //频率,单位:Hz
static int high_cnt = 50;//占空比,gpio拉高的时间百分比,当前将一个周期分为period_cnt(100)个时间段
static int period_cnt = 100;//一个周期分为100个时间段。
static struct hrtimer pwm_timer1;
static int pwm_period = 10000;//一个周期的时间,纳秒
ktime_t kt;
static ssize_t gpioled_pwm_level_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", high_cnt);
}
static ssize_t gpioled_pwm_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
long gpioled_pwm_level_val;
int ret;
ret = kstrtoul(buf, 10, &gpioled_pwm_level_val);
if(ret < 0){
printk("%s kstrtoul failed!\n",__func__);
return -1;
}
high_cnt = gpioled_pwm_level_val;
return count;
}
static struct device_attribute gpioled_pwm_level = {
.attr = {
.name = "gpioled_pwm_level",
.mode = S_IRUGO | S_IWUGO | S_IXUGO ,
},
.show = gpioled_pwm_level_show,
.store = gpioled_pwm_level_store,
};
/*************************************************************************/
static enum hrtimer_restart pwm_timer_callback(struct hrtimer *timer) {
if (gpio_get_value(gpioled_gpio) == 1) {
/* There is no need to pull down when the duty cycle is 100% */
if (high_cnt >= 0 && high_cnt < period_cnt) {
gpio_set_value(gpioled_gpio, 0);
kt = ktime_set(0, (period_cnt - high_cnt)*pwm_period);
}
/* timer overflow */
hrtimer_forward_now(&pwm_timer1, kt);
} else {
/* There is no need to pull up when the duty cycle is 0 */
if (high_cnt > 0) {
gpio_set_value(gpioled_gpio, 1);
kt = ktime_set(0, high_cnt * pwm_period);
}
/* timer overflow */
hrtimer_forward_now(&pwm_timer1, kt);
}
return HRTIMER_RESTART;
}
static int io_to_pwm_init(void) {
//计算PWM周期为pwm_period*period_cnt=pwm_period*100=1*10^(-6)
pwm_period = 1000000000/frequency/period_cnt;
kt = ktime_set(0, high_cnt * pwm_period);
// 初始化定时器
hrtimer_init(&pwm_timer1, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pwm_timer1.function = pwm_timer_callback;//回调函数操作GPIO
// 启动高精度定时器
hrtimer_start(&pwm_timer1, kt, HRTIMER_MODE_REL);
printk(KERN_INFO "PWM Timer Module Loaded\n");
printk("%s\n",__func__);
return 0;
}
/*************************************************************************/
static int gpio_pwm_probe(struct platform_device *pdev)
{
int ret = 0;
struct device *dev = &pdev->dev;
struct gpio_pwm_dev *gblacklight_dev;
printk("%s\n",__func__);
gblacklight_dev = devm_kzalloc(dev, sizeof(struct gpio_pwm_dev), GFP_KERNEL);
memset(gblacklight_dev, 0, sizeof(*gblacklight_dev));
if (!gblacklight_dev)
{
printk("devm_kzalloc gblacklight_dev fail \n");
return -1;
}
gblacklight_dev->reg_base = of_iomap(pdev->dev.of_node, 0);
if (gblacklight_dev->reg_base == 0)
{
printk("%s:Failed to ioremap() io memory region.\n", __func__);
ret = -EBUSY;
}
else
printk("xxkey base: %p !\n", gblacklight_dev->reg_base);
gpioled_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "gpioled-gpio", 0, NULL);
if (gpioled_gpio < 0)
{
printk("%s() Can not read property gpioled-gpio\n", __func__);
gpioled_gpio = 0;
return -1;
}
ret = gpio_request(gpioled_gpio, "gpioled-gpio");
if (ret < 0)
{
printk("gpio request gpioled-gpio error!\n");
}
else
{
printk("%s get gpioled-gpio:%d success!\n",__func__, gpioled_gpio);
gpio_direction_output(gpioled_gpio, 1);
}
io_to_pwm_init();
if(device_create_file(&pdev->dev, &gpioled_pwm_level))
printk("%s create gpioled_pwm_level file fail because of erorr\n",__func__);
platform_set_drvdata(pdev, gblacklight_dev);
return ret;
}
static int gpio_pwm_remove(struct platform_device *pdev)
{
struct gpio_pwm_dev *gblacklight_dev = (struct gpio_pwm_dev *)dev_get_drvdata(&pdev->dev);
del_timer(&gblacklight_dev->pwm_timer);
device_remove_file(gblacklight_dev->dev, &gpioled_pwm_level);
dev_set_drvdata(gblacklight_dev->dev, NULL);
hrtimer_cancel(&pwm_timer1);
return 0;
}
static const struct of_device_id of_gpio_pwms_match[] = {
{ .compatible = "gpioled,gpiopwm" },
{},
};
static struct platform_driver gpio_pwm_driver = {
.probe = gpio_pwm_probe,
.remove = gpio_pwm_remove,
.driver = {
.name = "gpio_pwm",
.of_match_table = of_gpio_pwms_match,
},
};
static int __init gpio_pwm_init(void)
{
printk("%s\n",__func__);
platform_driver_register(&gpio_pwm_driver);
return 0;
}
static void __exit gpio_pwm_exit(void)
{
printk("%s\n",__func__);
platform_driver_unregister(&gpio_pwm_driver);
return;
}
module_init(gpio_pwm_init);
module_exit(gpio_pwm_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("hedalei");
对应系统dts添加如下配置:
gpioled {
status = "okay";
compatible = "gpioled,gpiopwm";
gpioled-gpio = <&pio PI 6 GPIO_ACTIVE_HIGH>;
};
上面驱动添加以后,会生成/sys/devices/platform/gpioled/gpioled_pwm_level节点,对节点写0~100的数值就可以进行亮度调节了。设置0的时候是一直拉低,亮度为0,设置为100的时候一直拉高,亮度为100.