2021.05.16 嵌入式学习笔记2:proc文件,中断,io映射

添加的功能

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);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值