⑦tiny4412 Linux驱动开发之PWM驱动程序

这次主要是说一下PWM驱动,本来这一次想做一下LCD背光的,我看网上都是通过PWM1的方式调节LCD背光的,然后看了一下电路图,我这个LCD没有接那个接口,就接了一个w1总线的接口,通过网上查询,我这一款好像是通过1-wire总线的方式进行调节的,所以这次准备的PWM就没有写成LCD背光,只是单纯地通过蜂鸣器测试一下PWM,电路图如下:


所以这里测试蜂鸣器,相关电路图如下:


因为三星已经把相应的驱动写好了,我们只需要写少量代码即可实现PWM驱动.

因为现在一些特殊的情况,没有时间细细研究目前所写的驱动程序,关于PWM的基础知识,请自行百度,主要是SOC集成的定时器来做的,然后高低电平会根据用户不同的配置,进行不同比例的高低电平时间,称之为占空比,这里就废话不多说,直接上PWM驱动蜂鸣器的的代码,因为,三星已经帮我们把PWM驱动的框架都写好了,所以,我们不需要自己去操作寄存器来配置PWM的一些参数,我们直接一个pwm_request()就搞定了.下面是驱动代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>


#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>



#define BUZZER_PWM_GPIO     EXYNOS4_GPD0(1)
#define DEVICE_NAME         "pwm1"
#define PWM_BUZ_ID          1               // Backlight
#define NS_IN_1HZ           (1000000000UL)  // 纳秒级,1Hz就是1秒1次,就是1000000000UL纳秒

#define PWM_SET_FREQ        _IOW('L', 0x1234, int)
#define PWM_STOP            _IOW('L', 0x1235, int)

static struct semaphore sem_lock;
static struct pwm_device *pwm_buz = NULL;


void 
pwm_set_freq(unsigned long freq)
{
    int period_ns = NS_IN_1HZ / freq;

    pwm_config(pwm_buz, period_ns / 2, period_ns);
    pwm_enable(pwm_buz);

    s3c_gpio_cfgpin(BUZZER_PWM_GPIO, S3C_GPIO_SFN(2));
}

void 
pwm_stop(void)
{
    s3c_gpio_cfgpin(BUZZER_PWM_GPIO, S3C_GPIO_OUTPUT);
    
    pwm_config(pwm_buz, 0, NS_IN_1HZ / 100);
    pwm_disable(pwm_buz);
}

int 
exynos_pwm_open(struct inode *inode, struct file *filp)
{
    int ret = -1;

    down_trylock(&sem_lock);

    // 1,申请GPIO口
    ret = gpio_request(BUZZER_PWM_GPIO, DEVICE_NAME);
    if(ret < 0){
        printk("gpio request failed !\n");
        return -ENODEV;
    }

    // 2,设置GPIO口为输出,电平为1
    gpio_direction_output(BUZZER_PWM_GPIO, 0);

    return 0;
}

long 
exynos_pwm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case PWM_SET_FREQ:
            if(0 == arg)
                return -EINVAL;
            pwm_set_freq(arg);
            break;

        case PWM_STOP:
        default:
            pwm_stop();
            break;
    }

    return 0;
}

int 
exynos_pwm_release(struct inode *inode, struct file *filp)
{
    up(&sem_lock);

    // 释放GPIO
    gpio_free(BUZZER_PWM_GPIO);

    return 0;
}

const struct file_operations exynos4412_fops = {
    .open = exynos_pwm_open,
    .unlocked_ioctl = exynos_pwm_ioctl,
    .release = exynos_pwm_release,
};

struct miscdevice exynos4412_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &exynos4412_fops,
};

static void __exit
exynos4412_pwm_exit(void)
{
    pwm_stop();
    misc_deregister(&exynos4412_misc);
    gpio_free(BUZZER_PWM_GPIO);
}

static int __init
exynos4412_pwm_init(void)
{
    int ret = -1;

    // 1,申请GPIO口
    ret = gpio_request(BUZZER_PWM_GPIO, DEVICE_NAME);
    if(ret < 0){
        printk("gpio request failed !\n");
        return -ENODEV;
    }

    // 2,设置GPIO口为输出,电平为0
    gpio_direction_output(BUZZER_PWM_GPIO, 0);

    // 3,释放GPIO
    gpio_free(BUZZER_PWM_GPIO);

    // 4,申请pwm
    pwm_buz = pwm_request(PWM_BUZ_ID, DEVICE_NAME);
    if(IS_ERR(pwm_buz)){
        printk("pwm request failed !\n");
        return -ENODEV;
    }

    // 5,pwm关闭
    pwm_stop();

    // 6,初始化信号量,在开启此驱动时保护资源独享
    sema_init(&sem_lock, 1);

    // 7,注册杂项字符设备
    ret = misc_register(&exynos4412_misc);
    if(ret < 0)
        printk("misc register failed !\n");

    return ret;
}

module_init(exynos4412_pwm_init);
module_exit(exynos4412_pwm_exit);

MODULE_LICENSE("GPL");

然后,接下来是驱动测试代码:

#include <stdio.h>
#include <stdlib.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

#include <linux/input.h>

#define PWM_SET_FREQ        _IOW('L', 0x1234, int)
#define PWM_STOP            _IOW('L', 0x1235, int)

int main(void)
{
    int fd;

    fd = open("/dev/pwm1", O_RDWR);
    if(fd < 0){
        perror("open failed");
        exit(1);
    }

    for(;;)
    {
        ioctl(fd, PWM_SET_FREQ, 2);
        sleep(1);
        ioctl(fd, PWM_SET_FREQ, 4);
        sleep(1);
        ioctl(fd, PWM_SET_FREQ, 10);
        sleep(1);
        ioctl(fd, PWM_STOP, 0);
        sleep(1);
    }

    ioctl(fd, PWM_STOP, 0);

    if(close(fd) < 0){
        perror("close failed");
        exit(1);
    }

    return 0;
}

还有Makefile:

#指定内核源码路径
KERNEL_DIR = /home/george/1702/exynos/linux-3.5

#指定当前路径
CUR_DIR = $(shell pwd)


MYAPP = pwm_app
MODULE = exynos4412_pwm

all:
	make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
	arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
clean:
	make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
	$(RM) $(MYAPP)
install:
	cp -raf *.ko $(MYAPP) /home/george/1702/exynos/filesystem/1702

#指定编译当前目录下那个源文件
obj-m = $(MODULE).o

当然,我们需要把友善之臂写的PWM的驱动通过make menuconfig去掉:


之后,编译内核,重新加载新内核,并安装自己写的驱动程序,然后测试验证OK,发出不同频率的响声.

猜你喜欢

转载自blog.csdn.net/qq_23922117/article/details/80567638