本帖最后由 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.3 开发采用keil MDK,但是写入代码需要采用专用的下载工具,可以通过uart口写入,虽然在两个程序中切换略显不便,但因为写入的频率不高,这样的方法也是非常好用。
写入代码后,通常会输出下述提示符
采用出厂代码,在uart的通讯显示如下,显示蓝牙GAP的连接和基于lvgl的GUI启动的成功信号
3、智能家居控制终端的实现
3.1 基本设计思路
使用FreeRTOS创建lvgl驱动任务和低功耗蓝牙ble任务,创建两个页面,一个是主控页面,一个是蓝牙驱动页面。采用lvgl的驱动来添加标签,控制字体等。这个主控页面的图片需要从内存的固定地址直接读取,采用连续图片加载可以实现动画的效果。本次没有合适的图片工具生成,所以就暂时取消该动画的显示。启动就直接进入页面。
3.2 主要解决的问题
解决快速智能终端开发的功能。
3.3 开发作品和开发思路
基于FreeRTOS实现LVGL和蓝牙驱动的双重任务实现,启动任务是引入freeRTOS,然后分别配置硬件接口,设置中断级别,然后在各个任务中分别开发。
使用keil编译成功
在使用范例代码进行修改可以很顺利完成工作,生成Projecto_burn.bin。
在通常编译只会生成project.bin,但是还需要启动一个后处理的python脚本,才能链接起来形成上述可以写入的代码。
3.4 功能测试后运行成功,如下图
使用手机可以连接来自开发板的蓝牙信标,显示为frq30xx_9d17
因此显示和控制基本功能可以顺利实现。
4、评价及总结
4.1 通过这次项目开发,可以实现上述评测任务,同时可以理解整个开发流程。上述过程是一个经过多次摸索和测试的过程总结,花了很长的时间,按照这个过程就可以比较顺利完成任务,同时避免弯路。最后的结论就是,如果熟悉了开发工具,掌握了这个流程,那么基于富芮坤FR3068x-C的开发是非常快捷可靠的。即使是进入生产阶段的开发也是很快的。
4.2 开发遇到的主要问题
* 开发板的供电问题,这个开发板需要把USB micro-C和USB micro-B两个接口都连接上,因为串口的连接没有提供供电轨的电压,单独是无法驱动开发板的。因此,另外的USB micro-C是一个供电接口,负责串口的上拉电压和开发板的供电。通常需要仔细看开发板的电路图才能找到方法。
* 使用KEIL MDK的设备包的问题。这里富芮坤FR3068x-C的选择其实是非常理性的,并没有单独开发驱动包,而是直接采用了公版的通用驱动,也就是不需要特别确定内核,而对于蓝牙内核的驱动,其实是采用了一个内嵌内核的方式,用通讯的方式实现,其实也很可以
*使用后处理脚本的问题。这里加入后处理脚本是必须的选项,
\components\tools\keil\post_process.bat" "@L" "#L" "$J"
这里详细研究这个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
VOLATILE_MODE = 1
UNPROTECT_BITS = 0x00
DEAL_CMP_BIT = 1
CMP_BIT_POS = 6
WRITE_STATUS_2_SEPERATE = 1
ENABLE_CACHE_FOR_B = 0
QSPI_DIVIDOR = 1
WRITE_TYPE = 0
READ_TYPE = 2
OVERLAP = 1
ENABLE_CACHE_FOR_U = 1
CMP_BIT_SET = 0
REPROTECT = 0
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', 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来实现。
如何配置页面,主要是在fr_lv_list_page.c中实现
5、附件
智能家居的keil MDK工程文件如下
智能家居.zip
(4.55 MB, 下载次数: 0)