amba H2系列CPU的GPIO口能作PWM使用的个数有限(GPIO0-GPIO3),从PRM里GPIO: Function Selection章节可以得到如何配置GPIO为PWM功能。

假设想把GPIO0配置成PWM输出,根据上图修改ambarella/boards/h2_xxx/bsp/bsp.h ,如下:

即DEFAULT_IOMUX_REG0_2、DEFAULT_IOMUX_REG0_1、DEFAULT_IOMUX_REG0_0的第0位要分别设为0、1、1。

一、系统层用脚本控制PWM0示例

echo 0 > /sys/class/pwm/pwmchip0/export
echo 5000000 > /sys/class/pwm/pwmchip0/pwm0/period (设置周期)
echo 2000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle (设置有效时间)
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable (使能PWM)
占空比= 有效时间/周期, 占空比越大,LCD背光越亮(有效时间要小于等于周期时间)。

为了方便使用写成脚本set_pwm.sh:

#! /bin/bash
#
# set_pwm.sh <gpio_id>  < 1 | 0 > <duty_cycle> <period>

do_cmd()
{
        local id=$1
        local ena=$2
        local d_cycle=$3
        local per=$4
        local GPIO_ROOT=/sys/class/pwm/pwmchip0
        local GPIO_DIR=$GPIO_ROOT/pwm$id

        if [[ ! -d $GPIO_DIR ]]; then
                echo $id > $GPIO_ROOT/export
        fi

        echo $ena > $GPIO_DIR/enable
        echo $per > $GPIO_DIR/period
        echo $d_cycle > $GPIO_DIR/duty_cycle
}

usage()
{
        echo " set_pwm.sh <gpio_id>  < 1 | 0 > <duty_cycle> <period>"
}

do_main()
{
        case "$2" in
                1)
                        do_cmd $1 1 $3 $4
                        ;;
                0)
                        do_cmd $1 0 $3 $4
                        ;;
                *)
                        usage
                        ;;
        esac
}

if [[ -z $4 ]]; then
        usage
else
        do_main $*
fi

二、写一个pwm驱动: pwm.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/pwm.h>
#include <linux/uaccess.h>


#define PWM_MAGIC 'P' //定义一个幻数
#define PWM_ON _IOW(PWM_MAGIC, 0, struct pwm_capture_t) //定义一个ioctl cmd
#define PWM_OFF _IOW(PWM_MAGIC, 1, struct pwm_capture_t) //定义另一个ioctl cmd struct pwm_device *pwm_dev_0; struct pwm_capture_t { unsigned int period; unsigned int duty_cycle; }pwmm; static long pwm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret; switch(cmd) { case PWM_ON: ret = copy_from_user(&pwmm, (void *)arg, sizeof(struct pwm_capture_t)); if (ret < 0) return ret; ret = pwm_config(pwm_dev_0, pwmm.duty_cycle, pwmm.period); if (ret < 0) { printk("pwm_dev_0 ioctl fail"); return 0; } pwm_enable(pwm_dev_0); break; case PWM_OFF: ret = copy_from_user(&pwmm, (void *)arg, sizeof(struct pwm_capture_t)); if (ret < 0) return ret; ret = pwm_config(pwm_dev_0, 0, pwmm.period); if (ret < 0) { printk("pwm_dev_2 ioctl fail"); return 0; } pwm_disable(pwm_dev_0); break; } return 0; } //定义初始化硬件操作方法 static struct file_operations pwm_fops = { .owner = THIS_MODULE, .unlocked_ioctl = pwm_ioctl }; //定义初始化混杂设备对象 static struct miscdevice pwm_misc = { .minor = MISC_DYNAMIC_MINOR, //动态分配次设备号 .name = "mypwm", //dev/mypwm .fops = &pwm_fops }; static int pwm_init(void) { int ret; printk("regisger pwm_misc device\n"); //1.申请pwm资源,设置输出为0 pwm_dev_0 = pwm_request(0, "pwm_0"); if (pwm_dev_0 == NULL) { printk("pwm_dev_0 register fail\n"); } ret = pwm_config(pwm_dev_0, 0, 500000); if (ret < 0) { printk("pwm_dev_0 config fail\n"); return 0; } ret = pwm_enable(pwm_dev_0); if (ret == 0) { printk("pwm_dev_0 enable success\n"); } else if (ret < 0 ) { printk("pwm_dev_0 enable fail\n"); return 0; } //2.注册混杂设备 misc_register(&pwm_misc); return 0; } static void pwm_exit(void) { printk("unregister pwm_misc device\n"); //1.卸载混杂设备 misc_deregister(&pwm_misc); //2.释放pwm资源 pwm_config(pwm_dev_0, 0, 500000); pwm_disable(pwm_dev_0); pwm_free(pwm_dev_0); } module_init(pwm_init); module_exit(pwm_exit); MODULE_LICENSE("GPL");

测试程序pwm_test.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define PWM_MAGIC 'P' //定义一个幻数,要和pwm.c里定义的相同
#define PWM_ON _IOW(PWM_MAGIC, 0, struct pwm_capture_t)  //定义一个ioctl cmd, 要和pwm.c里定义的相同
#define PWM_OFF _IOW(PWM_MAGIC, 1, struct pwm_capture_t) //同上

struct pwm_capture_t {
    unsigned int period;
    unsigned int duty_cycle;
};

int main(int argc, char **argv)
{
    int fd;
    int a;
    int period, duty_cycle;
    if (argc < 4) {
        printf("usage: %s on/off duty_cycle period\n", argv[0]);
        return -1;
    }
    duty_cycle = strtol(argv[2], NULL, 10);
    period = strtol(argv[3], NULL, 10);
    if (duty_cycle > period) {
        printf("duty_cycle shouldn't larger than period\n");
        return -1;
    }
    struct pwm_capture_t pwmm = {period, duty_cycle};

    fd = open("/dev/mypwm", O_RDWR);
    if (fd < 0)
        return -1;
    while(1) {
    /*    for(a = 0; a <= period; a += 5) {
            pwmm.duty_cycle = a;
            ioctl(fd, PWM_ON, &pwmm);
            usleep(1000);
        }

        for(a = period; a >= 0; a -= 5) {
            pwmm.duty_cycle = a;
            ioctl(fd, PWM_ON, &pwmm);
            usleep(1000);
        }
        usleep(1000000);
    }
    */

    if (strcmp(argv[1], "on") == 0) {
        ioctl(fd, PWM_ON, &pwmm);
    } else if (strcmp(argv[1], "off") == 0) {
        ioctl(fd, PWM_OFF, &pwmm);
    } else {
        printf("invalid parameter\n");
    }


    close(fd);
    return 0;
}

 执行./pwm_test on 200000 500000,会看到LCD背光点亮,增加duty_cycle的值,背光会变得更亮。

pwm除了可以控制屏幕亮度外还可以控制风扇转速,根据cpu负载大小调整散热风扇的转速,也是一个常用的功能。

12-31 15:22