linux SDK的构建系统的自动检测ncurses是否安装功能不准确。
Manjaro Linux系统,ncurses被一大堆软件包依赖,肯定是已经安装了的
$ pacman -Q ncurses
ncurses 6.5-4
$ make menuconfig
*** Unable to find the ncurses libraries or the
*** required header files.
*** 'make menuconfig' requires the ncurses libraries.
***
*** Install ncurses (ncurses-devel or libncurses-dev
*** depending on your distribution) and try again.
***
make[1]: *** [Makefile:253:/home/chenss/git/ArtInChip/d211/output/d211_D213ecvDemoV4/build/luban-config/dochecklxdialog] 错误 1
make: *** [package/Makefile.sdk:706:/home/chenss/git/ArtInChip/d211/output/d211_D213ecvDemoV4/build/luban-config/mconf] 错误 2
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构建来更新检索文件
海石生风 说:这个是有的,D13x显控一体。定时器多达26个。
如果是伺服驱动器应用,建议选择M6800系列,SDK是分开的。
那期待工业控制相关外设的文档及SDK能早日开放,我做的产品大多就是都包含屏显和电机控制的。
脚本:/etc/init.d/S20network
#!/bin/sh
#
# Start the network....
#
case "$1" in
start)
printf "Start dhcpc: "
/sbin/ifconfig lo up
/sbin/ifconfig eth0 up
udhcpc &
[ $? = 0 ] && echo "OK" || echo "FAIL"
;;
static)
printf "Set network IP: "
/sbin/ifconfig lo up
/sbin/ifconfig eth0 192.168.1.127 netmask 255.255.255.0 up
/sbin/route add default gw 192.168.1.1
echo -e "nameserver 202.96.134.133\nnameserver 202.96.128.166" > /etc/resolv.conf
[ $? = 0 ] && echo "OK" || echo "FAIL"
;;
stop)
printf "Bring down network: "
killall udhcpc
ifconfig lo down
ifconfig eth0 down
[ $? = 0 ] && echo "OK" || echo "FAIL"
;;
restart|reload)
"$0" stop
"$0" start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit $?
参考这个帖子 https://whycan.com/t_10597.html,解决DNS污染。实测github的clone速率为170.00 KiB/s左右,还可以接受。
@海石生风
元组是在编译时确定的,那怎么实现的状态diff和更新呢?还有UI元素的增减怎么实现的?比如根据条件显示一个label与否
zig只在编译时支持泛型和反射,所以只能在编译时确定声明。控件一般来说对用户是隔离的,用户通常只操作模型;控件状态的更新来源于与其绑定的模型。
需要UI元素增减的场景是List和TableView控件吧, 这种控件会绑定一个模型数组,用户对表内的UI元素进行声明,UI框架会在编译时依据声明来生成一个用于构建UI元素的函数,模型有变化时就调用这个构建函数。
目前这还是一个构思,这种操作能否实现,还有待研究;毕竟zig这个编译时特性在众多编程言语中是绝无仅有的。
这三个项目整理好了:将lvgl的C源码直接放入到zlvgl项目内,zlvgl和zdec两个项目分开管理而不使用git submodule。工程已经分别上传到gitee和github:
https://gitee.com/ufbycd/zlvgl
https://gitee.com/ufbycd/zdec
https://github.com/ufbycd/zlvgl
https://github.com/ufbycd/zdec
lvgl的意思是lv_obj_add_event_cb就是列表里加个函数指针 根本不会 fail 不过列表如果静态的会不会占好大地方 如果动态满了怎么办
lv_obj_add_event_cb内部会有动态内存分配,只有一个fail原因,那就是内存分配失败。
后面看了不少C代码,lvgl对于内存分配失败都是只用LV_ASSERT_MALLOC触发断言而没有在返回值上体现来处理的,跟我之前所用的UI库处理习惯上有点不同。
而zig这边因为有完善的错误处理机制,一个函数返回OOM(Out Of Memory)错误是很常见的。zig提倡由调用者决定什么时候处理错误,这就可以让软件更健壮,不会一触发OOM就因ASSERT断言而死掉。
海石生风 说:现在都流行提问说一半不说一半让大家猜谜吗?
atof("000000003.1")的结果为0
你单个写个C源码测试,结果是正确的吧。是你实际调用有问题,不是atof("000000003.1")问题。
提问最好要把怎么使用的细节列出来。不要一开始就怀疑一个很多人在用且用了很多年的C库有问题。
#include <stdio.h>
#include <stdlib.h>
int main()
{
double d = atof("000000003.1");
printf("d = %f\n", d);
return 0;
}
PS:标准的C库atof返回的是一个double,printf的"%f"接受的是一个double,其他C库特别是MCU平台的C库要另行确认是否有不同。
借鉴Flutter,不使用编程语言之外的标记语言而是使用编程语言本身来实现UI描述。这就要求编程语言支持泛型和反射特性。目前支持这两个特性又可以用于MCU平台的语言就只有Rust跟Zig了,但Rust太复杂了用在MCU平台大材小用,故选择Zig。Zig惟一的问题是目前还远没达到1.0版本,但当前v0.11版本的实用性也不错了。
项目命名为"zdec",“z”取自Zig,“dec”取自英文“声明”的前三个字母;而在汇编语言上一般用“dec”指令表示减法,所以"dec"也有在UI实现上做减法从而方便使用的意思。
目前已初步实现大体框架,实现了控件的创建/属性初始化和命令/属性的绑定。
UI构建方式如下:
const main_ui = .{
.{
d.Id.Button,
d.Size{ .width = 160, .height = 48 },
d.Align{ .lv_align = .Center, .y_ofs = -100 },
d.Text{ .text = "button" },
struct {
user_data: *Model,
pub fn onClicked(event: anytype) void {
const the_model = event.userData();
const step = 10;
std.debug.print("{s}: add Model.count by {d}\n", .{ @typeName(@TypeOf(event.target())), step });
the_model.add(step);
}
}{ .user_data = &_model },
},
.{
d.Id.Slider,
d.Size{ .width = 240, .height = 16 },
d.Align{ .lv_align = .Center, .y_ofs = 100 },
d.Range{ .min = 0, .max = 200 },
d.Bind(d.BindType.Value, @TypeOf(_model.count)){ .property = &_model.count },
},
};
var widget = try d.buildUI(lv.Screen.active(), main_ui);
效果是这样的:点击button,Slider的游标就会变化
上述例子的完整代码在这里:https://gitee.com/ufbycd/zdec/blob/dev/zdec/example.zig
项目主页在这里:https://gitee.com/ufbycd/zdec
目前只支持Linux,并使用SDL2作为后端来显示窗口。
海石生风 说:我以为你是说按Tab键滑动,理解错了。
让Table控件接收KEY事件,在事件处理函数里调用API滑动就行了。没理解是什么意思
事件处理函数就是丢给lv_obj_add_event_cb的那个回调函数,你的用法不太对,改成如下:
lv_obj_add_event_cb(DataStream_table_1, DataStream_table_1_event_handler, LV_EVENT_KEY, DataStream_table_1);
// 那么DataStream_table_1就存储在lv_event_t的user_data里,就可以在回调函数里取出来使用:
void DataStream_table_1_event_handler(lv_event_t * e) {
lv_table_t *table = e->user_data;
// 使用table来调用Table的API来滑动
}
我最近启动了一个项目:实现一个应用于嵌入式的声明式UI框架: https://gitee.com/ufbycd/zdec
用Zig语言编写,底层基于LVGL进行绘图。我用过emWin、AWTK、QT(widget/qml/pyqt)、 wxWidgets,但我之前对LVGL只是耳闻并在PC上跑了下Demo,实际项目从来没用过。
在实现命令绑定时发现LVGL的事件处理函数的添加和删除的API好丑呀。
lv_obj_add_event_cb的返回值在V8版本已经标记为遗弃。而在V9版本则没有返回值了,API调用到底是成功还是失败,用户不知道!太不严谨了。
并且不能通过lv_obj_add_event_cb返回的东西来删除处理函数了。这对于实现一个声明式UI框架很不友好!
AWTK相应的API是这样的:
uint32_t widget_on(widget_t* widget, uint32_t type, event_func_t on_event, void* ctx);
ret_t widget_off(widget_t* widget, uint32_t id);
用法是这样的:
// 添加
uint32_t event_id = widget_on(widget, EVT_CLICK, callback, ctx);
if(event_id == TK_INVALID_ID) {
// 失败处理
}
// 删除
widget_off(widget, event_id);
LVGL的版本号都这么大了,大家都没有意见吗?我打算到github上提下意见。
另外,将来空闲时也会写点这个Zig项目的心得。
RT-Thread团队在Smart内核上对接了Linux的DRM (Direct Rendering Manager)并移植了wayland从而可以在RT-Thread通过DRM后端来跑weston合成器(显示服务器)。将来在RT-Thread上移植GUI将非常简单容易了!
详情参见官方新闻: https://www.rt-thread.org/newsDetail.html?id=cb5491b3c0088c80
@EE
起初是裸机,由于对可靠性要求极高,于是最起码要对不同任务进行内存地址隔离。隔离后,不同任务间要通信,就是要做通信组件;还要监控不同任务是否正常运行,如果异常就要做最小损失处理。还要对整个任务系统进行备份(我国当前的航天器已经做3套备份了)。
这些东西做下来,就不由自主地变成一个OS了。
顺便一提,经常飞越星际的VxWorks已经支持“容器”这个虚拟系统的概念了,将实时性要求不高又容易出错的任务跑在容器里,以隔离其对整个系统的影响。
航空航天的可靠性要求不是民用的能比的,而航空航天系统又越来越复杂,肯定要对硬件资源进行管理,这一管理就变成一个OS了。
这不是为了OS而上OS,而是无奈地成为了OS。
在vectos_stm32f103xb.c里有定义向量表__isr_ectors[],在sections.ld里把向量表定义在flash 0x08000000位置,stm32上电后从这里运行即可。startup.s是st库里常用写法,但不是唯一方法
本质不单单是中断向量表,而是启动代码,即执行main函数之前的初始化.DATA段和.BSS段的代码是用C语言写的。
之所以能用C语言实现启动代码,是因为cortex-m的中断向量表的前4个字节的值为栈指针位置。MCU启动时先从中断向量表获取栈指针位置,确定了栈位置C语言的运行环境就初始化OK了,就可以调用C函数了。
至于中断向量表的定位是通过链接脚本指定的:
先在C语言里指定中断向量表这个数组所处的段(section)
__attribute__ ((section(".isr_vector"),used))
pHandler __isr_vectors[] =
{
(pHandler) &_estack, // The initial stack pointer
Reset_Handler, // The reset handler
...
};
再在链接脚本里指明这个段的链接位置:
MEMORY
{
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
}
SECTIONS
{
/*
* For Cortex-M devices, the beginning of the startup code is stored in
* the .isr_vector section, which goes to FLASH.
*/
.isr_vector : ALIGN(4)
{
FILL(0xFF)
KEEP(*(.isr_vector)) /* Interrupt vectors */
} >FLASH
链接脚本里的内容是按出现的先后顺序排列的,isr_vector 写在最前所以就链接在FLASH起始位置。
@lfs911
见 https://whycan.com/t_10374.html 这里有运行日志
Startup time: 0.456 sec
这是跑的rtthread的demo
程序是是在哪跑的,Flash、SRAM还是PSRAM?
http://science.china.com.cn/2023-11/24/content_42608233.htm
今天看到这个消息,来转发一下。
hp5301, 8元,300M,没can,16位adc,开发板40元。看了下sdk,gitee上有,rtos开发。
顶一下,HPM5300系列芯片有针对电机控制的运动控制系统,非常强大。如:
旋变解调器:根据电机的两路线圈的电流信号评估转子位置
支持串行口编码器;如 磁编码器MT6701的SPI接口,配置好寄存器后无需CPU干预就能定时获取编码器数值
运动处理单元:根据输入的电机位置值来预测电机的位置、速度、加速度
其它还有强点还有:双精度FPU、64位定时计数器、纳秒计数器、32位编码器计数器、编码器输出、CAN FD、运算放大器OPAMP、两个16位ADC等就不一一列举了,感觉兴趣的同学可以看芯片手册深入了解。
海石生风 说:tomyqg 说:@海石生风
听说c906的浮点也是个残废啊哪里道听途说的,RVV + FPU 的浮点性能怕是跟ARM9比得差两个量级
我看了下,大意是说C910的FPU少了个寄存器标志位,没有完全兼容IEE-754。
但说它“残废”,就等于说它没什么用处,就是谣言了。
音频功放驱动实现步进电机的加减速及正反转控制,视频请移步B站:https://www.bilibili.com/video/BV1nj411D754/
生成wav波形文件的python脚本如下:
#!/usr/bin/env python
import numpy as np
import scipy.signal as signal
import wave
freq_min = 10
freq_max = 50
shifting_seconds = 0.5 # 加速或减速时长
con_seconds = 1.5 # 匀速时长
SAMPLE_RATE = 48000 # 采样率
def genwave(f0, f1, seconds, phase_diff=90):
t = np.arange(0, seconds, 1.0/SAMPLE_RATE)
vleft = signal.chirp(t, f0=f0, f1=f1, t1=seconds, method='linear')
vright = signal.chirp(t, f0=f0, f1=f1, t1=seconds, method='linear', phi=phase_diff)
left_scale = vleft * 32000
right_scale = vright * 32000
left_data = left_scale.astype(np.int16)
right_data = right_scale.astype(np.int16)
cat_data = np.array([left_data, right_data])
frame_data = np.transpose(cat_data)
return frame_data
def genaction(f_min, f_max, shifting_t, con_t, is_forward=True):
phase = 90 if is_forward else -90
acc = genwave(f_min, f_max, shifting_t, phase)
con = genwave(f_max, f_max, con_t, phase)
dec = genwave(f_max, f_min, shifting_t, phase)
action = np.concatenate((acc, con, dec))
return action
def genround(f_min, f_max, shifting_t, con_t):
forward = genaction(f_min, f_max, shifting_t, con_t, True)
backward = genaction(f_min, f_max, shifting_t, con_t, False)
return np.concatenate((forward, backward))
# frames = genaction(freq_min, freq_max, shifting_seconds, con_seconds, False)
frames = genround(freq_min, freq_max, shifting_seconds, con_seconds)
seconds = shifting_seconds*2 + con_seconds
fname = "{}~{}Hz-{}s.wav".format(freq_min, freq_max, seconds)
wf = wave.open(fname, "wb")
wf.setnchannels(2) # 2声道
wf.setsampwidth(2) # 采样宽(字节)
wf.setframerate(SAMPLE_RATE)
wf.setcomptype('NONE','not compressed') # 设置采样格式:无压缩
wf.writeframes(frames.tobytes())
wf.close()
print("generated: {}".format(fname))
优化双声道数据的合并
#!/usr/bin/env python
import numpy as np
import scipy.signal as signal
import wave
freq0 = 10 # 信号起始频率(Hz)
freq1 = 50 # 信号终止频率(Hz)
seconds = 20 # 音频时长(秒)
sample_rate = 4800 # 采样率
t = np.arange(0, seconds, 1.0/sample_rate)
vleft = signal.chirp(t, f0=freq0, f1=freq1, t1=seconds, method='linear')
vright = signal.chirp(t, f0=freq0, f1=freq1, t1=seconds, method='linear', phi=90)
# import matplotlib.pyplot as plt
# plt.plot(t, vleft)
# plt.plot(t, vright)
# plt.show()
# exit()
left_scale = vleft * 32000
right_scale = vright * 32000
left_data = left_scale.astype(np.int16)
right_data = right_scale.astype(np.int16)
cat_data = np.array([left_data, right_data])
frame_data = np.transpose(cat_data)
fname = "{}~{}Hz-{}s.wav".format(freq0, freq1, seconds)
wf = wave.open(fname, "wb")
wf.setnchannels(2) # 2声道
wf.setsampwidth(2) # 采样宽(字节)
wf.setframerate(sample_rate)
wf.setcomptype('NONE','not compressed') # 设置采样格式:无压缩
wf.writeframes(frame_data.tobytes())
wf.close()
print("generated: {}".format(fname))
突发奇想,D类音频功放是用两个H桥来驱动两线圈,电磁驱动原理跟步进电机驱动是一样的。那么能否用D类音频功放直接驱动步进电机呢?
只要在左右声道分别输出相位差是90度的正弦波应该就可以驱动电机了。于是说干就干,立马淘了TPA3116D2功放板来实验。
音频功放确实能够驱动步进电机。但经测试发现驱动特性比较一般,抖动挺大,信号频率到100Hz左右电机就转不动。
视频可移步B站:https://www.bilibili.com/video/BV1MW4y1Q7kP/
用于生成驱动信号的wav文件的python如下,感兴趣的同学也可以尝试下。
#!/usr/bin/env python
import numpy as np
import scipy.signal as signal
import wave
from array import array
freq0 = 20 # 信号起始频率(Hz)
freq1 = 20 # 信号终止频率(Hz)
seconds = 20 # 音频时长(秒)
sample_rate = 4800 # 采样率
t = np.arange(0, seconds, 1.0/sample_rate)
vleft = signal.chirp(t, f0=freq0, f1=freq1, t1=seconds, method='linear')
vright = signal.chirp(t, f0=freq0, f1=freq1, t1=seconds, method='linear', phi=90)
# import matplotlib.pyplot as plt
# plt.plot(t, vleft)
# plt.plot(t, vright)
# plt.show()
# exit()
left_scale = vleft * 32000
right_scale = vright * 32000
left_data = left_scale.astype(np.int16)
right_data = right_scale.astype(np.int16)
frame_data = array('h')
for i in range(len(left_data)):
frame_data.append(left_data[i])
frame_data.append(right_data[i])
fname = "{}~{}Hz-{}s.wav".format(freq0, freq1, seconds)
wf = wave.open(fname, "wb")
wf.setnchannels(2) # 2声道
wf.setsampwidth(2) # 采样宽(字节)
wf.setframerate(sample_rate)
wf.setcomptype('NONE','not compressed') # 设置采样格式:无压缩
wf.writeframes(frame_data.tobytes())
wf.close()
print("generated: {}".format(fname))
海石生风 说:发现 灵动 的方案还有带预驱和MOSFET管的,集成5v LDO最高16V供电,完全单芯片解决方案,电路极其简洁!一个MCU加几个阻容器件搞定BLDC FOC!
有没有型号呢 我去看看
https://mindmotion.com.cn/products/mm32mcu/mm32spin/mm32spin_driver_mcu/
三个型号都带MOSFET管和高压LDO,只有MM32SPIN422C带运放。
slint发展得挺快的,昨天即4月3号就在官网“announce version 1.0”。个人比较看好这个UI库,基于rust还是很有前途的:C太简单,C++又太复杂。但愿嵌入式领域能有个趁手GUI。
发布详情见这里: https://slint-ui.com/blog/announcing-slint-1.0.html
120通道逻辑分析仪
https://github.com/gusmanb/logicanalyzer
树莓派单片机24通道100Msps逻辑分析仪提升至支持120通道。
支持了菊花链方式级联五个设备,从而允许捕获120个通道。包括渲染引擎的性能改进和可见性改进、屏幕中的更多样本、自动选择捕获模式、编辑功能等。
摘自:硬汉嵌入式 https://www.bilibili.com/read/cv22245829?spm_id_from=333.999.0.0 出处:bilibili
有满足我需求的型号,感谢回复。
Could someone upload 212 SDK to some git, etc? For uses outside China it's not possible to register on this comake site.
Thanks
You can contact them right here: https://www.sigmastar.com.cn/index.php?p=contact&lanmu=6
突然发现github的littlevgl找不到了,是发生了什么事情么?
就是这个链接https://github.com/littlevgl/
成立LVGL组织了 https://github.com/lvgl/lvgl