这次主要是说一下PWM驱动,本来这一次想做一下LCD背光的,我看网上都是通过PWM1的方式调节LCD背光的,然后看了一下电路图,我这个LCD没有接那个接口,就接了一个w1总线的接口,通过网上查询,我这一款好像是通过1-wire总线的方式进行调节的,所以这次准备的PWM就没有写成LCD背光,只是单纯地通过蜂鸣器测试一下PWM,电路图如下:
所以这里测试蜂鸣器,相关电路图如下:
因为三星已经把相应的驱动写好了,我们只需要写少量代码即可实现PWM驱动.
因为现在一些特殊的情况,没有时间细细研究目前所写的驱动程序,关于PWM的基础知识,请自行百度,主要是SOC集成的定时器来做的,然后高低电平会根据用户不同的配置,进行不同比例的高低电平时间,称之为占空比,这里就废话不多说,直接上PWM驱动蜂鸣器的的代码,因为,三星已经帮我们把PWM驱动的框架都写好了,所以,我们不需要自己去操作寄存器来配置PWM的一些参数,我们直接一个pwm_request()就搞定了.下面是驱动代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#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,发出不同频率的响声.