⑦tiny4412 Linux驱动开发之PWM驱动程序
程序员文章站
2022-06-08 20:38:38
...
这次主要是说一下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,发出不同频率的响声.
推荐阅读
-
linux驱动程序开发详细介绍
-
Linux字符设备驱动程序开发(1)-使用字符设备驱动
-
字符设备驱动开发 Linux 设备号 字符设备驱动开发步骤 open 函数调用流程 设备号的组成 设备号的分配 Linux 应用程序对驱动程序的调用 字符设备注册与注销 实现设备的具体操作函数
-
linux驱动开发之输入子系统编程(一)使用工作队列实现中断下半部
-
手把手教你写Linux设备驱动---input子系统(四)--电容屏驱动ft5x06编写(一)(基于友善之臂4412开发板)...
-
Linux驱动开发11:【设备树】nanopi的PWM驱动
-
linux 驱动之PWM蜂鸣器驱动
-
linux PWM蜂鸣器移植以及驱动程序分析
-
linux 驱动开发之平台设备驱动设备树 input子系统的使用:按键中断驱动
-
linux驱动开发内核模块编译之Makefile入门教程