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.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运行效果图
我要赚赏金
