如何在qualcomm 8960和8921上使用PWM功能 (GPIO24)

一、PWM 分析

1. PWM: Pulse Width Modulator; LPG: Light Pulse Generator; 两者指的是同一东西。 

2. PWM driver: /kernel/drivers/mfd/pm8xxx-pwm.c

3. 手机中注册路径: /sys/devices/platform/msm_ssbi.0/pm8921-core/pm8xxx-pwm
4. 利用debugfs调试
    a. #mkdir /data/debugfs
    b. #mount -t debugfs none /data/debugfs
    c. #cd /data/debugfs/pmx8xxx-pwm-dbg
    d. #ls 可以看到共有8路pwm可用
    e. #cd 0; ls
        duty-cycle    //占宽比 (0,100)之间
        enable
        period        //输出波周期 [7, 327*1000000L]微秒之间
    f. 需要先设置period, #echo 50 > period
    g. #echo 50 > duty-cycle

    h. #echo 1 > enable        


二、调查完成,所需要修改的代码
1. @kernel/arch/arm/mach-msm/board-semc_blue_cdb.c, 设置PM8921的GPIO工作在PWM function
    static struct pm8xxx_gpio_init pm8921_gpios[] __initdata = {
        ...
        //PM8XXX_GPIO_DISABLE(24),
        PM8XXX_GPIO_INIT(    24, 
                    PM_GPIO_DIR_OUT,
                    PM_GPIO_OUT_BUF_CMOS,
                    1,
                    PM_GPIO_PULL_NO,
                    PMIC_GPIO_VIN0,
                    PM_GPIO_STRENGTH_HIGH,
                    PM_GPIO_FUNC_2,
                    1,
                    0
                ),    

        ...
    }
2. @kernel/arch/arm/mach-msm/board-semc_blue.c, 设置PWM/LPG 的bank1 可用,该bank1 对应的是GPIO24
    static struct pm8xxx_pwm_platform_data pm8xxx_pwm_pdata = {
        //.dtest_channel    = PM8XXX_PWM_DTEST_CHANNEL_NONE,
        .dtest_channel    = 0,  
    };
3. 进入手机adb shell
    a. #mount -t debugfs none /data/debugfs
    b. #cd data/debugfs/ pm8xxx-pwm-dbg/0
    c. #echo 2000 > period //必须先设置period, 单位为usecond, 区间为[7, 327*1000000L]us
    d. #echo 50 > duty-cycle //占空比 (0,100)之间
    e. #echo 1 > enable //现在有LPG 方波输出    
    f. #echo 1 > enable //取消 LPG 方波输出    

三、如何检测PM8921 GPIO 的状态
1. 利用手机系统中 已有 /sys/kernel/debug/pm8921-dbg ,gpio检测接口
    a. #echo 0x??? > addr //输入GPIO register addrress
    b. #echo 0x??? > data //输入控制GPIO,写入register 的值
    c. #cat data //查看当前bank输入的值
2. 查看PM8921 register information 文档
    a. Function,Sub funtion,Register name,Register Address,Type,Reset code,default value,comments
        GPIO,   GPIO_24,     GPIO_CNTRL,    0x167,        8B_WR, 5,    00865A01,    Power-off and Power-on default state is Hi-Z
    b. 所以GPIO24的 Register Address 是0x167
3. 检测GPIO24的配置状态,先设置 bank0-7,再读取在该bank设置的参数
    #echo 0x167 > addr 
    echo 0x0 > data //选择bank0
    cat data --> 0x01 //得到在bank0 设置的参数
    echo 0x10 > data 
    cat data -->0x18
    echo 0x20> data 
    cat data -->0x2A
    echo 0x30 > data 
    cat data -->0x34
    echo 0x40 > data 
    cat data -->0x46
    echo 0x50 > data 
    cat data 
    echo 0x60 > data 
    cat data 
    echo 0x70 > data 
    cat data 
    直接设置某个bank的参数
    #echo 0x167 > addr 
    #echo 0xc6 > data //各bit含义: [7]->(1W,0R),[6:4]->Bank Select, [3:0]->各bank含义不一样
    所以0xC6表示0b11000110, 写入,bank4,写入的参数为0110

四,如何用GPIO24模拟 PWM 输出,主要修改kernel/drivers/mfd/pm8xxx-pwm.c

#define TEST_PWM

#ifdef TEST_PWM
#include <linux/mfd/pm8xxx/gpio.h>
#include <linux/delay.h>
#endif


struct pm8xxx_pwm_dbg_device {
    struct mutex        dbg_mutex;
    struct device        *dev;
    struct dentry        *dent;

    struct pm8xxx_pwm_user    *user;

#ifdef TEST_PWM
    struct work_struct pwm_work;
    struct workqueue_struct *pwm_wq;
    bool pwm_run;
#endif    

};


#ifdef TEST_PWM
static int dbg_pwm_value_set(void *data, u64 val)
{
    struct pm8xxx_pwm_user      *puser = data;
    struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
    int gpio_sys = 175;//PM8921_GPIO_PM_TO_SYS(24);

    mutex_lock(&dbgdev->dbg_mutex);

    gpio_set_value_cansleep(gpio_sys, val);
        
    mutex_unlock(&dbgdev->dbg_mutex);

    pr_debug("#### value = %d\n",(int)val);
    
    return 0;
}

static int dbg_pwm_value_get(void *data, u64 *val)
{
    struct pm8xxx_pwm_user      *puser = data;
    struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
    int gpio_sys = 175;//PM8921_GPIO_PM_TO_SYS(24);

    mutex_lock(&dbgdev->dbg_mutex);
    
    *val =gpio_get_value_cansleep(gpio_sys);

    mutex_unlock(&dbgdev->dbg_mutex);

    pr_debug("#### value = %d\n",(int)*val);

    return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_value_fops,
            dbg_pwm_value_get, dbg_pwm_value_set, "%lld\n");

static void pwm_instant_work(struct work_struct *work)
{
    int gpio_sys = 175;//PM8921_GPIO_PM_TO_SYS(24);
    struct pm8xxx_pwm_dbg_device  *dbgdev= container_of(work, struct pm8xxx_pwm_dbg_device, pwm_work);
    struct pm8xxx_pwm_user      *puser;
    int  period;
    int  duty_cycle;
    int  high_us, low_us;
    
    pr_debug(" ### start output signal\n");

    puser = dbgdev->user;
    period = puser->period;
    duty_cycle = puser->duty_cycle;

    if( !period || !duty_cycle )
        return;
    
    high_us = period * duty_cycle /100;
    low_us = period - high_us;
    pr_debug("### hight_us = %d, low_us = %d \n ", high_us, low_us);
    
    while(dbgdev->pwm_run)
    {
        gpio_set_value_cansleep(gpio_sys, 1);
        //usleep(high_us); 
        udelay(high_us);
        
        gpio_set_value_cansleep(gpio_sys, 0);
        //usleep(low_us);
        udelay(low_us);
    }
}

static int dbg_pwm_output_set(void *data, u64 val)
{
    struct pm8xxx_pwm_user      *puser = data;
    struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;

    mutex_lock(&dbgdev->dbg_mutex);

    pr_debug("#### value = %d\n",(int)val);

    if( val )
    {
        dbgdev->pwm_run = true;
        queue_work(dbgdev->pwm_wq, &dbgdev->pwm_work);
    }
    else
    {
        dbgdev->pwm_run = false;
    }

    mutex_unlock(&dbgdev->dbg_mutex);

    return 0;
}

static int dbg_pwm_output_get(void *data, u64 *val)
{
    struct pm8xxx_pwm_user      *puser = data;
    struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;

    mutex_lock(&dbgdev->dbg_mutex);
    
    *val = dbgdev->pwm_run;

    mutex_unlock(&dbgdev->dbg_mutex);

    return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_output_fops,
            dbg_pwm_output_get, dbg_pwm_output_set, "%lld\n");
#endif


static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)

{

     ....

        if (temp == NULL || IS_ERR(temp)) {
            pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
            rc = -ENOMEM;
            goto debug_error;
        }
#ifdef TEST_PWM
        temp = debugfs_create_file("value", S_IRUGO | S_IWUSR,
                        dent, puser, &dbg_pwm_value_fops);
        if (temp == NULL || IS_ERR(temp)) {
            pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
            rc = -ENOMEM;
            goto debug_error;
        }

        temp = debugfs_create_file("output", S_IRUGO | S_IWUSR,
                        dent, puser, &dbg_pwm_output_fops);
        if (temp == NULL || IS_ERR(temp)) {
            pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
            rc = -ENOMEM;
            goto debug_error;
        }
#endif

    ....


    pmic_dbg_device = dbgdev;

#ifdef TEST_PWM
    dbgdev->pwm_wq = create_singlethread_workqueue("pwm_wq");
    INIT_WORK(&dbgdev->pwm_work, pwm_instant_work);
#endif    


    return 0;

   ....

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用RPi.GPIO库在树莓派上发送PWM波来实现云台拍照的示例代码: ```python import RPi.GPIO as GPIO import time # 设置GPIO模式为BCM GPIO.setmode(GPIO.BCM) # 设置PWM引脚 pwm_pin = 18 GPIO.setup(pwm_pin, GPIO.OUT) # 创建PWM对象,设置频率为50Hz pwm = GPIO.PWM(pwm_pin, 50) # 设置初始占空比为7.5% pwm.start(7.5) try: while True: # 将占空比设置为使云台保持静止的位置 pwm.ChangeDutyCycle(7.5) time.sleep(1) # 拍照时,将占空比设置为其他值以控制云台的运动 pwm.ChangeDutyCycle(10) time.sleep(1) except KeyboardInterrupt: # 中断时停止PWM pwm.stop() # 清理GPIO设置 GPIO.cleanup() ``` 在这个示例代码中,我们使用BCM模式来设置GPIO引脚。你需要根据你所连接的GPIO引脚进行相应的更改。将pwm_pin变量设置为你所使用GPIO引脚的编号。 这段代码中,我们创建了一个PWM对象,并设置频率为50Hz。然后,我们通过改变占空比来控制云台的位置。将占空比设置为7.5%时,云台保持静止的位置;将占空比设置为其他值时,云台会运动到相应的位置。 你可以根据需要修改代码中的占空比和延时时间来实现你想要的效果。记得在程序结束时调用GPIO.cleanup()函数来清理GPIO设置。 请注意,具体的实现方式可能会因为你所使用的硬件和云台的不同而有所差异。在实际使用中,你需要根据你的硬件和云台的特性进行相应的调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值