RT1060 ADC demo 寄存器解析_Part1

RT1060 ADC demo 寄存器解析_Part1

RT1060 ADC demo 寄存器解析_Part1

参考RT1060-EVKB SDK demo : adc_12b1msps_sar_polling

要求:

ADC1_IN0 is ADC input. Please sample voltage by J33-2 pin.

详细分析 adc_polling.c 从 API 调用到底层寄存器写入的顺序(软件触发情形),并给出时序说明与 mermaid 流程图,便于调试与理解硬件交互。


RT1062的ADC寄存器全览:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** ADC - Size of Registers Arrays */
#define ADC_HC_COUNT 8u
#define ADC_R_COUNT 8u

/** ADC - Register Layout Typedef */
typedef struct {
__IO uint32_t HC[ADC_HC_COUNT]; /**< Control register for hardware triggers, array offset: 0x0, array step: 0x4 */
__I uint32_t HS; /**< Status register for HW triggers, offset: 0x20 */
__I uint32_t R[ADC_R_COUNT]; /**< Data result register for HW triggers, array offset: 0x24, array step: 0x4 */
__IO uint32_t CFG; /**< Configuration register, offset: 0x44 */
__IO uint32_t GC; /**< General control register, offset: 0x48 */
__IO uint32_t GS; /**< General status register, offset: 0x4C */
__IO uint32_t CV; /**< Compare value register, offset: 0x50 */
__IO uint32_t OFS; /**< Offset correction value register, offset: 0x54 */
__IO uint32_t CAL; /**< Calibration value register, offset: 0x58 */
} ADC_Type;

概要

典型动作(软件触发):

  1. 初始化(ADC_Init
  2. 禁用硬件触发并校准(ADC_EnableHardwareTrigger(false)、ADC_DoAutoCalibration
  3. 选择通道并触发(ADC_SetChannelConfig -> 写 HC[0]
  4. 硬件采样/转换(ADC 内部使用 CFG 中的 ADCK/ADSTS/ADLSMP 配置)
  5. 写结果到 R[0] 并置 HS.COCO[0]
  6. CPU 轮询 HS,读 R[0]

下面给出更详细的寄存器写入顺序及每步与时间/注意事项。


步骤与寄存器写入(详细)

  1. ADC_Init(base, config)

    • CLOCK_EnableClock(s_adcClocks[instance])

      • 结果:ADC 模块时钟使能,外设开始有时钟脉冲。

      • 注意:若时钟未使能,后续读写寄存器可能无效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 保留了 硬件触发or软件触发 + n samples平均
tmp32 = base->CFG & (ADC_CFG_AVGS_MASK | ADC_CFG_ADTRG_MASK);
// 其他重新配置
// 1. 参考电压:
// 2. 采样周期:
// 3. 输入时钟源选择:可选 IPG时钟/IPG➗2/异步时钟ADACK; 这里选了异步时钟
// 4. 时钟分频: 配置为0,即 clock/1
// 5. 转换模式:8-bit/10-bit/12-bit
tmp32 |= ADC_CFG_REFSEL(config->referenceVoltageSource) |
ADC_CFG_ADSTS(config->samplePeriodMode) |
ADC_CFG_ADICLK(config->clockSource) |
ADC_CFG_ADIV(config->clockDriver) |
ADC_CFG_MODE(config->resolution);
if (config->enableOverWrite) tmp32 |= ADC_CFG_OVWREN_MASK;
...
base->CFG = tmp32;

​ - 写入 CFG:配置 ADICLK(决定 ADCK)、ADIV(分频)、MODE(位宽)、ADSTS(采样时钟长度)等。


关于RT1060的ADC参考电压:

唯一有效选项:外部 VREFH/VREFL 引脚

  • VREFH:通常接 3.3V 或 1.8V(取决于模拟电源域),必须满足数据手册要求(比如 2.7V–3.6V,如果 ADC 工作在 3.3V 模拟域)。
  • VREFL:通常接地(VSSAD)。

关于ADC采样周期 ADSTS:

字段作用:

  • ADC 在转换前需要将输入信号采样到内部采样电容上,采样时间越长,电容充电越充分,适合高阻抗信号源。
    • 如果信号源阻抗低,可以用短采样时间提高转换速度。
    • 长采样模式(ADLSMP=1)会显著增加采样周期,降低速率,但提高精度,尤其在高阻抗输入或低功耗场景。
  • ADSTS(Sample Period Mode)是一个 2 位字段,取值 00、01、10、11。
  • ADLSMP 决定是短采样还是长采样:
    • ADLSMP=0 → 短采样模式
    • ADLSMP=1 → 长采样模式
  • 每种组合对应不同的采样周期(单位:ADC 时钟周期)。

时间影响:CFG 写入之后,采样/转换的时钟源和采样窗口由这些字段决定;若使用异步时钟(AD),ADCK 会由所选时钟源产生。

1
2
3
4
tmp32 = base->GC & ~(ADC_GC_ADCO_MASK | ADC_GC_ADACKEN_MASK);
if (config->enableContinuousConversion) tmp32 |= ADC_GC_ADCO_MASK;
if (config->enableAsynchronousClockOutput) tmp32 |= ADC_GC_ADACKEN_MASK;
base->GC = tmp32;
  • 写入 GC:连续模式、异步时钟输出及其他控制位(DMA、校准启动位也存在于 GC)。
  1. 禁用硬件触发(若需要)

    • ADC_EnableHardwareTrigger(base, false) -> clear CFG.ADTRG
      • 置 ADTRG=0 表示软件触发,写 HC[0] 会触发转换。
  2. 自动校准(可选但建议)

    • base->GS = ADC_GS_CALF_MASK; // 清掉 CALF

    • base->GC |= ADC_GC_CAL_MASK; // 设置 CAL 位启动校准

    • 等待:while (base->GC & ADC_GC_CAL_MASK) { check GS.CALF }

    • 完成后:驱动读取 HS.COCO[0] 来确认结果(驱动中会读 R[0] 清 COCO

    • 时间:校准时间与 ADC 的硬件规格有关(可在数据手册查到绝对时间或以 ADCK 周期计)。校准期间不要触发其他转换。

  3. 触发一次转换(软件触发)

    • ADC_SetChannelConfig(base, 0, &cfg) => base->HC[0] = ADC_HC_ADCH(channel) | (AIEN?ADC_HC_AIEN:0)
      • 写 HC[0] 在软件触发模式下是“启动一次转换命令”。
      • 硬件:ADC 在写入后开始采样(采样保持)并随后转换。
  4. 硬件采样 -> 转换 -> 把结果写入 R[0] 并在 HS 对应位置 1

    • ADC 内部:采样期(由 ADSTS/ADLSMP 决定) -> 采样电容充电 -> 转换周期(受 ADCK 影响)

    • 完成:ADC 写 R[0]、设置 HS.COCO[0]。

  5. CPU 读取

    • 使用 ADC_GetChannelStatusFlags 检查 HS 的 COCO 位。

    • 读 R[0] 得到原始结果;读取可能会清除某些完成标志(驱动用读取来清 COCO)。


时序图(mermaid)

sequenceDiagram
    participant App as adc_polling.c (CPU)
    participant SDK as fsl_adc.c/h
    participant ADC as ADC hardware (registers)
    participant CLOCK as Clock controller
    participant UART as UART / Printf

    App->>SDK: ADC_GetDefaultConfig()
    App->>SDK: ADC_Init(base, config)
    SDK->>CLOCK: CLOCK_EnableClock(ADC)
    SDK->>ADC: write CFG (REFSEL, ADICLK, ADIV, MODE, ADSTS...)
    SDK->>ADC: write GC (ADCO, ADACKEN...)
    App->>SDK: ADC_EnableHardwareTrigger(false)
    SDK->>ADC: clear CFG.ADTRG (software trigger mode)

    App->>SDK: ADC_DoAutoCalibration()
    SDK->>ADC: GS = CLEAR_CALF
    SDK->>ADC: GC |= CAL (start calibration)
    Note over SDK,ADC: SDK polls until GC.CAL clears and GS.CALF is set
    ADC-->>SDK: Calibration complete

    App->>SDK: ADC_SetChannelConfig(base, 0, cfg)
    SDK->>ADC: write HC[0] (ADCH=channel, AIEN?) -> triggers conversion
    Note over ADC: sample (ADSTS/ADLSMP) -> convert (ADCK cycles) -> write R[0]
    ADC-->>SDK: set HS.COCO[0] = 1 (conversion complete)

    loop Poll until ready
        App->>SDK: ADC_GetChannelStatusFlags()
        SDK->>ADC: read HS
    end

    App->>SDK: ADC_GetChannelConversionValue()
    SDK->>ADC: read R[0]
    App->>UART: PRINTF(value)

延伸

  • 如果你打算改为硬件触发:设置 CFG.ADTRG = 1,然后通过外设(例如 PIT、FTM、GPIO 事件或 ADC_ETC)输出触发信号,HC 寄存器将被外部事件触发转换。
  • 若要用 DMA:设置 GC.DMAEN = 1 并配置 DMA 控制器为在 COCO 事件后搬运 R[n] 数据。
  • 若要提高稳定性:使用 ADC_SetHardwareAverageConfig() 来启用硬件平均(需配置 CFG.AVGSGC.AVGE)。

快速参考表(寄存器 => 关键位)

  • CFG:

    • REFSEL - 参考电压选择
    • ADSTS - 采样时序选择
    • ADICLK - AD 时钟源选择
    • ADIV - AD 时钟分频
    • MODE - ADC 分辨率
    • ADTRG - 0 = 软件触发,1 = 硬件触发
  • GC:

    • ADCO - 连续转换
    • ADACKEN - 异步时钟输出
    • CAL - 启动校准
    • DMAEN - 使能 DMA 触发
    • AVGE - 硬件平均使能
  • HC[n]:

    • ADCH - 要采样的通道号
    • AIEN - 转换完成中断
  • HS:

    • COCO[n] - channel group n 的转换完成标志
  • GS:

    • CALF - 校准失败标志
    • ADACT - 转换正在进行标志

附录一(总结版)

  1. 变量与初始化
  • 常量 g_Adc_12bitFullRange = 4096U:12-bit ADC 的满量程值是 4096。
  • BOARD_InitHardware():板级启动例程,包含引脚复用(Pin mux)、时钟系统初始化与调试串口(UART)初始化。若 BOARD_InitHardware() 没执行或配置不当,ADC 引脚可能无法作为模拟输入使用。
  1. ADC 配置与初始化
  • ADC_GetDefaultConfig(&adcConfigStruct):用 SDK 提供的默认值填充 adc_config_t(详见 fsl_adc.h)。这一步不会触碰寄存器,只是准备结构体。
  • ADC_Init(DEMO_ADC_BASE, &adcConfigStruct):底层实现会:
    • 通过 CLOCK_EnableClock(...) 打开 ADC 外设时钟(必须);
    • ADC->CFG(CFG 寄存器)来设置分辨率(MODE)、时钟来源(ADICLK)、分频(ADIV)、采样时序(ADSTS)、参考电压选择(REFSEL)和长采样/低功耗/高速标志;
    • ADC->GC(GC 寄存器)设置是否连续转换(ADCO)和异步时钟输出(ADACKEN)等。
  1. 触发与校准
  • ADC_EnableHardwareTrigger(DEMO_ADC_BASE, false):把 CFG.ADTRG 置 0 —— 软件触发模式(写 HC 寄存器时会立即触发转换)。
  • ADC_DoAutoCalibration(DEMO_ADC_BASE):执行自动校准流程,关键寄存器交互:
    • 清除 GS.CALF(写 ADC->GS = ADC_GS_CALF_MASK);
    • GC 寄存器置 CALADC->GC |= ADC_GC_CAL_MASK)来启动校准;
    • 等待 GC.CAL 清零(表示校准结束),中间检查 GS.CALF(校准失败标志)。校准完成后 SDK 会检查 HS.COCO[0] 是否置位并读取 R[0] 清除 COCO 标志。
  1. 通道选择与轮询读取
  • adcChannelConfigStruct.channelNumber = DEMO_ADC_USER_CHANNEL:选择模拟输入通道编号(该编号如何映射到引脚由 pin_mux.c 和器件参考手册决定)。
  • ADC_SetChannelConfig(DEMO_ADC_BASE, DEMO_ADC_CHANNEL_GROUP, &adcChannelConfigStruct):写 HC[channelGroup] 寄存器(字段 ADCH = channelNumber,AIEN = 中断使能)。在软件触发模式下,写 HC[0] 即会触发一次采样/转换。
  • while (0U == ADC_GetChannelStatusFlags(...)) { }:轮询 HS 寄存器对应位(COCO)直到转换完成。
  • ADC_GetChannelConversionValue(...):读 R[channelGroup],获得原始 ADC 值(0..4095)。
  1. 打印与使用
  • PRINTF("ADC Value: %d\\r\\n", raw):将数值通过串口打印。若要得到电压值,使用 V = raw / (2^N - 1) * Vref(N = 12,2^12 - 1 = 4095)。

附录二 (纯寄存器版代码实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
int main(void)
{
/* 我们将直接通过寄存器访问实现与原示例等价的行为(不调用 ADC_Init / ADC_SetChannelConfig 等 API)
但仍保留 BOARD_InitHardware() 以确保 pinmux 和串口已初始化。 */
adc_channel_config_t adcChannelConfigStruct;
uint32_t tmp32;
uint32_t result;

BOARD_InitHardware();

PRINTF("\r\nADC polling Example (register-level).\r\n");

// 一、ADC_Init() 开始
/* 1) 使能 ADC 外设时钟(对应 ADC1) */
CLOCK_EnableClock(kCLOCK_Adc1);

/* 2) 配置 ADCx_CFG 寄存器:参考默认配置(参考 fsl_adc.c 中的默认值)
- 保留 AVGS 与 ADTRG 位(AVGS 用于硬件平均,ADTRG 用于触发模式)
- 设置 REFSEL, ADSTS, ADICLK, ADIV, MODE 等字段 */
tmp32 = DEMO_ADC_BASE->CFG & (ADC_CFG_AVGS_MASK | ADC_CFG_ADTRG_MASK);
tmp32 |= ADC_CFG_REFSEL(kADC_ReferenceVoltageSourceAlt0) | ADC_CFG_ADSTS(kADC_SamplePeriod2or12Clocks) |
ADC_CFG_ADICLK(kADC_ClockSourceAD) | ADC_CFG_ADIV(kADC_ClockDriver1) | ADC_CFG_MODE(kADC_Resolution12Bit);
/* 不启用覆盖、长采样、低功耗或高速(与默认一致) */
DEMO_ADC_BASE->CFG = tmp32;

/* 3) 配置 ADCx_GC 寄存器:不连续转换,开启异步时钟输出(与默认保持一致) */
// ADCO=0:每次触发只进行一次转换;如果硬件平均(AVGE)开启,则进行一组平均(比如 4/8/16/32 次)后停止。
// ADCO=1:在启动转换后,ADC会持续不断进行转换;如果硬件平均开启,则持续进行一组组平均转换(每组完成后立即开始下一组),直到软件停止或硬件条件结束。

tmp32 = DEMO_ADC_BASE->GC & ~(ADC_GC_ADCO_MASK | ADC_GC_ADACKEN_MASK);
tmp32 |= ADC_GC_ADACKEN_MASK; /* enable asynchronous clock output as default */
DEMO_ADC_BASE->GC = tmp32;

// 一、ADC_Init() 结束
// 二、Disable 掉 ADC_EnableHardwareTrigger
/* 4) 强制软件触发模式:清除 CFG.ADTRG 位(ADTRG=0 表示软件触发) */
DEMO_ADC_BASE->CFG &= ~ADC_CFG_ADTRG_MASK;

// 三、ADC_DoAutoCalibration() 替代
/* 5) 自动校准(直接按寄存器序列实现) */
/* 清除校准失败标志 */
DEMO_ADC_BASE->GS = ADC_GS_CALF_MASK;
/* 启动校准(设置 GC.CAL 位) */
DEMO_ADC_BASE->GC |= ADC_GC_CAL_MASK;
/* 等待校准完成(GC.CAL 清零),期间检查 CALF */
while (0U != (DEMO_ADC_BASE->GC & ADC_GC_CAL_MASK))
{
if (0U != (DEMO_ADC_BASE->GS & ADC_GS_CALF_MASK))
{
PRINTF("ADC auto calibration failed (CALF set).\r\n");
break;
}
}
if (0U == (DEMO_ADC_BASE->GS & ADC_GS_CALF_MASK))
{
PRINTF("ADC_DoAutoCalibration() Done.\r\n");
}
else
{
PRINTF("ADC_DoAutoCalibration() Failed.\r\n");
}

/* 配置要采样的通道(channel group 与 channel number)
这里直接设置 HC[DEMO_ADC_CHANNEL_GROUP] 的 ADCH 字段,软件触发时写 HC[0] 会启动一次转换 */
adcChannelConfigStruct.channelNumber = DEMO_ADC_USER_CHANNEL;
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false; /* 轮询,不使用中断 */

PRINTF("ADC Full Range: %d\r\n", g_Adc_12bitFullRange);
while (1)
{
PRINTF("Press any key to get user channel's ADC value.\r\n");
GETCHAR();

/* 写 HC 寄存器触发一次软件转换(ADCH = 通道号,AIEN 可用于中断) */
tmp32 = ADC_HC_ADCH(adcChannelConfigStruct.channelNumber);
/* 不启用中断 */
DEMO_ADC_BASE->HC[DEMO_ADC_CHANNEL_GROUP] = tmp32;

/* 轮询 HS 的 COCO 标志,等待转换完成 */
while (0U == ((DEMO_ADC_BASE->HS >> DEMO_ADC_CHANNEL_GROUP) & 0x1U))
{
; /* busy-wait */
}

/* 读取结果寄存器 R[group] */
result = DEMO_ADC_BASE->R[DEMO_ADC_CHANNEL_GROUP];
PRINTF("ADC Value: %d\r\n", result);
}
}
作者

Gavin

发布于

2025-12-11

更新于

2025-12-11

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×