为了能够使用数字系统(如MCU)处理模拟信号,必须把模拟信号转换成相应的数字
信号。能够实现这种转换的电路称为ADC(Analog-to-Digital Converter,模-数转换器)。
ADC能够将连续变化的模拟电压转换成离散的数字量。
Stellaris系列ARM集成有一个10位的ADC模块,支持8个输入通道,以及一个内部温度传感器。
ADC模块含有一个可编程的序列发生器,可在无需控制器干涉的情况下对多个模拟输入源进行采样。
每个采样序列均对完全可配置的输入源、触发事件、中断的产生和序列优先级提供灵活的编程。
Stellaris系列ARM的ADC模块提供系列特性:
8个模拟输入通道
单端和差分输入配置
内部温度传感器
高达1Msps(每秒采样一百万次)的采样率
4个可编程的采样转换序列,入口长度1到8,每个序列均带有相应的转换结果FIFO
灵活的触发控制:处理器(软件)、定时器、模拟比较器、PWM、GPIO
硬件可对多达64个采样值进行平均计算(牺牲速度换取精度)
转换器采用内部的3V参考电压
分开的模拟电源和模拟地,跟数字电源和数字地分离
ADC功能描述
Stellaris系列ARM的ADC通过使用一种基于序列(sequence-based)的可编程方法来收集采样数据,取代了传统ADC模块使用的单次采样或双采样的方法。每个采样序列均为一系列程序化的连续(back-to-back)采样,使得ADC可以从多个输入源中收集数据,而无需控制器对其进行重新配置或处理。对采样序列内的每个采样进行编程,包括对某些参数进行编程,如输入源和输入模式(差分输入还是单端输入),采样结束时的中断产生,以及指示序列最后一个采样的指示符。
1. 采样序列发生器
采样控制和数据捕获由采样序列发生器(Sample Sequencer)进行处理。所有序列发生器的实现方法都相同,不同的只是各自可以捕获的采样数目和FIFO深度。每个序列发生器可捕获的最大采样数及相应的FIFO深度。在本实现方案中,每个FIFO入口均为32位(1个字),低10位包含的是转换结果。
ADC序列发生器的采样数和FIFO深度
序列发生器 采样数 FIFO深度
SS0 8 8
SS1 4 4
SS2 4 4
SS3 1 1
对于一个指定的采样序列,每个采样均可以选择对应的输入管脚,以及温度传感器的选择、中断使能、序列末端和差分输入模式。
当配置一个采样序列时,控制采样的方法是灵活的。每个采样的中断均可使能,这使得在必要时可在采样序列的任意位置产生中断。同样地,也可以在采样序列的任何位置结束采样。例如,如果使用序列发生器0,那么可以在第5个采样后结束并产生中断,中断也可以在第3个采样后产生。
在一个采样序列执行完后,可以利用函数ADCSequenceDataGet( )从ADC采样序列FIFO里读取结果。上溢和下溢可以通过函数ADCSequenceOverflow( )和ADCSequenceUnderflow( )进行控制。
2. 模块控制
在采样序列发生器的外面,控制逻辑的剩余部分负责对中断产生、序列优先级设置和触发配置等任务。
大多数的ADC控制逻辑都是14~18MHz的ADC时钟速率下运行。当选择了系统XTAL时,内部的ADC分频器通过硬件自动配置。自动时钟分频器的配置对所有Stellaris系列ARM均以16.667MHz操作频率为目标。
3. 中断
采样序列发生器虽然会对引起中断的事件进行检测,但它们不控制中断是否真正被发送到中断控制器。ADC模块的中断信号由相应的状态位来控制。ADC中断状态分为原始的中断状态和屏蔽的中断状态,这可以通过函数ADCIntStatus( )来查知。函数ADCIntClear( )可以清除中断状态。
4. 优先级设置
当同时出现采样事件(触发)时,可以将为这些事件设置优先级,安排它们的处理顺序。优先级值的有效范围是0到3,其中0代表优先级最高,而3代表优先级最低。优先级相同的多个激活采样序列发生器单元不会提供一致的结果,因此软件必须确保所有激活采样序列发生器单元的优先级是唯一的。
5. 采样事件
采样序列发生器可以通过多种方式激活,如处理器(软件)、定时器、模拟比较器、PWM、GPIO。对于某些型号如LM3S1138并不存在专门的硬件PWM模块,因此也不会存在PWM触发方式。外部的外设触发源随着Stellaris家族成员的变化而改变,但所有器件都公用“控制器”和“一直(Always)”触发器。软件可通过函数ADCProcessorTrigger( )来启动采样。
在使用“一直(Always)”触发器时必须非常小心。如果一个序列的优先级太高,那么可能会忽略(starve)其它低优先级序列。
6. 硬件采样平均电路
使用硬件平均电路可产生具有更高精度的结果,然而结果的改善是以吞吐量的减小为代价的。硬件平均电路可累积高达64个采样值并进行平均,从而在序列发生器FIFO中形成一个数据入口。吞吐量根据平均计算中的采样数而相应地减小。例如,如果将平均电路配置为对16个采样值进行平均,则吞吐量也减小了16因子(factor)。
平均电路默认是关闭的,因此,转换器的所有数据直接传送到序列发生器FIFO中。进行平均计算的硬件由相关的硬件寄存器进行控制。ADC中只有一个平均电路,所有输入通道(不管是单端输入还是差分输入)都是接收相同数量的平均值。
7. 模数转换器
转换器本身会为所选模拟输入产生10位输出值。通过某些特定的模拟端口,输入的失真可以降到最低。转换器必须工作在16MHz左右,如果时钟偏差太多,则会给转换结果带来很大误差。
8. 差分采样
除了传统的单边采样(single-ended sampling)外,ADC模块还支持两个模拟输入通道的差分采样(differential Sampling)。
当队列步(a sequence step)被配置为差分采样,会形成4个差分对(differential pairs)之一,编号0~3。差分对0采样模拟输入0和1,差分对1采样模拟输入2和3,依此类推。ADC不会支持其它差分对形式,比如模拟输入0跟模拟输入
3。差分对所支持的编号有赖于模拟输入的编号(详见表1.2)。
差分对 模拟输入
0 0和1
1 2和3
2 4和5
3 6和7
在差分模式下被采样的电压是奇数和偶数通道的差值,即:
ΔV = VIN_ENEN - VIN_ODD
其中ΔV是差分电压,VIN_EVEN是偶数通道,VIN_ODD是奇数通道。因此:
如果ΔV = 0,则转换结果 = 0x1FF
如果ΔV > 0,则转换结果 > 0x1FF(范围在0x1FF~0x3FF)
如果ΔV < 0,则转换结果 < 0x1FF(范围在0~0x1FF)
好了,理论讲完了,我的例子调通了也不太容易,我用了两个晚上在琢磨,终于搞定哈
大家注意两点:
一、不管你用什么作为主时钟源,频率毕需大于16MHZ
二、采样序列完全可配成深度为1,就象以前用51单片机一样的AD转换
我这个例子,没有输出,但我用仿真读出值为0x00000665转换成电压后为1637
就是1.637伏,我用胜利表量下,真准啊
拿出分享哈!!!!!!
上程序:
等明后天调一下输出再分享个哈:
#include <LM3Sxxx.H>
#include <systick.h>
#include <adc.h>
#include <stdio.h>
#define ADCSequEnable ADCSequenceEnable
#define ADCSequDisable ADCSequenceDisable
#define ADCSequConfig ADCSequenceConfigure
#define ADCSequStepConfig ADCSequenceStepConfigure
#define ADCSequDataGet ADCSequenceDataGet
tBoolean ADC_EndFlag = false; // 定义ADC转换结束的标志
//unsigned long TheSysClock;
// 防止JTAG失效
void jtagWait(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); // 使能KEY所在的GPIO端口
GPIOPinTypeGPIOInput(GPIO_PORTC_BASE,GPIO_PIN_4); // 设置KEY所在管脚为输入
if (GPIOPinRead(GPIO_PORTC_BASE,GPIO_PIN_4) == 0x00) // 若复位时按下KEY,则进入
{
while(1); // 死循环,以等待JTAG连接
}
SysCtlPeripheralDisable(SYSCTL_PERIPH_GPIOC); // 禁止KEY所在的GPIO端口
}
// ADC初始化
void adcInit(void)
{
SysCtlLDOSet(SYSCTL_LDO_2_75V); //配置PLL前需将LDO电压设置为2.75V
SysCtlClockSet(SYSCTL_USE_PLL | // 系统时钟设置,采用PLL
SYSCTL_OSC_MAIN | // 主振荡器
SYSCTL_XTAL_6MHZ | // 外接6MHz晶振
SYSCTL_SYSDIV_4); // 分频结果为50MHz
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC); // 使能ADC模块
SysCtlADCSpeedSet(SYSCTL_ADCSPEED_125KSPS); // 设置ADC采样速率
ADCSequDisable(ADC_BASE, 0); // 配置前先禁止采样序列
// 采样序列配置:ADC基址,采样序列编号,触发事件,采样优先级
ADCSequConfig(ADC_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
// 采样步进设置:ADC基址,采样序列编号,步值,通道设置
ADCSequStepConfig(ADC_BASE, 0, 0, ADC_CTL_CH0 |
ADC_CTL_END |
ADC_CTL_IE);
ADCIntEnable(ADC_BASE, 0); // 使能ADC中断
IntEnable(INT_ADC0); // 使能ADC采样序列中断
IntMasterEnable( ); // 使能处理器中断
ADCSequEnable(ADC_BASE, 0); // 使能采样序列
}
// ADC采样
unsigned long adcSample(void)
{
unsigned long ulValue;
ADCProcessorTrigger(ADC_BASE, 0); // 处理器触发采样序列
while (!ADC_EndFlag); // 等待采样结束
ADC_EndFlag = false; // 清除ADC采样结束标志
ADCSequDataGet(ADC_BASE, 0, &ulValue); // 读取ADC转换结果
return(ulValue);
}
// 主函数(程序入口)
int main(void)
{
unsigned long ulVal;
char cBuf[30];
jtagWait( ); // 防止JTAG失效,重要!
SysTickEnable( ); // 使能SysTick计数器
adcInit( ); // ADC初始化
for (;;)
{
ulVal = adcSample( ); // ADC采样
ulVal = (ulVal * 3000) / 1024; // 转换成电压值
//sprintf(cBuf, "ADC0 = %ld(mV)\r\n", ulVal); // 输出格式化
//uartPuts(cBuf); // 通过UART显示结果
SysCtlDelay(1500 * (SysTickValueGet( ) / 3000)); // 延时约1500ms
}
}
// ADC采样序列0的中断
void ADC_Sequence_0_ISR(void)
{
unsigned long ulStatus;
ulStatus = ADCIntStatus(ADC_BASE, 0, true); // 读取中断状态
ADCIntClear(ADC_BASE, 0); // 清除中断状态,重要
if (ulStatus != 0) // 如果中断状态有效
{
ADC_EndFlag = true; // 置位ADC采样结束标志
}
}
打包上传:
myADC.zip
(1.52 KB, 下载次数: 97)