一、 概述
ADC 转换就是输入模拟的信号量转换成数字量,读取数字量必须等 ADC 转换完成后,完成一个通道的读取叫做采样周期;采样周期 = 转换时间 + 读取时间,而转换时间 = 采样时间 + 转化时间;采样时间通过寄存器进行设置,设置越长越精确,E3 ADC 固定转化时间为 13.5 个时钟周期;在某些领域对 ADC 采样频率要求极高,通常需要精确计算 ADC 的采样频率;本文通过 ADC 同步采样模式,RC 定时器触发 ADC 采样,采用中断读取的方式实时读取 ADC 转化时间,做到 ADC 采样频率的精准计算。
硬件平台:E3640 官方开发板 (SD103_E3_GATEWAY_ePOWERTRAIN_A03_019),如下图所示;
软件平台:e3_gateway_E3640_mcu_demo_E3_SSDK_PTG3.0 中 driver_demo adc_sync_poll、adc_Async_int 例程。
图1 E3640 官方开发板
二、 ADC 配置详解
1. ADC 引脚设置
配置管脚相应 scr,使得片外管脚信号与片内 ADC 电路导通。
const scr_signal_t adc_scr[] = {
SCR_SF_ANA_SF_CFG_APD_A_A12_CTRL_3_0,
SCR_SF_ANA_SF_CFG_APD_A_A13_CTRL_3_0,
SCR_SF_ANA_SF_CFG_APD_A_A14_CTRL_3_0,
SCR_SF_ANA_SF_CFG_APD_A_A15_CTRL_3_0
};
for (i = 0; i < sizeof(adc_scr) / sizeof(adc_scr[0]); i++) {
/* set APD_A ctrl to 1 */
scr_set(&scr_ctrl, &adc_scr[i], 1);
}
用 ADC_CH_SEL_TAISHAN 构建单端通道号,通过设置 ADC_CH_SEL_TAISHAN 的变量设置 ADC 模块与外部引脚的对应,从 E3 DataSheet 截取 ADC 引脚对应部分如下表1,参数设置主要包含:采集通道,采集极性,引脚复用 MUX 配置,详细对应关系如下图所示。
enum adc1_ch5n_mux {
ADC1_CH5N_MUX_A0 = 0u,
ADC1_CH5N_MUX_A2,
ADC1_CH5N_MUX_A4,
ADC1_CH5N_MUX_A6,
ADC1_CH5N_MUX_A8,
ADC1_CH5N_MUX_A10,
ADC1_CH5N_MUX_A12, // CH5_N.MUX_6
ADC1_CH5N_MUX_A14
};
2. 采样模拟量设置
sdrv_adc_ana_param_cfg_t 这个结构体主要设置 ADC 模拟量采集相关的参数,主要设置模拟量的采样时间、参考电压、采样模式(单端采样、差分采样)。
typedef struct sdrv_adc_ana_param_cfg {
uint32_t sample_time : 3; /* ADC采样时间设置 */
uint32_t : 1;
uint32_t ref_sel : 1; /* ADC参考电压设置 */
uint32_t input_mode : 1; /* ADC 采样模式:单端采样,差分采样 */
} sdrv_adc_ana_param_cfg_t;
sdrv_adc_ana_param_cfg_t ana_param[] = {
{SDRV_ADC_SAMPLE_TIME_10D5, SDRV_ADC_REF_VREFP1, SDRV_ADC_INPUT_SINGLE},
{SDRV_ADC_SAMPLE_TIME_34D5, SDRV_ADC_REF_VREFP1, SDRV_ADC_INPUT_SINGLE},
{SDRV_ADC_SAMPLE_TIME_18D5, SDRV_ADC_REF_VREFP1, SDRV_ADC_INPUT_DIFF},
};
/* 将ana_param依次配置到 0~2 位置上 */
for (i = 0; i < sizeof(ana_param) / sizeof(ana_param[0]); i++) {
sdrv_adc_ana_param_cfg(ADC_DEV_TEST, i, ana_param[i]);
}
3. RC寄存器设置
ADC有两种触发方式,包含:HTC、RC0、RC1、RC2、RC3,HTC 为硬件通道触发,RC 可以通过定时器方式定时触发 ADC 采样,HTC 和 RC 将触发信息发送给 SCHEDULER,SCHEDULER 通过仲裁方式优先处理优先级高的触发信号,RC0、RC1、RC2、RC3 均可单独配置使用,也可以将 RC0 设置为主模式,将 RC1、RC2、RC3 设置成从模式组合使用; RC 对于 RC0 中的计时器 r0,它将向RC1/2/3 中的计时器 r1/2/3 生成计数器清除信号;对于 RC1/2/3 中的时间 1/2/3,它可以选择独立工作或通过计数器获取清除两者来自时间 r0 的阈值或清除信号。
sdrv_adc_rc_entry_cfg_t 主要是设置 ADC 采样通道、模拟量采集设置、ADC RC 触发模式、ADC RC 重复采样次数;
typedef struct sdrv_adc_rc_entry_cfg {
uint32_t channel : 9; /**ADC 采样通道设置 */
uint32_t : 3;
uint32_t cfg_sel : 4; /**选择模拟量设置通道 */
uint32_t repeat_mode : 1; /**RC 硬件触发模式或软件触发模式 */
uint32_t : 7;
uint32_t repeat_cnt : 5; /**ADC 重复采集次数 */
} sdrv_adc_rc_entry_cfg_t;
在上述 sdrv_adc_ana_param_cfg_t 结构体中 sample time 参量主要设置 ADC 采样时间,而sdrv_adc_rc_cfg_t 结构体中 terminal compare 是采样触发的时间点,通过修改 Terminal 的数据更改 ADC 采样频率,RC entry 采样序列,RC 是否使能等设置。
typedef struct sdrv_adc_rc_cfg {
uint16_t terminal; /**< rc timer terminal value */
uint16_t compare; /**< rc timer compare value */
uint32_t q_cur : 4; /**rc_entry 当前位置*/
uint32_t q_start : 4; /**rc_entry 起始位置 */
uint32_t q_end : 4; /**rc_entry 终止位置 */
uint32_t : 1;
uint32_t tmr_mode : 1; /**RC模式:主模式、从模式 */
uint32_t trg_mode : 1; /**RC触发模式:软件触发、硬件触发 */
uint32_t : 2;
uint32_t trg_en : 1; /**< RC使能 */
} sdrv_adc_rc_cfg_t;
4. ADC模式设置
配置同步模式 slot 间隔最大值为 50clock ,其中根据 ADC 最大采样时间 34.5clock,ADC固定转化时间 13.5clock,再预留 2clock 时间间隔,将 RC entry 变量依次添加至 cid_buf 数组中,ADC 同步开启后将按照 cid_buf 数组的顺序存放 ADC 转化结果。
sdrv_adc_sync_cfg_t sync_cfg = {50, 0}; //max samctrl 5 (48 + 2);
uint8_t cid_buf[8];
/* RC0 repeat_cnt 总数为 2,需要占用 2 个 slot。 */
cid_buf[0] = SDRV_ADC_SYNC_CID_RC0;
cid_buf[1] = SDRV_ADC_SYNC_CID_RC0;
/* RC1 repeat_cnt 总数为 3,需要占用 3 个 slot。 */
cid_buf[2] = SDRV_ADC_SYNC_CID_RC1;
cid_buf[3] = SDRV_ADC_SYNC_CID_RC1;
cid_buf[4] = SDRV_ADC_SYNC_CID_RC1;
sdrv_adc_sync_cfg(ADC_DEV_TEST, sync_cfg, cid_buf, 5);
5. 中断设置
irq_initialize(VIC1_BASE, IRQ_MAX_INTR_NUM);
sdrv_adc_clear_int_status(ADC_DEV_TEST, 0xFFFFFFFFu);
/* 使能所有中断状态的记录 */
sdrv_adc_int_status_en_cfg(ADC_DEV_TEST, 0xFFFFFFFFu, 1);
/* 使能 FIFO 阈值中断的触发功能 */
sdrv_adc_int_status_sig_en_cfg(ADC_DEV_TEST, SADC_INT_STAT_SUB_FIFO3, 1);
/* 捕获 ADC2 中断,并设置中断回调函数 */
irq_attach(ADC_DEV_IRQ, adc_demo_int, (void *)ADC_DEV_TEST);
/* 中断使能 */
irq_enable(ADC_DEV_IRQ);
/* 中断禁止 */
irq_detach(ADC_DEV_IRQ);
6. ADC 频率计算
计算 ADC 采样频率需要精准的系统计数,需要初始化 ARM 性能监视器周期计数器,用于得到系统计数,通过 pmu_get_cycle_cntr() 得到系统计数周期,通过 sdrv_ckgen_bus_get_rate() 读取系统时钟频率, ADC 频率 = CNT(sample) * F(系统时钟) * (CYCLE_CNT(ADC采样后)- CYCLE_CNT(ADC采样前)) ;
//初始化 ARM 性能监视器周期计数器
sdrv_pmu_counter_init(); /* 用于 udelay 及获取时间*/
udelay(1);
cnt_per_us = sdrv_ckgen_bus_get_rate(CLK_NODE(g_ckgen_bus_cr5_sf),
CKGEN_BUS_CLK_OUT_M) / (1000 * 1000);
//读取 ADC 转化之前系统计数值
pmu_cnt_stamp[0] = pmu_get_cycle_cntr();
/* 开启 ADC 转化模式 */
//在 ADC 中断回调函数中读取最后一次 ADC 转化的系统计数值
static int adc_demo_int(uint32_t irq, void *arg)
{
uint32_t temp;
if (sdrv_adc_read_int_status(arg) & SADC_INT_STAT_SUB_FIFO3) {
while (!(sdrv_adc_fifo_status(arg) & SADC_SUB_FIFO_EMPTY)) {
temp = sdrv_adc_read_fifo(ADC_DEV_TEST); //读取 ADC 数值
adc_data_cnt++;
if (adc_data_cnt == ADC_DATA_CNT_FOR_SAMPLE_RATE)
{
pmu_cnt_stamp[1] = pmu_get_cycle_cntr(); //获取循环计数器的值
adc_data_sample_Finish_flag = 1;
}
}
sdrv_adc_clear_int_status(arg, SADC_INT_STAT_SUB_FIFO3);
}
return 0;
}
//ADC采样频率计算公式
printf("Sample rate %f Msps\n", ADC_DATA_CNT_FOR_SAMPLE_RATE * (float)cnt_per_us /
(pmu_cnt_stamp[1] - pmu_cnt_stamp[0]));
7. 结果验证
结果验证如下图:
图2 ADC 频率输出结果
三、 参考文档
《AppNote_E3_Boot_and_OTA_Rev01.05》
《AppNote_E3_烧录流程_Rev2.0》
《E3110_MCU_TRM_Rev00.13》
《E3110_MCU_Datasheet_Rev00.17》
欢迎在博文下方留言评论,我们会及时回复您的问题如有更多需求,欢迎联系大联大世平集团 ATU 部门:atu.sh@wpi-group.com
作者:Linna Wang /王丽娜
评论