打印
[PSOC™]

【英飞凌PSOC 4000T DIY】温奶器DIY原型设计实现

[复制链接]
153|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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活动的网友们!
        谢谢你们!我们英飞凌论坛天天见!











使用特权

评论回复
沙发
星辰大海不退缩| | 2025-5-26 16:21 | 只看该作者
非常不错的案例分享

使用特权

评论回复
板凳
小夏天的大西瓜| | 2025-5-27 10:19 | 只看该作者
非常不错的DIY设计

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:嵌入式技术专家
简介:热爱开源,乐于分享。在嵌入式技术领域里面,主攻通讯协议,Modbus,TCP/IP以及虚拟化和RTOS

28

主题

621

帖子

5

粉丝