Linux2.6.32.2内核在mini2440上的移植(七)添加ADC驱动

news2025/1/16 1:31:22

Linux-2.6.32.2内核在mini2440上的移植()---添加ADC驱动  

2】在内核中添加ADC 驱动

Linux-2.6.32.2 内核并没有提供支持S3C2440 ADC 驱动程序,由于《移植开发实战指南》中ADC部分代码在实际测试中始终输出-1,而无法通过测试,于是结合博主黄刚嵌入式Linux之我行——S3C2440上ADC驱动实例开发讲解ADC驱动程序作了下修改,经过修改后有一个好处是方便地通过s3c24xx-adc.h文件中提供的宏修改通道获取采样数据,该头文件的代码也在drivers/char目录下内容为:

#ifndef _S3C2410_ADC_H_
#define _S3C2410_ADC_H_

#define ADC_WRITE(ch, prescale) ((ch)<<16|(prescale))
#define ADC_WRITE_GETCH(data) (((data)>>16)&0x7)
#define ADC_WRITE_GETPRE(data) ((data)&0xff)

#endif /* _S3C2410_ADC_H_ */

驱动程序的文件名为:mini2440_adc.c位于drivers/char 目录下。由上述内容可知,ADC 驱动和触摸屏驱动若想共存,就必须解决共享“A/D 转换器资源这个问题,因此在ADC 驱动程序中声明了一个全局的“ADC_LOCK”信号量,ADC 驱动程序的内容和注解如下:

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

//;自己定义的头文件,因原生内核并没有包含
#include "s3c24xx-adc.h"

#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(__FUNCTION__"(%d): ",__LINE__);printk(##x);}
#else
#define DPRINTK(x...) (void)(0)
#endif

//;定义ADC 转换设备名称,将出现在/dev/adc
#define DEVICE_NAME "adc"

static void __iomem *adc_base;/*定义了一个用来保存经过虚拟映射后的内存地址*/

//;定义ADC 设备结构
typedef struct {
 wait_queue_head_t wait;
 int channel;
 int prescale;
}ADC_DEV;
static ADC_DEV adcdev;

//;声明全局信号量,以便和触摸屏驱动程序共享A/D 转换器
DECLARE_MUTEX(ADC_LOCK);

//;ADC驱动是否拥有A/D 转换器资源的状态变量
//static volatile int OwnADC = 0;

/*用于标识AD转换后的数据是否可以读取,0表示不可读取*/
static volatile int ev_adc = 0;

/*用于保存读取的AD转换后的值,该值在ADC中断中读取*/
static int adc_data;

/*保存从平台时钟队列中获取ADC的时钟*/
static struct clk *adc_clk;

//;定义ADC 相关的寄存器
#define ADCCON (*(volatile unsigned long *)(adc_base + S3C2410_ADCCON)) //ADC control
#define ADCTSC (*(volatile unsigned long *)(adc_base + S3C2410_ADCTSC)) //ADC touch screen control
#define ADCDLY (*(volatile unsigned long *)(adc_base + S3C2410_ADCDLY)) //ADC start or IntervalDelay
#define ADCDAT0 (*(volatile unsigned long *)(adc_base + S3C2410_ADCDAT0)) //ADC conversion data 0
#define ADCDAT1 (*(volatile unsigned long *)(adc_base + S3C2410_ADCDAT1)) //ADC conversion data 1
#define ADCUPDN (*(volatile unsigned long *)(adc_base + 0x14)) //Stylus Up/Down interrupt status
#define PRESCALE_DIS (0 << 14)
#define PRESCALE_EN (1 << 14)
#define PRSCVL(x) ((x) << 6)
#define ADC_INPUT(x) ((x) << 3)
#define ADC_START (1 << 0)
#define ADC_ENDCVT (1 << 15)

//;定义开启AD 输入宏,因为比较简单,故没有做成函数

//#define START_ADC_AIN(ch, prescale)
#define start_adc(ch, prescale) \
do{ \
 ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \
 ADCCON |= ADC_START; \
}while(0)
/*设置ADC控制寄存器,开启AD转换*/
/*static void start_adc(int ch,int prescale)
{
    unsigned int tmp;

    tmp = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT(ch);//(1 << 14)|(255 << 6)|(0 << 3);// 0 1 00000011 000 0 0 0
 //此处writl()的原型是void writel(u32 b, volatile void __iomem *addr),addr是经过地址重映射后的地址
    writel(tmp, ADCCON); //AD预分频器使能、模拟输入通道设为AIN0

    tmp = readl(ADCCON);
    tmp = tmp | ADC_START; //(1 << 0);   // 0 1 00000011 000 0 0 1
    writel(tmp, ADCCON);   //AD转换开始
}
问题:此函数被调用时为什么地址映射错误?答案应该需要使用专用的函数iowrite32操作。
*/

//;ADC 中断处理函数
static irqreturn_t adc_irq(int irq, void *dev_id)
{
 //;如果ADC 驱动拥有“A/D 转换器资源,则从ADC 寄存器读取转换结果
 if (!ev_adc)
 {
  /*读取AD转换后的值保存到全局变量adc_data中,S3C2410_ADCDAT0定义在regs-adc.h中,
           这里为什么要与上一个0x3ff,很简单,因为AD转换后的数据是保存在ADCDAT0的第0-9位,
           所以与上0x3ff(即:1111111111)后就得到第0-9位的数据,多余的位就都为0*/
  adc_data = ADCDAT0 & 0x3ff;

  /*将可读标识为1,并唤醒等待队列*/
  ev_adc = 1;
  wake_up_interruptible(&adcdev.wait);
 }
 return IRQ_HANDLED;
}

//;ADC 读函数,一般对应于用户层/应用层的设备读函数(read)
static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
 
 /*试着获取信号量(即:加锁)*/
 if (down_trylock(&ADC_LOCK))
 {
  return -EBUSY;
 }
 if(!ev_adc) /*表示还没有AD转换后的数据,不可读取*/
     {
      if(filp->f_flags & O_NONBLOCK)
         {
              /*应用程序若采用非阻塞方式读取则返回错误*/
         return -EAGAIN;
         }
      else /*以阻塞方式进行读取*/
         {
              /*设置ADC控制寄存器,开启AD转换*/
         start_adc(adcdev.channel, adcdev.prescale);

              /*使等待队列进入睡眠*/
         wait_event_interruptible(adcdev.wait, ev_adc);
         }
     }
 /*能到这里就表示已有AD转换后的数据,则标识清0,给下一次读做判断用*/
   ev_adc = 0;

     /*将读取到的AD转换后的值发往到上层应用程序*/
   copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

     /*释放获取的信号量(即:解锁)*/
   up(&ADC_LOCK);

   return sizeof(adc_data);
 
}

//;打开ADC设备的函数,一般对应于用户态程序的open
static int adc_open(struct inode *inode, struct file *filp)
{
 int ret; 
 /* normal ADC */
 ADCTSC = 0;
 //;初始化中断队列
 init_waitqueue_head(&(adcdev.wait));
 adcdev.channel=0;//;缺省通道为“0”
 adcdev.prescale=0xff;
 /* 申请ADC中断服务,这里使用的是共享中断:IRQF_SHARED,为什么要使用共享中断,因为在触摸屏驱动中
      也使用了这个中断号。中断服务程序为:adc_irq在下面实现,IRQ_ADCADC的中断号,这里注意:
      申请中断函数的最后一个参数一定不能为NULL,否则中断申请会失败,这里传入的是ADC_DEV类型的变量*/
 ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, &adcdev);
 if (ret)
     {
           /*错误处理*/
        printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret);
        return -EINVAL;
     }

 DPRINTK( "adc opened\n");
   return 0;

}
static int adc_release(struct inode *inode, struct file *filp)
{
 DPRINTK( "adc closed\n");
 return 0;
}
static struct file_operations dev_fops = {
 owner: THIS_MODULE,
 open: adc_open,
 read: adc_read,
 release: adc_release,
};
static struct miscdevice adc_miscdev = {
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};
static int __init dev_init(void)
{
 int ret;
 /*  1,从平台时钟队列中获取ADC的时钟,这里为什么要取得这个时钟,因为ADC的转换频率跟时钟有关。
     系统的一些时钟定义在arch/arm/plat-s3c24xx/s3c2410-clock.c*/
 adc_clk = clk_get(NULL, "adc");
 if (!adc_clk) {
  printk(KERN_ERR "failed to get adc clock source\n");
  return -ENOENT;
 }
 /*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c*/
 clk_enable(adc_clk);

 /*  2,ADCIO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
      注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,
   S3C2410_PA_ADC
ADC控制器的基地址,定义在mach-s3c2410/include/mach/map.h中,0x20是虚拟地址长度大小*/
 adc_base=ioremap(S3C2410_PA_ADC,0x20);
 if (adc_base == NULL) {
  printk(KERN_ERR "Failed to remap register block\n");
  ret = -EINVAL;
      goto err_noclk;
 }

 /*   3,把看ADC注册成为misc设备,misc_register定义在miscdevice.h
   adc_miscdev结构体定义及内部接口函数在第2步中讲,MISC_DYNAMIC_MINOR是次设备号,定义在miscdevice.h*/
   ret = misc_register(&adc_miscdev);
   if (ret)
     {
          /*错误处理*/
      printk(KERN_ERR "Cannot register miscdev on minor=%d (%d)\n", MISC_DYNAMIC_MINOR, ret);
      goto err_nomap;
     }
 
   printk(DEVICE_NAME "\tinitialized!\n");
 
 return 0;
 
//以下是上面错误处理的跳转点
err_noclk:
   clk_disable(adc_clk);
   clk_put(adc_clk);

err_nomap:
   iounmap(adc_base);

   return ret;

}

static void __exit dev_exit(void)
{
 
 free_irq(IRQ_ADC, &adcdev); //;释放中断
 iounmap(adc_base); /*释放虚拟地址映射空间*/
 if (adc_clk)  /*屏蔽和销毁时钟*/
 {
  clk_disable(adc_clk);
  clk_put(adc_clk);
  adc_clk = NULL;
 }
 misc_deregister(&adc_miscdev);
}
//;导出信号量“ADC_LOCK”,以便触摸屏驱动使用
EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("singleboy");
MODULE_DESCRIPTION("Mini2440 ADC Driver");

说明:杂项设备(misc device
杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include/linux 目录下有Miscdevice.h 文件,要把自己定义的misc device 从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10,一起归于misc device,其实misc_register 就是用主标号10 调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备。

然后打开drivers/char/Makefile 文件,在大概24 行加入ADC 驱动程序目标模块

obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_C2PORT)  += c2port/

obj-$(CONFIG_MINI2440_ADC) += mini2440_adc.o
obj-y    += eeprom/
obj-y    += cb710/

再打开drivers/char/Kconfig 文件,定位到16行附近,加入ADC 驱动配置选项:

menuconfig MISC_DEVICES
 bool "Misc devices"
 default y
 ---help---
   Say Y here to get to see options for device drivers from various
   different categories. This option alone does not add any kernel code.

   If you say N, all options in this submenu will be skipped and disabled.

if MISC_DEVICES

config MINI2440_ADC
 bool "ADC driver for FriendlyARM Mini2440 development boards"
 depends on MACH_MINI2440
 default y if MACH_MINI2440
 help
  this is ADC driver for FriendlyARM Mini2440 development boards
  Notes: the touch-screen-driver required this option

config ATMEL_PWM
 tristate "Atmel AT32/AT91 PWM support"
 depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
 help
   This option enables device driver support for the PWM channels
   on certain Atmel processors.  Pulse Width Modulation is used for
   purposes including software controlled power-efficient backlights
   on LCD displays, motor control, and waveform generation.

这样,我们就在内核中添加了ADC 驱动。

3】确认配置选项

现在内核源代码目录的命令行执行:make menuconfig,依次选择如下子菜单项,找到刚刚添加的ADC 驱动配置选项:
Device Drivers --->
      [*] Misc devices  --->  

如图所示,按空格键选中 ADC 配置选项

 

然后退出保存所选配置, 在命令行执行: make zImage 将会生成arch/arm/boot/zImage

brd: module loaded
adc     initialized!
S3C24XX NAND Driver, (c) 2004 Simtec Electronics

... ...

说明ADC设备加载成功。

 “adc-test” 测试程序已经集成到我们的文件系统中, 因此在开发板的命令行终端输入:adc-test,旋转开发板上的 W1可调电阻,可以看到 ADC 转换的结果也在变动,按下触摸屏时,会输出“-1” ,这和我们在驱动程序中设置的结果是一样的,如图:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/829999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

离散Hopfield神经网络的联想记忆与matlab实现

1案例背景 1.1离散Hopfield神经网络概述 Hopfield网络作为一种全连接型的神经网络,曾经为人工神经网络的发展开辟了新的研究途径。它利用与阶层型神经网络不同的结构特征和学习方法,模拟生物神经网络的记忆机理,获得了令人满意的结果。这一网络及学习算法最初是由美国物理学家…

01背包详解(二维到一维)

有 N件物品和一个容量为 V 的背包&#xff0c;每件物品有各自的价值且只能被选择一次&#xff0c;要求在有限的背包容量下&#xff0c;装入的物品总价值最大。「0-1 背包」是较为简单的动态规划问题&#xff0c;也是其余背包问题的基础。 动态规划是不断决策求最优解的过程&am…

基于Stm32的宠物自动喂食装置(包含::论文、代码、外文原文、外文翻译、手册、建模、答辩PPT、原理图等 )

基于Stm32的宠物自动喂食装置 目录 基于Stm32的宠物自动喂食装置 一、Solidworks建模部分 装置外壳 二、TLink物联网平台 1.TLINK平台配置 2.TLINK平台的功能 &#xff08;2&#xff09;定时发送指令 &#xff08;3&#xff09;自动报警 三、Stm32控制部分 1.整体流程图…

STM32 DHT11

DHT11 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。 使用单总线通信 该传感器包括一个电容式感湿元件和一个NTC测温元件&#xff0c;并于一个高性能8位单片机相连&#xff08;模数转换&#xff09;。 DHT11引脚说明 开漏模式下没有输出高电平的能…

代码调试2:coco数据集生成深度图

代码调试:coco数据集生成深度图 作者:安静到无声 个人主页 问题1:图片存在异常,跳过不处理 在获取深度图的时候,直接执代码,会产生以下错误:RuntimeError和ValueError。 因此我重新修改了代码,如果出现以下两种错误,则执行下一次循环,代码如下: 修改之后代码可以…

Day11-Webpack前端工程化开发

Webpack 一 webpack基本概念 遇到问题 开发中希望将文件分开来编写,比如CSS代码,可以分为头部尾部内容,公共的样式。 JS代码也希望拆分为多个文件,分别引入,以后代码比较好维护。 本地图片,希望可以实现小图片不用访问后端,保存在前端代码中就可以了 运行程序时我…

火山引擎DataLeap的Data Catalog系统搜索实践 (上)

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 摘要 火山引擎大数据研发治理套件 DataLeap的Data Catalog系统通过汇总和组织各种元数据&#xff0c;解决了数据生产者梳理数据、数据消费者找数和理解数的业务场景…

生产事故-记一次特殊的OOM排查

0x01 事故背景 2023年3月10日14时19分&#xff0c;C公司开发人员向A公司开发人员反映某开放接口从2023年3月10日14时许开始无法访问和使用。该系统为某基础数据接口服务&#xff0c;基于 HTTP 协议进行通信。按照惯例&#xff0c;首先排查网络是否异常&#xff0c;经运维人员检…

【NX】NX开发入门练习寻找倒圆角设置颜色和添加属性

实现功能如标题&#xff0c;当时学习nx开发用的练习题&#xff0c;用的是nx8.5的32位版本&#xff0c;对于设置属性&#xff0c;一种是可以在界面中查看的属性&#xff0c;一种是隐藏的不能再界面中查看的自定义属性&#xff08;但是可以正常读取&#xff09;。 下面是完整代码…

Netty:ByteBuf的引用计数

说明 Netty的ByteBuf有一个对它本身的引用计数。 可以通过ByteBuf的retain()增加1个引用计数&#xff0c;通过retain(int increment)增加一定数量的引用计数。 通过release()减少1个引用计数&#xff0c;通过release(int decrement)减少一定数量的引用计数。 通过refCnt()查看…

科普 | 以太坊坎昆升级是什么

坎昆升级是什么 坎昆&#xff0c;是墨西哥一个著名的旅游城市&#xff0c;也是 Devcon 3 大会的举办地&#xff0c;按照以太坊升级命名的规律&#xff0c;以地名命名的升级&#xff0c;是针对以太坊执行层的升级。 之前同样命名的还有柏林升级、伦敦升级和这次的上海升级等。…

webapi部署几个错误

第一个错误&#xff1a;无法读取配置节“system.serviceModel”&#xff0c;因为它缺少节声明 发现服务器上没有安装.net Framework 3.5.1 第二个错误&#xff1a;未能从程序集“SYSTEM.SERVICEMODEL, VERSION3.0.0.0 先安装.net framework 4.0再安装 3.5 就会出现这个问题…

8.3线程状态

new: 调用start()方法之前的状态. blocked: 因为锁而阻塞. waiting: 因为调用wait()方法而阻塞. timed_waiting: 因为调用sleep()方法而阻塞. terminated: run()方法执行完毕之后线程的状态.

opencv-33 图像平滑处理-中值滤波cv2.medianBlur()

中值滤波是一种常见的图像处理滤波技术&#xff0c;用于去除图像中的噪声。它的原理是用一个滑动窗口&#xff08;也称为卷积核&#xff09;在图像上移动&#xff0c;对窗口中的像素值进行排序&#xff0c;然后用窗口中像素值的中值来替换中心像素的值。这样&#xff0c;中值滤…

【Valgrind】Valgrind安装(ubuntu系统)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

SonarQube入门 - 搭建本地环境

一、SonarQube是什么&#xff1f; SonarQube是一种自我管理的自动代码审查工具&#xff0c;可以系统地帮助您交付干净的代码。作为我们Sonar 解决方案的核心元素 &#xff0c;SonarQube 集成到您现有的工作流程中并检测代码中的问题&#xff0c;以帮助您对项目执行持续的代码检…

GD32F103VE点灯

GD32F103VE点灯主要用来学习端口引脚的输出配置。它由LED.c&#xff0c;LED.h&#xff0c;SoftDelay.c和main.c组成。 #include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t #include "SoftDelay.h"#include …

企业微信小程序在调用wx.qy.login时返回错误信息qy.login:fail

原因是大概是绑定了多个企业但是在开发者工具中没有选择正确的企业 解决方法&#xff1a; 重新选择企业后即可成功获取code

BS框架说明

B/S架构 1.B/S框架&#xff0c;意思是前端&#xff08;Browser 浏览器&#xff0c;小程序、app、自己写的&#xff09;和服务器端&#xff08;Server&#xff09;组成的系统的框架结构 2.B/S框架&#xff0c;也可理解为web架构&#xff0c;包含前端、后端、数据库三大组成部分…

考研/面试 数据结构大题必会代码(理解+记忆,实现使用C++,STL库)

文章目录 一. 线性表1. 逆置顺序表所有元素2. 删除线性链表中数据域为 item 的所有结点3. 逆转线性链表(递归(快速解题)非递归)4. 复制线性链表&#xff08;递归&#xff09;5. 将两个按值有序排列的非空线性链表合并为一个按值有序的线性链表 二. 树1. 建立二叉树&#xff08;…