使用zlg的m6708开发板时(内核kernel3.0.15),由于自带得开开发板系统只有一路PWM,并且该路PWM已经给了LCD作背光调节,为此接下来学习如何将其他io口修改为PWM输出。
首先参考emb_hao的博客:Linux系统PWM驱动
内核的配置
在linux内核中有一个规律,Linux内核开发者把通用的东西都总结出来,个性化的东西就留出接口,和GPIO驱动类似,PWM驱动在内核中也提供了对应的接口函数,内核提供的接口函数声明在include/linux/pwm.h中
//申请一个PWM资源
struct pwm_device *pwm_request(int pwm_id, const char *label);
//释放一个PWM资源
void pwm_free(struct pwm_device *pwm);
//配置PWM
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
//使能PWM
int pwm_enable(struct pwm_device *pwm);
//不使能PWM
void pwm_disable(struct pwm_device *pwm);
针对内核的框架,IMX6对这些函数的实现位于arch/arm/plat-mxc/pwm.c中,利用这些接口函数我们就可以编写自己的PWM驱动程序
步骤:
1 添加对PWM端口的初始化(本驱动基于飞思卡尔IMX6(zlgm6708dl),这部分不同的板子会有些许不同)
在arch/arm/mach-mx6/board-m6708dl.h
在MX6Q_PAD_SD1_DAT3__PWM1_PWMO,后面添加
MX6Q_PAD_SD1_DAT1__PWM3_PWMO,
MX6Q_PAD_SD1_CMD__PWM4_PWMO,
MX6Q_PAD_SD1_DAT2__PWM2_PWMO,
2 使用pwm_request,pwm_free,pwm_config,pwm_enable,pwm_disable五个函数编写混杂设备驱动
编写pwm_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/gpio.h>
#include <linux/pwm.h>
#define PWM_ON 0x100001
#define PWM_OFF 0x100002
struct pwm_device *pwm_dev_2;
struct pwm_device *pwm_dev_3;
static long pwm_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
int ret;
switch(cmd) {
case PWM_ON:
ret = pwm_config(pwm_dev_2,20000,50000);
if(ret < 0){
printk("pwm_dev_2 ioctl fail");
return 0;
}
ret = pwm_config(pwm_dev_3,30000,50000);
if(ret < 0){
printk("pwm_dev_3 ioctl fail");
}
pwm_enable(pwm_dev_2);
pwm_enable(pwm_dev_3);
printk("pwm_enable init success\n");
break;
case PWM_OFF:
ret = pwm_config(pwm_dev_2,0,50000);
if(ret < 0){
printk("pwm_dev_2 ioctl fail");
return 0;
}
ret = pwm_config(pwm_dev_3,0,50000);
if(ret < 0){
printk("pwm_dev_3 ioctl fail");
}
pwm_disable(pwm_dev_2);
pwm_disable(pwm_dev_3);
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_2 = pwm_request(1,"pwm_2");
if(pwm_dev_2 == NULL){
printk("pwm_dev_2 register fail\n");
}
pwm_dev_3 = pwm_request(2,"pwm_3");
if(pwm_dev_3 == NULL){
printk("pwn_dev_3 register fail\n");
}
ret = pwm_config(pwm_dev_2,0,50000);
if(ret < 0){
printk("pwm_config_2 init fail\n");
return 0;
}
ret = pwm_config(pwm_dev_3,0,50000);
if(ret < 0){
printk("pwm_config_3 init fail\n");
return 0;
}
ret = pwm_enable(pwm_dev_2);
if(ret == 0){
printk("pwm_enable_dev_2 init success\n");
}
if(ret < 0 ){
printk("pwm_enable_dev_2 init fail\n");
return 0;
}
ret = pwm_enable(pwm_dev_3);
if(ret == 0){
printk("pwm_enable_dev_3 init success\n");
}
if(ret < 0 ){
printk("pwm_enable_dev_3 init 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_2,0,50000);
pwm_disable(pwm_dev_2);
pwm_free(pwm_dev_2);
pwm_config(pwm_dev_3,0,50000);
pwm_disable(pwm_dev_3);
pwm_free(pwm_dev_3);
}
module_init(pwm_init);
module_exit(pwm_exit);
MODULE_LICENSE("GPL");
编写pwn_test测试程序
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#define PWM_ON 0x100001
#define PWM_OFF 0x100002
int main(void)
{
int fd;
int a;
fd = open("/dev/mypwm", O_RDWR);
if (fd < 0)
return -1;
printf("pwm_start\n");
while(1) {
ioctl(fd, PWM_ON);
}
close(fd);
return 0;
}
将pwm_drv.c编译为驱动模块,pwm_test.c编译后测试,成功输出PWM