bootloader主程序在 application/baremetal/bootloader/main.c的main函数内,通过调用console_set_bootcmd函数来执行不同介质的启动命令,这些命令在application/baremetal/bootloader/cmd文件夹内。
SDK文档的“SDK 编译”章节对Eclipse和VS Code均有描述
芯片ID读取可参考/bsp/artinchip/drv/efuse/drv_efuse.c(或bsp/artinchip/drv_bare/efuse/efuse.c)的drv_efuse_read_chip_id函数
方法二改法不太妥当。新版本下,Env.get函数返回的是deque、group.get函数返回的是list,再者不能用加号来组合。最好按下述修改:将deque转为list
diff --git a/kernel/rt-thread/tools/building.py b/kernel/rt-thread/tools/building.py
index 24bcf5cc..cf329981 100644
--- a/kernel/rt-thread/tools/building.py
+++ b/kernel/rt-thread/tools/building.py
@@ -779,8 +779,8 @@ def DoBuilding(target, objects):
CFLAGS = Env.get('CFLAGS', '') + group.get('LOCAL_CFLAGS', '')
CCFLAGS = Env.get('CCFLAGS', '') + group.get('LOCAL_CCFLAGS', '')
CXXFLAGS = Env.get('CXXFLAGS', '') + group.get('LOCAL_CXXFLAGS', '')
- CPPPATH = Env.get('CPPPATH', ['']) + group.get('LOCAL_CPPPATH', [''])
- CPPDEFINES = Env.get('CPPDEFINES', ['']) + group.get('LOCAL_CPPDEFINES', [''])
+ CPPPATH = list(Env.get('CPPPATH', [''])) + group.get('LOCAL_CPPPATH', [''])
+ CPPDEFINES = list(Env.get('CPPDEFINES', [''])) + group.get('LOCAL_CPPDEFINES', [''])
ASFLAGS = Env.get('ASFLAGS', '') + group.get('LOCAL_ASFLAGS', '')
for source in group['src']:
我之前试过是可以的,需要修改Flash设备类型及额外一些配置,参见这里:
今年年初起的这个项目至今都有任何进展,是因为还有很多细节一直没有搞定。并且LVGL的API一直都不稳定,单单是搞其API绑定层都要不少精力。
因为Zig基本上可以直接调用C,故放弃API绑定层的思路,直接在框架内调用UI库的C接口,并将框架转为MVVM。
MVVM框架本身就规定好了View跟Model的绑定规则,非常适合声明式UI框架。
目前已经基本在awtk-mvvm上添加实现了zig语言支持:https://gitee.com/ufbycd/awtk-mvvm-zig-example,在zig上实现Model要比C/C++方便很多,便捷性几乎跟javascript的差不多。
下一步,将用zig元组替换xml来实现View声明,即实现一个zig的MVVM框架:zig-mvvm
再下一步,将zig-mvvm框架推广到其它UI库,如LVGL等。
因为业余自由时间不是很多,一天至多只能抽出两三个小时,也是随缘更新。
我用AWTK有一两年了,只要在awtk.zlg.cn网站保持登录就会持续发放LIC。
不过我不怎么用AWStudio,实际项目都是手写UI描述文件XML,特别是用了MVVM的情形。即使是手写XML肯定也比LVGL好用,LVGL现在才开始搞UI描述文件。
如果对MVVM感兴趣,可以关注我这几天的研究成果,用zig写MVVM应用,利用zig的泛型/反射特性直接从Model构建出View-Model:
https://gitee.com/ufbycd/awtk-mvvm-zig-example
找到问题了!D21x是64位机,C++的初始化函数表.init_array应该是8字节对齐的,而SDK的链接脚本里弄成4字节对齐了。
按以下patch将链接脚本里的改为8字节对齐即可:
diff --git a/bsp/artinchip/sys/d21x/link_script/gcc_aic.ld.S b/bsp/artinchip/sys/d21x/link_script/gcc_aic.ld.S
index 7600a467..561a79ed 100644
--- a/bsp/artinchip/sys/d21x/link_script/gcc_aic.ld.S
+++ b/bsp/artinchip/sys/d21x/link_script/gcc_aic.ld.S
@@ -149,7 +149,7 @@ SECTIONS
*(.rodata.*)
*(.srodata*)
*(.rodata.str1.4)
- . = ALIGN(0x4) ;
+ . = ALIGN(0x8) ;
PROVIDE(__ctors_start__ = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
试过将main线程栈开到4MB都一样。C++代码如下,非常简单,且只是加入编译没有在其它地方调用C++源码的函数。
发现只要用了C++的标准库就出问题,不用C++标准库就没有问题;即使能宏ENABLE_CPP_STD就会出问题
#include <stdio.h>
#define ENABLE_CPP_STD 1
#if ENABLE_CPP_STD
#include <vector>
#endif
class MyOutputStream {
public:
MyOutputStream(){}
MyOutputStream& operator<<(int v) {
printf("%d", v);
return *this;
}
MyOutputStream& operator<<(unsigned int v) {
printf("%u", v);
return *this;
}
MyOutputStream& operator<<(long int v) {
printf("%ld", v);
return *this;
}
MyOutputStream& operator<<(unsigned long int v) {
printf("%lu", v);
return *this;
}
MyOutputStream& operator<<(char v) {
printf("%c", v);
return *this;
}
MyOutputStream& operator<<(const char* v) {
printf("%s", v);
return *this;
}
MyOutputStream& operator<<(char* v) {
printf("%s", v);
return *this;
}
};
class A {
public:
A();
~A();
private:
#if ENABLE_CPP_STD
std::vector<int> vi;
#endif
MyOutputStream cout;
};
A::A()
{
cout << "A init\n";
#if ENABLE_CPP_STD
vi = {1, 2, 3};
vi.push_back(4);
vi.push_back(5);
cout << "is size:" << vi.size() << '\n';
for(auto &i: vi) {
cout << i << ", ";
}
cout << '\n';
#endif
}
A::~A()
{
cout << "A deinit\n";
}
void a_test()
{
A a;
}
经测试AWTK和C++源码只要不是同时使能,系统都能正常运行。但两都同时使能编译在一起运行就会进入不了main函数并出现Exception如下:
CPU Exception: NO.1
x1(ra) : 00000000400361c6 x2(sp) : 00000000402de558 x3(gp) : 000000004026e530f
x5(t0) : 000000004026fef8 x6(t1) : 0000000000000002 x7(t2) : 00000000000000724
x9(s1) : 0000000040269670 x10(a0) : 0000000000000000 x11(a1) : 00000000000000008
x13(a3) : 00000000402a35a0 x14(a4) : 0000000066f25211 x15(a5) : 401f1830000000008
x17(a7) : 0000000000000064 x18(s2) : 00000000deadbeef x19(s3) : 00000000deadbeeff
x21(s5) : 00000000deadbeef x22(s6) : 00000000deadbeef x23(s7) : 00000000deadbeeff
x25(s9) : 00000000deadbeef x26(s10) : 00000000deadbeef x27(s11) : 00000000deadbeefa
x29(t4) : 0000000000000190 x30(t5) : 000000000000002d x31(t6) : 0000000000000000
mcause : 0000000000000001
mtval : 0000003000000000
mepc : 0000003000000000
mstatus : 8000000a00007880
完整启动日志如下:
tinySPL [Built on Sep 11 2024 16:34:02]
[W] usbh_is_connected()105 usb 1 port change wait failed.
[E] main()172 Not find udisk.
qspi0 freq (input): 91636363Hz
qspi0 freq ( bus ): 91636363Hz
nftl vol: data, size 0
Selecting default config 'Luban-lite firmware'
spl read: 2549040 byte, 348034 us -> 7152 KB/s
Boot time:
108099 : Enter main
109631 : Clock and pinmux done
110143 : Console UART ready
111287 : Heap init done
114980 : Banner shown
204155 : UDISK checked
571345 : Run APP
Welcome to ArtInChip Luban-Lite 1.0.5 [D21x Inside]
Built on Sep 24 2024 13:43:04
09-24 13:45:53 I/PWM main: ArtInChip PWM loaded
09-24 13:45:53 I/touch main: rt_touch init success
09-24 13:45:53 I/gt911 main: touch device gt911 init success
[I] aic_find_panel()83 find panel driver : panel-lvds
[I] aicfb_probe()978 fb0 allocated at 0x42000040
[I] hal_ge_init()1620 dither line phys: 0x424B0100
[I] pcm1803a_init()22 pcm1803a init
09-24 13:45:53 I/PSADC main: ArtInChip PSADC loaded
[I] aic_sdmc_clk_init()548 SDMC1 sclk: 50400 KHz, parent clk 1008000 KHz
09-24 13:45:53 I/SDMC main: SDMC1 BW 1, sclk 50400 KHz, clk 400 KHz(406 KHz), div 2-62
[I] aic_sdmc_probe()665 SDMC1 driver loaded
qspi0 freq (input): 91636363Hz
qspi0 freq ( bus ): 91636363Hz
[I] spinand_info_read()473 find raw ID efaa2200
[I] spinand_flash_init()524 Enabled BUF, HWECC. Unprotected.
nftl vol: data, size 0
09-24 13:45:53 I/sensor main: rt_sensor[temp_tsen_cpu] init success
09-24 13:45:53 I/WDT main: ArtInChip WDT loaded
CPU Exception: NO.1
x1(ra) : 00000000400361c6 x2(sp) : 00000000402de558 x3(gp) : 000000004026e530f
x5(t0) : 000000004026fef8 x6(t1) : 0000000000000002 x7(t2) : 00000000000000724
x9(s1) : 0000000040269670 x10(a0) : 0000000000000000 x11(a1) : 00000000000000008
x13(a3) : 00000000402a35a0 x14(a4) : 0000000066f25211 x15(a5) : 401f1830000000008
x17(a7) : 0000000000000064 x18(s2) : 00000000deadbeef x19(s3) : 00000000deadbeeff
x21(s5) : 00000000deadbeef x22(s6) : 00000000deadbeef x23(s7) : 00000000deadbeeff
x25(s9) : 00000000deadbeef x26(s10) : 00000000deadbeef x27(s11) : 00000000deadbeefa
x29(t4) : 0000000000000190 x30(t5) : 000000000000002d x31(t6) : 0000000000000000
mcause : 0000000000000001
mtval : 0000003000000000
mepc : 0000003000000000
mstatus : 8000000a00007880
其实就一个问题:烧录工具软件只支持Windows。可能是官方错认为大家都是在Windows下用虚拟机Linux做开发,实则在实体Linux下做开发的应该不在少数,MACOS也算类Linux环境。
RTOS SDK那边有Linux下的命令行烧录工具(upgcmd),但不稳定,且不支持指定分区烧录。
Linux SDK那边还没怎么研究,似乎没有Linux烧录工具。不过Linux下如果板子有网口很少在开发时直接烧录,而是通过网络共享的方式应用新固件或App,但没看到有文档介绍。板子没网口的话,也只能通过烧录来验证调试。
本人的开发环境是实体ManjaroLinux,目前用RTOS SDK做开发,SDK依赖库都能装上、源码编译无问题,唯独要时常开个Windows虚拟机来做烧录,肯定是不便利的。
目前手上有个带7寸显示屏和步进电机的新项目,用的是D21x单主控方案。步进电机只需控制AB点往返,但需要加减速控制,而Linux的PWM不支持细粒度控制,所以选了RTOS SDK进行开发,UI用AWTK。也是第一次用RT-Thread,但发现其代码质量和功能都要弱于Linux,后续还是打算切换到Linux,这就需要搞定Linux下的步进电机的加减速控制。
之前已经研究过播放wav音频 + 音频功放驱动步进电机是可行的,参见这里:
https://www.bilibili.com/video/BV1nj411D754
然而D21x的音频输出信号不是模拟信号而是PWM信号,是否可以参考上述方案用这个PWM实现step/dir接口的步进电机驱动?或者是否有其它更好的单主控驱动方案?
再次测试chart demo,可以肯定,是移植有问题:打开宏ENABLE_PERFORMANCE_PROFILE后,在demo主界面只有顶部app bar的时间在按秒更新,其它元素静止时,时间每更新一次就会有以下打印信息:
packages/third-party/awtk-ui/awtk/src/base/lcd_profile.c:322
-------------------------------------
total_cost=490
draw_image_cost=4 times=6
draw_text_cost=0 times=26
fill_cost=3 times=4
stroke_cost=0 times=0
end_frame_cost=4
-------------------------------------
其中 total_cost 指的是界面刷新耗时毫秒数,这里要490ms,太离谱!
上述调试说明参见这里: https://gitee.com/zlgopen/awtk/blob/master/docs/optimation.md#%E4%BA%8C%E5%B7%A5%E5%85%B7
根据上述信息可以得出UI性能弱鸡原因:
- 局部text更新触发整屏刷新
- 整屏刷新耗时半秒
每一条都是逆天般的存在!
再细看源码,LCD显存只开了双buffer没有开三buffer,AWTK官方说三buffer可以大大提高帧率。
像D21x这样的MPU HMI芯片只支持LVGL是不够的,LVGL的中文输入都是个问题,这个问题我另外发帖说明。
按上述改动后不知为何每次编译都更新compile_commands文件,导致编译时间变长好多。于是按下述修改,改为只有添加命令行参数时才更新:scons --cdb
# compilation database
AddOption('--cdb', dest='cdb', action='store_true', default=False, help='generate compilation database')
if GetOption('cdb'):
env.Tool('compilation_db')
compilation_db_file_name = PRJ_KERNEL + '_compile_commands.json'
env.CompilationDatabase(compilation_db_file_name)
又有新发现,scons本身就可以生成compile_commands.json文件。按下述diff修改SDK根目录上的SConstruct文件:
diff --git a/SConstruct b/SConstruct
index 9750ab30..a51b0846 100644
--- a/SConstruct
+++ b/SConstruct
@@ -102,6 +102,11 @@ env['ASCOM'] = env['ASPPCOM']
# signature database
env.SConsignFile(PRJ_OUT_DIR + ".sconsign.dblite")
+# compilation database
+env.Tool('compilation_db')
+compilation_db_file_name = PRJ_KERNEL + '_compile_commands.json'
+env.CompilationDatabase(compilation_db_file_name)
+
Export('RTT_ROOT')
Export('rtconfig')
- 编译boot时会生成baremetal_compile_commands.json
- 编译rt-thread时会生成rt-thread_compile_commands.json
那么搞两个项目Configuration分别叫boot和rt-thread分别各自使用上面的json db文件就可以灵活地对boot和rtos码字时进行完满的代码补全和阅读跳转了。
发现RTOS SDK里有这个API:aic_get_time_us,其实现就是读time寄存器,于是找到上面问题的原因了。
我用的芯片是D21x,其CSR寄存器time是64位的,所以此芯片应该没有timeh寄存器,只需读time寄存器即可。
那么,基本上只需两条指令就能获取分辨率为1us的时基,不错!
最后D21x上读取内核时基的函数如下,D13x等32位MCU就没那么便利了:要读取寄存器2次且需处理读取过程中的进位情况
static inline uint64_t _read_csr_time(void)
{
uint64_t value;
__asm__ __volatile__ ("csrr %0, time\n\t"
: "=r" (value) :
: "memory");
return value;
}
PS:RTOS SDK里d13x、d12x等读取系统时基的函数 aic_get_ticks,没有处理读取过程中可能出现的进位情况(读取时低32位寄存器发生32位进位),是否是一个BUG?!
u64 aic_get_ticks(void)
{
return (((u64)csi_coret_get_valueh() << 32U) | csi_coret_get_value());
}
@xdlkliang
感谢解答。我上面的需求说法有误,我要的是分辨率为1us,精度是us级(10us左右)的时基计时。
我测试了第二种方式,发现读取timeh寄存器时会再现cpu异常。寄存器读取函数如下:
static inline uint32_t _read_csr_time_lo(void)
{
uint32_t value;
__asm__ __volatile__ ("csrr %0, time\n\t"
: "=r" (value) :
: "memory");
return value;
}
static inline uint32_t _read_csr_time_hi(void)
{
uint32_t value;
__asm__ __volatile__ ("csrr %0, timeh\n\t"
: "=r" (value) :
: "memory");
return value;
}
RTOS SDK 1.0.5下经测试读寄存器time没有问题,读寄存器timeh时出现cpu异常:
CPU Exception: NO.2
x1(ra) : 00000000400338b8 x2(sp) : 000000004022d2c8 x3(gp) : 00000000401ba318 x4(tp) : 00000000deadbeef
x5(t0) : 00000000401bbef8 x6(t1) : 0000000000000001 x7(t2) : 00000000deadbeef x8(s0/fp): 0000000000000006
x9(s1) : 00000000400bb7e0 x10(a0) : 0000000000000001 x11(a1) : 000000004022d2e8 x12(a2) : ffffffff00000000
x13(a3) : 0000000040223228 x14(a4) : 0000000000000000 x15(a5) : 0000000000000000 x16(a7) : 0000000000000009
x17(a7) : 0000000040223228 x18(s2) : 0000000040223222 x19(s3) : 0000000040150570 x20(s4) : 00000000401829f8
x21(s5) : 0000000040150900 x22(s6) : 000000004015bd28 x23(s7) : 00000000401c2400 x24(s8) : 000000000000000d
x25(s9) : 0000000040223222 x26(s10) : 0000000040222dd0 x27(s11) : 00000000deadbeef x28(t3) : 0000000000000022
x29(t4) : 000000000000005c x30(t5) : 000000000000000a x31(t6) : 00000000deadbeef
mcause : 0000000000000002
mtval : 00000000c8102773
mepc : 00000000400bb7f0
mstatus : 8000000a00007880
什么问题?
按照10.1. Dynamic Module 使用指南生成的aic-dm-apps默认编译及运行测试都没问题。
在尝试测试C++编译,添加C++编译选项时编译失败:
编译配置文件 aic-dm-apps/hello/SConscript 添加 CXXFLAGS
from building import *
src = Glob('*.c') + Glob('*.cpp')
cwd = GetCurrentDir()
CPPPATH = [cwd]
CXXFLAGS = ' -std=c++11'
group = DefineGroup('', src, depend = [''], CPPPATH=CPPPATH, CXXFLAGS=CXXFLAGS)
Return('group')
编译输出:
$ scons --app=hello
scons: Reading SConscript files ...
args.outfile: /home/chenss/projects/test/source/luban-lite/partition_table.h
scons: done reading SConscript files.
scons: Building targets ...
CXX hello/cpp_test.o
riscv-none-embed-g++: error: -std=c++11: No such file or directory
scons: *** [hello/cpp_test.o] Error 1
scons: building terminated because of errors.
海石生风 说:PMOS开关电路,很常见呀。不过要注意VGS数值不要超过12V
https://whycan.com/files/members/1798/屏幕截图_20240722_122744.png这个电路有问题,这个GS上并一个这么大的电容,这个PMOS大负载的时候分分钟烧掉
GS并联电容是为了减小驱动容性负载导通时的瞬时电流,使其不超过MOS管的最大漏极电流,起到保护作用。实在想不出哪里会烧掉。
海石生风 说:PMOS开关电路,很常见呀。不过要注意VGS数值不要超过12V
https://whycan.com/files/members/1798/屏幕截图_20240722_122744.png这个电路有问题,这个GS上并一个这么大的电容,这个PMOS大负载的时候分分钟烧掉
什么原因烧的?
目前SDK里有命令行下载工具:upgcmd,此工具也可以通过构建系统来调用(scons --aicupg)。但我在D21x上测试发现很不稳定,只下载成功过一次。
Fatfs data 分区是支持读写的,需要一个nftl 做中间层,配置那里不要用自动计算
加了nftl中间层 "nftl": { "data": { "size": "-" } }后Fatfs data 分区确实是可读写的
将data分区选择为uffs格式在编译时会出现以下编译错误
是因为分区太小。你可以改大试试
我把uffs分区大小改为100MB后依然出现同样的编译错误:
page_2k_block_128k_oob_64_data.fs file_size: 0x6720000 is over much than part_size: 0x6400000
scons: *** [output/d21x_d213ecv-demo-v4_rt-thread_helloworld/images/d21x.elf] Error 1
另外,littlefs是否优于fatfs和uffs?是否应该着重支持该格式?
分区表为:
"spi-nand": { // Device, The name should be the same with string in image:info:media:type
"size": "128m", // Size of SPI NAND
"partitions": {
"spl": { "size": "1m" },
"env": { "size": "256k" },
"env_r": { "size": "256k" },
"os": { "size": "8m" },
"rodata": { "size": "16m" },
"data": { "size": "16m" }
}
}
将data分区选择为littlefs格式在烧录时会出现以下错误:
Firmware Component:
name: image.target.data
partition: data
attr: mtd;optional
Media: SPI_NAND(2)
[E] mtd_spinand_block_isbad()124 Offset: 0x1000000 is out of mtd size: 0x1000000.
[E] nand_fwc_mtd_write()348 Write block is bad, skip it.
[E] nand_fwc_mtd_write()320 Not enough space to write mtd data
并且RTOS内mount失败。
将data分区选择为uffs格式在编译时会出现以下编译错误:
page_2k_block_128k_oob_64_data.fs file_size: 0x1080000 is over much than part_size: 0x1000000
scons: *** [output/d21x_d213ecv-demo-v4_rt-thread_helloworld/images/d21x.elf] Error 1
data分区只有在格式为fat32时才能成功挂载,此格式在意外关机时容易出现错误,不能用于商用环境中的可读写的文件系统。
开发环境基于Eclipse或VS Code都没有啥问题,这是当前开源且功能丰富的工具的两大主流选择。问题是PlatformIO主要面向萌新,有点能力的老鸟用这个就很不搭调。
这问题其实跟MounRiver差不多,起初MounRiver为了迎合Keil/IAR开发者的口味,对Eclispe大肆裁减,企图将Ecipse改为Keil,这引起Eclipse开发者的不满。
于是就有非社区版和社区版之分,非社区版裁掉了很多Ecipse特色的实用功能就是个开源版的Keil,而社区版则没有功能裁减只是增加了WCH家的插件。
我个人认为,作为一个面向各种资历的开发者的工具,没必要为了迎合新人而降低工具的天花板,把新人的入门引导做好即可。
参照上面SEGGER那个非常有问题的测试对比文章,我用Zig写了类似的测试源码,其中两个测试结果:
$ zig build size --release -Dtarget=arm-freestanding-gnueabi -Dcpu=cortex_m23
text data bss dec hex filename
28828 16 0 28844 70ac /home/chenss/workspace/zig/code_size/zig-out/bin/code_size
$ zig build size --release -Dtarget=riscv32-freestanding-gnueabi -Dcpu=sifive_e34
text data bss dec hex filename
31326 232 0 31558 7b46 /home/chenss/workspace/zig/code_size/zig-out/bin/code_size
跑了多个测试发现RISC-V跟ARM的代码密码其实差不多(Zig对RISC-V32的支持要弱于ARM32)
详情参见这里:https://gitee.com/ufbycd/code_size
这个测试其实有问题,测试代码全部是调用math.h库函数。而不同编译器所用库是不同的,即本质上各个测试用例的测试代码并不全部相同。
不过上述方法有个大问题,因为很多编译相关的宏没有设置,导致工程里的很多符号没有被eclipse识别,这样写代码时很多东西不能自动补全,极不方便。
另一方面,按理来说,重复执行 scons --target=eclipse_sdk 后eclipse应该可以自动更新工程,并且应该不会的编译问题才对。
另一种工程管理方法:用VS Code配合bear(https://github.com/rizsotto/Bear)工具就可以检索复杂工程,用法如下:
* 先安装好bear工具,ArchLinux系统可以直接安装:sudo pacman -S bear
* 配合bear构建工程来生成clang的检索文件compile_commands.json:bear -- scons -j16
* 然后使用VS Code打开工程的文件夹即可,此时所有编译时用到的文件内的符号都能自动补全
* 工程因menuconfig更新后,需要重新执行第2部的bear构建来更新检索文件