null第六章 A/D与D/A转换 第六章 A/D与D/A转换 本章目标
掌握逐次逼近型A/D转换器的工作原理和驱动程序设计方法
学会编写A/D转换器的测试程序
掌握 D/A 转换驱动程序的设计方法
学会编写 D/A转换器的测试程序null6.1 AD转换的过程
模数转换时必须在一系列选定的瞬间,即时
间轴的规定点上,进行信号采样,然后再将这些
采样值转换为输出的数字量。可见,A/D转换过
程可以分成采样、保持、量化、编码4个
步骤
新产品开发流程的步骤课题研究的五个步骤成本核算步骤微型课题研究步骤数控铣床操作步骤
来
完成,但在实际电路中,这些步骤往往是合并进
行的。 null6.1.1 采样和保持
1.采样过程
采样就是周期性的读取或测量某个连续
信号。测量的周期就是采样周期Ts,采样
周期的倒数称为采样频率 fs=1/Ts。 null A/D转换的完成将会占用一段时间,为了让转换器能够稳定可靠地工作,人们通常选用采样开关(又称采
样保持器),先对连续信号进行采样,即只在转换周期中一段很短的时间间
隔完成对输入信号的测量。null 采样开关的输入信号是连续的,而输出是离散的采样信号,它是连续信号的一部分,不同于数字信号。所以,采样信号仍然是模拟量,是一种脉冲调制信号,如图6.1所示。 null图6.1 脉冲幅度调制null2. 采样定理
采样定理是指在何种情况下采样开关输出的离散信号y(t)可以正常恢复输入信号x(t)。采样定理可表述为:为了使采样信号y(t)能完全恢复连续信号x(t),包含任何干扰在内的信号x(t) 的最高有效频率 必须小于等于采样频率的一半。 null 图6.2给出了不同频率的正弦波在相同抽样频率下的采样结果。在图(a)中,采样频率为正弦信号的6倍,采样出来的信号点连线可以恢复成原来较完整的正弦波;在图(b)中,采样信号的频率不足正弦信号的两倍,由图可见,虽然也能恢复正弦信号,但频率与原信号差距很大,这种现象就叫做混叠。null图6.2 不同频率正弦波被采样的波形图null6.1.2 量化和编码
量化是一种用数字量逼近模拟量的过程,因而也是一种舍入过程,所以量化误差等效于数值
分析
定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析
中的舍入误差。量化过程中引入舍入误差(量化误差)的主要原因是由于数字量的数有限,仅能表示有限的量化幅度(n 位数字量只能表示个数值),因此,在量化过程中必然存在误差,并且这种误差仅能通过数字量位数的增加或量化间隔的减小而减少,不能消除。 null 图6.3所示波形说明了数/模转换中量化的过程。图中虚线为采样保持电路的输出电平即采样信号,实线为量化后的电平。 图 6.3 量化编码 null6.1.3 ADC的主要参数及意义
1. 量化误差和分辨率
A/D转换器的分辨率用输出信号二进制数的位数或者BCD码的位数来表示。它的分辨率为1LSB。
量化误差和分辨率是统一的。量化误差是因为有限数字位数为模拟数字进行量化而引起的误差。所以说,量化误差理论上为一个单位的分辨率,即±1/2LSB,提高分辨率就可以减小误差。null2.转换速度(转换速率)
转换速度是指完成一次A/D转换所需要的时间。转换时间是从接收到转换控制信号开始到输出端得到稳定的数字输出信号所经过的时间。通常,转换速率是指转换速度的倒数。null3.相对精度
转换器输出的数字信号对应的实际模拟输出电压与理论输入电压之间会存在一个差值,可以表示成绝对误差和相对误差。这个误差并不是常数,也是有一个范围,将偏差的最大值与满刻度模拟电压的百分比称为相对误差或相对精度。 null4.电源抑制比
电源抑制比 (PSRR)反映A/D转换器对电源电压变化的抑制能力。通过改变电源电压,使数据发生±1LSB变化,此时电源电压的变化范围间接反映了A/D转换器对电源电压变化的抑制能力。 null6.2 S3C2410 A/D转换驱动设计实例
S3C2410X内部集成了8路10位A/D转换器,分辨率为10位。同时,该A/D转换器可以通过软件设置为休眠模式,以减少功率损失,最大转换速率可达500kbit/s,非线性度为土l位。null6.2.1 S3C2410的A/D转换电路
本例中的A/D转换外接电路通过可变电阻改变输入信号的电平值,然后将这个模拟信号输入通道1,观察A/D采样的结果。null图 6.4 S3C2410的A/D转换电路 null6.2.2 S3C2410X的 A/D转换控制寄存器
A/D 转换控制寄存器负责对S3C2410内部的A/D转换器进行参数设置以完成需要的功能。A/D转换控制寄存器地址为 0x58000000,复位值默认0x3FC4,如表6-1和表6-2 所示。表4-1 A/D 转换控制寄存器nullnull1.A/D采样的频率
A/D采样的时钟频率由A/D采样预分频寄存器(ADCPSR)决定。假设CPU主时钟频率为50MHz,ADCPSR设置值为49,完成一次A/D转换需要至少5个时钟周期,那么采样
频率为:f=50MHz/ (49+1)=1MHz
转换时间=1/(1MHz/5cycles)=5μsnull2. 启动采样
A/D控制寄存器的bit[0]置1可以启动转换,当启动转换后,该位会被自动清除。同时启动转换时还需要指定转换通道,人们通过设置控制寄存器的bit[5:3]来完成8个通道的选择。例如,下面的代码完成通道2的选择:
rADCCON = 0x1 | (0x2<<3) ;null3.取得A/D转换结果
当A/D转换结束后,可以通过读取A/D数据寄存器(ADCDAT)的值获取转换结果。通过检查A/D控制转换器ECFLG位(bit[15])的值,可以确定A/D转换是否完成,当转换完成后就可以从数据寄存器读出转换结果。 null6.2.3 S3C2410X的A/D转换数据寄存器
表4.3和表4.4给出了S3C2410X的A/D转换数据寄存器及其各位的定义。可见,A/D数据寄存器的地址为0x580000C,XPDATA[9:0]中保存A/D转换的10位二进制码,即数字转换结果。null表 6.3 A/D转换数据寄存器表6.4 A/D转换数据寄存器设置null6.2.4 S3C2410X中A/D转换驱动程序的设计
1.A/D转换驱动程序的底层操作函数
A/D转换的驱动程序负责完成A/D转换器的打开、关闭操作以及对A/D转换器转换的结果进行读取操作等。这些操作通过以下3个函数实现:
static int adc_enable ( ) ;
static int adc_disable ( ) ;
static int adc_read ( ) ;null打开A/D转换器采集模拟信号进行A/D转换输出数字信号关闭A/D转换器adc_enable ( )adc_disable ( )}adc_read ( )图6.5 驱动程序的工作
流程
快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计
null// 定义A/D转换频率
#define ADC_FREQ 2500000
// 使能 A/D 转换
adc_enable ( int ch )
{
static int prevCh = -1;
// 设定分频值
preScaler = PCLK/ADC_FREQ -1; // PCLK: 50.7MHz
// 设置 A/D 通道
rADCCON = (1<<14) | (preScaler <<6) | (ch <<3) ;null// 如配置有变化,重新设置 A/D
if ( prevCh!=ch)
{
// 设置A/D 通道
rADCCON = (1<<14) | (preScaler <<6) | (ch <<3) ;
}
usleep (200000);
return 0;
}nulladc_enable()函数:首先计算出A/D转换器预分频器的分频值preScaler,然后设置A/D转换控制寄存器rADCCON选择A/D转换通道等。nullint adc_read (int ch)
{
int i ;
// 开始进行A/D转换
rADCCON |= 0x 1 ;
//
检测
工程第三方检测合同工程防雷检测合同植筋拉拔检测方案传感器技术课后答案检测机构通用要求培训
Enable start 位是否为低电平
while ( rADCCON & 0x 1 ) ;
// 检查ECFLG位是否为高电平,为高电平时表示转换结束
while ( ! (rADCCON & 0x8000 )) ;
// 返回转换结果
return ( ( int ) rADCDAT0 & 0x3ff ) ;
}nulladc_read() 函数:调用该函数时,首先将A/D转换控制寄存器的转换启动位置为高电平,开始执行A/D转换操作。然后,程序不断检测控制寄存器的转换开始位(此位在转换开始时将自动清除)和转换状态标志位,当转换结束时,函数返回转换结果,此结果保存在数据寄存器rADCDAT0中。nulladc_disable ( )
{
// 复位A/D 转换器
rADCCON = 0x 3fc4 ;
return 0;
}
adc_disable ( ) 函数:在完成A/D转换操作并读取到转换结果后,程序将对A/D转换控制寄存器进行复位操作,使A/D转换器进入休眠模式。null2.A/D驱动程序的I/O控制模块
A/D驱动程序通过这个模块顺序调用adc_enable()、adc_read()、adc_disable()这3个函数,从而实现对模拟信号的采集、转换和输出。nullssize_t adctl_ioctl ( struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg )
{ int val ;
// 使能 A/D 转换
adc_enable (1) ;
// 读取转换结果
val = adc_read (1) ;
// 屏蔽转换
adc_disable ( ) ;
// 返回 val 的值
return ( ( int ) val & 0x3ff ) ;
}null3.A/D驱动程序的文件操作结构
static struct file_operation adc_ops = {
read : adc_read ,
write : adc_write ,
ioctl : adc_ioctl ,
open : adc_open ,
release : adc_release ,
} ;null4. A/D转换器驱动程序初始化
static int _init HW_AD_init ( void )
{ int ret = -ENODEV ;
adc = adc_get ( ) ;
// 注册 A/D 转换设备
ret = devfs_register_chrdev ( ADC_MAJOR, "adc", &adc_ops );null// 判断是否注册成功
if (ret <0)
{ printk ( " S3C2410 init_module failed with %d\n ", ret ) ;
return ret ;
}
else
{
printk ( " S3C2410 adc register success !!! \n ");
}
adc_dev_handle = devfs_register ( NULL , "adc", DEVFS_FL_DEFAULT, ADC_MAJOR, 0,S_IFCHR,
& adc_ops, NULL );
return ret ;
}
null5. A/D 驱动程序的模块化加载和卸载
// A/D 驱动程序模块化加载函数
static int _init AD_init (void )
{ int ret = -ENODEV ;
ret = HW_AD_init ( ) ;
if ( ret )
return 0 ;
}null// A/D 驱动程序模块化卸载函数
static void _exit cleanup_AD (void )
{ /* 注销设备 */
devfs_unregister_chrdev ( AD_MAJOR, "abc" );
devfs_unregister ( adc_dev_handle ) ;
}
module_init ( AD_init );
module_exit ( cleanup_AD ) ;null6.2.5 S3C2410X 中A/D转换测试程序的设计
本例中的测试程序将驱动程序中A/D转换的结果打印出来,代码如下:
# include < stdio.h >
# include < string.h >
# include < stdlib.h >
# include < fcntl.h > // open ( ) close ( )
# include < unistd.h > // read ( ) write ( )
// 定义设备号
# define DEVICE_NAME " /dev /adc "nullint main ( void )
{ int fd ;
int ret, val ;
char *i ;
printf ( " \n start adc test \n \n" ) ;
fd = open (DEVICE_NAME, O_RDWR ) ; // 打开 A/D 设备文件
printf ( " fd = %d \n ", fd ) ; // 打印文件句柄的值
// 若成功打开设备,则进行 A/D 转换;否则打印报错语句null if ( fd = = -1 )
{ printf ( " open device %s error \n", DEVICE_NAME ) ; }
else
{
ioctl ( fd ) ; // 调用驱动程序的 I/O 控制模块
for ( i=0; i< 50; i++ )
{
val = ioctl (fd , 0, 0 ) // 调用ioctl () 函数进行转换
printf ( " val= %d \n", val ) ;
usleep ( 500000 ) ;
}
ret = close (fd ) ;
printf ( " close adc driver test \n");
}
return 0 ;
} null6.3 A/D接口实验
一.实验目的
通过本实验,使学生掌握Linux下A/D驱动程序的设计,了解在S3C2410平台上通过CPLD控制A/D的工作方式和原理
null二.实验原理和说明
1. A/D转换芯片ADC0809及其接口
ADC0809是采用CMOS工艺制成的8位8通道逐次渐近型A/D转换器。其原理框图如图6.6所示。
null图6.6 ADC0809原理框图null1.1主要性能
8位逐次逼近型A/D转换器,所有引脚的逻辑电平与TTL兼容;
带有锁存功能的8路模拟量转换开关,可对8路0~5V模拟量进行分时转换;
输出具有三态锁存/缓冲功能;
分辨率:8位,
转换时间:100us;
不可调误差:±1LSB,
功耗:15mW;
工作电压:+5V,参考电压标准值+5V;
片内无时钟,一般需外加1280KHz以下且不低于10KHz的时钟信号,推荐的外部输入时钟频率为640KHz 。
null1.2 ADC0809的引脚功能
D7~D0:8位数据输出线;
IN7~IN0:8路模拟信号输入;
ADDC、ADDB、ADDA:8路模拟信号输入通道的地址选择线;
ALE:地址锁存允许,其正跳变锁存地址选择线状态,经译码选通对应的模拟输入信号;
START:启动信号,上升沿使片内所有寄存器清零,下降沿启动A/D转换;
EOC:转换开始后,此引脚变为低电平,转换一结束,此引脚变为高电平;
OE:输出允许,此引脚为高电平有效,当有效时,芯片内部三态数据输出锁存缓冲器被打开,转换结果送到D7~D0;
nullCLOCK:采样时钟,要求的频率范围为10KHz-1280KHz,由外部提供;
REF(+)、REF(-):参考电压正极、负极,通常REF(+)接Vcc,REF(-)接GND;
Vcc:电源,+5V ;
GND:地线。
模拟量输入与数字量输出的关系:
N=(VIN-VREF(-))×256/(VREF(+)-VREF(-))
null1.3 输入通道选择表6.5 ADC0809真值表null1.4转换时序
图6.7 ADC0809时序图null1.5 ADC0809的多路转换
参见图6.8。
图6.8null2.ADC0809在HHARM9-EDU开发板上的连接
null(1) IN0-IN7八路输入信号中,EDU实验板上只使用了IN0,IN1,IN2,IN3四路信号,分别连接到相应的接线电极、可调电位器R36的输出电压信号和DAC的模拟输出信号VOUT。
(2) START,OE,ALE,CLK直接连接到CPLD的IO引脚上,由CPLD控制。EOC连接到2410的EINT2引脚,做为中断信号(此时应把CPU的中断信号EINT2设置为高电平有效)。
(3) D0-D7直接连接到S3C2410的数据线D0-D7上。
null三.实验内容和步骤
null6.4 D/A转换原理和说明
D/A 转换器:数/模转换器,它把数字量转换成电模拟量。即把二进制数字量转换为与其数值成正比的电模拟量。D/A转换器的主要性能指标如下:null1. 分辨率
分辨率是说明分辨输出最小电压的能力,是指最小输出电压(对应输入数字量仅最低有效位为“1”)与最大输出电压(对应输入数字量所有的有效位全为“l”)之比,即当输入数字发生单位数码变化时所对应的输出模拟量(电压或电流)的变化量。 null2.转换精度
D/A转换器的转换精度与D/A转换集成芯片的结构和接口配置的电路有关。实际上,转换精度是以最大的静态转换误差的形式给出的。在输入端加对应满刻度的数字量时,实际输出的模拟量与理想情况下应有的输出量之间的差值,称为转换精度。 null3.转换时间
转换时间是判断一个D/A转换器转换速率快慢的重要参数之一,通常指数字量输入转换到模拟量输出稳定为止所需的时间。
4. 线性度
当数字量变化时,D/A输出的电模拟量按比例关系变化的程度。模拟量输出偏离理想输出的最大值称为线性误差。
null6.5 D/A 转换器的驱动程序设计实例
6.5.1 D/A 驱动程序中的宏定义
// 定义D/A设备的主设备号
# define DA 100
static long ioremap_addr ; // 定义 I/O 地址转换变量null6.5.2 D/A的模块加载
D/A转换器驱动程序的模块加载功能仍然是由模块加载函数和模块卸载函数来完成。其中,模块加载函数负责调用DA_init ( )函数,并根据返回值判断调用情况,模块卸载函数则负责卸载D/A转换器的驱动程序,并使D/A转换器输出最小值,通常为零值。null/* 模块加载函数 */
static int _init pxa270_DA_init ( void )
{
int ret = -ENODEV ;
printk ( " pxa270_DA_init \n ") ;
ret = DA_init ( ) ;
if (ret )
return ret ;
return 0 ;
}null/* 模块卸载函数 */
static void _exit cleanup_DA ( void )
{
printk ( " cleanup_DA \n " ) ;
outb ( 0x00, ioremap_addr ); // D/A模块输出最小值
devfs_unregister_chrdev (DA, "da" ) ;
}
module_init ( pxa270_DA_init );
module_exit ( cleanup_DA );null// ----------------------------------- 初始化 ---------------------------------
static int _init DA_init ( void )
{
int ret = - ENODEV;
ret = devfs_register_chrdev ( DA, "da", &DA_ops ) ;
if (ret < 0 )
{
printk ( " PXA270 : init_module failed \n ");
return ret;
}
else
{
printk ( " PXA270 : da_driver register success! \n ");
}
ioremap_addr = ioremap (0x08014000, 0x0f );
//D/A地址映射:0x08808004(虚拟地址映射)
outb ( 0xff, ioremap_addr ); // 向 D/A转换器写入最大值
return ret ;
}null6.5.3 D/A转换器的文件操作模块
该模块主要负责将标准的文件操作函数如 read ( )、write ( )等指向对应于设备专用的文件操作函数。
struct file_operations DA_ops = {
open: DA_open ,
read: DA_read ,
write: DA_write ,
ioctl: DA_ioctl ,
release: DA_release ,
} ;null6.5.4 D/A 转换器的读写控制模块
// --------------------------------写函数 -----------------------
ssize_t DA_write ( struct file *file, const char *buf, size_t count, loff_t *f_ops)
{
outb (buf[0], ioremap_addr); // 向D/A写入buf [0] 的数据
return count ;
}
// ------------------------------ 读函数 ---------------------
ssize_t DA_read ( struct file *file, const char *buf, size_t count, loff_t *f_ops)
{
return count ;
}
// ----------------------------- I/O 控制模块--------------
ssize_t DA_ioctl ( struct inode *inode, struct file *file, unsigned int cmd, long data)
{
return 0;
} null6.5.5 D/A转换器的打开、退出模块
// -----------------------------------OPEN-------------------------------------
ssize_t DA_open (struct inode *inode, struct file *file)
{
MOD_INC_USE_COUNT;
return 0;
}
// -----------------------------------RELEASE / CLOSE ---------------------
ssize_t DA_release (struct inode *inode, struct file *file)
{
outb ( 0x00, ioremap_addr ) ; // D/A输出最小值
MOD_DEC_USE_COUNT;
return 0;
}null6.6 测试程序的设计
D/A转换器驱动程序所完成的工作就是让嵌入式处理器将数字信号按照一定的时序规律发送给D/A转换器,并得到正确的数/模转换结果。为了检测驱动程序的正确性,需要设计相应的测试程序。D/A转换中经常使用方波信号和正弦波信号,下面就以这两种模拟信号为例介绍D/A转换测试程序的设计方法。 null6.6.1 D/A测试程序中的宏定义
# define DEVICE_NAME “/dev/da” // 定义设备名
# define FUNC_RUN 11
# define FUNC_NOT_RUN 22
# define DA_SIN 1 //正弦波形
# define DA_FANG 2 // 方波波形
# define FUNC_QUIT 3 // 退出功能选择
# define POINT 50.0 // 定义取点数null6.6.2 D/A测试程序的主函数
int main ( void )
{
int fd, ret ; // D/A测试的开始
printf ( “ \n start DA_driver test \n \n ” ) ;
// 打开DA设备文件
fd = open ( DEVICE_NAME, O_RDWR ) ;
// 返回 fd 的值
printf ( “ fd = %d \n”, fd );
nullif ( fd = = -1 ) {
printf ( “ open device %s error \n ”, DEVICE_NAME) ;
}
else {
// 调用 D/A功能函数
da_func (fd );
// 关闭设备文件
ret = close (fd ) ;
printf ( “ ret = %d \n ”, ret );
printf (“ close da_driver test \n ” );
}
return 0;
}
null6.63 D/A测试程序中的功能选择函数
void da_func ( int fd )
{
int flag_func_run ;
int flag_select_func ;
ssize _t ret ;
flag_func_run = FUNC_RUN ;
while ( flag_func_run = = FUNC _RUN )
{
// 打印功能选择界面
print_prompt ( ) ;
scanf ( “ % d ” , & flag_select_func );
// 用户输入选择
getchar () ; // 取得回车键 nullswitch ( flag_select_func )
{
case DA_SIN: { da_create_sin (fd ); break; } // 调用正弦波形生成函数
case DA_FANG: {da_create_fang(fd); break; } // // 调用方波生成函数
case FUNC_QUIT:
{
flag_func_run = FUNC_NOT_RUN ;
printf ( “ Quit DA function. \n”);
break ;
}nulldefault:
{
printf ( “ input = %x \n”, flag_select_func );
printf ( “ - - please input your select use 1 to 3 - - \n”);
}
}
}
}null6.6.4 D/A测试程序中的功能打印函数
void print_prompt (void )
{ // 打印选择界面
printf ( “ Select what you want to read: \n”);
printf (“ 1: create sin wave \n”);
printf ( “ 2: create fang wave \n” );
printf ( “ 3: quit \n” );
printf ( “ >”);
}null6.6.5 D/A测试程序中的波形生成函数
1. 正弦波生成函数
void da_create_sin ( int fd )
{
unsigned char buf [ ( int ) POINT ];
unsigned char *c ;
unsigned long i ;
int j;
double x;
for ( j=0; j< POINT ; j++ )
{
// 在一个正弦波周期内取 50 个点
x = sin ( ( j / POINT) * (2 * M_PI));
// 计算正弦波对应的数字二进制码序列
buf [ j ] = ( unsigned char ) 255* (x/2 +1) / 2;
}nullprintf ( “ create sin wave \n”);
printf ( “ Use \” Ctrl + c \ “ quit the function \n ”); // 使用“Ctrl+c”退出
while (1) {
c = &buf [0] ;
for ( j=0; j< POINT; j++ ) {
// 写入正弦波的数字信号进行转换
write (fd , c, 1 ) ;
c++;
usleep (1) ;
}
}
} null2. 方波生成函数
void da_create_fang ( int fd )
{
unsigned char buf [1] ;
printf ( “ create fang bo \n” );
printf ( “ Use \” Ctrl + c \ “ quit the function \n”) ; //使用“Ctrl+c”退出
while ( 1) {
*buf = 0xfe; // 向 D/A转换器输入二进制码 0xfe
write (fd, buf, 1); // 写入buf中的二进制码序列,输出方波信号的高电平
usleep (100000 ); // 延时一段时间
*buf = 0x0 ; // 向D/A转换器输入二进制码 0x00
write (fd, buf, 1); // 写入buf中的二进制码序列,输出方波信号的低电平
usleep (100000 ); // 延时一段时间
}
}null6.6.6 D/A测试程序的效果
图 6.8 示波器打印方波效果null