本帖最后由 jobszheng 于 2025-5-26 02:43 编辑
在完成了使用英飞凌PSOC 4000T的基础功能实验后,我们进入本次DIY的实现环节——温奶器的DIY。
一、概述 国家在鼓励生育,做为电子工程师我也利用自身的技术所长做出支持。我申请英飞凌PSoC 4000T Protokit评估板用来DIY一个婴幼儿温奶与消毒器小家电。温奶器一般有恒温水壶和消毒仓组成,通过控制面板设置恒温水温度和控制功能。我家里的温奶器的控制面板人机交互按钮使用的是电容式按键,平时使用时也挺方便的,但是电容式按键一旦滴上水,按键便不再灵敏。温奶器无论在消毒,还是倒水的时候滴上水是很正常的现象。理论上擦干即可,可我家这个不知道为什么擦干要等些时间,而在等待期间按键失灵,温奶器也不能正常工作。虽然我也是电子工程师,但我也没搞懂。这其中的关联不过我知道,如果可以带水操作,则上述问题定不会存在英飞凌PSOC 4000T就可以解决我的问题。还有另外一个问题:我家小宝宝已经3岁,他可以踮着脚伸手触碰到我温奶器的按键。我家的温奶器却没有一个童锁!有时候我们发现温奶器没有正常工作以为系统自己复位了,后来才了解到是小家伙直接给关闭掉了! 二、硬件设计 英飞凌PSOC 4000T系列是基于ARM公司Cortex-M0+内核设计的一款多传感器用途的MCU,适配英飞凌自家的CapSense第五代专利技术,轻松实现高效、准确、稳定的电感按键采样与识别。 英飞凌还有一项更加令人兴奋的硬件功能特性——悬停按键,这也实现了隔离触发按键的功能。悬停生效 时,这对小朋友来说仅靠模仿怕是非常困难,也能成功地把小朋友误操作设备导致烫伤的可能性排除了。我们设计了基于英飞凌PSOC 4000T-MS开发板套件的原理设计硬件框图。
2.1 硬件框图 从上面的硬件框图,我们看到UART外设用于与温奶器的主控MCU通讯,后者需要显示LED屏与控制加热器加热,采集温度传感器获取温度等等。 考虑到本次DIY的时间略紧,以PC机串口为通讯接口。这也满足PSOC 4000T的原型开发的需求。 2.2 硬件配置 对于硬件外设的配置,我们使用ModusToolBox中的Device Configurator工具软件实现。我们依次配置如下外设:系统时钟,外设分频时钟,串口外设,CapSense外设,GPIO等 系统时钟 系统时钟使用HSI的48MHz。即英飞凌PSOC 4000T将工作在48MHz的主频下。 外设分频时钟 我们使用了串口外设,需要我们使用PSOC 4000T的16位,分频时钟,为其提供时钟源输入,具体配置如下: 串口时钟 SCB串口时钟,在设置了分频时钟后,即可在SCB UART外设界面配置UART时钟,之后将其配置参数设置为115200-8-N-1即可。 CapSense 本次原型开发使用了4键按键的硬件扩展板。故参照官方示例配置。 GPIO配置 GPIO主要用于显示设备状态。对于温奶器来说,设备状态使用主控MCU来完成,而PSOC 4000T的LED灯仅使用于我们调试时使用。以上就是我们的硬件配置,接下来我们看看软件设计实现。 三、软件设计 硬件设计参照英飞凌ModusToolBox来实现,软件外设底层代码也是通过ModusToolBox来实现。其实在device configurator保存一下,代码便自动生成了,自己修改一下主函数的调用即可完成. 3.1 软件流程图
3.2 按键通讯帧 流程图相对简单,主要是我们将PSOC 4000T的功能点减少,仅作为输入情况处理与主控MCU通讯的数据格式。我们采用类似标准键盘上报键码的形式。以固定长度为一个通讯帧,通过空闲帧和帧头标识来做帧首,帧尾判断,源代码如下: typedef struct key_frame_s {
uint8_t id;
uint8_t func_key;
uint8_t reserved0;
uint8_t key[4];
uint8_t reserved1;
} key_frame_t;
帧首:第1个字节,固定标识号0x55,用于帧首判断。非此值,则将此帧丢弃。 功能按键:按位表示功能按键,最多支持8个功能按键。其中按下生效时置位,抬起时清零。 Key键码域:共4个字节,每次通讯时填充键码值,即按键被按下时依次写入K0、K1、K2、K3。当被按键抬起时,再重新发送此时按下状态的一帧键码。 3.3 程序关键代码 按键上报代码: static void uart_msg_poll(void) {
int ret = 0;
if (CAPSENSE_WIDGET_INACTIVE
!= Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON0_WDGT_ID,
&cy_capsense_context)) {
Cy_GPIO_Write(CYBSP_KEYPAD_LED1_PORT, CYBSP_KEYPAD_LED1_NUM,
CYBSP_LED_ON);
key_frame.key[0] = CY_CAPSENSE_BUTTON0_WDGT_ID;
} else {
Cy_GPIO_Write(CYBSP_KEYPAD_LED1_PORT, CYBSP_KEYPAD_LED1_NUM,
CYBSP_LED_OFF);
key_frame.key[0] = 0;
}
if (CAPSENSE_WIDGET_INACTIVE
!= Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON1_WDGT_ID,
&cy_capsense_context)) {
Cy_GPIO_Write(CYBSP_KEYPAD_LED2_PORT, CYBSP_KEYPAD_LED2_NUM,
CYBSP_LED_ON);
key_frame.key[1] = CY_CAPSENSE_BUTTON1_WDGT_ID;
} else {
Cy_GPIO_Write(CYBSP_KEYPAD_LED2_PORT, CYBSP_KEYPAD_LED2_NUM,
CYBSP_LED_OFF);
key_frame.key[1] = 0;
}
if (CAPSENSE_WIDGET_INACTIVE
!= Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON2_WDGT_ID,
&cy_capsense_context)) {
Cy_GPIO_Write(CYBSP_KEYPAD_LED3_PORT, CYBSP_KEYPAD_LED3_NUM,
CYBSP_LED_ON);
key_frame.key[2] = CY_CAPSENSE_BUTTON2_WDGT_ID;
} else {
Cy_GPIO_Write(CYBSP_KEYPAD_LED3_PORT, CYBSP_KEYPAD_LED3_NUM,
CYBSP_LED_OFF);
key_frame.key[2] = 0;
}
if (CAPSENSE_WIDGET_INACTIVE
!= Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON3_WDGT_ID,
&cy_capsense_context)) {
Cy_GPIO_Write(CYBSP_KEYPAD_LED4_PORT, CYBSP_KEYPAD_LED4_NUM,
CYBSP_LED_ON);
key_frame.key[3] = CY_CAPSENSE_BUTTON3_WDGT_ID;
} else {
Cy_GPIO_Write(CYBSP_KEYPAD_LED4_PORT, CYBSP_KEYPAD_LED4_NUM,
CYBSP_LED_OFF);
key_frame.key[3] = 0;
}
ret = key_frame_update_check(&key_frame, &key_frame_shadow);
if (ret != 0) {
Cy_SCB_UART_PutArrayBlocking(UART_HW, &key_frame, sizeof(key_frame_t));
key_frame_sync(&key_frame, &key_frame_shadow);
}
}
主函数源代码(包含低功耗):
int main(void) {
cy_rslt_t result;
uint32_t capsense_state_timeout;
uint32_t interruptStatus;
cy_stc_scb_uart_context_t UART_context;
#if ENABLE_RUN_TIME_MEASUREMENT
static uint32_t active_processing_time;
static uint32_t alr_processing_time;
#endif
/* Initialize the device and board peripherals */
result = cybsp_init();
#if ENABLE_RUN_TIME_MEASUREMENT
init_sys_tick();
#endif
/* Board init failed. Stop program execution */
if (result != CY_RSLT_SUCCESS) {
CY_ASSERT(CY_ASSERT_FAILED);
}
/* Configure and enable the UART peripheral */
Cy_SCB_UART_Init(UART_HW, &UART_config, &UART_context);
Cy_SCB_UART_Enable(UART_HW);
/* Enable global interrupts */
__enable_irq();
/* Register callbacks */
register_callback();
/* Define initial state of the device and the corresponding refresh rate*/
capsense_state = ACTIVE_MODE;
capsense_state_timeout = ACTIVE_MODE_TIMEOUT;
/* Initialize MSC CAPSENSE™ */
initialize_capsense();
/* Measures the actual ILO frequency and compensate MSCLP wake up timers */
Cy_CapSense_IloCompensate(&cy_capsense_context);
/* Configure the MSCLP wake up timer as per the ACTIVE mode refresh rate */
Cy_CapSense_ConfigureMsclpTimer(ACTIVE_MODE_TIMER, &cy_capsense_context);
//Cy_SCB_UART_PutString(UART_HW, "Hello PSoC 4000T, Hello 21ic.com\r\n");
Cy_SCB_UART_PutArrayBlocking(UART_HW, uart_tx_buf, 8);
for (;;) {
switch (capsense_state) {
case ACTIVE_MODE:
Cy_CapSense_ScanAllSlots(&cy_capsense_context);
interruptStatus = Cy_SysLib_EnterCriticalSection();
while (Cy_CapSense_IsBusy(&cy_capsense_context)) {
Cy_SysPm_CpuEnterDeepSleep();
Cy_SysLib_ExitCriticalSection(interruptStatus);
/* This is a place where all interrupt handlers will be executed */
interruptStatus = Cy_SysLib_EnterCriticalSection();
}
Cy_SysLib_ExitCriticalSection(interruptStatus);
#if ENABLE_RUN_TIME_MEASUREMENT
active_processing_time=0;
start_runtime_measurement();
#endif
Cy_CapSense_ProcessAllWidgets(&cy_capsense_context);
/* Scan, process and check the status of the all Active mode sensors */
if (Cy_CapSense_IsAnyWidgetActive(&cy_capsense_context)) {
capsense_state_timeout = ACTIVE_MODE_TIMEOUT;
} else {
capsense_state_timeout--;
if (TIMEOUT_RESET == capsense_state_timeout) {
capsense_state = ALR_MODE;
capsense_state_timeout = ALR_MODE_TIMEOUT;
/* Configure the MSCLP wake up timer as per the ALR mode refresh rate */
Cy_CapSense_ConfigureMsclpTimer(ALR_MODE_TIMER,
&cy_capsense_context);
}
}
#if ENABLE_RUN_TIME_MEASUREMENT
active_processing_time=stop_runtime_measurement();
#endif
break;
/* End of ACTIVE_MODE */
/* Active Low Refresh-rate Mode */
case ALR_MODE:
Cy_CapSense_ScanAllSlots(&cy_capsense_context);
interruptStatus = Cy_SysLib_EnterCriticalSection();
while (Cy_CapSense_IsBusy(&cy_capsense_context)) {
Cy_SysPm_CpuEnterDeepSleep();
Cy_SysLib_ExitCriticalSection(interruptStatus);
/* This is a place where all interrupt handlers will be executed */
interruptStatus = Cy_SysLib_EnterCriticalSection();
}
Cy_SysLib_ExitCriticalSection(interruptStatus);
#if ENABLE_RUN_TIME_MEASUREMENT
alr_processing_time=0;
start_runtime_measurement();
#endif
Cy_CapSense_ProcessAllWidgets(&cy_capsense_context);
/* Scan, process and check the status of the all Active mode sensors */
if (Cy_CapSense_IsAnyWidgetActive(&cy_capsense_context)) {
capsense_state = ACTIVE_MODE;
capsense_state_timeout = ACTIVE_MODE_TIMEOUT;
/* Configure the MSCLP wake up timer as per the ACTIVE mode refresh rate */
Cy_CapSense_ConfigureMsclpTimer(ACTIVE_MODE_TIMER,
&cy_capsense_context);
} else {
capsense_state_timeout--;
if (TIMEOUT_RESET == capsense_state_timeout) {
capsense_state = WOT_MODE;
}
}
#if ENABLE_RUN_TIME_MEASUREMENT
alr_processing_time=stop_runtime_measurement();
#endif
break;
/* End of Active-Low Refresh Rate(ALR) mode */
/* Wake On Touch Mode */
case WOT_MODE:
Cy_CapSense_ScanAllLpSlots(&cy_capsense_context);
interruptStatus = Cy_SysLib_EnterCriticalSection();
while (Cy_CapSense_IsBusy(&cy_capsense_context)) {
Cy_SysPm_CpuEnterDeepSleep();
Cy_SysLib_ExitCriticalSection(interruptStatus);
/* This is a place where all interrupt handlers will be executed */
interruptStatus = Cy_SysLib_EnterCriticalSection();
}
Cy_SysLib_ExitCriticalSection(interruptStatus);
if (Cy_CapSense_IsAnyLpWidgetActive(&cy_capsense_context)) {
capsense_state = ACTIVE_MODE;
capsense_state_timeout = ACTIVE_MODE_TIMEOUT;
/* Configure the MSCLP wake up timer as per the ACTIVE mode refresh rate */
Cy_CapSense_ConfigureMsclpTimer(ACTIVE_MODE_TIMER,
&cy_capsense_context);
} else {
capsense_state = ALR_MODE;
capsense_state_timeout = ALR_MODE_TIMEOUT;
/* Configure the MSCLP wake up timer as per the ALR mode refresh rate */
Cy_CapSense_ConfigureMsclpTimer(ALR_MODE_TIMER,
&cy_capsense_context);
}
break;
/* End of "WAKE_ON_TOUCH_MODE" */
default:
/** Unknown power mode state. Unexpected situation. **/
CY_ASSERT(CY_ASSERT_FAILED);
break;
}
uart_msg_poll();
#if ENABLE_TUNER
/* Establishes synchronized communication with the CAPSENSE™ Tuner tool */
Cy_CapSense_RunTuner(&cy_capsense_context);
#endif
}
}
四、DIY成果与创新点 先来上一段视频转换的gif动图。 通过上面的视频,可以看到我们的英飞凌PSOC 4000T成功识别出来我的按键操作。也可以看到在按键按下后,英飞凌PSOC 4000T快速的通过串口上报了键码。当按键抬起时,我们英飞凌PSOC 4000T也迅速的发送空值的键码。通过串口的实时键码上报,可以让主控MCU及时获取到按键的状态,从而做出正确的控制执行操作。 本次DIY利用了英飞凌PSOC 4000 T MS套件的电感触摸按键和悬浮按键的优秀性能。在根本上解决了温奶器面板在滴水时,遇到电容按键失灵的严重用户体验问题。悬浮按键技术又解决了小朋友爱模仿,多动手操作的天性下“误按”按键带来的烫伤的风险。我个人觉得英飞凌PSOC 4000T为下一代按键面板的理想选择。我们又体验了英飞凌PSOC 4000T的平衡成本与性能,虽然仅预留了一个Uart外设接口,仍轻松通过协议帧完成按键操作的实时响应与数据上报操作。 五、致谢 感谢英飞凌中国提供这么高大上的开发套件,让我充分感受到英飞凌中国对国内工程师的热情,践行了英飞凌“在中国,服务中国”的口号。 感谢21ic论坛,感谢21ic小跑堂的大力支持。 感谢一直关注我DIY活动的网友们! 谢谢你们!我们英飞凌论坛天天见!
|