这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 开源硬件 » SIN210学习笔记__ADC

共6条 1/1 1 跳转至

SIN210学习笔记__ADC

助工
2015-02-20 22:32:38     打赏

ADC,Analog to Digital Converter

ADC就是模拟信号转换成数字信号的转换器,这个东东和DAC是模拟世界和数字世界的桥梁,如今DAC在单片机上已经是标配了,一般都是10/12可配置的,若不是特殊需求,12bit的分辨率在日常使用中也是够用的。当然也有一些单片机竟然配备了24bit的ADC,一般用于医疗电子。


S5PV210 内部集成了10 通道 10/12-bit(可选) 多路复用 ADC。

微分线性误差: ± 1.0 LSB (Max.)
积分线性误差: ± 4.0 LSB (Max.)
最大转换速率: 1 MSPS
低功耗
工作电压: 3.3V
模拟信号输入范围: 0 ~ 3.3V
片上集成采样和保持功能
普通转换模式
分离 X/Y 转换模式
自动(连续) X/Y 转换模式
等待中断模式
IDLE, DIDLE, STOP 和 DSTOP 唤醒
两个触摸屏接口


                                                        图10-1:AD和触摸屏框架图


AD 驱动,三星公司已经写好,路径是:kernel/arch/arm/mach-s5pv210/adc.c 代码如下:


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 

#define ADC_MINOR 	131
#define ADC_INPUT_PIN   _IOW('S', 0x0c, unsigned long)

#define ADC_WITH_TOUCHSCREEN

static struct clk	*adc_clock;

static void __iomem 	*base_addr;
static int adc_port;
struct s3c_adc_mach_info *plat_data;


#ifdef ADC_WITH_TOUCHSCREEN
static DEFINE_MUTEX(adc_mutex);

static unsigned long data_for_ADCCON;
static unsigned long data_for_ADCTSC;

static void s3c_adc_save_SFR_on_ADC(void)
{
	data_for_ADCCON = readl(base_addr + S3C_ADCCON);
	data_for_ADCTSC = readl(base_addr + S3C_ADCTSC);
}

static void s3c_adc_restore_SFR_on_ADC(void)
{
	writel(data_for_ADCCON, base_addr + S3C_ADCCON);
	writel(data_for_ADCTSC, base_addr + S3C_ADCTSC);
}
#else
static struct resource	*adc_mem;
#endif

static int s3c_adc_open(struct inode *inode, struct file *file)
{
	return 0;
}

unsigned int s3c_adc_convert(void)
{
	unsigned int adc_return = 0;
	unsigned long data0;
	unsigned long data1;

	writel((adc_port & 0x7), base_addr + S3C_ADCMUX);

	udelay(10);

	writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_ENABLE_START, base_addr + S3C_ADCCON);

	do {
		data0 = readl(base_addr + S3C_ADCCON);
	} while (!(data0 & S3C_ADCCON_ECFLG));

	data1 = readl(base_addr + S3C_ADCDAT0);

	if (plat_data->resolution == 12)
		adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK_12BIT;
	else
		adc_return = data1 & S3C_ADCDAT0_XPDATA_MASK;

	return adc_return;
}


int s3c_adc_get(struct s3c_adc_request *req)
{
	unsigned adc_channel = req->channel;
	int adc_value_ret = 0;

	adc_value_ret = s3c_adc_convert();

	req->callback(adc_channel, req->param, adc_value_ret);

	return 0;
}
EXPORT_SYMBOL(s3c_adc_get);

static ssize_t
s3c_adc_read(struct file *file, char __user *buffer,
		size_t size, loff_t *pos)
{
//	printk("++++++adc read.\n");
	int  adc_value = 0;

#ifdef ADC_WITH_TOUCHSCREEN
	mutex_lock(&adc_mutex);
	s3c_adc_save_SFR_on_ADC();
#endif
	printk(KERN_INFO "## delay: %d\n", readl(base_addr + S3C_ADCDLY));
	adc_value = s3c_adc_convert();

#ifdef ADC_WITH_TOUCHSCREEN
	s3c_adc_restore_SFR_on_ADC();
	mutex_unlock(&adc_mutex);
#endif
	
//	printk("+++++%d\n",adc_value);
	if (copy_to_user(buffer, &adc_value, sizeof(unsigned int)))
		return -EFAULT;

	return sizeof(unsigned int);
}


static int s3c_adc_ioctl(struct inode *inode, struct file *file,
	unsigned int cmd, unsigned long arg)
{
	cmd = ADC_INPUT_PIN;
	switch (cmd) {
	case ADC_INPUT_PIN:
		adc_port = (unsigned int) arg;
		printk("port=%d\n",arg);
		if (adc_port >= 4)
			printk(KERN_WARNING
				" %d is already reserved for TouchScreen\n",
				adc_port);
		return 0;

	default:
		return -ENOIOCTLCMD;
	}
}

static const struct file_operations s3c_adc_fops = {
	.owner		= THIS_MODULE,
	.read		= s3c_adc_read,
	.open		= s3c_adc_open,
	.ioctl		= s3c_adc_ioctl,
};

static struct miscdevice s3c_adc_miscdev = {
	.minor		= ADC_MINOR,
	.name		= "adc",
	.fops		= &s3c_adc_fops,
};

static struct s3c_adc_mach_info *s3c_adc_get_platdata(struct device *dev)
{
	if (dev->platform_data != NULL)
		return (struct s3c_adc_mach_info *) dev->platform_data;
	else
		return 0;
}

/*
 * The functions for inserting/removing us as a module.
 */

static int __init s3c_adc_probe(struct platform_device *pdev)
{
	printk("+++++++++++++s3c-adc probe.\n");
	struct resource	*res;
	struct device *dev;
	int ret;
	int size;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	dev = &pdev->dev;

	if (res == NULL) {
		dev_err(dev, "no memory resource specified\n");
		return -ENOENT;
	}

	size = (res->end - res->start) + 1;

#if !defined(ADC_WITH_TOUCHSCREEN)
	adc_mem = request_mem_region(res->start, size, pdev->name);
	if (adc_mem == NULL) {
		dev_err(dev, "failed to get memory region\n");
		ret = -ENOENT;
		goto err_req;
	}
#endif

	base_addr = ioremap(res->start, size);
	if (base_addr ==  NULL) {
		dev_err(dev, "fail to ioremap() region\n");
		ret = -ENOENT;
		goto err_map;
	}

	adc_clock = clk_get(&pdev->dev, "adc");

	if (IS_ERR(adc_clock)) {
		dev_err(dev, "failed to fine ADC clock source\n");
		ret = PTR_ERR(adc_clock);
		goto err_clk;
	}

	clk_enable(adc_clock);

	/* read platform data from device struct */
	plat_data = s3c_adc_get_platdata(&pdev->dev);

	if ((plat_data->presc & 0xff) > 0)
		writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(plat_data->presc & 0xff), base_addr + S3C_ADCCON);
	else
		writel(0, base_addr + S3C_ADCCON);

	/* Initialise registers */
	if ((plat_data->delay & 0xffff) > 0)
		writel(plat_data->delay & 0xffff, base_addr + S3C_ADCDLY);

	if (plat_data->resolution == 12)
		writel(readl(base_addr + S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT, base_addr + S3C_ADCCON);

	ret = misc_register(&s3c_adc_miscdev);
	if (ret) {
		printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
			ADC_MINOR, ret);
		goto err_clk;
	}
	
	printk("+++++++++++++s3c-adc probe successful\n");
	return 0;

err_clk:
	clk_disable(adc_clock);
	clk_put(adc_clock);

err_map:
	iounmap(base_addr);

#if !defined(ADC_WITH_TOUCHSCREEN)
err_req:
	release_resource(adc_mem);
	kfree(adc_mem);
#endif

	return ret;
}


static int s3c_adc_remove(struct platform_device *dev)
{
	return 0;
}

#ifdef CONFIG_PM
static unsigned int adccon, adctsc, adcdly;

static int s3c_adc_suspend(struct platform_device *dev, pm_message_t state)
{
	adccon = readl(base_addr + S3C_ADCCON);
	adctsc = readl(base_addr + S3C_ADCTSC);
	adcdly = readl(base_addr + S3C_ADCDLY);

	clk_disable(adc_clock);

	return 0;
}

static int s3c_adc_resume(struct platform_device *pdev)
{
	clk_enable(adc_clock);

	writel(adccon, base_addr + S3C_ADCCON);
	writel(adctsc, base_addr + S3C_ADCTSC);
	writel(adcdly, base_addr + S3C_ADCDLY);

	return 0;
}
#else
#define s3c_adc_suspend NULL
#define s3c_adc_resume  NULL
#endif

static struct platform_driver s3c_adc_driver = {
       .probe          = s3c_adc_probe,
       .remove         = s3c_adc_remove,
       .suspend        = s3c_adc_suspend,
       .resume         = s3c_adc_resume,
       .driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c-adc",
	},
};

static char banner[] __initdata = KERN_INFO "S5PV210 ADC driver, (c) 2010 Samsung Electronics\n";

int __init s3c_adc_init(void)
{
	printk(banner);
	return platform_driver_register(&s3c_adc_driver);
}

void __exit s3c_adc_exit(void)
{
	platform_driver_unregister(&s3c_adc_driver);
}

module_init(s3c_adc_init);
module_exit(s3c_adc_exit);

MODULE_AUTHOR("dsfine.ha@samsung.com");
MODULE_DESCRIPTION("S5PV210 ADC driver");
MODULE_LICENSE("GPL");


驱动已经解决,我们就可以直接写ADC的应用了。这次也是让ADC自动采集AD0端口的值,然后输出到Qt中显示



/*adc.h*/
#ifndef AD_H
#define AD_H
#ifdef __cplusplus
extern "C" {
#endif

extern int adc_open(const char *devname);
extern int adc_ioctl(int ad_ch);
extern int adc_read();
extern int adc_close(void);
extern int adc_fd;

#ifdef __cplusplus
}
#endif
#endif


/*adc.c*/
#include 
#include 
#include 
#include 
#include 
#include "adc.h"

int adc_fd=0;


int adc_open(const char*devname)
{
  adc_fd=open(devname,O_RDWR);
  if(adc_fd<0)
  {
     printf("open device %s failed.\n",devname);
     return -1;
  }
  printf("AD driver is ok\n");
  return 0;
}

int adc_ioctl(int adc_ch)
{
  if(adc_ch > 4)
  {
    printf("AD Channel %d is reserved for TouchScreen!\n",adc_ch);
    return -1;
  }
  ioctl(adc_fd,'s',adc_ch);
  printf("AD Channel %d is open\n",adc_ch);
  return 0;
}
int adc_read()
{
   usleep(500*1000);
   int i,sum=0,j=10;
   while(j--)
   {
     read(adc_fd, &i, sizeof(int));
     sum+=i;
   }

   printf("AD value :%d\n",i);
   return i;
}


int adc_close(void){
 if(adc_fd)
   close(adc_fd);
}


编写Qt应用程序即可通过adc_read()函数读取adc的值,通过adc_ioctl(int acd_ch) 来选择adc的通道(0~4可选,其它SIN210开发板用于触摸屏了,保留)。

adc_open(const char*devname)打开adc驱动文件。

下面是运行的效果图:

                                       图10-2:AD运行效果图





关键词: SIN210学习笔记     AD    

院士
2015-02-20 23:45:59     打赏
2楼

真棒啊~~

楼主 最后的图貌似没有成功上传哟~~


专家
2015-02-21 13:40:29     打赏
3楼

楼主是我学习的榜样


助工
2015-02-21 21:38:48     打赏
4楼
昨天在Ubuntu系统里,就没上传。。今天搞定了。。。

助工
2015-02-21 21:49:46     打赏
5楼
过奖了。。。。

助工
2015-02-24 09:20:25     打赏
6楼

共6条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]