本帖最后由 norths 于 2025-2-13 14:44 编辑

智能家居控制终端的实现
1、概述
    本设计方案是采用富芮坤FR3068E-C开发板,基于FreeRTOS实现LVGL和蓝牙驱动的双重任务实现。
    设计的目的是基于富芮坤提供的开发环境和范例,来测试独立开发的方法和路径。
2、开箱和测试
2.1 富芮坤FR3068E-C开发板
  富芮坤FR3068E-C开发板(上海富芮坤微电子有限公司)是一款基于独立蓝牙内核的低功耗,高安全性的高性能无线 MCU,内置了蓝牙 BR/EDR/BLE 的收发器和控制器,以及 CAN FD 总线控制器。
    FR306x-C 符合蓝牙 V5.3 标准,支持 BR 1Mbps GFSK, EDR 2Mbps π/4-DQPSK, 3Mbps 8DPSK; BLE1M/2Mbps GFSK, 125K/500K 多种模式,支持单独打开和关闭不同的模式,支持蓝牙多主多从多连接。FR306x-C 支持 AUTOSAR 软件框架;FR306x-C 内置最多 2 个独立的 CAN FD 控制器,向下兼容CAN2.0 A/B 部分。
2.2 开发板出厂配置了一个LCD屏并配套综合开发工具。出厂能够实现多种lvgl的显示驱动,使用按键实现UI控制。
2.PNG
其中的主页面如下
1.PNG
2.3 开发采用keil MDK,但是写入代码需要采用专用的下载工具,可以通过uart口写入,虽然在两个程序中切换略显不便,但因为写入的频率不高,这样的方法也是非常好用。
frq_01.PNG
写入代码后,通常会输出下述提示符
frq_02.PNG
采用出厂代码,在uart的通讯显示如下,显示蓝牙GAP的连接和基于lvgl的GUI启动的成功信号
frq_03.PNG

3、智能家居控制终端的实现
3.1 基本设计思路
   使用FreeRTOS创建lvgl驱动任务和低功耗蓝牙ble任务,创建两个页面,一个是主控页面,一个是蓝牙驱动页面。采用lvgl的驱动来添加标签,控制字体等。这个主控页面的图片需要从内存的固定地址直接读取,采用连续图片加载可以实现动画的效果。本次没有合适的图片工具生成,所以就暂时取消该动画的显示。启动就直接进入页面。
3.2 主要解决的问题
    解决快速智能终端开发的功能。
3.3 开发作品和开发思路
基于FreeRTOS实现LVGL和蓝牙驱动的双重任务实现,启动任务是引入freeRTOS,然后分别配置硬件接口,设置中断级别,然后在各个任务中分别开发。
使用keil编译成功
1.PNG
在使用范例代码进行修改可以很顺利完成工作,生成Projecto_burn.bin。
在通常编译只会生成project.bin,但是还需要启动一个后处理的python脚本,才能链接起来形成上述可以写入的代码。
3.4 功能测试后运行成功,如下图
o.gif
使用手机可以连接来自开发板的蓝牙信标,显示为frq30xx_9d17
1059270068.jpg
因此显示和控制基本功能可以顺利实现。

4、评价及总结
4.1 通过这次项目开发,可以实现上述评测任务,同时可以理解整个开发流程。上述过程是一个经过多次摸索和测试的过程总结,花了很长的时间,按照这个过程就可以比较顺利完成任务,同时避免弯路。最后的结论就是,如果熟悉了开发工具,掌握了这个流程,那么基于富芮坤FR3068x-C的开发是非常快捷可靠的。即使是进入生产阶段的开发也是很快的。

4.2 开发遇到的主要问题
* 开发板的供电问题,这个开发板需要把USB micro-C和USB micro-B两个接口都连接上,因为串口的连接没有提供供电轨的电压,单独是无法驱动开发板的。因此,另外的USB micro-C是一个供电接口,负责串口的上拉电压和开发板的供电。通常需要仔细看开发板的电路图才能找到方法。
* 使用KEIL MDK的设备包的问题。这里富芮坤FR3068x-C的选择其实是非常理性的,并没有单独开发驱动包,而是直接采用了公版的通用驱动,也就是不需要特别确定内核,而对于蓝牙内核的驱动,其实是采用了一个内嵌内核的方式,用通讯的方式实现,其实也很可以
2.PNG
*使用后处理脚本的问题。这里加入后处理脚本是必须的选项,
\components\tools\keil\post_process.bat" "@L" "#L" "$J"
3.PNG
这里详细研究这个post_process.bat
import os, sys
  • import struct
  • import zlib

  • CHIP_TYPE_FR303x = 0
  • CHIP_TYPE_FR509x = 1

  • CHIP_TYPE = CHIP_TYPE_FR509x

  • UNPROTECT = 1                   # unprotect enable, for OTA
  • VOLATILE_MODE = 1               # volatile mode enable when unprotect flash, for OTA
  • UNPROTECT_BITS = 0x00           # protect bits setting when unprotect flash, for OTA
  • DEAL_CMP_BIT = 1                # deal cmp bit when unprotect flash, for OTA
  • CMP_BIT_POS = 6                 # cmp bit position in status-2, for OTA
  • WRITE_STATUS_2_SEPERATE = 1     # write status-2 with 0x31(1) or 0x01(0) when unprotect flash, for OTA
  • ENABLE_CACHE_FOR_B = 0          # enable cache in ota procedure, for OTA
  • QSPI_DIVIDOR = 1                # qspi dividor
  • WRITE_TYPE = 0                  # write type
  • READ_TYPE = 2                   # read type
  • OVERLAP = 1                     # execute and store zone of B is overlap or not, for OTA
  • ENABLE_CACHE_FOR_U = 1          # enable cache before enter user code
  • CMP_BIT_SET = 0                 # cmp bit setting when unprotect flash, for OTA
  • REPROTECT = 0                   # reset protect bits after OTA is finished, for OTA

  • def fill_header(f_in_file,
  •                     f_out_file,
  •                     store_offset,
  •                     version = 0xffffffff,
  •                     exec_offset = 0x2000,
  •                     copy_unit = 16*1024,
  •                     copy_flag_store_step = 32):
  •     info_crc = 0xffffffff
  •     code_length = os.path.getsize(f_in_file)
  •     f_out = open(f_out_file, 'wb')
  •     f_out.write(struct.pack('I', version))
  •     f_out.write(struct.pack('I', store_offset))
  •     f_out.write(struct.pack('I', code_length))
  •     f_out.write(struct.pack('I', exec_offset))
  •     f_out.write(struct.pack('I', copy_unit))
  •     f_out.write(struct.pack('I', copy_flag_store_step))
  •     f_in = open(f_in_file, 'rb')
  •     array = f_in.read(code_length)
  •     image_crc = zlib.crc32(array)
  •    
  •     f_out.write(struct.pack('B', image_crc&0xff))
  •     f_out.write(struct.pack('B', (image_crc&0xff00)>>8))   
  •     f_out.write(struct.pack('B', (image_crc&0xff0000)>>16))   
  •     f_out.write(struct.pack('B', (image_crc&0xff000000)>>24))
  •    
  •     #f_out.write(struct.pack('I', image_crc))
  •     f_out.write(struct.pack('I', 0x51525251))
  •     if CHIP_TYPE == CHIP_TYPE_FR509x:
  •         image_tlv_length = 0
  •         f_out.write(struct.pack('I', image_tlv_length))
  •     options = (UNPROTECT<<0)        \
  •                 | (VOLATILE_MODE<<1)    \
  •                 | (UNPROTECT_BITS<<2) \
  •                 | (DEAL_CMP_BIT<<7)    \
  •                 | (CMP_BIT_POS<<8)    \
  •                 | (WRITE_STATUS_2_SEPERATE<<11)   \
  •                 | (ENABLE_CACHE_FOR_B<<12)   \
  •                 | (QSPI_DIVIDOR<<13)   \
  •                 | (WRITE_TYPE<<17)   \
  •                 | (READ_TYPE<<19)   \
  •                 | (OVERLAP<<22)     \
  •                 | (ENABLE_CACHE_FOR_U<<23)  \
  •                 | (CMP_BIT_SET<<24) \
  •                 | (REPROTECT<<25)
  •     f_out.write(struct.pack('I', options))
  •     f_out.write(struct.pack('I', info_crc))
  •     f_out.write(struct.pack('I', 0x51525251))
  •     first_sector_last_size = 0
  •     if CHIP_TYPE == CHIP_TYPE_FR509x:
  •         first_sector_last_size = 0x1000-12*4
  •     else:
  •         first_sector_last_size = 0x1000-4-9*4
  •     for i in range(first_sector_last_size):
  •         f_out.write(struct.pack('B', 0xff))
  •     f_out.seek(0x2000)
  •     f_out.write(array)
  •     f_out.close()

  • if __name__ == '__main__':
  •     '''
  •     argv[1]: project name
  •     argv[2]: output path
  •     '''

  •     input_bin_name = "%s\\%s.bin" % (sys.argv[2], sys.argv[1])
  •     if os.path.exists(input_bin_name):
  •         output_bin_name = "%s\\%s_burn.bin" % (sys.argv[2], sys.argv[1])
  •         fill_header(input_bin_name, output_bin_name, 0)
  •         print("program target with file %s" % output_bin_name)
  •     else:
  •         print("INVALID INPUT PARAMTER for python script")
  • 复制代码

    这样就可以看得更清楚了,是在版本号和内存定位上进行了设置。
    * 代码的逻辑和流程。这个基于lvgl的例程是一个面向生产的开发模板,因此包括了多样的灵活配置空间。但是如果花费时间少的话,那么就比较难于理解和定位,这个就是攻城狮的本职工作了。这个过程只需要自由定义标签页的结构体,按照lvgl的定义,去定义各种小插件就可以实现所希望实现的界面控制。而硬件的访问可以独立创建freeRTOS线程,在不对结构调整的情况下,实现设计。这个例程中,专门定义了mcu速度计量线程,可以监测开发项目的性能,这部分代码都是注释掉了,没有使用,可以随时取消注释,非常便于开发。另外,button的按键功能也是采用独立线程监测,也可以在独立的代码段定义。详细的代码在附件打包,可以下载参考。
    * 本项目的开发主要是围绕fr_guimain.c代码中的lv_prj_main()函数来实现,以蓝牙功能为例,是fr_lv_blt_page.c来实现。
    5.PNG
    如何配置页面,主要是在fr_lv_list_page.c中实现
    6.PNG
    5、附件
    智能家居的keil MDK工程文件如下
    智能家居.zip (4.55 MB, 下载次数: 0)
    全部回复 0
    暂无评论,快来抢沙发吧