ADC - DLPS
该示例演示系统支持 DLPS 时,使用 ADC 单次采样模式进行内部 VBAT 电压检测。
当系统处于 IDLE 状态时,会自动进入 DLPS 状态。当引脚 P4_0 输入电平为低电平时,会将系统从 DLPS 唤醒。
系统唤醒后以中断的方式检测内部电压 VBAT。当 ADC 采样完成之后触发 ADC_INT_ONE_SHOT_DONE
中断,在中断函数内读取 raw data 并进行电压转换计算。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
更多信息请参考 快速入门。
硬件连线
连接 P4_0 至外部信号输入。
编译和下载
该示例的工程路径如下:
Project file: board\evb\io_sample\ADC\DLPS\mdk
Project file: board\evb\io_sample\ADC\DLPS\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
当 EVB 复位后,先执行一次 ADC 采样。ADC 采样结束后,会在 Debug Analyzer 工具内打印采集得到的数据量和 raw data 值。
io_adc_handle_msg: len = xxx adc raw data = xxx ...
第一次采样结束后,系统进入 DLPS 状态。在 Debug Analyzer 工具内打印相应信息。
io_adc_dlps_enter
当 P4_0 检测到低电平输入时,系统被唤醒,退出 DLPS 状态。在 Debug Analyzer 工具内打印相应信息。
io_adc_dlps_exit
当 P4_0 检测到低电平输入时,系统被唤醒的同时会触发 GPIO 中断,在 Debug Analyzer 工具内打印相应信息。
GPIO_Input_Handler
GPIO 中断会触发 ADC 采样。ADC 采样结束后,会在 Debug Analyzer 工具内打印采集得到的数据量和 raw data 值。
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\board\evb\io_sample\ADC\DLPS
源码路径:
sdk\src\sample\io_sample\ADC\DLPS
该工程的工程文件代码结构如下:
└── Project: adc_dlps
└── secure_only_app
└── include
├── app_define.h
└── rom_uuid.h
├── cmsis includes CMSIS header files and startup files
├── overlay_mgr.c
├── system_rtl876x.c
└── startup_rtl876x.s
├── lib includes all binary symbol files that user application is built on
├── rtl8752h_sdk.lib
├── gap_utils.lib
├── ROM.lib
└── adc.lib
├── peripheral includes all peripheral drivers and module code used by the application
├── rtl876x_rcc.c
├── rtl876x_pinmux.c
├── rtl876x_nvic.c
└── rtl876x_adc.c
├── profile
└── app includes the ble_peripheral user application implementation
├── main.c
├── ancs.c
├── app.c
├── app_task.c
└── io_adc.c
初始化
当 EVB 复位启动时,调用 main()
函数,将执行以下流程:
int main(void)
{
extern uint32_t random_seed_value;
srand(random_seed_value);
global_data_init();
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_le_gap_init();
app_le_profile_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
备注
le_gap_init()
,gap_lib_init()
,app_le_gap_init
,app_le_profile_init
等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。
与外设相关的初始化流程具体如下:
在
global_data_init
中,执行global_data_adc_init
,该函数为全局初始化,包含如下流程:设置全局变量
IO_ADC_DLPS_Enter_Allowed
为PM_CHECK_PASS
,代表可以进入 DLPS 状态。初始化全局变量
ADC_Recv_Buffer
。
void global_data_adc_init(void) { IO_ADC_DLPS_Enter_Allowed = PM_CHECK_PASS; ADC_DATA_Length = 0; memset(ADC_DATA_Buffer, 0, sizeof(ADC_DATA_Buffer)); }
在
board_init
中,执行board_gpio_init
,该函数为 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚、 PINMUX 模式、 PowerOn、内部上拉、输出失能。
配置 PINMUX:分配引脚为 GPIO 功能。
在执行
os_sched_start()
开启任务调度后,在app_main_task
主任务内,执行driver_init
对外设驱动进行初始化配置。在
driver_init
中执行driver_adc_init
,该函数为 ADC 外设的初始化,包含如下流程:使能 RCC 时钟。
配置 ADC 的采样通道,配置通道 0 为 VBAT 电压模式,设置 Bitmap 为 0x01。
配置 ADC
ADC_INT_ONE_SHOT_DONE
中断。
void driver_adc_init(void) { RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); ADC_InitTypeDef ADC_InitStruct; ADC_StructInit(&ADC_InitStruct); ADC_InitStruct.ADC_SampleTime = 255; /* (n + 1) cycle of 10MHz,n = 0~255 or n = 2048~14591 */ ADC_InitStruct.ADC_SchIndex[0] = INTERNAL_VBAT_MODE; ADC_InitStruct.ADC_Bitmap = 0x01; ADC_InitStruct.ADC_PowerAlwaysOnEn = ADC_POWER_ALWAYS_ON_ENABLE; ADC_Init(ADC, &ADC_InitStruct); ADC_INTConfig(ADC, ADC_INT_ONE_SHOT_DONE, ENABLE); NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); }
在
driver_init
中执行driver_gpio_init
,该函数为 GPIO 外设的初始化,包含如下流程:使能 RCC 时钟。
配置 GPIO 引脚,模式为输入模式。
配置 GPIO 中断使能,触发模式与极性为下降沿触发。
使能 GPIO 去抖功能,设置去抖时间。
配置 GPIO 中断。
void driver_gpio_init(void) { /* Initialize GPIO peripheral */ RCC_PeriphClockCmd(APBPeriph_GPIO, APBPeriph_GPIO_CLOCK, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_PIN_INPUT; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStruct.GPIO_ITCmd = ENABLE; GPIO_InitStruct.GPIO_ITTrigger = GPIO_INT_Trigger_EDGE; GPIO_InitStruct.GPIO_ITPolarity = GPIO_INT_POLARITY_ACTIVE_LOW; GPIO_InitStruct.GPIO_ITDebounce = GPIO_INT_DEBOUNCE_ENABLE; GPIO_InitStruct.GPIO_DebounceTime = 64;/* unit:ms , can be 1~64 ms */ GPIO_Init(&GPIO_InitStruct); NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = GPIO_PIN_INPUT_IRQN; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); GPIO_MaskINTConfig(GPIO_PIN_INPUT, DISABLE); GPIO_INTConfig(GPIO_PIN_INPUT, ENABLE); }
执行
pwr_mgr_init
,该函数为 DLPS 的电压模式设置,包含如下流程:注册用户进入 DLPS 回调函数
app_enter_dlps_config
,注册用户退出 DLPS 回调函数app_exit_dlps_config
。在
app_enter_dlps_config
内执行io_adc_dlps_enter
函数,设置引脚为 SW 模式,设置 DLPS 唤醒方式。void io_adc_dlps_enter(void) { /* Switch pad to Software mode */ Pad_ControlSelectValue(ADC_DLPS_WAKEUP_PIN, PAD_SW_MODE); System_WakeUpPinEnable(ADC_DLPS_WAKEUP_PIN, PAD_WAKEUP_POL_LOW, 0, 0); DBG_DIRECT("io_adc_dlps_enter"); }
在
app_exit_dlps_config
内执行io_adc_dlps_exit
函数,设置引脚为 PINMUX 模式。void io_adc_dlps_exit(void) { /* Switch pad to Pinmux mode */ Pad_ControlSelectValue(ADC_DLPS_WAKEUP_PIN, PAD_PINMUX_MODE); DBG_DIRECT("io_adc_dlps_exit"); }
注册硬件控制回调函数
DLPS_IO_EnterDlpsCb
和DLPS_IO_ExitDlpsCb
,进入 DLPS 会保存 CPU、PINMUX、Peripheral 等,退出 DLPS 会恢复 CPU、PINMUX、Peripheral 等。设置电源模式为 DLPS 模式。
设置唤醒 DLPS 方式为 ADC_DLPS_WAKEUP_PIN 低电平唤醒。
void pwr_mgr_init(void) { #if DLPS_EN if (false == dlps_check_cb_reg(app_dlps_check_cb)) { APP_PRINT_ERROR0("Error: dlps_check_cb_reg(app_dlps_check_cb) failed!"); } DLPS_IORegUserDlpsEnterCb(app_enter_dlps_config); DLPS_IORegUserDlpsExitCb(app_exit_dlps_config); DLPS_IORegister(); lps_mode_set(PLATFORM_DLPS_PFM); /* Config WakeUp pin */ System_WakeUpPinEnable(ADC_DLPS_WAKEUP_PIN, PAD_WAKEUP_POL_LOW, 0, 0); #else lps_mode_set(LPM_ACTIVE_MODE); #endif }
功能实现
执行
os_sched_start()
,开启任务调度。当 stack 准备好时,执行app_handle_dev_state_evt
,执行adc_sample_start
,执行ADC_Cmd()
,开始 ADC 采样。void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause) { ... if (gap_dev_state.gap_init_state != new_state.gap_init_state) { if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY) { APP_PRINT_INFO0("GAP stack ready"); /*stack ready*/ adc_sample_start(); } } ... } void adc_sample_start(void) { /* Enable adc sample */ ADC_Cmd(ADC, ADC_ONE_SHOT_MODE, ENABLE); }
当 ADC 完成单次采样时,触发
ADC_INT_ONE_SHOT_DONE
中断,进入中断处理函数ADC_Handler
。读取 ADC 采样值。
定义消息类型
IO_MSG_TYPE_ADC
,保存采集数据至全局变量,执行app_send_msg_to_apptask
发送 msg 给 task。
void ADC_Handler(void) { uint16_t sample_data[32]; if (ADC_GetINTStatus(ADC, ADC_INT_ONE_SHOT_DONE) == SET) { ADC_ClearINTPendingBit(ADC, ADC_INT_ONE_SHOT_DONE); /* Send msg to app task */ ADC_DATA_Length = 1; sample_data[0] = ADC_ReadRawData(ADC, ADC_Schedule_Index_0); ADC_DATA_Buffer[0] = sample_data[0]; T_IO_MSG int_adc_msg; int_adc_msg.type = IO_MSG_TYPE_ADC; int_adc_msg.u.buf = (void *)(&ADC_DATA_Buffer); if (false == app_send_msg_to_apptask(&int_adc_msg)) { APP_PRINT_ERROR0("[io_adc] ADC_Handler: Send int_adc_msg failed!"); //Add user code here! return; } } }
在
app_main_task
中循环检测消息队列。当检测到 msg 时,执行app_handle_io_msg
函数对 msg 进行处理。在
app_handle_io_msg
函数中,若判断消息类型为IO_MSG_TYPE_ADC
,执行io_handle_adc_msg
函数,执行io_adc_handle_msg
。从 msg 中取出采样数据。
打印 ADC 数据长度和数据值。
设置全局变量
IO_ADC_DLPS_Enter_Allowed
为PM_CHECK_PASS
,代表可以进入 DLPS 状态。
void io_adc_handle_msg(T_IO_MSG *io_adc_msg) { uint16_t *p_buf = io_adc_msg->u.buf; uint16_t type = io_adc_msg->type; if (IO_MSG_TYPE_ADC == type) { DBG_DIRECT("io_adc_handle_msg: len = %d", ADC_DATA_Length); for (uint8_t i = 0; i < ADC_DATA_Length; i++) { DBG_DIRECT("adc raw data = %d", p_buf[i]); } platform_delay_ms(1000); IO_ADC_DLPS_Enter_Allowed = PM_CHECK_PASS; } }
当引脚 P4_0 输入低电平时,系统退出 DLPS 状态,当系统被唤醒时,进入
System_Handler
。清除 P4_0 的唤醒中断挂起位。
失能 P4_0 的唤醒功能。
设置全局变量
IO_ADC_DLPS_Enter_Allowed
为PM_CHECK_FAIL
,代表不可进入 DLPS 状态。
void System_Handler(void) { if (System_WakeUpInterruptValue(ADC_DLPS_WAKEUP_PIN) == SET) { APP_PRINT_INFO0("System_Handler"); Pad_ClearWakeupINTPendingBit(ADC_DLPS_WAKEUP_PIN); System_WakeUpPinDisable(ADC_DLPS_WAKEUP_PIN); IO_ADC_DLPS_Enter_Allowed = PM_CHECK_FAIL; } }
当引脚 P4_0 输入低电平时,触发
GPIO_PIN_INPUT
中断,进入中断处理函数GPIO_Input_Handler
。关 GPIO 中断,屏蔽 GPIO 中断。
定义消息类型
IO_MSG_TYPE_GPIO
,执行app_send_msg_to_apptask
发送 msg 给 task。清除 GPIO 中断挂起位,取消屏蔽 GPIO 中断,使能 GPIO 中断。
在
app_handle_io_msg
函数中,若判断消息类型为IO_MSG_TYPE_GPIO
,执行io_adc_sample_start
,开始 ADC 采样。
void GPIO_Input_Handler(void) { DBG_DIRECT("GPIO_Input_Handler"); GPIO_INTConfig(GPIO_PIN_INPUT, DISABLE); GPIO_MaskINTConfig(GPIO_PIN_INPUT, ENABLE); T_IO_MSG int_gpio_msg; int_gpio_msg.type = IO_MSG_TYPE_GPIO; int_gpio_msg.subtype = 0; if (false == app_send_msg_to_apptask(&int_gpio_msg)) { APP_PRINT_ERROR0("[io_gpio] GPIO_Input_Handler: Send int_gpio_msg failed!"); //Add user code here! GPIO_ClearINTPendingBit(GPIO_PIN_INPUT); return; } GPIO_ClearINTPendingBit(GPIO_PIN_INPUT); GPIO_MaskINTConfig(GPIO_PIN_INPUT, DISABLE); GPIO_INTConfig(GPIO_PIN_INPUT, ENABLE); }
ADC 采样完成后,系统会再次进入 DLPS 状态,循环重复上述过程。