[MSPM0开发]之九 MSPM0G3507的ADC
[MSPM0开发]之九 MSPM0G3507的ADC
- 一、 MSPM0G3507 ADC概述
- 二、 MSPM0G3507 ADC系统框图
- 2.1 电压基准
- 2.2 分辨率
- 2.3 硬件均值计算
- 2.4 采样触发源和采样模式
- 2.5 转换模式
- 2.6 转换结果数据格式
- 2.7 高级特性
- 2.7.1 非FIFO模式下的ADC操作(单次转换和重复单次转换)
- 2.7.2 非FIFO模式下的ADC操作(序列转换和重复序列转换)
- 2.7.3 FIFO模式下的ADC操作(单次转换和重复单次转换)
- 2.7.4 FIFO模式下的ADC操作(序列转换)
- 三、 基于driverLib的例程
- 四、 牛刀小试1 非FIFO模式单通道连续转换+转换完成中断
- 五、 牛刀小试2 非FIFO模式连续序列转换模式+转换完成中断
一、 MSPM0G3507 ADC概述
MSPM0G3507的ADC 是一款高性能逐次逼近型寄存器 (SAR) 模数转换器
ADC 支持快速的 12 位、 10 位和 8 位模数转换, 具有 12 位 SAR 内核、采样和转换模式控制功能和多达 12 个独立的转换和控制缓冲器。此 ADC 允许在无需 CPU 干预的情况下, 转换和存储多达 12 个独立的模数转换器(ADC) 样本。
ADC 模块特性包括:
• 12 位分辨率下的转换率为 4Msps
• 集成硬件过采样, 平均处理多达 128 个样本
• 满量程 ADC 工作电压范围
• 由软件或定时器控制的可编程采样周期的采样保持
• 两个采样触发源: 软件触发和事件触发
• 可通过软件选择 1.4V 或 2.5V 片上基准电压
• 可配置 ADC 基准源: VDD、内部基准 (VREF) 或外部基准( VREF+ 和 VREF-)
• 多达 16 个可单独配置的外部输入通道
• 具有用于温度检测、电源监控和模拟信号链的内部转换通道( 请参阅器件特定的数据表, 了解可用性和通道映射)
• 可配置 ADC 时钟源
• 单通道、重复单通道、序列( 自动扫描) 和重复序列( 重复自动扫描) 转换模式
• 12 个转换结果存储寄存器 (MEMRES0:11)
• 使用窗口比较器对来自转换结果寄存器的输入信号进行低功耗监控
• DMA 支持在传输完成时生成中断事件
• 在 RUN、 SLEEP 和 STOP 模式下运行
• 可以在除 SHUTDOWN 之外的任何工作模式下触发
• 针对低功耗工作模式的自动电源、基准和时钟控制
• 支持并行的两个 ADC 同时和同步运行( 采样和转换):需要多个ADC外设的芯片才支持
• CDAC 修整值半自动校准
MSPM0G3507支持两个具有总计多达 17 个外部通道的 12 位 4Msps 同步采样模数转换器 (ADC); 硬件均值计算可在 250ksps 下实现 14 位有效分辨率。
二、 MSPM0G3507 ADC系统框图
采样时钟 (SAMPCLK) 以 ULPCLK、 SYSOSC 或 HFCLK 为时钟源, 转换时钟 (CONVCLK) 以 ADC IP内的 80Mhz 振荡器为时钟源。 转换时钟80MHz
关于ADC转换结果的访问:
2.1 电压基准
2.2 分辨率
ADC 支持在 12 位( 默认) 、 10 位和 8 位分辨率模式下运行。分辨率模式通过 CTL2 寄存器中的 RES 位进行配置。
• 当选择 12 位模式时, 转换阶段总共需要 14 个转换时钟周期
• 当选择 10 位模式时, 转换阶段总共需要 12 个转换时钟周期
• 当选择 8 位模式时, 转换阶段总共需要 9 个转换时钟周期
2.3 硬件均值计算
ADC 在硬件中实现数字样本均值计算( 硬件均值计算), 无需软件或 CPU 干预即可有效地提高 ADC 的有效分辨率。硬件均值计算功能通过 CTL1 寄存器中的 AVGN 和 AVGD 位进行配置。
• AVGN 定义为当前 MEMCTLx 累积的转换次数
• AVGD 定义累加值使用位移所除以的值
启用硬件均值计算后,所有的通道均有效。
2.4 采样触发源和采样模式
采样触发器
可以通过 CTL1 寄存器中的 TRIGSRC 位选择两个采样触发源: 一个是软件触发器, 另一个是事件触发器。
当选择软件触发器作为触发源时, 应用软件可以设置 CTL1 寄存器中的 SC 位来启动采样阶段。当选择事件触发器作为触发源时, 事件管理器中所选事件的上升沿将启动采样阶段。事件始终为边沿触发。
采样模式
有两种可用的采样模式: AUTO 和 MANUAL, 可以通过 CTL1 寄存器中的 SAMPMODE 位进行选择。
自动采样模式
自动采样模式下,可以使用采样定时器设置采样时间“间隔”,采样计时器为 10 位宽, 并且有两个采样时间比较寄存器 (SCOMPx) ,可以使用 MEMCTL 寄存器中的== STIME== 位来选择这两个 SCOMP 寄存器中的一个。
手动采样模式(仅支持单通道单次转换)
在手动模式下, 当设置 SC 位时将生成采样信号, 该信号可以与采样时钟异步。采样窗口的持续时间由软件通过将 SC 位保持在高电平来控制。由于事件始终是边沿触发的, 因此, 任何转换模式都不支持通过事件触发的手动模式。仅单通道单次转换模式支持手动采样模式的软件触发, 而其他三种转换模式均不支持。
从采样窗口结束到转换窗口开始, 具有 2-3 个周期的同步延迟。
2.5 转换模式
ADC 提供四种转换模式:
1. 单通道单次转换
• 可使用 MEMCTL 选择通道
• 所选通道仅采样和转换一次
• 启用硬件均值计算后, 执行多次转换
2. 单通道重复转换
• 可使用 MEMCTL 选择通道
• 所选通道将被重复采样和转换, 直到 ENC 被软件清零
– 如果设置了 TRIG 位, 那么需要一次触发来进行下一个转换
• 启用硬件均值计算后, 执行多次转换
3. 序列通道单次转换
• 可使用 STARTADD、 ENDADD 和 MEMCTL 寄存器形成通道组
• 组中的每个通道仅采样和转换一次
• 启用硬件均值计算后, 序列期间会在一个通道上执行多次转换
• 即使 ENC 在序列中被清零, 也将完成该序列
4. 序列通道重复转换
• 可使用 STARTADD、 ENDADD 和 MEMCTL 寄存器形成通道组
• 通道组将被重复采样和转换, 直到 ENC 被软件清零
– 如果设置了 TRIG 位, 那么需要一次触发来进行下一个转换
• 如果 ENC 被清零, 则在最后一次转换结束时停止操作
• 启用均值计算后, 序列期间会在一个通道上执行多次转换
触发和转换模式用法矩阵
2.6 转换结果数据格式
ADC 支持两种数据格式 – 无符号二进制和二进制补码有符号二进制。无符号二进制结果右对齐存储在 MEMRES寄存器或 FIFO 中。有符号二进制结果左对齐存储在 MEMRES 寄存器或 FIFO 中。
2.7 高级特性
MSPM0G3507内部具有2个ADC外设,可以实现同步采样。
同时采样
电流和电压检测等一些应用需要同时在多个模拟信号中进行测量。在这些情况下, 需要在单个 MCU 上使用多个ADC 来执行同步采样。 MSPM0xx 平台中具有多个 ADC 外设的任何器件都支持同步采样。
对于这个用例, 应该使用 ULPCLK 作为 ADCCLK 的源, 因为它也是 PD0 中所有外设的时钟。这意味着它已经与总线时钟同步, 所以可以确保采样开始的确定性时序。除了使用 ULPCLK 作为两个 ADC 外设的采样时钟源外,用户还应选择相同的采样触发器, 并在两个 ADC 上使用相同的值对时钟预分频器和/或 SCOMP 进行编程。
简单理解就是使用ULPCLK低速时钟源,然后其它参数(采样、触发)保持一致。
ABOUT DMA
ADC 具有一个用于与 DMA 进行通信的专用接口。该接口可使用 DMA 自动将 ADC 结果存储到存储器。
“DMA 触发计数”信号指示 DMA 在一次触发请求时可以传输的样本数。
ADC 使用“DONE 状态”信号生成 DMA DONE 中断, 该信号指示编程块大小的 DMA 数据传输是否完成。
CTL2 寄存器中的 DMAEN 位用于使 DMA 能够进行 ADC 数据传输。当 DMA 发出“DONE 状态”信号时, ADC硬件会将 DMAEN 位清零。
ADC 还包含一个可选的先入先出缓冲区FIFO, 提供了一种方法来存储 ADC 结果供将来使用.
2.7.1 非FIFO模式下的ADC操作(单次转换和重复单次转换)
– 配置 STARTADD 位以选择所需的 MEMCTLx 寄存器
• MEMCTLx 与 MEMRESx 相关
• MEMRESx 与 MEMRESIFGx= 相关
– 配置 MEMCTL CHANSEL 位以选择所需的 ADC 通道
– MEMRESx 中提供转换数据
– 可以设置 MEMRESIFGx 以生成 CPU 中断或 DMA 触发
– 必须通过软件将 SAMPCNT 编程为 1 以执行 DMA 操作
– 当 ADC 在 CPU 或 DMA 读取前一个样本之前更新 MEMRESx 时, 会设置转换溢出标志 OVIFG(读慢了)
– 当 CPU 或 DMA 在下一个转换结果可用前读取 MEMRESx 寄存器时, 会设置转换下溢标志UVIFG==(读快了)
2.7.2 非FIFO模式下的ADC操作(序列转换和重复序列转换)
– 配置 STARTADD 位以选择序列中的第一个 MEMCTL
– 配置 ENDADD 位以选择序列中的最后一个 MEMCTL
• MEMCTLx 与 MEMRESx 相关
• MEMRESx 与 MEMRESIFGx相关
– 配置每个 MEMCTLx CHANSEL 位以选择所需的 ADC 通道
– MEMRESx 中提供转换数据
– 可以设置 MEMRESIFGx 以生成 CPU 中断或 DMA 触发
– 必须根据软件针对 DMA 操作进行的阈值设置, 通过软件将 SAMPCNT 编程为一个合适的值
– 当 ADC 在 CPU 或 DMA 读取前一个样本之前更新 MEMRESx 时, 会设置转换溢出标志 OVIFG(读慢了)
– 当 CPU 或 DMA 在下一个转换结果可用前读取 MEMRESx 寄存器时, 会设置转换下溢标志 UVIFG(读快了)
2.7.3 FIFO模式下的ADC操作(单次转换和重复单次转换)
– 配置 STARTADD 位以选择所需的 MEMCTLx 寄存器
• MEMCTLx 与 MEMRESx 不相关
• MEMRESx 与 MEMRESIFGx 相关
– 配置 MEMCTL CHANSEL 位以选择所需的 ADC 通道
– 转换数据按顺序加载到 MEMRES0/1/2/…/N( FIFO 结构)
– CPU 或 DMA 必须从专用的 FIFODAT 寄存器( 而不直接从 MEMRES 寄存器) 读取 ADC 样本
• FIFO 中的数据始终压缩为两个样本, 并在 CPU 或 DMA 读取 FIFODAT 时作为 32 位数据提供
– MEMRESIFGx 可用作阈值条件以生成 CPU 中断或 DMA 触发
• 为了充分利用 FIFO,== 可以使用最后一个 MEMRESIFG==
– 必须根据针对 DMA 操作进行的阈值设置, 通过软件将 SAMPCNT 编程为一个合适的值
– 当 ADC 在 CPU 或 DMA 读取前一个样本之前更新 MEMRESx 时, 会设置转换溢出标志 OVIFG
– 当 CPU 或 DMA 在 MEMRESx 寄存器中的转换结果可用之前读取 FIFODAT 寄存器时, 会设置转换下溢标志 UVIFG。
2.7.4 FIFO模式下的ADC操作(序列转换)
– 配置 STARTADD 位以选择序列中的第一个 MEMCTL
– 配置 ENDADD 位以选择序列中的最后一个 MEMCTL
• MEMCTLx 与 MEMRESx 不相关
• MEMRESx 与 MEMRESIFGx 相关
– 配置每个 MEMCTLx CHANSEL 位以选择所需的 ADC 通道
– 转换数据按顺序加载到 MEMRES0/1/2/…/N( FIFO 结构)
– CPU 或 DMA 必须从专用的 FIFODAT 寄存器( 而不直接从 MEMRES 寄存器) 读取 ADC 样本
• FIFO 中的数据始终压缩为两个样本, 并在 CPU 或 DMA 读取 FIFODAT 时作为 32 位数据提供
– MEMRESIFGx 可用作阈值条件以生成 CPU 中断或 DMA 触发
• 为了充分利用 FIFO, 可以使用最后一个 MEMRESIFG
– 必须根据针对 DMA 操作进行的阈值设置, 通过软件将 SAMPCNT 编程为一个合适的值
– 当 ADC 在 CPU 或 DMA 读取前一个样本之前更新 MEMRESx 时, 会设置转换溢出标志 OVIFG
– 当 CPU 或 DMA 在 MEMRESx 寄存器中的转换结果可用之前读取 FIFODAT 寄存器时, 会设置转换下溢标志 UVIFG
-------------- 其它注意事项 ---------------
• 对于基于 CPU 或 DMA 的操作, 不建议采用启用了 FIFO 的单次转换模式。这将导致下溢情况, 并且必须在软件中丢弃不需要的 16 位数据。
• 重复序列转换模式不支持基于 DMA 的数据传输, 因为 DMA 不支持循环寻址模式。
• CPU 或 DMA 读取后, 不会自动清除 FIFODAT 寄存器中的数据。新的转换数据会覆盖 FIFODAT 寄
存器中的先前数据。
• 为确保同步读取存储 16 位样本的 32 位 FIFO 中的字节, 可以使用特定的 DMA 触发器。尤其是,
选择 MEMRES1、 MEMRES3、 MEMRES5、 MEMRES7、 MEMRES9 和 MEMRES11 将使 FIFO
中的字节读取与相应的 MEMRESx 字节同步。
• 如果 ADC 在重复序列模式或正常重复模式期间被禁用, 则值得注意的是在 ADC 完全停止之前可能
会发生额外的转换。
三、 基于driverLib的例程
四、 牛刀小试1 非FIFO模式单通道连续转换+转换完成中断
时钟选择内部高速时钟,32MHz
配置uart用于发送结果
ADC配置
选择的转换寄存器编号和通道编号可以不一致。 比如这里选择MEMCTL2 & MEMRES2 & MEMRESIFG2,实际转换通道为ch3(PA18)。
MEMCTLx 与 MEMRESx 相关
• MEMRESx 与 ==MEMRESIFGx= 相关
ADC初始化代码:
/* ADC12_0 Initialization */
static const DL_ADC12_ClockConfig gADC12_0ClockConfig = {.clockSel = DL_ADC12_CLOCK_SYSOSC,.divideRatio = DL_ADC12_CLOCK_DIVIDE_1,.freqRange = DL_ADC12_CLOCK_FREQ_RANGE_24_TO_32,
};
SYSCONFIG_WEAK void SYSCFG_DL_ADC12_0_init(void)
{DL_ADC12_setClockConfig(ADC12_0_INST, (DL_ADC12_ClockConfig *) &gADC12_0ClockConfig);DL_ADC12_initSingleSample(ADC12_0_INST,DL_ADC12_REPEAT_MODE_ENABLED, DL_ADC12_SAMPLING_SOURCE_AUTO, DL_ADC12_TRIG_SRC_SOFTWARE,DL_ADC12_SAMP_CONV_RES_12_BIT, DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED);DL_ADC12_setStartAddress(ADC12_0_INST, DL_ADC12_SEQ_START_ADDR_02);DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_2,DL_ADC12_INPUT_CHAN_3, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);DL_ADC12_setSampleTime0(ADC12_0_INST,32000);/* Enable ADC12 interrupt */DL_ADC12_clearInterruptStatus(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM2_RESULT_LOADED));DL_ADC12_enableInterrupt(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM2_RESULT_LOADED));DL_ADC12_enableConversions(ADC12_0_INST);
}
main.c部分代码:
int main(void) {SYSCFG_DL_init();NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);gCheckADC = false;UART_printf("helloworlf\r\n");DL_ADC12_startConversion(ADC12_0_INST);while (1) {while (false == gCheckADC) {}gAdcResult = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_2);if (gAdcResult > 0x7ff) {DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);} else {DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);}UART_printf("res = %d\r\n", gAdcResult);gCheckADC = false;}
}
void ADC12_0_INST_IRQHandler(void) {switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {case DL_ADC12_IIDX_MEM2_RESULT_LOADED: // 这里选择配置的对应MEM编号gCheckADC = true;break;default:break;}
}
运行结果:
五、 牛刀小试2 非FIFO模式连续序列转换模式+转换完成中断
时钟选择内部高速时钟,32MHz
ADC初始化代码:
/* ADC12_0 Initialization */
static const DL_ADC12_ClockConfig gADC12_0ClockConfig = {.clockSel = DL_ADC12_CLOCK_SYSOSC,.divideRatio = DL_ADC12_CLOCK_DIVIDE_1,.freqRange = DL_ADC12_CLOCK_FREQ_RANGE_24_TO_32,
};
SYSCONFIG_WEAK void SYSCFG_DL_ADC12_0_init(void)
{DL_ADC12_setClockConfig(ADC12_0_INST, (DL_ADC12_ClockConfig *) &gADC12_0ClockConfig);DL_ADC12_initSeqSample(ADC12_0_INST,DL_ADC12_REPEAT_MODE_ENABLED, DL_ADC12_SAMPLING_SOURCE_AUTO, DL_ADC12_TRIG_SRC_SOFTWARE,DL_ADC12_SEQ_START_ADDR_00, DL_ADC12_SEQ_END_ADDR_01, DL_ADC12_SAMP_CONV_RES_12_BIT,DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED);DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_0,DL_ADC12_INPUT_CHAN_2, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_1,DL_ADC12_INPUT_CHAN_3, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);DL_ADC12_setSampleTime0(ADC12_0_INST,32000);/* Enable ADC12 interrupt */DL_ADC12_clearInterruptStatus(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM1_RESULT_LOADED));DL_ADC12_enableInterrupt(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM1_RESULT_LOADED));DL_ADC12_enableConversions(ADC12_0_INST);
}
main.c主要代码
int main(void) {/* Initialize peripherals and enable interrupts */SYSCFG_DL_init();NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);UART_printf("helloworld\r\n");gCheckADC = false;DL_ADC12_startConversion(ADC12_0_INST);while (1) {/* Wait until all data channels have been loaded. */while (gCheckADC == false) {}/* Store ADC Results into their respective buffer */gAdcResult0 = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);gAdcResult1 = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_1);UART_printf("res1=%4d,res2=%4d\r\n", gAdcResult0, gAdcResult1);gCheckADC = false;}
}/* Check for the last result to be loaded then change boolean */
void ADC12_0_INST_IRQHandler(void) {switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {case DL_ADC12_IIDX_MEM1_RESULT_LOADED:gCheckADC = true;break;default:break;}
}