添加的功能
procfs
在添加设备到proc目录下,procfs,sysfs,debugfs注册方式类似,可以提供一文件访问接口,在proc_dir_entry中有fops,如read_proc,wirte_proc和fops都进行初始化,在使用echo 或者cat等命令访问文件时,fops执行。如下图:
当注释掉fops之后
这两个之间,有个优先级的关系,内核代码还没有分析,先挖个坑。
static struct proc_dir_entry *proc_dir,*proc_file;
int led_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
gpio = (struct gpio_led *)data;
printk("proc read\n");
sprintf(page,"%s proc read\n",gpio->mem);
return count ;
}
int led_proc_write(struct file *file, const char *buffer,unsigned long count, void *data)
{
int len,ret;
gpio = (struct gpio_led *)data;
if(count > SIZE)
len = SIZE;
else
len = count;
ret = copy_from_user(gpio->mem, buffer, len);
if(ret)
printk(KERN_EMERG "copy_from_user failed\n");
gpio->mem[len]='\0';
printk("proc write\n");
return count ;
}
//init函数中
proc_dir = proc_mkdir(NAME,NULL);// 创建proc下的目录
proc_file = create_proc_entry(NAME,0644,proc_dir);//在该目录下创建文件
proc_file -> read_proc = led_proc_read;//关联读函数
proc_file -> write_proc = led_proc_write;//关联写函数
//proc_file -> proc_fops = &led_proc_fops;//priority higher than proc_read and write
中断
申请中断的gpio,对gpio口进行初始化,并释放,之后申请中断号,注册相应的中断处理函数。在中断中添加了tasklet。在open中有spin_lock_irq,是为了验证在文件打开后没有释放锁时,中断确实是关闭的 。此时摁按键,中断函数不会执行。
查手册可发现9与10对应int1[1],int1[2],需要设置为0xf,且为下降沿出触发。
static irqreturn_t eint9_interrupt(int irq,void *dev)
{
gpio = (struct gpio_led *)dev;
printk("int9 %s\n",gpio->name);
printk("%s(%d)\n", __FUNCTION__,__LINE__);
if(gpio_get_value(gpio_led_array[0]))
gpio_set_value(gpio_led_array[0],0);
else
gpio_set_value(gpio_led_array[0],1);
tasklet_schedule(&gpio_tasklet);
return IRQ_HANDLED;
}
static irqreturn_t eint10_interrupt(int irq,void *dev)
{
gpio = (struct gpio_led *)dev;
printk("int10 %s\n",gpio->name);
printk("%s(%d)\n", __FUNCTION__,__LINE__);
if(gpio_get_value(gpio_led_array[1]))
gpio_set_value(gpio_led_array[1],0);
else
gpio_set_value(gpio_led_array[1],1);
return IRQ_HANDLED;
}
//init函数中
ret = gpio_request(EXYNOS4_GPX1(1),"irq1");
if (ret)
printk(KERN_EMERG"interrupt1 request failed ret = %d\n",ret);
ret = gpio_request(EXYNOS4_GPX1(2),"irq2");
if (ret)
printk(KERN_EMERG"interrupt2 request failed ret = %d\n",ret);
s3c_gpio_cfgpin(EXYNOS4_GPX1(1),S3C_GPIO_SFN(0XF));
s3c_gpio_setpull(EXYNOS4_GPX1(1),S3C_GPIO_PULL_UP);
gpio_free(EXYNOS4_GPX1(1));
s3c_gpio_cfgpin(EXYNOS4_GPX1(2),S3C_GPIO_SFN(0XF));
s3c_gpio_setpull(EXYNOS4_GPX1(1),S3C_GPIO_PULL_UP);
gpio_free(EXYNOS4_GPX1(2));
ret = request_irq(IRQ_EINT(9),eint9_interrupt,IRQ_TYPE_EDGE_FALLING,"interrupt9",gpio);
if(ret){
printk("request_irq faile IRQ_EINT(9) = %d",IRQ_EINT(9));
}
ret = request_irq(IRQ_EINT(10),eint10_interrupt,IRQ_TYPE_EDGE_FALLING,"interrupt10",gpio);
if(ret){
printk("request_irq faile IRQ_EINT(10) = %d",IRQ_EINT(10));
PWM
此处主要时为了学一下如何使用数据手册。
时钟源有内部的和外部的 ,时钟经过倍频后分频,得到PCLK、FCLK和HCLK,PCLK供APB总线使用,HCLK供AHB总线使用,FCLK则供cpu使用。
pclk经过(1-255)的预分频器,得到合适的时钟信号,由多路选择器mux输出分频之后时钟信号,时钟信号的计时需要用到TCMP和TCNT两个寄存器。TCNT>TCMP,TCNT是递减计数,每有一次时钟信号则减一,当等于TCMP的值时,电平反转,当减到零时,电平再次反转。pwm的占空比就由两个寄存器的值确定了。
由此可知想要产生一个pwm,需要配置一个CPIO口,并且初始化该口相应的TCMP和TCNT寄存器。
蜂鸣器链接的是MOTOR_PWM,对应端口是GPIOD0,查数据手册得:
GPD0控制寄存器,低四位赋值0X2确定为模拟信号输出
GPIOD0数据寄存器,这里使用默认值即可
GPIOD0上/下拉寄存器,控制端口上拉还是下拉,此处要输出pwm波,必要关掉。
以上就是GPIO的配置过程,之后还需要配置TCMP和TCNT寄存器。
在数据手册中直接搜PWM
由此可知PCLK是66Mhz。、
数据手册中说的很清楚,想要高占空比,就增大TCMP,反之则减小。起始电平是低,cnt--,减到和cmp的值一样了,反转从成高电平,cnt继续减减,减到0,变成低电平。因此cmp值越大,低电平时间越少。占空比约大。
继续往下找则会查到相应的寄存器,这个才是想要的
这里用到了前五个寄存器。
用到是timer0,给低八位赋值即可,我用的是0xf9及就是250.
赋值0x4,16分频
赋值cmp和cnt寄存器,调节占空比
0x3 更新cnt和cmp的值并启动定时器0
0x8| 0x1 重载并启动定时器0
volatile unsigned long virt_addr_tcfg,virt_addr_gpio,phys_addr_tcfg,phys_addr_gpio;//用于存放虚拟地址和物理地址
volatile unsigned long *GPD0CON,*GPD0PUD,*GPD0DAT;
struct {
unsigned int TCFG0;
unsigned int TCFG1;
unsigned int TCON;
unsigned int TCNTB0;
unsigned int TCMPB0;
}*pwm;
void pmw_init()
{
phys_addr_gpio = 0x11400000+0xA0;
virt_addr_gpio =(unsigned long ) ioremap(phys_addr_gpio,0x0c);//需要12个字节
GPD0CON = (unsigned long *) virt_addr_gpio;
GPD0DAT = (unsigned long *) (virt_addr_gpio+0x04);
GPD0PUD = (unsigned long *) (virt_addr_gpio+0x08);
*GPD0CON = (*GPD0CON&(~(0xf)))|(0x2);//TOUT_0 响
*GPD0PUD = *GPD0PUD&(~(0xf));//浮空
phys_addr_tcfg = EXYNOS4_PA_TIMER;// 0x139D0000
virt_addr_tcfg =(unsigned long ) ioremap(phys_addr_tcfg,0x14);//需要20个字节
pwm = (unsigned long *)virt_addr_tcfg;
(*pwm).TCFG0 = (*pwm).TCFG0 | 0xf9;
(*pwm).TCFG1 = (*pwm).TCFG1 | 0x04;
(*pwm).TCMPB0 = 8;
(*pwm).TCNTB0 = 16;
(*pwm).TCON = (*pwm).TCON | 0x03;
(*pwm).TCON = (*pwm).TCON | 0x01 | 0x08;
}
示例代码
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/platform_device.h>
#include<linux/miscdevice.h>
#include<linux/slab.h>
#include<linux/device.h>
#include<linux/gpio.h>
#include<plat/gpio-cfg.h>
#include<mach/gpio.h>
#include<mach/gpio-exynos4.h>
#include<mach/map-exynos4.h>//0x139D0000
#include<linux/miscdevice.h>
#include<linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/jiffies.h>
#include <asm/uaccess.h>//copy_from_user
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/limits.h>
#include <asm/io.h>
#include<linux/jiffies.h>//计时器
#define NAME "gpio_led"
#define SIZE 50
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("CASEY");
struct gpio_led {
struct cdev cdev;
char *name;
spinlock_t lock;
unsigned char mem[SIZE];
};
static struct gpio_led *gpio;
static struct proc_dir_entry *proc_dir,*proc_file;
int gpio_tasklet_data = 0;
volatile unsigned long virt_addr_tcfg,virt_addr_gpio,phys_addr_tcfg,phys_addr_gpio;//用于存放虚拟地址和物理地址
volatile unsigned long *GPD0CON,*GPD0PUD,*GPD0DAT;
struct {
unsigned int TCFG0;
unsigned int TCFG1;
unsigned int TCON;
unsigned int TCNTB0;
unsigned int TCMPB0;
}*pwm;
void gpio_tasklet_fun(unsigned long data)
{
gpio_tasklet_data++;
printk("call gpio_tasklet data = %lu \n",data);
if(gpio_tasklet_data>10)
printk(KERN_INFO "%s : data = %d\n", __func__, gpio_tasklet_data);
}
DECLARE_TASKLET(gpio_tasklet,gpio_tasklet_fun,0);
int led_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
gpio = (struct gpio_led *)data;
printk("proc read\n");
sprintf(page,"%s proc read\n",gpio->mem);
return count ;
}
int led_proc_write(struct file *file, const char *buffer,unsigned long count, void *data)
{
int len,ret;
gpio = (struct gpio_led *)data;
if(count > SIZE)
len = SIZE;
else
len = count;
ret = copy_from_user(gpio->mem, buffer, len);
if(ret)
printk(KERN_EMERG "copy_from_user failed\n");
gpio->mem[len]='\0';
printk("proc write\n");
return count ;
}
static struct class *this_class;
int dev_major;
int dev_minor;
static int gpio_led_array[] = {
EXYNOS4_GPK1(1),
EXYNOS4_GPL2(0),
};
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
// struct gpio_led *dev = file -> private_date;
printk("cmd is %d, arg is %ld \n",cmd,arg);
gpio_set_value(gpio_led_array[arg],cmd);
return 0;
}
static int led_open(struct inode *inode, struct file *file)
{
// file -> private_date = gpio;
/* 自选锁实验,在open后,按键的中断被禁止了
int k =1000;
spin_lock_irq(&gpio->lock);
while(k--)
printk("locking\n");
spin_unlock_irq(&gpio->lock);
*/
printk("led open\n");
return 0;
}
static int led_release(struct inode *inode, struct file *file)
{
printk("led release\n");
return 0;
}
static ssize_t led_read(struct file *filei,char __user *buff,size_t count,loff_t *ppos)
{
printk("led read\n");
return 0;
}
static ssize_t led_write(struct file *file,const char __user * buff,size_t count,loff_t *ppos)
{
printk("led write\n");
return 0;
}
static struct file_operations led_fops={
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,//close
.unlocked_ioctl = led_ioctl,
};
// pay attention to the return value ,irqreturn_t is function type
static irqreturn_t eint9_interrupt(int irq,void *dev)
{
gpio = (struct gpio_led *)dev;
printk("int9 %s\n",gpio->name);
printk("%s(%d)\n", __FUNCTION__,__LINE__);
if(gpio_get_value(gpio_led_array[0]))
gpio_set_value(gpio_led_array[0],0);
else
gpio_set_value(gpio_led_array[0],1);
tasklet_schedule(&gpio_tasklet);
return IRQ_HANDLED;
}
static irqreturn_t eint10_interrupt(int irq,void *dev)
{
gpio = (struct gpio_led *)dev;
printk("int10 %s\n",gpio->name);
printk("%s(%d)\n", __FUNCTION__,__LINE__);
if(gpio_get_value(gpio_led_array[1]))
gpio_set_value(gpio_led_array[1],0);
else
gpio_set_value(gpio_led_array[1],1);
return IRQ_HANDLED;
}
const static struct file_operations led_proc_fops={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,//close
.unlocked_ioctl = led_ioctl,
};
void pmw_init()
{
phys_addr_gpio = 0x11400000+0xA0;
virt_addr_gpio =(unsigned long ) ioremap(phys_addr_gpio,0x0c);
GPD0CON = (unsigned long *) virt_addr_gpio;
GPD0DAT = (unsigned long *) (virt_addr_gpio+0x04);
GPD0PUD = (unsigned long *) (virt_addr_gpio+0x08);
*GPD0CON = (*GPD0CON&(~(0xf)))|(0x2);//TOUT_0 响
*GPD0PUD = *GPD0PUD&(~(0xf));//浮空
phys_addr_tcfg = EXYNOS4_PA_TIMER;// 0x139D0000
virt_addr_tcfg =(unsigned long ) ioremap(phys_addr_tcfg,0x14);
pwm = (unsigned long *)virt_addr_tcfg;
(*pwm).TCFG0 = (*pwm).TCFG0 | 0xf9;
(*pwm).TCFG1 = (*pwm).TCFG1 | 0x04;
(*pwm).TCMPB0 = 8;
(*pwm).TCNTB0 = 16;
(*pwm).TCON = (*pwm).TCON | 0x03;
(*pwm).TCON = (*pwm).TCON | 0x01 | 0x08;
}
static int led_init(void)
{
int ret,i;
dev_t devnum=0;
/*
baseminor :次设备号的起始
count: 申请次设备号的个数
name :执行 cat /proc/devices显示的名称
*/
int baseminor = 0;
int count = 1;
ret = alloc_chrdev_region(&devnum,baseminor,count,NAME);//动态注册设备号
//devnum = MKDEV(200,0);
//ret = register_chrdev_region(devnum,count,NAME);//静态注册设备号
//以上两种方式都可以在/proc/devices 下读到设备号
if(ret<0){
printk(KERN_EMERG "request fail %d",ret);
}else{
dev_major = MAJOR(devnum);
dev_minor = MINOR(devnum);
printk(KERN_EMERG "major = %d , minor = %d\n",dev_major,dev_minor);
this_class = class_create(THIS_MODULE,NAME);
device_create(this_class,NULL,devnum,NULL,NAME);
gpio = kmalloc(sizeof(struct gpio_led),GFP_KERNEL);
if(!gpio){
printk(KERN_EMERG "request mem failed \n");
goto fail_malloc;
}else{
for(i=0;i<ARRAY_SIZE(gpio_led_array);i++){
ret = gpio_request(gpio_led_array[i],"GPIO_LED");
if(ret){
printk(KERN_EMERG "request_gpio failed ret = %d \n",ret);
}
s3c_gpio_cfgpin(gpio_led_array[i],S3C_GPIO_OUTPUT);
gpio_set_value(gpio_led_array[i],0);
}
cdev_init(&gpio->cdev,&led_fops);
gpio->cdev.owner = THIS_MODULE;
gpio->name = NAME;
ret = cdev_add(&gpio->cdev,devnum,1);
/**************proc fs****************/
proc_dir = proc_mkdir(NAME,NULL);// create directory
proc_file = create_proc_entry(NAME,0644,proc_dir);//创建文件,在刚申请的路径下。
proc_file -> read_proc = led_proc_read;
proc_file -> write_proc = led_proc_write;
spin_lock_init(&gpio->lock);//初始化自旋锁
//proc_file -> proc_fops = &led_proc_fops;//priority higher than proc_read and write
strcpy(gpio->mem, "procfs"); proc_file -> data = gpio;
/**************proc fs*************/
/***************interrupt **************/
ret = gpio_request(EXYNOS4_GPX1(1),"irq1");
if (ret)
printk(KERN_EMERG"interrupt1 request failed ret = %d\n",ret);
ret = gpio_request(EXYNOS4_GPX1(2),"irq2");
if (ret)
printk(KERN_EMERG"interrupt2 request failed ret = %d\n",ret);
s3c_gpio_cfgpin(EXYNOS4_GPX1(1),S3C_GPIO_SFN(0XF));
s3c_gpio_setpull(EXYNOS4_GPX1(1),S3C_GPIO_PULL_UP);
gpio_free(EXYNOS4_GPX1(1));
s3c_gpio_cfgpin(EXYNOS4_GPX1(2),S3C_GPIO_SFN(0XF));
s3c_gpio_setpull(EXYNOS4_GPX1(1),S3C_GPIO_PULL_UP);
gpio_free(EXYNOS4_GPX1(2));
ret = request_irq(IRQ_EINT(9),eint9_interrupt,IRQ_TYPE_EDGE_FALLING,"interrupt9",gpio);
if(ret){
printk("request_irq faile IRQ_EINT(9) = %d",IRQ_EINT(9));
}
ret = request_irq(IRQ_EINT(10),eint10_interrupt,IRQ_TYPE_EDGE_FALLING,"interrupt10",gpio);
if(ret){
printk("request_irq faile IRQ_EINT(10) = %d",IRQ_EINT(10));
}
/***************interrupt **************/
/**************pwm******************/
pmw_init();
/**************pwm*****************/
printk(KERN_EMERG "success \n");
}
}
printk(KERN_EMERG "led diver Init\n");
return 0;
// 此处踩了个小坑,正常的return要在这种错误处理之前,否则会执行,所以一开始/proc下没有打印设备号,因为被下面的代码取消了
fail_malloc:
unregister_chrdev_region(devnum,1);
return 1;
}
static void led_exit(void)
{
int i;
dev_t devnum = MKDEV(dev_major,dev_minor);
for(i=0;i<ARRAY_SIZE(gpio_led_array);i++){
gpio_free(gpio_led_array[i]);
}
free_irq(IRQ_EINT(9),gpio);
free_irq(IRQ_EINT(10),gpio);
(*pwm).TCON = (*pwm).TCON | 0x00;
*GPD0CON = (*GPD0CON)|(0x1);//OUTPUT
*GPD0DAT = (*GPD0DAT)|(0x0);//拉低
cdev_del(&gpio->cdev);
kfree(gpio);
unregister_chrdev_region(devnum,1);
device_destroy(this_class,devnum);
class_destroy(this_class);
remove_proc_entry(NAME, proc_dir);
remove_proc_entry(NAME, NULL);
printk(KERN_EMERG "led driver exit\n");
}
module_init(led_init);
module_exit(led_exit);