linux adc测试程序,S3C2410多通道adc驱动及测试程序

后续之事:修改网上流行的adc驱动,使用write或ioctl从应用层传递数据到驱动。

在开发板上运行结果如下:

./myadc

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信息如下(举例,与前面显示无直接关系):

# dmesg | tail

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

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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");0b1331709591d260c1c78e86d0c51c18.png

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值