s3c2410多通道adc驱动及测试程序
网上流行很多基于2410的ADC驱动及测试程序。本文所使用的是开发板光盘中带有了经过修改后的adc驱动。笔者在这个基础上再作一点修改。由于那个文件已经删除了版权信息,这里也不知道如何添加了,可以肯定的是,它使用了GPL,这里公开源代码,也算是GPL了。
原来的代码默认使用ADC第0个通道,本文将添加ioctl接口,可以通过应用层的ioctl来选择多个通道。
与原来的代码相比,添加了如下几个方面:
1、添加头文件<linux/ioctl.h>,不过经测试,没有也可以通过编译。
2、修改原来的调试信息为:
#define DEBUG
#ifdef DEBUG /* define it in Makefile of somewhere */
/* KERN_INFO */
#define DPRINTK(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
#else
#define DPRINTK(fmt, ...)
#endif
这个便于查看调试信息。
3、添加ioctl相关宏定义:
#ifndef u16
#define u16 unsigned short
#endif
#define ADC_IOC_MAGIC 0xd2
#define ADC_SET_CHANNEL _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV _IOW(ADC_IOC_MAGIC, 1, u16)
#define ADC_MAX_IOC 2 /* we only have 2 ioctl commands */
#define MAX_ADC 4 /* we have 4 adc chnnels */
/* end of ioctl */
4、添加ioctl接口:
网上有资料说要返回-ENOTTY,不过个人认为这里返回-EINVAL更恰当一些。
*/
static int s3c2410_adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
if ((_IOC_TYPE(cmd) != ADC_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_MAX_IOC))
return -EINVAL;
switch (cmd) {
/* set channel */
case ADC_SET_CHANNEL:
arg &=3; //多此一举??
if (arg > 3)
arg = 3;
adcdev.channel=arg;
break;
case ADC_SET_CLKDIV:
arg &= 0xff; // ??
if (arg > 0xff)
arg = 0xff;
adcdev.prescale=arg;
break;
default:
return -EINVAL;
break;
}
return 0;
}
当然,也要在这个文件的file_operations结构体添加这个接口:
.ioctl = s3c2410_adc_ioctl,
5、copy_to_user
原来的代码使用了sprintf将ADC转换的结果转换为字符串类型,不过却在后面添加一个“/n”,不知是何意。
len = sprintf(str, "%d", value);
……
copy_to_user(buffer, str, len);
也正是这个原因,在测试程序中要使用sscanf将字符串转换成整数类型的才能得到正常的结果。
其它的修改不影响使用。
测试代码也简单,如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <linux/ioctl.h>
#define ADC "/dev/adc"
#ifdef DEBUG
#define debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
#ifndef u16
#define u16 unsigned short
#endif
// 控制字,与驱动一致
#define ADC_IOC_MAGIC 0xd2
#define ADC_SET_CHANNEL _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV _IOW(ADC_IOC_MAGIC, 1, u16)
// 特意的错误控制字
// test
#define AAA 333
// test end
int fd;
// 由于是死循环,需要捕获SIGINT信号
void sig_handle(int sig)
{
//debug("you press ^C: %d/n", sig);
close(fd); /* we colse it here */
exit(0);
}
int main(void)
{
char buff[10];
int val;
int len;
int i;
signal(SIGINT, sig_handle);
debug("Test of ADC. ^C to exit/n");
fd = open(ADC, O_RDWR);
if (fd < 0){
perror("Open /dev/adc failed");
exit(1);
}
// test
/* it will return -EINVAL(which specified in ADC driver) */
if (ioctl(fd, AAA,0) < 0)
perror("ioctl");
while (1) {
/* we have 4 channels ADC*/
for (i=0; i<4; i++) {
ioctl(fd, ADC_SET_CHANNEL, i);
len = read(fd, buff,sizeof(buff));
if (len < 0) {
perror("read adc device failed");
exit(0);
} else {
buff[len] = '/0';
sscanf(buff, "%d", &val);
printf("read AIN[%d]: %d value:%d/n", i, len, val);
}
sleep(1); // 每隔1秒采1个通道
}
//sleep(1); // 每隔1秒采集4个通道
}
close(fd); /* just kidding */
return 0;
}
在开发板上运行结果如下:
read AIN[0]: 3 value:629
read AIN[1]: 3 value:316
read AIN[2]: 3 value:257
read AIN[3]: 3 value:303
read AIN[0]: 1 value:0
read AIN[1]: 1 value:0
read AIN[2]: 3 value:259
read AIN[3]: 3 value:297
……
read AIN[1]: 3 value:468
read AIN[2]: 3 value:299
read AIN[3]: 3 value:327
read AIN[0]: 3 value:629
read AIN[1]: 1 value:0
……
read AIN[0]: 3 value:629
read AIN[1]: 3 value:287
read AIN[2]: 3 value:273
read AIN[3]: 1 value:0
read AIN[0]: 3 value:629
read AIN[1]: 3 value:296
read AIN[2]: 3 value:271
read AIN[3]: 3 value:306
read AIN[0]: 3 value:629
read AIN[1]: 3 value:281
read AIN[2]: 1 value:0
read AIN[3]: 3 value:269
上面显示0的就是用导线将开发板上几个ADC通道分别接触板子上GND出现的。如果接VCC,值应该是1023。就是说,ADC的值为0~1023,ADC驱动将转换结果与0x3ff相与,这可以看datasheet。
至于测试代码中使用sscanf,是因为驱动中使用了sprintf。
驱动的debug信息如下(举例,与前面显示无直接关系):
AIN[3] = 0x012c, 1
AIN[0] = 0x0275, 1
AIN[1] = 0x0146, 1
AIN[2] = 0x0126, 1
AIN[3] = 0x0150, 1
AIN[0] = 0x0275, 1
AIN[1] = 0x012d, 1
AIN[2] = 0x0109, 1
AIN[3] = 0x0126, 1
adc closed
本人虽然学过一段时间的单片机,但是对于电机控制、PWM、ADC等等兴趣不大,了解也不多。其实我也不知这个多通道的ADC能做什么。
文中ioctl控制字参考《ARM徽处理器与应用开发》一书,电子工业出版社出版。
完整的驱动程序如下:
#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/ioctl.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>
#define DEBUG
#ifdef DEBUG /* define it in Makefile of somewhere */
/* KERN_INFO */
#define DPRINTK(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
#else
#define DPRINTK(fmt, ...)
#endif
#define DEVICE_NAME "adc"
static void __iomem *base_addr;
typedef struct {
wait_queue_head_t wait;
int channel;
int prescale;
}ADC_DEV;
DECLARE_MUTEX(ADC_LOCK);
/* you may need to change is to the following */
//DEFINE_SEMAPHORE(ADC_LOCK);
static int OwnADC = 0;
static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;
static struct clk *adc_clock;
#define ADCCON (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON)) //ADC control
#define ADCTSC (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC)) //ADC touch screen control
#define ADCDLY (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY)) //ADC start or Interval Delay
#define ADCDAT0 (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0)) //ADC conversion data 0
#define ADCDAT1 (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1)) //ADC conversion data 1
#define ADCUPDN (*(volatile unsigned long *)(base_addr + 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)
#define START_ADC_AIN(ch, prescale) /
do{ /
ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; /
ADCCON |= ADC_START; /
}while(0)
/* ioctl */
#ifndef u16
#define u16 unsigned short
#endif
#define ADC_IOC_MAGIC 0xd2
#define ADC_SET_CHANNEL _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV _IOW(ADC_IOC_MAGIC, 1, u16)
#define ADC_MAX_IOC 2 /* we only have 2 ioctl commands */
#define MAX_ADC 4 /* we have 4 adc chnnels */
/* end of ioctl */
static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{
if (OwnADC) {
adc_data = ADCDAT0 & 0x3ff;
ev_adc = 1;
wake_up_interruptible(&adcdev.wait);
}
return IRQ_HANDLED;
}
static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
char str[20];
int value;
size_t len;
if (down_trylock(&ADC_LOCK) == 0) {
OwnADC = 1;
START_ADC_AIN(adcdev.channel, adcdev.prescale);
wait_event_interruptible(adcdev.wait, ev_adc);
ev_adc = 0;
DPRINTK("AIN[%d] = 0x%04x, %d/n", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);
value = adc_data;
#if 0
sprintf(str,"%5d", adc_data);
copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));
#endif
OwnADC = 0;
up(&ADC_LOCK);
} else {
value = -1;
//len = sprintf(str, "%d/n", value); // why '/n'?
len = sprintf(str, "%d", value);
if (count >= len) {
int r = copy_to_user(buffer, str, len);
return r ? r : len;
} else {
return -EINVAL;
}
}
static int s3c2410_adc_open(struct inode *inode, struct file *filp)
{
init_waitqueue_head(&(adcdev.wait));
adcdev.channel=0;
adcdev.prescale=0xff;
DPRINTK( "adc opened/n");
return 0;
}
static int s3c2410_adc_release(struct inode *inode, struct file *filp)
{
DPRINTK( "adc closed/n");
return 0;
}
static int s3c2410_adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
if ((_IOC_TYPE(cmd) != ADC_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_MAX_IOC))
return -EINVAL;
switch (cmd) {
/* set channel */
case ADC_SET_CHANNEL:
arg &=3; //??
if (arg > 3)
arg = 3;
adcdev.channel=arg;
break;
case ADC_SET_CLKDIV:
arg &= 0xff; // ??
if (arg > 0xff)
arg = 0xff;
adcdev.prescale=arg;
break;
default:
return -EINVAL;
break;
}
return 0;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c2410_adc_open,
.read = s3c2410_adc_read,
.release = s3c2410_adc_release,
.ioctl = s3c2410_adc_ioctl,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
base_addr=ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register block/n");
return -ENOMEM;
}
/* test: ioremap: c4876000(kernel space) ADCCON: 3fc4 */
DPRINTK("ioremap: %x ADCCON: %x/n", base_addr, ADCCON);
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source/n");
return -ENOENT;
}
clk_enable(adc_clock);
/* normal ADC */
ADCTSC = 0;
ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
if (ret) {
iounmap(base_addr);
return ret;
}
ret = misc_register(&misc);
printk (DEVICE_NAME"/tinitialized/n");
return ret;
}
static void __exit dev_exit(void)
{
free_irq(IRQ_ADC, &adcdev);
iounmap(base_addr);
if (adc_clock) {
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
misc_deregister(&misc);
}
EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
s3c2410多通道adc驱动及测试程序相关推荐
- 多通道ADC一致性的高精度测量方法
在散射计数字处理系统中,需要多通道ADC电路来对数据进行采样.在该系统中,需要对ADC采集后的数据进行数字下变频.脉冲压缩和波束合成等处理.然而,由于采样电路的路数很多,各个ADC通道由于布线差异.时 ...
- STM32的ADC采样与多通道ADC采样
一 单通道采样 参考资料: <STM32库开发实战指南> 刘火良,杨森著 原理性质的东西还是少讲,因为上面那本书里面讲解的很详细了,直接来看硬件电路图 这里使用的是3362电位器(10K ...
- 线性序列机与串行接口ADC驱动设计与验证
线性序列机与串行接口ADC驱动设计与验证 ADC128S022 型 ADC 内部工作原理 在 AC620 开发板上使用的模数转换器为逐次逼近型的低功耗芯片 ADC128S022,其具有 8 通道以及 ...
- stm32f429之多通道ADC通过DMA数据采集
stm32f429之多通道ADC通过DMA数据采集 原来的程序使用时stm32f103的芯片,现在给为stm32f429的芯片,查看一下几家开发板例程,发现没有使用adc+dma的,在网上也搜索了一下 ...
- 基于STM32F4:多通道ADC采集,采用DMA的形式,亲测有效
基于STM32F4的多通道ADC采集 单片机源程序如下: #include "sys.h" #include "delay.h" #include " ...
- 外设驱动库开发笔记47:ADS111x系列ADC驱动
关于ADC我们已经讨论过不少了,但在不同的应用需求下,我们会选择不同的原件.在这里我们将讨论ADS111x系列ADC驱动的设计与实现. 1.功能概述 ADS1113. ADS1114 和 AD ...
- cubemx 配置多通道ADC进行ADC采样
cubemx 配置多通道ADC进行ADC采样 AD的基础知识 AD很复杂,其实也不复杂,因为我们用的不多. AD:模拟量转换数字量(模拟信号转换数字信号). ADC:模拟量转数字量的转换器. 为什么需 ...
- linux 混杂设备驱动之adc驱动
linux2.6.30.4中,系统已经自带有了ADC通用驱动文件---arch/arm/plat-s3c24xx/adc.c,它是以平台驱动设备模型的架构来编写的,里面是一些比较通用稳定的代码,但是l ...
- S3C6410开发板adc驱动代码分析及测试代码分析
在本文中,我们对S3C6410开发板adc驱动代码的实现过程进行分析,然后通过一个实例对adc功能进行测试.在本文的资源中包含了设备驱动的源码和测试的源码. 一.设备驱动源码分析 adc的设备驱动主要 ...
最新文章
- 判断一个字符串是另外字符串的旋转字符串
- 基于ArcGIS API for JavaScript加载百度各种类型切片地图
- JS编程建议——2:正确辨析JavaScript句法中的词、句和段
- asp.net mysql 中文乱码_mysql4导入mysql5中文乱码问题
- JAVA几何图注水,如何使用java绘制几何形状到图片?
- 前端-chromeF12 谷歌开发者工具详解 Network篇
- 函数可以作为Javascript对象(哈希表)的键吗
- 内存少导致编译出错,内存增加到24G
- 推荐CSDN排名前1000博主
- vsco怎么两个滤镜叠加_做图比设计师还快?!这帮饭圈女孩是怎么做到的?
- 作为技术面试官,我在面试时考虑什么?
- 万用表如何进行欧姆校零
- C语言推箱子完整代码
- ThinkPad电脑黑屏只显示鼠标
- 【计算机毕业设计】437物流管理系统设计与实现
- 一般树与二叉树的相互转换
- python生成曼德勃罗分形图形
- Office 2016 定制安装工具 v1.0 | Office 2016 自定义安装组件
- sql2000不显示服务器失败,安装sql server 2000数据库提示程序配置服务器失败.(解决方法)...
- CP2102国产替代DPU02— USB 转 UART 桥接芯片