UART - DLPS
该示例演示系统支持 DLPS 时,UART 与 PC 终端进行数据通信。
当系统处于 IDLE 状态时,会自动进入 DLPS 状态。
PC 终端程序(如 PUTTY 或 UartAssist)发送数据,UART_RX_PIN 出现低电平会将系统从 DLPS 唤醒。 PC 终端程序再次发送数据,SoC 接收到数据,并将相同数据发回给 PC 终端,在 PC 终端观察收到相同数据。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
更多信息请参考 快速入门。
硬件连线
连接 P3_0(UART TX)和 FT232 的 RX,P3_1(UART RX)和 FT232 的 TX。
编译和下载
该示例的工程路径如下:
Project file: board\evb\io_sample\UART\DLPS\mdk
Project file: board\evb\io_sample\UART\DLPS\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
准备阶段
启动 PuTTY 或 UartAssist 等 PC 终端,连接到使用的 COM 端口,并进行以下 UART 设置:
波特率: 115200
8 数据位
1 停止位
无校验
无硬件流控
测试阶段
当 EVB 复位后,该示例开始发送
### Welcome to use UART demo ###\r\n
,观察 PC 终端上出现的字符串,随后系统进入 DLPS 状态,在 Debug Analyzer 内显示进入 DLPS 信息。enter dlps
PC 端发送任意数据,UART_RX_PIN 出现低电平会将系统从 DLPS 唤醒,在 Debug Analyzer 内显示退出 DLPS 信息。
exit dlps
PC 终端程序再次发送数据,SoC 接收到数据,并将相同数据发回给 PC 终端,在 PC 终端端观察收到相同数据。
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\board\evb\io_sample\UART\DLPS
源码路径:
sdk\src\sample\io_sample\UART\DLPS
该工程的工程文件代码结构如下:
└── Project: 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
├── peripheral includes all peripheral drivers and module code used by the application
├── rtl876x_rcc.c
├── rtl876x_pinmux.c
├── rtl876x_nvic.c
├── rtl876x_io_dlps.c
└── rtl876x_uart.c
├── profile
└── app includes the ble_peripheral user application implementation
├── main.c
├── ancs.c
├── app.c
├── app_task.c
└── io_uart.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_uart_init
,该函数为全局初始化,包含如下流程:将标志位
IO_UART_DLPS_Enter_Allowed
置为PM_CHECK_PASS
,表示可以进入 DLPS 状态。初始化 UART 接收计数器
UART_RX_Count
,初始化 UART 接收数组UART_RX_Buffer
。
void global_data_uart_init(void) { IO_UART_DLPS_Enter_Allowed = PM_CHECK_PASS; UART_RX_Count = 0; memset(UART_RX_Buffer, 0, sizeof(UART_RX_Buffer)); }
在
board_init
中,执行board_uart_init
,该函数为 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚,PINMUX 模式,PowerOn,内部上拉,输出失能。
配置 PINMUX:配置引脚分别为 UART0_TX 和 UART0_RX 功能。
在执行
os_sched_start()
开启任务调度后,在app_main_task
主任务内,执行driver_init
对外设驱动进行初始化配置。在
driver_init
中执行driver_uart_init
,该函数为 UART 外设的初始化,包含如下流程:使能 RCC 时钟。
默认配置 UART 的波特率为 115200。
配置 UART 接收中断
UART_INT_RD_AVA
和 UART 接收空闲中断UART_INT_RX_IDLE
。
void driver_uart_init(void) { RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE); /* uart init */ UART_InitTypeDef UART_InitStruct; UART_StructInit(&UART_InitStruct); UART_Init(UART0, &UART_InitStruct); //enable rx interrupt and line status interrupt UART_MaskINTConfig(UART0, UART_INT_RD_AVA, DISABLE); UART_MaskINTConfig(UART0, UART_INT_RX_IDLE, DISABLE); UART_INTConfig(UART0, UART_INT_RD_AVA, ENABLE); UART_INTConfig(UART0, UART_INT_RX_IDLE, ENABLE); /* Enable UART IRQ */ NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = UART0_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd = (FunctionalState)ENABLE; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_Init(&NVIC_InitStruct); }
执行
pwr_mgr_init
,该函数为 DLPS 的电压模式设置,包含如下流程:注册用户进入 DLPS 回调函数
app_enter_dlps_config
,注册用户退出 DLPS 回调函数app_exit_dlps_config
。在
app_enter_dlps_config
内执行io_uart_dlps_enter
函数,设置引脚为 SW 模式,设置 DLPS 唤醒方式。void io_uart_dlps_enter(void) { DBG_DIRECT("enter dlps"); /* Switch pad to Software mode */ Pad_ControlSelectValue(UART_TX_PIN, PAD_SW_MODE); Pad_ControlSelectValue(UART_RX_PIN, PAD_SW_MODE); System_WakeUpPinEnable(UART_RX_PIN, PAD_WAKEUP_POL_LOW, 0, 0); }
在
app_exit_dlps_config
内执行io_uart_dlps_exit
函数,设置引脚为 PINMUX 模式。void io_uart_dlps_exit(void) { /* Switch pad to Pinmux mode */ Pad_ControlSelectValue(UART_TX_PIN, PAD_PINMUX_MODE); Pad_ControlSelectValue(UART_RX_PIN, PAD_PINMUX_MODE); DBG_DIRECT("exit dlps"); }
注册硬件控制回调函数
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 }
功能实现
在
app_main_task
内,初始化 UART 外设后,发送字符串### Welcome to use UART demo ###\r\n
至 PC 端。void app_main_task(void *p_param) { ... driver_init(); /* Send demo string */ uint8_t demo_str_len = 0; char *demo_str = "### Welcome to use UART demo ###\r\n"; demo_str_len = strlen(demo_str); memcpy(String_Buf, demo_str, demo_str_len); uart_senddata_continuous(UART0, String_Buf, demo_str_len); ... }
当系统处于 IDLE 状态时,会自动进入 DLPS 状态。当 PC 端发送数据至 SoC 时,UART_RX_PIN 出现低电平,唤醒系统,退出 DLPS 状态。当系统被唤醒时,进入
System_Handler
。清除唤醒中断挂起位。
失能唤醒功能。
设置全局变量
IO_UART_DLPS_Enter_Allowed
为PM_CHECK_FAIL
,代表不可进入 DLPS 状态。
void System_Handler(void) { APP_PRINT_INFO0("[main] System_Handler"); if (System_WakeUpInterruptValue(UART_RX_PIN) == SET) { System_WakeUpPinDisable(UART_RX_PIN); Pad_ClearWakeupINTPendingBit(UART_RX_PIN); IO_UART_DLPS_Enter_Allowed = PM_CHECK_FAIL; } }
当 PC 端再次发送数据时,SoC 接收到数据,触发
UART_INT_RD_AVA
或UART_INT_RX_IDLE
中断,进入中断处理函数。在 UART 中断处理函数中,若 UART 正在接收数据会触发
UART_INT_RD_AVA
中断,处理流程如下:关闭
UART_INT_RD_AVA
中断。执行
UART_GetIID()
,获得中断标志 ID 类型。当 ID 为
UART_INT_ID_RX_LEVEL_REACH
(RX FIFO 数据长度达到 RX FIFO 阈值UART_RxThdLevel
)时,接收 FIFO 数据,保存到UART_RX_Buffer
中。当 ID 为
UART_INT_ID_RX_DATA_TIMEOUT
(RX FIFO 中至少有一个 UART 数据,并且不再有数据进来保持 4 个字节时间)时,接收 FIFO 数据,保存到UART_RX_Buffer
中。
开启
UART_INT_RD_AVA
中断。
void UART0_Handler() { uint16_t rx_len = 0; /* Read interrupt id */ uint32_t int_status = UART_GetIID(UART0); /* Disable interrupt */ UART_INTConfig(UART0, UART_INT_RD_AVA | UART_INT_RX_LINE_STS, DISABLE); ... switch (int_status & 0x0E) { /* Rx time out(0x0C). */ case UART_INT_ID_RX_DATA_TIMEOUT: rx_len = UART_GetRxFIFODataLen(UART0); UART_ReceiveData(UART0, &UART_RX_Buffer[UART_RX_Count], rx_len); UART_RX_Count += rx_len; break; /* Receive line status interrupt(0x06). */ case UART_INT_ID_LINE_STATUS: break; /* Rx data valiable(0x04). */ case UART_INT_ID_RX_LEVEL_REACH: rx_len = UART_GetRxFIFODataLen(UART0); UART_ReceiveData(UART0, &UART_RX_Buffer[UART_RX_Count], rx_len); UART_RX_Count += rx_len; break; /* Tx fifo empty(0x02), not enable. */ case UART_INT_ID_TX_EMPTY: /* Do nothing */ break; default: break; } /* enable interrupt again */ UART_INTConfig(UART0, UART_INT_RD_AVA, ENABLE); }
在 UART 中断处理函数中,UART 完成接收数据会触发
UART_FLAG_RX_IDLE
中断(读空 RX FIFO 数据后,在 RX 空闲超时时间内没有数据进入 RX FIFO),工作流程如下:失能
UART_INT_RX_IDLE
中断。定义消息类型,发送消息给 app task。主 task 检测到 UART 消息后,在
io_uart_handle_msg
内对消息进行处理。SoC 将收到的消息发回至 PC 端。
执行全局初始化函数,重置 UART 接收数组和接收数量。
等待 UART 发送数据完毕后,将
IO_UART_DLPS_Enter_Allowed
置为PM_CHECK_PASS
,代表可以进入 DLPS 状态。
清除接收 FIFO,重新使能
UART_INT_RX_IDLE
中断。退出中断处理函数后,系统重新进入 DLPS 状态。
void UART0_Handler() { ... if (UART_GetFlagStatus(UART0, UART_FLAG_RX_IDLE) == SET) { /* Clear flag */ UART_INTConfig(UART0, UART_INT_RX_IDLE, DISABLE); /* Send msg to app task */ T_IO_MSG int_uart_msg; int_uart_msg.type = IO_MSG_TYPE_UART; int_uart_msg.subtype = IO_MSG_UART_RX; UART_RX_Buffer[UART_RX_Count] = UART_RX_Count; int_uart_msg.u.buf = (void *)(&UART_RX_Buffer); APP_PRINT_INFO0("[io_uart] UART0_Handler: Send int_uart_msg"); if (false == app_send_msg_to_apptask(&int_uart_msg)) { APP_PRINT_INFO0("[io_uart] UART0_Handler: Send int_uart_msg failed!"); //Add user code here! return; } // IO_UART_DLPS_Enter_Allowed = PM_CHECK_PASS; UART_ClearRxFIFO(UART0); UART_INTConfig(UART0, UART_INT_RX_IDLE, ENABLE); } ... } void io_uart_handle_msg(T_IO_MSG *io_uart_msg) { // uint8_t *p_buf = io_uart_msg.u.buf; uint16_t subtype = io_uart_msg->subtype; if (IO_MSG_UART_RX == subtype) { uart_senddata_continuous(UART0, UART_RX_Buffer, UART_RX_Count); global_data_uart_init(); while (UART_GetFlagStatus(UART0, UART_FLAG_TX_FIFO_EMPTY) == 0) { IO_UART_DLPS_Enter_Allowed = PM_CHECK_PASS; } } }