一、分析原理图及特性
图1 图2
S5p6818的ADC0是去读取电源电压,通过ADC0将模拟量转化为数字量。
图2为ADC内部构造图
ADC特性
- 分辨率:12bit
- 最大转换率:1Msps(main clock:6MHZ,sampling clock:1MHZ).
- 供电0~1.8V
- 输入频率:DC to 100KHZ
二、ADC转换流程
- PCLK Supply: CLKENB.PCLKMODE=1
- Analog Input Select: ADCCON.ASELADC
- Power On: ADCCON.STBY=0
- CLKIN Divide Value: ADCCON.APSV
- CLKIN On:ADCCON.APE
- ADC Enable: ADCCON.ADEN
- AD Conversion Process
- Read ADCDAT.ADCDAT
- CLKIN Off
- ADC Power Off
三、相关寄存器分析
四、代码 编写
1、通过读ADC转换器是否转换完成的方式去读ADC值
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <mach/devices.h>
#include <mach/soc.h>
#define ADCREG(x) ((x) + IO_ADDRESS(PHY_BASEADDR_ADC)) //物理地址映射到虚拟地址
#define ADCCON ADCREG(0)
#define ADCDAT ADCREG(0x0004)
#define ADCINTENB ADCREG(0x0008)
#define ADCINTCLR ADCREG(0x000C)
#define PRESCALERCON ADCREG(0x0010)
static ssize_t adc_read(struct file *filp, char __user * buf, size_t size, loff_t *oft)
{
unsigned int adcValue;
unsigned int count=0;
int ret;
if(size!=4)
{
return -EINVAL;
}
writel((readl(ADCCON)&(~(1<<2))),ADCCON); //poweron
writel(readl(ADCCON)|(1<<0),ADCCON); //start AD conversion
while(readl(ADCCON)&(1<<0)) //等待
{
count++;
udelay(1);
if(count>200)
{
printk(KERN_ERR"adc read error\n");
writel((readl(ADCCON)|(1<<2)),ADCCON); //poweroff
return -EAGAIN;
}
}
adcValue = readl(ADCDAT)&0xfff;
writel((readl(ADCCON)|(1<<2)),ADCCON); //poweroff
ret = copy_to_user(buf, &adcValue, sizeof(adcValue));
if(ret != 0)
{
return (size -ret);
}
return size;
}
struct file_operations adc_misc_fops=
{
.read= adc_read,
};
static struct miscdevice adc_misc={
.minor = MISC_DYNAMIC_MINOR,
.name = "adc_misc",
.fops = &adc_misc_fops,
};
static int __init adc_init(void)
{
int ret;
printk(KERN_INFO"adc_init\n");
ret = misc_register(&adc_misc);
if(ret < 0)
{
printk(KERN_INFO"adc misc register fail.\n");
goto misc_register_err;
}
//复位 ADC
nxp_soc_peri_reset_set(RESET_ID_ADC);
writel((readl(ADCCON)&~(7<<3)),ADCCON); //选择通道0
writel( ( (readl(ADCCON) & (~(0xf<<6))) | (6<<6) ), ADCCON);//SOC的时序参数6
writel((readl(ADCCON)|(1<<2)),ADCCON); //poweroff
writel((readl(PRESCALERCON)&~(1<<15)),PRESCALERCON); //prescaler disbale
writel((readl(PRESCALERCON)&~(0x3ff))|(199<<0),PRESCALERCON); //200M/(199+1) =1M
writel((readl(PRESCALERCON)|(1<<15)),PRESCALERCON); //prescaler enable
return 0;
misc_register_err:
return ret;
}
static void __exit adc_exit(void)
{
printk(KERN_INFO"adc__exit\n");
misc_deregister(&adc_misc);
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
2、通过中断的方式读adc值
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <mach/devices.h>
#include <mach/soc.h>
#define ADCREG(x) ((x) + IO_ADDRESS(PHY_BASEADDR_ADC)) //物理地址映射到虚拟地址
#define ADCCON ADCREG(0)
#define ADCDAT ADCREG(0x0004)
#define ADCINTENB ADCREG(0x0008)
#define ADCINTCLR ADCREG(0x000C)
#define PRESCALERCON ADCREG(0x0010)
static DECLARE_WAIT_QUEUE_HEAD(adc_wq) ; //静态定义等待队列
static bool flag = false;
static ssize_t adc_read(struct file *filp, char __user * buf, size_t size, loff_t *oft)
{
int ret;
unsigned int adcValue=0;
if(size!=4)
{
return -EINVAL;
}
writel((readl(ADCCON)&(~(1<<2))),ADCCON); //poweron
writel(readl(ADCCON)|(1<<0),ADCCON); //start AD conversion
wait_event_interruptible(adc_wq, flag);
flag = false; //唤醒一次队列后要复位flag的值
adcValue = readl(ADCDAT)&0xfff;
writel((readl(ADCCON)|(1<<2)),ADCCON); //poweroff
ret = copy_to_user(buf, &adcValue, sizeof(adcValue));
if(ret != 0)
{
return (size -ret);
}
return size;
}
struct file_operations adc_misc_fops=
{
.read= adc_read,
};
static struct miscdevice adc_misc={
.minor = MISC_DYNAMIC_MINOR,
.name = "adc_misc",
.fops = &adc_misc_fops,
};
static irqreturn_t adc_irq_handler(int irq, void * dev)
{
writel( ( readl(ADCINTCLR) |(1<<0) ), ADCINTCLR); //clear interrupt
flag = true; //设置flag为true
wake_up_interruptible(&adc_wq);
return IRQ_HANDLED;
}
static int __init adc_init(void)
{
int ret;
printk(KERN_INFO"adc_init\n");
ret = misc_register(&adc_misc);
if(ret < 0)
{
printk(KERN_INFO"adc misc register fail.\n");
goto misc_register_err;
}
//adc转换器转换完成后会触发中断
ret = request_irq(IRQ_PHY_ADC, adc_irq_handler, 0, "adc_irq", NULL);
if(ret < 0)
{
printk(KERN_INFO"request_irq fail.\n");
goto irq_request_err;
}
//复位 ADC
nxp_soc_peri_reset_set(RESET_ID_ADC);
writel((readl(ADCCON)&~(7<<3)),ADCCON); //选择通道0
writel( ( (readl(ADCCON) & (~(0xf<<6))) | (6<<6) ), ADCCON);//SOC的时序参数6
writel((readl(ADCCON)|(1<<2)),ADCCON); //poweroff
writel((readl(PRESCALERCON)&~(1<<15)),PRESCALERCON); //prescaler disbale
writel((readl(PRESCALERCON)&~(0x3ff))|(199<<0),PRESCALERCON); //200M/(199+1) =1M
writel((readl(PRESCALERCON)|(1<<15)),PRESCALERCON); //prescaler enable
writel((readl(ADCINTENB)|(1<<0)),ADCINTENB); //enable 中断
writel((readl(ADCINTCLR)|(1<<0)),ADCINTCLR); //清空中断
return 0;
irq_request_err:
free_irq(IRQ_PHY_ADC,NULL);
misc_register_err:
return ret;
}
static void __exit adc_exit(void)
{
printk(KERN_INFO"adc__exit\n");
free_irq(IRQ_PHY_ADC,NULL);
misc_deregister(&adc_misc);
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
main.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
int main()
{
int fd,ret;
int adcvalue;
fd = open("/dev/adc_misc",O_RDWR);
if(fd<0)
{
perror("open adc_misc error!");
}
while(1)
{
ret = read(fd,&adcvalue,4);
if(ret != 4)
{
perror("read adc value error");
continue;
}
printf("adcvalue=0x%x\n",adcvalue);
usleep(200*1000);
}
close(fd);
return 0;
}