前言现在为止也开发了许多杰理TWS蓝牙耳机、音响项目SDK的案子在调试案子时不断的向前辈们学习到了很多关于蓝牙音响、蓝牙TWS耳机专业的知识。想在这里做一个学习汇总方便各位同行和对杰理芯片SDK感兴趣的小伙伴们学习本章详细讲解杰理可视化SDK开发-NTC温度保护功能中硬件原理图和软件配置教程我们在调试一个TWS蓝牙耳机项目时遇到高端的TWS蓝牙耳机项目客户需求一般都是带有NTC温度保护功能。特别是出口到欧美国家的耳机产品都会要求必带NTC温度保护功能用于过认证本章将围绕杰理官方原理图中的NTC温度保护功能进行详细的分析和讲解1、NTC温度保护原理图电路2、NTC温度保护工作原理3、软件实现温度保护功能逻辑一、NTC温度保护原理图电路如图是杰理标准NTC电路原理图这是TWS 蓝牙耳机中最经典的外部上拉 NTC 接地的串联分压测温电路属于锂电池充电安全的标准硬件设计整体结构非常简洁1、核心元器件与连接关系R4100KΩ 固定电阻上拉分压电阻上端接NTC_VDD供电下端接分压节点NTC_DER5100K/4250 NTC 热敏电阻负温度系数热敏电阻上端接NTC_DET下端直接接地100K标称阻值指 25℃环境下 NTC 的阻值为 100KΩ4250B 值材料常数描述 NTC 阻值随温度变化的灵敏度NTC_DET 节点分压输出点直接连接到芯片的 ADC 采样引脚将模拟电压送入芯片模数转换NTC_VDD 节点分压电路的供电端由芯片的一个 GPIO 引脚控制通断实现低功耗控制2、芯片引脚对应从原理图芯片引脚可见26 脚ADC2/PLINK_DAT0/PA6对应代码中的NTC_POWER_IO作为供电控制 IO输出高电平时给 NTC 电路供电27 脚ADC1/PLINK_SCLK/PA5对应代码中的NTC_DETECT_IO作为 ADC 模拟输入引脚采集分压电压二、NTC温度保护核心测温原理整个电路的工作基于两个基础原理串联分压定律 NTC 负温度系数特性。1、NTC负温度系数特性NTCNegative Temperature Coefficient热敏电阻的核心特性温度升高 → 半导体载流子浓度升高 → 电阻阻值下降​​​​​​ ​温度降低 → 电阻阻值上升阻值与温度呈非线性负相关B 值越大温度变化时阻值变化越明显对应本电路低温0℃NTC 阻值大 → 分压占比高 →NTC_DET电压高 → ADC 采样值大高温45℃NTC 阻值小 → 分压占比低 →NTC_DET电压低 → ADC 采样值小2、串联分压的定量计算根据串联分压公式NTC_DET节点的电压为Vdet​ Vdd​×Rntc​/(R4Rntc)​​芯片ADC为10位精度满量程对应供电电压Vdd,量化范围0~1023因此最终读到的AD值为ADualue 1023 ×Rntc​/(R4Rntc)​​这和代码中ntc_get_detect_upper、ntc_get_detect_lower的计算公式完全一致是整个模块的核心数学依据。三、软件实现温度保护功能逻辑上拉电阻方案选择代码中通过NTC_DET_PULLUP_TYPE切换两种上拉方案对应两种硬件设计两者的区别和适用场景如下1. 外部上拉方案当前原理图采用实现方式在 PCB 板上贴装独立的 100K 固定电阻R4由芯片 GPIO 输出供电优势1、精度高贴片电阻的阻值误差、温漂远优于芯片内部集成电阻测温更准确2、可控性强可以通过 GPIO 完全断电零功耗待机3、灵活性高可根据 NTC 型号灵活调整上拉电阻阻值劣势增加 1 颗物料BOM 成本略高占用 1 个供电 IO2. 内部上拉方案代码兼容的替代方案实现方式不贴外部 R4 电阻直接利用芯片 GPIO 内部集成的 100K 上拉电阻NTC 一端接 GPIO、一端接地优势省掉外部电阻降低 BOM 成本节省 1 个供电 IO劣势1、芯片内部电阻工艺偏差大因此代码中加入了efuse出厂校准机制PULLUPRES_IS_TRIM等宏通过熔丝存储的校准值修正个体误差2、温漂、精度整体弱于外部高精度电阻代码对应配置 GPIO 为PORT_INPUT_PULLUP_100K模式无需单独供电 IO硬件IO与代码配置逻辑原理图节点代码宏定义代码中的配置逻辑NTC_VDDNTC_POWER_IO启动检测时设为推挽输出高电平给电路供电停止时设为高阻切断供电省电NTC_DETNTC_DETECT_IO设为高阻模拟输入模式避免 GPIO 内阻影响分压精度接入 ADC 通道阻值与阈值对应填入外部上拉 R4100K → 代码中默认pull_up_res 100000NTC 的 0℃阻值 → 传入ntc_get_detect_upper计算出 AD 上限NTC_DET_UPPERNTC 的 45℃阻值 → 传入ntc_get_detect_lower计算出 AD 下限NTC_DET_LOWER故障检测的硬件对应代码中NTC_IS_BAD_RES(value)判断 AD 值≥1020 或≤5完全对应硬件故障AD 值接近 1023满量程NTC 电阻开路 / 脱落分压接近供电电压AD 值接近 0NTC 电阻短路分压接近地电位低功耗设计对应原理图采用可控供电设计和代码逻辑完全匹配充电时启动检测供电 IO 输出高电路工作充电停止后供电 IO 设为高阻NTC 电路完全断电无静态功耗适配 TWS 耳机的低功耗需求代码讲解//*********************************************************************************// // LYF_USER NTC温度保护实现功能 // //*********************************************************************************// //【NTC检测总开关】 #define NTC_DET_EN 1 //必须同时注释掉board_ac700n_demo_cfg.h里的NTC_DET_EN定义避免产生冲突 #if NTC_DET_EN //【NTC检测引脚】 #define NTC_DETECT_IO IO_PORTA_05 // 检测IO选择 //【NTC内部/外部上拉电阻选择】 #define NTC_DET_PULLUP_TYPE 0 // 上拉电阻: 0外部上拉,1内部上拉 //【NTC供电引脚】 #define NTC_POWER_IO IO_PORTA_06 // 供电脚位选择 //【NTC阻值上限】 #define NTC_UPPER_RES 332150 // NTC阻值上限 0度时阻值(根据RT表填入) //【NTC阻值下限】 #define NTC_LOWER_RES 43530 // NTC阻值下限 45度时阻值(根据RT表填入) #endif #ifdef SUPPORT_MS_EXTENSIONS #pragma bss_seg(.ntc_det.data.bss) #pragma data_seg(.ntc_det.data) #pragma const_seg(.ntc_det.text.const) #pragma code_seg(.ntc_det.text) #endif //*********************************************************************************// // NTC det // //*********************************************************************************// #include app_config.h // 应用总配置头文件 #include ntc_det_api.h // 本模块NTC对外接口头文件 #include system/includes.h // 系统通用头文件 #include asm/charge.h // 充电驱动头文件 #include asm/efuse.h // 芯片熔丝驱动头文件存储校准参数 #include gpadc.h // 通用ADC驱动头文件 #include app_msg.h // 系统消息机制头文件 #if NTC_DET_EN #define NTC_DET_BAD_RES 0 //分压电阻损坏关闭检测 0关闭1使能后检测到硬件故障自动停止检测 #ifndef NTC_DET_DUTY1 #define NTC_DET_DUTY1 5000 // 大检测周期单位ms正常状态下的轮询间隔兼顾功耗 #endif #ifndef NTC_DET_DUTY2 #define NTC_DET_DUTY2 10 // 小检测周期单位ms连续采样时的间隔用于防抖采样 #endif #ifndef NTC_DET_CNT #define NTC_DET_CNT 3 // 单轮采样次数多次采样表决防止单次干扰误触发 #endif #define NTC_DET_AD_CH ntc_get_detect_ch() // 重定义ADC通道通过IO口动态映射通道号比硬编码兼容性更强 #ifndef NTC_DET_UPPER #define NTC_DET_UPPER ntc_get_detect_upper(NTC_UPPER_RES) //正常范围AD值上限0度时 #endif #ifndef NTC_DET_LOWER #define NTC_DET_LOWER ntc_get_detect_lower(NTC_LOWER_RES) //正常范围AD值下限45度时 #endif /** * brief 判断AD值是否在正常温度范围 * param value 当前ADC采样值 * param offset 滞回偏移量异常状态下偏移更大防止临界抖动 * return 1正常0异常 */ #define NTC_IS_NORMAL(value, offset) (value NTC_DET_LOWER(offset) value NTC_DET_UPPER-(offset)) /** * brief 判断分压电阻是否损坏开路/短路 * param value 当前ADC采样值 * return 1损坏0正常 * note 接近满量程1020为开路接近0为短路 */ #define NTC_IS_BAD_RES(value) (value 1020 || value 5) // 读取efuse判断内部100K上拉电阻是否做过出厂校准0xFF表示未校准 #define PULLUPRES_IS_TRIM (efuse_get_io_pu_100k() ! 0xFF) // 读取校准值符号位最高位为1表示负校准为0表示正校准 #define GET_PULLUPRES_SIGN ((efuse_get_io_pu_100k() 0x80) ? -1 : 1) // 读取校准值数值位低7位为校准步长 #define GET_PULLUPRES_TRIM_SIZE (efuse_get_io_pu_100k() 0x7F) // NTC检测状态枚举 enum { NTC_STATE_NORMAL 0, // 温度正常状态 NTC_STATE_ABNORMAL, // 温度异常状态 }; /** * brief NTC检测状态机结构体位域设计节省RAM */ struct ntc_det_t { u16 normal_cnt : 4; //温度正常的次数 u16 cnt : 4; //温度检测的次数 u16 res_cnt : 4; //分压电阻脱落或损坏 u16 state : 1; //是否超出范围 u16 timer; }; static struct ntc_det_t ntc_det {0}; // NTC检测全局状态实例静态初始化 extern u8 get_charge_full_flag(void); // 外部声明获取充电充满标志 /** * brief 查询NTC检测是否正在运行 * return 非0运行中0已停止 */ u16 ntc_det_working() { return ntc_det.timer; } /* ***************************************************************************/ /** * \Brief : 获取内部上拉电阻校准阻值 * * \Return : 内部上拉电阻校准值 */ /* ***************************************************************************/ u32 ntc_get_inside_pull_up_res_trim_value(void) { // 杰理原厂校准公式将熔丝trim值换算为实际电阻值 u32 res_value (u32)((3072 - (524 GET_PULLUPRES_TRIM_SIZE * GET_PULLUPRES_SIGN) * 3) / 0.015f); return res_value; } /* ***************************************************************************/ /** * \Brief : 获取NTC检测ADC通道 * * \Return : NTC检测通道 */ /* ***************************************************************************/ u32 ntc_get_detect_ch(void) { return adc_io2ch(NTC_DETECT_IO); } /* ***************************************************************************/ /** * \Brief : 获取正常温度范围AD值下限 * * \Param : ntc_res:0°时NTC的阻值 * * \Return : 根据上拉电阻计算得到的0°时的采样值 */ /* ***************************************************************************/ u32 ntc_get_detect_upper(u32 ntc_res) { u32 pull_up_res; u32 upper_value; // 选择上拉电阻阻值内部校准方案用校准值否则用默认100K if (NTC_DET_PULLUP_TYPE PULLUPRES_IS_TRIM) { pull_up_res ntc_get_inside_pull_up_res_trim_value(); } else { pull_up_res 100000; } // 10位ADC分压公式AD值 满量程1023 * NTC阻值 / (NTC阻值 上拉阻值) upper_value 1023 * ntc_res / (ntc_res pull_up_res); return upper_value; } /* ***************************************************************************/ /** * \Brief : 获取正常温度范围AD值下限 * * \Param : ntc_res:45°时NTC的阻值 * * \Return : 根据上拉电阻计算得到的45°时的采样值 */ /* ***************************************************************************/ u32 ntc_get_detect_lower(u32 ntc_res) { u32 pull_up_res; u32 lower_value; if (NTC_DET_PULLUP_TYPE PULLUPRES_IS_TRIM) { pull_up_res ntc_get_inside_pull_up_res_trim_value(); } else { pull_up_res 100000; } lower_value 1023 * ntc_res / (ntc_res pull_up_res); return lower_value; } /** * brief NTC检测定时器回调函数核心检测逻辑 * param priv 定时器私有参数未使用 * note 实现双周期采样、多次表决、滞回判断、状态切换与充电控制 */ static void ntc_det_timer_deal(void *priv) { u32 value; #if NTC_DET_CNT // 本轮首次采样将定时器切换为小周期快速完成连续采样 if (ntc_det.cnt 0) { sys_timer_modify(ntc_det.timer, NTC_DET_DUTY2); } #endif // 读取ADC采样值并打印调试 value adc_get_value(NTC_DET_AD_CH); printf(%d, value); ntc_det.cnt; // 温度检测次数加1 // 滞回判断异常状态下偏移量为8正常判定范围更窄防止临界抖动 if (NTC_IS_NORMAL(value, ntc_det.state * 8)) { //温度恢复一定范围后才算正常防止临界状态 ntc_det.normal_cnt; // 温度正常次数加1 } else if (NTC_IS_BAD_RES(value)) { ntc_det.res_cnt; // 分压电阻异常次数加1 } // 完成一轮采样达到设定采样次数进行状态表决 if (ntc_det.cnt NTC_DET_CNT) { // 多数表决超过半数采样正常判定温度正常 if (ntc_det.normal_cnt NTC_DET_CNT / 2) { if (ntc_det.state NTC_STATE_ABNORMAL) { // 从异常状态恢复到正常状态重启充电 printf(temperature recover, start charge); ntc_det.state NTC_STATE_NORMAL; charge_start(); // 恢复充电 } } #if NTC_DET_BAD_RES else if (ntc_det.res_cnt NTC_DET_CNT / 2) { printf(bad res, stop det); ntc_det_stop(); } #endif else { if (ntc_det.state NTC_STATE_NORMAL) { printf(temperature is abnormall, stop charge); ntc_det.state NTC_STATE_ABNORMAL; charge_close(); CHARGE_EN(0); } /* power_set_soft_poweroff(); */ } ntc_det.cnt 0; ntc_det.res_cnt 0; ntc_det.normal_cnt 0; sys_timer_modify(ntc_det.timer, NTC_DET_DUTY1); } } /** * brief 启动NTC温度检测 * note 初始化硬件、ADC通道、定时器根据上拉类型配置IO模式 */ void ntc_det_start(void) { if (ntc_det.timer 0) { printf(ntc det start); memset(ntc_det, 0, sizeof(ntc_det)); if (NTC_DET_PULLUP_TYPE PULLUPRES_IS_TRIM) { /* 内部上拉 */ printf(PULLUP_RES: %d, ntc_get_inside_pull_up_res_trim_value()); gpio_set_mode(IO_PORT_SPILT(IO_PORTC_05), PORT_INPUT_PULLUP_100K); } else { /* 外部上拉 */ gpio_set_mode(IO_PORT_SPILT(NTC_POWER_IO), PORT_OUTPUT_HIGH); gpio_set_mode(IO_PORT_SPILT(NTC_DETECT_IO), PORT_HIGHZ); } adc_add_sample_ch(NTC_DET_AD_CH); printf(NTC_DET_UPPER: %d NTC_DET_LOWER: %d, NTC_DET_UPPER, NTC_DET_LOWER); // 创建系统定时器启动周期检测 ntc_det.timer sys_timer_add(NULL, ntc_det_timer_deal, NTC_DET_DUTY1); } } /** * brief 停止NTC温度检测 * note 带保护锁定温度异常保护中不允许停止检测防止保护失效 */ void ntc_det_stop(void) { if (!get_charge_full_flag() get_charge_online_flag() ntc_det.state NTC_STATE_ABNORMAL) { printf(charge protecting, wait recover); return; } if (ntc_det.timer) { printf(ntc det stop); sys_timer_del(ntc_det.timer); ntc_det.timer 0; adc_delete_ch(NTC_DET_AD_CH); gpio_set_mode(IO_PORT_SPILT(NTC_POWER_IO), PORT_HIGHZ); } } #if TCFG_CHARGE_ENABLE /** * brief 系统消息回调监听充电事件联动NTC检测启停 * param msg 消息指针 * return 0处理完成 */ static int ntc_msg_entry(int *msg) { switch (msg[0]) { case CHARGE_EVENT_CHARGE_START: // 充电开始事件启动NTC检测 ntc_det_start(); break; case CHARGE_EVENT_CHARGE_CLOSE: // 充电关闭事件停止NTC检测 ntc_det_stop(); break; case CHARGE_EVENT_LDO5V_KEEP: // 5V稳压保持事件停止NTC检测 ntc_det_stop(); } return 0; } APP_MSG_PROB_HANDLER(ntc_event_handler) { .owner 0xff, .from MSG_FROM_BATTERY, .handler ntc_msg_entry, }; #endif #endif代码工作流程实现NTC温度保护逻辑讲解结合电路与代码NTC 温度保护的完整工作过程如下阶段 1检测启动充电插入触发1、耳机插入充电仓芯片收到充电开始事件调用ntc_det_start()2、配置NTC_POWER_IO为输出高电平给 NTC 分压电路上电3、配置NTC_DETECT_IO为高阻模拟输入注册对应 ADC 采样通道4、初始化状态机启动 5 秒周期的系统定时器进入低功耗轮询模式阶段 2周期采样与防抖判断1、定时器触发后先切换为 10ms 小周期连续采集 3 次 ADC 值2、每次采样后通过分压公式对应的阈值判断温度是否在正常范围3、次采样完成后采用多数表决超过半数结果一致才确认状态避免单次干扰误触发阶段 3温度异常保护若判定温度超出正常范围AD 值低于下限 过温或高于上限 过冷1、状态切换为异常打印温度异常日志2、调用charge_close()CHARGE_EN(0)硬件关闭充电输出防止电池损坏3、切回 5 秒大周期持续监测温度是否恢复阶段 4温度恢复与滞回1、异常状态下正常判定范围会收窄 8 个 AD 值滞回设计必须温度恢复到更安全的区间才会解除保护2、确认温度恢复后重新开启充电状态切回正常阶段 5检测停止充电移除 / 充满1、收到充电结束事件后调用ntc_det_stop()2、若当前正处于温度异常保护状态则拒绝停止保护锁定防止保护中途失效3、正常状态下删除定时器、移除 ADC 通道、供电 IO 设为高阻NTC 电路完全断电进入低功耗状态制作不易喜欢的小伙伴给个小赞赞喜欢我的小伙伴点个关注有不懂的地方和需要的资源随时问我哟