全志在线(AWOL)LOGO:
带中文白底版本:
带中文无底版本::
Posts made by q1215200171
-
【Sipeed D1 Dock Pro】YoC RTOS 实战:LCD屏动态显示
1. 准备工作
建议在操作前先阅读以下技术文章:
Lichee D1 dock 开发板用户指南
Lichee D1 dock 开发板快速上手教程2. 示例介绍
本示例主要使用了chip_d1组件驱动组件里的mipi-dsi驱动,用户可以参考驱动,对屏显示进行控制显示,包括屏的亮度显示等。- 屏初始化
int csi_display_init(void)
- 屏显示控制
int csi_display_ioctl(disp_cmd_e cmd, void *arg)
屏显示控制命令包括以下几类:
Global interface Layer interface Capture interface LCD interface Smart backlight
具体可以参考sunxi_display2.h里的tag_DISP_CMD数据定义。
**3. 示例演示
3.1 剑池CDK(以下简称CDK)演示
3.1.1 示例获取**
双击打开CDK,点击工具栏最右侧的平头哥图标。点击新建工程按钮。
在搜索栏里输入lcd_demo, 在结果里选择lcd_demo,点击右侧的创建工程,版本选择v7.5.1。
3.1.2 编译
右键点击lcd_demo工程,选择build,进行编译,直到结束。
编译完成后,可以在工程目录下out文件夹里找到yoc_rtos_8M.img文件。
3.1.3 下载
- 通过CDK烧写
先连接好硬件JTAG。然后点击红色框框处的按钮开始烧写。
烧写完毕之后,按复位键即可启动。
- 通过全志工具烧写
在没有JTAG的情况下,只能通过全志的PhoenixSuit工具进行烧写。将编译出来的yoc_rtos_8M.img文件选中,
按住FEL按钮,重新上电后,重启后固件开始烧录,直到完成。
如果当前镜像支持xfex模式烧写,直接在串口命令行执行“xfex"命令,再执行"reboot"命令进行重启,重启后固件开始烧录,直到完成。
3.2 命令行演示
3.2.1 示例获取docker-ubuntu18:~$ mkdir test docker-ubuntu18:~$ cd test docker-ubuntu18:~$ yoc init docker-ubuntu18:~$ yoc install sdk_app_d1 -b v7.5.1 Start to install components... sdk_app_d1 (v7.5.1), clone https://gitee.com/yocop/sdk_app_d1.git ... …… (省略) …… d1_evb (v7.5.1), clone https://gitee.com/yocop/d1_evb.git ... chip_d1 (v7.5.1), clone https://gitee.com/yocop/chip_d1.git ... Download components finish. docker-ubuntu18:~/test$ ls boards components solutions
3.2.2 编译
docker-ubuntu18:~/test/solutions$ cd lcd_demo docker-ubuntu18:~/test/solutions/lcd_demo$ make …… (省略) …… LINK out/lcd_demo/yoc.elf Generating yoc.bin riscv64-unknown-elf-objdump -d out/lcd_demo/yoc.elf > yoc.asm INSTALL yoc.elf scons: done building targets. YoC SDK Done [INFO] Create bin files [2022-02-23 16:12:38] Start to sign images with key:def_otp [2022-02-23 16:12:38] Sign [prim] with [def_otp] [2022-02-23 16:12:38] rsa verify ok.... [2022-02-23 16:12:38] Sign prim ok. ---------------------------------------------------------------- boot0, 0, 0, 0x00000000, 0x0000c000, 0x0000c000, boot0 gpt, 0, 0, 0x0000c000, 0x00004000, 0x00010000, gpt boot, 0, 0, 0x00010000, 0x00025000, 0x00035000, boot imtb, 0, 0, 0x00035000, 0x00002000, 0x00037000, imtb prim, 1, 0, 0x00037000, 0x00360000, 0x00397000, prim lfs, 0, 0, 0x00397000, 0x00452000, 0x007e9000 misc, 0, 0, 0x007e9000, 0x00013000, 0x007fc000 kv, 0, 0, 0x007fc000, 0x00004000, 0x00800000 boot0, 49152 bytes gpt, 8192 bytes boot, 143896 bytes prim, 229532 bytes imtb, 8192 bytes ---------------------------------------------------------------- Create yoc_rtos_8M.img in out directory Success!
3.2.3 下载
可以通过3.1.3的下载方式进行烧录,也可以通过命令行进行烧录。
命令行烧录之前要先连接好T-HeadDebugServer。然后在终端输入make flashall命令:
lh@lh:~/.../solutions/lcd_demo > make flashall [2022-02-15 17:52:00] I am RISC-V Program partition: boot0 address: 0x0, size 49152 byte erasing... program 00x0000c000, 100% Program partition: gpt address: 0xc000, size 8192 byte erasing... program 00x0000e000, 100% Program partition: boot address: 0x10000, size 142980 byte erasing... program 0x00032000, 100% Program partition: imtb address: 0x35000, size 8192 byte erasing... program 00x00037000, 100% Program partition: prim address: 0x37000, size 131072 byte erasing... program 00x00057000, 100% lh@lh:~/.../solutions/lcd_demo >
4. 运行
重新上电或按下RST键,系统启动,串口会有以下打印信息,表示系统运行成功。
[ 0.190]<I>[app]<app_task>app start........ [ 0.200]<I>[app]<app_task>Display screen background color testing
查看显示屏上会交错显示红、绿、蓝三色。
5. 总结
本文介绍了DOCK开发板的LCD显示示例,包括如何进行显示屏的显示,底色显示等操作,从而达到了学习显示屏显示接口的目的。原文链接:https://occ.t-head.cn/community/post/detail?spm=a2cl5.14300979.0.0.6719180fD7t7rB&id=4037257955560267776
作者@我爱下载 -
【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
1.主题
APST平台无法下载或者更新工具
2.问题背景
产品: APST
硬件:不限制
软件:APST V1.3
其他:无3.问题描述
3.1复现步骤
1.打开APST,找到需要下载或者更新的工具,点击“下载”或者“更新”按钮。
3.2具体表现
可在一号通上下载APST平台的使用指南
4.问题分析
1.APST版本过低,需要将APST平台软件的版本升级到V1.3的版本,具体下载位置在一号通–>开发工具–>windows工具下载。如下图所示。
-
APST软件损坏或者中毒,将APST卸载,然后重装。
-
AW工具ftp服务连接不上,可以在cmd下Ping toolftp.allwinnertech.com,看是否能ping通服务。
4.客户的账号和密码太久,连接的服务器数据库还是旧的,所以没法进行工具的使用。
5.根本原因
网络不稳定或者断网。6.解决办法
1.Ping toolftp.allwinnertech.com,看是否能ping通服务。
2.在可以ping通的情况,将网络稳定好或者重新连接上网络,然后重试。 -
-
【Sipeed D1 Dock Pro】YoC RTOS 实战:FOTA系统升级
1. 准备工作
建议在操作前先阅读以下技术文章:
Lichee D1 dock 开发板用户指南
Lichee D1 dock 开发板快速上手教程2. 示例介绍
本文介绍如何在D1开发板上进行FOTA升级。
FOTA 是 Firmware Over the Air 的缩写,即固件空中升级,最常见的就是手机固件的升级。固件的定义范围比较模糊,windows操作系统升级、手机升级、嵌入式系统、单片机控制程序等都的远程升级可以笼统地称为FOTA。用这种方式,设备厂商可以更加快速地进行系统软件的迭代;能够更加快速地向市场推出具有新功能的设备并以此提高用户对其设备的满意度。
下面我们以fota_demo为例来介绍如何进行FOTA升级。
3. 示例获取
3.1 通过剑池CDK(以下简称CDK)获取
双击打开CDK,点击工具栏最右侧的平头哥图标。点击新建工程按钮。在搜索栏里输入fota_demo, 在结果里选择fota_demo,点击右侧的创建工程,版本选择v7.5.1。
3.2 通过命令行获取
docker-ubuntu18:~$ mkdir test docker-ubuntu18:~$ cd test docker-ubuntu18:~$ yoc init docker-ubuntu18:~$ yoc install sdk_app_d1 -b v7.5.1 Start to install components... sdk_app_d1 (v7.5.1), clone https://gitee.com/yocop/sdk_app_d1.git ... …… (省略) …… d1_evb (v7.5.1), clone https://gitee.com/yocop/d1_evb.git ... chip_d1 (v7.5.1), clone https://gitee.com/yocop/chip_d1.git ... Download components finish. docker-ubuntu18:~/test$ ls boards components solutions
4. 示例演示
我们分成3个大的步骤进行介绍:
- 不同版本固件的生成
- OCC固件添加,升级策略配置
- 设备端配置参数,下载固件并进行系统的升级
请注意:以下出现的方案目录指的是solutions/fota_demo这个文件夹下。
4.1 编译生成不同版本固件
4.1.1 基础镜像包1.0.0生成
在示例获取那个章节我们已经把fota_demo工程拉取下来了,接下来我们要编译这个工程并生成相应的镜像包。如下图所示,点击红色方框处按钮进行编译。
编译结束之后,会在方案目录的generated下生成images.zip包。
如下所示,打开方案目录。将这个images.zip拷贝到上级目录(方案目录)并重命名为images_1.0.0.zip。
4.1.2 镜像包1.1.1版本生成
可以在方案的app/src/app_main.c文件中加个打印保存文件,并重新编译。将生成的generated/images.zip拷贝到方案目录,并重命名为images_1.1.1.zip,作为高版本。
4.2 OCC版本管理
4.2.1 创建产品,获取许可证
注册登陆平头哥芯片开放社区(OCC)后,进入到控制台中在平头哥芯片开发社区上创建产品,在控制台->我的产品->添加产品,如下图所示,点击提交按钮。
点击编辑按钮
选择许可证, 点击生成按钮,稍等刷新下即可
如下图所示,表示开发许可证已经生成完毕
4.2.2 上传镜像包至OCC服务器,并发布
在我的产品->编辑->FOTA页面选择添加固件点击提交按钮
点击红色方框
点击发布按钮
返回之后,发布状态就会变成已发布,如果状态未变,可以再次刷新下。
发布1.1.1版本
提交完毕之后,选择1.1.1版本那一行进行发布。发布成功之后,服务器就会生成FOTA固件了。
4.2.3 云端选择升级策略
如右边红色框框,添加标签:下图中的CID的值后面需要用到,请先记录下来
选择升级策略:
选择刚才写的标签,并提交:
到此为止,云端的配置基本结束了。
4.3 设备检测升级
4.3.1 将发布的镜像包下载到本地
点击红色框图位置下载镜像包到本地4.3.2 基础版本固件烧录
选择通过CDK或者全志工具进行烧录。- 通过CDK烧录
将下载过来的镜像包解压,并从hex目录取出total_image.hex文件,放到方案目录的generated目录下。
切换到CDK工程,点击红色方框处按钮开始烧录。
烧录过程中。。。
烧录完毕
- 通过全志工具烧录
比如从OCC上下载过了的压缩包名字为xx_factory.zip。在方案目录下,打开Windows的cmd环境,执行以下命令:packimg_win.bat xx_factory.zip,执行成功之后会有提示:Create yoc_rtos_xx_factory_16M.img in out directory Success!。然后通过全志烧录工具把yoc_rtos_xx_factory_16M.img进行烧录到开发板。
按住FEL按钮,重新上电后,重启后固件开始烧录,直到完成。
如果当前镜像支持xfex模式烧写,直接在串口命令行执行“xfex"命令,再执行"reboot"命令进行重启,重启后固件开始烧录,直到完成。
按复位键启动的打印如下,可以看到当前的版本为1.0.0的版本
###YoC###[Feb 15 2022,11:10:45] cpu clock is 1008000000Hz [ 0.360]<I>init app_init.c[62]: find 9 partitions [ 0.370]<I>init app_init.c[36]: filesystem init ok. [ 0.380]<D>WIFI l8723ds_devops.c[1595]: Init WLAN enable [ 0.480]<D>WIFI_IO wifi_io.c[255]: __sdio_bus_probe SD:mmc_card_create card:0x4025dec0 id:1 [ERR] SDC:__mci_irq_handler,879 raw_int:100 err! [ERR] SDC:SDC err, cmd 8, [ERR] SDC:sdc 663 abnormal status: RespErr SD:sd1.0 or mmc SD:***** Try sdio ***** [WRN] SD:card claims to support voltages below the defined range.These will be ignored. SD:sdio highspeed SD:mmc_sdio_init_card bus width type:2 SD: ============= card information ============== SD:Card Type : SDIO SD:Card Spec Ver : 1.0 SD:Card RCA : 0x0001 SD:Card OCR : 0x90ffffff SD: vol_window : 0x00ffffff SD: to_1v8_acpt : 1 SD: high_capac : 1 SD:Card CSD : SD: speed : 50000 KHz SD: cmd class : 0x0 SD: capacity : 0MB SD:Card CUR_STA : SD: speed_mode : DS: 25 MHz SD: bus_width : 2 SD: speed_class : 0 SD:============================================= SD:***** sdio init ok ***** [ 0.610]<I>app_fota cop_fota.c[165]: ver=============> 1.0.0-20220215.1431-R-d1fota [ 0.620]<I>netmgr netmgr_service.c[316]: start wifi [ 0.620]<I>app_fota cop_fota.c[166]: deviceid========> (null) [ 0.630]<D>WiFiCONF wifi_conf.c[1392]: WIFI is not running [ 0.640]<I>app_fota cop_fota.c[167]: model===========> (null) [ 0.640]<I>netmgr_wifi netmgr_wifi.c[52]: ssid{SSID_Undef}, psk{} [ 0.650]<D>fota fota.c[62]: fota: 0x40277eb8 path:flash://misc [ 0.660]<D>WiFiCONF wifi_conf.c[1392]: WIFI is not running [ 0.680]<D>WiFiCONF wifi_conf.c[1314]: Initializing WIFI ... [ 0.710]<D>WIFI_IO wifi_io.c[143]: sdio_irq_thread enter IRQ routine [ 3.270]<D>WiFiCONF wifi_conf.c[1334]: WIFI initialized [ 3.270]<D>WiFiCONF wifi_conf.c[1373]: a2dp_case_wifi_slot: 35 [ 4.610]<E>WIFI l8723ds_devops.c[570]: ERROR: STA Task, wifi connect failed! try another
4.3.3 设备端设置deivce_id/model,接收固件**
我们需要设置一些参数才能联网并且接受新的固件。
- WiFi设置
通过串口终端输入:
kv set wifi_ssid <your_wifi_ssid>
kv set wifi_psk <your_wifi_password>
your_wifi_ssid:你的wifi名字
your_wifi_password:你的wifi密码- 设备信息设置
通过串口终端输入:
kv set device_id <cid>
kv set model d1fota
cid:即4.2.3章节图片中提到的CID的值
model:即4.1章节中的产品型号值设置完毕之后按开发板上的复位键重启。
4.3.4 设备端完成固件接收,进入系统升级
请确保设备联网成功。设备检测到新版本的打印:
###YoC###[Feb 15 2022,11:10:45] cpu clock is 1008000000Hz [ 0.360]<I>init app_init.c[62]: find 9 partitions [ 0.370]<I>init app_init.c[36]: filesystem init ok. [ 0.370]<D>WIFI l8723ds_devops.c[1595]: Init WLAN enable [ 0.480]<D>WIFI_IO wifi_io.c[255]: __sdio_bus_probe SD:mmc_card_create card:0x4025dec0 id:1 [ERR] SDC:__mci_irq_handler,879 raw_int:100 err! [ERR] SDC:SDC err, cmd 8, [ERR] SDC:sdc 663 abnormal status: RespErr SD:sd1.0 or mmc SD:***** Try sdio ***** [WRN] SD:card claims to support voltages below the defined range.These will be ignored. SD:sdio highspeed SD:mmc_sdio_init_card bus width type:2 SD: ============= card information ============== SD:Card Type : SDIO SD:Card Spec Ver : 1.0 SD:Card RCA : 0x0001 SD:Card OCR : 0x90ffffff SD: vol_window : 0x00ffffff SD: to_1v8_acpt : 1 SD: high_capac : 1 SD:Card CSD : SD: speed : 50000 KHz SD: cmd class : 0x0 SD: capacity : 0MB SD:Card CUR_STA : SD: speed_mode : DS: 25 MHz SD: bus_width : 2 SD: speed_class : 0 SD:============================================= SD:***** sdio init ok ***** [ 0.610]<I>app_fota cop_fota.c[165]: ver=============> 1.0.0-20220215.1431-R-d1fota [ 0.620]<I>netmgr netmgr_service.c[316]: start wifi [ 0.620]<I>app_fota cop_fota.c[166]: deviceid========> d17dd3720440000030e8aeb4e9b0e3f2 [ 0.630]<D>WiFiCONF wifi_conf.c[1392]: WIFI is not running [ 0.640]<I>app_fota cop_fota.c[167]: model===========> d1fota [ 0.640]<I>netmgr_wifi netmgr_wifi.c[52]: ssid{TEST2}, psk{TEST1234} [ 0.650]<D>fota fota.c[62]: fota: 0x40277eb8 path:flash://misc [ 0.660]<D>WiFiCONF wifi_conf.c[1392]: WIFI is not running [ 0.680]<D>WiFiCONF wifi_conf.c[1314]: Initializing WIFI ... [ 0.710]<D>WIFI_IO wifi_io.c[143]: sdio_irq_thread enter IRQ routine [ 3.270]<D>WiFiCONF wifi_conf.c[1334]: WIFI initialized [ 3.270]<D>WiFiCONF wifi_conf.c[1373]: a2dp_case_wifi_slot: 35 [ 10.940]<E>WIFI l8723ds_devops.c[570]: ERROR: STA Task, wifi connect failed! try another [ 12.270]<D>WIFI l8723ds_devops.c[1127]: scan done! [ 13.450]<D>WIFI l8723ds_devops.c[408]: Wait for 4-way handshake [ 13.510]<D>WIFI l8723ds_devops.c[386]: @@@@@@@@@@@@@@ Connection Success @@@@@@@@@@@@@@ [ 13.520]<I>netmgr netmgr_service.c[187]: start dhcp [ 13.600]<I>netmgr netmgr_service.c[211]: IP: 172.20.10.3 [ 13.610]<I>init app_wifi.c[26]: Got IP [ 13.610]<I>init app_wifi.c[33]: NTP Start [ 13.780]<D>NTP ntp.c[194]: ntp1.aliyun.com [ 13.830]<D>NTP ntp.c[251]: NTP sec: 1644907747 usec: 824016 [ 13.840]<D>NTP ntp.c[276]: sync success [ 13.840]<I>init app_wifi.c[41]: NTP Success
开始下载数据:
[15:48:50:777][ 36.650]<D>NTP ntp.c[194]: ntp1.aliyun.com [15:48:50:830][ 36.700]<D>NTP ntp.c[251]: NTP sec: 1644911331 usec: 497327 [15:48:50:830][ 36.700]<D>NTP ntp.c[276]: sync success [15:48:50:843][ 36.710]<I>init app_wifi.c[41]: NTP Success [15:48:50:843][ 36.710]<D>fota fota.c[373]: fota do check signal........ [15:48:50:854][ 36.720]<D>fota fota.c[196]: fota_task start: flash://misc [15:48:50:854][ 36.720]<D>fota fota.c[199]: fota_task FOTA_INIT! wait...... [15:48:50:867][ 36.730]<D>app_fota cop_fota.c[61]: FOTA START :1 [15:48:50:879][ 36.740]<D>fotacop fota_cop.c[226]: check: {"cid":"d17dd3720440000030e8aeb4e9b0e3f2","model":"d1fota","version":"1.0.0-20220215.1431-R-d1fota"} [15:48:50:880][ 36.750]<D>fotacop fota_cop.c[230]: ota url:http://occ.t-head.cn/api/image/ota/pull [15:48:50:891][ 36.760]<D>fotacop fota_cop.c[237]: http client init start. [15:48:50:891][ 36.760]<D>HTTP_CLIENT http_client.c[779]: ###path:/api/image/ota/pull [15:48:50:903][ 36.770]<D>HTTP_CLIENT http_client.c[788]: New path assign = /api/image/ota/pull [15:48:50:903][ 36.780]<D>fotacop fota_cop.c[244]: http client init ok. [15:48:50:915][ 36.780]<D>HTTP_CLIENT http_client.c[1047]: Begin connect to: http://occ.t-head.cn:80 [15:48:50:963][ 36.830]<D>TRANS_TCP transport_tcp.c[83]: [sock=20],connecting to server IP:203.119.214.112,Port:80... [15:48:51:074][ 36.940]<D>fotacop fota_cop.c[68]: HTTP_EVENT_ON_CONNECTED [15:48:51:086][ 36.950]<D>HTTP_CLIENT http_client.c[1159]: Write header[6]: POST /api/image/ota/pull HTTP/1.1 [15:48:51:086]User-Agent: CK HTTP Client/1.0 [15:48:51:086]Host: occ.t-head.cn [15:48:51:086]Content-Type: application/json [15:48:51:086]Connection: keep-alive [15:48:51:096]Cache-Control: no-cache [15:48:51:096]Content-Length: 100 [15:48:51:096] [15:48:51:096] [15:48:51:096][ 36.970]<D>fotacop fota_cop.c[71]: HTTP_EVENT_HEADER_SENT [15:48:51:105][ 36.970]<D>fotacop fota_cop.c[169]: write payload ok... [15:48:51:324][ 37.190]<D>HTTP_CLIENT http_client.c[184]: on_message_begin [15:48:51:336][ 37.200]<D>HTTP_CLIENT http_client.c[226]: HEADER=Date:Tue, 15 Feb 2022 07:48:51 GMT [15:48:51:337][ 37.210]<D>HTTP_CLIENT http_client.c[226]: HEADER=Content-Type:application/json;charset=utf-8 [15:48:51:347][ 37.210]<D>HTTP_CLIENT http_client.c[226]: HEADER=Content-Length:278 [15:48:51:357][ 37.220]<D>HTTP_CLIENT http_client.c[226]: HEADER=Connection:keep-alive [15:48:51:358][ 37.230]<D>HTTP_CLIENT http_client.c[226]: HEADER=Set-Cookie:XSRF-TOKEN=b488eb10-8cda-4c58-8dea-756581a6b074; Path=/ [15:48:51:369][ 37.240]<D>HTTP_CLIENT http_client.c[226]: HEADER=X-Content-Type-Options:nosniff [15:48:51:382][ 37.240]<D>HTTP_CLIENT http_client.c[226]: HEADER=X-XSS-Protection:1; mode=block [15:48:51:392][ 37.250]<D>HTTP_CLIENT http_client.c[226]: HEADER=Cache-Control:no-cache, no-store, max-age=0, must-revalidate [15:48:51:392][ 37.260]<D>HTTP_CLIENT http_client.c[226]: HEADER=Pragma:no-cache [15:48:51:402][ 37.270]<D>HTTP_CLIENT http_client.c[226]: HEADER=Expires:0 [15:48:51:403][ 37.270]<D>HTTP_CLIENT http_client.c[226]: HEADER=X-Frame-Options:DENY [15:48:51:413][ 37.280]<D>HTTP_CLIENT http_client.c[226]: HEADER=Server:Tengine/Aserver [15:48:51:424][ 37.290]<D>HTTP_CLIENT http_client.c[226]: HEADER=EagleEye-TraceId:0b8879b916449113318991107e26a7 [15:48:51:424][ 37.290]<D>HTTP_CLIENT http_client.c[226]: HEADER=Timing-Allow-Origin:* [15:48:51:435][ 37.300]<D>HTTP_CLIENT http_client.c[244]: http_on_headers_complete, status=200, offset=495, nread=495 [15:48:51:447][ 37.310]<D>HTTP_CLIENT http_client.c[267]: http_on_message_complete, parser=0x402780c0 [15:48:51:447][ 37.320]<D>HTTP_CLIENT http_client.c[1023]: content_length = 278 [15:48:51:457][ 37.320]<D>fotacop fota_cop.c[175]: header_ret:278 [15:48:51:457][ 37.330]<D>fotacop fota_cop.c[177]: status code:200 [15:48:51:468][ 37.330]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=0, is_chunked=0, content_length=278 [15:48:51:480][ 37.340]<D>fotacop fota_cop.c[260]: resp: {"result":{"size":840248,"version":"1.1.1-20220215.1435-R-d1fota","url":"https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager","timestamp":1644906947},"msg":"","code":0,"requestId":"4014046784811765760"} [15:48:51:502][ 37.370]<D>fotacop fota_cop.c[275]: code: 0 [15:48:51:502][ 37.370]<D>fotacop fota_cop.c[294]: version: 1.1.1-20220215.1435-R-d1fota [15:48:51:514][ 37.380]<D>fotacop fota_cop.c[303]: url: https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:527][ 37.400]<I>fotacop fota_cop.c[317]: continue fota :127 [15:48:51:538][ 37.400]<D>fotacop fota_cop.c[331]: get url: https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:549][ 37.420]<D>fotacop fota_cop.c[83]: HTTP_EVENT_DISCONNECTED [15:48:51:560][ 37.420]<D>fota fota.c[90]: get image url: https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:572][ 37.440]<D>app_fota cop_fota.c[65]: FOTA VERSION CHECK :1 [15:48:51:572][ 37.440]<D>app_fota cop_fota.c[83]: {"code":0,"timestamp":0} [15:48:51:583][ 37.450]<D>fota fota.c[115]: ###fota->from_path:https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager, fota->to_path:flash://misc [15:48:51:604][ 37.470]<D>fota netio.c[36]: path:https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager delim:://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:618] [15:48:51:626][ 37.490]<D>fota-httpc httpc.c[266]: http open:https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:638][ 37.510]<D>fota netio.c[56]: open break,http [15:48:51:638] [15:48:51:638][ 37.510]<D>fota netio.c[36]: path:flash://misc delim:://misc [15:48:51:638] [15:48:51:651][ 37.520]<D>fota netio.c[56]: open break,flash [15:48:51:651] [15:48:51:658][ 37.530]<I>fota fota.c[138]: FOTA seek 0 [15:48:51:658][ 37.530]<D>fota fota.c[152]: fota prepare ok. [15:48:51:669][ 37.530]<D>fota fota.c[242]: fota_task download! wait...... [15:48:51:680][ 37.540]<D>HTTP_CLIENT http_client.c[779]: ###path:/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:691][ 37.550]<D>HTTP_CLIENT http_client.c[788]: New path assign = /image/4014025440049041408/4014028368646844416/20220215143546783_update.imager [15:48:51:702][ 37.560]<D>fota-httpc httpc.c[178]: http client init ok.[https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/image/4014025440049041408/4014028368646844416/20220215143546783_update.imager] [15:48:51:714][ 37.580]<D>fota-httpc httpc.c[179]: http read connecting........ [15:48:51:714][ 37.590]<D>fota-httpc httpc.c[193]: range:bytes=0- [15:48:51:725][ 37.590]<D>HTTP_CLIENT http_client.c[1047]: Begin connect to: https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com:443 [15:48:51:725][ 37.600]<D>tls tls.c[362]: tls init... [15:48:51:738][ 37.600]<D>tls tls.c[375]: use_host:occ-oss-prod.oss-cn-hangzhou.aliyuncs.com, port:443 [15:48:51:813][ 37.680]<D>tls tls.c[378]: _tls_net connect 0 [15:48:51:813][ 37.690]<D>tls tls.c[397]: tls connecting... [15:48:51:820][ 37.690]<D>tls tls.c[433]: handshake in progress... [15:48:51:995][ 37.860]<D>tls tls.c[477]: open new connection ok [15:48:51:995][ 37.870]<D>fota-httpc httpc.c[29]: HTTP_EVENT_ON_CONNECTED [15:48:52:006][ 37.870]<D>HTTP_CLIENT http_client.c[1159]: Write header[6]: GET /image/4014025440049041408/4014028368646844416/20220215143546783_update.imager HTTP/1.1 [15:48:52:017]User-Agent: CK HTTP Client/1.0 [15:48:52:017]Host: occ-oss-prod.oss-cn-hangzhou.aliyuncs.com [15:48:52:017]Range: bytes=0- [15:48:52:028]Connection: keep-alive [15:48:52:028]Cache-Control: no-cache [15:48:52:028]Content-Length: 0 [15:48:52:028] [15:48:52:028] [15:48:52:028][ 37.900]<D>fota-httpc httpc.c[32]: HTTP_EVENT_HEADER_SENT [15:48:52:038][ 37.910]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:52:167][ 38.040]<D>tls tls.c[81]: tls read... [15:48:52:229][ 38.100]<D>HTTP_CLIENT http_client.c[184]: on_message_begin [15:48:52:241][ 38.100]<D>HTTP_CLIENT http_client.c[226]: HEADER=Server:AliyunOSS [15:48:52:241][ 38.110]<D>HTTP_CLIENT http_client.c[226]: HEADER=Date:Tue, 15 Feb 2022 07:48:52 GMT [15:48:52:252][ 38.120]<D>HTTP_CLIENT http_client.c[226]: HEADER=Content-Type:application/octet-stream [15:48:52:252][ 38.120]<D>HTTP_CLIENT http_client.c[226]: HEADER=Content-Length:840248 [15:48:52:263][ 38.130]<D>HTTP_CLIENT http_client.c[226]: HEADER=Connection:keep-alive [15:48:52:274][ 38.140]<D>HTTP_CLIENT http_client.c[226]: HEADER=x-oss-request-id:620B5AE494C77F3432880A98 [15:48:52:274][ 38.150]<D>HTTP_CLIENT http_client.c[226]: HEADER=Content-Range:bytes 0-840247/840248 [15:48:52:285][ 38.150]<D>HTTP_CLIENT http_client.c[226]: HEADER=Accept-Ranges:bytes [15:48:52:296][ 38.160]<D>HTTP_CLIENT http_client.c[226]: HEADER=ETag:"2D8F0BBB41F7090E7808A48F1152A303" [15:48:52:307][ 38.170]<D>HTTP_CLIENT http_client.c[226]: HEADER=Last-Modified:Tue, 15 Feb 2022 06:35:47 GMT [15:48:52:307][ 38.180]<D>HTTP_CLIENT http_client.c[226]: HEADER=x-oss-object-type:Normal [15:48:52:319][ 38.180]<D>HTTP_CLIENT http_client.c[226]: HEADER=x-oss-hash-crc64ecma:11891437651563570590 [15:48:52:319][ 38.190]<D>HTTP_CLIENT http_client.c[226]: HEADER=x-oss-storage-class:Standard [15:48:52:330][ 38.200]<D>HTTP_CLIENT http_client.c[226]: HEADER=Content-MD5:LY8Lu0H3CQ54CKSPEVKjAw== [15:48:52:340][ 38.210]<D>HTTP_CLIENT http_client.c[226]: HEADER=x-oss-server-time:54 [15:48:52:352][ 38.210]<D>HTTP_CLIENT http_client.c[244]: http_on_headers_complete, status=206, offset=532, nread=532 [15:48:52:352][ 38.220]<D>HTTP_CLIENT http_client.c[1023]: content_length = 840248 [15:48:52:367][ 38.230]<D>fota-httpc httpc.c[130]: header_ret:840248 [15:48:52:367][ 38.230]<D>fota-httpc httpc.c[132]: status code:206 [15:48:52:367][ 38.240]<D>fota-httpc httpc.c[227]: range_len: 840248 [15:48:52:374][ 38.240]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:52:386][ 38.250]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:52:386][ 38.250]<D>tls tls.c[81]: tls read... [15:48:52:397][ 38.260]<D>HTTP_CLIENT http_client.c[883]: need_read=532, byte_to_read=532, rlen=532, ridx=15852 [15:48:52:397][ 38.270]<D>fota fota.c[250]: fota_task FOTA_DOWNLOAD! total:840248 offset:0 [15:48:52:405][ 38.270]<D>fota fota.c[251]: ##read: 16384 [15:48:52:585][ 38.450]<I>fota fota.c[312]: write size: 16384 [15:48:52:585][ 38.460]<D>app_fota cop_fota.c[90]: FOTA PROGRESS :2, 16384, 840248 [15:48:52:599][ 38.460]<D>app_fota cop_fota.c[131]: {"code":0,"total_size":840248,"cur_size":16384,"percent":1,"speed":0} [15:48:52:606][ 38.470]<D>fota fota.c[242]: fota_task download! wait...... [15:48:52:617][ 38.480]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:52:617][ 38.490]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:52:628][ 38.490]<D>TRANS_SSL transport_ssl.c[91]: remain data in cache, need to read again [15:48:52:629][ 38.500]<D>tls tls.c[81]: tls read... [15:48:52:639][ 38.500]<D>HTTP_CLIENT http_client.c[883]: need_read=16384, byte_to_read=16384, rlen=15852, ridx=0 [15:48:52:651][ 38.510]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:52:652][ 38.520]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:52:652][ 38.520]<D>tls tls.c[81]: tls read... [15:48:52:667][ 38.530]<D>HTTP_CLIENT http_client.c[883]: need_read=532, byte_to_read=532, rlen=532, ridx=15852 [15:48:52:672][ 38.540]<D>fota fota.c[250]: fota_task FOTA_DOWNLOAD! total:840248 offset:16384 [15:48:52:672][ 38.540]<D>fota fota.c[251]: ##read: 16384 [15:48:52:857][ 38.720]<I>fota fota.c[312]: write size: 16384 [15:48:52:857][ 38.730]<D>app_fota cop_fota.c[90]: FOTA PROGRESS :2, 32768, 840248 [15:48:52:869][ 38.740]<D>app_fota cop_fota.c[116]: interval time: 280 ms [15:48:52:880][ 38.740]<D>app_fota cop_fota.c[131]: {"code":0,"total_size":840248,"cur_size":32768,"percent":3,"speed":57} [15:48:52:880][ 38.750]<D>fota fota.c[242]: fota_task download! wait...... [15:48:52:890][ 38.760]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:52:890][ 38.760]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:52:901][ 38.770]<D>TRANS_SSL transport_ssl.c[91]: remain data in cache, need to read again [15:48:52:901][ 38.780]<D>tls tls.c[81]: tls read... [15:48:52:913][ 38.780]<D>HTTP_CLIENT http_client.c[883]: need_read=16384, byte_to_read=16384, rlen=15852, ridx=0 [15:48:52:923][ 38.790]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:52:923][ 38.800]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:52:935][ 38.800]<D>tls tls.c[81]: tls read... [15:48:52:946][ 38.810]<D>HTTP_CLIENT http_client.c[883]: need_read=532, byte_to_read=532, rlen=532, ridx=15852 [15:48:52:946][ 38.820]<D>fota fota.c[250]: fota_task FOTA_DOWNLOAD! total:840248 offset:32768 [15:48:52:952][ 38.820]<D>fota fota.c[251]: ##read: 16384 [15:48:53:134][ 39.000]<I>fota fota.c[312]: write size: 16384 [15:48:53:134][ 39.010]<D>app_fota cop_fota.c[90]: FOTA PROGRESS :2, 49152, 840248 [15:48:53:145][ 39.010]<D>app_fota cop_fota.c[116]: interval time: 270 ms [15:48:53:156][ 39.020]<D>app_fota cop_fota.c[131]: {"code":0,"total_size":840248,"cur_size":49152,"percent":5,"speed":59} [15:48:53:156][ 39.030]<D>fota fota.c[242]: fota_task download! wait...... [15:48:53:166][ 39.030]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:53:167][ 39.040]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:53:178][ 39.050]<D>TRANS_SSL transport_ssl.c[91]: remain data in cache, need to read again [15:48:53:179][ 39.050]<D>tls tls.c[81]: tls read... [15:48:53:189][ 39.060]<D>HTTP_CLIENT http_client.c[883]: need_read=16384, byte_to_read=16384, rlen=15852, ridx=0 [15:48:53:200][ 39.070]<D>HTTP_CLIENT http_client.c[873]: is_data_remain=1, is_chunked=0, content_length=840248 [15:48:53:200][ 39.070]<D>TRANS_SSL transport_ssl.c[159]: ssl read... [15:48:53:212][ 39.080]<D>tls tls.c[81]: tls read... [15:48:53:223][ 39.080]<D>HTTP_CLIENT http_client.c[883]: need_read=532, byte_to_read=532, rlen=532, ridx=15852 [15:48:53:223][ 39.090]<D>fota fota.c[250]: fota_task FOTA_DOWNLOAD! total:840248 offset:49152 [15:48:53:229][ 39.100]<D>fota fota.c[251]: ##read: 16384 [15:48:53:414][ 39.280]<I>fota fota.c[312]: write size: 16384 [15:48:53:414][ 39.290]<D>app_fota cop_fota.c[90]: FOTA PROGRESS :2, 65536, 840248 [15:48:53:425][ 39.290]<D>app_fota cop_fota.c[116]: interval time: 280 ms [15:48:53:436][ 39.300]<D>app_fota cop_fota.c[131]: {"code":0,"total_size":840248,"cur_size":65536,"percent":7,"speed":57} [15:48:53:436][ 39.310]<D>fota fota.c[242]: fota_task download! wait......
下载检验完毕,设备自动重启进入系统升级:
[15:49:06:789][ 52.650]<D>app_fota cop_fota.c[131]: {"code":0,"total_size":840248,"cur_size":840248,"percent":100,"speed":23} [15:49:06:789][ 52.660]<D>fota fota.c[242]: fota_task download! wait...... [15:49:06:799][ 52.660]<W>fota-httpc httpc.c[238]: http_read done: offset:840248 tsize:840248 [15:49:06:811][ 52.670]<D>fota fota.c[250]: fota_task FOTA_DOWNLOAD! total:840248 offset:840248 [15:49:06:811][ 52.680]<D>fota fota.c[251]: ##read: 0 [15:49:06:811][ 52.680]<D>fota fota.c[268]: read size 0. [15:49:06:821][ 52.690]<D>app_fota cop_fota.c[141]: FOTA VERIFY :2 [15:49:06:822][ 52.690]<D>fotav fota_verify.c[76]: start fota verify... [15:49:06:832][ 52.700]<D>fotav fota_verify.c[128]: image_size:839836 [15:49:06:832][ 52.700]<D>fotav fota_verify.c[129]: digest_type:1 [15:49:06:833][ 52.710]<D>fotav fota_verify.c[130]: sign_type:1 [15:49:06:844][ 52.710]<D>fotav fota_verify.c[131]: hash_len:20 [15:49:06:844][ 52.710]<D>fotav fota_verify.c[132]: signature_len:128 [15:49:06:856][ 52.720]<D>fotav fota_verify.c[133]: signature_offset:848028 [15:49:06:856][ 52.720]<D>fotav fota_verify.c[134]: hash_offset:848284 [15:49:07:002][ 52.870]<I>fotav fota_verify.c[189]: ###fota data hash v ok. [15:49:07:003][ 52.880]<D>fota fota.c[174]: fota_release,174 [15:49:07:014][ 52.880]<D>fota-httpc httpc.c[144]: httpc cleanup... [15:49:07:014][ 52.890]<D>fota-httpc httpc.c[44]: HTTP_EVENT_DISCONNECTED [15:49:07:026][ 52.890]<D>fota fota.c[289]: fota data verify ok. [15:49:07:026][ 52.900]<D>app_fota cop_fota.c[144]: FOTA FINISH :4 [15:49:08:075][34]HELLO! BOOT0 is starting![Sep 18 2021, 11:27:51] [15:49:08:075][39]BOOT0 commit : 3b45046 [15:49:08:076][42]set pll start [15:49:08:087][44]periph0 has been enabled [15:49:08:087][47]set pll end [15:49:08:087][48][pmu]: bus read error [15:49:08:087][50]board init ok [15:49:08:087][52]enable_jtag [15:49:08:087][54]DRAM only have internal ZQ!! [15:49:08:109][57]get_pmu_exist() = -1 [15:49:08:109][59]ddr_efuse_type: 0x0 [15:49:08:109][62][AUTO DEBUG] single rank and full DQ! [15:49:08:109][66]ddr_efuse_type: 0x0 [15:49:08:112][69][AUTO DEBUG] rank 0 row = 15 [15:49:08:112][72][AUTO DEBUG] rank 0 bank = 8 [15:49:08:112][75][AUTO DEBUG] rank 0 page size = 2 KB [15:49:08:112][79]DRAM BOOT DRIVE INFO: V0.24 [15:49:08:123][82]DRAM CLK = 792 MHz [15:49:08:123][84]DRAM Type = 3 (2:DDR2,3:DDR3) [15:49:08:123][87]DRAMC ZQ value: 0x7b7bfb [15:49:08:123][90]DRAM ODT value: 0x42. [15:49:08:137][93]ddr_efuse_type: 0x0 [15:49:08:137][95]DRAM SIZE =512 M [15:49:08:137][99]DRAM simple test OK. [15:49:08:137][101]dram size =512 [15:49:08:137][103]spinor id is: ef 40 18, read cmd: 0b [15:49:08:148][107]Succeed in reading toc file head. [15:49:08:148][110]The size of toc is cc000. [15:49:08:283][247]start to copy bootloader. [15:49:08:322][281]copy bootloader over. [15:49:08:322][284]Entry_name = melis-lz4 [15:49:08:322][287]Entry_data_offset = 0x400 [15:49:08:335][290]Entry_data_len = 0xc9011 [15:49:08:335][293]run_addr = 0x0 [15:49:08:335][295]image_base = 0x37cd8189 [15:49:08:335][299]come to LZ4 decompress. [15:49:08:351][308]LZ4 decompress ok. [15:49:08:351][310]Jump to second Boot. [15:49:08:351][313]jump to bootloader,[0x40000000] [15:49:08:365] [15:49:08:365]Welcome boot2.0! [15:49:08:365]build: Feb 15 2022 15:41:15 [15:49:08:366]cpu clock is 1008000000Hz [15:49:09:054][boot][I] fota data hash verify ok [15:49:09:055][boot][I] start to upgrade [15:49:09:180][boot][I] fd:0x40025b20,fd_num:0 [15:49:09:251][boot][I] start FULL update [15:49:25:111][boot][I] fd:0x40025b20,fd_num:0 [15:49:25:184][boot][I] fd:0x40025b20,fd_num:0 [15:49:25:338][boot][I] suc update ^_^
4.3.5 系统升级完毕,设备重启,正常运行
系统升级完毕之后,设备自动重启,可以看到刚才加的打印信息出来了,版本号也更新了。[15:49:35:597]###YoC###[Feb 15 2022,11:10:45] [15:49:35:597]cpu clock is 1008000000Hz [15:49:35:598][ 0.360]<I>init app_init.c[62]: find 9 partitions [15:49:35:613][ 0.370]<I>init app_init.c[36]: filesystem init ok. [15:49:35:613][ 0.370]<D>WIFI l8723ds_devops.c[1595]: Init WLAN enable [15:49:35:613] [15:49:35:714][ 0.480]<D>WIFI_IO wifi_io.c[255]: __sdio_bus_probe [15:49:35:714]SD:mmc_card_create card:0x4025dec0 id:1 [15:49:35:774][ERR] SDC:__mci_irq_handler,879 raw_int:100 err! [15:49:35:774][ERR] SDC:SDC err, cmd 8, [ERR] SDC:sdc 663 abnormal status: RespErr [15:49:35:787]SD:sd1.0 or mmc [15:49:35:787]SD:***** Try sdio ***** [15:49:35:787][WRN] SD:card claims to support voltages below the defined range.These will be ignored. [15:49:35:796]SD:sdio highspeed [15:49:35:796]SD:mmc_sdio_init_card bus width type:2 [15:49:35:797]SD: [15:49:35:797]============= card information ============== [15:49:35:797]SD:Card Type : SDIO [15:49:35:808]SD:Card Spec Ver : 1.0 [15:49:35:808]SD:Card RCA : 0x0001 [15:49:35:808]SD:Card OCR : 0x90ffffff [15:49:35:808]SD: vol_window : 0x00ffffff [15:49:35:819]SD: to_1v8_acpt : 1 [15:49:35:819]SD: high_capac : 1 [15:49:35:819]SD:Card CSD : [15:49:35:820]SD: speed : 50000 KHz [15:49:35:820]SD: cmd class : 0x0 [15:49:35:820]SD: capacity : 0MB [15:49:35:830]SD:Card CUR_STA : [15:49:35:830]SD: speed_mode : DS: 25 MHz [15:49:35:830]SD: bus_width : 2 [15:49:35:830]SD: speed_class : 0 [15:49:35:845]SD:============================================= [15:49:35:845]SD:***** sdio init ok ***** [15:49:35:846]I am fota test..mg[ 0.610]<I>netmgr netmgr_service.c[316]: start wifi [15:49:35:857][ 0.620]<I>app_fota cop_fota.c[165]: ver=============> 1.1.1-20220215.1435-R-d1fota [15:49:35:869][ 0.620]<D>WiFiCONF wifi_conf.c[1392]: WIFI is not running [15:49:35:869][ 0.630]<I>app_fota cop_fota.c[166]: deviceid========> d17dd3720440000030e8aeb4e9b0e3f2 [15:49:35:888][ 0.640]<I>netmgr_wifi netmgr_wifi.c[52]: ssid{TEST2}, psk{TEST1234} [15:49:35:888] [15:49:35:888][ 0.640]<I>app_fota cop_fota.c[167]: model===========> d1fota [15:49:35:895][ 0.650]<D>WiFiCONF wifi_conf.c[1392]: WIFI is not running [15:49:35:896][ 0.660]<D>fota fota.c[62]: fota: 0x402753f8 path:flash://misc [15:49:35:914][ 0.680]<D>WiFiCONF wifi_conf.c[1314]: Initializing WIFI ... [15:49:35:948][ 0.710]<D>WIFI_IO wifi_io.c[143]: sdio_irq_thread enter IRQ routine [15:49:38:512][ 3.270]<D>WiFiCONF wifi_conf.c[1334]: WIFI initialized [15:49:38:512] [15:49:38:512][ 3.270]<D>WiFiCONF wifi_conf.c[1373]: a2dp_case_wifi_slot: 35 [15:49:39:216][ 3.980]<D>WIFI l8723ds_devops.c[559]: @@@@@@@@@@@@@@ Connection Success @@@@@@@@@@@@@@ [15:49:39:216] [15:49:39:223][ 3.990]<I>netmgr netmgr_service.c[187]: start dhcp [15:49:39:265][ 4.030]<I>netmgr netmgr_service.c[211]: IP: 172.20.10.3 [15:49:39:265][ 4.030]<I>init app_wifi.c[26]: Got IP [15:49:39:273][ 4.030]<I>init app_wifi.c[33]: NTP Start [15:49:39:287][ 4.050]<D>NTP ntp.c[194]: ntp1.aliyun.com [15:49:39:397][ 4.160]<D>NTP ntp.c[251]: NTP sec: 1644911380 usec: 91886 [15:49:39:398][ 4.160]<D>NTP ntp.c[276]: sync success [15:49:39:410][ 4.170]<I>init app_wifi.c[41]: NTP Success
至此,说明整个FOTA升级已经成功完成了。
5. 注意事项
5.1 分区配置
使用升级功能需要指定升级包的存储区域。 配置文件:configs/config.yamlmtb_version: 4 chip: d1 diff: fota_version: 0 ram_buf: 50 #DEC KB ( max ram need) flash_buf: 16 #DEC KB ( buffer size) flash_sector: 4096 #DEC byte ( flash sector) diff_mode: 010 #BIN double_control: 1 flash: base_address: 0 # 存储基地址,比如FLASH的基地址 sector: 4096 # Bytes size: 16777216 # 4096 $(sector count) * sector partitions: - { name: boot0, address: 0x000000, size: 0x00C000 } # don't touch - { name: gpt, address: 0x00C000, size: 0x004000 } # don't touch - { name: boot, address: 0x010000, size: 0x025000 } - { name: imtb, address: 0x035000, size: 0x002000 } - { name: prim, address: 0x037000, size: 0x400000, verify: true, update: FULL } - { name: lfs, address: 0x437000, size: 0x700000 } - { name: misc, address: 0xB37000, size: 0x410000 } - { name: kv, address: 0xF47000, size: 0x004000 } - { name: kp, address: 0xF4B000, size: 0x001000 }
对需要升级的分区加上update字段。
- 分区说明
6. 总结
以上即为整个FOTA升级示例详细的操作说明。有关FOTA升级相关的代码主要是在app/src/cop_fota.c文件中。实际应用过程中需要做好版本的管理。
原文链接:https://occ.t-head.cn/community/post/detail?spm=a2cl5.14300979.0.0.6719180frS8Xlm&id=4037225231922827264
作者 @ 未来开发者 -
技术流直播即将开始!“晕哥”带你玩转“玄铁杯”参赛开发板全志哪吒D1-H开发板
- 讲座时间:2022-05-27 14:00 - 15:00
- 报名方式:扫描海报二维码进入钉钉报名直播讲座
- 主讲嘉宾:贺兴哇 -酷网主理人-全志在线开发者社区高级顾问
- 讲座亮点:D1-H哪吒开发板相关介绍
主讲嘉宾 贺兴
哇酷网主理人
全志在线开发者社区高级顾问东莞市哇酷科技有限公司创始人贺兴,人称晕哥,拥有个人管理的开发者社区——Whycan Forum(哇酷开发者社区),主要讨论全志Soc,因此同时也被全志在线开发者社区聘为高级顾问,晕哥十分热爱开源项目的分享,已就全志D1-H/D1s等芯片开源了不少优质项目,吸引了一大批开发者慕名而来。
赛事详情
2022“玄铁杯”RISC-V应用创新大赛正式启动,本次大赛分设“碳中和”、工业控制及机器人、视觉及可穿戴设备、智慧家居4条赛道,免费开放“云上实验室”助理参赛者探索开发不同领域的创新应用,感受RISC-V“算力自由”
本次“玄铁杯”第二届RISC-V应用创新大赛即将于5月31日结束创意方案提交的阶段,目前已有1000+开发者基于全志D1-H哪吒和Sipeed Lichee D1-H DocK Pro两款开发板提交了300+份创意方案,方案从Linux和RTOS两种类型的操作系统出发,衍生出工业机器人、视觉及可穿戴设备、社区养老以及车载设备等时下最火热的赛道方案,赛事火爆程度可见一斑,开发者们赶快抓紧最后几天的上车机会,扫描参赛二维码,提交自己的创意方案。
-
【硬科技·芯物种】D1-H系列产品助力“玄铁杯”第二届RISC-V应用创新大赛火热进行
由芯片开放社区(OCC)主办的“玄铁杯”第二届RISC-V应用创新大赛即将于5月31日结束创意方案提交的阶段,目前已有1000+开发者基于全志D1-H哪吒和Sipeed Lichee D1-H DocK Pro两款开发板提交了300+份创意方案,方案从Linux和RTOS两种类型的操作系统出发,衍生出工业机器人、视觉及可穿戴设备、社区养老以及车载设备等时下最火热的赛道方案,赛事火爆程度可见一斑,开发者们赶快抓紧最后几天的上车机会,扫描参赛二维码,提交自己的创意方案。
赛事设备
本届RISC-V应用创新大赛的赛事设备指定了两款基于全志D1-H芯片的开发板全志D1-H哪吒和Sipeed Lichee D1-H DocK Pro,D1-H芯片搭载了平头哥C906核心,也是由全志和阿里平头哥联合开发的首款平头哥C906芯片,D1-H哪吒开发板更是全球首款支持64bit RISC-V指令集并支持Linux系统的可量产开发板。
全志D1-H哪吒
Sipeed Lichee D1-H DocK Pro
配套资源
赛事方准备了针对赛事选用的两款开发板的介绍直播,旨在为参赛者提供更佳的赛事技术支持,让参赛者有机会深入了解开发板,扫描二维码即可在对应时间参加对应的直播活动。
全志在线开发者社区为使用D1-H芯片系列开发板的开发者准备了充足的学习资料及芯片资源,包括在线文档、资源下载以及论坛中已经发布的很多开源项目。
全志在线开发者论坛:
https://bbs.aw-ol.com/D1-H在线文档:
https://d1.docs.aw-ol.com/D1-H资料下载:
https://www.aw-ol.com/downloads -
【FAQ】全志XR系列 设置音频结构体HttpStreamBufferConfig成员有什么意义?
问题背景
有客户放映在播放网络音频时经常在最后时返回recv err(104),但是通过修改HttpStreamBufferConfig成员的大小偶尔又不会出现。问题描述
为什么XRMCU播放网络音频时会出现recv err(104)?为什么修改HttpStreamBufferConfig又可以令异常消失?问题分析
HttpStreamBufferConfig结构体的成员如下:typedef struct HttpStreamBufferConfig { int maxBufferSize; int maxProtectAreaSize; int thresholdSize; int seekIgnoreThresholdSize; } HttpStreamBufferConfig;
XRMCU在播放网络音频时,通过lwip和服务器建立连接请求数据,其中maxBufferSize就是能接收到的最大缓存数据,缓存区满了之后,需要等待播放器播放消耗掉才能继续接收数据。
maxProtectAreaSize,考虑到播放器可能向前seek。如果数据已经丢弃,就必须重新连接服务器获取数据,效率较低,所以设置了maxProtectAreaSize,即使播放了数据依旧不会马上丢弃,而是继续保留在maxBufferSize定义的缓存区中,直到超出了maxProtectAreaSize。
thresholdSize,音频解码时必须是一帧完整的帧,否则会引起解码器异常,所以所有音视频解码器都是必须缓存够一段数据后才开始解码,这个变量的意思是数据缓存必须大于这个值才进行解码。
当往前seek的时候,我们可以采取两种方式来实现seek。一种是重新和服务器进行connect协商,一种是我们继续正常read数据,但把read的无效数据直接丢弃,直到read到需要的数据。这个seekIgnoreThresholdSize的含义就是,如果seek的距离小于这个值,则我们采用丢数据的方式;如果seek的距离大于这个值,则采用重新connect的方式。
至于出现的recv err(104),通过抓包数据分析,原因为服务器下发数据的速度过快了,导致maxBufferSize大小的缓冲区不够而停止接收,而这段时间服务器依旧往下发,并且在发送完成后主动关闭了连接,导致cedarx再次去申请数据时无法申请,所以出现了recv err(104)。
问题解决由于播放不能加快,如果服务器端不能修改数据下发速度,则只能通过修改maxBufferSize的方式扩大缓存,确保不会丢数据。
如果没有需求,则可以减少maxProtectAreaSize的数值。
-
眇视万里一毫端!全志V853三核异构边缘AI视觉处理芯片全新发布
V853 是一颗面向智能视觉领域推出的新一代高性能、低功耗的处理器SOC,芯片采用三核异构设计,同时搭配了全志自研的新一代视觉处理引擎和疾风系统,最高算力可达1T的NPU助力V853可广泛用于智能门锁、智能考勤门禁、网络摄像头、行车记录仪、智能台灯等智能化升级相关行业。
三核异构协同
V853采用高效能的三核异构设计,是由ARM Corte-A7主核、RISC-V 协处理核与AI NPU结合的创新型架构。ARM Corte- A7主核可适应于兼容各类应用开发调试,RISC-V 协处理核重点负责各类传感器场景,提高实时响应速度,AI NPU则专注解决复杂多变的视觉检测识别场景,其检测识别帧率相较于传统CPU方式可最少提升20倍以上,解决了传统单核面对复杂视觉场景体验不佳的痛点问题。
同时,V853在关键模块上进行了低功耗设计和系统层级的优化,实现了每百Gflops算力仅需20mW的优秀功耗表现,3K AI视觉典型解决方案整体能耗小于500mW,结合全志科技全新一代μA级保活待机WiFi芯片XR806,满足用户对低功耗AI产品长时间续航的体验需求。
多目星光眼擎
针对智能视觉核心多角度多深度等应用场景,V853 集成了全志科技最新一代视觉处理引擎,支持多目接入,可同步处理最高“1+4”五路输入,芯片最高支持5M@25fps H.265编码和5M@25fps H.264编解码,同时也支持 4lane MIPI-CSI/DVP/MIPI-DSI/RGB 等丰富的专用视频输入输出接口,搭配上集成的高性能 ISP 图像处理器,可为客户在目标场景下提供专业级图像质量,以满足各类AI视觉产品需求。
疾风系统
V853内置64MB DDR的极小封装,除此之外还支持 16-bit DDR3/DDR3L的外接,配合芯片集成的增强在线通路技术,最高可减少40%~50%的视频处理内存占用以及编码延迟问题,以满足各类产品高带宽需求,与此同时三核异构、软硬协同以及采用先进22nm工艺技术,使得V853芯片具有更小的面积、更强的性能以及更优的功耗
基本参数规格:
CPU
- Arm Cortex-A7 CPU core @ 1 GHz ,32 KB I-cache, 32 KB D-cache,and 128 KB L2 cache
- Xuantie E907 RISC-V core @ 600MHz,16 KB I-cache and 16 KB D-cache
NPU
- 最高1T算力
- 内置128KB buffer
- 支持TensorFlow, Caffe, Tflite, Pytorch, ONNX等深度学习框架
Memory
- 外接最高1GB SPI
- SD3.0/eMMC5.1接口
- SPI Nor/SPI Nand Flash接口
Video Engine
- 4K@15fps H.264/H.265 编码
- 5M@25fps H.264/H.265 编码
- 1080p@60fps JPEG 编码
- H.264 BP/MP/HP, JPEG 解码
- 5M@25fps H.264实时多码流 解码
- 1080P@60fps JPEG 解码
Display Engine
- 全志自研Smart视频引擎
- 2个视频通道和1个UI通道
- G2D
Video Input
- ISP
- VIPP
- 8-/10-/12-/16-bit paraller CSI
- 4lane MIPI-CSI/DVP/MIPI-DSI/RGB
Video Out
- 1920 x 1080@60fps RGB LCD
- 1920 x 1200@60fps 4lane MIPI DSI
Connectivity
- USB2.0 DRD, SDIO 3.0, SPI *4
- UART * 4, TWI * 5, WIEGAND
- PWM (12-ch), GPADC (4-ch)
- 10/100/1000M EMAC with RMII/RGMII
-
LVGL日历控件和显示天气
利用TCP封装HTTP包请求天气信息
Linux还真是逐步熟悉中,现在才了解到Linux即没有原生的GUI,也没有应用层协议栈,所以要实现HTTP应用,必须利用TCP然后自己封装HTTP数据包。本篇即记录封装HTTP数据包,到心知天气请求天气信息的案例实现过程。
1、心知天气API说明
心知天气应该是当下国内使用很普遍的一个天气数据站点。相关注册和使用过程,这里就不再啰嗦了,不清楚的朋友可以自己到官网上查看(https://www.seniverse.com/)。本例仅测试实时天气数据获取,天气相关数据只有“状态(晴朗之类)”和“气温”,请求接口地址如下:
可以看到请求地址给的是域名,TCP连接需要直接给IP地址,所以用ping来获取其IP为“116.62.81.138”,端口自然是80。
得到IP地址后,先不着急编程,通过网络助手实验一把,具体过程是:选择TCP Client,连接对方IP和端口(116.62.81.138:80),然后将请求地址前加上方法字串“GET”,结尾还要有两个回车换行“\r\n\r\n”。初次测试时,忘记了回车换行符没有成功,加上后就好了。
封装好的数据包是:“GET https://api.thinkpage.cn/v3/weather/now.json?key=yourkey&location=tianjin&language=en&unit=c\r\n\r\n”。
2、JSON分析
请求到的数据是JSON格式,贴到Json.cn(https://www.json.cn/)的在线工具里,可以更清晰的看到其结构。可以看到请求实时数据(now.json),得到一个JSON对象,包含一个“results”引导的JSON数组,且数组只有一个元素,元素中又包含“location”、“now”和“last_update”三个JSON对象,内部还有键值对。
既然是开发Linux API的C程序,当然利用cJSON库来帮助进行数据解析了。本人使用的库是从网上搜到的一个百度网盘分享。
链接:https://pan.baidu.com/s/1DQynsdlNyIvsVXmf4W5b8Q
提取码:ww4z3、请求天气案例
具体思路就是建立TCP Client连接心知天气的Server,然后发送请求包,得到响应包,解析并打印出结果,案例比较简单做成单次的——开启即运行到底,代码如下:#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include "cJSON.h" #define SERVER_IP "116.62.81.138" #define SERVER_PORT 80 #define NOW "now.json" #define DAILY "daily.json" #define API_KEY "SK0LJ8FI2TP0L-IsQ" #define CITY "tianjin" #define REQ_PACK "GET https://api.thinkpage.cn/v3/weather/%s?key=%s&location=%s&language=en&unit=c\r\n\r\n" #define N 1024 #define errlog(errmsg) do{ perror(errmsg);\ printf("----%s----%s----%d----\n", __FILE__, __func__, __LINE__);\ return -1;\ } while(0) //struct for weather data typedef struct { char id[16]; char name[32]; char country[16]; char path[64]; char timezone[32]; char tz_offset[16]; char text[16]; char code[4]; char temp[8]; char last_update[32]; } weather_t; //parse function & print weather_t data function void aita_ParseJsonNow(char *json, weather_t *w); void aita_PrintWeather(weather_t *w); int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in serveraddr; socklen_t addrlen = sizeof(serveraddr); char sendbuf[N] = ""; char recvbuf[N] = ""; weather_t weather = {0}; //create socket if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { errlog("socket error"); } //connect to server of seniverse.com serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(SERVER_IP); serveraddr.sin_port = htons(SERVER_PORT); if((connect(sockfd, (struct sockaddr*)&serveraddr, addrlen)) < 0) { errlog("connect error"); } //build & send request package sprintf(sendbuf, REQ_PACK, NOW, API_KEY, CITY); if(send(sockfd, sendbuf, N, 0) < 0) { errlog("send error"); } //waiting server response if(recv(sockfd, recvbuf, N, 0) < 0) { errlog("recv error"); } printf("recv: %s\n", recvbuf); //parse & print data aita_ParseJsonNow(recvbuf, &weather); aita_PrintWeather(&weather); close(sockfd); return 0; } void aita_ParseJsonNow(char *msg, weather_t *w) { cJSON *json, *ja, *jo, *josub, *item; json = cJSON_Parse(msg); //parse string to cJSON type if(json == NULL) { printf("json type cast error: %s", cJSON_GetErrorPtr()); return; } else { printf("parse now pack\n"); if((ja=cJSON_GetObjectItem(json, "results")) != NULL) { //get results array if((jo=cJSON_GetArrayItem(ja, 0)) != NULL) { //get array[0](the only item) //get location object if((josub=cJSON_GetObjectItem(jo, "location")) != NULL) { if((item=cJSON_GetObjectItem(josub, "id")) != NULL) { memcpy(w->id, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "name")) != NULL) { memcpy(w->name, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "country")) != NULL) { memcpy(w->country, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "path")) != NULL) { memcpy(w->path, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "timezone")) != NULL) { memcpy(w->timezone, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "timezone_offset")) != NULL) { memcpy(w->tz_offset, item->valuestring, strlen(item->valuestring)); } } //get now object if((josub=cJSON_GetObjectItem(jo, "now")) != NULL) { if((item=cJSON_GetObjectItem(josub, "text")) != NULL) { memcpy(w->text, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "code")) != NULL) { memcpy(w->code, item->valuestring, strlen(item->valuestring)); } if((item=cJSON_GetObjectItem(josub, "temperature")) != NULL) { memcpy(w->temp, item->valuestring, strlen(item->valuestring)); } } //get last_update object if((josub=cJSON_GetObjectItem(jo, "last_update")) != NULL) { memcpy(w->last_update, josub->valuestring, strlen(josub->valuestring)); } } } } //delete original json pack free memory cJSON_Delete(json); return; } void aita_PrintWeather(weather_t *w) { printf("id: %s\n", w->id); printf("name: %s\n", w->name); printf("country: %s\n", w->country); printf("path: %s\n", w->path); printf("timezone: %s\n", w->timezone); printf("timezone_offset: %s\n", w->tz_offset); printf("text: %s\n", w->text); printf("code: %s\n", w->code); printf("temperature: %s\n", w->temp); printf("last_update: %s\n", w->last_update); }
项目路径中建立了源文件main.c,编写上述代码,并导入cJSON.c和cJSON.h,编译命令为:“riscv64-unknown-linux-gnu-gcc main.c cJSON.c -o weather -lm”。因为cJSON会用到math库,而它需要“-lm”来动态链接。
lvgl显示图片和本地时间
1、lvgl的图片显示
lvgl框架中图片可以是一个文件也可以是一个变量(数组形式的图片码),当然文件还需要初始化lvgl对文件系统的接口,本例暂以变量形式提供。应用要显示图片,则需要引入一个图片控件,然后设置它的数据源——使用“lv_img_set_src()”函数。示例如下:
lv_obj_t * icon = lv_img_create(lv_scr_act(), NULL); /*From variable*/ lv_img_set_src(icon, &my_icon_dsc);
上述代码中“icon”是一个lvgl对象指针,通过“lv_img_create()”实例化,则对应图片控件。设置数据源时传入参数“my_icon_dsc”是lvgl中的图片描述符数据结构“lv_img_dsc_t”——本身是一个结构体类型,其定义源码如下:
//in “../lvgl/src/draw/lv_img_buf.h” typedef struct { uint32_t cf : 5; /*Color format: See `lv_img_color_format_t`*/ uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like a non-printable character*/ uint32_t reserved : 2; /*Reserved to be used later*/ uint32_t w : 11; /*Width of the image map*/ uint32_t h : 11; /*Height of the image map*/ } lv_img_header_t; typedef struct { lv_img_header_t header; /**< A header describing the basics of the image*/ uint32_t data_size; /**< Size of the image in bytes*/ const uint8_t * data; /**< Pointer to the data of the image*/ } lv_img_dsc_t;
示例代码中,图片描述符变量的定义过程如下代码:
uint8_t my_icon_data[] = {0x00, 0x01, 0x02, ...}; static lv_img_dsc_t my_icon_dsc = {![8.png](/assets/uploads/files/1653270047514-8.png) .header.always_zero = 0, .header.w = 80, .header.h = 60, .data_size = 80 * 60 * LV_COLOR_DEPTH / 8, .header.cf = LV_IMG_CF_TRUE_COLOR, /*Set the color format*/ .data = my_icon_data, };
其中,枚举“LV_IMG_CF_TRUE_COLOR”是色彩格式定义,表示RGB格式。
宏“LV_COLOR_DEPTH”则定义色彩深度,它位于“lv_conf.h”,用户可以自定义。本例中设置为32,即4字节的ARGB8888格式。
2、时间获取
86板的Tina Linux可以通过C time库轻松地获得本地时间等数据。本例使用的API有:time()、localtime()、strftime()以及time_t、struct tm。3、图片和时间显示案例
本例继续使用线程管理lvgl刷新,创建1s周期的lvgl定时器,在定时器回调中获取本地时间并格式化输出。另外,系统初始时显示一个“天津”的Logo,而且初始即做一次时间获取和输出(如果不做,初始刹那label会显示默认“text”字样)。图片码通过软件“Img2Lcd”获取,软件配置方式如下图所示。图片生成的数组有72008个字节,被放置到头文件“aita_logo.h”。
/* Includes ------------------------------------------------------- */ #include "lvgl/lvgl.h" #include "lv_drivers/display/fbdev.h" #include "lv_drivers/indev/evdev.h" #include <stdio.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <time.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "aita_logo.h" /* Private macro -------------------------------------------------- */ #define AITA_DISP_BUF_SIZE (128 * 1024) #define AITA_SCREEN_WIDTH 480 #define AITA_SCREEN_HEIGHT 480 #define AITA_TITLE_STRING "AITA Weather for LicheeRV with LVGL" #define SEND_PERIOD 1000 #define errlog(errmsg) do{ perror(errmsg);\ printf("----%s----%s----%d----\n", __FILE__, __func__, __LINE__);\ return;\ } while(0) /* Global variables ----------------------------------------------- */ lv_indev_t *aita_indev; //pointer of indev lv_obj_t *sys_scr; //pointer of system screen instance lv_obj_t *head_label; //pointer of title label instance lv_obj_t *main_label; //pointer of main label instance char main_label_text[32]; //main label text string for datetime lv_obj_t *logo_img; //pointer of city logo image instance lv_timer_t *sec_timer; //pointer of timer instance for tcp polling pthread_t lvgl_tid; //lvgl thread id pthread_t tcprecv_tid; //tcp receive thread id pthread_mutex_t lvgl_mutex; //mutex for lvgl tick //image descriptor for logo_img //ARGB8888 image 180*100 which code array is 'tj_logo' lv_img_dsc_t img_dsc_city = { .header.always_zero = 0, .header.w = 180, .header.h = 100, .data_size = 18000 * LV_COLOR_SIZE / 8, .header.cf = LV_IMG_CF_TRUE_COLOR, .data = tj_logo, }; /* Private function prototypes ------------------------------------ */ void aita_InitLVGL(void); void aita_CreateMainUI(void); void *thread_lvgl(void *arg); void sec_timer_cb(lv_timer_t *timer); void aita_InitTimer(void); void aita_GetTime(void); /* Private functions ---------------------------------------------- */ int main(void) { void *retval; //by author. initialize lvgl including displaybuffer, device for disp & input aita_InitLVGL(); //by author. initialize and register event device //these code must be in main(), otherwise the touch will fail. static lv_indev_drv_t indev_drv; lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_POINTER; //by author. choice touchpad indev_drv.read_cb = evdev_read; //by author. input callback aita_indev = lv_indev_drv_register(&indev_drv); //by author. create the main view when the demo starts up aita_CreateMainUI(); //by author. create a timer aita_InitTimer(); //by author. create mutex for lvgl if(pthread_mutex_init(&lvgl_mutex, NULL) != 0) { errlog("initialize mutex error"); } //by author. create lvgl thread if(pthread_create(&lvgl_tid, NULL, thread_lvgl, (void *)0) != 0) { errlog("create lvgl thread error"); } //by author. wait for thread exit, this demo should never be here. pthread_join(lvgl_tid, &retval); printf("lvgl thread exit, return value: %s\n", (char *)retval); pthread_mutex_destroy(&lvgl_mutex); return 0; } /*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/ uint32_t custom_tick_get(void) { static uint64_t start_ms = 0; if(start_ms == 0) { struct timeval tv_start; gettimeofday(&tv_start, NULL); start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000; } struct timeval tv_now; gettimeofday(&tv_now, NULL); uint64_t now_ms; now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000; uint32_t time_ms = now_ms - start_ms; return time_ms; } void aita_InitLVGL(void) { /*LittlevGL init*/ lv_init(); /*Linux frame buffer device init*/ fbdev_init(); //by author. initialize framebuffer device for display evdev_init(); //by author. initialize event device for touchpad /*A small buffer for LittlevGL to draw the screen's content*/ static lv_color_t buf[AITA_DISP_BUF_SIZE]; /*Initialize a descriptor for the buffer*/ static lv_disp_draw_buf_t disp_buf; lv_disp_draw_buf_init(&disp_buf, buf, NULL, AITA_DISP_BUF_SIZE); /*Initialize and register a display driver*/ static lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.draw_buf = &disp_buf; disp_drv.flush_cb = fbdev_flush; disp_drv.hor_res = 480; disp_drv.ver_res = 480; lv_disp_drv_register(&disp_drv); } void aita_CreateMainUI(void) { //by author. create system screen which is basic graphic level sys_scr = lv_obj_create(lv_scr_act()); lv_obj_set_size(sys_scr, AITA_SCREEN_WIDTH, AITA_SCREEN_HEIGHT); //by author. create the main title which is just a label head_label = lv_label_create(sys_scr); lv_label_set_text(head_label, AITA_TITLE_STRING); lv_obj_align(head_label, LV_ALIGN_TOP_MID, 0, 10); //by author. create the city logo image logo_img = lv_img_create(sys_scr); lv_img_set_src(logo_img, &img_dsc_city); lv_obj_align(logo_img, LV_ALIGN_TOP_LEFT, 10, 40); //by author. get local time and show string aita_GetTime(); main_label = lv_label_create(sys_scr); lv_label_set_text(main_label, main_label_text); lv_obj_align(main_label, LV_ALIGN_TOP_LEFT, 200, 40); lv_obj_set_style_text_font(main_label, &lv_font_montserrat_20, 0); } //by author. lvgl core thread function void *thread_lvgl(void *arg) { while(1) { pthread_mutex_lock(&lvgl_mutex); lv_task_handler(); pthread_mutex_unlock(&lvgl_mutex); usleep(5000); /* sleep for 5 ms */ } } //by author. sec_timer callback which refresh date string void sec_timer_cb(lv_timer_t *timer) { aita_GetTime(); lv_label_set_text(main_label, main_label_text); } //by author. initialize timer for 1s timing void aita_InitTimer(void) { sec_timer = lv_timer_create(sec_timer_cb, 1000, NULL); lv_timer_set_repeat_count(sec_timer, -1); } //by author. get local time string void aita_GetTime(void) { time_t tsec; struct tm *tlocal; tsec = time(NULL); tlocal = localtime(&tsec); memset(main_label_text, 0, 32); strftime(main_label_text, 32, "%Y-%m-%d %a %H:%M:%S", tlocal); }
lvgl日历控件和显示天气
本篇结合本人前两篇的HTTP请求天气数据(通过“心知天气”网站)和lvgl显示图片及时间,在案例主界面上增加了日历显示和实时天气显示,先直接上图。
1、lvgl日历控件
calendar是lvgl提供的“Extra widgets”组件之一,需要注意的是8.0版本后有几个API的传参发生了变化,本例使用8.3版本,设置日期是需要同时传递“年、月、日”三个参数。本例使用的API有:lv_calendar_create()、lv_canlendar_set_today_date()、lv_calendar_set_showed_date()和lv_calendar_header_arrow_create()。
lv_calendar_create()函数用于实例化calendar控件,传参是控件的父容器指针,本例使用“lv_scr_act()”即系统屏幕。
lv_canlendar_set_today_date()函数用于设置当前日期,本人使用发现lvgl是附带万年历功能的,只要设置好当天的年月日,就可以自动生成正确的日历排布。函数传参分别是控件指针和年月日数据。
关于年月日参数有两点注意事项。一是v7版本中,传参通过lv_calendar_date_t结构体,其包含年月日三个成员。二是如果使用了C time库的struct tm,注意其中年份需要加上“1900”,而月份则需要加“1”。
lv_calendar_set_showed_date()函数用于设置日历当前显示页,也就是设置当前月份。本人实验的效果是当天日期框会自动高亮,如果想设置多个高亮日期,可以使用函数lv_calendar_set_highlighted_dates()。
lv_calendar_header_arrow_create()函数用于向日历控件顶部增加“左、右箭头”两个按钮用于日历翻页(一页是一月)。此外,还有函数lv_calendar_header_dropdown_create()则是设置两个下拉列表分别用于选择年份和月份。这两个函数都只用传递日历控件指针一个参数,且是8.1版本新增API。
2、日历和天气显示案例
本案例的思路是:1)在应用启动时,获取当前时间(上篇中已经实现),然后将时间保存在全局量“struct tm today”中,并利用变量“today”来初始化日历控件的日期数据。2)上篇实现的时间显示案例,通过lvgl定时器,每秒获取本地数据,此处在定时器回调中再增加一个每到正分钟发送“Linux条件变量”。3)同时,应用启动时建立两个线程——lvgl线程和请求天气线程,请求天气线程等待条件变量到来,开启一次天气数据请求过程。本例代码结合文章上半部分已经给出的案例,这里只给出改变部分。
/* Includes ------------------------------------------------------- */ // 增加头文件,cJSON用于解析JSON格式的天气数据 #include "cJSON.h" /* Private macro -------------------------------------------------- */ // 增加请求天气数据相关的宏定义 #define HTTP_IP "116.62.81.138" #define HTTP_PORT 80 #define NOW "now.json" #define API_KEY "SK0LJ8FI2TP0L-IsQ" #define CITY "tianjin" #define REQ_PACK "GET https://api.thinkpage.cn/v3/weather/%s?key=%s&location=%s&language=en&unit=c\r\n\r\n" #define N 1024 // struct for weather data 建立结构体存储解析后的天气数据 typedef struct { char id[16]; char name[32]; char country[16]; char path[64]; char timezone[32]; char tz_offset[16]; char text[16]; char code[4]; char temp[8]; char last_update[32]; } weather_t; /* Global variables ----------------------------------------------- */ // 增加显示天气的标签控件定义 lv_obj_t *weather_label; //pointer of weather label instance // 增加日历控件定义 lv_obj_t *calendar; //pointer of calendar instance // 定义today变量存储当前日期,用于设置日历 struct tm today; // // 请求天气的线程ID pthread_t reqweather_tid; //request weather thread id // 请求天气线程等待的条件变量(min_cond) // Linux中需要互斥量包含条件变量的使用,所以定义cond_mutex pthread_mutex_t cond_mutex; //mutex for 1-min cond pthread_cond_t min_cond; //1-min cond /* Private functions ---------------------------------------------- */ int main(void) { // other code from previous demo // main()函数中创建互斥量、条件变量、请求天气线程 //by author. create mutex for 1-min cond if(pthread_mutex_init(&cond_mutex, NULL) != 0) { errlog("initialize cond mutex error"); } //by author. create condition for 1-min if(pthread_cond_init(&min_cond, NULL) != 0) { errlog("initialize 1 minute condition error"); } //by author. create request weather thread if(pthread_create(&reqweather_tid, NULL, thread_reqweather, (void *)0) != 0) { errlog("create request weather thread error"); } //by author. wait for thread exit, this demo should never be here. pthread_join(lvgl_tid, &retval); printf("lvgl thread exit, return value: %s\n", (char *)retval); pthread_join(reqweather_tid, &retval); printf("request weather thread exit, return value: %s\n", (char *)retval); pthread_mutex_destroy(&lvgl_mutex); pthread_mutex_destroy(&cond_mutex); pthread_cond_destroy(&min_cond); return 0; } void aita_CreateMainUI(void) { // other code from previous demo // aita_CreateMainUI()被main()函数调用,初始化主界面。 //by author. create the weather label weather_label = lv_label_create(sys_scr); lv_label_set_text(weather_label, " "); lv_obj_align(weather_label, LV_ALIGN_TOP_LEFT, 200, 120); //by author. create the calendar calendar = lv_calendar_create(sys_scr); lv_obj_set_size(calendar, 235, 235); lv_obj_align(calendar, LV_ALIGN_BOTTOM_LEFT, 10, -50); lv_calendar_set_today_date(calendar, today.tm_year+1900, today.tm_mon+1, today.tm_mday); lv_calendar_set_showed_date(calendar, today.tm_year+1900, today.tm_mon+1); lv_calendar_header_arrow_create(calendar); } // 增加正分钟发送条件变量 void sec_timer_cb(lv_timer_t *timer) { aita_GetTime(); lv_label_set_text(main_label, main_label_text); if(today.tm_sec == 0) { //by author. send condition signal per whole minute pthread_cond_signal(&min_cond); } } // 增加对today的赋值 void aita_GetTime(void) { time_t tsec; struct tm *tlocal; tsec = time(NULL); tlocal = localtime(&tsec); today = *tlocal; memset(main_label_text, 0, 32); strftime(main_label_text, 32, "%Y-%m-%d %a %H:%M:%S", tlocal); } // 请求天气线程业务逻辑 void *thread_reqweather(void *arg) { int sockfd; struct sockaddr_in serveraddr; socklen_t addrlen = sizeof(serveraddr); char sendbuf[N] = ""; char recvbuf[N] = ""; weather_t weather = {0}; char w_string[64] = ""; while(1) { pthread_mutex_lock(&cond_mutex); pthread_cond_wait(&min_cond, &cond_mutex); pthread_mutex_unlock(&cond_mutex); //create socket if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { errlog("socket error"); } //connect to server of seniverse.com serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(HTTP_IP); serveraddr.sin_port = htons(HTTP_PORT); if((connect(sockfd, (struct sockaddr*)&serveraddr, addrlen)) < 0) { errlog("connect error"); } //build & send request package memset(sendbuf, 0, N); sprintf(sendbuf, REQ_PACK, NOW, API_KEY, CITY); if(send(sockfd, sendbuf, N, 0) < 0) { errlog("send error"); } //waiting server response if(recv(sockfd, recvbuf, N, 0) < 0) { errlog("recv error"); } printf("recv: %s\n", recvbuf); //parse & print data,下面两个函数来自于“十三”案例 aita_ParseJsonNow(recvbuf, &weather); aita_PrintWeather(&weather); close(sockfd); memset(recvbuf, 0, N); //show weather string memset(w_string, 0, 64); sprintf(w_string, "weather:%s temperatur:%s", weather.text, weather.temp); pthread_mutex_lock(&lvgl_mutex); lv_label_set_text(weather_label, w_string); pthread_mutex_unlock(&lvgl_mutex); } }
另外,本例在lvgl工程中增加了cJSON.c和cJSON.h文件,Makefile也做出了调整,具体如下所示。
原文链接:https://occ.t-head.cn/community/post/detail?spm=a2cl5.25411629.0.0.6597180fVP7giT&id=4039525135236603904
作者 @ firr -
【FAQ】全志XR系列 如何修改录音编码器的输入数据?
问题背景
有客户希望把现有的PCM数据编码成AMR,或者希望把录音得到的PCM数据经过处理后再进行音频编码。问题描述
AMR的输入数据是可以由客户自定义的。问题分析
梳理录音初始化流程recorder_base *recorder_create() //初始化录音设备 CaptureCtrl *CaptureDeviceCreate() //录音数据流处理结构体 mCaptureControlOps.cdxRead //获取录音数据数据流
可以知道,录音数据由project/common/apps/cedarx/capture_ctrl/captureControl_rtos.c :static int __Read(CaptureCtrl *c, void *pData, int nDataSize)中获取,如果希望PCM数据经过处理后再进行编码,则可以通过修改该函数,把数据修改完毕后再返回。
解决方法
以把SD卡中的PCM编码成AMR格式为例。
/* 打开 PCM数据 */ int encode_input_init() { FRESULT result; result = f_open(&inContext.fp, "0:/audio/8000.pcm", FA_READ); if (result != FR_OK) { printf("open file fail.\n"); return -1; } return 0; } /* 获取PCM数据 */ unsigned int encode_input_data(void *buffer, unsigned int len) { unsigned int act_read; f_read(&inContext.fp, buffer, len, &act_read); return act_read; } /* 替换掉录音数据 */ static int __Read(CaptureCtrl *c, void *pData, int nDataSize) { int ret; .......... //ret = snd_pcm_read(CAPTURE_SOUND_CARD, pData, nDataSize); ret = encode_input_data(pData, nDataSize); .......... return ret; }
-
【FAQ】全志XR系列 如何调试wifi频偏问题?
问题背景
系统平台:Tina,RTOS
硬件平台:XR829,XR808,XR806,XR819S问题描述
在测试WiFi传导或者过SRRC认证时,输出功率正常,但是“SymClockErr”指标或者“载波容限”不满足需求,需要做频偏校准。解决方案及步骤
1、让芯片TX WiFi信号;
2、输入指令:etf get_freq_offset,获取当前的频偏设置值,记为x;
3、输入指令:etf set_freq_offset (0~127),设置频偏值y;
4、查看IQ或者极致汇仪(或者其它测试仪器)上面的频偏值。如果这个频偏值比之前的低(例如15ppm变成了10ppm),当y>x:继续增大y;当y<x:继续减小y。直到测试仪器上面频偏值满足需求(越低越好,一般调到5ppm以内),记录下来这个值y;反之,这个频偏值比之前的高(例如15ppm变成了20ppm),当y>x:把y值设置成小于x;当y<x:把y值设置成大于x。直到测试仪器上面频偏值满足需求(越低越好,一般调到5ppm以内),记录下来这个值y;
5、输入指令:etf set_sdd_freq_offset (0~127),把频偏值保存到sdd里面;(如需保存到efuse,则参考保存到efuse的教程);
6、如通过软件的方法,无法使频偏值满足需要,则需要调整wifi芯片晶振旁边的两个电容(一般是10~22pF)。查看IQ或者极致汇仪(或者其它测试仪器)上面的频偏值,直到满足需求(越低越好,一般调到5ppm以内)。记录下当前的电容值,以后贴片就用这个电容值。
-
【FAQ】全志XR系列 如何播放xip中的音频?
1.问题背景
有客户因为担心音频存放在flash中会因为没有烧录,导致播放异常,所以希望可以提供播放xip中的音频数据的方法。2.问题分析
XRMCU允许使用raw_bin的方式烧录,确保烧录固件时音频也能下载到flash中,请参考https://one.allwinnertech.com/#/faq/0/show 。
如果确定要播放xip中的数据,需要把计算出音频数据在flash中的实际地址。3.解决步骤
1.使用bin2hex或者HxD等工具把音频文件转变成c文件,并保存在xip中。__xip_rodata //保存在xip中 const unsigned char testmusic[39197] = { 0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x33, 0x54, 0x53, 0x53, 0x45, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x4C, 0x41, 0x4D, 0x45, 0x20, 0x33, 0x32, 0x62, 0x69, 0x74, 0x73, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x31, 0x30, 0x30, 0x2E, 0x31, ......
2.计算音频数据在flash中的地址。
参照xip初始化platform_xip_init();可以知道app_xip.bin在flash中的位置是image_get_section_addr(IMAGE_APP_XIP_ID) + IMAGE_HEADER_SIZEstatic void platform_xip_init(void) { uint32_t addr; addr = image_get_section_addr(IMAGE_APP_XIP_ID); //通过ID获取app_xip.bin在flash中的地址 if (addr == IMAGE_INVALID_ADDR) { FWK_NX_ERR("no xip section\n"); return; } HAL_Xip_Init(PRJCONF_IMG_FLASH, addr + IMAGE_HEADER_SIZE); //IMAGE_HEADER_SIZE 为头码地址 }
可以得出音频数据在flash中的地址。
/* __xip_start__指xip的入口地址,在appos.ld中定义,数值也在appos.ld中定义为0x400000。 (uint32_t)testmusic - (uint32_t)__xip_start__也就是相对于xip入口的偏移量。 image_get_section_addr(IMAGE_APP_XIP_ID) + IMAGE_HEADER_SIZE就是app_xip.bin在flash中的实际地址,会被映射到0x400000 */ uint32_t music_addr = ((uint32_t)testmusic - (uint32_t)__xip_start__ + image_get_section_addr(IMAGE_APP_XIP_ID)+ IMAGE_HEADER_SIZE);
3.把数值格式化为cedarx能识别的字符串。
char *song_addr = malloc(50); sprintf(song_addr,"flash://0?addr=%u&length=%u",music_addr,sizeof(testmusic));
4.播放音频
player_base *mAwPlayer; mAwPlayer = player_create(); mAwPlayer->play(mAwPlayer,song_addr);
-
Reply: 【素材汇总】V853素材汇总
- Arm Cortex-A7 + RISC-V E907 + 1T NPU 高性能算力组合
- 512MB DDR3 + 8G eMMC
- 高清双目摄像头 + 高清屏幕
- WiFi + 蓝牙 + 以太网
- USB/UART/SD CARD/MIC/Line-in/SPEAKER/ISP/MIPI/TP/BAT等丰富接口
-
Reply: 【素材汇总】V853素材汇总
产品包装清单
包括:
V853主板 *1
7寸屏幕及转接板 *1
双摄像头模组 *1
亚克力支架 *1
Type-C USB数据线 *1
串口线 *1
电源适配器 *1
小喇叭 *1
包装盒 *1
-
【D1-H 哪吒开发板】Debian系统安装调教和点灯指南
原文链接:https://occ.t-head.cn/community/post/detail?id=4042007089404059648
作者 @ HonestQiaoD1开发板【哪吒】使用Deabian系统入门
特别说明:
-
因为涉及到操作较多,博文可能会导致格式丢失,可通过微信联系 HonestQiao 获取原始markdown文档。
-
其中内容,会根据后续使用做优化调整
目录:
- 参考资料
- 固件烧录
- 启动
- 调教
- 点灯
- 问题
〇、参考资料
-
学习资料
参考的学习资料较多,感谢下列资料作者:
Debian by Sipeed - D1-H
D1 Nezha Debian镜像基础使用教程
Nezha D1 debian镜像更新帖 - Sipeed 开源社区
「RVBoards-哪吒」开启 SSH 和 VNC 远程访问,摆脱烦人的鼠标键盘显示器 | RVBoards 论坛
【Station M2】打造开发人员专用最强便携小主机 - 广受欢迎的专业电子论坛!
Debian安装中文支持 - Linux运维 - 运维网
Habitat: Debian WQY
debian stretch下的 lxde 配置 | Frapples的博客
怎样在 Debian 11 上设置静态 IP | 月灯依旧
全志D1开发板(哪吒 RISCV64)开箱评测_专栏RISC-V MCU中文社区
生信之旅-debian11增加开机自启脚本
How to Change Your MAC Address on Linux
全志在线 D1哪吒开发板开机连接wifi热点, 启动 sshd 服务
How to set up static IP address on Debian Linux 10/11 - nixCraft
如何在Debian 10安装vscode | myfreax
一、固件烧录
-
固件下载
链接: https://pan.baidu.com/s/1-3CocbTUWPLi2XKrb87LpA 提取码:z4gn -
烧录工具
PhoenixCard.zip -
SD卡准备
准备一张8G以上的SD卡,建议至少32G,以免后悔来不及 -
烧录
- 将SD卡插到读卡器,连接到电脑
- 打开PhoenixCard,选择好固件(需解压),确认自动选择的盘正确,勾选启动卡,点击烧录即可
- 预计15到30分钟,先干点别的
- 分区大小调整
务必要到Linux环境下,调整一下SD卡上分区的大小,不然后面的apt upgrade会挂了
# 查看sd卡挂载到哪个挂载点了, sudo fdisk -l Device Start End Sectors Size Type /dev/sda1 41464 49527 8064 3.9M Microsoft basic data /dev/sda2 49528 50031 504 252K Microsoft basic data /dev/sda3 50032 50535 504 252K Microsoft basic data /dev/sda4 50536 71199 20664 10.1M Microsoft basic data /dev/sda5 71200 72207 1008 504K Microsoft basic data /dev/sda6 72208 100431 28224 13.8M Microsoft basic data /dev/sda7 100432 16877647 16777216 8G Microsoft basic data /dev/sda8 16877648 60504063 43626416 20.8G Microsoft basic data # sd卡会被分为八个区,sdX1-sdX8,将sdX替换为上一步实际显示的即可 sudo e2fsck -f /dev/sdX7 sudo resize2fs -p /dev/sdX7
二、启动:
- 插卡
在背后插上SD卡,然后连接Type-C、网线,接上USB键盘,Type-C通电即可启动
- 上电
- 启动时,会显示SIPEED的图标;然后会进入登录界面;启动速度不是很快,耐心等待
- 默认账户:
sipeed / licheepi
root / licheepi
- 远程ssh连接
- 进入系统后,通过开始菜单中的Netwrok connection查看当前ip地址,然后连接
- ssh sipeedd@ip
三、调教:
默认的系统,得好好调教才好使用
- 了解系统基本情况
sudo lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux bookworm/sid Release: unstable Codename: sid
- 通过以上输出,可以了解系统为Debian 开发版;sid表示开发版
- 设置默认shell使用bash
# 选择No即可 sudo dpkg-reconfigure dash # 查看默认shell,设置后,显示为 /bin/sh -> bash ls -l /bin/sh
- 设置ssh证书登陆
sudo vim /etc/ssh/sshd.conf # -------- 取消下面这行的注释 -------- PubkeyAuthentication yes
重启sshd服务
sudo systemctl restart sshd # 设置当前用户的key登录 mkdir ~/.ssh chmod 0700 ~/.ssh # 设置登录pub key sudo vim ~/.ssh/authorized_keys # -------- 将ssh登录的pub key拷贝到这里 -------- ssh-rsa 公钥 邮箱
- 设置完成后,即可在主机或者其他电脑上,使用对应的私钥登录
- 时区设置
sudo tzselect # 依次选择4-Asia、9-China、1-Beijing Time、1-Yes # 设置当前环境时区: TZ='Asia/Shanghai'; export TZ # 查看当前时间: date # 执行下面的指令,并在最后添加时区设置 sudo vim /etc/profile # -----------以下内容为添加内容------------- TZ='Asia/Shanghai'; export TZ # -----------以上内容为添加内容-------------
- 设置apt更新源
# 备份原有的更新源配置 sudo mv /etc/apt/sources.list /etc/apt/sources.list.bak # 使用阿里源,打开后输入i即可开始填写内容,按ESC,再按!wq回车,即可保存 sudo vim /etc/apt/sources.list # -----------以下内容为填写内容------------- deb https://mirrors.aliyun.com/debian-ports/ sid main # -----------以上内容为填写内容-------------
- 更新系统
sudo apt-key add archive_2022.key sudo apt update sudo apt upgrade -y # 中途如弹出选择界面,直接回车即可 sudo apt autoremove
- 安装中文环境支持
- 安装中文locale、字体,并启用中文支持
安装locales sudo apt install -y locales # 所有的选项,都选择zh_CN.UTF-8,然后等待完成 sudo dpkg-reconfigure locales # 安装中文字体 sudo apt install -y fonts-arphic-ukai fonts-arphic-gkai00mp fonts-arphic-bkai00mp sudo apt install -y xfonts-wqy ttf-wqy-microhei ttf-wqy-zenhei # 设置当前用户locale,按照如下信息进行设置 vim /home/sipeed/.bashrc export LC_ALL="zh_CN.UTF-8" export LANG="zh_CN.UTF-8" export LANGUAGE="zh_CN.UTF-8:zh:en_US.UTF-8:en" # 设置系统全局locale,按照如下信息进行设置 sudo vim /etc/environment LANGUAGE="zh_CN.UTF-8:zh:en_US.UTF-8:en" LANG="zh_CN.UTF-8" # 测试是否生效:设置生效后,执行data指令应返回中文 source ~/.bashrc date
- 启用开机脚本
- 后续有不少操作,需要设置开机启动,所以此处县启动,方便后续操作
- 在rc.local服务中添加下面的信息
sudo vim /lib/systemd/system/rc-local.service # -----------以下内容为添加内容------------- [Install] WantedBy=multi-user.target # -----------以上内容为添加内容------------- # 设置/etc/rc.local启动脚本 sudo vim /etc/rc.local # -----------以下内容为填写内容------------- #!/bin/sh -e # 在这里输入需要自启的脚本 exit 0 # -----------以上内容为填写内容------------- # 设置启动脚本执行权限 sudo chmod +x /etc/rc.local # 启动对应的服务 sudo systemctl enable rc-local # 启用 sudo systemctl start rc-local.service # 开始运行 sudo systemctl status rc-local.service # 查看状态
- 设置有线使用固定mac地址
- 这块板子有个奇葩的地方,有线网卡的mac地址,重启一次变一次,通过下面的方式,设置为固定值即可
# 安装screen,防止网络更改中途失联,命令执行不完全 sudo apt install -i screen macchanger # 开启screen screen # 查看当前的eth0 mac addr sudo macchanger -s eth0 Current MAC: 86:53:0e:f0:ee:29 (unknown) Permanent MAC: 00:00:00:00:00:00 (XEROX CORPORATION) # 将上一步中显示Current MAC,进行设置;请根据上一步显示的实际结果设置 sudo macchanger -m eth0 86:53:0e:f0:ee:29 eth0 Current MAC: 86:53:0e:f0:ee:29 (unknown) Permanent MAC: 00:00:00:00:00:00 (XEROX CORPORATION) New MAC: 86:53:0e:f0:ee:29 (unknown) It's the same MAC!! # 重启网络 sudo /etc/init.d/networking restart # 写入到开机启动中,将下面的对应内容,添加到/etc/rc.local启动脚本 sudo vim /etc/rc.local # 在这里输入需要自启的脚本 # -----------以下内容为添加内容------------- # 设置网卡mac地址 /usr/bin/macchanger -m 86:53:0e:f0:ee:29 eth0 /etc/init.d/networking restart # -----------以上内容为添加内容------------- exit 0
- 设置完成后,可以尝试重启板子,查看mac地址是否已经固定
- 设置静态IP地址
- 请根据实际情况,设置对应的ip、掩码、网关、DNS
# 备份默认配置 sudo cp /etc/network/interfaces /etc/network/interfaces.bak sudo vim /etc/network/interfaces # -------- 参考如下信息进行设置 -------- #iface eth0 inet dhcp # The loopback network interface auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.1.141 netmask 255.255.255.0 gateway 192.168.1.1 dns-domain wowo dns-nameservers 192.168.1.1
11.远程桌面登录
11.1 安装vncapt install tigervnc-standalone-server -y
11.2 启停vnc
- 首次启动时,会提示设置用户密码,请仔细设置
# 启动,可添加参数-geometry 1280x800设置分辨率 vncserver -localhost no -display :1 # 查看 vncserver -list # 停止 vncserver -kill :1
11.3 设置开机启动
#在开机脚本exit 0前面添加下面的脚本 sudo vim /etc/rc.local # -----------以下内容为添加内容------------- echo "start vnc server" export HOME=/home/sipeed /usr/bin/sudo -u sipeed -i vncserver -localhost no -display :1 -geometry 1280x800 echo "vnc server started" # 这里需要先配置 HOME 环境变量, vncserver 需要 # -----------以上内容为添加内容------------- exit 0
- 列表设置完成后,重启时,将会自动启动vnc server,方便远程桌面连接
11.4 远程连接:
- 在其他电脑上,使用vnc viewer进行连接
- 连接地址: http://开发板ip:5901
- 安装vscode[跳过]
- 安装好vscode,就能当开发小强用了
- vscode目前还没有提供Debian@risc-v的运行版本,以下操作跳过
# 导入微软GPG秘钥: sudo apt install -y software-properties-common apt-transport-https curl curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - # 添加微软vscode安装源 sudo add-apt-repository "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" # 更新安装源索引,并安装vscode sudo apt update sudo apt install code
四. 点灯
- 最激动人心的时刻,不就是点个灯么;好在这个Debian by Sipeed已经提供了LED支持,使用下面的代码,就能很方便的点灯了
vim ~/test_led.sh # -----------以下内容为填写内容------------- #!/bin/bash function set_led(){ echo $1 > /sys/class/leds/sunxi_led0r/brightness echo $2 > /sys/class/leds/sunxi_led0g/brightness echo $3 > /sys/class/leds/sunxi_led0b/brightness } let type=8 for i in $(seq 1 1000); do if [[ $((i % type)) -eq 0 ]];then set_led 0 0 0 elif [[ $((i % type)) -eq 1 ]];then set_led 255 0 0 elif [[ $((i % type)) -eq 2 ]];then set_led 0 255 0 elif [[ $((i % type)) -eq 3 ]];then set_led 0 0 255 elif [[ $((i % type)) -eq 4 ]];then set_led 255 255 0 elif [[ $((i % type)) -eq 5 ]];then set_led 255 0 255 elif [[ $((i % type)) -eq 6 ]];then set_led 0 255 255 elif [[ $((i % type)) -eq 7 ]];then set_led 255 255 255 fi sleep 0.5 done # -----------以上内容为填写内容------------- # 设置执行权限 chmod a+x test_led.sh # 执行 sudo ./test_led.sh
现在请看板子,板载WS2812 LED欢快的亮起来了,颜色还是变化的!
五、问题
- WiFi驱动问题
Anne Nas :“Hello World, from the Nezha via Brutaldon on Lynx!” - Vachtnoes
哪吒D1 wifi无法使用 | 全志在线开发者论坛
sipeed@sipeed:~$ sudo dmesg | grep -Ei 'wlan|ieee|wireless' -A2 -B2 [ 10.871117] proc: Bad value for 'hidepid' [ 15.036791] ======== XRADIO WIFI OPEN ======== [ 15.041751] ieee80211_init failed (-12)! [ 15.421480] usbcore: registered new interface driver uvcvideo [ 15.507489] USB Video Class driver (1.1.1) sipeed@sipeed:~$ sudo modprobe xradio_wlan modprobe: ERROR: could not insert 'xr829': Cannot allocate memory modprobe: ERROR: could not insert 'xr829': Cannot allocate memory sipeed@sipeed:~$ sudo modprobe xr829 modprobe: ERROR: could not insert 'xr829': Cannot allocate memory sipeed@sipeed:~$
- 列表已经在全志和Spieed对应的社区帖子,提交了问题,待回复
- 有线网卡mac地址问题
- 每次重启后,有线网卡mac地址会随机生成,参考 三-9 进行设置即可。
-
-
平头哥Sipeed LicheeRV 86 Panel GPIO管脚引出,RGB三色LED闪烁
作者@zhang1gong
原文链接:https://occ.t-head.cn/community/post/detail?spm=a2cl5.14300636.0.0.7f1f180fHnTGVq&id=40398097139641262081 核心板LED点亮
LicheeRV教程提供了核心板上的LED点亮教程。LED点亮或闪烁往往是广泛应用于自动控制的嵌入式系统运行的第一个试验程序,如同一般在桌面系统上学习编程语言运行的第一个程序:“Hello, world!”。我是第一次在Linux系统下运行点灯程序,感觉和在裸机上用c或汇编编程完全不同:基本上不用关心硬件,完全是对文件操作,充分提现了Linux系统“万物皆文件”的理念。核心板上驱动LED的GPIO口(PC1)与底板上的其他应用冲突,因此必须把核心板从底板上拆下,才能做这个点灯试验。但是,核心板上的USB口并不具备模拟串口功能,对此教程似乎并没有明确说明。几位已经做了这个试验的测试者都提到了:要用ADB。虽然大概早晚都会在我的桌面系统中装ADB以搭建交叉编译环境,我还是想先看看有没有其他办法。将核心板从底板拆下后,发现在USB口旁边有4个预留的焊盘,PCB板背面在焊盘旁边标出了“T R G 5V”,不禁使人想到:这难道是个串口?查了一下原理图,果然如此!正好我手头有不止一个串口转USB的小板(某宝上几块钱一个还包邮),为什么不用这个串口呢?忽然想到开发套件中有个小口袋装了4脚插针,应该就是干这个用的。顺利地将4脚插针焊上,但马上发现:如果将核心板插回底板,由于在串口插针下是底板上的复位按键,需要对焊上的插针修剪,否则插针的焊接端就会顶到复位按键上。焊接、修剪过程其实十分简单,但我差点儿在阴沟里翻船。经历了核心板不能工作、终于又恢复的过程(此处略去具体翻船现场和恢复过程200字),总算有惊无险!
通过核心板上的串口,经小板转换成USB连到桌面系统的模拟终端,按照教程给出的命令行指令逐条执行,点灯过程很顺利。(教程给出的命令行指令有一处小错:“cd /sys/class/gpio/export/gpio65”)
2 引脚扩展,RGB三色LED闪烁
底板上预留的扩展引脚区给人以无限遐想,总觉得如不把它们引出来,似乎对不起设计者的初衷。将引脚引出的主要障碍是需要把显示屏与底板分离。分离本身其实并不困难,难的是下决心去做这种带一点儿破坏性的事情(恳请厂家在出厂时就把双排插座焊上吧)。用刀片将显示屏和底板之间的连接分离,小心地在底板上焊上插座,可以方便地用杜邦线连接扩展引脚了。
根据底板上扩展引脚的标号,对照原理图,在感觉没有被占用的引脚中选择B2、B3、B4来用,分别用来驱动三色LEB中的R、G、B。参照教程,先用
“cat /sys/kernel/debug/pinctrl/2000000.pinctrl/pinmux-pins”
命令查询管脚标号对应的数字编号:
然后参照教程写了两个脚本,并放到“/mnt/SDCARD”目录下,这样掉电文件也不会丢失:
\
# rgb_config.sh echo 34 > /sys/class/gpio/export echo 35 > /sys/class/gpio/export echo 36 > /sys/class/gpio/export cd /sys/class/gpio/gpio34 echo out>direction cd /sys/class/gpio/gpio35 echo out>direction cd /sys/class/gpio/gpio36 echo out>direction
\
# rgb_blink.sh for a in $(seq 1 5) do cd /sys/class/gpio/gpio34 echo 1 > value sleep 0.5 cd /sys/class/gpio/gpio35 echo 1 > value sleep 0.5 cd /sys/class/gpio/gpio36 echo 1 > value sleep 1 cd /sys/class/gpio/gpio34 echo 0 > value sleep 0.5 cd /sys/class/gpio/gpio35 echo 0 > value sleep 0.5 cd /sys/class/gpio/gpio36 echo 0 > value sleep 1 done
脚本“rgb_config.sh”用来初始化管脚,“rgb_blink.sh”控制三色LED的闪烁。
Linux系统下万物皆文件,还要进一步好好体会。
-
大二学生DIY RISC-V开发板,获阿里批量采购订单
DIY D1s开发板
00后开源创客 @YuzukiTsuru 基于全志D1s芯片设计并制作了一款RISC-V开发板,目前该开发板已经获得了阿里等多家知名科技公司的批量采购订单,将会被用于嵌入式系统的研究和IoT产品的研发。
值得一提的是,本开发板部分小件如电容电阻等是嘉立创机贴的,但D1s主控、XR829等大件物料为心灵手巧的作者本人手贴。
大佬手贴D1s实录
免费开源全部设计资料
该开发板基于全志D1s芯片(阿里平头哥C906 RISC-V核)设计,可用于方案评估、方案预研和个人DIY,可应用于游戏机、智能商显、智能中控等产品形态。作者将开源开发板的全部设计资料。
开发板将提供的资料有:
- 硬件资料:原理图、PCB layout、BOM list
- 软件资料:适配的Tina Linux SDK(全志官方客户平台可下载)、基于官方SDK修改的适配补丁、测试用的固件。
- 技术支持:将提供力所能及的基础技术支持,请到【全志在线开发者论坛】提问,作者和社区的爱好者会回复。
以上所有资料获取:https://bbs.aw-ol.com/topic/1257/
贴片图
实物图规格介绍:
- 基于全志D1s芯片,阿里平头哥C906 RISC-V核心
- 支持全志官方Tina Linux系统,标准linux5.4内核
- 支持RGB显示接口;
- 支持DSI接口;
- 支持TP接口;
- 支持SD卡;
- 支持JATG/UART debug;
- 支持USB Host/device;
- 支持LINEIN接口,支持HPOUT接口;
- 集成全志XR829 WiFi/BT芯片,支持a2dp与hfp
D1s开发板拓展应用-桌面小电脑00后创客大佬
开发板作者 @YuzukiTsuru 是一位00后的大二学生,目前就国内某院校物联网专业。他高中时就搭建了自己的个人博客,上大学后开始玩嵌入式,恰逢全志在线成立并进行资料开源,就开始围绕全志芯片进行项目开发,目前已经基于全志的芯片做了十几个不同型号的开发板并开源到立创开源硬件平台等社区。
立创开源硬件平台个人主页
Github个人主页
全志在线个人主页:https://bbs.aw-ol.com/user/yuzukitsuru
购买途径
本开发板由原作者委托哇酷科技代售并开具发票,所有收益将由哇酷转交给原作者@YuzukiTsuru,有兴趣的可以扫码到淘宝店购买。
-
RISC-V国际基金会CEO:Yes!
知名开源方案品牌芒果派最近发布了基于全志D1-H芯片(阿里平头哥C906 RISC-V核心)的开发板,定义为树莓派ZeroW的替代方案。RISC-V国际基金会CEO Calista Redmond 女士转发了相关信息,配文简短而有力,她说:
“Yes!”
海外开发者社区Electromaker在推特上发布了关于芒果派MQ Pro的报道,报道内容是:“MangoPi MQ Pro是RaspberryPi Zero W的一款售价20美元的RISC-V替代品,虽有不足,但你有理由使用这款主板”。基于全志D1系列芯片的产品也再一次在国外社区平台上得到认可。
芒果派MQ Pro
芒果派MQ Pro延续了芒果派开发板一贯小而美的作风,板子虽小,但却具备了所有重要的功能,MQ Pro以全志D1-H为主控,内置最高1G DDR,板载WiFi/BT/,并提提供了多种常用的外设接口,完整的功能也使得开发板可以很好地运行Tina/Debian之类的嵌入式软件系统。
板载资源:
- D1-H,C906 内核
- 512MB 或 1GB DDR3/DDR3L
- USB-OTG Type-C
- USB-HOST Type-C
- 40Pin RPI-扩展
- 24Pin DVP/RGMII连接器
- HDMI连接器
- TF卡
- 20Pins DSI/CTP/LVDS FPC连接器
- Audio OUT
- 板载WiFi/BT
- 6.5x3cm
型号:
- MPi-MQ1PL:WiFi+BT,512MB DRAM
- MPi-MQ1PH:WiFi+BT,1GB DRAM
全志 · 开源
全志在线开发者社区秉持着开源开放的初心,持续扶植广大厂商和个人开发者进行SBC方案的设计。
基于全志在线社区的开源资料,从在校学生到在职员工,已经有大量开发者参与到基于全志系列芯片的开发板DIY行动中,为开源社区和科研教育贡献了巨大的力量。
D1s-Nezha开发板 https://bbs.aw-ol.com/topic/1257
作者@Yuzuki - 在校学生
D1s开发板Xassette-Asterisk https://bbs.aw-ol.com/topic/766/
作者@SdtEE - 在校学生
《小巧精湛!基于D1s的全开源芒果派-哪吒MQ正式开售》 https://bbs.aw-ol.com/topic/793/
Pewered by 歪朵拉-芒果派
《D1计算条上线!为智能家居提供高性价比RISC-V算力》 https://bbs.aw-ol.com/topic/633/
Pewered by Sipeed
《基于全志D1-H东山派发布,免费开源嵌入式视频课程》 https://bbs.aw-ol.com/topic/1246/
Pewered by 百问科技-韦东山老师全志在线将坚持践行开源理念,为从个人开发者到厂商的用户,提供到硬件开发工作的技术支持与资料便利,也期待有更多的开发者和厂商可以加入全志在线社区的大家庭。
-
【FAQ】全志D1芯片 uart测试用例(支持自发自收,板间收发,数据校验,收发时间统计)
问题背景
部分客户或者技术人员需要验证uart的功能,包括数据准确性、传输速度、长时间传输稳定性等,但发现没有方便使用的测试用例。用例功能
1.支持短接外部接口或者使能Loopback自发自收
2.可配置用例为发送模式和接收模式进行板间收发测试
3.可打印收传输时间信息,用于计算传输速率
4.支持各项参数如传输通道、波特率、data size、传输次数的个性化设置
5.支持校验收发信息是否准确使用步骤
使能uart_test
make menuconfig 使能 PACKAGE_UART_TEST宏,即可编译出uart_test文件使用说明
例子:
/*Master模式短接外部引脚即可自发自收*/ Master:uart_test -d 2 -n 100 -t 10 -g -p Slave: uart_test -d 2 -n 100 -t 10 -g -p -m Loopback:uart_test -d 1 -b 1500000 -n 10 -s 256 -l -g 参数选择: 1.-d,选择uart通道,默认通道1 2.-b,波特率选择,默认115200Hz 3.-n,传输次数,默认100次 4.-s,发送数据size,默认16bytes 5.-p,开启打印统计时间信息 6.-t,收发多少轮数据后打印统计的时间信息,默认收发10次后打印一次 7.-l,开启loopback回环 8.-g,开启打印发送数据信息 9.-m,以接收模式调用测试程序,用于板间收发测试
测试验证
root@TinaLinux:/# uart_test -d 2 -n 100 -t 10 -f -l -g =======uart_test test args======= uart_port: /dev/ttyS2. baudrate: 115200. test cycle: 100. bytes_per_cycle: 16. flow ctrl:true. debug log:true. trans mode nSpeed:115200 set done! Select(/dev/ttyS2), Cnt 100. [main] line:292 read_size=0 0:transmit 16 bytes. ==== write data ==== 0x39 0x34 0x33 0x34 0x39 0x38 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 1:transmit 16 bytes. ==== write data ==== 0x31 0x34 0x33 0x36 0x39 0x37 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 2:transmit 16 bytes. ==== write data ==== 0x33 0x34 0x33 0x38 0x37 0x33 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 3:transmit 16 bytes. ==== write data ==== 0x35 0x34 0x34 0x30 0x34 0x32 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 4:transmit 16 bytes. ==== write data ==== 0x37 0x34 0x34 0x32 0x30 0x34 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 5:transmit 16 bytes. ==== write data ==== 0x39 0x34 0x34 0x33 0x36 0x34 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 6:transmit 16 bytes. ==== write data ==== 0x31 0x34 0x34 0x35 0x32 0x36 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 7:transmit 16 bytes. ==== write data ==== 0x33 0x34 0x34 0x36 0x38 0x37 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 8:transmit 16 bytes. ==== write data ==== 0x35 0x34 0x34 0x38 0x35 0x35 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 9:transmit 16 bytes. ==== write data ==== 0x37 0x34 0x35 0x30 0x35 0x37 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 cnt: 10 err_cnt: 0 suc_cnt: 10 time: 2s 1675us 10:transmit 16 bytes. ==== write data ====
-
【FAQ】全志D1芯片 如何在tina使用tplayerdemo 进行rtsp拉流说明?
make menuconfig 选项rtsp
Allwinner libcedarx....................................... libcedarx for allwinner (PACKAGE_libcedarx [=y]) Select cedarx configuration options [*] Support rtsp stream
打上附件的 "support_rtsp.patch"补丁(主要是开启rtsp依赖的模块, 后续会加宏配置好,只选rtsp就好) support_rtsp.patch
diff --git a/allwinner/tina_multimedia/libcedarx/libcore/parser/Makefile.am b/allwinner/tina_multimedia/libcedarx/libcore/parser/Makefile.am index f33af3743..5d1aeaf0c 100755 --- a/allwinner/tina_multimedia/libcedarx/libcore/parser/Makefile.am +++ b/allwinner/tina_multimedia/libcedarx/libcore/parser/Makefile.am @@ -72,4 +72,7 @@ if PLS_PARSER_ENABLE SUBDIRS += pls endif -SUBDIRS += base \ No newline at end of file +SUBDIRS += remux + +SUBDIRS += base + diff --git a/allwinner/tina_multimedia/libcedarx/libcore/parser/base/CdxParser.c b/allwinner/tina_multimedia/libcedarx/libcore/parser/base/CdxParser.c index 44305f3c6..a24273fbc 100755 --- a/allwinner/tina_multimedia/libcedarx/libcore/parser/base/CdxParser.c +++ b/allwinner/tina_multimedia/libcedarx/libcore/parser/base/CdxParser.c @@ -50,7 +50,7 @@ static struct ParserUriKeyInfoS asfKeyInfo = }; #endif -#if 0 +#if 1 extern CdxParserCreatorT remuxParserCtor; static struct ParserUriKeyInfoS remuxKeyInfo = { @@ -460,7 +460,7 @@ void AwParserInit(void) AwParserRegister(&movParserCtor, CDX_PARSER_MOV, &movKeyInfo); #endif -#if 0 +#if 1 AwParserRegister(&remuxParserCtor, CDX_PARSER_REMUX, &remuxKeyInfo); #endif diff --git a/allwinner/tina_multimedia/libcedarx/libcore/parser/base/Makefile.am b/allwinner/tina_multimedia/libcedarx/libcore/parser/base/Makefile.am index 9dbb73590..86174f94e 100755 --- a/allwinner/tina_multimedia/libcedarx/libcore/parser/base/Makefile.am +++ b/allwinner/tina_multimedia/libcedarx/libcore/parser/base/Makefile.am @@ -65,6 +65,8 @@ libcdx_parser_la_LIBADD += $(top_srcdir)/libcore/parser/avi/libcdx_avi_parser.la libcdx_parser_la_CFLAGS += -DAVI_PARSER_ENABLE endif +libcdx_parser_la_LIBADD += $(top_srcdir)/libcore/parser/remux/libcdx_remux_parser.la + if TS_PARSER_ENABLE libcdx_parser_la_LIBADD += $(top_srcdir)/libcore/parser/ts/libcdx_ts_parser.la libcdx_parser_la_CFLAGS += -DTS_PARSER_ENABLE
使用ffmpeg 进行推流
sudo ffmpeg -stream_loop -1 -i 720x480.mp4 -vcodec copy -acodec aac -rtsp_transport tcp -f rtsp rtsp://1.116.32.155/media/test.mp4
拉流
4.1 wifi联网 4.2 tplayerdemo rtsp://1.116.32.155/media/test.mp4
rtsp 播放效果:
-
周易AIPU加持!R329助力研电赛人工智能选题
第十七届中国研究生电子设计竞赛(以下简称“研电赛”)现已正式开赛,比赛已经进入报名阶段,各企业命题也已在官网发布,此次研电赛安谋科技(Arm China)的命题将聚焦人工智能领域,围绕着基于特定开发平台的理性智能体设计的主题进行,有着周易AIPU处理器加持的全志R329,也被选中为此次研电赛的开发平台之一。
本次研电赛选择搭载R329的开发平台是SIPEED Maix Sense R329开发平台。MaixSense开发板的套件包含了一个搭载全志R329的高度集成核心板,以及一个多功能的IO扩展底板,R329内置的周易AIPU处理器同时支持智能语音和视频图像处理的功能,开发者可以直接在开发板上轻松跑通相关AI模型,其特性与安谋科技聚焦人工智能领域设计的选题不谋而合。
Maix Sense板载资源
Maix Sense硬件参数
R329是全志科技针对智能语音旗舰市场推出的一款高集成度SoC,搭载双核A53 1.5G CPU,内置双核400MHz HiFi4 和 800MHz AIPU 0.25TOPS,可以满足各种智能产品开发的各种需求。
本次研电赛给出的赛题围绕具体应用场景,结合传感器、算法和控制设备,搭建完整理性智能体以实现针对性的场景业务需求为目的,对算法、传感器、控制设备等内容不限制使用,但需要基于所选择的开发平台进行开发,安谋科技也提供了50套免费的开发平台供本次比赛申请使用,关于本次研电赛的基本信息、报名链接等将一一给出:
报名链接:
https://cpipc.acge.org.cn/cw/hp/6开发平台申请:
https://aijishu.com/e/1120000000259996R329介绍文档:
https://r329.docs.aw-ol.com/命题技术交流群:
https://aijishu.com/e/1120000000259996R329技术讨论区:
https://bbs.aw-ol.com/category/2/r329 -
Melis4.0系统架构以及V833/V831-IPC开发QuickStart
本帖转自CSDN:https://blog.csdn.net/tugouxp/article/details/116980346
作者:papaofdoudouMelis4.0简介
Melis3.0及之前的版本过于严格的模块化设计,增加了系统不必要的复杂性,有过度设计之嫌,随着新的特性和问题fix补丁的不断引入,系统出现了僵化,顽固,粘滞,重复的问题, 使设计难以改变,难以重用,难以做正确的事情,八股文式的模块封装机制,产生了大量重复的代码,不但增加了系统运行时负载,还限制了系统的开放能力和方案容量。有鉴于此,Melis4.0在Melis3.0的基础上,对系统架构进行了重新设计,去除了全系统模块化,混合内核等复杂的内核机制,淡化模块化带来的影响,采用大内核小模块,弱化混合内核机制,增强宏内核特性等措施。增加和完善了对Posix,V4L2, OpenMax,MPP, Debug子系统,USB UVC Class, Linux style的设备管理以及抽象Hardware层的支持,整体开发风格向Linux靠拢,不但使系统更容易使用, 而且在多媒体处理能力上得到了增强。
Sunxi OS 全家福以及Melis的定位:
Melis4.0整体架构如下图所示.
内核仍然是基于熊大的rt-thread进行拓展,作为一款有高度的高性能内核,RT-Thread不但可以适配MCU级的应用方案,还可以支撑Linux级的大型应用方案,这是zephyr, freeRTOS等内核无法比拟的,另外,pthread, shell,以及网络组件等部分也是从熊大社区直接拿来用的,在这里向熊大表示深深的感谢 @熊大.RT-Thread, RTOS, 物联网操作系统 - RT-Thread物联网操作系统
Melis4.0的方案架构如下图所示,其中核心的两大块,Middleware和Framework起到呈上启下,继往开来的作用,呈的是应用端的枝繁叶茂,花团锦簇,启的是内核乃至硬件的各种潜能。
具体的讲,浅色的ffmpeg/gstreamer组件是未来计划引入的部分,之所以引用gstreamer是因为相对于其它的多媒体框架,Gstreamer整体框架调度性能更优秀,而FFMPEG的跨平台性性能和软编性能够好,可以作为核心编解码组件,两者相辅相成,并不冲突。
V4L2,OMX, MPP的引入增强的了系统对多媒体的扩展能力和兼容能力,不但可以降低sensor移植时的难度,还能够有效利用既有Linux上的方案成果,使用户能够快速从Linux向小成本的Melis4.0方案上迁移。
其中,RTOS HAL存在的目的就是为了屏蔽Sunxi F,V,R三系平台的差异,本来计划用CMSIS,结果搞着搞着,渐渐发现CMSIS面向的主要是MCU级的接口定义,针对偏重型一些的SOC并不适用,所以我就自定义了一套HAL API标准,取名 RTOS-HAL。
驱动操作SOP:
环境配置:
配置环境变量:
选择工程,公版工程为:v833-smart-doorbell
如果需要进行自定义配置,可以在lunch基础上执行make menuconfig
如果不需要,PASS掉这一步即可。
编译:
执行make -j4,启动多线程编译:
打包:
执行pack,进行打包:
烧录:
验证:
启动系统:
链接好串口终端,在终端下,输入vin_preview,即可从屏幕端观察到图像输出。
Sensor->CSI->ISP->V4L2->VIPP通路显示:
Melis4.0 RoadMap
Melis系统的强项是多媒体处理,未来Melis系统开发的三个方向:1.图形图显增强,支持用户界面设计工具.
2.网络:melis虽然移植了各种各样的协议栈,但支持的模组却不多,这方面有待加强。
3.开源.Melis4.0上的网络
物联网时代,大家都很关连接能力,那么在Melis上,是如何使用网络的呢?关于Melis系统对外挂模组集成的支持这一块,现在基本所有模组都需要下载一个firmware,这个firmware是三方原厂提供,质量保证,驱动这边需要负责对其装载到模组端,运行以及控制进行管理。
我们则提供了完整的,经过验证过的lwip协议栈,WPA_supplicant实现和稳定的SDIO接口支持,其它的工作包括:
1.解决一些模组驱动的编译问题,firmware的装在还是驱动自己做的.
2.sdio驱动适配, 调试。
3.配置供电,引脚等。
4.主控主要和wpa_supplicant对接,做指令下发,数据接收。
5.按照传统的TCP/IP理解的话,主控协议栈都在网络层及以上,驱动层主要处理的都在mac层,驱动层我们不需要太多关心,但是也不全是库,具体看驱动厂商愿意开源多少了。
另外,不同的模组MAC层的实现不同,分为softmac和fullmac,支持工作量也不一样,softmac需要在SOC端跑mac协议,工作量会大一些,但总体还好.
Norflash方案一例:
典型方案的flash layout
产品优势
Melis4.0的一大优势是快启动,体验快起的丝滑:快速出图内核配置
-# CONFIG_BOOTUP_TURBO is not set -# CONFIG_DISABLE_ALL_DEBUGLOG is not set +CONFIG_BOOTUP_TURBO=y +CONFIG_DISABLE_ALL_DEBUGLOG=y +CONFIG_SDK_THUMB_MODE=y
同时为了加快启动速度,减少不必要的打印,要把sys_config.fex中的debug_mode配置为0
boot0阶段会根据debug_mode来决定是否初始化串口,没有打印,系统会更快的进入应用。
debug_mode设置为0会带来一个小小的技术性问题,就是由于卡量产固件中也会使用这个配置,没有打印,卡量产、卡升级的进度无法显示出来,用户会误以为卡升级功能卡死,问题误报,实际上只是一个打印,功能仍然是正常的.
298ms出图效果:
关于性能:
固件SIZE大小优化,Melis系统支持全系统Thumb编译,所以最终生成的固件是16位/32位指令混合的形式。可以减少1/3的固件SIZE。性能优化,NEON优化在复制内存块时提供了1.5倍的加速。 在melis上,针对支持SIMD的ARM处理器,Melis支持全系统NEON编译,典型的,通过它连接使用的C库就可以看得出来:
关于智能应用:
基于V833平台,运行目标检测网络的用例:
USB UVC Camera设备支持:
UVC Camera设备功配置单:
重新编译,打包,烧录,启动后,确认是否存在/dev/uvcout设备节点,此节点是用于输出视频流到PC端的节点:
用例已经编译到系统中,首先将USB线连接 Mircro USB Port(端侧)和USB-Type A port(PC侧),执行用例命令uvcout:
此时,PC侧已有响应:
用guvcview工具查看:
作为MSC设备挂载:
有些产品形态在使用过程中,由于外壳的层层保护,内置的TF卡不宜拆装,比如打猎机这种形态,由于常年安装在野外,TF是由产品外壳保护的,这个时候,就需要一种机制将设备里面的数据读出来,Melis4.0支持MSC功能,可以将设备的存储器挂载到PC上直接读取。
最终配置如下:
编译,烧录固件,执行mscd命令:
执行mscd命令后显示如下,红色的部分是打印的调试信息,可忽略。
此时,盘符已经在PC上出现了,截图如下:
ADB调试:
首先在端侧开启ADBD服务:
重新编译,打包,烧录,启动后,在PC端控制台执行adb shell连接端侧ADBD服务。
注意:MSC 和 ADBD属于两个configuration,不能同时使用。
总结:
之前和熊大交流过melis新架构和rtt smart架构的变化的对比,熊大意味深长的讲“RTT 拥抱多进程,你们放弃多进程”。熊大说的不假,但是放弃模块化和多进程不一定意味着远离Linux的开发风格,Melis系统可以认为是单进程多线程的应用方案,整个系统就是一个进程,在一个进程中当然可以做到与Linux的风格很像的。 -
Melis3.0系统Quick Start
本帖转自CSDN:https://blog.csdn.net/tugouxp/article/details/78320833
作者:papaofdoudouMelis3.0系统Quick Start
Melis3.0系统是全志科技面向数字媒体和IoT领域的轻量级物联网嵌入式实时操作系统,主打视频多媒体编,解,录周边产品应用。在整合内部开发资源的基础上, 与开源社区深度合作,致力建立服务于多媒体视频编解码,语音处理的端侧基础设施平台. Melis3.0系统具备极致性能, 极简开发,富媒体,富组件,wireless连接,模块化开发等功能特点, 可广泛应用于多媒体播放器,行车记录仪,游戏模拟器,DVB/DTMB机顶盒,儿童早教机,卡拉OK机, 传感器/网络模组,智能家居,智能IPC等产品的开发.
Melis3.0在Sunxi OS家谱中的位置,关于Melis4.0的介绍请移步这里。
支持多平台,多方案的多媒体架构
Melis 3.0的特性
极简开发
- 代码量500~600万行,属于中大规模RTOS系统方案.
- 基于Kbuild构建扩展,支持C和C++编译. menuconfig字符菜单配置界面. 支持Windows/Linux开发环境.
- 支持ARMCC/GNU GCC CrossToochain.
- 支持ARMDS5/开源GDB+OCD+ICE(JLink/OpenJtag/CMSIS-DAP)调试方式.
- 支持KGDB裸机串口调试 .
- 支持ADBD服务,可以通过adb进行调试与连接.
- 采用松散多bin架构,系统由具备独立子功能的binary动态组合实现,模块之间高内聚,低耦合。便于分布式开发.
- 支持V4L2和AW多媒体框架,方便集成各种编,解码方案.
- 支持32位虚拟内存管理,单进程多线程模式共享系统0~4G虚拟存储空间.
- 支持Devicetree和fex系统配置文件方式进行运行时配置,修改配置不需要重新编译内核。
- 支持静态共享库(区别于UNIX&Linux家族的动态共享库),库的加载运行地址是固定的。
网络服务
- 支持BLE/BE协议栈
- 支持IPV4/IPV6网络协议栈.
- 支持基于mbedtls的安全套接字服务
- 标准SDIO驱动兼容各种外挂模组,客户可自行决定支持新的WIFI模组.
模块化设计
类微内核架构(混合内核),支持应用,驱动和中间件代码独立编译链接,映像高度压缩,不用时可卸载节省运存。
模块化要求内存管理支持二级页表设计。
高性能内核
按照内核和方案解耦的方式设计,当前基于zephyr,rt-thread双内核, 具备硬实时能力,核心架构良好,支持极小footprint的设备.
RT-Thread和zephyr各有特点,两者比较,zephyr有广度,但高度欠缺,而RT-Thread恰恰相反,它支持较高的Linux语义,有高度,但广度不如zephyr.
Melis3.0主要面向全志自身的编码解码产品线进行开发,需要一个API层面有高度的系统,但不需要支持太多的开发板,所以广度方面不甚重要.
内存管理
支持slab内存管理算法,有效减少内存外部碎片。
支持虚拟空间动态创建,页面可支持4K大小调度策略
在任何时候,系统执行有资格获得处理器的优先级最高的任务,在优先级相同的情况下,采用时间片轮转的调度策略. 这种调度策略有个简单的名字,叫做Round Robin(RR)调度策略
丰富的文件系统支持
支持常见文件系统格式,包括:
- Fat12/16/32
- exFat
- udffs
- ntfs
- cdfs
- devfs
- ramfs
- littlefs
- spiffs
- 支持文件系统块缓冲bcache,可以有效提高存储价质的读写效率。
丰富的多媒体和GUI支持
集成全志AW多媒体框架和OrangeGUI/MiniGUI图形框架,可支持中大规模多媒体录,编,解码产品的开发.
支持全部的常见音频格式,支持的视频解码格式包括:
- H263/H264/H265
- VP9/VP8/VP6
- MJPEG/MPEG2/MPEG4
- AVS/AVS2/AVS+
- Divx/Xvid
- WMV1/WMV2/WMV3/WMV9/VC-1
- Sorenson Spark
- 支持LBC在线压缩/解压缩算法,有效提升高清多媒体码流的兼容度.
等主流解码格式。
支持的编码格式包括: - H264
- H265
- VP8
- MJPEG
Posix兼容
支持Posix完整语义,面向MacOS,Unix,Linux可移植应用/中间件的跨系统移植.设备管理
支持linux style的设备文件系统,所有设备以设备节点的方式向应用提供服务,应用通过标准化的接口open/read/write/ioctl/close对设备进行操作在Linux上搭建Melis3.0 开发环境
1.下载交叉编译工具链:
GCC 6.3.0 On Linux
或者执行: git clone https://github.com/caozilong/melis-toolchain.git2.ToolChain的安装与配置:
进入到控制台程序,在$(HOME)(其它具备权限的目录也可,例如/opt)目录下创建tools目录 并把工具链包copy到此目录.
执行下图命令将工具链解压到当前目录下:
设置环境变量,执行如下命令将工具链所在目录加入到当前用户的PATH环境变量下:
设置完毕后,退出当前终端重新登录, 在新控制台下执行命令行
arm-melis-eabi-gcc -v
输出信息如下图所示,表示编译器安装正确:
3.获取Melis SDK源码:
略
4.配置melis sdk.
Melis的构建系统基于Linux Kbuild实现,执行进入到 ./src 目录,执行 make menuconfig
选用默认的配置,直接保存.config文件,即是D100 数字电视老人机方案.
5.编译melis sdk.
退出menuconfig菜单, 执行make melis (或者make, make all) 编译完整的SDK, 编译结束后,目标文件放置在./src/workspace/$(project) 目录下.
关于内核选型,为什么基于RT-Thread内核?
最初进行开发的时候,候选内核其实有包括RT-Thread,FreeRTOS, Arm mBed, Zephyr以及 Nuttx在内的多个内核,并且Zephy已经开发适配到能够做视频播放的程度,但是最终选择了RT-Thread作为Melis3.0方案新架构的内核。有些内核,比如zephyr,mbed os, freeRTOS,这类系统用户很多,支持的开发板也很多,但是局限在一些小资源的开发板上,总体上给人的感觉是广度有余,高度不足,难以承担多媒体这种比较垂直类的应用, 而RT-Thread则具备更多的可能性,经过多方考量,最终选择了它并进行了深度适配。
-
RTSP串流加上人形/人脸检测
本篇介绍的过程并不复杂,原理上比上篇博客介绍的要简单一些,有趣的地方在于将之前的编码落盘和预览转换为了两路RTSP子码流输出,这样码流可以传的更远一些。
V833/V831目标检测demo方案开发_papaofdoudou的博客-CSDN博客_v833
整个应用方案原理如下:
闲言少叙,下面开始技术流水账:
第一步,打开Tina EMAC以太网络支持
由于uart2和emac phy是share pin的,所以需要事先将默认配置为uart功能的PIN角修改为emac功能,方法是cconfigs到目录,修改board.dts:之后,重新编译,打包,烧录,系统启动后,执行ifconfig -a命令,查看是否存在eth0设备,正常情况下,eth0网卡应该如下图所示,会出现的,此时你会看到以太网和本地回环两个网络接口设备。
平台和开发PC主机建立网络连接
RTSP是一种实时串流协议,当然离不开网络连接,由于我们需要将端侧的网络流通过RTSP协议推送到PC侧实时播放,所以必须建立端侧平台和PC主机的以太网连接。将端侧平台和PC主机通过网线直连,如果你不想拉断主机原来的网络,可以用USB转以太网接口给主机再扩展一个以太网口,产品如下图所示,好用不贵,价格实惠,不超过50买个不错的,由于不想断网,这里我采用后者的方案:
关于USB网卡类产品的体验,可以参考博客:
绿联USB网卡的使用记录_papaofdoudou的博客-CSDN博客_usb网卡
在windows10上插入usb转以太网卡,无需安装驱动,在用网线将两个以太网口直连起来。之后就需要设置端侧IP地址了。
首先为了保证两台设备在同一个网段,先看一下USB网卡的IP地址,下图的以太网适配器 以太网3即是USB网卡的IP地址,是169.254.173.243,169.254.0.0到169.254.255.255是B类IP保留地址。如果你的IP地址是自动获取IP地址,而你在网络上又没有找到可用的DHCP服务器,这时你将会从169.254.0.1到169.254.255.254中临时获得一个IP地址,我们就用这个地址。
我们需要保证端侧和PC在同一个网段内,所以必须将端侧配置成同一个网段的B类保留地址,方法和过程如下:
ifconfig -a ifconfig eth0 up ifconfig eth0 169.254.173.233 netmask 255.255.0.0 ifconfig -a
设置IP为169.254.173.233。
网络测试:直接ping peer.从PC端ping端侧:
端侧ping PC侧:
都可以ping通,网络配置完成。
为了避免每次重启都要配IP的不便,我们将配置放在脚本中,cdevice到设备配置目录,打开busybox-init-base-files/etc/init.d/rc.final文件,将以上命令添加到末尾:
重新编译,烧录,之后每次启动IP都已经配置好。
方便多了,接下来实现方案。
方案流程
一个有趣的问题
我们知道,常用的开源人形检测算法,比如yolo系列,mobilenet-ssd系列,它们的输入图形尺寸都是正方形的,也就是长==宽,比如yolov支持的三种尺寸输入(320x320, 416x416, 608x608),以及mobilenet-ssd(224x224).虽然VIPP也支持无极缩放和变AR缩放,但是为了保证图像缩放时不裁剪内容也不将内容压缩,通路中建议使用16:9的长宽比压缩数据,如下图所示:
所以,这里就有一个不匹配的问题发生,如果VIPP通路送的图像是16:9的,而网络则希望输入图为方形的,该如何处理?
处理的方法很简单,你不是16:9吗,直接将9补到16就可以了,也就是说,图像下方留黑边,以352*352的网络输入尺寸为例,下方补154行的而黑边,变成方形,如下图所示:
480*480的做法类似
这样做虽然可以解决问题,但是有一个弊端,就是黑边的部分其实是在浪费算力,使原本可以提高的帧率部分的算力浪费在黑边的无效处理上。
架构都是虚的,无图无真相,我们看一下处理前后YUV图形的变化:
处理前是的480*270:
处理后的480*480:
这样处理,由于下面补的部分经过有效卷积后为0,不会检测出框来.而上方内容区的检测内容和实际的16:9的检测图像效果是等效的,所以,网络的检出结果可以直接经过线性变换还原回大图中对应框的位置。
关于补黑边的逻辑很简单,由于VIPP输出16:9的NV12格式YUV, 我们直接将Y部分拷贝到方形图像的Y部分,UV拷贝到方形尺寸的UV部分,其余补0即可。对应代码,注意下图的SIZE对应方形尺寸的长宽,不对应VIPP输出图的长宽,只有这样,才能起到补黑边的作用。
RTSP实跑测试:
打开VLC,选择媒体->打开网络串流... 对话框,配置RTSP服务端的拉流地址,点确定后即可拉流成功。人形检测算法的效果:
人脸检测算法效果:
基于以上功能,可以做出很多有意思的应用场景出来,比如,在安防领域,由于网络带宽的限制,一般需要压低编码码率从而减少带宽,这会造成整幅画面的区域码率变低。但是针对具体场景,又有针对个别区域做精细控制的需求,这是一对矛盾,而AI检测框的机制给解决这种矛盾提供了一种优美的方案,在行业内,它被称为ROI(Region Of Interst).
本帖转自CSDN:https://blog.csdn.net/tugouxp/article/details/123692835
作者:papaofdoudou -
【资源汇总】D1-H/D1s/哪吒开发板成果汇报第三期(2022.4.08)
点亮技能 开发者 简介 基于D1开发板的 微信/支付宝 物联网(MQTT)支付平台 kw_ 基于D1哪吒开发板/联网/HDMI显示收款二维码/扫码付款成功后声光提醒用户并触发IO动作 D1s运行自制立即式UI框架--XUI tripod9 在D1s运行自制立即式UI框架--XUI 试试用哪吒MQ板运行lvgl demo(鼠标) mangogeek 在哪吒MQ上跑LVGL 芒果的D1s麻雀还没出来,我们的 40PIN RGB 转 I80 模块安排上了 whycan 基于D1s能直接点I80屏的模块 一位韩国的开发者做得D1s板子 陈塘关李靖 韩国开发者基于D1s做的板子,看样子是想做一个便携式的视频播放类的东西 FlyThings UI@ D1s F133 kkzhong 在D1s的板子上跑UI框架FlyThings LicheeRV 入门开发一帖通 LicheeRV 针对LicheeRV系列开发板的开发内容 【单板仅需99】D1哪吒计算条上线!为智能家居提供高性价比的RISC-V算力 LicheeRV D1哪吒计算条发布 Sipeed Lichee RV 86 Panel 智能家居 中控开发板 支持Linux WAFT memory D1计算条配套86盒开发板 SIPEED Lichee RV DOCK 全志D1开发板 memory D1哪吒计算条的使用记录 RISC-V SoC + AI 在全志 D1「哪吒」开发板上,跑个 ncnn 神经网络推理框架的 demo verimake 在D1哪吒上应用ncnn的demo 小麻雀直接驱动树莓派的DSI屏 mangogeek 麻雀板载15Pin直接接上DSI屏 【新人上手】D1哪吒开发板Tina Linux下动态调CPU频率和电压 Randolph D1哪吒开发板Tina Linux下动态调CPU频率和电压 【新人上手】D1哪吒开发板Tina Linux下查看系统内存和测试存储器件读写速率 Randolph D1哪吒开发板Tina Linux下查看系统内存和测试存储器件读写速率 【新人上手】D1哪吒开发板Tina Linux下分区烧写与分区只读 Randolph D1哪吒开发板Tina Linux下分区烧写与分区只读 【新人上手】D1哪吒开发板Tina Linux下查看DDR参数、内核版本、uboot版本和系统版本 Randolph D1哪吒开发板Tina Linux下查看DDR参数、内核版本、uboot版本和系统版本 【新人上手】D1哪吒开发板Tina Linux 下WiFi的连接 Randolph D1哪吒开发板Tina Linux 下WiFi的连接 小巧精湛!基于D1s的全开源芒果派-哪吒MQ正式开售! mangogeek 基于D1s的哪吒MQ发布 天冷了,给麻雀加衣 mangogeek 给哪吒MQ做的外壳 给麻雀做件衣裳 memory 给哪吒MQ做的外壳 芒果派麻雀到手试玩 memory 新发布的哪吒MQ从零开始试用 带WiFi的【开源D1s开发板】来了,Xassette-Asterisk升级完成 whycan 在校大学生自制的D1s开发板的迭代升级 又在全志d1开发板上玩ncnn nihui 用全新工具链优化的ncnn 欢迎测试 opencv-mobile-4.5.4 有 rvv 支持的预编译包 nihui rvv intrinsic 的代码魔改,编译出了有 rvv 支持的 opencv-mobile D1移植rtl8723wifi驱动 yaoxiaoyao 往D1上移植WIFI驱动 Lichee RV 内存小了?改到2G来玩! YuzukiTsuru 给D1哪吒计算条焊上2G内存 【跑个算法】之模拟退火算法在D1的执行 Randolph 之前有做过一个VLSI布局布线相关的课题,学了一点关于模拟退火算法的毛皮,今天来跑个demo复习一下 看到一篇关于D1 RVV性能评测文,这性能什么水平? yanmingjian D1 riscv芯片上运行rt-thread进行RVV性能评估 D1移植讯为7寸MIPI屏 yaoxiaoyao 移植MIPI屏幕 基于D1s的QT5移植 ricky 移植QT5 把QtWebKit浏览器引擎移植到了D1上,内存消耗约80MB a44670 把Qt+WebKit的组合移植到了D1上面 【开源】YuzukiNezha D1s 核心板 Mini-PICE 核心板 YuzukiTsuru 自制的 D1s 核心板 Mini-PICE 核心板 麻雀D1s开发板支持buildroot 一件构建了 100ask 这个是针对 芒果PI MQ 开发板专门支持的 BR2_EXTERNAL 外部扩展支持 在哪吒sdk中新建D1s方案的方法 Randolph 在哪吒sdk中新建D1s方案的方法 OpenPPL也已支持了在RISC-V的D1上跑啦! huioma 深度学习推理引擎OpenPPL移植到D1上 【D1 Lichee RV & WIFI】RTL8723DS & BS 的 WiFi 移植记录 dalaoshu D1计算条的RTL8723DS & BS 的 WiFi 移植记录 D1 LicheeRV Dock 移植RTL8723DS驱动 YuzukiTsuru D1 LicheeRV Dock 移植RTL8723DS驱动 在 Lichee RV 上玩游戏:DOOM YuzukiTsuru 移植了一下DOOM到 Lichee RV 使用 WSL2 编译 LicheeRV Tina BSP YuzukiTsuru 使用 WSL2 编译 LicheeRV Tina BSP 因为喜欢用ADB,因为不喜欢插拔TF卡,所以D1直接用PhoenixSuit烧录TF(SDNAND) TEVET 直接用PhoenixSuit烧录TF(SDNAND) 用buildroot自带的genimage把tina dragon的活干完,让dragon无活可干 whycan 用buildroot自带的genimage把tina dragon的活干完 麻雀板使用micropython开发lvgl lyr2021 在哪吒MQ上用micropython开发lvgl 芒果派 MPi-MQ1 全志 D1s 开发板初体验 ytf4425 芒果派 MPi-MQ1 全志 D1s 开发板初体验 搭建了一个 Tina 的 opkg 仓库,软件包逐渐完善中 YuzukiTsuru 实现Tina用包管理器安装软件 linuxfb屏幕旋转90度 yaoxiaoyao 在不改变驱动和设备树的情况下,在应用层实现屏幕旋转也能满足我们的需求 既然D1/D1S的LVGL8有G2D和双缓 那么LVGL7也值得拥有 TEVET 给LVGL7配上tina最新的版本放出了双缓和G2D 在TinaV2.2 D1s上面搞LVGL8去调用G2D旋转让LVGL8的软件旋转再见 TEVET 在TinaV2.2 D1s上面搞LVGL8去调用G2D旋转让LVGL8的软件旋转再见 开源个D1-H核心板 MajorTom 开源个D1-H核心板 DongshanNezhaSTU RISCV架构学习开发板来啦! 100ask 基于全志D1-H的东山派发布 基于全志D1-H的东山派发布,将免费开源完整嵌入式视频课程 budbool 基于全志D1-H的东山派发布 风靡一时的自行车码表已经移植到D1s了 FASTSHIFT X-Track码表移植到D1s上 华清远见推出基于全志D1-H的嵌入式RISC-V实验箱 budbool 华清远见推出基于全志D1-H的嵌入式RISC-V实验箱 【惊】在麻雀上运行国产rt-smart系统 mangogeek 在芒果派哪吒MQ上运行rtt 在D1的tina上整上overlayfs TEVET 在D1的tina上整上overlayfs D1哪吒开发板使用 RTL8723DS 作为WIFI中继 memory D1哪吒开发板使用 RTL8723DS 作为WIFI中继 基于全志D1-H芯片的首台64位RISC-V便携式计算机上市 budbool 基于全志D1-H芯片的首台64位RISC-V便携式计算机上市 使用 LicheeRV 86 Panel 与 Tina BSP 实现 RGB 与 SPI 双屏显示 budbool 使用 LicheeRV 86 Panel 与 Tina BSP 实现 RGB 与 SPI 双屏显示 D1-H / D1s / T113 / R528 SDK 、资料下载大汇总 uuuuid 资料汇总 D1s有芯片了兄弟们!开源方案设计送芯片啦 DOT小文哥 真 · D1s芯片 D1s芯片&开发板 ready,准备放出来给大家玩 DOT小文哥 真 · D1s芯片&开发板 【Yuzuki手作】D1s-Nezha开发板手贴预售版 YuzukiTsuru D1s-Nezha开发板手贴预售版 MQ-Pro 量产版本V1.2归来 mangogeek D1 MQ-Pro Buildroot-2022.2主线已经支持Nezha开发板,原来有这么多大佬喜欢这款芯片,真的太香了! 100ask Buildroot主线已支持Nezha开发板 Nezha D1s 教程:使用PhoenixSuit刷写系统 YuzukiTsuru 用PhoenixSuit刷写系统 -
稚晖君又整活啦!基于全志F1C200s的超迷你&低成本开发板开源
B站最强小电视(基于全志H3)
钢铁侠机械臂曾经靠着“B站最强小电视”以及号称“钢铁侠机械臂”等硬核产品出圈的稚晖君又双叒叕来整活啦!
这次稚晖君开源了基于全志F1C200s的一个超迷你&低成本的Linux开发板,项目名为Planck Pi,F1C200s采用的ARM9架构,SIP内置DDR的极简封装很适合作为Linux开发入门板卡。
稚晖君在个人的github仓库开源了软硬件所有资料,该项目内核版本用的5.4.77,移植了Debian系统,跟Ubuntu基本没有差异。
项目资料获取方式:
Github链接:https://github.com/peng-zhihui/Planck-Pi项目说明
本项目是一个基于全志F1C200s芯片的超迷你&低成本的Linux开发板,本来是用于个人的某个小项目调试,现把所有硬件、软件(u-boot、内核、root-fs)开源出来。板卡成本应该不到50RMB,而且提供了很多资料,很适合用于新手作为入门Linux学习的开发板。
板载资源:
- 一个OLED 128x80
- 一个麦克风 & 功放可以外接喇叭
- 双面不同功能的Type-C接口分别提供USB转串口以及USB-OTG功能
- 一个USB-A口用于外接设备
- SD卡插槽
- 引出绝大部分IO
芯片介绍
全志F1C200s是全志的一款高度集成、低功耗的移动应用处理器,可用于多种多媒体音视频设备中。
全志F1C200s基于ARM 9架构,芯片集成了SiP的DDR,外围电路可以极其简单;它支持高清视频解码,包括H.264、H.263、MPEG 1/2/4等,还集成了音频编解码器和I2S/PCM接口,是一款开发简单、性价比较高的产品,也适合用来做入门级的Linux开发板。
参数规格
芯片框图
硬件开发
原理图见仓库的源文件和PDF,需要说明的点是:板子的Type-C采用正反插不同功能,正面是USB转TTL串口功能,用于内核调试,反面是芯片的USB功能,在内核中我开启了USB的RNDIS网卡也就是说可以通过这个USB口模拟出一个网卡然后共享电脑的网络,也就不需要外接WiFi和以太网模块了很方便。
由于芯片只有一个USB接口,因此为了能使板子作为Host外接其他设备,我在板卡上添加了一个OTG的跳线:
正常情况下不接跳线的话OTG功能为Device模式,也就是可以通过TypeC接口模拟网卡或者其他设备如MTP;当插上跳线帽之后,就可以作为Host在右边的A口插入USB设备了如U盘、键盘、鼠标等,注意此时C口的USB功能失效,需要通过串口登录板子。项目资料目录
-
基于全志D1-H的东山派发布,将免费开源完整嵌入式视频课程
从环境搭建到教学课程的全栈开源
百问科技发布了针对嵌入式教育学习定制的开发板——东山哪吒STU。主控为使用全志科技研发的RISC-V架构芯片——D1-H,其使用的是阿里平头哥基于RISC-V架构设计的C906核心。
国内嵌入式软件教育教父韦东山老师团队将针对此开发板制作一整套完整的嵌入式课程,并会将课程通过百问网官网、B站等渠道免费开放。课程内容将会包括环境搭建、设备驱动、应用开发、系统开发等多个方面,延续韦老师系列课程的优秀质量,实现真正的普惠教育,实现从指令集-开发板-教学课程的全栈开源。
韦东山老师B站首页:https://space.bilibili.com/275908810?spm_id_from=333.337.0.0课程章节:
全志D1-H主板
D1-H 是全志科技首款基于RISC-V指令集的芯片,集成了阿里平头哥64位C906核心,此芯片以国产全开源为出发点,提供了全套的开发SDK源码、交叉编译器以及配套的开发手册、芯片寄存器手册,也为广大开发者提供了多种开源示例,使得东山哪吒STU开发板可以同时满足玩乐、学习、工作等多种应用场景的需求。
主板板载资源:- 正面:TYPE-C TTL供电与调试接口,直接连接电脑USB接口即可实现 串口调试与供电二合一,无需额外的连接线
- 正面:RJ45千兆以太网接口,主要用于网络启动系统下载内核等操作,方便调试开发
- 正面:TYPE-C的USB OTG接口,用于烧写系统与作为OTG主从设备使用
- 正面:引出 HDMI接口,可用于连接显示器等设备
- 背面:TF卡接口,可用于调试与连接TF卡启动系统
- 背面:预留SPI NAND FLASH 芯片焊盘位置,可以直接焊接SPI Nand FLASH芯片
D1-H部分资源参数:- 玄铁C906,RISC-V CPU
- DDR2/DDR3,up to 2GB
- 4K@30fps,H265/H264
- MPEG/JPEG/VC1/MJPEG
- USB2.0,SDIO3.0,1000M EMAC
- I2S/PCM,DMIC
- SD3.0/eMMC5.0
- SPI Nor/Nand Flash
- ......
DIY全针脚底板
全针脚DIY底板,将主板的所有未使用引脚全都引出到底板排针上,并提供全部硬件设计资料,可以自行使用嘉立创设计生产,也可以直接从我们这里购买。主要适用于喜欢DIY的同学。
项目底板
专门的项目底板正在设计中,拥有标准的USB接口,常见的模块排针接口,RGB&MIPI屏幕接口、4G模块接口、声卡、麦克风等设备,敬请期待。
百问网
百问网是韦东山老师牵头的以嵌入式培训为核心的专业机构,百问网以线上培训的模式,为社会各界提供专业的嵌入式学习开发板及配件,和嵌入式Linux、单片机、安卓、编程语言等线上培训课程服务。
韦东山老师作为多家知名培训机构特邀讲师,著有《嵌入式Linux应用开发完全手册》。同时,韦东山老师作为各平台讲师,已成功录制了上千期教学视频,在业内有口皆碑。由韦老师牵头的百问网已在嵌入式培训方向深耕多年,已发布教学课程无数,此次百问网针对东山哪吒STU设计配套课程也将延续之前的优秀课程框架。
全志科研教育领域发展
全志目前已经和中科院软件所、清华大学、同济大学、华侨大学、挪威科技大学等高校及科研机构达成了科研教育合作意向,也有进行与华清远见辅助校园实验室建设的工作,未来全志将继续积极助力高校科研和教育行业,促进产学研合作。
《华清远见推出基于全志D1-H的嵌入式RISC-V实验箱》:https://bbs.aw-ol.com/topic/1162/
-
一条命令搞定全志XR806编译环境
本文转自极术社区:https://aijishu.com/a/1060000000311849、
XR806是全志科技旗下子公司广州芯之联研发设计的一款支持Wi-Fi和BLE的高集成度无线MCU芯片,基于安谋科技STAR-MC1架构,支持鸿蒙L0系统。
全志科技同时也发布了一款XR806的开发板,具体长这样:
之前有很多刚接触它的朋友,被编译环境难住了。
最近正好有空,干脆把相关代码和编译工具都做了配置和集成,做了个容器给大家。
系统是Ubuntu 20.04,OpenHarmony代码版本是1.0.1_release。
项目地址:
https://hub.docker.com/r/verdureorange/ubuntu_xr806/具体用法:
装完Docker以后,docker run -it verdureorange/ubuntu_xr806:v1 /bin/bash如果你是Windows或Mac,可以下载Docker桌面版:
https://www.docker.com/products/docker-desktop/主要代码在/root/xr806_openharmony/,大家可以直接在里面编译或调试。
如果有不清楚的地方可以看以下视频,感谢前面很多开发者所做的工作。
-
基于全志D1-H芯片的首台64位RISC-V便携式计算机上市
近日,基于D1-H生态开发板的第一款RISC-V便携式计算机也已经宣布上市。
技术开放社区Clockwork公司于近日宣布要推出一个由 RISC-V CPU驱动的DevTerm。这款设备被命名为 DevTerm R-01,售价为 239 美元,其配置主要如下:
- ClockworkPi v3.14 主板
- R-01 核心模块 ,RISC-V 64位单核 RV64IMAFDCVU @ 1.0GHz,1GB DDR3
- Ext.模块
- 6.86英寸 IPS 显示屏
- Clockwork 65%比例的键盘
- 电池模块
- 双扬声器
- 58毫米200dpi 热敏打印机组件
- shell 和 bracket 系统
- 带有 clockworkOS 的32GB 高速 TF 存储卡
这款RISC-V便携式计算机一经推出便受到了RISC-V International CEO Calista Redmond 女士的青睐,“RISC-V生态快速发展,并可为开发者提供低成本且便捷易用的64 bit的RV终端。”
RISC-V作为免费且灵活的开放指令集架构,在单片机和FPGA方面,已经取得了良好的开端。在商业落地方面,也开始应用在物联网安全、工业控制等领域。
2021年4月,全志推出D1-H芯片,其是全球首颗量产的搭载平头哥玄铁906 RISC-V的应用处理器,为万物互联AIoT时代提供了新的智能关键芯片。
D1-H搭载了阿里平头哥64位C906核心,支持RVV,1GHz+主频,可支持Linux、RTOS等系统。同时支持最高4K的H.265/H.264解码,内置一颗HiFi4 DSP,最高可外接2GB DDR3,可以应用于智慧城市、智能汽车、智能商显、智能家电、智能办公和科研教育等多个领域。
2021年5月,全志携手平头哥发布基于玄铁C906处理器的全球首款支持64bit RISC-V指令集的D1-H哪吒开发板,为推动RISC-V生态在国内的发展贡献了厚重的力量。
微信公众号推文:基于全志D1-H芯片的首台64位RISC-V便携式计算机上市
-
Reply: 基于T507的 OpenHarmony 分布式音乐播放器2.0
@ufbycd 引用B站的一条评论:看起来慢是因为没有GPU驱动,不过现在应该解决了,开源GPU用户态驱动预计三月底入仓。(据说,华为自研RS+开源GPU驱动,可以让大家在普通的芯片上感受到接近麒麟的性能)
-
华清远见推出基于全志D1-H的嵌入式RISC-V实验箱
这是华清远见首次使用全志芯片推出实验箱走进高校实验室,D1-H使用的是阿里平头哥的RISC-V C906核心,同时集成了大量全志自研IP,这将极大地提升国内高校嵌入式教育的国产化程度,让课程和实验都在自主可控的科研环境下进行。
全志D1-H主板
主板采用全志的D1-H芯片为主控平台,D1-H是全球首颗量产的搭载平头哥玄铁906 RISC-V的应用处理器,支持RVV,1GHz+主频,可支持Linux系统。同时支持最高4K的H.265/H.264解码,最高可外接2GB DDR3,可以应用于智慧城市、智能汽车、智能商显、智能家电、智能办公和科研教育等多个领域。
主板采用底板+核心板结构、核心板采用6层板设计、底板和核心板采用BTB连接器,方便插拔,且比传统插针稳定可靠,同时也配备了一块7寸电容触控屏。
主板上留有2个2.54MM间距,双排插孔,50PIN标准Arduino插座,可以更换核心板运行不同的操作程序。
配套Linux系统编程实验Linux网络编程实验、Linux设备驱动实验、综合项目案例,更加方便学习,易上手。
嵌入式RISC-V实验箱
实验箱分为两层结构,上层实验层,下层储物层,储物层的存放各种接线、传感器、一件还原节点等;主板带管理锁,方便储物层的设备安全管理;针对实验功能,实验箱具有整体化、一体化设计,铝合金包边,承重抗压不易变形。
图片
D1-H可以独立直接访问3个标准的Arduino板载扩展接口及资源;也可以D1-H通过串口通信间接访问,再由串口通信直接访问控制3个标准的Arduino板载扩展接口及资源,形成微处理器+微控制器的复合应用。
除了主控板之外,实验箱内还有若干外接的拓展板。
- RISC-V核心板
- 4G/GPS二合一模块
- 无线传感网节点
- 传感器模块
- arduino键盘控制扩展板
- arduino电机控制扩展板
- arduino传感器扩展板
- RFID模块
*......
华清远见
在科研教育领域华清远见已深耕多年,科研中心推出的嵌入式、物联网、人工智能、AIoT虚拟仿真实验室已广泛应用于包括武汉大学、哈尔滨工业大学、北京邮电大学等知名高校,在业内拥有极高的知名度及认可度。
于武汉大学完成物联网、嵌入式ARM实验箱的验收培训
为山东大学搭建的高级自动驾驶飞行控制平台
为沈阳工业大学搭建的人工智能实验室全志科研教育领域发展
全志目前已经和中科院软件所、清华大学、同济大学、华侨大学、挪威科技大学等高校及科研机构达成了科研教育合作意向,未来全志将继续积极助力高校科研和教育行业,促进产学研合作。
同济大学分享现场 -
OpenHarmony智能体重秤
一、简介
本demo基于OpenHarmony3.1Beta版本开发,该样例能够接入数字管家应用,通过数字管家应用监测体重秤上报数据,获得当前测量到的体重,身高,并在应用端形成一段时间内记录的体重值,以折线图的形式表现出来,根据计算的BMI值来提醒当前身体健康状态,推送健康小知识。
1.交互流程
交互图如上图所示,智能体重称整体方案原理图可以大致分成:智能体重称设备、数字管家应用、云平台三部分。智能体重称通过MQTT协议连接华为IOT物联网平台,从而实现命令的接收和属性上报。 关于智能设备接入华为云IoT平台的详细细节可以参考 连接IOT云平台指南;智能设备同数字管家应用之间的设备模型定义可以参考profile .
2.实物简介
如上图示,左边为xr806模组,右边为超声波测距模块,echo脚连接PA19,Triq脚连接PA20,Vcc脚连接5V电源,Gnd脚接地,
如上图示,右边为称重模块,clk脚接PB15,dt脚接PB14,vcc脚接5V,gnd脚接地,称重传感器红色线接E+,黑色线接E-,白色线接A-,绿色线接A+
左边xr806模块左下角k1按键,长按k1按键不放,同时上电,4-5秒后松开按键,可以清除已保存得配网信息
xr806模块,在设备正常工作后,按k1按键,可以初始化当前得重量为0,高度为0
二、 快速上手
1.硬件准备
- xr806模组
- hcsr04超声波模块
- hx711称重模块带支架托盘
- 预装HarmonyOS手机一台
2、环境准备
参照文档: XR806快速上手指导文档3、编译前准备
设备侧代码下载
具体仓库地址:https://gitee.com/openharmony-sig/knowledge_demo_smart_home/下载方式:使用git 命令下载,指令如下(用户也可以根据需要将该仓库fork到自己的目录下后进行下载)
cd ~ git clone git@gitee.com:openharmony-sig/knowledge_demo_smart_home.git
代码拷贝
cp -rfa ~/knowledge_demo_smart_home/dev/team_x ~/openharmony/vendor/ cp -rfa ~/knowledge_demo_smart_home/dev/third_party/iot_link ~/openharmony/third_party/
SOC代码下载替换
当前官方soc代码由于DHCP暂未适配,所以暂时不支持AP模式,这时需要下载并替换之前SOC代码。如果官方soc代码已修复该问题,可忽略此步骤。git clone https://gitee.com/moldy-potato-chips/xr806_-ap_mode.git mv ~/openharmony/device/soc/allwinner ~/allwinner.org // 不建议直接删除, cp -raf xr806_-ap_mode ~/openharmony/device/soc/allwinner
整合并修改完成后的目录结构如下图
修改文件
- 修改编译依赖
打开 device/soc/allwinner/xradio/xr806/BUILD.gn,添加应用依赖(deps字段):
module_group(module_name) { modules = [ "src", "project", "include", ] configs = [ ":SdkLdCconfig", ] deps = [ "//vendor/team_x/smart_weight_scale/demo_smart_weight_scale:smart_weight_scale" ] }
- 修改编译方式
将demo依赖的库编译方式(static_library)修改为(source_set):
具体依赖查看demo_smart_weight_scale目录下的BUILD.gn:
deps = [ "../../common/iot_wifi_xradio:iot_wifi", "../../common/iot_cloud:iot_cloud", "//third_party/cJSON:cjson", "../../common/iot_boardbutton_xradio:iot_boardbutton", "../../common/iot_boardled_xradio:iot_boardled_xradio", ]
其中//third_party/cJSON目录下的BUILD.gn建议参照下面的修改:
source_set("cJSON") { sources = [ "cJSON.c", "cJSON_Utils.c", ] ldflags = [ "-lm" ] }
third_party/iot_link目录下的各级使用到的BUILD.gn也需要将编译方式修改为source_set,或者将所有需要编译的文件放在iot_link目录的BUILD.gn中,如下:
source_set("iot_link") { sources = [ "link_log/link_log.c", "link_misc/link_random.c", "link_misc/link_ring_buffer.c", "link_misc/link_string.c", "network/dtls/dtls_al/dtls_al.c", "network/dtls/mbedtls/mbedtls_port/dtls_interface.c", "network/dtls/mbedtls/mbedtls_port/mbed_port.c", "network/dtls/mbedtls/mbedtls_port/timing_alt.c", "network/mqtt/mqtt_al/mqtt_al.c", "network/mqtt/paho_mqtt/port/paho_mqtt_port.c", "network/mqtt/paho_mqtt/port/paho_osdepends.c", "network/mqtt/paho_mqtt/paho/MQTTClient-C/src/MQTTClient.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTConnectClient.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTConnectServer.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTDeserializePublish.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTFormat.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTPacket.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSerializePublish.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSubscribeClient.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTSubscribeServer.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTUnsubscribeClient.c", "network/mqtt/paho_mqtt/paho/MQTTPacket/src/MQTTUnsubscribeServer.c", "oc_mqtt/oc_mqtt_al/oc_mqtt_al.c", "oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_profile.c", "oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_profile_package.c", "oc_mqtt/oc_mqtt_profile_v5/oc_mqtt_event.c", "oc_mqtt/oc_mqtt_tiny_v5/oc_mqtt_tiny.c", "oc_mqtt/oc_mqtt_tiny_v5/hmac.c", "queue/queue.c", ] cflags = [ "-Wno-unused-variable" ] cflags += [ "-Wno-unused-but-set-variable" ] cflags += [ "-Wno-sign-compare" ] cflags += [ "-Wno-unused-parameter" ] cflags += [ "-Wno-unused-function" ] ldflags = [ "-Wl,-rpath-link=//device/xradio/xr806/xr_skylark/lib" ] ldflags += [ "-lmbedtls" ] include_dirs = [ "inc", "link_log", "link_misc", "queue", "oc_mqtt/oc_mqtt_tiny_v5", "oc_mqtt/oc_mqtt_profile_v5", "oc_mqtt/oc_mqtt_al", "network/dtls/mbedtls/mbedtls_port", "network/mqtt/paho_mqtt/port", "network/mqtt/paho_mqtt/paho/MQTTClient-C/src", "network/mqtt/paho_mqtt/paho/MQTTPacket/src", "//third_party/mbedtls/include/", "//third_party/mbedtls/include/", "//third_party/cJSON", "//kernel/liteos_m/components/cmsis/2.0", "//device/xradio/xr806/xr_skylark/include/net/mbedtls-2.2.0/", ] defines = [ "MQTTCLIENT_PLATFORM_HEADER=paho_osdepends.h", "WITH_DTLS", "MBEDTLS_AES_ROM_TABLES", "MBEDTLS_CONFIG_FILE=\"los_mbedtls_config_dtls.h\"", "CONFIG_DTLS_MBEDTLS_CERT", "CONFIG_DTLS_MBEDTLS_PSK", "CFG_MBEDTLS_MODE=PSK_CERT", "CONFIG_OC_MQTT_TINY_ENABLE=1" ] }
- 修改iot_link中的部分文件
1.third_party/iot_link/network/mqtt/paho_mqtt/port/paho_mqtt_port.c
测试发现,当fd为0的时候,在执行recv时会立马返回-1,因此做下面规避操作。
static int __socket_connect(Network *n, const char *host, int port) { ... int tmpfd = socket(AF_INET,SOCK_STREAM,0); // to skip fd = 0; fd = socket(AF_INET,SOCK_STREAM,0); if(fd == -1) { return ret; } close(tmpfd); // to skip fd = 0; ... }
系统setsockopt函数未适配,因此需要做下面的修改:
static int __socket_read(void *ctx, unsigned char *buf, int len, int timeout) { int fd; int ret = 0; #if 0 struct timeval timedelay = {timeout / 1000, (timeout % 1000) * 1000}; if(NULL== uf) { return ret; } fd = (int)(intptr_t)ctx; ///< socket could be zero if (timedelay.tv_sec < 0 || (timedelay.tv_sec == 0 && timedelay.tv_usec <= 0)) { timedelay.tv_sec = 0; timedelay.tv_usec = 100; } if(0 != setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,&timedelay,sizeof(struct timeval))) { return ret; //could not support the rcv timeout } int bytes = 0; while (bytes < len) { int rc = recv(fd, &buf[bytes], (size_t)(len - bytes), 0); printf("[%s|%s|%d]fd = %d, rc = %d\n", __FILE__,__func__,__LINE__, fd, rc); if (rc == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { bytes = -1; } break; } else if (rc == 0) { bytes = 0; break; } else { bytes += rc; } } return bytes; #else int bytes = 0; fd_set fdset; struct timeval timedelay = {timeout / 1000, (timeout % 1000) * 1000}; if(NULL== buf) { return ret; } fd = (int)(intptr_t)ctx; ///< socket could be zero if (timedelay.tv_sec < 0 || (timedelay.tv_sec == 0 && timedelay.tv_usec <= 0)) { timedelay.tv_sec = 0; timedelay.tv_usec = 100; } timedelay.tv_sec = 2; FD_ZERO(&fdset); FD_SET(fd, &fdset); ret = select(fd + 1, &fdset, NULL, NULL, &timedelay); if (ret > 0) { while (bytes < len) { int rc = recv(fd, &buf[bytes], (size_t)(len - bytes), 0); // printf("[%s|%s|%d]fd = %d, rc = %d, errno=%d(%s)\n", __FILE__,__func__,__LINE__, fd, rc,errno, strerror(errno)); if (rc == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { bytes = -1; } break; } else if (rc == 0) { bytes = 0; break; } else { bytes += rc; } } } return bytes; #endif }
2.third_party/iot_link/network/dtls/mbedtls/mbedtls_port/dtls_interface.c
在文件顶部添加打印函数定义以及添加mbedtls_calloc以及mbedtls_free的定义,否则编译会提示错误:
#define MBEDTLS_LOG LINK_LOG_DEBUG #ifndef mbedtls_calloc #define mbedtls_calloc calloc #endif #ifndef mbedtls_free #define mbedtls_free free #endif
系统部分mbedtls接口不一致,固需要注释部分接口代码:
mbedtls_ssl_context dtls_ssl_new(dtls_establish_info_s *info, char plat_type) { ... if (info->psk_or_cert == VERIFY_WITH_PSK) { /* if ((ret = mbedtls_ssl_conf_psk(conf, info->v.p.psk, info->v.p.psk_len, info->v.p.psk_identity, strlen((const char *)info->v.p.psk_identity))) != 0) { MBEDTLS_LOG("mbedtls_ssl_conf_psk failed: -0x%x", -ret); goto exit_fail; } */ } ... } int dtls_shakehand(mbedtls_ssl_context *ssl, const dtls_shakehand_info_s *info) { ... if (MBEDTLS_SSL_IS_CLIENT == info->client_or_server) { ret = mbedtls_net_connect(server_fd, info->u.c.host, info->u.c.port, info->udp_or_tcp); if( 0 != ret) { ret = MBEDTLS_ERR_NET_CONNECT_FAILED; goto exit_fail; } } else { //server_fd = (mbedtls_net_context*)atiny_net_bind(NULL, info->u.s.local_port, MBEDTLS_NET_PROTO_UDP); ///< --TODO ,not implement yet } ... } void dtls_init(void) { (void)mbedtls_platform_set_calloc_free(calloc, free); (void)mbedtls_platform_set_snprintf(snprintf); // (void)mbedtls_platform_set_printf(printf); }
在iot_link/network/dtls/mbedtls/mbedtls_port/mbed_port.c文件中的dtls_imp_init()函数中,也需要注释掉未实现的接口,否则编译报错:
int dtls_imp_init(void) { int ret =-1; // (void)mbedtls_platform_set_calloc_free(calloc, free); // (void)mbedtls_platform_set_snprintf(snprintf); // (void)mbedtls_platform_set_printf(printf); ret = dtls_al_install(&s_mbedtls_io); return ret; }
3.在文件iot_link/network/mqtt/paho_mqtt/port/paho_osdepends.c中添加对应timersub和timeradd的实现(系统中未实现该函数):
// add this for "timersub" && "timeradd" #ifndef timersub #define timersub(s,t,a) (void) ( (a)->tv_sec = (s)->tv_sec - (t)->tv_sec, \ ((a)->tv_usec = (s)->tv_usec - (t)->tv_usec) < 0 && \ ((a)->tv_usec += 1000000, (a)->tv_sec--) ) #endif #ifndef timeradd #define timeradd(s,t,a) (void) ( (a)->tv_sec = (s)->tv_sec + (t)->tv_sec, \ ((a)->tv_usec = (s)->tv_usec + (t)->tv_usec) >= 1000000 && \ ((a)->tv_usec -= 1000000, (a)->tv_sec++) ) #endif
4.编译中会有部分头文件提示找不到,这个时候直接将其注释即可
(iot_link/network/mqtt/paho_mqtt/port/paho_osdepends.h): #define INVALID_SOCKET SOCKET_ERROR // #include <sys/socket.h> #include <sys/param.h> #include <sys/time.h> // #include <netinet/in.h> // #include <netinet/tcp.h> // #include <arpa/inet.h> // #include <netdb.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #endif #if defined(WIN32) #include <Iphlpapi.h> #else // #include <sys/ioctl.h> // #include <net/if.h> #endif
5.因为弱引用导致无法链接相关符号,因此需要注释以下几个文件中的弱引用。
文件一 third_party/iot_link/network/dtls/dtls_al/dtls_al.c #if 0 __attribute__((weak)) int dtls_imp_init(void) { LINK_LOG_DEBUG("%s:###please implement dtls by yourself####",__FUNCTION__); return -1; } #endif extern int dtls_imp_init(void); 文件二 third_party/iot_link/network/mqtt/mqtt_al/mqtt_al.c #if 0 __attribute__((weak)) int mqtt_imp_init(void) { LINK_LOG_DEBUG("%s:###please implement mqtt by yourself####",__FUNCTION__); return -1; } #endif extern int mqtt_imp_init(void); 文件三 third_party/iot_link/oc_mqtt/oc_mqtt_al/oc_mqtt_al.c #if 0 __attribute__ ((weak)) int oc_mqtt_imp_init(void) { LINK_LOG_DEBUG("%s:###please implement oc mqtt by yourself####",__FUNCTION__); return 0; } __attribute__ ((weak)) int oc_mqtt_demo_main(void) { LINK_LOG_WARN("Please implement the oc mqtt v5 demo yourself"); return -1; } #endif extern int oc_mqtt_demo_main(void);
- 修改GPIO查找方式
因为GPIO框架修改了设备驱动注册的管脚号,导致应用无法根据HCS的引脚操作对应的GPIO,此问题已经提issue,如果该问题已解决,可以忽略此步骤。
打开drivers/framework/support/platform/src/gpio/gpio_manager.c,将cntlr->start = start;注释即可。
static int32_t GpioManagerAdd(struct PlatformManager *manager, struct PlatformDevice *device) { uint16_t start; struct GpioCntlr *cntlr = CONTAINER_OF(device, struct GpioCntlr, device); if ((start = GpioCntlrQueryStart(cntlr, &manager->devices)) >= GPIO_NUM_MAX) { PLAT_LOGE("GpioCntlrAdd: query range for start:%d fail:%d", cntlr->start, start); return HDF_ERR_INVALID_PARAM; } // cntlr->start = start; DListInsertTail(&device->node, &manager->devices); PLAT_LOGI("%s: start:%u count:%u", __func__, cntlr->start, cntlr->count); return HDF_SUCCESS; }
- 将对应的驱动文件复制到drvier对应目录:
因为主仓代码中未将对应的驱动文件合并到driver/adpater/platform对应的目录下,固需要手动将文件拷贝到对应目录。若主仓已合入,可忽略此步骤。
// 拷贝gpio驱动 cp -af device/soc/allwinner/xradio/drivers/gpio/gpio_xradio.* driver/adpater/platform/gpio // 修改driver/adpater/platform/gpio/BUILD.gn文件,加上gpio_xradio的编译 hdf_driver(module_name) { sources = [] if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) { sources += [ "gpio_bes.c" ] } if (defined(LOSCFG_SOC_COMPANY_ALLWINNER)) { sources += [ "gpio_xradio.c" ] } include_dirs = [ "." ] }
为了节省ram资源,可以把无用的资源先关闭,如关闭内部codec,将 device/soc/allwinner/xradio/xr806/project/prj_config.h中的PRJCONF_INTERNAL_SOUNDCARD_EN设置为0,如下:
/* Xradio internal codec sound card enable/disable */ #define PRJCONF_INTERNAL_SOUNDCARD_EN 0
4、代码编译
#首先可以查看一下hb的版本,如果hb版本为0.4.4版本就不需要更新。
查看hb版本
hb --version
更新hb, 以下指令需要在openharmony SDK根目录执行
pip3 uninstall ohos_build
pip3 install build/li编译命令: hb set // 如果是第一次编译,Input code path 命令行中键入"./" 指定OpenHarmony工程编译根目录后 回车,
如下图所示,使用键盘上下键选中wifi_skylark
hb build // 如果需要全量编译,可以添加-f 选项
生成的固件保存在out/xradio/smart_weight_scale目录下
5、固件烧录
参照文档: XR806快速上手指导文档
6、设备配网
在设备上电前需准备好安装了数字管家应用的HarmonyOS手机,详情见数字管家应用开发, 并在设置中开启手机的NFC功能;
写设备NFC标签,详细操作见设备NFC标签指导文档;
烧录完成后,上电。开发者在观察开发板上状态LED灯以8Hz的频率闪烁时,将手机上半部靠近开发板NFC标签处(无NFC标签的可用NFC贴纸替代);
碰一碰后手机将自动拉起数字管家应用并进入配网状态;
配网过程中需要 连接设备的AP热点,然后填写需要配置的wifi的密码;
最后点击配置,手机会将ssid以及对应的密码通过AP热点发送到设备。
原文链接:https://growing.openharmony.cn/mainPlay/detail?sampleId=3300
gitee地址:https://gitee.com/openharmony-sig/knowledge_demo_smart_home/tree/master/dev/docs/smart_weight_scale#智能体重秤 -
基于XR806在OpenHarmony下实现的智能门锁样例
一. 简介
本demo是基于Openharmony 3.1 Beta本版开发,不仅可以接收数字管家应用下发的指令来控制门锁开启,而且还可以通过数字管家设置不同的开锁密码以及一次性密码,实现给临时用户一个临时密码,保证门户安全。当然除了开锁的功能,智能门锁还可以通过检测门与门锁距离自动上锁以及如果长时间未上锁,上报告警消息到数字管家,及时提醒用户关门关锁等功能。
1. 交互流程:
如上图所示,智能门锁整体方案原理图可以大致分成:智能门锁设备、数字管家应用、云平台三部分。智能门锁通过MQTT协议连接华为IOT物联网平台,从而实现命令的接收和属性上报。 关于智能设备接入华为云IoT平台的详细细节可以参考 连接IOT云平台指南;智能设备与数字管家应用之间的设备模型定义可以参考profile .2. 实物简介:
如上图所示,上面是XR806开发板,中间的是hcsr04超声波距离传感器,下面是E53_IA1扩展板。我们是通过距离传感器感应门的距离,进行自动上锁,也可以在一定时间内无法上锁而产生告警信息上传到数字管家。
E53_IA1扩展板主要是通过控制电机模拟开关锁的一个动作。
接线说明:
距离传感器有4根线,其中echo接XR806的PA19,trig接XR806的PA20。
E53_IA1扩展板主需要接3根线,其中IO控制脚接XR806的PA12。
3. 实物操作体验
二. 快速上手
1. 硬件准备
- xr806模组
- hcsr04超声波模块
- E53_IA1扩展板
- 预装HarmonyOS手机一台
2. 环境准备
参照文档: XR806快速上手指导文档3. 编译前准备
设备侧代码下载
具体仓库地址:https://gitee.com/openharmony-sig/knowledge_demo_smart_home/下载方式:使用git 命令下载,指令如下(用户也可以根据需要将该仓库fork到自己的目录下后进行下载)
cd ~/ git clone git@gitee.com:openharmony-sig/knowledge_demo_smart_home.git
代码拷贝
cp -rfa ~/knowledge_demo_smart_home/dev/team_x ~/openharmony/vendor/ cp -rfa ~/knowledge_demo_smart_home/dev/third_party/iot_link ~/openharmony/third_party/
整合并修改完成后的目录结构如下图:
SOC代码下载替换
当前官方soc代码由于DHCP暂未适配,所以暂时不支持AP模式,这时需要下载并替换之前SOC代码。如果官方soc代码已修复该问题,可忽略此步骤。git clone https://gitee.com/moldy-potato-chips/xr806_-ap_mode.git mv ~/openharmony/device/soc/allwinner ~/allwinner.org // 不建议直接删除, cp -raf xr806_-ap_mode ~/openharmony/device/soc/allwinner
修改文件
- 修改编译依赖
打开 device/soc/allwinner/xradio/xr806/BUILD.gn,添加应用依赖(deps字段):
module_group(module_name) { modules = [ "src", "project", "include", ] configs = [ ":SdkLdCconfig", ] deps = [ "//vendor/team_x/smart_lock/demo_smart_lock:app_smart_lock" ] }
- 修改编译方式
将demo依赖的库编译方式(static_library)修改为(source_set):
具体依赖查看demo_smart_lock目录下的BUILD.gn:
deps = [ "../../common/iot_wifi_xradio:iot_wifi", "../../common/iot_cloud:iot_cloud", "../../common/iot_boardbutton_xradio:iot_boardbutton", "../../common/iot_list:iot_list", "../../common/iot_sntp:iot_sntp", "../../common/iot_boardled_xradio", "//third_party/cJSON:cJSON" ]
其中//third_party/cJSON目录下的BUILD.gn建议参照下面的修改:
source_set("cJSON") { sources = [ "cJSON.c", "cJSON_Utils.c", ] ldflags = [ "-lm" ] }
- 修改iot_link中的部分文件
third_party/iot_link目录下文件改动较多,此处以patch方式做修改,patch在路径在(team_x/smart_lock/iot_link_patch_xr806.patch),主要修改内容:
1.BUILD.gn修改source_set.
2.fd为0时通讯会异常,做了规避操作(socket创建时多创建一个)。
3.部分mbedtl接口未适配,做一些简单适配以及无法适配的接口需要注释
4.弱引用导致无法链接相关符号,因此需要注释相关文件中的弱引用。
5.osDelay接口在XR806中未实现,需要替换为OS_Msleep();以上内容修改的修改均在iot_link_patch_xr806.patch中,只需将该patch文件拷贝到third_party/iot_link目录下,并执行打补丁即可:
cp -af vendor/team_x/smart_lock/iot_link_patch_xr806.patch third_party/iot_link // 拷贝patch文件到对应目录 cd third_party/iot_link/ // cd 到对应目录 patch -p1<./iot_link_patch_xr806.patch // 执行打patch动作
- 修改GPIO查找方式
因为GPIO框架修改了设备驱动注册的管脚号,导致应用无法根据HCS的引脚操作对应的GPIO,此问题已经提issue,如果该问题已解决,可以忽略此步骤。
打开drivers/framework/support/platform/src/gpio/gpio_manager.c,将cntlr->start = start;注释即可。
static int32_t GpioManagerAdd(struct PlatformManager *manager, struct PlatformDevice *device) { uint16_t start; struct GpioCntlr *cntlr = CONTAINER_OF(device, struct GpioCntlr, device); if ((start = GpioCntlrQueryStart(cntlr, &manager->devices)) >= GPIO_NUM_MAX) { PLAT_LOGE("GpioCntlrAdd: query range for start:%d fail:%d", cntlr->start, start); return HDF_ERR_INVALID_PARAM; } // cntlr->start = start; DListInsertTail(&device->node, &manager->devices); PLAT_LOGI("%s: start:%u count:%u", __func__, cntlr->start, cntlr->count); return HDF_SUCCESS; }
- 将对应的驱动文件复制到drvier对应目录:
因为主仓代码中未将对应的驱动文件合并到driver/adpater/platform对应的目录下,固需要手动将文件拷贝到对应目录。若主仓已合入,可忽略此步骤。
注意:(如果已用xr806_-ap_mode替换原来的soc文件,则需要将拷贝原来被替换的对应文件,因为xr806_-ap_mode中的驱动文件非最新版)
// 拷贝gpio驱动 cp -af device/soc/allwinner/xradio/drivers/gpio/gpio_xradio.* driver/adpater/platform/gpio // 修改driver/adpater/platform/gpio/BUILD.gn文件,加上gpio_xradio的编译 hdf_driver(module_name) { sources = [] if (defined(LOSCFG_SOC_COMPANY_BESTECHNIC)) { sources += [ "gpio_bes.c" ] } if (defined(LOSCFG_SOC_COMPANY_ALLWINNER)) { sources += [ "gpio_xradio.c" ] } include_dirs = [ "." ] }
- 为了节省ram资源,可以把无用的资源先关闭,如关闭内部codec,将 device/soc/allwinner/xradio/xr806/project/prj_config.h中的PRJCONF_INTERNAL_SOUNDCARD_EN设置为0,如下:
/* Xradio internal codec sound card enable/disable */ #define PRJCONF_INTERNAL_SOUNDCARD_EN 0
4. 编译&烧录
更新hb
首先可以查看一下hb的版本,如果hb版本为0.4.4就不需要更新。## 查看hb版本 hb --version ## 更新hb, 以下指令需要在openharmony SDK根目录执行 pip3 uninstall ohos_build pip3 install build/lite
编译
1.hb set 选择demo指令,具体命令如下: hb set -root
如下图所示,使用键盘上下键选中smart_lock
2.hb build 全量编译命令
hb build -f
烧录
参照文档: XR806快速上手指导文档5. 设备配网
- 在设备上电前需准备好安装了数字管家应用的HarmonyOS手机,详情见数字管家应用开发, 并在设置中开启手机的NFC功能;
- 写设备NFC标签,详细操作见设备NFC标签指导文档;
- 烧录完成后,上电。开发者在观察开发板上状态LED灯以8Hz的频率闪烁时,将手机上半部靠近开发板NFC标签处(无NFC标签的可用NFC贴纸替代);
- 碰一碰后手机将自动拉起数字管家应用并进入配网状态。
- 配网过程中需要 连接设备的AP热点,然后填写需要配置的wifi的密码。
- 最后点击配置,手机会将ssid以及对应的密码通过AP热点发送到设备。
- 当设备端蓝灯每5s闪烁一次则代码设备联网成功。
6. 设置主密码并通过主密码开锁
- 数字管家下发修改密码指令以及修改后的密码
- 设备端接收指令以及修改后的密码进行更新
- 数字管家下方开锁指令以及开锁密码
- 设备端接收指令并判断密码是否正确,正确的话电机转动模拟开锁,否则向数字管家发送密码错误信息
7. 设置临时密码
- 数字管家下发创建临时密码指令以及创建临时密码时的信息,主要包括临时密码标志,临时密码信息,临时密码生效时间段。
- 设备端接收指令并保存信息。
- 临时用户通过数字管家下发开锁指令以及开发密码。
- 设备端接收指令并判断密码是否正确以及该密码是否在当前时间段生效,正确的话电机转动模拟开锁,否则向数字管家发送密码错误信息。
8. 门未关告警
- 设备端执行完开门指令后会一直通过超声波距离传感器来判断门是否可以关闭,如果超过5分钟发现门不能关闭,则主动向数字管家发送门未关的告警信息。
- 数字管家通过系统消息查看到告警消息。
- 数字管家可以从设备控制页面进入到告警消息页面,也可以通过点击告警消息进入到告警界面。
9. 模拟自动关门
- 设备端执行开门动作后,用手或者其他物体靠近超声波距离传感器
- 设备每隔10S检测一次超声波距离传感器,发现在设定范围内(模拟门已关),则控制电机转动(模拟关门动作),同时上报关门信息到数字管家
转载自OpenHarmony官网:https://growing.openharmony.cn/mainPlay/detail?sampleId=3299
gitee地址:https://gitee.com/openharmony-sig/knowledge_demo_smart_home/tree/master/dev/docs/smart_lock -
在基于全志D1的RISCV64架构下 Kubernetes(k8s) 相关软件编译
本帖转载自知乎,原文链接:https://zhuanlan.zhihu.com/p/443777923
作者:在路上Kubernetes(k8s)
中文文档:http://docs.kubernetes.org.cn/
Kubernetes是Google开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便对应用请求进行负载均衡。
在Kubernetes中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。
为什么要使用容器?通过以下两个图对比:
传统的应用部署方式是通过插件或脚本来安装应用。这样做的缺点是应用的运行、配置、管理、所有生存周期将与当前操作系统绑定,这样做并不利于应用的升级更新/回滚等操作,当然也可以通过创建虚机的方式来实现某些功能,但是虚拟机非常重,并不利于可移植性。
新的方式是通过部署容器方式实现,每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。相对于虚拟机,容器能快速部署,由于容器与底层设施、机器文件系统解耦的,所以它能在不同云、不同版本操作系统间进行迁移。
容器占用资源少、部署快,每个应用可以被打包成一个容器镜像,每个应用与容器间成一对一关系也使容器有更大优势,使用容器可以在build或release 的阶段,为应用创建容器镜像,因为每个应用不需要与其余的应用堆栈组合,也不依赖于生产环境基础结构,这使得从研发到测试、生产能提供一致环境。类似地,容器比虚机轻量、更“透明”,这更便于监控和管理。
编译环境
- OS : openEuler Linux 5.4.61 (openEuler镜像下载地址:openEuler-D1-wifi-hdmi-docker-20210826.img.bz2)
- ARCH : riscv64
- 平台 : 全志D1开发板
- go版本 : go version go1.17 linux/riscv64
go和docker安装
详见教程:在D1/openEuler上安装docker,并运行docker/Debian
docker安装前请先确认一下内核配置是否满足:check-config.sh
安装完成docker后还是无法正常使用docker(报错与libseccomp相关),建议重新安装一下libseccomp。
conntrack安装
## 安装支持包 yum install -y bison yum install -y flex ## 手动编译安装conntrack-tools, 因为openEuler-riscv64的软件源中还没有 git clone git://git.netfilter.org/conntrack-tools ./configure --prefix=/usr/local/conntrack && make && make install
在编译conntrack-tools会出现依赖包文件找不到的问题,根据提示进行安装即可。目前openEuler软件源有大部分的包,可直接下载软件以及对应的devel软件。依赖包源码下载地址:https://git.netfilter.org/
# 依赖包如果需要手动安装,一般默认安装路径为/usr/local/lib,需要把对应文件拷贝到以下位置中 /usr/include/libnetfilter_conntrack/ /usr/include/libnfnetlink/ /usr/lib64/libnetfilter_conntrack.la /usr/lib64/libnetfilter_conntrack.so /usr/lib64/libnetfilter_conntrack.so.3 /usr/lib64/libnetfilter_conntrack.so.3.1.3 /usr/lib64/pkgconfig/libnetfilter_conntrack.pc /usr/lib64/pkgconfig/libnfnetlink.pc
编译Kubernetes
由于目前还没有基于riscv架构的kubernetes release包,因此需要从源码入手。
RISCV移植过的源码地址:https://github.com/carlosedp/kubernetes/tree/riscv64_build
对应的PR地址:https://github.com/kubernetes/kubernetes/pull/86011
# 全部编译 KUBE_BUILD_PLATFORMS=linux/riscv64 make # 编译指定组件 KUBE_BUILD_PLATFORMS=linux/riscv64 make all WHAT=cmd/kubelet GOFLAGS=-v GOGCFLAGS="-N -l"
编译完成后,可以在 _output/local/bin/linux/riscv64/ 中找到需要的二进制文件
[root@openEuler-RISCV-rare kubernetes]# ls -l _output/local/bin/linux/riscv64/ total 1166679 -rwxr-xr-x 1 root root 41711688 Nov 30 06:43 apiextensions-apiserver -rwxr-xr-x 1 root root 6227408 Nov 27 08:57 conversion-gen -rwxr-xr-x 1 root root 5961136 Nov 27 08:51 deepcopy-gen -rwxr-xr-x 1 root root 5944720 Nov 27 08:54 defaulter-gen -rwxr-xr-x 1 root root 107038704 Nov 30 06:44 e2e.test -rwxr-xr-x 1 root root 116796584 Nov 30 06:42 e2e_node.test -rwxr-xr-x 1 root root 38709064 Nov 30 06:44 gendocs -rwxr-xr-x 1 root root 129439752 Nov 30 06:44 genkubedocs -rwxr-xr-x 1 root root 135353224 Nov 30 06:42 genman -rwxr-xr-x 1 root root 3285672 Nov 30 06:42 genswaggertypedocs -rwxr-xr-x 1 root root 38704744 Nov 30 06:45 genyaml -rwxr-xr-x 1 root root 7187536 Nov 30 06:42 ginkgo -rwxr-xr-x 1 root root 1900544 Nov 27 09:09 go-bindata -rwxr-xr-x 1 root root 1801424 Nov 30 06:43 go-runner -rwxr-xr-x 1 root root 3366768 Nov 27 08:51 go2make -rwxr-xr-x 1 root root 102367232 Nov 30 06:44 kube-apiserver -rwxr-xr-x 1 root root 95092736 Nov 30 06:43 kube-controller-manager -rwxr-xr-x 1 root root 33816576 Nov 30 06:42 kube-proxy -rwxr-xr-x 1 root root 37289984 Nov 30 06:45 kube-scheduler -rwxr-xr-x 1 root root 35192832 Nov 30 06:44 kubeadm -rwxr-xr-x 1 root root 39321600 Nov 30 06:44 kubectl -rwxr-xr-x 1 root root 96825128 Nov 30 06:43 kubelet -rwxr-xr-x 1 root root 95325544 Nov 30 06:43 kubemark -rwxr-xr-x 1 root root 4674000 Nov 30 06:43 linkcheck -rwxr-xr-x 1 root root 1572864 Nov 30 06:43 mounter -rwxr-xr-x 1 root root 9751176 Nov 27 09:04 openapi-gen
本实验在全志D1的开发板上进行,系统采用的openEuler,由于CPU核数和存储的限制,以下是编译过程中可能遇到的错误:
(1).network: /usr/local/go/pkg/tool/linux_riscv64/compile: signal: killed
问题重现:go build k8s.io/kubernetes/vendor/github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network: /usr/local/go/pkg/tool/linux_riscv64/compile: signal: killed
解决方法:
属于OOM错误,需要扩大swap分区
1.创建作为swap分区的文件:增加1GB大小的交换分区
dd if=/dev/zero of=/root/swapfile bs=1M count=1024
2.格式化为交换分区文件
mkswap /root/swapfile
3.启用交换分区文件
swapon /root/swapfile
(2)1_output/bin/deepcopy-gen: Permission denied
问题重现:编译kubernetes时遇到报错./hack/run-in-gopath.sh: line 33: _output/bin/deepcopy-gen: Permission denied
解决方法
yum install -y rsync chmod +x _output/bin/deepcopy-gen make clean make clean_generated
然后重新 make 编译操作。
(3)vendor/http://github.com/onsi/ginkgo/internal/remote/syscall_dup_unix.go:10:9: undefined: syscall.Dup2
问题重现:编译kubernetes时遇到报错信息# k8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/remote vendor/github.com/onsi/ginkgo/internal/remote/syscall_dup_unix.go:10:9: undefined: syscall.Dup2
解决方法:
这是由于kubernetes库 go.mod 中依赖 ginkgo ,但是 go.mod 中为 ginkgo v1.10.1 版本,而支持riscv的ginkgo版本从v1.11.0开始,因此在编译前需要将go.mod中的ginkgo版本修改为1.11.0
github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.11.0
(4)tmp存储Out of Memory
解决方法参考,如何增加 Linux 下临时文件夹 /tmp 的大小
http://xiehongfeng100.github.io/2016/01/18/ops-how-to-increase-tmp-partition-size/(5)编译etcd
etcd的RISCV移植已经并入主线可直接下载主线代码:https://github.com/etcd-io/etcd
对应的PR地址:https://github.com/etcd-io/etcd/pull/10834# 编译命令 GOOS=linux GOARCH=riscv64 ARCH=riscv64 GO_BUILD_FLAGS='-v -mod=readonly' ./build.sh # 编译结果 [root@k8s-01 etcd]# ls -l bin/ total 70377 -rwxr-xr-x 1 root root 30298920 Nov 28 05:46 etcd -rwxr-xr-x 1 root root 22962560 Nov 28 05:48 etcdctl -rwxr-xr-x 1 root root 18803030 Nov 28 05:46 etcdutl
etcd编译过程中出现的问题
(1)etcd on unsupported platform without ETCD_UNSUPPORTED_ARCH=riscv64 set
问题重现:安装etcd时出现etcd on unsupported platform without ETCD_UNSUPPORTED_ARCH=riscv64 set,致使etcd启动失败。解决方法
vim etcd/server/etcdmain/etcd.go # 在最后checkSupportArch()函数中添加 if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" { return } # 重新编译 GOOS=linux GOARCH=riscv64 ARCH=riscv64 GO_BUILD_FLAGS='-v -mod=readonly' ./build.sh 重启一下etcd即成功。
编译flannel
尚未进行RISCV移植,目前只支持二进制编译。
源码地址:https://github.com/flannel-io/flannel
# 编译命令 CGO_ENABLED=1 make dist/flanneld
部署flannel需要dist目录下编译出的flanneld二进制文件和mk-docker-opts.sh文件。
-
【直播预告】全志XR806芯片应用实战分享
活动简介
XR806是全志科技研发设计的一款支持WiFi和BLE的IoT芯片,它采用了安谋科技自主研发的星辰“STAR-MC1”处理器,具有集成度高、硬件设计简单、BOM成本低、安全可靠等优点。可广泛满足智能家居、智慧楼宇、工业互联、儿童玩具、电子竞赛、极客DIY 等领域的无线连接需求。为了方便开发者自由开发和定制化,全志在线释放了包括完整系统源码、硬件原理图、bomlist、硬件位号图在内的全部软硬件资料。
gittee仓库地址:https://gitee.com/awol/open-harmony_-xr806_manifest
硬件资料下载地址:https://www.aw-ol.com/downloads?cat=12
技术问题讨论地址:https://bbs.aw-ol.com
活动报名及直播地址:https://aijishu.com/l/1110000000301384/join
XR806优秀开发例程
-
开发者成长计划正式上线!一起争做社区”高手“!
相信大家都已经发现社区有了一点小小的变化,头像下方多了个金闪闪的大V,通知栏有积分获取的通知,发帖、评论、点赞所有行为都有了实时的反馈,这其实就是全志在线社区为开发者们全新设立的开发者成长计划。
LV2最速升级方法:
① 1次主题帖发布==========50积分
② 1次有效回帖+4次点赞=====49积分
开发者成长体系是以成长值作为核心衡量指标的激励体系,成长值获取公式为:成长值= 20%活跃 + 80%贡献,成长值是根据开发者在论坛中的行为,结合活跃与贡献进行的综合评价,成长值即决定了开发者在社区的用户等级,积分与成长值一同获取,在不久的将来将会上线积分商城,获取的积分可以兑换包括开发板在内的精美礼品,只要在论坛足够活跃,有足够多的贡献,心爱的板子再也不用花998,用积分兑换也可以把他带回家。
积分细则如下:
积分、等级及积分记录查看方式如下:
-
【FAQ】全志R329在Tina如何在蓝牙已连接情况下拒绝其他耳机回连
问题背景
系统:Tina
平台:R818、V833 扫描笔产品
蓝牙功能:a2dp source问题概述
(1)客户有一个蓝牙音箱和一个蓝牙耳机,并且这两个设备之前都已经跟扫描笔连接配对过了。
(2)客户主动让扫描笔连接上蓝牙音箱。
(3)打开蓝牙耳机,此时蓝牙耳机回连上扫描笔。
但是客户不想要这个场景存在,希望只有一个连接存在。问题分析
蓝牙耳机打开后回连这个动作,我们无法阻止它,只能想办法拒绝他。
如果在应用层处理,连上了再把它断开,这个会影响状态的管理,也不是最好的解决方法。
所以我们考虑再收到连接请求事件时,就拒绝了它。linux的蓝牙驱动层会处理HCI上报的
事件,因此我们可以在驱动完成这个逻辑。解决方法
代码路径:
lichee/linux-4.9/net/bluetooth/hci_event.c
连接请求处理函数:hci_conn_request_evt 中增加如下代码:
首先判断连接类型是否是ACL_LINK,然后获取当前连接数,如果当前已经有连接了,就拒绝本次的连接请求。if (ev->link_type == ACL_LINK) { if ((hci_conn_num(hdev, ACL_LINK) != 0) && (hdev->dev_type == HCI_PRIMARY)) { BT_INFO("already exist acl link, reject new! %s, %d", __func__, __LINE__); hci_reject_conn(hdev, &ev->bdaddr); return; } }
修改过之后的代码如下:
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; struct inquiry_entry *ie; struct hci_conn *conn; __u8 flags = 0; BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr, ev->link_type); mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type, &flags); if (!(mask & HCI_LM_ACCEPT)) { hci_reject_conn(hdev, &ev->bdaddr); return; } if (ev->link_type == ACL_LINK) { if ((hci_conn_num(hdev, ACL_LINK) != 0) && (hdev->dev_type == HCI_PRIMARY)) { BT_INFO("already exist acl link, reject new! %s, %d", __func__, __LINE__); hci_reject_conn(hdev, &ev->bdaddr); return; } } if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, BDADDR_BREDR)) { hci_reject_conn(hdev, &ev->bdaddr); return; }
-
【FAQ】全志R329如何进行Tina Linux蓝牙低功耗默认连接参数修改
【问题背景】
硬件:R328/other + 蓝牙模组(XR829/other)
软件:Tina【问题简述】
btmanger里面有更新连接参数的函数bt_manager_le_update_conn_params,
但是该函数需要在连接完成后调用,有部分客户想要在发起连接时就使用一些连接参数。【问题分析】
在运行蓝牙后,Linux内核蓝牙协议栈会创建调试节点,可以用于修改一些参数,其中就包括发起连接时的连接参数。具体位置在:
/sys/kernel/debug/bluetooth/hci0
里面内容有:
adv_channel_map idle_timeout adv_max_interval inquiry_cache adv_min_interval le_max_key_size auto_accept_delay le_min_key_size blacklist link_keys conn_info_max_age long_term_keys conn_info_min_age manufacturer conn_latency quirk_simultaneous_discovery conn_max_interval quirk_strict_duplicate_filter conn_min_interval random_address dev_class remote_oob device_id rpa_timeout device_list sc_only_mode discov_interleaved_timeout sniff_max_interval dut_mode sniff_min_interval features ssp_debug_mode force_bredr_smp static_address force_static_address supervision_timeout hardware_error use_debug_keys hci_revision uuids hci_version voice_setting identity white_list identity_resolving_keys white_list_size
我们主要关心下面几个参数:
符号 描述 说明
conn_latency 从机潜伏时间 可以跳过连接事件最大次数 conn_max_interval 最大连接间隔 连接事件间隔最大值 conn_min_interval 最小连接间隔 连接事件间隔最小值 supervision_timeout 监督超时时间 两次成功连接事件之间的最长时间。 连接间隔范围: 6(7.5ms)< 1.25ms * conn_interval < 3200(4.0s) 监督超时时间范围: 10(100 ms)< 10ms * supervision_timeout < 3200(32.0 s) 从机潜伏时间范围: 0 < conn_latency < 499
iPhone对这些参数有要求,具体看文章最后面。
【解决方法】
修改Linux内核代码tina/lichee/linux-4.9/net/bluetooth/hci_core.c
函数 struct hci_dev *hci_alloc_dev(void)里面的默认参数。实现函数修改内核蓝牙协议栈的debugfs调试节点,启动蓝牙后调用一次即可:
int bt_le_default_conn_params_set(uint16_t min_conn_interval, uint16_t max_conn_interval, uint16_t slave_latency, uint16_t conn_sup_timeout) { if(min_conn_interval < 0x0006 || min_conn_interval > 0x0C80 || max_conn_interval < 0x0006 || max_conn_interval > 0x0C80 || min_conn_interval > max_conn_interval || slave_latency > 0x01F3 || conn_sup_timeout < 0x000A || conn_sup_timeout > 0x0C80) { printf("param error!\n"); return -1; } char buf[128]; if(access("/sys/kernel/debug/bluetooth/hci0",F_OK) == 0) { snprintf(buf, sizeof(buf), "echo %d > /sys/kernel/debug/bluetooth/hci0/conn_min_interval", min_conn_interval); printf("run:%s\n",buf); system(buf); snprintf(buf, sizeof(buf), "echo %d > /sys/kernel/debug/bluetooth/hci0/conn_max_interval", max_conn_interval); printf("run:%s\n",buf); system(buf); snprintf(buf, sizeof(buf), "echo %d > /sys/kernel/debug/bluetooth/hci0/conn_latency", slave_latency); printf("run:%s\n",buf); system(buf); snprintf(buf, sizeof(buf), "echo %d > /sys/kernel/debug/bluetooth/hci0/supervision_timeout", conn_sup_timeout); printf("run:%s\n",buf); system(buf); } return 0; }
// iphone 参数要求 // Interval Min ≥ 15 ms (multiples of 15 ms) // Interval Min + 15 ms ≤ Interval Max (Interval Max == 15 ms is allowed) // Interval Max * (Slave Latency + 1) ≤ 2 seconds // Interval Max * (Slave Latency + 1) * 3 < connSupervisionTimeout // Slave Latency ≤ 30 // 2 seconds ≤ connSupervisionTimeout ≤ 6 seconds
-
【FAQ】全志R328如何进入monitor模式
【问题背景】
硬件:R328+ Wi-Fi模组(XR829)
软件:Tina
说明:该FAQ旨在记录在Tina系统上XR829如何进入monitor模式【问题简述】
我们知道xradio的xconfig配网方式,会自动切到monitor模式,那么驱动如何手动设置进入monitor模式呢?【问题分析】
1.既然xconfig配网可以进入monitor模式,那么直接分析代码就可以找到对应的操作。
2.可以通过添加打印来跟踪xconfig的调用流程。echo 0xff > /sys/kernel/debug/xradio_host_dbg/dbg_common echo 0xff > /sys/kernel/debug/xradio_host_dbg/dbg_sta
【解决方法】
手动设置进入monitor模式:
insmod /system/vendor/modules/xradio_wlan.ko //加载xr829.ko ifconfig wlan0 down //可能系统起来会默认进入sta模式,所以先down掉 iw wlan0 set type monitor //最关键的,设置进入monitor模式 iw wlan0 info //获取wlan0网卡信息 ifconfig wlan0 up //up wlan0 iw wlan0 set channel 6 //设置信道 echo 0xffff,0xffff > /sys/kernel/debug/ieee80211/phy0/xradio/parse_flags //打开收发帧调试,可以分析monitor模式
-
【FAQ】全志R329如何使用audiocodec如何减少snd_pcm_open的耗时
问题背景
客户在使用audiocodec进行声音播放时,发现调用snd_pcm_open所用的时间达400多ms,客户希望能减少耗时开发环境
硬件:R328
软件:Tina
内核: Linux-4.9问题分析
客户调用snd_pcm_open时,传入的PCM NAME是使用了dmix插件的,dmix插件的功能是可以让多个应用同时访问音频通道,实现声音同时输出的效果。当不使用任何插件时(-D hw:audiocodec,0),调用snd_pcm_open会马上获取对应的pcm句柄,获取成功后再配置音频通路和寄存器,而当使用dmix插件时,调用snd_pcm_open会先解析插件中的参数,再根据参数配置音频通路和寄存器,通过dapm打开功放和操作DAC寄存器,之后再获取对应的pcm句柄,执行的逻辑更多。
当打开功放和操作DAC寄存器时,为了防止pop音的产生,驱动中加上了160ms + 2*30ms的sleep(乘以2是因为有左右声道),这里的sleep就是耗时产生的地方
解决方案
- 保证没有pop音的前提下,可以在dts中减少pa_msleep的值,这个值是操作功放的使能位后产生的延迟,对应上图的spk_cfg->pa_msleep
- 调整内核printk打印级别
snd_pcm_open,2646,s:0.439500 Used Time:0.439527 ----> 调整前 snd_pcm_open,2646,s:0.258538 Used Time:0.258548 ----> 调整后
- 删去内核不必要的打印或者用ftrace代替,调试发现一条内核打印耗时约4ms,过多的打印会严重影响性能
- 列表保证没有pop音的前提下,可以在驱动程序中直接减少sunxi_codec_playback_event的msleep值,因为这个sleep是为了避免操作DAC后出现尖刺导致的pop音,因此此方式应为最低优先级
-
【FAQ】全志R系列在Tina下如何设置optee-secure-storage默认保存路径
问题背景
optee REE SecureStorage的默认存储路径是 /data/tee,不符合客户自身的使用场景,需要进行更换。问题分析
首先,上官网查找相关资料,官网上介绍比较简单,没有具体说明。TEE File Structure in Linux File System OP-TEE by default uses /data/tee/ as the secure storage space in the Linux file system. Each persistent object is assigned an internal identifier. It is an integer which is visible in the Linux file system as /data/tee/<file number>. A directory file, /data/tee/dirf.db, lists all the objects that are in the secure storage. All normal world files are integrity protected and encrypted, as described below.
接着分析,rootfs中的/data/tee目录原本是不存在的,但是启动到控制台后,默认就存在了,所以极有可能是在非安全端的后台应用程序tee-supplicant中进行创建的,打开其源码,进行搜索,结果如下
x```
xx@xxx:~/xxx/optee_client-3.7.0$ grep -r '/data/tee'
config.mk:CFG_TEE_FS_PARENT_PATH ?= /data/tee
匹配到二进制文件 out/export/usr/sbin/tee-supplicant
匹配到二进制文件 out/tee-supplicant/tee_supp_fs.o
匹配到二进制文件 out/tee-supplicant/tee-supplicant
匹配到二进制文件 ipkg-sunxi/optee-client-3.7/usr/sbin/tee-supplicant
libteec/CMakeLists.txt:set (CFG_TEE_CLIENT_LOG_FILE "/data/tee/teec.log" CACHE STRING "Location of libteec log")
tee-supplicant/CMakeLists.txt:set (CFG_TEE_FS_PARENT_PATH "/data/tee" CACHE STRING "Location of TEE filesystem (secure storage)")可以看到,最后一行CMakeLists.txt有个宏变量 CFG_TEE_FS_PARENT_PATH ,其注释为TEE FS的路径,有可能是这个。接续迭代搜索,见下面流程,阅读代码,确认就是这个影响。
xxx@xxx:~/xxx/optee_client-3.7.0$ grep -r 'CFG_TEE_FS_PARENT_PATH'
tee-supplicant/tee_supplicant_android.mk: -DTEE_FS_PARENT_PATH=\"$(CFG_TEE_FS_PARENT_PATH)\" tee-supplicant/Makefile: -DTEE_FS_PARENT_PATH=\"$(CFG_TEE_FS_PARENT_PATH)\" \ tee-supplicant/CMakeLists.txt:set (CFG_TEE_FS_PARENT_PATH "/data/tee" CACHE STRING "Location of TEE filesystem (secure storage)") tee-supplicant/CMakeLists.txt: PRIVATE -DTEE_FS_PARENT_PATH="${CFG_TEE_FS_PARENT_PATH}" ... xxx@xxx:~/xxx/optee_client-3.7.0$ grep -rn 'TEE_FS_PARENT_PATH' tee-supplicant/src/tee_supp_fs.c:124: n = snprintf(tee_fs_root, sizeof(tee_fs_root), "%s/", TEE_FS_PARENT_PATH);
查看tee-supplicant/src/tee_supp_fs.c文件第124行,符合猜想。
static int tee_supp_fs_init(void) { size_t n = 0; mode_t mode = 0700; n = snprintf(tee_fs_root, sizeof(tee_fs_root), "%s/", TEE_FS_PARENT_PATH); if (n >= sizeof(tee_fs_root)) return -1; if (mkpath(tee_fs_root, mode) != 0) return -1; return 0; }
问题解决
config.mk与tee-supplicant/CMakeLists.txt都包含CFG_TEE_FS_PARENT_PATH,根据Tina下编译规则,发现这个包是用Makefile编译的,修改config.mk下的CFG_TEE_FS_PARENT_PATH为目标路径即可解决。 -
【FAQ】全志R系列在Tina下如何确认方案的optee版本信息
问题背景
Tina环境下当前支持两个版本的optee,一个是2.5.0,一个是3.7.0。由于Tina下平台众多,客户不是很清楚哪些平台支持哪个版本的optee。
问题分析
optee版本主要涉及三个部分optee-client,optee-os-dev-kit,optee.bin。由于2.5与3.7版本跨度太大,所以这三者必须版本匹配,系统才能正常工作,Tina release版本SDK是通过make menuconfig中的OPTEE_VERSION_2_5与OPTEE_VERSION_3_7来统一配置版本信息。可以参考《TinaLinux_安全使用指南》第1.2章节的适用范围来,确认平台的optee版本,旧芯片支持2.5版本,新芯片使用3.7版本。这里汇总如下:
optee-2.5.0: R18、MR133、R311、R328…
optee-3.7.0: R329、MR813、R818、R528…
理论上来说,所有平台都可以支持optee的3.7.0版本。有些客户希望对旧芯片,如R328,升级到3.7.0,此时,系统中就会存在两个版本optee,如果没有统一管理,就容易造成混乱。
问题解决
optee-client
查看tina/package/security/optee-client*/Makefile中的PKG_VERSION来确认版本信息。PKG_VERSION:=2.5.0
optee-os-dev-kit
查看tina/package/security/optee-os-dev-kit/dev_kit/arm-plat-xxx/export-ta_arm32/host_include/conf.mk中的CFG_OPTEE_REVISION_MAJOR与CFG_OPTEE_REVISION_MINOR CFG_OPTEE_REVISION_MAJOR=2 CFG_OPTEE_REVISION_MINOR=5
optee.bin
optee.bin头部包含版本信息,可以通过hexdump进行查看。$ hexdump -C optee_xxx.bin -n 64 00000000 fd 03 00 ea 6f 70 74 65 65 00 00 00 00 00 00 00 |....optee.......| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 33 2e 37 00 |............3.7.|
-
【XR806开发板试用】驱动总线舵机例程
本文转自极术社区:https://aijishu.com/a/1060000000300549
作者:A.0王海智简介
申请到XR806开发板很高兴,因为它可以基于鸿蒙L0系统下开发。在试用中,这个开发板的编译环境的搭建,程序的编写,都走很多的弯路,遇到了很多的坑。编译环境的搭建就用了很长的时间。也谢谢群里的大佬的帮助和极术小姐姐的鼓励,才能完成这次测试。
编译环境
Windows下安装VMware虚拟机,虚拟机中安装Ubuntu20.04.2 64位桌面版。搭建教程是参考的【XR806开发板试用】shell脚本一键配置XR806开发环境。
总线舵机说明
总线舵机:
舵机参数说明
总线舵机是通过单线收发串口数据,通过之前做的RT-PI的扩展板实现。电路如图
工作原理
XR806通过UART1串口发送十位十六进制的数据,扩展板将串口数据转换为单线串口和舵机通讯,完成总线舵机的控制。
舵机数据协议图
程序说明
程序结构图
1.ohosdemo文件夹下的BUILD.gn文件group("ohosdemo"){ deps = [ #"TD-LED:app_TDLED", "uart_demo:app_uart", #"LED:app_led", # "hello_demo:app_hello", ] }
2.uart_demo文件夹下的BUILD.gn文件
import("//device/xradio/xr806/liteos_m/config.gni") static\_library("app\_uart") { configs = [] sources = [ "src/main.c", #"src/test_flash.c", #"src/test_gpio.c", #"src/test_i2c.c", #"src/test_lowpower.c", #"src/test_pwm.c", #"src/test_reset.c", #"src/test_uart.c", #"src/test_watchdog.c", ] cflags = board\_cflags include\_dirs = board\_include\_dirs include\_dirs += [ "//kernel/liteos_m/kernel/arch/include", "include", "//base/iot_hardware/peripheral/interfaces/kits", ] }
3.uart_demo/src/main.c代码如下
#include <stdio.h> #include "ohos\_init.h" #include "kernel/os/os.h" #include <string.h> #include "iot\_uart.h" #include "driver/chip/hal\_uart.h" #define UARTID UART1\_ID #define UART\_BUFFER\_MAXSIZE 50 #define UART\_RECEIVE\_DATALED 30 const uint8\_t play\_Buffer[10] = {0xFA,0xAF,0x00,0x01,0x96,0x00,0x00,0x00,0x97,0xED}; const uint8\_t play\_Buffer1[10] = {0xFA,0xAF,0x00,0x01,0x00,0x00,0x00,0x00,0x01,0xED}; static OS\_Thread\_t g\_main\_thread; static int uart\_init(void) { HAL_Status status = HAL_ERROR; UART_InitParam param; param.baudRate = 115200; // 波特率为115200 param.dataBits = UART_DATA_BITS_8; param.stopBits = UART_STOP_BITS_1; param.parity = UART_PARITY_NONE; param.isAutoHwFlowCtrl = 0; status = HAL_UART_Init(UARTID, ¶m); if (status != HAL_OK) printf("uart init error %d\n", status); return status; } static void MainThread(void \*arg) { unsigned char uart_buffer[UART_BUFFER_MAXSIZE]; uart_init(); while (1) { HAL_UART_Transmit_Poll(UARTID, (uint8_t *)play_Buffer, 10); LOS_Msleep(2000); HAL_UART_Transmit_Poll(UARTID, (uint8_t *)play_Buffer1, 10); LOS_Msleep(2000); } } void UARTMain(void) { printf("UART Test Start\\n"); if (OS\_ThreadCreate(&g\_main\_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS\_RUN(UARTMain); // Harmony线程入口
-
【FAQ】全志XR系列 如何统计XRMCU的内存使用情况
问题背景
有客户反馈代码运行奔溃,但始终找不到原因,经排查后发现是剩余RAM不足导致的。客户把所有应用内存直接保存到SRAM中,导致内存不足,跑应用时踩内存导致系统奔溃。问题描述
因为代码全放在RAM中导致内存不足,跑应用时容易踩内存系统奔溃,但如何统计内存使用情况并优化?问题分析
-
内存是如何保存到指定位置的?
GCC链接操作是以section作为最小的处理单元,只要一个section中的某个符号被引用,该section就会被加入到可执行程序中去。因此,GCC在编译时可以使用 -ffunction-sections和 -fdata-sections 将每个函数或符号创建为一个sections,其中每个sections名与function或data名保持一致。而在链接阶段, -Wl,–gc-sections 指示链接器去掉不用的section(其中-wl, 表示后面的参数 -gc-sections 传递给链接器),这样就能减少最终的可执行程序的大小了。 -
哪些代码可以放在XIP,哪些代码必须放在SRAM?
可以简单记忆为XIP需要初始化,XIP初始化前会调用的代码不用放在XIP,如malloc,rtos的代码。中断的时间要求尽量短,也不要调用XIP的代码。
解决方法
SDK中提供了内存分析工具,以XR806为例,内存分析工具为tools\map_parse_gcc_v3.py,以audio_demo为例,使用方法为:python map_parse_gcc_v3.py audio_demo.map
稍等片刻在bash中会显示内存统计情况:
================================= Usage: map_parse_version: 1.0.4 map_parse_gcc.py xxx.map ================================= Memory Sections: RAM 0x0000000000201000 0x000000000004b000 xrw FLASH 0x0000000000400000 0x0000000000800000 xr PSRAM 0x0000000001400000 0x0000000000000000 xrw *default* 0x0000000000000000 0xffffffffffffffff MEMORY MAP |===========================================================================| | MODULE | RAM | FLASH | PSRAM | |===========================================================================| | atomic | 0 | 28 | 0 | | board | 20 | 14 | 0 | | board_common | 602 | 0 | 0 | | board_config | 1216 | 304 | 0 | | captureControl_rtos | 0 | 467 | 0 | | card_pcm | 0 | 712 | 0 | ................ ................ | sys_monitor | 0 | 814 | 0 | | *fill* | 149 | 1179 | 0 | |===========================================================================| | TOTAL (bytes) | 95045 | 1032493 | 0 | | MEM LIMIT | 307200 | 8388608 | 0 | | MEM LEFT | 212155 | 7356115 | 0 | |===========================================================================|
其中lib打头的一般是.a静态库,其余为.o可执行文件,如果确定该section可以放在xip,修改project\linker_script\gcc\appos.ld,在xip.section添加对应的目标,常用的写法如下:
.xip : { . = ALIGN(16); __xip_start__ = .; *AAA.a: (.text .text.* .rodata .rodata.*) //某个静态库的text和rodata都存到xip中 *AAA.a:bbb.o (.text .text.* .rodata .rodata.*) //某个静态库中的某个.o存到xip中 *AAA.a: (EXCLUDE_FILE (bbb.o) .text) //某个静态库中除了bbb.o的text段,其余放到xip中 *bbb.*.o (.text .text.* .rodata .rodata.*) //bbb打头的所有.o存进xip中,常用于同一个make,但没有编译出静态库的场合
-
-
【FAQ】全志XR系列 如何调试wifi频偏问题?
问题背景
系统平台:Tina,RTOS
硬件平台:XR829,XR808,XR806,XR819S问题描述
在测试WiFi传导或者过SRRC认证时,输出功率正常,但是“SymClockErr”指标或者“载波容限”不满足需求,需要做频偏校准。解决方案及步骤
- 让芯片TX WiFi信号;
- 输入指令:etf get_freq_offset,获取当前的频偏设置值,记为x;
- 输入指令:etf set_freq_offset (0~127),设置频偏值y;
- 查看IQ或者极致汇仪(或者其它测试仪器)上面的频偏值。如果这个频偏值比之前的低(例如15ppm变成了10ppm),当y>x:继续增大y;当y<x:继续减小y。直到测试仪器上面频偏值满足需求(越低越好,一般调到5ppm以内),记录下来这个值y;反之,这个频偏值比之前的高(例如15ppm变成了20ppm),当y>x:把y值设置成小于x;当y<x:把y值设置成大于x。直到测试仪器上面频偏值满足需求(越低越好,一般调到5ppm以内),记录下来这个值y;
- 输入指令:etf set_sdd_freq_offset (0~127),把频偏值保存到sdd里面;(如需保存到efuse,则参考保存到efuse的教程);
- 如通过软件的方法,无法使频偏值满足需要,则需要调整wifi芯片晶振旁边的两个电容(一般是10~22pF)。查看IQ或者极致汇仪(或者其它测试仪器)上面的频偏值,直到满足需求(越低越好,一般调到5ppm以内)。记录下当前的电容值,以后贴片就用这个电容值。
-
【FAQ】全志XR系列 XRMCU如何修改录音编码器的输入数据?
问题背景
有客户希望把现有的PCM数据编码成AMR,或者希望把录音得到的PCM数据经过处理后再进行音频编码。问题描述
AMR的输入数据是可以由客户自定义的。问题分析
梳理录音初始化流程recorder_base *recorder_create() //初始化录音设备 CaptureCtrl *CaptureDeviceCreate() //录音数据流处理结构体 mCaptureControlOps.cdxRead //获取录音数据数据流
可以知道,录音数据由project/common/apps/cedarx/capture_ctrl/captureControl_rtos.c : static int __Read(CaptureCtrl *c, void *pData, int nDataSize)中获取,如果希望PCM数据经过处理后再进行编码,则可以通过修改该函数,把数据修改完毕后再返回。
解决方法
以把SD卡中的PCM编码成AMR格式为例。/* 打开 PCM数据 */ int encode_input_init() { FRESULT result; result = f_open(&inContext.fp, "0:/audio/8000.pcm", FA_READ); if (result != FR_OK) { printf("open file fail.\n"); return -1; } return 0; } /* 获取PCM数据 */ unsigned int encode_input_data(void *buffer, unsigned int len) { unsigned int act_read; f_read(&inContext.fp, buffer, len, &act_read); return act_read; } /* 替换掉录音数据 */ static int __Read(CaptureCtrl *c, void *pData, int nDataSize) { int ret; .......... //ret = snd_pcm_read(CAPTURE_SOUND_CARD, pData, nDataSize); ret = encode_input_data(pData, nDataSize); .......... return ret; }
-
【FAQ】全志XR系列 XRMCU如何播放xip中的音频?
问题背景
有客户因为担心音频存放在flash中会因为没有烧录,导致播放异常,所以希望可以提供播放xip中的音频数据的方法。问题分析
XRMCU允许使用raw_bin的方式烧录,确保烧录固件时音频也能下载到flash中,请参考(XR806如何添加本地音频到flash)[https://one.allwinnertech.com/#/faq/0/show]。
如果确定要播放xip中的数据,需要把计算出音频数据在flash中的实际地址。解决步骤
- 使用bin2hex或者HxD等工具把音频文件转变成c文件,并保存在xip中。
__xip_rodata //保存在xip中 const unsigned char testmusic[39197] = { 0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x33, 0x54, 0x53, 0x53, 0x45, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x4C, 0x41, 0x4D, 0x45, 0x20, 0x33, 0x32, 0x62, 0x69, 0x74, 0x73, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x31, 0x30, 0x30, 0x2E, 0x31, ......
- 计算音频数据在flash中的地址。
参照xip初始化platform_xip_init();可以知道app_xip.bin在flash中的位置是image_get_section_addr(IMAGE_APP_XIP_ID) + IMAGE_HEADER_SIZE
static void platform_xip_init(void)
{ uint32_t addr; addr = image_get_section_addr(IMAGE_APP_XIP_ID); //通过ID获取app_xip.bin在flash中的地址 if (addr == IMAGE_INVALID_ADDR) { FWK_NX_ERR("no xip section\n"); return; } HAL_Xip_Init(PRJCONF_IMG_FLASH, addr + IMAGE_HEADER_SIZE); //IMAGE_HEADER_SIZE为头码地址 }
可以得出音频数据在flash中的地址。
/* __xip_start__指xip的入口地址,在appos.ld中定义,数值也在appos.ld中定义为0x400000。 (uint32_t)testmusic - (uint32_t)__xip_start__也就是相对于xip入口的偏移量。 image_get_section_addr(IMAGE_APP_XIP_ID) + IMAGE_HEADER_SIZE就是app_xip.bin在flash中的实际地址,会被映射到0x400000 */ uint32_t music_addr = ((uint32_t)testmusic - (uint32_t)__xip_start__ + image_get_section_addr(IMAGE_APP_XIP_ID)+ IMAGE_HEADER_SIZE);
- 把数值格式化为cedarx能识别的字符串。
char *song_addr = malloc(50); sprintf(song_addr,"flash://0?addr=%u&length=%u",music_addr,sizeof(testmusic));
- 播放音频
player_base *mAwPlayer; mAwPlayer = player_create(); mAwPlayer->play(mAwPlayer,song_addr);
-
【FAQ】全志D1芯片 MiniGUI如何显示鼠标?
【问题描述】
有时候开发MiniGUI程序需要显示鼠标,但是怎么配置和调试都不显示,那么可以按照下面几步依次检查【解决方案】
- 查看编译libminigui-gpl的时候,是否指定了–disable-cursor,如果指定了是没有鼠标显示的
- 查看MiniGUI.cfg,cursorpath的路径下是否有鼠标图片,cursornumber是否大于0
- 查看窗口的过程函数MSG_ERASEBKGND下是否返回了return 0并且什么也没有画,没有填充背景,这样是不显示鼠标的
- 查看MiniGUI.cfg的输入引擎配置的是否是鼠标,如
# IAL engine ial_engine=console mdev=/dev/input/mouse0 mtype=IMPS2
- 查看创建窗口是否获取了鼠标
CreateInfo.hCursor = GetSystemCursor(0);
- 查看创建自定义控件是否获取了鼠标
MyClass.hCursor = GetSystemCursor(0);
-
【FAQ】全志D1芯片 如何在休眠唤醒过程中通过 sunxi_dump 读写外设寄存器?
问题背景
设备休眠唤醒出错 是在休眠唤醒问题中最常见的一种,因此需要在休眠过程中,读取设备寄存器信息,分析设备状态,成为一种常见的需求。
sunxi_dump 是 AW 提供的一种通过控制台 dump 设备寄存器的模块,但由于在休眠唤醒过程中控制台不可用,因此需要一种在休眠唤醒过程中,可以直接dump设备寄存器的机制。
因此,拓展了 sunxi_dump 的功能,使其支持通过休眠唤醒前的配置,在休眠过程中执行 dump。
使用方法
1, 使能休眠唤醒日志
- 使控制台不休眠,必须
echo N > /sys/module/printk/parameters/console_suspend
- 使能 kernel 休眠打印 可选
echo 1 > /sys/module/kernel/parameters/initcall_debug
- 使能 设备休眠唤醒调用顺序 可选
echo 1 > /sys/power/pm_print_times
- 调整控制台打印级别 可选
echo 8 > /proc/sys/kernel/printk
- 也可以执行
echo Y > /sys/module/printk/parameters/ignore_loglevel
2, 根据需要,关闭异步休眠唤醒
默认是并行休眠,可能出现多个核同时休眠设备,打印日志比较乱的情况echo 0 > /sys/power/pm_async # 关闭异步
3, 设置休眠过程需要执行的动作
/sys/class/sunxi_dump/standby_dump_ctrl 是为了方便在 standby 等流程中读写寄存器的值,增加的接口。其每个bit对应一个动作开关,目前支持一下控制位。 可以通过位操作同时使能多个动作。
enum { DUMP_CTRL_DEV_PREPARE = 0, // 在 device prepare 阶段执行dump DUMP_CTRL_DEV_SUSPEND, // 在 device suspend 阶段执行dump DUMP_CTRL_DEV_SUSPEND_LATE, // 在 device suspend_late 阶段执行dump DUMP_CTRL_DEV_SUSPEND_NOIRQ, // 在休眠唤醒的 device suspend_noirq 阶段执行dump DUMP_CTRL_SYSCORE_SUSPEND, // 在 syscore suspend 阶段执行dump DUMP_CTRL_DEV_COMPLETE = 8, // 在 device complete 阶段执行dump DUMP_CTRL_DEV_RESUME, // 在 device resume 阶段执行dump DUMP_CTRL_DEV_RESUME_EARLY, // 在 device resume_early 阶段执行dump DUMP_CTRL_DEV_RESUME_NOIRQ, // 在 device resume_noirq 阶段执行dump DUMP_CTRL_SYSCORE_RESUME, // 在 syscore resume 阶段执行dump DUMP_CTRL_SUSPEND_WRITE = 16, // 在 syscore suspend 阶段执行 write DUMP_CTRL_RESUME_WRITE = 17, // 在 syscore resume 阶段执行 write };
例如
# echo 0x11111 > /sys/class/sunxi_dump/standby_dump_ctrl [ 2750.841496] standby_dump_ctrl change to 0x00011111
4, 按需要设置,需要读写的寄存器域
- 读取一个区域
echo 0x07090100,0x0709010c > /sys/class/sunxi_dump/dump
- 写一个寄存器
# echo 0x0709010c 0x12345678 > /sys/class/sunxi_dump/write [ 2783.442051] Will write it actually when suspend.
5, 休眠即可
注意实现
1, 由于该模块在standby中被抽象为设备,因此需要注意该模块与目标设备执行先后顺序是不是满足要求。
2, 由于有些设备会关闭自己的时钟,之后访问该设备寄存器可能存在 全 0 值。
以上,可以调整执行的动作 来确认和规避。
-
【FAQ】全志D1芯片 如何解决在创建视频解码器后,未送入视频帧数据之前,cpu被占满的问题
1、现象
目前D1使用vdecoder SDK,其中调用完初始化完解码器函数后,在未送入视频帧数据让该解码器工作之前,cpu会被占满;2、问题原因
目前代码 vdecoder.c 中 ENABLE_SBM_FRAME_INTERFACE 默认值为1,使用帧接口,此时解码H264会使用软件搜头,CPU占用率会比较高。3、解决方法
将 ENABLE_SBM_FRAME_INTERFACE 改为 0 即可,此时使用硬件搜头。单核且CPU频率不高的情况,建议 ENABLE_SBM_FRAME_INTERFACE 置为 0 。
-
麻雀d1s编译修复adb和phoenixsuit连接问题
- 修复pc连接电脑掉电问题;
- 修复phoenixsuit连接问题;
修复pc连接电脑后掉电问题
官方的tina编译出来之后,连接电脑,如果使用了otg口,会导致这个开发板不断的断开和重连.
而且串口不断地打印断连重连的信息.有这个报错信息:
WARN: get power supply failed\n
看起来是和供电有关的问题,我在网上找到这个文章.
作者说这是因为开发板连接usb之后会把某个电流限制到500ma.我们可以改一下
在文件tina-d1-open/lichee/linux-5.4/drivers/usb/sunxi_usb/udc/sunxi_udc.c中,修改函数sunxi_set_cur_vol_work.
void sunxi_set_cur_vol_work(struct work_struct *work) { #if !defined(SUNXI_USB_FPGA) && defined(CONFIG_POWER_SUPPLY) struct power_supply *psy = NULL; union power_supply_propval temp; if (of_find_property(g_udc_pdev->dev.of_node, "det_vbus_supply", NULL)) psy = devm_power_supply_get_by_phandle(&g_udc_pdev->dev, "det_vbus_supply"); if (!psy || IS_ERR(psy)) { DMSG_PANIC("%s()%d WARN: get power supply failed\n", __func__, __LINE__); } else { temp.intval = 2500; //改为2500ma power_supply_set_property(psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &temp); } #endif }
改完之后编译打包,这个现象不会出现了.
修复phoenixsuit连接问题
修改文件tina-d1-open/lichee/brandy-2.0/u-boot-2018/drivers/sunxi_flash/mmc/sdmmc.c中的sunxi_sprite_mmc_probe函数:
int sunxi_sprite_mmc_probe(void) { #ifndef CONFIG_MACH_SUN50IW11 return sdmmc_init_for_sprite(0, 0); //修改了这里. #else int workmode = uboot_spare_head.boot_data.work_mode; if (workmode == WORK_MODE_CARD_PRODUCT) return -1; else return sdmmc_init_for_sprite(0, 0); #endif }
改了之后就可以直接用phoenixsuit来刷机.而不需要使用读卡器.
文章作者: zzidun pavo
文章链接: https://zzidun.github.io/2022/02/08/ma-que-d1s-bian-yi-xiu-fu-adb-he-phoenixsuit-lian-jie-wen-ti/ -
【XR806开发板试用】儿童遥控挖掘机无线化升级改造
本文转自极术社区:https://aijishu.com/a/1060000000295282
作者:寒冰1988一、项目简介
能够在XR806开发板试用活动中成功申请到该开发板,这为自己之前一直心心念的对儿子玩具升级计划提供了最核心的部件。
如下图所示,为老母亲为孙子在路边摊花了25个铜板购置的遥控玩具车,这款车是有线遥控的,线长大约有1.5米,小朋友在玩的时候需要跟随玩具车移动,玩的很不尽兴,虽然喜欢挖掘机,也只能放起来了。如果能够将其改造为无线遥控那么毕竟棒极了。既然XR806到手了,就让我们行动起来吧。
二、硬件方案
2.1 分析玩具挖掘机的结构
如下图我对挖掘机进行了拆解,可以看出有线挖掘机的内部单元很简单,有三个直流电机,其中两个电机用于提供车行动的动力,剩下的一个电机用于提供机械臂的升降。而且内部空间可以放下XR806,这就可以将开发板内置于汽车内增加美观性。
如下图所示,该款玩具底部还预留了电池仓和开关的位置,这真是棒极了。
2.2 分析玩具挖掘机的控制逻辑
在改造之前,我们还需要知道电机的控制逻辑,以便于我们选用合适的逻辑器件与XR806配套实现对应的功能。
如下图所示为有线遥控器的控制按键的定义。该款遥控器共提供三个按钮,实现前进、后退、左拐、右拐、起重臂升降等功能,我们只需要复现这几个功能即可。
结合2.1节的内部拆解,通过万用表即可探测出整套玩具的原理图如下图所示。S1开关对应前进后退按键;S2对应左拐右拐按键;S3对应起重臂升降功能。
2.3 无线遥控硬件逻辑
2.3.1 直流电机控制逻辑
参考2.2章节的分析,需要找到一款逻辑芯片或模组实现对三个电机的控制,因为挖掘机本身还支持前进后退功能,所以还需要支持电极的反转。经过淘宝的搜索,寻得如下板卡一块。
这块板卡的使用说明如下:
我们需要驱动3个电机,因此需要使用到6个GPIO,由XR806的管脚定义可知能够满足我们的改造需求。遥控端直接利用手机实现。
2.3.2 连线
按照如下示意图将XR806、直流电机驱动板和电机连接在一起。通过XR806的PIN[2:7]共6个管脚实现对电机的控制,管脚和直流电机的对应关系按图所示。
三、软件方案
实现无线遥控的目的需要一种无线信息媒介将遥控器(手机)和XR806连接起来,实现控制命令的下发。由XR806开发板的说明可知,目前有WIFI和BT两种方案,基于自身的知识储备,本次先选WIFI来实现,利用MQTT实现命令的下发和状态的接受。
3.1 MQTT topic的定义
定义如下topic和msg用于控制命令的发送,设备端(XR806)监听这些topic接受来自于遥控器的命令。#topic /digger/cmd #msg forward backup turnright turnleft bucketlift stop
定义如下topic用于命令执行结果的返回,遥控器(手机)监听这些topic接受来自于设备端的执行结果
#topic /digger/cmdret #msg success fail
3.2 编码实现
3.2.1 环境搭建
参考【XR806开发板试用】XR806与鸿蒙,简化构建环境流程完成开发环境的搭建3.2.2 设备端编码
主程序如下#include <stdio.h> #include <string.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "iot_gpio.h" #include "wifi_device.h" #include "common/framework/net_ctrl.h" #include "net/mqtt/MQTTClient-C/MQTTClient.h" static OS_Thread_t g_main_thread; static OS_Thread_t g_mqtt_thread; #define WIFI_DEVICE_CONNECT_AP_SSID "ssid" #define WIFI_DEVICE_CONNECT_AP_PSK "pwm" #define MQTT_DEMO_CLIENT_ID "6688" #define MQTT_DEMO_HOST_NAME "broker-cn.emqx.io" #define MQTT_DEMO_PORT "1883" #define MQTT_DEMO_USERNAME "dev" #define MQTT_DEMO_PASSWORD "dev" #define MQTT_SUB_TOPIC "/digger/cmd" #define MQTT_PUB_TOPIC "/digger/cmdret" #define MQTT_DEMO_BUF_SIZE (2*1024) static MQTTPacket_connectData mqtt_demo_connectData = MQTTPacket_connectData_initializer; static Client mqtt_demo_client; static Network mqtt_demo_network; static int max_duty_ratio = 0; static int mqtt_demo_publish(char *topic, char *msg) ; static int mqtt_demo_init(void) { char *send_buf; char *recv_buf; mqtt_demo_connectData.clientID.cstring = MQTT_DEMO_CLIENT_ID; mqtt_demo_connectData.keepAliveInterval = 30; // 30s mqtt_demo_connectData.cleansession = 0; mqtt_demo_connectData.MQTTVersion = 4; //Version of MQTT 3.1.1 send_buf = malloc(MQTT_DEMO_BUF_SIZE); if (send_buf == NULL) { printf("no memory\n"); return -1; } recv_buf = malloc(MQTT_DEMO_BUF_SIZE); if (recv_buf == NULL) { free(send_buf); printf("no memory\n"); return -1; } /* init network */ NewNetwork(&mqtt_demo_network); /* init mqtt client object */ MQTTClient(&mqtt_demo_client, &mqtt_demo_network, 6000, (unsigned char *)send_buf, MQTT_DEMO_BUF_SIZE, (unsigned char *)recv_buf, MQTT_DEMO_BUF_SIZE); /* set username and password */ mqtt_demo_connectData.username.cstring = MQTT_DEMO_USERNAME; mqtt_demo_connectData.password.cstring = MQTT_DEMO_PASSWORD; return 0; } static int mqtt_demo_connect(char *host_name, char *host_port) { int ret = -1; ret = ConnectNetwork(&mqtt_demo_network, host_name, atoi(host_port)); if (ret != 0) { printf("mqtt connect faild, ret:%d, host:%s, port:%s\n", ret, host_name, host_port); goto exit; } ret = MQTTConnect(&mqtt_demo_client, &mqtt_demo_connectData); if (ret != 0) { printf("mqtt connect faild, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); goto exit; } printf("mqtt connected\n"); exit: return ret; } static void mqtt_demo_msg_cb(MessageData *data) { printf("get a message, topic: %.*s, msg: %.*s\n", data->topicName->lenstring.len, data->topicName->lenstring.data, data->message->payloadlen, (char *)data->message->payload); if(!strncmp(data->topicName->lenstring.data, "/digger/cmd", 11) && data->message->payloadlen) { if(!strcmp(data->message->payload, "forward") { IoTGpioSetOutputVal(GPIO_ID_PA23, 1); IoTGpioSetOutputVal(GPIO_ID_PA22, 0); IoTGpioSetOutputVal(GPIO_ID_PA21, 1); IoTGpioSetOutputVal(GPIO_ID_PA20, 0); } else if (!strcmp(data->message->payload, "backup") { IoTGpioSetOutputVal(GPIO_ID_PA23, 0); IoTGpioSetOutputVal(GPIO_ID_PA22, 1); IoTGpioSetOutputVal(GPIO_ID_PA21, 0); IoTGpioSetOutputVal(GPIO_ID_PA20, 1); } else if (!strcmp(data->message->payload, "turnright") { IoTGpioSetOutputVal(GPIO_ID_PA21, 1); IoTGpioSetOutputVal(GPIO_ID_PA20, 0); } else if (!strcmp(data->message->payload, "turnleft") { IoTGpioSetOutputVal(GPIO_ID_PA23, 1); IoTGpioSetOutputVal(GPIO_ID_PA22, 0); } else if (!strcmp(data->message->payload, "bucketlift") { IoTGpioSetOutputVal(GPIO_ID_PA19, 1); IoTGpioSetOutputVal(GPIO_ID_PA13, 0); } else { IoTGpioSetOutputVal(GPIO_ID_PA23, 0); IoTGpioSetOutputVal(GPIO_ID_PA22, 0); IoTGpioSetOutputVal(GPIO_ID_PA21, 0); IoTGpioSetOutputVal(GPIO_ID_PA20, 0); IoTGpioSetOutputVal(GPIO_ID_PA19, 0); IoTGpioSetOutputVal(GPIO_ID_PA13, 0); } mqtt_demo_publish(MQTT_PUB_TOPIC, "success"); } } static int mqtt_demo_subscribe(char *topic) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTSubscribe(&mqtt_demo_client, topic, 0, mqtt_demo_msg_cb); if (ret != 0) printf("mqtt subscribe faild ret:%d\n", ret); } return ret; } static int mqtt_demo_unsubscribe(char *topic) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTUnsubscribe(&mqtt_demo_client, topic); if (ret != 0) printf("mqtt unsubscribe faild, ret:%d\n", ret); } return ret; } static int mqtt_demo_publish(char *topic, char *msg) { int ret = -1; MQTTMessage message; memset(&message, 0, sizeof(message)); message.qos = 0; message.retained = 0; /* disable retain the message in server */ message.payload = msg; message.payloadlen = strlen(msg); ret = MQTTPublish(&mqtt_demo_client, topic, &message); if (ret != 0) printf("mqtt publish faild, ret:%d\n", ret); return ret; } static int mqtt_demo_disconnect(void) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTDisconnect(&mqtt_demo_client); if (ret != 0) printf("mqtt disconnect fail, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); } return ret; } static void mqtt_demo_deinit(void) { if (mqtt_demo_client.buf) { free(mqtt_demo_client.buf); mqtt_demo_client.buf = NULL; } if (mqtt_demo_client.readbuf) { free(mqtt_demo_client.readbuf); mqtt_demo_client.readbuf = NULL; } } static void mqtt_task(void *arg) { int ret; int reconnect_times = 0; mqtt_demo_init(); ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) goto exit; ret = mqtt_demo_subscribe(MQTT_RECV_TOPIC); if (ret != 0) goto exit; mqtt_demo_publish(MQTT_PUB_TOPIC, "dev ready"); while (1) { ret = MQTTYield(&mqtt_demo_client, 300); if (ret != 0) { printf("mqtt yield err, ret:%d\n", ret); reconnect: printf("mqtt reconnect\n"); mqtt_demo_disconnect(); ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) { reconnect_times++; if (reconnect_times > 5) goto exit; OS_MSleep(5000); //5s goto reconnect; } } } exit: mqtt_demo_unsubscribe(MQTT_RECV_TOPIC); mqtt_demo_disconnect(); mqtt_demo_deinit(); OS_ThreadDelete(&g_mqtt_thread); } static void net_cb(uint32_t event, uint32_t data, void *arg) { uint16_t type = EVENT_SUBTYPE(event); switch (type) { case NET_CTRL_MSG_NETWORK_UP: printf("NET_CTRL_MSG_NETWORK_UP\n"); if (!OS_ThreadIsValid(&g_mqtt_thread)) { OS_ThreadCreate(&g_mqtt_thread, "connect_to_server_task", mqtt_task, (void *)NULL, OS_THREAD_PRIO_APP, (8 * 1024)); } break; case NET_CTRL_MSG_NETWORK_DOWN: break; default: break; } } static void MainThread(void *arg) { printf("MainThread start\r\n"); HAL_Status status = HAL_ERROR; /*Config GPIO*/ IoTGpioInit(GPIO_ID_PA23); IoTGpioSetDir(GPIO_ID_PA23, IOT_GPIO_DIR_OUT); IoTGpioSetOutputVal(GPIO_ID_PA23, 0); IoTGpioInit(GPIO_ID_PA22); IoTGpioSetDir(GPIO_ID_PA22, IOT_GPIO_DIR_OUT); IoTGpioSetOutputVal(GPIO_ID_PA22, 0); IoTGpioInit(GPIO_ID_PA21); IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT); IoTGpioSetOutputVal(GPIO_ID_PA21, 0); IoTGpioInit(GPIO_ID_PA20); IoTGpioSetDir(GPIO_ID_PA20, IOT_GPIO_DIR_OUT); IoTGpioSetOutputVal(GPIO_ID_PA20, 0); IoTGpioInit(GPIO_ID_PA19); IoTGpioSetDir(GPIO_ID_PA19, IOT_GPIO_DIR_OUT); IoTGpioSetOutputVal(GPIO_ID_PA19, 0); IoTGpioInit(GPIO_ID_PA13); IoTGpioSetDir(GPIO_ID_PA13, IOT_GPIO_DIR_OUT); IoTGpioSetOutputVal(GPIO_ID_PA13, 0); if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail\n"); return; } OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } OS_Sleep(3); const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID; const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK; WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { printf("ssid: %s ", scan_results[i].ssid); printf("securityType: %d\n", scan_results[i].securityType); if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, psk); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return; } if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return; } printf("Config Success\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return; } observer_base *net_ob; net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, NET_CTRL_MSG_ALL, net_cb, NULL); if (net_ob == NULL) return; if (sys_ctrl_attach(net_ob) != 0) return; while (1) { OS_MSleep(500); } } void DiggerMain(void) { if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(DiggerMain);
这部分主要是参考了【XR806开发板试用】通过MQTT实现手机远程实现PWM控灯
3.2.3 遥控器(手机端)配置
手机端安装mqtt调试软件即可,后期可以考虑自行开发APP。在调试阶段利用PC端的mqtt工具也可以,我使用的mqttfx后记
后续还有几个优化的点,第一个是增加蓝牙的控制通路,第二个是安装电池仓。
-
【XR806鸿蒙开发板实践】实现IOT智慧机房方案(一)
本文转自极术社区:https://aijishu.com/a/1060000000292658
作者:kiwin一、设计目的
随着信息网络技术的不断发展,各类规模大小不等,设备种类不同的网络设备机房随着数据处理能力和数据量急剧增长。数据中心机房内,环境监控十分重要,一旦机房环境出问题,轻则引起服务器故障,重则导致大量设备损毁和数据丢失,甚至酿成火灾。结合现有的XR806开发板,作为一个IOT模组,可以拿来实现一个搭配鸿蒙APP的IOT智慧机房方案,实现对机房环境温湿度、烟感等的实时检测、查看、设置和报警功能。
二、系统组成及功能说明
XR806芯片是一款基于安谋科技STAR-MC1处理器来研发设计的支持WIFI和BLE的高集成度无线MCU芯片,同时搭载了鸿蒙L0系统,眼下鸿蒙开发正当热,所以想到将XR806运用于机房中,实现对机房环境的实时检测,并上报到自家服务平台,同时开发一个鸿蒙APP实现对其的查看,设置功能等。
此次先实现的是将手上现有的sht30温湿度传感器模块外接到XR806上,通过I2C通讯,对传感器实时采集温湿度数据;然后通过MQTT协议将XR806设备注册到自己搭建的服务器平台,同时维持心跳,并将主控实时采集到的温湿度数据上报到MQTT服务器;最后,自行开发一个鸿蒙手机APP,通过WebSocket协议实现对温湿度传感器数据的实时展示及报警阈值设置等业务功能。
三、具体实施步骤
环境及物料准备
编译环境采用的是openharmony官网的docker镜像(省去了自己折腾环境的过程)
XR806开发板、sht30温湿度传感器模块、鸿蒙系统的手机、自己搭建的服务器一台。
sht30温湿度传感器模块通过I2C0引脚外接到XR806上(参照主芯片引脚说明图进行接线)。
参照如上两图;sht30和主控I2C通讯,使用I2C0,手动将sht30上的GND接到XR806的11引脚,将SDA和SCL接到XR806上的第7、8引脚。
2.新建工程及代码实现
参考ohosdemo目录下的各种demo示例代码,创建自己的工程目录iot_smartroom。
目录结构如下:
iot_smartroom |-- BUILD.gn |-- include | |-- i2c.h | |-- mqtt.h |-- src |-- i2c.c |-- main.c |-- mqtt.c
其中mqtt.c是封装了mqtt相关接口,i2c.c是封装了i2c相关接口,main.c负责业务流程,依赖mqtt.h和i2c.h中提供的接口进行调用(后续再将main.c中有关wifi热点扫描及连接的接口独立封装出来)
详细代码如下:
device\xradio\xr806\ohosdemo\BUILD.gn
新增"iot_smartroom:app_smartroom"group("ohosdemo") { deps = [ #"hello_demo:app_hello", #"iot_peripheral:app_peripheral", #"wlan_demo:app_WlanTest", #"led:app_led", "iot_smartroom:app_smartroom", ] } device\xradio\xr806\ohosdemo\iot_smartroom\BUILD.gn import("//device/xradio/xr806/liteos_m/config.gni") static_library("app_smartroom") { configs = [] sources = [ "src/main.c", "src/i2c.c", "src/mqtt.c", ] cflags = board_cflags include_dirs = board_include_dirs include_dirs += [ "//kernel/liteos_m/kernel/arch/include", "include", "//base/iot_hardware/peripheral/interfaces/kits", ".", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", "//device/xradio/xr806/xr_skylark/project" ] }
device\xradio\xr806\ohosdemo\iot_smartroom\src\i2c.c 主要代码:
#include <stdio.h> #include <string.h> #include <fcntl.h> #include "stdarg.h" #include "kernel/os/os.h" #include "i2c.h" #include "ohos_init.h" #define I2C_SPEED (400000) #define I2C_DEVICE_ADDR (0x45) #define I2C_DEVICE_ID_ADDR (0x0F) #define I2C_DEVICE_ID (0x65) #define I2C_ID (0) static OS_Thread_t g_main_thread; static SHT30_DATA sht30_data; static unsigned char sht30_checkcrc(unsigned char data[], unsigned char numOfBytes, unsigned char checkSum) { unsigned char crc = 0xFF; unsigned char bit = 0; unsigned char byteCtr ; const unsigned short POLYNOMIAL = 0x131; for(byteCtr = 0; byteCtr < numOfBytes; ++byteCtr) { crc ^= (data[byteCtr]); for ( bit = 8; bit > 0; --bit) { if (crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL; else crc = (crc << 1); } } if(crc != checkSum) return 1; else return 0; } static float sht30_calcrh(unsigned short u16sRH) { float humidityRH = 0; // variable for result u16sRH &= ~0x0003; // clear bits [1..0] (status bits) //-- calculate relative humidity [%RH] -- humidityRH = (100 * (float)u16sRH / 65535); // RH = rawValue / (2^16-1) * 10 return humidityRH; } static float sht30_calctemperature(unsigned short u16sT) { float temperature = 0; // variable for result u16sT &= ~0x0003; // clear bits [1..0] (status bits) //-- calculate temperature [℃] -- temperature = (175 * (float)u16sT / 65535 - 45); //T = -45 + 175 * rawValue / (2^16-1) return temperature; } static int sht30_init(void) { int s32Ret; unsigned char send_data[2] = {0x23, 0x34}; s32Ret = IoTI2cWrite(I2C_ID, I2C_DEVICE_ADDR, send_data, 2); if (s32Ret != 0) { printf("Error: I2C write s32Ret = 0x%x!\r\n", s32Ret); return -1; } return 0; } int IotI2C_Init(void) { int s32Ret = 0; s32Ret = IoTI2cInit(I2C_ID, I2C_SPEED); if(s32Ret != 0){ printf("IoTI2cInit failed.\r\n"); return -1; }else{ printf("IoTI2cInit success.\r\n"); } s32Ret = sht30_init(); //sht30温湿度模块初始化 if(s32Ret != 0){ printf("sht30_init failed.\r\n"); return -1; }else{ printf("sht30_init success.\r\n"); } return 0; } int IoTI2c_Deinit(void) { IoTI2cDeinit(I2C_ID); return 0; } int IotI2C_ReadData(SHT30_DATA *pData) { float a = 0; unsigned int s32Ret; float temperature = 0; float humidity = 0; unsigned char data[3]; //data array for checksum verification unsigned short dat, tmp; unsigned char SHT3X_Data_Buffer[6]; //byte 0,1 is temperature byte 4,5 is humidity unsigned char send_data[2] = {0xE0,0x00}; s32Ret = IoTI2cWrite(I2C_ID, I2C_DEVICE_ADDR, send_data, 2); if (s32Ret != 0) { printf("Error: I2C write s32Ret = 0x%x!\r\n", s32Ret); return -1; } s32Ret = IoTI2cRead(I2C_ID, I2C_DEVICE_ADDR, SHT3X_Data_Buffer, 6); if (s32Ret != 0) { printf("Error: I2C read s32Ret = 0x%x!\r\n", s32Ret); return -1; } //check tem data[0] = SHT3X_Data_Buffer[0]; data[1] = SHT3X_Data_Buffer[1]; data[2] = SHT3X_Data_Buffer[2]; tmp = sht30_checkcrc(data, 2, data[2]); if(!tmp) /* value is ture */ { dat = ((unsigned short)data[0] << 8) | data[1]; temperature = sht30_calctemperature(dat); printf("Temperature = %f\r\n",temperature); } //check humidity data[0] = SHT3X_Data_Buffer[3]; data[1] = SHT3X_Data_Buffer[4]; data[2] = SHT3X_Data_Buffer[5]; tmp = sht30_checkcrc(data, 2, data[2]); if(!tmp) /* value is ture */ { dat = ((unsigned short)data[0] << 8) | data[1]; humidity = sht30_calcrh(dat); printf("Humidity = %.02f\r\n",humidity); } pData->temperature = temperature; pData->humidity = humidity; return 0; }
device\xradio\xr806\ohosdemo\iot_smartroom\src\mqtt.c 主要代码:
#include <stdio.h> #include <string.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "iot_gpio.h" #include "wifi_device.h" #include "net/mqtt/MQTTClient-C/MQTTClient.h" #define MQTT_DEMO_USERNAME "admin" #define MQTT_DEMO_PASSWORD "Unionman" #define MQTT_DEMO_CLIENT_ID "plug_ipc" #define MQTT_DEMO_BUF_SIZE (2*1024) static MQTTPacket_connectData mqtt_demo_connectData = MQTTPacket_connectData_initializer; static Client mqtt_client; static Network mqtt_demo_network; static int max_duty_ratio = 0; int IoTMqtt_init(void) { char *send_buf; char *recv_buf; mqtt_demo_connectData.clientID.cstring = MQTT_DEMO_CLIENT_ID; mqtt_demo_connectData.keepAliveInterval = 30; // 30s mqtt_demo_connectData.cleansession = 0; mqtt_demo_connectData.MQTTVersion = 4; //Version of MQTT 3.1.1 send_buf = malloc(MQTT_DEMO_BUF_SIZE); if (send_buf == NULL) { printf("no memory\n"); return -1; } recv_buf = malloc(MQTT_DEMO_BUF_SIZE); if (recv_buf == NULL) { free(send_buf); printf("no memory\n"); return -1; } /* init network */ NewNetwork(&mqtt_demo_network); /* init mqtt client object */ MQTTClient(&mqtt_client, &mqtt_demo_network, 6000, (unsigned char *)send_buf, MQTT_DEMO_BUF_SIZE, (unsigned char *)recv_buf, MQTT_DEMO_BUF_SIZE); /* set username and password */ mqtt_demo_connectData.username.cstring = MQTT_DEMO_USERNAME; mqtt_demo_connectData.password.cstring = MQTT_DEMO_PASSWORD; return 0; } int IoTMqtt_connect(char *host_name, char *host_port) { int ret = -1; ret = ConnectNetwork(&mqtt_demo_network, host_name, atoi(host_port)); if (ret != 0) { printf("mqtt connect faild, ret:%d, host:%s, port:%s\n", ret, host_name, host_port); goto exit; } ret = MQTTConnect(&mqtt_client, &mqtt_demo_connectData); if (ret != 0) { printf("mqtt connect faild, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); goto exit; } printf("mqtt connected\n"); exit: return ret; } int IoTMqtt_subscribe(char *topic) { int ret = -1; if (mqtt_client.isconnected) { ret = MQTTSubscribe(&mqtt_client, topic, 0, NULL); if (ret != 0) printf("mqtt subscribe faild ret:%d\n", ret); } return ret; } int IoTMqtt_unsubscribe(char *topic) { int ret = -1; if (mqtt_client.isconnected) { ret = MQTTUnsubscribe(&mqtt_client, topic); if (ret != 0) printf("mqtt unsubscribe faild, ret:%d\n", ret); } return ret; } int IoTMqtt_publish(char *topic, char *msg) { int ret = -1; MQTTMessage message; memset(&message, 0, sizeof(message)); message.qos = 0; message.retained = 0; /* disable retain the message in server */ message.payload = msg; message.payloadlen = strlen(msg); ret = MQTTPublish(&mqtt_client, topic, &message); if (ret != 0) printf("mqtt publish faild, ret:%d\n", ret); return ret; } int IoTMqtt_disconnect(void) { int ret = -1; if (mqtt_client.isconnected) { ret = MQTTDisconnect(&mqtt_client); if (ret != 0) printf("mqtt disconnect fail, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); } return ret; } void IoTMqtt_deinit(void) { if (mqtt_client.buf) { free(mqtt_client.buf); mqtt_client.buf = NULL; } if (mqtt_client.readbuf) { free(mqtt_client.readbuf); mqtt_client.readbuf = NULL; } }
device\xradio\xr806\ohosdemo\iot_smartroom\src\main.c主要代码:
#include <stdio.h> #include "wifi_device.h" #include "ohos_init.h" #include "kernel/os/os.h" #include "mqtt.h" #include "i2c.h" #include "common/framework/net_ctrl.h" #define IOT_DEVICEID "666666" #define MQTT_DEMO_HOST_NAME "192.168.220.220" //MQTT服务器IP #define MQTT_DEMO_PORT "1883" #define WIFI_DEVICE_CONNECT_AP_SSID "yur" #define WIFI_DEVICE_CONNECT_AP_PSK "06184188" #define MQTT_PUB_REG_TOPIC "/device/register" //设备注册 #define MQTT_PUB_HEART_TOPIC "/device/heartbeat" //设备心跳 #define MQTT_PUB_TEM_TOPIC "/door/device/temperature" //设备发布温度TOPIC #define MQTT_PUB_HUM_TOPIC "/door/device/humidity" //设备发布湿度TOPIC static OS_Thread_t g_mqtt_thread; static OS_Thread_t g_main_thread; static SHT30_DATA sht30_data; static void iot_task(void *arg) { int ret; int reconnect_times = 0; unsigned char payload[64] = ""; //温湿度传感器初始化 IotI2C_Init(); //MQTT初始化 IoTMqtt_init(); ret = IoTMqtt_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) { goto exit; } //发布设备注册主题 IoTMqtt_publish(MQTT_PUB_REG_TOPIC, IOT_DEVICEID); while (1) { IoTMqtt_publish(MQTT_PUB_HEART_TOPIC, IOT_DEVICEID); IotI2C_ReadData(&sht30_data); memset(payload, 0x0, sizeof(payload)); snprintf(payload, sizeof(payload), "%s/%.2f/%d", IOT_DEVICEID, \ sht30_data.temperature, 0); IoTMqtt_publish(MQTT_PUB_TEM_TOPIC, payload); memset(payload, 0x0, sizeof(payload)); snprintf(payload, sizeof(payload), "%s/%.2f/%d", IOT_DEVICEID, \ sht30_data.humidity, 0); IoTMqtt_publish(MQTT_PUB_HUM_TOPIC, payload); OS_MSleep(2000); //2s } exit: IoTMqtt_unsubscribe(MQTT_PUB_TEM_TOPIC); IoTMqtt_unsubscribe(MQTT_PUB_HUM_TOPIC); IoTMqtt_disconnect(); IoTMqtt_deinit(); IoTI2c_Deinit(); OS_ThreadDelete(&g_mqtt_thread); } static void net_cb(uint32_t event, uint32_t data, void *arg) { uint16_t type = EVENT_SUBTYPE(event); switch (type) { case NET_CTRL_MSG_NETWORK_UP: printf("NET_CTRL_MSG_NETWORK_UP\n"); if (!OS_ThreadIsValid(&g_mqtt_thread)) { OS_ThreadCreate(&g_mqtt_thread, "iot_task", iot_task, (void *)NULL, OS_THREAD_PRIO_APP, (8 * 1024)); } break; case NET_CTRL_MSG_NETWORK_DOWN: break; default: break; } } static int MainThread(void *arg) { int s32Ret = 0; //使能、扫描、连接热点 if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail\n"); return; } OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } OS_Sleep(3);//这里为了方便用延时,实际用回调更好,否则3秒可能不够 const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID; const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK; WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { printf("ssid: %s ", scan_results[i].ssid); printf("securityType: %d\n", scan_results[i].securityType); if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, psk); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return -1; } if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return -1; } printf("Config Success\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return -1; } observer_base *net_ob; net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, NET_CTRL_MSG_ALL, net_cb, NULL); if (net_ob == NULL) { return -1; } if (sys_ctrl_attach(net_ob) != 0) { return -1; } while(1) { OS_MSleep(2000); } return 0; } void SmartRoomMain(void) { printf("\r\SmartRoomMain Start\r\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } }
SYS_RUN(SmartRoomMain);
完整代码上传到:
链接:https://pan.baidu.com/s/11Iy-...
提取码:80qj3.编译及烧录
编译:hb build -f [OHOS INFO] [238/256] AR libs/libxr_wifi_adapter.a [OHOS INFO] [239/256] STAMP obj/device/xradio/xr806/adapter/hals/adapter.stamp [OHOS INFO] [240/256] gcc cross compiler obj/kernel/liteos_m/kal/cmsis/libcmsis.cmsis_liteos.o [OHOS INFO] [241/256] AR libs/libcmsis.a [OHOS INFO] [242/256] AR libs/libkal.a [OHOS INFO] [243/256] gcc cross compiler obj/third_party/bounds_checking_function/src/libsec.secureinput_w.o [OHOS INFO] [244/256] gcc cross compiler obj/kernel/liteos_m/kernel/src/mm/libkernel.los_memory.o [OHOS INFO] [245/256] gcc cross compiler obj/third_party/bounds_checking_function/src/libsec.secureinput_a.o [OHOS INFO] [246/256] AR libs/libsec.a [OHOS INFO] [247/256] AR libs/libwifiservice.a [OHOS INFO] [248/256] STAMP obj/foundation/communication/wifi_lite/wifi.stamp [OHOS INFO] [249/256] gcc cross compiler obj/kernel/liteos_m/kernel/src/libkernel.los_task.o [OHOS INFO] [250/256] AR libs/libkernel.a [OHOS INFO] [251/256] STAMP obj/kernel/liteos_m/kernel.stamp [OHOS INFO] [252/256] STAMP obj/build/lite/ohos.stamp [OHOS INFO] [253/256] ACTION //build/lite:gen_rootfs(//build/lite/toolchain:arm-none-eabi-gcc) [OHOS INFO] [254/256] STAMP obj/build/lite/gen_rootfs.stamp [OHOS INFO] [255/256] ACTION //device/xradio/xr806:libSDK(//build/lite/toolchain:arm-none-eabi-gcc) [OHOS INFO] [256/256] STAMP obj/device/xradio/xr806/libSDK.stamp [OHOS INFO] /sdg/xiaoyuan.cai/project/xr806/vendor/xradio/xr806/fs.yml not found, stop packing fs. If the product does not need to be packaged, ignore it. [OHOS INFO] wifi_skylark build success [OHOS INFO] cost time: 0:00:11 [OpenHarmony][xr806]#
代码编译成功,参照官方烧录文档,使用PhoenixMC工具烧录到XR806开发板上:
4.服务端搭设及鸿蒙APP开发
MQTT服务端搭设省略,可自行百度;服务端由于需要同时MQTT对接XR806设备端以及WebSocket对接鸿蒙APP端,所以需要实现业务逻辑,在此是通过java编写打包成jar运行于服务器上,主要是实现了对接XR806 MQTT发起的设备注册以及心跳,同时通知给APP,此实现还涉及到mqsql数据库的应用,在此不赘述。
因为XR806是运行的鸿蒙L0系统,为了统一到鸿蒙的旗帜下,此次手机应用也采用了基于鸿蒙手机开发APP,重点是实现了对设备端采集到的温湿度实时展示,其他功能后续继续完善。
服务端运行LOG监视(接收XR806的心跳及上报的温湿度信息):
2022-01-11 16:41:14.692 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 接收到信息topic:/device/heartbeat msg:666666,开始处理 2022-01-11 16:41:14.692 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : ------刷新心跳------ 2022-01-11 16:41:14.696 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 接收到信息topic:/door/device/temperature msg:666666/23.33/0,开始处理 2022-01-11 16:41:14.696 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : ------温度信息处理------ 2022-01-11 16:41:14.696 DEBUG 1 --- [C41A5031E110123] c.u.d.m.N.findNewDeviceBySerialId : ==> Preparing: select * from um_new_device where serial_id=? 2022-01-11 16:41:14.697 DEBUG 1 --- [C41A5031E110123] c.u.d.m.N.findNewDeviceBySerialId : ==> Parameters: 666666(String) 2022-01-11 16:41:14.697 DEBUG 1 --- [C41A5031E110123] c.u.d.m.N.findNewDeviceBySerialId : <== Total: 1 2022-01-11 16:41:14.697 DEBUG 1 --- [C41A5031E110123] d.m.N.UpdateTemperatureAndWarnBySerialId : ==> Preparing: update um_new_device set temperature = ?, temperature_warn = ? where serial_id = ? 2022-01-11 16:41:14.698 DEBUG 1 --- [C41A5031E110123] d.m.N.UpdateTemperatureAndWarnBySerialId : ==> Parameters: 23.33(String), 0(Integer), 666666(String) 2022-01-11 16:41:14.725 DEBUG 1 --- [C41A5031E110123] d.m.N.UpdateTemperatureAndWarnBySerialId : <== Updates: 1 2022-01-11 16:41:14.725 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 更新温度信息成功! 2022-01-11 16:41:14.725 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : ------------666666-------- 2022-01-11 16:41:14.725 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 发送消息+channelId:0242acfffe150002-00000001-0000004a-140074f3a73178fa-1e4d0f61param:{"temperatureWarn":0,"temperature":"23.33","state":1,"type":"smart_computer_room","deviceId":"666666"} 2022-01-11 16:41:14.725 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 接收到信息topic:/door/device/humidity msg:666666/48.00/0,开始处理 2022-01-11 16:41:14.725 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : ------湿度信息处理------ 2022-01-11 16:41:14.725 DEBUG 1 --- [C41A5031E110123] c.u.d.m.N.findNewDeviceBySerialId : ==> Preparing: select * from um_new_device where serial_id=? 2022-01-11 16:41:14.725 DEBUG 1 --- [C41A5031E110123] c.u.d.m.N.findNewDeviceBySerialId : ==> Parameters: 666666(String) 2022-01-11 16:41:14.726 DEBUG 1 --- [C41A5031E110123] c.u.d.m.N.findNewDeviceBySerialId : <== Total: 1 2022-01-11 16:41:14.726 DEBUG 1 --- [C41A5031E110123] .u.d.m.N.UpdateHumidityAndWarnBySerialId : ==> Preparing: update um_new_device set humidity = ?, humidity_warn = ? where serial_id = ? 2022-01-11 16:41:14.726 DEBUG 1 --- [C41A5031E110123] .u.d.m.N.UpdateHumidityAndWarnBySerialId : ==> Parameters: 48.00(String), 0(Integer), 666666(String) 2022-01-11 16:41:14.750 DEBUG 1 --- [C41A5031E110123] .u.d.m.N.UpdateHumidityAndWarnBySerialId : <== Updates: 1 2022-01-11 16:41:14.750 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 更新湿度信息成功! 2022-01-11 16:41:14.750 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : ------------666666-------- [0242acfffe150002-00000001-0000004a-140074f3a73178fa-1e4d0f61] 2022-01-11 16:41:14.750 INFO 1 --- [C41A5031E110123] c.u.smartroom.common.util.LogUtils : 发送消息+channelId:0242acfffe150002-00000001-0000004a-140074f3a73178fa-1e4d0f61param:{"humidityWarn":0,"humidity":"48.00","state":1,"type":"smart_computer_room","deviceId":"666666"}
最终华为Mate40E运行效果:
PS:由于鸿蒙APP开发经验不足,过程还是遇到不少问题,由于文章篇幅原因,这里不详细讲解服务端及APP代码,需要服务器JAVA及鸿蒙APP源码的可以私信。
四、参考资料
https://aijishu.com/a/1060000000256584 (基于安谋科技STAR-MC1处理器的「全志科技XR806开发板」简介)
https://aijishu.com/a/1060000000256608 (全志XR806开发板硬件bomlist,开发板及底板设计原理图下载)
https://aijishu.com/a/1060000000256612 (全志XR806开发板软件系统简介)
https://aijishu.com/a/1060000000148601 (【开发板试用报告】学习MQTT开发) -
【XR806开发板试用】在 xr806 上移植 LVGL
不多废话,直接开搞,先上效果图
准备
-
开发环境啥的,已经有很多文章了,这里就不再提搭建开发环境的相关内容了。
一个屏幕(1.8' 128x160)
-
LVGL源码(v8.0.2)
(链接: https://github.com/lvgl/lvgl)
开搞
新建工程
在ohosdome目录下新建一个工程"SPI_LCD",然后在工程文件加中添加"inc","src"文件夹和"BUILD.gn"文件,接着在src目录下创建"main.c"和"lcd.c",在inc目录下创建lcd.h,并将lvgl8.0.2源码也下载到工程目录下,如下图:
lcd.c 和 lcd.h,这两个文件是lcd屏幕的驱动文件,里面包含了lcd的驱动程序,这里主要看一下用于移植LVGL的刷屏函数lcd_flush:
void lcd_flush(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *color_p) { uint16_t w = x2 - x1 + 1; uint16_t h = y2 - y1 + 1; uint32_t draw_size = w * h; lcd_set_address(x1, y1, x2-1, y2-1); for (uint32_t i=0; i<draw_size; i++) { lcd_write_dat_16(color_p[i]); } }
移植LVGL
1、添加源文件
将lvgl目录下(包括子目录)所有".c"文件的相对路径添加到"BUILD.gn"文件中,以加入编译,如下图所示(展示部分):
2、添加头文件路径
一样修改"BUILD.gn"文件,如下图所示(对于移植LVGL,主要是红框中的):
3、LVGL配置文件
复制./lvgl/lv_conf_template.h为./lvgl/lv_conf.h,并作相应修改,主要修改如下:#define LV_COLOR_DEPTH 16 #define LV_TICK_CUSTOM_INCLUDE "kernel/os/os_time.h" #define LV_TICK_CUSTOM_SYS_TIME_EXPR (OS_GetTicks())
LVGL显示设备注册
复制./lvgl/examples/porting/lv_port_disp_template.c为./lvgl/examples/porting/lv_port_disp.c,并作相应修改,主要修改如下:
1、启用条件编译
2、添加LCD驱动头文件
3、创建LVGL显示缓存
红框中的值可以适当做相应调整
4、设置LVGL显示设备的参数
主要设置屏幕的宽度与高度
5、添加初始化LCD屏幕代码
6、添加LVGL的LCD刷屏函数
LVGL初始化
在应用程序的一开始,初始化LVGL和LVGL的显示设备,首先要导入LVGL的头文件:
定时调用LVGL任务处理器
创建一个任务来执行LVGL任务处理器
这里可以适当的在while(1)中加一个LOS_Msleep(1) 感谢群友.ACE彭洪权的反馈
测试
至此LVGL就移植完毕了,接下来编译测试代码
上面代码就是使用LVGL提供的标签控件在屏幕上显示"Hello, XR806!",就如开篇的第一张图片。总结
以上就是将LVGL移植到XR806上运行的整个流程,个人感觉XR806的编程体验还是挺好的,提供了各种外设的丰富驱动,可以方便开发者快速使用XR806实现功能。
文章转自极术社区:https://aijishu.com/a/1060000000292988
作者:25Hz. -
-
Reply: 【资料释放】D1哪吒计算条资源汇总(持续更新)
Sipeed 荔枝派 Lichee RV Dock 全志 D1开发板
https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-24053782153.3.ce3252b1M0foX4&id=666274331852 -
【XR806开发板试用】基于XR806的鸿蒙舵狗
一、狗子外观图
二、PCA9685开发流程
狗子身上有12个舵机,一条腿接3个舵机,连接到一个pca9685舵机驱动上,舵机驱动通过I2C与XR806通信,XR806有两个I2C接口,我主要是用第1个(B14, B15),通过控制PCA9685上的寄存器来简介控制舵机。
pca9685参数:
- I2C接口,支持高达16路PWM输出,每路12位分辨率(4096级);
- 内置25MHz晶振,可不连接外部晶振,也可以连接外部晶振,最大50MHz;
- 支持2.3V-5.5V电压,最大耐压值5.5V,逻辑电平3.3V;
- 具有上电复位,以及软件复位等功能。
引脚图示:
1. pca初始化
首先要进行i2c初始化:
i2c_init(i2c_id);
导入相关库:
#include "driver/chip/hal_i2c.h" #define i2c_id 1 # 使用第1个i2c
i2c初始化函数:
void i2c_init(unsigned int id){ I2C_InitParam initParam; initParam.addrMode = I2C_ADDR_MODE_7BIT; initParam.clockFreq = 40000; if (HAL_I2C_Init(id, &initParam) != HAL_OK) { printf("i2c init fail!\n"); while(1); } else { printf("i2c init success!\n"); } }
主要是调用HAL_I2C_Init函数对第1个i2c接口进行初始化。
初始化pca9685:
pca9685_init(60);
pca9685_init函数:
void pca9685_init(float hz) { pca_write(pca_mode1, 0x0); pca_setfreq(hz); OS_MSleep(500); }
2. pca操作
1. pca9685写寄存器
void pca_write(uint8_t reg_addr, uint8_t data) { uint8_t buf[1]; buf[0] = data; HAL_I2C_Master_Transmit_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1); }
2. pca9685读寄存器
uint8_t pca_read(uint8_t reg_addr) { uint8_t buf[1]; HAL_I2C_Master_Receive_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1); return buf[0]; }
3. pca9685设置频率
void pca_setfreq(float freq) { uint8_t prescale,old_mode,new_mode; double prescaleval; freq *= 0.92; prescaleval = 25000000; prescaleval /= 4096; prescaleval /= freq; prescaleval -= 1; prescale =(uint8_t)(prescaleval + 0.5f); old_mode = pca_read(pca_mode1); new_mode = (old_mode & 0x7F) | 0x10; pca_write(pca_mode1, new_mode); pca_write(pca_pre, prescale); pca_write(pca_mode1, old_mode); OS_MSleep(2); pca_write(pca_mode1, old_mode | 0xa1); }
这里的代码不像读函数和写函数一样好理解,主要是一般情况下,在用pca9685内置晶振,为25MHZ,通过配置PRE_SCALE寄存器进行配置。如果在舵机控制中,采用内置晶振,取osc_clock=25000000,update_rate=50(舵机控制频率50Hz)。
而且在写PRESCALE寄存器的时候,要先设置为Sleep模式,也就是将mode1寄存器的SLEEP标志位设置为1,具体可参考我上面写的代码。
3. pca设置pwm
void pca_setpwm(uint8_t num, uint32_t on, uint32_t off) { pca_write(LED0_ON_L+4*num,on); pca_write(LED0_ON_H+4*num,on>>8); pca_write(LED0_OFF_L+4*num,off); pca_write(LED0_OFF_H+4*num,off>>8); }
pwm通道寄存器如下图:
由图可知,对于每一个通道,有4个寄存器,在设置PWM占空比的时候,首先配置舵机的示例如下图所示(ON < OFF的情况):
狗子运行图:
核心代码:
#include <stdio.h> #include "ohos_init.h" #include "kernel/os/os.h" // #include "iot_i2c.h" #include "driver/chip/hal_i2c.h" #include "iot_errno.h" #include "math.h" #define i2c_id 1 #define pca_adrr 0x40 // pca9685设备地址 #define pca_mode1 0x0 #define pca_pre 0xFE #define LED0_ON_L 0x6 #define LED0_ON_H 0x7 #define LED0_OFF_L 0x8 #define LED0_OFF_H 0x9 static OS_Thread_t g_main_thread; void pca_write(uint8_t reg_addr, uint8_t data) { uint8_t buf[1]; buf[0] = data; HAL_I2C_Master_Transmit_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1); } uint8_t pca_read(uint8_t reg_addr) { uint8_t buf[1]; HAL_I2C_Master_Receive_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1); return buf[0]; } void pca_setfreq(float freq) { uint8_t prescale,old_mode,new_mode; double prescaleval; freq *= 0.92; prescaleval = 25000000; prescaleval /= 4096; prescaleval /= freq; prescaleval -= 1; prescale =(uint8_t)(prescaleval + 0.5f); old_mode = pca_read(pca_mode1); new_mode = (old_mode & 0x7F) | 0x10; pca_write(pca_mode1, new_mode); pca_write(pca_pre, prescale); pca_write(pca_mode1, old_mode); OS_MSleep(2); pca_write(pca_mode1, old_mode | 0xa1); } void pca_setpwm(uint8_t num, uint32_t on, uint32_t off) { pca_write(LED0_ON_L+4*num,on); pca_write(LED0_ON_H+4*num,on>>8); pca_write(LED0_OFF_L+4*num,off); pca_write(LED0_OFF_H+4*num,off>>8); } void pca9685_init(float hz) { pca_write(pca_mode1, 0x0); pca_setfreq(hz); OS_MSleep(500); } void pca_rotate(uint8_t num,uint8_t angle) { uint32_t off=0; off=floor(angle * 2 + angle / 5 + 158); pca_setpwm(num, 0, off); } void i2c_init(unsigned int id){ I2C_InitParam initParam; initParam.addrMode = I2C_ADDR_MODE_7BIT; initParam.clockFreq = 40000; if (HAL_I2C_Init(id, &initParam) != HAL_OK) { printf("i2c init fail!\n"); while(1); } else { printf("i2c init success!\n"); } } static void MainThread(void *arg) { uint8_t i = 0; i2c_init(i2c_id); pca9685_init(60); printf("i2c and pca9685 init done.\n"); while(1){ pca_rotate(0, 0); OS_MSleep(1000); pca_rotate(0, 180); OS_MSleep(1000); } } void PCAMain(void) { printf("PCA9685 Motor Start.\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 10 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(PCAMain);
狗子的相关步态算法还在调试,使用的舵机是MG90S,走得还不是很流畅,狗腿子会抖,后面需要继续调试,而且供电也是个大问题,不过已经焊了个供电模块稍微解决了,后面还要给狗子加个壳。
文章转自极术社区:https://aijishu.com/a/1060000000292068
作者:堇花还没开吗 -
【XR806开发板试用】shell脚本一键配置XR806开发环境
本文是我基于官方文档整理的最新XR806开发环境搭建步骤,所有步骤都合并为几段shell脚本,可以直接复制粘贴执行。
我的开发环境是基于Ubuntu 20.04的,目前XR806的OpenHarmony代码版本是1.0.1_release;
每段脚本下方的注都是容易遇到坑的地方,请大家遇到问题的时候多注意。
准备repo命令行工具
[ "$(id -u)" == "0" ] && alias sudo= sudo apt update sudo apt -y install curl git python3 python3-pip ln -s /usr/bin/python3 /usr/bin/python # 设置pip源为阿里镜像站 pip config set global.index-url http://mirrors.aliyun.com/pypi/simple/ pip config set global.timeout 120 pip config set global.trusted-host mirrors.aliyun.com # 下载repo工具 [ -e ~/bin/ ] || mkdir ~/bin/ curl -s https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > ~/bin/repo chmod +x ~/bin/repo pip install requests # 码云的 repo 依赖这个pip包 echo 'export PATH=$PATH:~/bin' | tee -a ~/.bashrc
注:
repo命令行工具本身是一个python脚本,所以需要先安装python3;
这里的repo为码云提供的版本,REPO_URL的值他们已经修改好了,不需要再修改;
他们修改的代码依赖了requests包,因此步骤中有配置pip源和下载requests包;下载ARM交叉编译工具链
# 下载 gn 压缩包 GN_TARBALL=gn-linux-x86-1717.tar.gz GN_URL_PREFIX=https://repo.huaweicloud.com/harmonyos/compiler/gn/1717/linux curl $GN_URL_PREFIX/$GN_TARBALL > $GN_TARBALL # 下载 ninja 压缩包 NINJA_TARBALL=ninja.1.9.0.tar NINJA_URL_PREFIX=https://repo.huaweicloud.com/harmonyos/compiler/ninja/1.9.0/linux curl $NINJA_URL_PREFIX/$NINJA_TARBALL > $NINJA_TARBALL # 下载clang 9.0 压缩包 CLANG_TARBALL=llvm-linux-9.0.0-36191.tar CLANG_URL_PREFIX=https://repo.huaweicloud.com/harmonyos/compiler/clang/9.0.0-36191/linux curl $CLANG_URL_PREFIX/$CLANG_TARBALL > $CLANG_TARBALL # 下载 gcc-arm-none-eabi-10-2020-q4-major 压缩包 GCC_TARBALL=gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 GCC_URL_PREFIX=https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/10-2020q4 curl $GCC_URL_PREFIX/$GCC_TARBALL > $GCC_TARBALL [ -e ~/tools/ ] || mkdir ~/tools/ # 解压gn和ninja压缩包 tar -C ~/tools/ -xvf $GN_TARBALL tar -C ~/tools/ -xvf $NINJA_TARBALL echo 'export PATH=$PATH:~/tools:~/tools/ninja' | tee -a ~/.bashrc # 解压clang压缩包 tar -C ~/tools/ -xvf $CLANG_TARBALL echo 'export PATH=$PATH:~/tools/llvm/bin' | tee -a ~/.bashrc # 解压gcc压缩包 tar -C ~/tools/ -xvf $GCC_TARBALL echo 'export PATH=$PATH:~/tools/gcc-arm-none-eabi-10-2020-q4-major/bin' | tee -a ~/.bashrc source ~/.bashrc # 生效环境变量
注:
全志在线的步骤描述里面没有写需要下载gn/ninja/clang,但是后续的hb build命令会依赖这几个命令行工具;
下载OpenHarmony和XR806代码
# 配置你的git作者信息 git config --global user.email "yourname@example.com" git config --global user.name "Your Name" # 创建目录 [ -e ~/xr806_openharmony ] || mkdir ~/xr806_openharmony cd ~/xr806_openharmony # 初始化清单仓 repo init -u https://gitee.com/openharmony/manifest.git -b OpenHarmony_1.0.1_release --no-repo-verify # 同步所有代码仓到本地 repo sync -c repo forall -c 'git lfs pull' # 下载 xr806 的 device和vendor 仓到 device/xradio和vendor/xradio目录 git clone https://gitee.com/XR806/devboard_device_allwinner_xr806.git device/xradio/ git clone https://gitee.com/XR806/devboard_vendor_allwinner_xr806.git vendor/xradio/
注:
最后的两个仓是我从uncleli克隆的两个代码仓,因为全职在线文档中openharmony-sig组织下的代码仓暂设置访问权限为私有了,组织外部人员暂时无法访问;
编译XR806的OpenHarmony代码
配置项目
第一次编译前需要执行“配置项目”的步骤。主要用于生成部分Kconfig配置和Makefile代码片段,后续编译不再需要执行这里的步骤。sudo apt install -y libncurses5-dev # menuconfig 依赖的ncurses库 # 进入SDK目录。 cd device/xradio/xr806/xr_skylark/ # 复制配置文件。 cp project/demo/audio_demo/gcc/defconfig .config # 使用图形化界面确认配置。 make menuconfig # 执行make menuconfig后,按方向键选择save保存后,选择exist退出即可。 # 清除过程文件。 make build_clean # 生成静态库已经自动生成头文件。 make lib -j # 返回根目录。 cd -
安装hb命令
第一次编译前,需要安装hb命令。后续的编译不再需要执行这里的命令。cd ~/xr806_openharmony cd build/lite pip install prompt_toolkit==1.0.14 python setup.py install --user echo 'export PATH=$PATH:~/.local/bin' | tee -a ~/.bashrc # 将hb命令所在目录加到PATH环境变量 source ~/.bashrc # 生效环境变量
编译代码
hb set #回车,并选择wifi_skylark,第一次编译需要执行这个命令,执行完成后会生成ohos_config.json文件 hb build -f # 编译代码
烧录镜像
运行device\xradio\xr806\xr_skylark\tools目录下的phoenixMC_v3.1.21014b.exe;
编译完的镜像文件在device\xradio\xr806\xr_skylark\out目录下;
烧录软件的使用参考全志在线的文档:固件烧录 - XR806 (aw-ol.com)
烧录需要注意:记得勾选“硬件复位烧写模式”,否则进度到92%就会报错。观察日志
XR806 OpenHarmony默认的串口配置为:波特率115200,无校验,8位数据位,1位停止位。
使用PuTTY查看启动日志,需要在的Terminal配置中,勾选“Implicit CR in every LF”和“Implicit LF in every CR”这两个选项。
参考链接
参考了以下链接:
全志在线XR806的文档:https://xr806.docs.aw-ol.com/ (aw-ol.com)
码云临时代码仓:uncleli/devboard_device_allwinner_XR806 (gitee.com)文章转自极术社区:https://aijishu.com/a/1060000000291606
作者:xusiwei1236 -
【XR806开发板试用】简单点灯-- 基于SPI控制W2812矩阵幻彩动图和字幕显示系统
1.效果展示
1.gif 动图展示
2.字幕展示
2.软件开发流程
2.1 全志XR806 基本开发流程
使用指南自己踩过的坑
必须app开头
鸿蒙hb 依赖python 环境。建议使用conda虚拟环境
下载开启硬件校验和烧录重启2.2 W2812 简单介绍
不是科普文,自行百度
`/*WS2812B Timing sequence________ | | T0L |
0 code |<------>|<-------------->|
| T0H |________________| ___________ | | T1L |
1 code |<--------->|<--------- -->|
| T1H |______________|
RET code
| Treset | |<------------------------->| |___________________________|
Data transefer time:
T0H 0 code ,high voltage time 0.4us ±150ns
T1H 1 code ,high voltage time 0.8us ±150ns
T0L 0 code , low voltage time 0.85us ±150ns
T1L 1 code ,low voltage time 0.45us ±150ns
RES low voltage time Above 50us*/`
硬件驱动
供电 5V
驱动电平 gpio口3.3v 需要加一些简单的转换电路到5v
驱动时序 这里使用SPI 驱动 支持DMA搬运
PB04 SPIO_MOSI 引脚
2.3 软件框架
2.3.1 UDP
1.联网获取IP
使用 wlan_demo 中 例子 wifi_device_connect_test2.绑定端口
void udp_echoserver_init(void) { struct udp_pcb *upcb; err_t err; /* Create a new UDP control block */ upcb = udp_new(); if (upcb) { /* Bind the upcb to the UDP_PORT port */ /* Using IP_ADDR_ANY allow the upcb to be used by any local interface */ err = udp_bind(upcb, IP_ADDR_ANY, UDP_SERVER_PORT); if (err == ERR_OK) { /* Set a receive callback for the upcb */ udp_recv(upcb, udp_server_receive_callback, NULL); printf("led udp bind OK \r\n"); }else printf("led udp bind error \r\n"); } }
3.注册接收回调
void udp_server_receive_callback(void arg, struct udp_pcb upcb,struct pbuf p, const ip_addr_t addr, u16_t port) { printf(" 11 udp receive_callback \r\n"); /* Connect to the remote client */ udp_connect(upcb, addr, port); /* Tell the client that we have accepted it */ udp_send(upcb, p); memset(udp_recv_date,0x00,UDP_RECV_DATE_MAX); uint32_t buf_len = pbuf_copy_partial(p, udp_recv_date, p->len, 42); uint32_t cur_pos; if(UDP_RECV_LED_BUFF_LEN==buf_len){ #if 1 for(u32_t i = 0;i < buf_len;){ if(i%48==0)printf("\r\n"); printf("%x-%x-%x ",udp_recv_date[i],udp_recv_date[i+1],udp_recv_date[i+2]); if(udp_recv_date[i]!=0)cur_pos = i; i+=3; } printf("\r\n recv data %d,pos=%d %d,%d\r\n",buf_len,cur_pos,cur_pos/48,(cur_pos%48)/3); #endif led_spi_update(udp_recv_date); //led_spi_update_test(); } /* free the UDP connection, so we can accept new clients */ udp_disconnect(upcb); /* Free the p buffer */ pbuf_free(p); }
4.提取UDP数据
pbuf_copy_partial(p, udp_recv_date, p->len, 42);
这里偏移了42。包含了一些UDP通信的信息。2.3.2 SPI 驱动
1.SPI初始化 这里使用6M的速率void led_spi_init(void) { printf("led_spi_init\r\n"); IoTGpioInit(GPIO_ID_PA21); //(3) IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT); //(4) uint16_t i = 0; uint16_t loop_color = 0; SPI_Global_Config spi_param; spi_param.cs_level = DEMO_SPI_CS_LEVEL; spi_param.mclk = DEMO_SPI_MCLK; HAL_SPI_Init(DEMO_SPI_PORT, &spi_param); SPI_Config spi_Config; HAL_Status ret = HAL_OK; spi_Config.firstBit = SPI_TCTRL_FBS_MSB; spi_Config.mode = SPI_CTRL_MODE_MASTER; spi_Config.opMode = SPI_OPERATION_MODE_DMA; spi_Config.sclk = DEMO_SPI_MCLK; spi_Config.sclkMode = SPI_SCLK_Mode1; printf("spi open...\n"); ret = HAL_SPI_Open(DEMO_SPI_PORT, DEMO_SPI_CS, &spi_Config, 5000); if (ret != HAL_OK) { printf("spi open failed"); return ret; } HAL_SPI_Config(DEMO_SPI_PORT, SPI_ATTRIBUTION_IO_MODE, SPI_IO_MODE_NORMAL); printf("led_spi_init ok.\n"); }
2.数据发送
HAL_SPI_Transmit
这里有坑:SPI驱动第一次数据第一个自己前2位。波形离谱。这里通过多发一个字节0x00.避免过去。点阵屏
点阵屏灯排布
这里需要根据自己的走向修改程序。void broad_ws2812_set_position(uint8_t w,uint8_t h,uint8_t r,uint8_t g,uint8_t b) { uint16_t pos = 0; pos = w*BROAD_WS2812_STRIP_LED_H; if(w%2==0){ pos +=h; } else{ pos +=(BROAD_WS2812_STRIP_LED_H-h-1); } if(r==g&&r==b) { led_spi_buff[pos*3+0] = 0; // led_spi_buff[pos*3+1] = 0; led_spi_buff[pos*3+2] = 0; } else{ led_spi_buff[pos*3+0] = b/2; // led_spi_buff[pos*3+1] = r/2; led_spi_buff[pos*3+2] = g/2; } }
把2812控制数据变化成SPI数据
static void led_data_to_ws2812(uint8_t val, uint8_t* dst) { int i = 7; for (i = 7; i >= 0; i--) { if ((val >> i) & 0x01) { *(dst++) = 0xfc; } else { *(dst++) = 0xc0; } } }
系统简介
本系统实现了,使用python 控制 wifi蓝牙 2812点阵板子控制。可以实现大数据传输,实时仿真控制,暂时很好效果。
开源 百度网盘大家自行下载测试
链接:https://pan.baidu.com/s/1b3u0MtCrWUPOOnkjKskyQw
提取码:6666文章转自极术社区:https://aijishu.com/a/1060000000291763
作者:甴尐 -
【全志&OpenHarmony】全志两款支持OpenHarmony开发板在开放原子开源基金会OpenHarmony见面会实况分享
2021年12月28日,OpenAtom OpenHarmony开源见面会首站在江苏南京圆满举行。本次活动为OpenHarmony城市和高校全年巡回活动的首发站,以“融合行业需求,夯实关键技术”为主题,精彩呈现OpenHarmony 2021年度的共建成果及未来发展规划。
本次会议特别设立了Dev-Board-SIG分论坛,各家共建单位详尽阐述了OpenHarmony开发板目前的开发概况和未来规划。目前,在众家共建单位的努力下,小组已经有40余块开发板完成或计划完成基于OpenHarmony 3.0 LTS版本的移植适配工作,给OpenHarmony主线代码演进提供了源源不断的硬件基础。
全志也在本次见面会的Dev-Board-SIG分论坛上展出了两款支持OpenHarmony的开发板,分别为哪吒D1开发板、XR806开源鸿蒙开发板,除了本次大会上展出的开发板,全志T507等多个芯片平台的开发版也在适配OpenHarmony系统。
哪吒D1开发板
目前,主干代码里已经支持部分友商的芯片,计划在2022年支持ARM、MIPS、RISC-V等构架及多数主流芯片,全志D1也在即将支持的RISC-V框架芯片范围之内。因为它的CPU是RISC-V架构的,使用的是阿里平头哥的C906核心。中科院软件所于佳耕团队和全志一同进行OpenHarmony系统的移植,并已经完成适配工作。作为全志在线基于全志D1芯片定制的AIoT开发板,是全球首款支持64bit RISC-V指令集并支持Linux系统的可量产开发板。可以应用于科研教学、产品项目预研、开发爱好者DIY等。
XR806开源鸿蒙开发板
XR806开源鸿蒙开发板是在Dev-Board-SIG开发板展区展出的第二块全志的板子,也是全志首款适配OpenHarmony系统的开发板,XR806开发板使用的是全志自研的XR806芯片,支持wifi和ble,可以应用于智能家居、工业控制等场景的无线互联,同时也使用的是Arm-Star ARMv8-M MCU,sip了ddr和flash。高集成度的特点,可以帮助我们的客户和开发者最大化地降低硬件设计的复杂度,降低bom成本。同时支持efuse和security,留给了开发者更大的软件开发和设计空间。目前,XR806开发板代码已经通过XTS认证合入主仓。
仓库地址:https://gitee.com/moldy-potato-chips/devboard_device_allwinner_xr806千行百业加速数字化转型的当下,OpenHarmony将坚持”融合行业需求 夯实关键技术”的重要信念,全志也会持续输出优质产品,并进行OpenHarmony的适配工作,与开放原子开源基金会和众友商共建OpenHarmony繁荣生态,共享共赢新未来。
-
XR806开发板驱动6轴MPU6050 陀螺仪+加速度计及数据上传至上位机
一.开发环境
- ubuntu16.04虚拟机
- MPU6050陀螺仪
- 匿名上位机
二.软件
(1).I2C引脚
使用Xr806硬件I2C,需要在文件夹找到“/home/a/xr806_openharmony/device/xradio/xr806/xr_skylark/project/common/board/xr806_OHOS/borad_config.c"板级配置文件,可以看到共有两个I2C端口可用,i2c0和i2c1,这里是使用i2c0,引脚为A12,A13,复用通道F9。找好端口号,插上MPU6050。
(2).12C协议
鸿蒙已自带多种I2C协议,MPU6050需要写寄存器地址,必须选用能写HAL_I2C_Master_Transmit_Mem_IT()函数,写寄存器大小设为8位,但是连续写协议,发送数据长度设为1。(3).上位机协议
1:在调试过程中可以将某些标志位、寄存器、变量实时发回上位机,并在DEBUG页面显示。 2:通讯格式为:0x88 + 0xAD + len + num + DATA + SUM, len为num与DATA的总长度,num表示要改变哪个显示 状态,例如num=0x01即是要改变第一个LED,num=0x07即是改变第一个数字输出显示。当要改变LED时, DATA只需一字节,DATA=0x00表示关闭LED,大于0x00表示点亮LED;当要改变数字输出时,DATA需要两字 节, 表示 一个uint16数字,高字节在前。SUM为从0x88开始到SUM前一字节的和校验,uint8格式。 例如:发送 0x88 + 0xAD + 0x02 + 0x01 + 0x01 + 0x39 表示点亮第一个LED 发送 0x88 + 0xAD + 0x03 + 0x07 + 0x00 + 0x05 + 0x44 表示在第一个数字输出位置显示 5 。
3.源码
#include <stdio.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "/home/zfy/xr806_openharmony/base/iot_hardware/peripheral/interfaces/kits/iot_gpio.h" //(8) #include "driver/chip/hal_i2c.h" static OS_Thread_t g_main_thread,g_main_thread2; #define GPIO_ID_PA21 21 #define I2C_SPEED (200000) #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x3D #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B #define WHO_AM_I 0x75 #define SlaveAddress 0x68 unsigned int i2c_id = 0; void mpu6050_send_data(short aacx,short aacy,short aacz,short gyrox,short gyroy,short gyroz,short p,short r,short y) { char str_buff[5]; unsigned char head_buff[]={0x88,0xAF,0x1C}; unsigned char end_buff[]={0x00,0x00,0x00,0x00}; unsigned char check_sum[1]={0}; unsigned char data[24]; check_sum[0]+=0x88+0xAF+0x1C; data[0]=aacx>>8&0xFF; data[1]=aacx&0xFF; data[2]=aacy>>8&0xFF; data[3]=aacy&0xFF; data[4]=aacz>>8&0xFF; data[5]=aacz&0xFF; data[6]=gyrox>>8&0xFF; data[7]=gyrox&0xFF; data[8]=gyroy>>8&0xFF; data[9]=gyroy&0xFF; data[10]=gyroz>>8&0xFF; data[11]=gyroz&0xFF; data[12]=0; data[13]=0; data[14]=0; data[15]=0; data[16]=0; data[17]=0; data[18]=r>>8&0xFF; data[19]=r&0xFF; data[20]=p>>8&0xFF; data[21]=p; data[22]=y>>8&0xFF; data[23]=y&0xFF; int i=0; for(i=0;i<24;i++){ check_sum[0]+=data[i]; } // printf("%s",head_buff); // printf("%s",data); // printf("%s",end_buff); // printf("%s",check_sum); for(int i=0;i<3;i++){ printf("%c",head_buff[i]);} for(int i=0;i<24;i++){ printf("%c",data[i]);} for(int i=0;i<4;i++){ printf("%c",end_buff[i]);} for(int i=0;i<1;i++){ printf("%c",check_sum[i]);} //uart_send_str(data,24); } short Acc[3],Gyro[3]; void GetData(unsigned char REG_Address,short data[3]) { unsigned char H[1]={0},L[1]={0}; //HAL_I2C_Master_Receive_Mem_IT(i2c_id, SlaveAddress, REG_Address+1, I2C_MEMADDR_SIZE_16BIT, L, 1); //return ((int)(H[0]<<8)+L[0]); for(int i=0;i<3;i++){ HAL_I2C_Master_Receive_Mem_IT(i2c_id, SlaveAddress, REG_Address+(i*2), I2C_MEMADDR_SIZE_8BIT, H, 1); HAL_I2C_Master_Receive_Mem_IT(i2c_id, SlaveAddress, REG_Address+(i*2+1), I2C_MEMADDR_SIZE_8BIT, L, 1); data[i]=(( short)(H[0]<<8))|L[0]; } } static void MainThread2(void *arg) { printf("LED test start\r\n"); IoTGpioInit(GPIO_ID_PA21); IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT); IoTI2cInit(i2c_id,I2C_SPEED); const unsigned char data[]={0x00}; const unsigned char data1[]={0x07}; const unsigned char data2[]={0x06}; const unsigned char data3[]={0x18}; const unsigned char data4[]={0x01}; printf("i2c test start\r\n"); int a=HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, PWR_MGMT_1, I2C_MEMADDR_SIZE_8BIT, data, 1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, PWR_MGMT_1, I2C_MEMADDR_SIZE_8BIT, data1, 1); OS_MSleep(1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, SMPLRT_DIV, I2C_MEMADDR_SIZE_8BIT, data2, 1); OS_MSleep(1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, GYRO_CONFIG, I2C_MEMADDR_SIZE_8BIT, data3, 1); OS_MSleep(1); HAL_I2C_Master_Transmit_Mem_IT(i2c_id, SlaveAddress, ACCEL_CONFIG, I2C_MEMADDR_SIZE_8BIT, data4, 1); OS_MSleep(1); while (1) { IoTGpioSetOutputVal(GPIO_ID_PA21, 1); OS_MSleep(10); IoTGpioSetOutputVal(GPIO_ID_PA21, 0); OS_MSleep(10); GetData(ACCEL_XOUT_H,Acc); GetData(GYRO_XOUT_H,Gyro); GetData(GYRO_XOUT_H,Gyro); mpu6050_send_data(Acc[0],Acc[1],Acc[2],Gyro[0],Gyro[1],Gyro[2],0,0,0); } } void LEDMain(void) { printf("LED Test Start\n"); if (OS_ThreadCreate(&g_main_thread2, "MainThread2", MainThread2, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } static void MainThread(void *arg) {int i=0; while (1) { printf("hello world:%d\n",i); LOS_Msleep(1000); i++; } } void HelloTestMain(void) { printf("Wifi Test Start\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } //SYS_RUN(HelloTestMain); SYS_RUN(LEDMain);
三、效果
文章转自极术社区:https://aijishu.com/a/1060000000288462
作者:zhai -
【FAQ】全志D1芯片FAQ汇总(你不知道的和你想知道的的这里都有)
01、【FAQ】全志D1芯片 如何解决Audiocodec使用S24_LE格式进行录音,软件分析波形异常的问题?
02、【FAQ】全志D1芯片 如何在 Linux Device Tree 中配置预留内存?
03、【FAQ】全志D1芯片 如何解决Gstreamer播放1080视频显示异常问题(重影)?
04、【FAQ】全志D1芯片 如何对D1主频进行调节?
05、【FAQ】全志D1芯片 如何解决Gstreamer:fb UI旋转(直接修改内核参数)后,sunxifbsink显示异常问题?
06、【FAQ】全志D1芯片 Tina 如何查看通过 procd init 脚本启动的应用输出到 stdout/stderr 的打印信息?
07、【FAQ】全志D1芯片 如何移植 rtl8821cu wifi 驱动到 Linux-5.4内核?
08、【FAQ】全志D1芯片 XR829扫卡失败问题排查
09、【FAQ】全志D1芯片 mp4(Xvid)视频文件播放花屏问题
10、【FAQ】全志D1芯片 如何解决在创建视频解码器后,未送入视频帧数据之前,cpu被占满的问题?
11、【FAQ】全志D1芯片 如何在休眠唤醒过程中通过-sunxi_dump-读写外设寄存器?
12、【FAQ】全志D1芯片 minigui如何显示鼠标?
13、【FAQ】全志D1芯片 uart测试用例(支持自发自收,板间收发,数据校验,收发时间统计)
14、【FAQ】全志D1芯片 如何在tina使用tplayerdemo 进行rtsp拉流说明?
15、【FAQ】全志全系列芯片 APST平台无法下载或者更新工具
16、【FAQ】全志 F系列/R系列/V系列 RTOS平台cache操作接口介绍
17、【FAQ】全志系列芯片如何把flash擦成空片?
18、【FAQ】持续更新...... -
【XR806开发板试用】TCP通信测试 && Ping 命令测试
1.工程准备
由于要使用wifi功能,直接从wlan_demo复制一份出来,然后修改。
源文件只留下 main.c 就可以了。BUILD.gn文件
import("//device/xradio/xr806/liteos_m/config.gni") static_library("app_mying") { configs = [] sources = [ "main.c", ] cflags = board_cflags include_dirs = board_include_dirs include_dirs += [ "//kernel/liteos_m/kernel/arch/include", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", ] }
2. XR806 SDK
仔细看下xr806工程库的结构,
xr806的xr_skylark路径下属于芯片原生驱动库!
该路径下面有各种功能参考示例,如trustzone、net、ping、json等。
因此,直接参考xr_skylark\include下的内容,来实现一些简单的功能。
3.实现的功能
0)连接WiFi;
根据自己的SSID和PSK修改,代码直接copy原来的。1)ping服务器,进行联通性测试;
ping一下自己的服务器,根据需要进行修改。include下面有ping/ping.h文件。很简单的一个结构体(如下所示),看情况就是给个地址,设置一下参数,然后就可以实现ping命令的功能了。
struct ping_data { ip_addr_t sin_addr; /* server addr */ u32_t count; /* number of ping */ u32_t data_long; /* the ping packet data long */ u32_t interval; /* Wait interval seconds between sending each packet, default 1s */ u32_t timeout; /* Time to wait for a response, in seconds */ u32_t deadline; /* Specify a timeout, in seconds, ping thread will stop if timeout */ u32_t ttl; /* ttl ping only. Set the IP Time to Live. */ int run_flag; /* run flag, 0:stop 1:start */ ![CB84C5B5-489A-4f24-BD4D-67909D582B90.png](/assets/uploads/files/1640308813559-cb84c5b5-489a-4f24-bd4d-67909d582b90.png) }; s32_t ping(struct ping_data *data);
2)作为TCP客户端,连接server,发数据;
通过TCP连接服务器,发数据。
设置服务器信息
【地址】(比如:192.168.1.100)
【端口号】(比如5679)
net路径下面有lwip库,借此实现网络通信功能。代码里,通过宏定义的方式,将lwip_xxx改成了与linux下的soket API一样的接口。
参考正常的TCP_Client程序就行了。
3)读取一下xr_skylark里的cjson版本信息。
发现有cjson库,然后就随便测试一下。4.程序示例
#include <stdio.h> #include <string.h> #include <stdlib.h> #include "ohos_init.h" #include "driver/chip/hal_gpio.h" #include "kernel/os/os.h" #include "wifi_device.h" #include "cjson/cJSON.h" #include "net/ping/ping.h" #include "net/lwip-2.1.2/lwip/sockets.h" #include "net/lwip-2.1.2/lwip/tcp.h" #include "net/lwip-2.1.2/lwip/inet.h" #include "net/lwip-2.1.2/lwip/ip_addr.h" #define WIFI_DEVICE_CONNECT_AP_SSID "ChinaNet-111" #define WIFI_DEVICE_CONNECT_AP_PSK "111666111" #define GPIO_OUTPUT_PORT GPIO_PORT_A static OS_Thread_t g_main_thread; static void gpio_output_init(void) { GPIO_InitParam param; param.driving = GPIO_DRIVING_LEVEL_1; param.mode = GPIOx_Pn_F1_OUTPUT; param.pull = GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_OUTPUT_PORT, GPIO_PIN_21, ¶m);//led灯对应IO } static void gpio_output_ctl(uint8_t level) { HAL_GPIO_WritePin(GPIO_OUTPUT_PORT, GPIO_PIN_21, level ? GPIO_PIN_HIGH : GPIO_PIN_LOW); } void wifi_connect(void) { const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID; const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK; printf("\n=========== Connect Test Start ===========\n"); if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail.\n"); return; } printf("EnableWifi Success.\n"); if (WIFI_STA_ACTIVE == IsWifiActive()) printf("Wifi is active.\n"); OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } printf("Wifi Scan Success.\n"); OS_Sleep(1); WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, psk); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return; } printf("GetScanInfoList Success.\n"); if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return; } printf("AddDeviceConfig Success.\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return; } printf("ConnectTo Success\n"); OS_Sleep(3); WifiLinkedInfo get_linked_res; if (WIFI_SUCCESS != GetLinkedInfo(&get_linked_res)) { printf("Error: GetLinkedInfo Fail\n"); return; } printf("GetLinkedInfo Success.\n"); printf("ssid: %s\n", get_linked_res.ssid); printf("bssid: "); for (int j = 0; j < WIFI_MAC_LEN; j++) { printf("%02X", get_linked_res.bssid[j]); } printf("\n"); printf("rssi: %d\n", get_linked_res.rssi); unsigned char get_mac_res[WIFI_MAC_LEN]; if (WIFI_SUCCESS != GetDeviceMacAddress(get_mac_res)) { printf("Error: GetDeviceMacAddress Fail\n"); return; } printf("GetDeviceMacAddress Success.\n"); for (int j = 0; j < WIFI_MAC_LEN - 1; j++) { printf("%02X:", get_mac_res[j]); } printf("%02X\n", get_mac_res[WIFI_MAC_LEN - 1]); } struct ping_data ping_t; //ping命令参数设置 void ping_init() { ip_addr_t server_ip; inet_aton("129.204.63.27", &server_ip); ping_t.sin_addr = server_ip; ping_t.count = 0xF; ping_t.data_long = 512; ping_t.timeout = 30; ping_t.run_flag = 1; } //TCP SOCKET int s; void tcp_test_init() { //socket create! s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //address info! struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(5679); inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr); //connect! if(connect(s,(void *)&server_addr,sizeof(server_addr)) < 0) { printf("connect tcp_server failed! \r\n"); } else { printf("connect tcp_server successfuly! \r\n"); } // send data send(s, "xr806\n", 7, 0); } static void MainThread(void *arg) { gpio_output_init(); wifi_connect(); ping_init(); ping(&ping_t); tcp_test_init(); char buf[32]; int cnt = 0; while(1) { sprintf(buf,"XR806:%s : %d \r\n",cJSON_Version(), cnt++); //向服务器发送数据 send(s,buf, sizeof(buf), 0); printf("%s:Hello XR806 \r\n",__func__); gpio_output_ctl(1); OS_Sleep(1); gpio_output_ctl(0); OS_Sleep(1); } } void WifiTestMain(void) { printf("Wifi Test Start\r\n"); if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(WifiTestMain);
5.结果展示
通过网络调试工具,建立TCP服务器,接收到了xr806发来的数据。
串口打印信息[net INF] netif (IPv4) is up [net INF] address: 192.168.1.110 [net INF] gateway: 192.168.1.1 [net INF] netmask: 255.255.255.0 [net INF] msg <network IPv6 state> GetLinkedInfo Success. ssid: ChinaNet-111 bssid: 5475956E3374 rssi: 110 GetDeviceMacAddress Success. 9C:9E:49:BA:5B:01 PING 129.204.63.27 520 bytes of data. Request timeout for icmp_seq=1 512 bytes from 129.204.63.27: icmp_seq=2 time=43 ms 512 bytes from 129.204.63.27: icmp_seq=3 time=43 ms 512 bytes from 129.204.63.27: icmp_seq=4 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=5 time=43 ms 512 bytes from 129.204.63.27: icmp_seq=6 time=45 ms 512 bytes from 129.204.63.27: icmp_seq=7 time=46 ms 512 bytes from 129.204.63.27: icmp_seq=8 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=9 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=10 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=11 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=12 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=13 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=14 time=44 ms 512 bytes from 129.204.63.27: icmp_seq=15 time=43 ms --- 129.204.63.27 ping statistics --- 15 packets transmitted, 14 received, 6% packet loss, time 672ms rtt min/avg/max/mdev = 43/43/46/3 ms connect tcp_server successfuly! MainThread:Hello XR806 MainThread:Hello XR806
文章转自极术社区:https://aijishu.com/a/1060000000286996
作者:Pingyang @Pingyang -
【XR806开发板试用】通过MQTT实现手机远程实现PWM控灯
一、例程编译、烧录确认
首先按照全志在线文档平台的点灯教程确保能正常编译、烧录和点灯:https://xr806.docs.aw-ol.com/...
确保例程没问题后,我们再改造例程,实现我们想要的功能二、代码编写
我们将led工程复制一份改文件夹名为mydemo,目录结构如下mydemo ├── BUILD.gn └── src └── main.c
然后BUILD.gn内容增加头文件引用,部分配置如下:
static_library("app_mydemo") { ... include_dirs += [ "//kernel/liteos_m/kernel/arch/include", "//base/iot_hardware/peripheral/interfaces/kits", ".", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", "//device/xradio/xr806/xr_skylark/project" ] }
接下来是main.c,内容如下:
#include <stdio.h> #include <string.h> #include "ohos_init.h" #include "kernel/os/os.h" #include "iot_gpio.h" #include "wifi_device.h" #include "common/framework/net_ctrl.h" #include "net/mqtt/MQTTClient-C/MQTTClient.h" #include "driver/chip/hal_pwm.h" static OS_Thread_t g_main_thread; static OS_Thread_t g_mqtt_thread; #define PWM_OUTPUT_CHL PWM_GROUP1_CH2 #define PWM_OUTPUT_MODE PWM_CYCLE_MODE #define WIFI_DEVICE_CONNECT_AP_SSID "ssid"//这里填你家路由器的SSID #define WIFI_DEVICE_CONNECT_AP_PSK "pwm"//这里填你家路由器的PWD #define MQTT_DEMO_CLIENT_ID "12345" #define MQTT_DEMO_HOST_NAME "broker-cn.emqx.io"//这个是免费调试用的MQTT服务器地址 #define MQTT_DEMO_PORT "1883" #define MQTT_DEMO_USERNAME "12345678" #define MQTT_DEMO_PASSWORD "12345678" #define MQTT_RESP_TOPIC "/to/master/light" //手机发出来的TOPIC #define MQTT_RECV_TOPIC "/to/slave/light" //设备发出来的TOPIC #define MQTT_DEMO_BUF_SIZE (2*1024) static MQTTPacket_connectData mqtt_demo_connectData = MQTTPacket_connectData_initializer; static Client mqtt_demo_client; static Network mqtt_demo_network; static int max_duty_ratio = 0; static int mqtt_demo_publish(char *topic, char *msg) ; static int mqtt_demo_init(void) { char *send_buf; char *recv_buf; mqtt_demo_connectData.clientID.cstring = MQTT_DEMO_CLIENT_ID; mqtt_demo_connectData.keepAliveInterval = 30; // 30s mqtt_demo_connectData.cleansession = 0; mqtt_demo_connectData.MQTTVersion = 4; //Version of MQTT 3.1.1 send_buf = malloc(MQTT_DEMO_BUF_SIZE); if (send_buf == NULL) { printf("no memory\n"); return -1; } recv_buf = malloc(MQTT_DEMO_BUF_SIZE); if (recv_buf == NULL) { free(send_buf); printf("no memory\n"); return -1; } /* init network */ NewNetwork(&mqtt_demo_network); /* init mqtt client object */ MQTTClient(&mqtt_demo_client, &mqtt_demo_network, 6000, (unsigned char *)send_buf, MQTT_DEMO_BUF_SIZE, (unsigned char *)recv_buf, MQTT_DEMO_BUF_SIZE); /* set username and password */ mqtt_demo_connectData.username.cstring = MQTT_DEMO_USERNAME; mqtt_demo_connectData.password.cstring = MQTT_DEMO_PASSWORD; return 0; } static int mqtt_demo_connect(char *host_name, char *host_port) { int ret = -1; ret = ConnectNetwork(&mqtt_demo_network, host_name, atoi(host_port)); if (ret != 0) { printf("mqtt connect faild, ret:%d, host:%s, port:%s\n", ret, host_name, host_port); goto exit; } ret = MQTTConnect(&mqtt_demo_client, &mqtt_demo_connectData); if (ret != 0) { printf("mqtt connect faild, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); goto exit; } printf("mqtt connected\n"); exit: return ret; } static void mqtt_demo_msg_cb(MessageData *data) { printf("get a message, topic: %.*s, msg: %.*s\n", data->topicName->lenstring.len, data->topicName->lenstring.data, data->message->payloadlen, (char *)data->message->payload); if(!strncmp(data->topicName->lenstring.data, "/to/slave/light", 15) && data->message->payloadlen) { char *payload = data->message->payload; char str[8] = ""; int max_len = data->message->payloadlen > 3 ? 3 : data->message->payloadlen; strncpy(str, payload, max_len); int duty = atoi(str); HAL_Status status = HAL_PWM_ChSetDutyRatio(PWM_OUTPUT_CHL, duty * max_duty_ratio / 100); if (status != HAL_OK) printf("%s(): %d, PWM set duty ratio error\n", __func__, __LINE__); if(duty) { mqtt_demo_publish(MQTT_RESP_TOPIC, "light on"); } else { mqtt_demo_publish(MQTT_RESP_TOPIC, "light off"); } } } static int mqtt_demo_subscribe(char *topic) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTSubscribe(&mqtt_demo_client, topic, 0, mqtt_demo_msg_cb); if (ret != 0) printf("mqtt subscribe faild ret:%d\n", ret); } return ret; } static int mqtt_demo_unsubscribe(char *topic) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTUnsubscribe(&mqtt_demo_client, topic); if (ret != 0) printf("mqtt unsubscribe faild, ret:%d\n", ret); } return ret; } static int mqtt_demo_publish(char *topic, char *msg) { int ret = -1; MQTTMessage message; memset(&message, 0, sizeof(message)); message.qos = 0; message.retained = 0; /* disable retain the message in server */ message.payload = msg; message.payloadlen = strlen(msg); ret = MQTTPublish(&mqtt_demo_client, topic, &message); if (ret != 0) printf("mqtt publish faild, ret:%d\n", ret); return ret; } static int mqtt_demo_disconnect(void) { int ret = -1; if (mqtt_demo_client.isconnected) { ret = MQTTDisconnect(&mqtt_demo_client); if (ret != 0) printf("mqtt disconnect fail, ret:%d\n", ret); mqtt_demo_network.disconnect(&mqtt_demo_network); } return ret; } static void mqtt_demo_deinit(void) { if (mqtt_demo_client.buf) { free(mqtt_demo_client.buf); mqtt_demo_client.buf = NULL; } if (mqtt_demo_client.readbuf) { free(mqtt_demo_client.readbuf); mqtt_demo_client.readbuf = NULL; } } static void mqtt_task(void *arg) { int ret; int reconnect_times = 0; mqtt_demo_init(); ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) goto exit; ret = mqtt_demo_subscribe(MQTT_RECV_TOPIC); if (ret != 0) goto exit; mqtt_demo_publish(MQTT_RESP_TOPIC, "light ready"); while (1) { ret = MQTTYield(&mqtt_demo_client, 300); if (ret != 0) { printf("mqtt yield err, ret:%d\n", ret); reconnect: printf("mqtt reconnect\n"); mqtt_demo_disconnect(); ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT); if (ret != 0) { reconnect_times++; if (reconnect_times > 5) goto exit; OS_MSleep(5000); //5s goto reconnect; } } } exit: mqtt_demo_unsubscribe(MQTT_RECV_TOPIC); mqtt_demo_disconnect(); mqtt_demo_deinit(); OS_ThreadDelete(&g_mqtt_thread); } static void net_cb(uint32_t event, uint32_t data, void *arg) { uint16_t type = EVENT_SUBTYPE(event); switch (type) { case NET_CTRL_MSG_NETWORK_UP: printf("NET_CTRL_MSG_NETWORK_UP\n"); if (!OS_ThreadIsValid(&g_mqtt_thread)) { OS_ThreadCreate(&g_mqtt_thread, "connect_to_server_task", mqtt_task, (void *)NULL, OS_THREAD_PRIO_APP, (8 * 1024)); } break; case NET_CTRL_MSG_NETWORK_DOWN: break; default: break; } } static void MainThread(void *arg) { printf("MainThread start\r\n"); HAL_Status status = HAL_ERROR; PWM_ClkParam clk_param; PWM_ChInitParam ch_param; clk_param.clk = PWM_CLK_HOSC; clk_param.div = PWM_SRC_CLK_DIV_1; status = HAL_PWM_GroupClkCfg(PWM_OUTPUT_CHL, &clk_param); if (status != HAL_OK) printf("%s(): %d, PWM group clk config error\n", __func__, __LINE__); ch_param.hz = 1000; ch_param.mode = PWM_OUTPUT_MODE; ch_param.polarity = PWM_HIGHLEVE; max_duty_ratio = HAL_PWM_ChInit(PWM_OUTPUT_CHL, &ch_param); if (max_duty_ratio == -1) printf("%s(): %d, PWM ch init error\n", __func__, __LINE__); printf("max_duty_ratio=%d\n", max_duty_ratio); status = HAL_PWM_ChSetDutyRatio(PWM_OUTPUT_CHL, 0); if (status != HAL_OK) printf("%s(): %d, PWM set duty ratio error\n", __func__, __LINE__); status = HAL_PWM_EnableCh(PWM_OUTPUT_CHL, PWM_OUTPUT_MODE, 1); if (status != HAL_OK) printf("%s(): %d, PWM ch enable error\n", __func__, __LINE__); if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail\n"); return; } OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } OS_Sleep(3);//这里为了方便用延时,实际用回调更好,否则3秒可能不够 const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID; const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK; WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { printf("ssid: %s ", scan_results[i].ssid); printf("securityType: %d\n", scan_results[i].securityType); if (0 == strcmp(scan_results[i].ssid, ssid_want_connect)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, psk); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return; } if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return; } printf("Config Success\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return; } observer_base *net_ob; net_ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, NET_CTRL_MSG_ALL, net_cb, NULL); if (net_ob == NULL) return; if (sys_ctrl_attach(net_ob) != 0) return; while (1) { OS_MSleep(500); } } void LEDMain(void) { if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(LEDMain);
三、配置上层的BUILD.gn,并编译
部分内容如下:group("ohosdemo") { deps = [ #"hello_demo:app_hello", #"iot_peripheral:app_peripheral", #"wlan_demo:app_WlanTest", "mydemo:app_mydemo", ] }
然后执行hb build -f,编译成功(若编译失败见论坛有解决方案)
然后烧录到开发板上四、运行实例
烧录完成后,开发板重新上电,输出如下日志==================================================================== Hello! OpenHarmony! System tag : OpenHarmony 1.1.2_LTS ==================================================================== use default flash chip mJedec 0x0 [FD I]: mode: 0x10, freq: 96000000Hz, drv: 0 [FD I]: jedec: 0x0, suspend_support: 1 mode select:e wlan information =================================================== firmware: version : R0-XR_C07.08.52.65_02.84 May 27 2021 11:41:33-Y02.84 buffer : 8 driver: version : XR_V02.05 mac address: in use : 1c:98:c9:bc:50:01 in use : 1c:98:c9:bc:50:02 ==================================================================== wlan mode:a [VFS INF] LittleFS mount success. platform information =============================================== XR806 SDK v1.2.0 Dec 13 2021 13:06:05 heap space [0x2238d0, 0x24bc00), size 164656 cpu clock 160000000 Hz HF clock 40000000 Hz sdk option: XIP : enable INT LF OSC : enable SIP flash : enable mac address: efuse : 80:74:84:21:38:8e in use : 1c:98:c9:bc:50:01 ==================================================================== hiview init success.MainThread start max_duty_ratio=40000 console init success [net INF] no need to switch wlan mode 0 [net INF] msg <wlan scan success> ssid: TP-LINK_A668 securityType: 2 Config Success [net INF] no need to switch wlan mode 0 en1: Trying to associate with a4:c7:4b:71:f9:84 (SSID='XXXXXX' freq=2462 MHz) en1: Associated with a4:c7:4b:71:f9:84 en1: WPA: Key negotiation completed with a4:c7:4b:71:f9:84 [PTK=CCMP GTK=CCMP] en1: CTRL-EVENT-CONNECTED - Connection to a4:c7:4b:71:f9:84 completed [id=0 id_str=] [net INF] msg <wlan connected> [net INF] netif is link up [net INF] start DHCP... [net INF] IPv6 addr state change: 0x0 --> 0x1 [net INF] msg <> [net INF] netif (IPv4) is up [net INF] address: 192.168.3.48 [net INF] gateway: 192.168.3.1 [net INF] netmask: 255.255.255.0 [net INF] msg <network IPv6 state> NET_CTRL_MSG_NETWORK_UP WAR drop=1117, fctl=0x00d0. mqtt connected get a message, topic: /to/slave/light, msg: 0
可以看到,开发板成功连接路由器,并接入了MQTT服务器
五、手机测试
本人的手机是Iphone,也不会手机APP开发,于是我们下载了一个叫MQTTTool的第三方APP验证点灯,其界面如下:Host和Port填对,由于是免费开放的MQTT服务器,其他参数任意,设置好以后点Connect即可连接上MQTT服务器。然后底部切换至Subscribe栏,订阅主题:
若手机比设备先连接服务器并订阅相关主题,那么你会在订阅栏收到topic为/to/master/light的消息:light ready,这是因为设备连上服务器就会发布该topic。
接下来是发布主题,我们往/to/slave/light主题发送一个0~100的数字,就会让设备以这个数字为占空比控制LED灯亮灭,如0表示灭灯,50表示50%的占空比驱动LED灯,100表示100%的占空比驱动LED灯。同时设备收到LED控制指令后,若占空比不为0,则发送消息light on,否则发送消息light off
部分日志如下:
WAR drop=1117, fctl=0x00d0. mqtt connected get a message, topic: /to/slave/light, msg: 0 [net INF] IPv6 addr state change: 0x0 --> 0x1 [net INF] msg <> get a message, topic: /to/slave/light, msg: 100 get a message, topic: /to/slave/light, msg: 100 get a message, topic: /to/slave/light, msg: 50
以上就是一个手机远程给LED进行PWM调光的应用实例
本帖转自极术社区:https://aijishu.com/a/1060000000284320
作者:ctspot