#include "am_mcu_apollo.h" // Apollo MCU 外设寄存器定义和HAL库
#include "am_bsp.h" // 板级支持包(引脚定义、LED函数等)
#include "am_util.h" // 通用工具函数(如printf重定向)
//*****************************************************************************
//
// 全局变量
//
//*****************************************************************************
static void *g_ADCHandle; // ADC设备句柄,用于操作ADC实例
uint32_t g_ui32SampleCount; // ADC采样计数信号量(ISR与主程序同步)
uint16_t g_ui16ADCVDD_code; // 电压分压器的ADC原始码值(电池电压)
uint16_t g_ui16ADCTEMP_code; // 温度传感器的ADC原始码值
//*****************************************************************************
//
// ADC配置结构体
//
//*****************************************************************************
const static am_hal_adc_config_t g_sADC_Cfg =
{
.eClock = AM_HAL_ADC_CLKSEL_HFRC_DIV2, // ADC时钟源选择高频RC时钟分频2(约6MHz)
.ePolarity = AM_HAL_ADC_TRIGPOL_RISING,// 触发极性为上升沿
.eTrigger = AM_HAL_ADC_TRIGSEL_SOFTWARE, // 触发源为软件触发(实际由Timer3触发)
.eReference = AM_HAL_ADC_REFSEL_INT_1P5,// 内部1.5V参考电压
.eClockMode = AM_HAL_ADC_CLKMODE_LOW_POWER, // 低功耗时钟模式
.ePowerMode = AM_HAL_ADC_LPMODE1, // 空闲时进入低功耗模式1
.eRepeat = AM_HAL_ADC_REPEATING_SCAN // 重复扫描模式(循环采样)
};
//*****************************************************************************
//
// 定时器3配置结构体(用于触发ADC采样)
//
//*****************************************************************************
am_hal_ctimer_config_t g_sTimer3 =
{
0, // 不链接A和B定时器
// Timer3A配置为PWM重复模式,时钟源为32Hz低频RC(LFRC)
(AM_HAL_CTIMER_FN_PWM_REPEAT | AM_HAL_CTIMER_LFRC_32HZ),
0 // Timer3B未使用
};
//*****************************************************************************
//
// ADC中断服务程序(ISR)
//
//*****************************************************************************
void am_adc_isr(void)
{
uint32_t ui32IntStatus;
// 获取并清除ADC中断状态
am_hal_adc_interrupt_status(g_ADCHandle, &ui32IntStatus, true);
am_hal_adc_interrupt_clear(g_ADCHandle, ui32IntStatus);
am_devices_led_toggle(am_bsp_psLEDs, 3); // 切换LED3指示中断触发
// 从ADC FIFO读取所有样本
am_hal_adc_sample_t sSample;
uint32_t ui32NumSamples = 1;
while (AM_HAL_ADC_FIFO_COUNT(ADC->FIFO))
{
am_hal_adc_samples_read(g_ADCHandle, true, NULL, &ui32NumSamples, &sSample);
// 根据Slot区分采样来源
if (sSample.ui32Slot == 5) // Slot5配置为BATT通道(电池电压)
{
g_ui16ADCVDD_code = AM_HAL_ADC_FIFO_SAMPLE(sSample.ui32Sample);
}
else if (sSample.ui32Slot == 7) // Slot7配置为TEMP通道(温度传感器)
{
// 取高10位(精度配置为10bit),低6位舍去
g_ui16ADCTEMP_code = sSample.ui32Sample & 0xFFC0;
}
}
g_ui32SampleCount++; // 更新采样计数(通知主程序)
}
//*****************************************************************************
//
// ADC初始化函数
//
//*****************************************************************************
void adc_init(void)
{
am_hal_adc_slot_config_t sSlotCfg = {0};
// 初始化ADC实例并获取句柄
if (am_hal_adc_initialize(0, &g_ADCHandle) != AM_HAL_STATUS_SUCCESS) {
am_util_stdio_printf("ADC实例初始化失败!\n");
}
// 上电ADC模块
am_hal_adc_power_control(g_ADCHandle, AM_HAL_SYSCTRL_WAKE, false);
// 应用ADC全局配置
am_hal_adc_configure(g_ADCHandle, (am_hal_adc_config_t*)&g_sADC_Cfg);
// 配置各Slot参数(0-4、6未使用)
for (int i = 0; i <= 6; i++) {
if (i != 5) {
sSlotCfg.bEnabled = false;
am_hal_adc_configure_slot(g_ADCHandle, i, &sSlotCfg);
}
}
// 配置Slot5(BATT电压分压器)
sSlotCfg.bEnabled = true;
sSlotCfg.eChannel = AM_HAL_ADC_SLOT_CHSEL_BATT; // 选择BATT通道
sSlotCfg.ePrecisionMode = AM_HAL_ADC_SLOT_14BIT;// 14位精度模式
am_hal_adc_configure_slot(g_ADCHandle, 5, &sSlotCfg);
// 配置Slot7(温度传感器)
sSlotCfg.eChannel = AM_HAL_ADC_SLOT_CHSEL_TEMP; // 选择TEMP通道
sSlotCfg.ePrecisionMode = AM_HAL_ADC_SLOT_10BIT;// 10位精度模式
am_hal_adc_configure_slot(g_ADCHandle, 7, &sSlotCfg);
am_hal_adc_enable(g_ADCHandle); // 使能ADC模块
}
//*****************************************************************************
//
// 定时器初始化(配置Timer3A触发ADC采样)
//
//*****************************************************************************
static void timer_init(void)
{
#define TIMERNUM 3 // Apollo3中仅Timer3支持ADC触发
uint32_t ui32Period = 2000; // 2000ms周期(2秒)
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_LFRC_START, 0); // 启动LFRC(32Hz)
am_hal_ctimer_clear(TIMERNUM, AM_HAL_CTIMER_TIMERA); // 清除Timer3A
am_hal_ctimer_config(TIMERNUM, &g_sTimer3); // 应用定时器配置
// 计算比较寄存器值(32Hz时钟下,2000ms对应64个计数)
ui32Period = ui32Period * 32 / 1000;
am_hal_ctimer_period_set(TIMERNUM, AM_HAL_CTIMER_TIMERA,
ui32Period, (ui32Period >> 1));
am_hal_ctimer_adc_trigger_enable(); // 使能Timer3触发ADC
am_hal_ctimer_start(TIMERNUM, AM_HAL_CTIMER_TIMERA); // 启动Timer3A
}
//*****************************************************************************
//
// 主函数
//
//*****************************************************************************
int main(void)
{
// 初始化系统时钟、缓存、低功耗模式等
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
am_hal_cachectrl_enable();
am_bsp_low_power_init();
// 外设初始化
am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS);
am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON0, g_AM_BSP_GPIO_BUTTON0);
am_bsp_itm_printf_enable(); // 启用ITM调试输出
// ADC和定时器初始化
adc_init();
timer_init();
// 获取并打印温度校准参数(OTP中存储的工厂校准值)
float fTrims[4];
am_hal_adc_control(g_ADCHandle, AM_HAL_ADC_REQ_TEMP_TRIMS_GET, fTrims);
am_util_stdio_printf("校准温度: %.3f°C, 电压: %.3fV, 偏移: %.3f\n",
fTrims[0], fTrims[1], fTrims[2]);
// 中断配置
NVIC_EnableIRQ(ADC_IRQn);
am_hal_interrupt_master_enable();
am_hal_adc_interrupt_enable(g_ADCHandle, AM_HAL_ADC_INT_WCINC | AM_HAL_ADC_INT_FIFOOVR2);
g_ui32SampleCount = 0;
am_hal_adc_sw_trigger(g_ADCHandle); // 启动首次ADC转换
// 主循环
while (1)
{
if (g_ui32SampleCount > 0)
{
// 计算电池电压(基于分压比3:1)
float fVBATT = (g_ui16ADCVDD_code * 3.0f * 1.5f) / (1024.0f / 64.0f);
// 计算温度(通过HAL函数转换电压到摄氏度)
float fVT[3] = { (g_ui16ADCTEMP_code * 1.5f) / (1024.0f * 64.0f), 0, 0 };
am_hal_adc_control(g_ADCHandle, AM_HAL_ADC_REQ_TEMP_CELSIUS_GET, fVT);
float fTempC = fVT[1];
float fTempF = fTempC * 9.0f/5.0f + 32.0f; // 转换为华氏度
am_util_stdio_printf("VBatt: %.2fV, Temp: %.1f°C (%.1f°F)\n",
fVBATT, fTempC, fTempF);
g_ui32SampleCount = 0; // 重置采样计数
}
// 进入深度睡眠,等待下次中断
am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
}
}
关键注解说明:
-
ADC配置:
-
使用内部1.5V参考电压,适合测量小信号
-
重复扫描模式实现连续采样
-
Slot5和Slot7分别对应BATT和TEMP通道,精度不同
-
-
定时器触发:
-
Timer3A配置为32Hz LFRC时钟,产生2秒周期
-
比较寄存器设置占空比50%,确保稳定触发
-
-
中断处理:
-
每次ADC完成转换后触发中断
-
通过Slot编号区分不同通道数据
-
原始数据存储到全局变量供主程序处理
-
-
电压计算:
c
复制
fVBATT = (ADC代码 * 3.0 * 1.5V) / (1024/64)
基于分压电阻比3:1和14位ADC量程(1.5V参考)
-
温度计算:
-
使用HAL库函数
am_hal_adc_control
直接转换 -
公式依据Apollo3温度传感器特性曲线
-
-
低功耗设计:
-
主循环中进入DEEP SLEEP模式
-
ADC和Timer均使用低功耗时钟源
-
-
校准参数:
-
从芯片OTP中读取出厂校准值
-
提高温度测量精度至±2°C以内
-
此程序实现了Apollo3芯片的电池电压监测和温度检测功能,通过定时器触发ADC采样,在中断中获取数据,主程序进行物理量转换和显示,具有低功耗特性。