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负载大小调整散热风扇的转速,也是一个常用的功能。