IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> Android9.0 S905D3 PWM背光调节 -> 正文阅读

[嵌入式]Android9.0 S905D3 PWM背光调节

【硬件平台】

CPU:S905D3 GPIOH_5 PWM_F(func4)
背光芯片:SGM3732YTN6G/TR

【底层控制】

1、dts设备树
1)芯片PWM实体描述
common/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi
pwm参考文档:common/Documentation/devicetree/bindings/pwm/pwm.txt

/ {
    soc {
        compatible = "simple-bus";
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        cbus: cbus@ffd00000 {
            compatible = "simple-bus";
            reg = <0x0 0xffd00000 0x0 0x26000>;
            #address-cells = <2>;
            #size-cells = <2>;
            ranges = <0x0 0x0 0x0 0xffd00000 0x0 0x26000>;

            pwm_ef: pwm@19000 {
                compatible = "amlogic,g12a-ee-pwm";
                reg = <0x0 0x19000  0x0 0x20>;
                #pwm-cells = <3>; ---》表示在其他地方可以引用pwm,并且可以传递3个参数
                clocks = <&xtal>,
                        <&xtal>,
                        <&xtal>,
                        <&xtal>;
                clock-names = "clkin0",
                        "clkin1",
                        "clkin2",
                        "clkin3";
                status = "disabled";
            };

common/arch/arm64/boot/dts/amlogic/fj-a2-n1904.dts

/ {
    ...
    lcd-pwm-backlight {
        compatible = "lcd-pwm-backlight";
        status = "okay";
        /* pwm-controller, channel-1, period-ns, polarity */
        pwms = <&pwm_ef MESON_PWM_1 1000000 0>;
    };
};

// 使能pwm_ef
&pwm_ef {
    status = "okay";
};

2、驱动代码
1)厂家pwm控制器驱动代码
common/drivers/amlogic/pwm/pwm_meson.c

static const struct pwm_ops meson_pwm_ops = {
	.request = meson_pwm_request,
	.free = meson_pwm_free,
	.apply = meson_pwm_apply,
	.get_state = meson_pwm_get_state,
	.owner = THIS_MODULE,
};
static int meson_pwm_probe(struct platform_device *pdev)
{
	struct meson_pwm_channel *channels;
	struct meson_pwm *meson;
	struct resource *regs;
	int err;

	meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
	if (!meson)
		return -ENOMEM;

	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	meson->base = devm_ioremap_resource(&pdev->dev, regs);
	if (IS_ERR(meson->base))
		return PTR_ERR(meson->base);

	mutex_init(&meson->lock);
	spin_lock_init(&meson->pwm_lock);
	meson->chip.dev = &pdev->dev;
	meson->chip.ops = &meson_pwm_ops;
	meson->chip.base = -1;
	meson->chip.of_xlate = of_pwm_xlate_with_flags;
	meson->chip.of_pwm_n_cells = 3;
	meson->data = (struct meson_pwm_data *)
		of_device_get_match_data(&pdev->dev);
	if (meson->data->double_channel)
		meson->chip.npwm = 4;
	else
		meson->chip.npwm = 2;
	meson->inverter_mask = BIT(meson->chip.npwm) - 1;

	channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, sizeof(*channels),
				GFP_KERNEL);
	if (!channels)
		return -ENOMEM;

	err = meson_pwm_init_channels(meson, channels);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to init PWM channels: %d\n", err);
		return err;
	}
	err = pwmchip_add(&meson->chip);
	if (err < 0) {
		dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err);
		return err;
	}

	/*for constant,blinks functions*/
	if (meson->data->double_channel)
		meson_pwm_sysfs_init(&pdev->dev);
		
	meson_pwm_add_channels(meson, channels);
	platform_set_drvdata(pdev, meson);
	return 0;
}

2)内核pwm测试代码
放在目录:common/driver/amlogic/pwm,并添加到该目录下的Makefile中

#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>

#define PWM_LASER_SET_PERIOD        _IOW('A', 0x11, unsigned int)
#define PWM_LASER_GET_PERIOD        _IOR('A', 0x12, unsigned int)
#define PWM_LASER_SET_DUTY          _IOW('A', 0x13, unsigned int)
#define PWM_LASER_GET_DUTY          _IOR('A', 0x14, unsigned int)
#define PWM_LASER_ENABLE            _IO('A', 0x15)
#define PWM_LASER_DISABLE           _IO('A', 0x16)

#define PWM_DEFAULT_DUTY_NS         1500000
#define PWM_DEFAULT_PERIOD_NS       2000000

struct pwm_para {
    unsigned int period;
    unsigned int level;
    unsigned int duty;
    struct pwm_device *pwm;
};

struct pwm_para laser;

static void pwm_laser_update(void)
{
    laser.duty = laser.level * laser.period / 100;
    printk("[%s] line%d, duty=%d\n", __FUNCTION__, __LINE__,  laser.duty);
    pwm_config(laser.pwm, laser.duty, laser.period);
}

static void pwm_laser_enable(void)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    pwm_enable(laser.pwm);
}

static void pwm_laser_disable(void)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    pwm_disable(laser.pwm);
}

static void pwm_laser_set_period(unsigned int period)
{
    laser.period = period * 1000000;
    printk("[%s] line%d period=%d\n", __FUNCTION__, __LINE__, laser.period);
    pwm_set_period(laser.pwm, laser.period);
}

static unsigned int pwm_laser_get_period(void)
{
    return (laser.period / 1000000);
}

static int pwm_laser_open(struct inode *inode, struct file *filp)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    return 0;
}

static int pwm_laser_release(struct inode *inode, struct file *filp)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    return 0;
}

static ssize_t pwm_laser_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
{ 
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    return 0;
}

static ssize_t pwm_laser_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    return 0;
}

static long pwm_laser_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    unsigned int period;
    unsigned int level;
    
    void __user *argp = (void __user *)arg;
    
    printk("[%s] line%d cmd=0x%x\n", __FUNCTION__, __LINE__, cmd);
    switch (cmd) {
        case PWM_LASER_SET_PERIOD:
            if (argp == NULL) {
                printk("laser: invalid argument.");
                return -EINVAL;
            }

            if (copy_from_user(&period, argp, sizeof(unsigned int))) {
                printk("copy_from_user failed.");
                return -EFAULT;
            }
            
            pwm_laser_set_period(period);
            break;
            
        case PWM_LASER_GET_PERIOD:
            period = pwm_laser_get_period();
        
            if (copy_to_user(argp, &period, sizeof(unsigned int))) {
                printk("copy_to_user failed.");
                return -EFAULT;
            }
            break;
            
        case PWM_LASER_SET_DUTY    :
            if (argp == NULL) {
                printk("laser: invalid argument.");
                return -EINVAL;
            }

            if (copy_from_user(&level, argp, sizeof(unsigned int))) {
                printk("copy_from_user failed.");
                return -EFAULT;
            }

            if ((level < 0) || (level > 100)) {
                printk("laser: invalid argument.");
                return -EINVAL;
            }

            laser.level = level;
            pwm_laser_update();
            break;
            
        case PWM_LASER_GET_DUTY    :
            if (copy_to_user(argp, &laser.level, sizeof(unsigned int))) {
                printk("copy_to_user failed.");
                return -EFAULT;
            }
            break;
            
        case PWM_LASER_ENABLE:
            pwm_laser_update();
            pwm_laser_enable();
            break;
            
        case PWM_LASER_DISABLE:
            pwm_laser_disable();
            break;
            
        default:
            printk("laser: cmd error!\n");
            return -EFAULT;
    }
    
    return 0;
}

struct file_operations pwm_laser_fops = {
    .owner = THIS_MODULE,
    .open = pwm_laser_open,
    .release = pwm_laser_release,
    .write = pwm_laser_write,
    .read = pwm_laser_read,
    .unlocked_ioctl = pwm_laser_ioctl,
};

struct miscdevice pwm_laser_dev =
{  
    .minor  =   MISC_DYNAMIC_MINOR,
    .fops   =   &pwm_laser_fops,
    .name   =   "lcd-pwm-backlight",
};

static int pwm_laser_probe(struct platform_device *pdev)
{
    int ret;

    printk("[%s] line%d entry\n", __FUNCTION__, __LINE__);   
    laser.pwm = devm_pwm_get(&pdev->dev, NULL); 
    if (laser.pwm == NULL) {
        dev_err(&pdev->dev, "laser: unable to request PWM device\n");
        ret = PTR_ERR(laser.pwm);
        return ret;
    }

    pwm_config(laser.pwm, PWM_DEFAULT_DUTY_NS, PWM_DEFAULT_PERIOD_NS);
    pwm_enable(laser.pwm);

    misc_register(&pwm_laser_dev);
    printk("[%s] line%d success\n", __FUNCTION__, __LINE__);
    return 0;
}

static int pwm_laser_remove(struct platform_device *pdev)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    pwm_disable(laser.pwm);
    pwm_free(laser.pwm);
    misc_deregister(&pwm_laser_dev);
    return 0;
}

int pwm_laser_suspend(struct device *dev)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    return 0;
}

int pwm_laser_resume(struct device *dev)
{
    printk("[%s] line%d\n", __FUNCTION__, __LINE__);
    return 0;
}

const struct dev_pm_ops pwm_laser_pm_ops = {
    .suspend = pwm_laser_suspend,
    .resume = pwm_laser_resume,
};

static const struct of_device_id of_pwm_laser_match[] = {
    {.compatible = "lcd-pwm-backlight", },
    {},
};

static struct platform_driver pwm_laser = {
    .probe = pwm_laser_probe,
    .remove = pwm_laser_remove,
    .driver = {
        .name = "lcd-pwm-backlight",
        .owner = THIS_MODULE,
        .pm = &pwm_laser_pm_ops,
        .of_match_table = of_match_ptr(of_pwm_laser_match),
    },
};
module_platform_driver(pwm_laser);
MODULE_DESCRIPTION("pwm control driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");

3)用户态pwm验证代码
编译命令:/opt/gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -o test --static pwm_test.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 PWM_LASER_SET_PERIOD            _IOW('A', 0x11, unsigned int)
#define PWM_LASER_GET_PERIOD            _IOR('A', 0x12, unsigned int)
#define PWM_LASER_SET_DUTY              _IOW('A', 0x13, unsigned int)
#define PWM_LASER_GET_DUTY              _IOR('A', 0x14, unsigned int)
#define PWM_LASER_ENABLE                _IO('A', 0x15)
#define PWM_LASER_DISABLE               _IO('A', 0x16)

int g_pwm_period = 1; // default 1ms
int g_pwm_duty = 50;  // default 50%

void print_help(void)
{
    printf("usage:\n");
    printf(" ./xxx period duty\n");
    printf(" period ms, duty 0~100\n");
}

int main(int argc,char **argv)
{
    unsigned int i = 0;
    int dir = 0;
    int duty, period;
    int fd;
    int count = 0;

    if ((argc != 1) && (argc != 3)) {
        print_help();
        return -1;
    }
    if (argc == 3) {
        g_pwm_period = atoi(argv[1]);
        g_pwm_duty = atoi(argv[2]);
    }
    printf("Test period = %d duty = %d\n", g_pwm_period, g_pwm_duty);

    fd = open("/dev/lcd-pwm-backlight", O_RDWR);
    if (fd < 0) {
            perror("open lcd-pwm-backlight fail");
            exit(1);
    }

    /* 关闭pwm */
    ioctl(fd, PWM_LASER_DISABLE);

    /* 获取pwm周期 */
    ioctl(fd, PWM_LASER_GET_PERIOD, &period);
    printf("pwm period before = %d\n", period);
    
    /* 设置pwm周期 */
    ioctl(fd, PWM_LASER_SET_PERIOD, &g_pwm_period);

    /* 获取pwm周期 */
    ioctl(fd, PWM_LASER_GET_PERIOD, &period);
    printf("pwm period after = %d\n", period);

    /* 获取pwm占空比 */
    ioctl(fd, PWM_LASER_GET_DUTY, &duty);
    printf("pwm duty before = %d\n", duty);

    /* set pwm占空比 */
    ioctl(fd, PWM_LASER_SET_DUTY, &g_pwm_duty);

    /* 获取pwm占空比 */
    ioctl(fd, PWM_LASER_GET_DUTY, &duty);
    printf("pwm duty after = %d\n", duty);

    /* 使能pwm */
    ioctl(fd, PWM_LASER_ENABLE);

    /* CTRL+C to stop test*/
    while (1) {
        sleep(1);
        count++;
        if ((count % 10) == 0) {
            printf("count=%d\n", count);
        }
    }

    close(fd);
    return 0;
}

【底层调试】

1、pwmchipN,N是PWM芯片的基础

:/ # ls -l /sys/class/pwm/pwmchip4
lrwxrwxrwx 1 root root 0 2009-01-01 00:01 /sys/class/pwm/pwmchip4 -> ../../devices/platform/soc/ff800000.aobus/ff802000.pwm/pwm/pwmchip4
:/ # ls -l /sys/class/pwm/pwmchip0        ---》对应pwm_ef模块
lrwxrwxrwx 1 root root 0 2009-01-01 00:01 /sys/class/pwm/pwmchip0 -> ../../devices/platform/soc/ffd00000.cbus/ffd19000.pwm/pwm/pwmchip0
:/ #

2、检查pin脚复用状态

// 查看管脚复用关系
:/sys/kernel/debug/pinctrl # cat /sys/kernel/debug/pinctrl/pinctrl@ff634480/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
...
pin 22 (GPIOH_5): backlight (GPIO UNCLAIMED) function pwm_f group pwm_f_h
...

// 查看管脚属性
:/sys/kernel/debug/pinctrl # cat /sys/kernel/debug/pinctrl/pinctrl@ff634480/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
...
pin 22 (GPIOH_5): input bias pull down, output drive strength (1), pin output (1 level)
...

// 检查pwm_f功能与哪些group绑定
ernel/debug/pinctrl/pinctrl@ff634480/pinmux-functions                         <
...
function: pwm_f, groups = [ pwm_f_x pwm_f_h pwm_f_z pwm_f_a11 ]
...

3、用户态修改PWM占空比

:/data # ./pwm_test 1 60 &
[1] 4249
:/data # Test period = 1 duty = 100
pwm period before = 1
pwm period after = 1
pwm duty before = 75
pwm duty after = 100

【应用控制】

1、添加pwm led背光驱动
驱动文件:common\drivers\leds\leds-pwm.c
配置文档:Documentation\devicetree\bindings\leds\leds-pwm.txt

确认内核有加入leds-pwm.c驱动
common/arch/arm64/configs/meson64_defconfig

CONFIG_LEDS_GPIO=y
CONFIG_LEDS_PWM=y       ---》确保为y
CONFIG_LEDS_TRIGGERS=y

2、增加设备树Dts节点
common/arch/arm64/boot/dts/amlogic/fj-a2-n1904.dts

    pwmleds {
        compatible = "pwm-leds";
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&pwm_f_pins2>;

        sys {
            active-low;
            label = "lcd-backlight";
            max-brightness = <255>;
            pwms = <&pwm_ef MESON_PWM_1 50000 0>;
        };
    };

【应用调试】

1、通过文件系统调节亮度

/ # cd /sys/class/leds/lcd-backlight
/sys/class/leds/lcd-backlight # ---》设置不同的亮度值
echo 0 > brightness
echo 100 > brightness
echo 200 > brightness
echo 255 > brightness

2、在系统界面上调节亮度调节进度条
To-do

【参考资料】

1、pwm设备驱动调试
https://www.cnblogs.com/lialong1st/p/11436190.html

2、android背光设置
https://blog.csdn.net/izhetu/article/details/51096697

3、Linux驱动分析——pwm-leds
https://blog.csdn.net/fang_yang_wa/article/details/112910107

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-27 10:04:07  更:2021-11-27 10:06:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 11:53:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码