linux驱动程序开发-第十节:动态定时器实现底层硬件工作状态

       linux 内核动态定时器是依赖于 linux 内核时钟的,动态定时器的周期只能是内核时钟周期的整数倍。
动态定时器不是硬件定时器的驱动,而是利用内核动态定时器产生的时钟周期。
       在项目当中常见于表示硬件工作的繁忙状态,不需要应用层控制参与。例如硬盘在频繁读写数据的时候,会发
现硬盘灯快速闪烁,空闲的时候慢速闪烁,这种实现方法就可以使用内核的动态定时器实现。

资料链接:链接:https://pan.baidu.com/s/1U30LSw4CU_yfSfpaoY6J9A 
提取码:qeph 


adc_drv.c

/*
	在项目当中常见于表示硬件工作的繁忙状态,不需要应用层控制参与。例如硬盘在频繁读写数据的时候,会发
	现硬盘灯快速闪烁,空闲的时候慢速闪烁,这种实现方法就可以使用内核的动态定时器实现。动态定时器不是硬件定时器的驱动
	
	本测试主要是实现led 闪烁频率受ADC 的电压高低影响,电压越高闪烁越快,否则相反,因为我们希望不需要应用层来控制闪烁频率,
	应用层主要是启动ADC工作即可,后面自动进入底层驱动工作,利用动态定时器实现循环自主控制led 闪烁。
*/

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>

#include <linux/io.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <cfg_type.h>

static struct timer_list gec6818_timer;

static unsigned int timer_period=HZ;

#define GEC6818_ADC_IN0		_IOR('A',  1, unsigned long)
#define GEC6818_ADC_IN1		_IOR('A',  2, unsigned long)

static void __iomem		*adc_base_va;		//adc的虚拟地址基址
static void __iomem		*adccon_va;		
static void __iomem		*adcdat_va;	
static void __iomem		*prescalercon_va;

void gec6818_timer_handler(unsigned long data)
{
	static int b=0;
	
	
	
	//printk("gec6818_timer_handler,data=%lu\n",data);
	
	//重新修改超时时间,进行周期性的超时
	mod_timer(&gec6818_timer,jiffies+timer_period);	
	
	//添加D7灯的操作,反转电平
	
	b=!b;
	
	gpio_set_value(PAD_GPIO_E+13,b);
	
}

static int  gec6818_adc_open (struct inode * inode, struct file *file)
{
	printk("gec6818_adc_open \n");
	
	return 0;
}

static int  gec6818_adc_release (struct inode * inode, struct file *file)
{
	printk("gec6818_adc_release \n");
	
	return 0;
}


static long gec6818_adc_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
	unsigned long adc_val=0,adc_vol=0; 
	
	int rt=0;
	//adc通道选择
	switch(cmd)
	{
		case GEC6818_ADC_IN0:
				iowrite32(ioread32(adccon_va)&(~(7<<3)),adccon_va);
		break;
		
		
		case GEC6818_ADC_IN1:
		{
				iowrite32(ioread32(adccon_va)&(~(7<<3)),adccon_va);
				iowrite32(ioread32(adccon_va)|(1<<3),adccon_va);
		}break;		
		
		default:
			printk("IOCTLCMD failed\n");
			return -ENOIOCTLCMD;
	}
	
	//将电源开启
	iowrite32(ioread32(adccon_va)&(~(1<<2)),adccon_va);
	
	//预分频值=199+1,ADC的工作频率=200MHz/(199+1)=1MHz
	iowrite32(ioread32(prescalercon_va)&(~(0x3FF<<0)),prescalercon_va);
	iowrite32(ioread32(prescalercon_va)|(199<<0),prescalercon_va);
	
	//预分频值使能
	iowrite32(ioread32(prescalercon_va)|(1<<15),prescalercon_va);
	
	//ADC使能
	//iowrite32(ioread32(adccon_va)&(~(1<<0)),adccon_va);
	iowrite32(ioread32(adccon_va)|(1<<0),adccon_va);
	
	//等待AD转换结束
	while(ioread32(adccon_va)&(1<<0));
	
	//读取12bit数据
	adc_val = ioread32(adcdat_va)&0xFFF;
	
	//关闭CLKIN时钟输入
	iowrite32(ioread32(prescalercon_va)&(~(1<<15)),prescalercon_va);
	
	//关闭ADC电源
	iowrite32(ioread32(adccon_va)|(1<<2),adccon_va);
	
	//将AD转换的结果值 换算为 电压值
	adc_vol = adc_val*1800/4095;
	
	//设置动态定时器的超时时间
	if(adc_vol <=450)
		timer_period=50;
	else if(adc_vol >450 && adc_vol<=900)
		timer_period=100;
	else if(adc_vol >900 && adc_vol<=1350)
		timer_period=250;
	else
		timer_period=HZ;
	
	rt = copy_to_user((void *)args,&adc_vol,4);
	
	if(rt != 0)
		return -EFAULT;
	return 0;
}

static ssize_t gec6818_adc_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{
	return 0;
}

static const struct file_operations gec6818_adc_fops = {
 	.owner 			= THIS_MODULE,
	.unlocked_ioctl = gec6818_adc_ioctl,
	.open 			= gec6818_adc_open,
	.release 		= gec6818_adc_release,
	.read 			= gec6818_adc_read,
};


static struct miscdevice gec6818_adc_miscdev = {
	.minor		= MISC_DYNAMIC_MINOR,	//MISC_DYNAMIC_MINOR,动态分配次设备号
	.name		= "adc_drv",		//设备名称,/dev/adc_drv	
	.fops		= &gec6818_adc_fops,	//文件操作集
};

//入口函数
static int __init gec6818_adc_init(void)
{
	int rt=0;
	
	//混杂设备的注册
	rt = misc_register(&gec6818_adc_miscdev);
	
	if (rt) 
	{
		printk("misc_register fail\n");
		return rt;
	}
	
	//IO内存动态映射,得到物理地址相应的虚拟地址
	adc_base_va = ioremap(0xC0053000,0x14);
	
	if(adc_base_va == NULL)
	{
		
		printk("ioremap 0xC0053000,0x14 fail\n");
		
		goto fail_ioremap_adc;		
		
	}	
	
	//得到每个寄存器的虚拟地址
	adccon_va 		= adc_base_va+0x00;
	adcdat_va 		= adc_base_va+0x04;	
	
	prescalercon_va = adc_base_va+0x10;	
	
//先释放内核占用的gpio引脚
	gpio_free(PAD_GPIO_E+13);
	
	//申请gpio引脚
	rt = gpio_request(PAD_GPIO_E+13,"gpioe_13");
	
	if(rt < 0)
	{
		printk("gpio_request fail\n");

		goto gpio_request_fail;
		
		
	}
	//配置GPIOE13为输出模式,GPIOE13引脚为高电平
	gpio_direction_output(PAD_GPIO_E + 13,1);	
	
	//初始化动态定时器
	init_timer(&gec6818_timer);
	
	//配置动态定时器
	gec6818_timer.function = gec6818_timer_handler;	//处理函数
	gec6818_timer.expires = jiffies + HZ;			//当前时间开始,HZ个jiffies后,会产生1秒超时		
	gec6818_timer.data	  =  100;						//传递的参数
		
	//动态定时器加入到内核
	add_timer(&gec6818_timer);	
	
	printk("gec6818 adc init\n");
	
	return 0;
	
gpio_request_fail:	
	iounmap(adc_base_va);
	
fail_ioremap_adc:

	misc_deregister(&gec6818_adc_miscdev);
	
	return rt;
}


//出口函数
static void __exit gec6818_adc_exit(void)
{	
	gpio_free(PAD_GPIO_E+13);
	
	//动态定时器从内核删除
	del_timer(&gec6818_timer);	

	iounmap(adc_base_va);

	
	misc_deregister(&gec6818_adc_miscdev);
	
	printk("gec6818 adc exit\n");
}

//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_adc_init。
module_init(gec6818_adc_init);

//驱动程序的出口:rmmod led_drv调用module_exit,module_exit又会去调用gec6818_adc_exit。
module_exit(gec6818_adc_exit)


//模块描述
MODULE_AUTHOR("stephenwen88@163.com");			//作者信息
MODULE_DESCRIPTION("gec6818 adc driver");		//模块功能说明
MODULE_LICENSE("GPL");							//许可证:驱动遵循GPL协议

 

test.c

 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>

#define GEC6818_ADC_IN0		_IOR('A',  1, unsigned long)
#define GEC6818_ADC_IN1		_IOR('A',  2, unsigned long)

int main(int argc, char **argv)
{
	int fd=-1;

	int rt;

	int i=0;
	
	unsigned long adc_vol = 0;
	
	//打开adc设备
	fd = open("/dev/adc_drv",O_RDWR);
	
	if(fd < 0)
	{
		perror("open /dev/adc_drv:");
		
		return fd;
		
	}
	
	
	while(1)
	{
		//读取ADC通道0的电压值
		rt=ioctl(fd,GEC6818_ADC_IN0,&adc_vol);
		
		if(rt != 0)
		{
			printf("adc in0 read filed\r\n");
			
			usleep(50*1000);
			
			continue;
		}
		
		printf("adc in0 vol: %lu=mv\r\n",adc_vol);

		sleep(1);
	}
	
	close(fd);
	
	
	return 0;
}

 

 

 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值