外挂MCU以及SOC 上下电方案
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 06/07/2024 |
1 概述¶
本文档用于说明在SOC未下电完成而上电事件触发导致的系统异常问题以及解决方案。主要介绍 SOC上下电流程、问题产生原因以及解决方案。
2 SOC 上下电流程¶
- SOC suspend 状态:此时 SOC 处于下电状态,RTC 事件可正常将SOC 唤醒
- SOC 上电流程:该阶段为 SOC 硬件上电流程
- SOC resume流程:该阶段为 SOC 软件 resume 流程
- SOC alive 状态:SOC 正常工作阶段
- SOC suspend 流程:该阶段为软件 suspend 流程
- SOC 下电流程:该阶段为 SOC 硬件下电流程,该阶段结束RTC 事件可正常将SOC 唤醒
3 存在问题¶
- SOC 对于电源上电有严格的时序要求,当 RTCIO0/1 事件在SOC 处于硬件下电流程时(图1阶段6)触发,会导致SOC 并未完全下电而又要走上电流程,最终导致上电时序异常。
- 由于RTC wakeup 节点只会在 RTCIO4 被拉高之后才会被更新,因此在 RTCIO4 为 HIGH 的阶段(阶段⅔/5)触发的 RTCIO0/1 事件就无法被AOV demo 获取到,导致 RTCIO0/1(pir/wifi) 事件会丢失。
- RTC 每天 8万余次,是否可靠,如何防呆问题
4 解决方案¶
4.1 方案一(推荐方案)¶
- 3.3V_GPIO:从SSC30XXE 芯片端任意选择一个3.3V且默认内部PU的GPIO
- PQ1: 增强型NMOS,建议VthGS越小越好
- MCU_INT: MCU外部事件中断输出脚
- RTC_IO1:自带按键防抖功能,有8ms delay才会拉高RTC_IO4
- R3: 该电阻值根据MCU IO 输出特性自行调整阻值
基本原理:Vg大于VthGS 则NMOS 保持导通,RTC_IO1保持低电平相当于会屏蔽掉MCU_INT 的信号,Vg 小于VthGS则NMOS保持关断,RTC_IO1相当于直连MCU_INT,可以正常接收MCU中断,无论何时MCU_INT 拉高都只能在此阶段被 RTC_IO1 响应,响应8ms之后,内部POC模块才会拉高RTC_IO4,硬件上有8ms给系统更多掉电时间,同时也能保证二次上电时序正常。
将 RTC IO0 的输入源由 RTC ALARM 修改为 MCU timer ,将RTC_IO1 的输入源按如下修改
原理:在gpio2 电平为 HIGH 时,根据 CMOS 原理,即使此时 MCU 拉高RTC_IO1,此时 SOC 也无法响应RTC_IO1上升沿中断,根据CMOS 管特性(导通电压是0.7v),图中的gpio2 下降沿时的电压为 0.7v,此外由于 RTCIO1 的防抖措施(拉高RTCIO1 8ms 之后才会拉高RTCIO4 )的存在,在 gpio 2下降沿后拉高 RTCIO1 一定是安全的。gpio2 上升沿事件是 SOC 软件 resume 时将其拉高,gpio2 下降沿事件是 SOC 硬件下电流程中 GPIO 电源域的电压拉低触发。
具体实现 flow :
-
MCU 通过timer 唤醒 SOC,MCU alive,在此后任意阶段,若发生 Pir & WiFi 事件,MCU 将 gpio1 举高,然后直接拉高 RTCIO1 即可
-
SOC 上电启动到阶段 2 到 gpio2 上升沿时,MCU 拉高 RTCIO1,此时由于gpio2 为 LOW ,SOC 虽然会响应,但是由于RTCIO4已经为高,SOC 无事发生
-
SOC 启动处于 gpio2 上升沿 至 gpio2 下降沿阶段内时,gpio2 为 HIGH,由于 CMOS 的原因,即使 MCU 拉高 RTCIO1,SOC 看到的 RTCIO1依旧为低
SOC 启动到阶段 4,通过查询 gpio1 电平状态确认是否有 Pir & WiFi 事件发生,若有则切换高帧模式,然后修改 gpio3 状态(比如连续多个上升沿)告知 MCU 已经查询过事件,MCU 获取gpio3状态后,将gpio 1 拉低
此后 SOC 在阶段4会持续 polling MCU gpio1 电平状态以及时响应阶段 4 发生的 Pir & WiFi 事件
-
SOC 启动到 gpio2 下降沿到 阶段1,此时gpio2 为 LOW ,MCU 拉高 RTCIO1 后 RTCIO1会在 8ms 之后才会真正将RTCIO4拉高(8ms 足够让gpio2 由0.7v 掉电到零),这时 SOC 会被直接唤醒。
注意:
1、若有多个外部唤醒事件的话,则 gpio1 的功能需要多根 gpio 实现
2、使用 gpio3 告知 MCU 状态已经被获取的方式,也可以由业务逻辑实现,此时只是用 gpio 举例而已
4.2 方案二¶
修改 RTCPOC 模块的输入信号源为下图所示
将 RTC IO0 的输入源由 RTC ALARM 修改为 MCU timer ,将RTC_IO1 的输入源 修改为 Pir & WiFi。
原理:MCU 通过 timer 唤醒 SOC 时,在 SOC 处于 2、3、4、5、6 阶段时,MCU 同样处于 ALIVE 状态,此时加上 1fps 的控制源修改,MCU 可以清楚地知道 SOC 的上下电时间点,就可以将 图4 的阶段 2、3、5、6 发生的 Pir & WiFi事件保存在 MCU 内部,在阶段 4 通过串口将事件状态发送给 SOC 。
具体实现 flow:
-
MCU 通过timer 唤醒 SOC,然后等待SOC通过串口发送的 suspend 信号或者是 询问信号,
suspend 信号:表示 SOC 开始下电
询问信号:询问 MCU 在 2、3、5、6 阶段是否发生 Pir & WiFi 事件,事件发生意味着 SOC 进入阶段 4
-
SOC 上电启动到阶段 2、3,此时若发生 Pir & WiFi 事件,MCU 保留事件发生状态,不去触发 RTCIO0/1事件
-
SOC 启动到阶段 4,通过串口发送问询信号给 MCU,MCU 通过串口回复有或者没有,若此时接收到了 MCU 有 Pir & WiFi 事件发生,那切换高帧模式
此后 SOC 在阶段4会持续等待 MCU 串口的发送信息,等待接收 MCU 串口的发送信息是为了响应阶段 4 发生的 Pir & WiFi 事件,当阶段 4 发生 Pir & WiFi 事件,直接通过串口告诉 SOC 即可。
SOC 阶段4进入 STR 之前,通过串口告诉 MCU 准备下电
-
SOC 启动到阶段 5、6,此时若发生 Pir & WiFi 事件,MCU 保留事件发生状态,不去触发 RTCIO0/1事件,阶段 6 SOC 硬件下电流程中 GPIO 电源域的电压拉低之后,GPIO 会被拉低,此时 MCU 会收到一个下降沿中断
-
MCU 接收到 下降沿中断后,延时一个 t 的时间等到 SOC 完全下电进入阶段 1
MCU 下电完成后,若在 SOC 阶段 5、6 有 Pir & WiFi 事件触发就立刻唤醒 SOC。回到步骤2
让 MCU 能够知道当前 SOC 下电完成时间节点,在下电之后再去响应 pir/wifi 事件唤醒SOC 即可。
t = 𝒉𝒘_𝒑𝒐𝒘𝒆𝒓𝒅𝒐𝒘𝒏 𝒉𝒘_𝒑𝒐𝒘𝒆𝒓𝒅𝒐𝒘𝒏:硬件开始掉电(RTC_IO4 为0)直到到板子完全掉电时间
4.3 RTC 如何防呆问题¶
方案1:基于上面的功能实现,可直接将问询信号或者 GPIO 下降沿中断作为 SOC 心跳包使用,在规定时间内没有接到 SOC 的心跳包,则认为 SOC 出现异常,
同理将 MCU 对问询信号的回复信息作为 MCU 的心跳包,在规定时间内没有接到 MCU 的心跳包,则认为 MCU 出现异常。
5 伪代码¶
5.1 SOC AOV demo 部分¶
init 阶段:pull up 指定的GPIO 引脚 // 创建新线程等待接收 MCU 串口信息 creat_new_thred( func) // 创建新线程检测心跳包 creat_new_thred( heart_beat) uart_recive_count=0 处理阶段: uart_trans( Querying ) uart_recive(event_flag) uart_recive_count++; if(event_flag==TRUE) { //切换高帧 . . . } else { //维持现状 . . . } // SOC ALIVE 处理完成 uart_trans( suspend ) //进入 suspend heart_beat() { temp_count = uart_recive_count sleep( 500ms ) if(uart_recive_count<=temp_count) { // MCU 异常 } } //线程处理函数 func func() { uart_recive() if(MCU 有事件触发) { //检查当前模式 if(mode=低帧) { //切换到高帧 }else{ sleep() } }else{ sleep() } }
5.2 MCU demo 部分¶
init 阶段: // 创建新线程等待接收 SOC 串口信息 creat_new_thred( func) // 创建新线程检测心跳包 creat_new_thred( heart_beat) Querying=false suspend=false uart_recive_count=0 处理阶段: send_pir_event_flag=true if(PIR || WIFI) { if(Querying=false ) { // 保存事件到变量 save_event() } else if(Querying=true&& suspend=false) { uart_trans(true) } else if(Querying=true&& suspend=true) { // 保存事件到变量 save_event() sleep( t ) pull_up(RTC_IO1) } } heart_beat() { temp_count = uart_recive_count sleep( 500ms ) if(uart_recive_count<=temp_count) { // MCU 异常 } } //线程处理函数 func func() { while(uart_recive()) { if(Querying) { uart_recive_count++ if(PIR || WIFI) { uart_trans(true) }else { uart_trans(false) } Querying=true }else if(suspend) { // 此时开始不要响应 IO0/1 事件 suspend=true break; } } }