注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
lugl4313820的个人空间 https://home.eeworld.com.cn/space-uid-1269709.html [收藏] [复制] [分享] [RSS]
日志

【DigiKey“智造万物,快乐不停”创意大赛】《电流电压综合测试系统》作品提交

已有 251 次阅读2024-1-9 17:36

一、作品简介
《电流电压综合测试系统》作者:lugl4313820
所用到的板卡为:
1、32F746GDISCOVERY
STM32F746NGH6微控制器具有1MB的闪存和340KB的RAM,BGA216封装
◆板载ST-LINK支持USB重新枚举能力
◆USB功能可可实现虚拟COM端口、海量存储、调试端口三大功能
◆4.3英寸480×272彩色LCD-TFT电容式触摸屏 (触摸芯片RK043FN48H-CT672B)
◆摄像头连接口(新提供)
◆音频输入、输出插孔
◆两个MEMS麦克风
◆SPDIF音频输入接口
◆用户和复位按键
◆128-Mbit四通道SPI闪存 (芯片 N25Q128A)
◆128-Mbit SDRAM(64Mbits 可访问) (芯片 MT48LC4M32B2)
◆提供microSD 卡
◆提供外部RF\EEPROM的IIC连接口
◆两个Micro-AB,类型为USB OTG HS 和USB OTG FS
◆以太网接口
◆Arduino 接口

2、电压采集芯片为ADS1115。16位ADC采集模块。

【作品功能】
在产品出厂前,都需要对产品的各项指标进行测试测量,以往都是手工进行测量,往往人工成本高,人为的误差比较大。所以我们在提供测试架的同时,结合开发了此款与测试架配套的产品,旨在提高检测精度,节省时间与人工成本。
本产品可以精确的测量到纳安级别的待机电流,也可以模拟吸唤气等动作,来模拟现实中的使用场景,从而实现应用环境的精准测量。并且设计了参数可调,让用户可以根据不同时的产品来设定工作、休眠、吸换气等参数,来达到多个产品应用同一个测试架。
二、系统框图(图文结合)
本产品的设计思路是结合日常工作中测试测量的经验,来设计充放电、吸唤气等来设计出流水线,实现对各个场景的电流、电压的采集,根据用户设置的限值范围,结合测试结果,智能的实现产品的测试结果判定。
其流程图如下:
原理图:
【软件】
  1. stm32CubeIDE
  2. TouchGFX Designer-4.23
  3. FreeRTOS
Stm32提供了免费的stm32cubeIDE以及图形设计软件TouchGFX。设计采用GUI界面与后台分离设计。通过Model-Presenter-View的设计模式来对产品进行设计,这样的好处是通过接口进行对接,在设计过程中各个模块可以实现独立的测试等优点。
【作品实物】
内部原始的结构图:
最终内倍结构图:
【注】由于是工程样品,所以接线有点乱,当然生产环节会打板,要不接线成本太高呀,我接了一天。
三、各部分功能说明
  1. 主界面为参数设置、测试、帮助进行了导航。
  2. 参数设置部分,主是要对电流、电压的限值进行设置,软件中结合大量的测试经验,提供了高低限值,方便用户根据本身的产品进行设置:

    主要代码如下:
    /* 静态电流下限减 */
    void testSetView::FunLowerOperatingCurrentISubstract()
    {
    Test_dian1.Lower_limit_operating_current_vlaue = Test_dian1.Lower_limit_operating_current_vlaue - 1 ;
    //如果超出了默认的下限就设置为下限
    if(Test_dian1.Lower_limit_operating_current_vlaue <= LOWER_LIMIT_OPERATING_CURRENT_VALUE)
    {
    Test_dian1.Lower_limit_operating_current_vlaue = LOWER_LIMIT_OPERATING_CURRENT_VALUE;
    }
    Unicode::snprintf(textAreaLowerLimitOperatingCurrentValueBuffer, TEXTAREALOWERLIMITOPERATINGVOLTAGE2VALUE_SIZE, "%d",Test_dian1.Lower_limit_operating_current_vlaue);
    textAreaLowerLimitOperatingCurrentValue.invalidate();
    }
    /* 静态电流下限加 */
    void testSetView::FunLowerOperatingCurrentAdd()
    {
    Test_dian1.Lower_limit_operating_current_vlaue +=1 ;
    /* 判断是否到达上限 */
    if(Test_dian1.Lower_limit_operating_current_vlaue >= UPPER_LIMIT_OPERATING_CURRENT_VALUE)
    {
    Test_dian1.Lower_limit_operating_current_vlaue = UPPER_LIMIT_OPERATING_CURRENT_VALUE;
    }
    /* 判断是否比上限高 */
    if(Test_dian1.Lower_limit_operating_current_vlaue >= Test_dian1.Upper_limit_operating_current_vlaue)
    {
    Test_dian1.Lower_limit_operating_current_vlaue = Test_dian1.Upper_limit_operating_current_vlaue -1;
    }
    Unicode::snprintf(textAreaLowerLimitOperatingCurrentValueBuffer, TEXTAREALOWERLIMITOPERATINGVOLTAGE2VALUE_SIZE, "%d",Test_dian1.Lower_limit_operating_current_vlaue);
    textAreaLowerLimitOperatingCurrentValue.invalidate();
    }

    其余的按键代码以此类推,详细见源码
    同时结合不同的产品,设置了不同测试时间段的测试时间

    代码的构思为每按下减进行递减,如果小于规定的下限值,则无效。上限也如此,四组按键均如此。
    void DelaySetView::Fun_but_C_T_Down()
    {
    Test_dian1.charging_current_times -= 100;
    //如果小于规定的下限,则无效
    if(Test_dian1.charging_current_times <= MIN_CHARGING_CURRENT_TIMES)
    {
    Test_dian1.charging_current_times = MIN_CHARGING_CURRENT_TIMES;
    }
    Unicode::snprintf(textAreaChargeCurrentTestTimeBuffer, TEXTAREACHARGECURRENTTESTTIME_SIZE, "%d", Test_dian1.charging_current_times);
    textAreaChargeCurrentTestTime.invalidate();
    }
    void DelaySetView::Fun_but_C_T_Up()
    {
    Test_dian1.charging_current_times += 100;
    //如果超出规定的上限,测无效
    if(Test_dian1.charging_current_times >= MAX_CHARGING_CURRENT_TIMES)
    {
    Test_dian1.charging_current_times = MAX_CHARGING_CURRENT_TIMES;
    }
    Unicode::snprintf(textAreaChargeCurrentTestTimeBuffer, TEXTAREACHARGECURRENTTESTTIME_SIZE, "%d", Test_dian1.charging_current_times);
    textAreaChargeCurrentTestTime.invalidate();
    }

    也可以根据不同的产品特点设置不同的工作模式:

    这里的代码主要是用按键组来选择工作模式:
    void worksequenceView::setupScreen()
    {
    if(Test_dian1.work_mode == 0)
    {
    radioBnt_0.setSelected(true);
    }
    else
    {
    radioBnt_1.setSelected(true);
    }
    worksequenceViewBase::setupScreen();
    }
    void worksequenceView::tearDownScreen()
    {
    worksequenceViewBase::tearDownScreen();
    }
    void worksequenceView::FunSetMode0()
    {
    Test_dian1.work_mode = 0;
    }
    void worksequenceView::FunSetMode1()
    {
    Test_dian1.work_mode = 1;
    }

    参数设置最后是给用户提供总结设置的确实界面,用户设置好后可以保存参数,方便重新开机后自动读取参数。

    实现的代码为将设置好的参数进行展示,让用户再次确认,如果按保存则更新到flash中以备下次重启里恢复参数。
  3. 测试界面,用户按下按键后开始测试,测试的结果会同步显示在界面。

    代码实现思路:
    当按下为开始测试按键时,通过信号量触发,阻塞的任务开始执行,通过规定的开关、电机的规定程序进行测量,测量完一个项日更新到下一个状态。
    void TaskChargeI(void *argument)
    {
    /* USER CODE BEGIN TaskChargeI */
    /* Infinite loop */
    for(;;)
    {
    if (xSemaphoreTake( xSemaChargeIHandle, portMAX_DELAY) == pdTRUE )
    {
    HAL_GPIO_WritePin(K3_GPIO_Port, K3_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(K6_GPIO_Port, K6_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(K5_GPIO_Port, K5_Pin, GPIO_PIN_RESET);
    osDelay(Test_dian1.start_wait_times);
    Test_dian1.charging_current_vlaue = (uint16_t)(ADS1115_ADDR_VDD.ADS1115_Vol[ADS1115_CHANNEL0_1]*10);
    //printf("ADS1115 VDD CH vol = %d \n\n",Test_dian1.charging_current_vlaue);
    if((Test_dian1.charging_current_vlaue > Test_dian1.Upper_limit_charging_current_vlaue) ||( Test_dian1.charging_current_vlaue < Test_dian1.Lower_limit_charging_current_vlaue) )
    {
    Test_dian1.charging_current_result = 1;
    }
    else
    {
    Test_dian1.charging_current_result = 0;
    }
    my_dianzhi_test_state = CHARGING_CURRENT_OK;
    osDelay(1000);
    HAL_GPIO_WritePin(K3_GPIO_Port, K3_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(K6_GPIO_Port, K6_Pin, GPIO_PIN_SET);
    //打开气泵
    //打开3.7V开关
    HAL_GPIO_WritePin(K5_GPIO_Port, K5_Pin, GPIO_PIN_RESET);
    //打开负载开关
    HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_RESET);
    //延时100ms
    //开气泵
    HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_RESET);
    //检测电压
    Get_adc();
    Test_dian1.operating_voltage1_vlaue = ((Get_adc() & 0x0FFF)*3.3f*5.2/4096)*1000;
    osDelay(1000);
    //关气泵
    HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);
    osDelay(1000);
    HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_RESET);
    //开气泵
    osDelay(Test_dian1.smokeing_times);
    Get_adc();
    Test_dian1.operating_voltage1_vlaue = ((Get_adc() & 0x0FFF)*3.3f*2.3/4096)*1000;
    //发送检测完毕的信号
    //判断检测结果
    if((Test_dian1.operating_voltage1_result > Test_dian1.Upper_limit_operating_voltage1_vlaue) || (Test_dian1.operating_voltage1_vlaue < Test_dian1.Lower_limit_operating_voltage1_vlaue))
    {
    Test_dian1.operating_voltage1_result = 1;
    }
    else
    {
    Test_dian1.operating_voltage1_result = 0;
    }
    my_dianzhi_test_state = OPERATING_VOLATGE_1_OK;
    //关闭负载开关
    HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);
    //延时到休眠状态
    osDelay(Test_dian1.wait_sleep_time);
    //打开检测电阻开关
    Test_dian1.operating_current_vlaue = (uint16_t)(ADS1115_ADDR_VDD.ADS1115_Vol[ADS1115_CHANNEL2_3]/100);
    my_dianzhi_test_state = OPERATING_CURRENT_OK;
    if((Test_dian1.operating_current_vlaue > Test_dian1.Upper_limit_operating_current_vlaue) || (Test_dian1.operating_current_vlaue < Test_dian1.Lower_limit_operating_current_vlaue) )
    {
    Test_dian1.operating_current_result = 1;
    }
    else
    {
    Test_dian1.operating_current_result = 0;
    }
    //测试结束
    osDelay(100);
    if( (Test_dian1.operating_current_result + Test_dian1.operating_voltage1_result + Test_dian1.charging_current_result) >0)
    {
    Test_dian1.test_result = 1;
    }
    else
    {
    Test_dian1.test_result = 0;
    }
    my_dianzhi_test_state = DIANZHI_TEST_FINISH;
    HAL_GPIO_WritePin(K4_GPIO_Port, K4_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(K5_GPIO_Port, K5_Pin, GPIO_PIN_SET);
    }
    osDelay(1);
    }
    /* USER CODE END TaskChargeI */
    }
    在model.cpp中,tick函数监检测的任务状态,更根据状态的不同,通过Persenter向View发送相应的数据,并在test_main中实时更新状态:
    void Model::tick()
    {
    if(my_dianzhi_test_state == CHARGING_CURRENT_OK)
    {
    //更新显示
    modelListener->notify_chargI_set(Test_dian1.charging_current_vlaue,Test_dian1.charging_current_result);
    }
    else if(my_dianzhi_test_state ==OPERATING_CURRENT_OK)
    {
    modelListener->notify_operatI_set(Test_dian1.operating_current_vlaue, Test_dian1.operating_current_result);
    }
    else if(my_dianzhi_test_state ==OPERATING_VOLATGE_1_OK)
    {
    modelListener->notify_volate1_set(Test_dian1.operating_voltage1_vlaue, Test_dian1.operating_voltage1_result);
    }
    else if(my_dianzhi_test_state == DIANZHI_TEST_FINISH)
    {
    //更新总测试结果
    modelListener->notify_test_result_set(Test_dian1.test_result);
    my_dianzhi_test_state = DIANZHI_TEST_START;
    //更新测试数
    }
    }

     

  4. 作品源码
五、作品功能演示视频
六、项目总结
本次项目是基于TouchGFX来做为GUI界面设计,用于用户的交互,主要功能是对测试产品的参数进行设定,测试结果进行展示,编程语言采用MPV模式的C++进行设计。后台的控制部分用C进行编程,实现了对电流电压进行实时采集,测试结果的判定,输出到GUI。工程采用了freeRTOS实时操作系统,实现GUI的以及后台控制、数据实时采集等多任务调度,前后台采用任务间通信,能实时、高效的完成前后端的协调一致。
通过本次参赛我将TouchGFX替换了原来用串口屏来实现用户界面交互,相比原来的设计方案,开发周期比原来缩短,不用定制复制的通迅协议来协调前后端的交互。同时也减少了开发人员之间的复杂沟通成本。
本次采用的开发软件为STM32CubeIDE、TouchGFX、FreeRTOS,均为免费的开发软件,对于小微企业来说也是成本的降低。
通过这次比赛,我进一步的提高了对touchGFX的熟练度,在今后的开发中可以减少开发成本,减少通信协议的设计,缩短开发周期,特别适合开发人员少的小微企业、对开发周期要求高的产品开发。
当然这次作品在UI设计方面没有专业的美工设计,作品的界面不是很美观,数据采集也没有做滤波。在实际的生产环境还需要继续完善。
七、其他
此次项目我原来是考虑使用lvgl做为GUI开发来设计一款冷链监控产品的,因为临时接到需要做一款多路电流电压测试架的项目,所以在原来的比赛作品进行到一半时,临时改变为本次作品,并且开源出来了单路的产品设计,希望对一些小微企业有这方面需求的有所帮助。
此致要感谢深圳市嘉欣钰电子科技的许总,经过他的同意,我们开源了共同设计的单独测试架的作品创意、原理图、源代码,这对于做企业的人来说,是一种无私奉献的开源精神。
也要感谢EEWORLD论坛与得捷电子,提供了这么好的平台,能让我在比赛中夯实自己的业务知识,同时也学习到其他参赛人员的许多优秀开源作品。
通过本次比赛后,进一步将比赛的作品快速的落地,将单路设计拓展为10路的测试产品交付用户使用,进一步提高工厂的测试精度,缩短产品交付时间,节约生产成本。

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章