Sipeed矽速科技
直播中

范嘉琦

7年用户 98经验值
擅长:可编程逻辑 嵌入式技术
私信 关注
[经验]

【LicheeRV-Nano开发套件试用体验】【更新主贴】高频电力计算终端开发

**申请理由:**目前在公司使用的众多产品中,以传统单片机为核心,高频采集数据,并上传。同时在上产数据时对数据进行清洗和计算,降低服务器负担,增加计算实时性。

**项目计划: **①拿到开发板,进行开箱演示 ②准备开发环境,测试demo ③尝试连接现有产品的传感器组,测试基本开发环境 ④测试与服务器的连通性,测试网络服务 ⑤测试一部分数据筛选算法 ⑥将模块放置到测试展示环境中,演示数据采集和上传功能 ⑦调节修改BUG ⑧上传完整测试内容 ⑨收集整理问题,做一期解答贴 ⑩完成测试

一、和一代对比

LicheeRV Nano是对milkv-duo的二代提升。与此同时,算力芯片也提升了一个台阶。

首先最明显的是1TOPS的NPU。相比一代TPU,功能更全面,同时算力有了较大的性能提升

视频方面,对视频的优化提供了更好地硬件支持

接口方面倒是没啥大的提升。只是增加了一个SD口,同时支持了EMMC。但是SD0和EMMC只能二选一

二代芯片增加了一个ARM主核。但是主核竟然不能和RiscV核同时使用,只能通过引脚二选一。这部分的应用场景不知道在哪里。毕竟硬件固件并不兼容。

二、板卡

1709890732731.png

这次测评厂商送的零件还是比较齐全的。首先网卡和7寸屏都给了。尤其是给了7寸屏。能做很多实验。而且屏幕成本稍高,确实对测评有着不小的帮助。

然而,网卡模块采用FPC软排自己手动焊接的方式。这个方式网卡模块一旦上去,就不好拿下来了。毕竟FPC软排不能被来回折腾。与此同时,固定网卡模块还占用了俩排针位。需要提前将这个位置的排针拔出去。同时还占用了本就不多的插针位置。有点不太友好。

三、上电

这次比之前人性化的地方就是USB供电接口,RNDIS,以及调试串口被集成了。这下可以节省掉令人烦躁的TTL调试口飞线,找USB转串口板子,以及维护这两根线的精力。

Image1.png

主流的一些工具软件确实没有。对于新手影响大

Image2.png

镜像配置了基本的扩容能力。所以可以完整地利用存储卡空间。

四、开发

这代比之前的一大好处是,CV1800B时代的编译链需要自己手动编译。虽然不难。但是这一版本直接由官方提供了工具链,免去了菜鸟刚入门的开发危机。
Image3.png

不过需要注意的是,其实她系统没给准备啥库。一打开就是空空荡荡的系统。所以想要开发啥软件,还是要自己搞一下库的问题。

测个小项目:

注意: 这里一定要用sysroot指定编译根目录。否则它会用编译器默认的库。而这个库和你官方固件对不上。第二就是一定要用CMAKE_C_FLAGS指定平台,否则也对不上库。

cmake_minimum_required(VERSION 3.20)

SET(CMAKE_C_COMPILER /home/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc)

project(CGobjectTest C)

SET(CMAKE_C_FLAGS "-mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d")

SET(CMAKE_SYSROOT "/home/host-tools/gcc/riscv64-linux-musl-x86_64/sysroot")

add_executable(CGobjectTest main.c)
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
  printf("Hello World!\n");

  for (int index=0; index<argc; index++){
    printf("get argv id: %d |info:%s\n", index, argv[index]);
  }

  if (argc >= 3){
    if (strcmp(argv[1], "-a") == 0 && argc >= 3){
      printf("get argv: %s\n", argv[2]);
    }else{
      printf("no params info\n");
    }
  }else{
    printf("not argv\n");
  }

  return 0;
}

输出如下:

# ./CGobjectTest -a 123
Hello World!
get argv id: 0 |info:./CGobjectTest
get argv id: 1 |info:-a
get argv id: 2 |info:123
get argv: 123

回帖(4)

dianzi_0101

2024-3-18 10:15:34
期待更深入的评测
1 举报
  • 范嘉琦: 哈哈哈。我会一直跟进的!测评结束也会在非测评板块跟进。这是我们公司拟发展使用的一个芯片

范嘉琦

2024-3-19 09:50:29
第二篇·基础开发环境


一、开发环境分类注意,虽然是开发程序,但是这块处理器涉及到TPU,且官方的基本环境以及教程没出完,导致这块处理器的开发SDK其实是分化的。分为常规APP开发以及TPU开发。开发TPU可以兼容APP。

其次,这颗处理器刨开RiscV和ARM V53的二选一。它还包含一个700Mhz的大MUC核,一个8051的小MCU小核。
但是很不幸,相比于CV1800B,这颗处理器的协同核开发SDK还没制作完毕。它的freertos大核毫无资料。小核8051也只给了个JTAG接口,甚至和主核的通信资料都没给出。

更离谱的事很多时候它的资料仍然和CV18XX的资料共用。导致很多库和资源现在根本对不上。

二、开发个简单APP首先官方给出了基本APP的sdk,那就是host-tools。

但是,你查过它的例程才会知道,它默认编译选项用的库版本不对!你需要特意选择你对应的核版本。因此你的CFLAGS和CXXFLAGS(注意,大部分库都会同时识别CXXFLAGS和CPPFLAGS,但是个别奇葩库只认CPPFLAGS)
  1. CFLAGS="-mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d"

CFLAGS和CXXFLAGS在这部分的设置是完全一样的。

根据我现在编译的库。大部分库和2024/03/17时候各官方库公布的最新版本是一样的。下载时候记得看看版本公布时间。

官方默认固件使用的gcc host版本为riscv64-unknown-linux-musl

对于部分不认基础环境变量的库,和需要双编译的库需要特殊处理。但是大部分库使用下面代码可以安全编译过去
  1. export TOOLCHAIN=/home/host-tools/gcc/riscv64-linux-musl-x86_64/bin PREFIX=/home/host-tools/gcc/riscv64-linux-musl-x86_64/sysroot/usr TARGET=riscv64-unknown-linux-musl CC=$TOOLCHAIN/riscv64-unknown-linux-musl-gcc CXX=$TOOLCHAIN/riscv64-unknown-linux-musl-g++ NM=$TOOLCHAIN/riscv64-unknown-linux-musl-nm RANLIB=$TOOLCHAIN/riscv64-unknown-linux-musl-ranlib LD=$TOOLCHAIN/riscv64-unknown-linux-musl-ld AR=$TOOLCHAIN/riscv64-unknown-linux-musl-ar STRIP=$TOOLCHAIN/riscv64-unknown-linux-musl-strip CPATH=$PREFIX/include LIBRARY_PATH=$PREFIX/lib CFLAGS="-fPIC --sysroot=$PREFIX/../ -mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d" CXXFLAGS="-fPIC --sysroot=$PREFIX/../ -mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d" CPPFLAGS="-fPIC --sysroot=$PREFIX/../ -mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d" LDFLAGS="-L$PREFIX/lib -Wl,-rpath-link=$PREFIX/lib -mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d" SYSROOT=$PREFIX/../ MAKEFLAGS="-j24"

自己的环境需要做如下改动:
首先TOOLCHAIN是你的编译器基本地址,也就是编译器bin目录所在位置。
第二是PREFIX是你库安装位置。官方给做了sysroot,你直接加紧sysroot按照标准隔离环境做就行。没必要强行分。到时候多版本编译它会自动加入版本尾号的so文件。这是linux应对多版本的一个方法,可以自行查询。
对于MAKEFLAGS来说就是你编译代码用几个线程。和make -j n-j n的作用一样。根据电脑性能自己改。
其他变量都是根据TOOLCHAINPREFIX生成出来的。但是也要注意,因为第一次运行这俩变量其实不存在。因此想要完整加载这些变量,需要将这个export的代码在bash中连续运行2次

本人是在docker中进行的编译。因此清除这些变量最好的办法就是直接退出docker命令行重新进入。对于真机编译的勇士,或者没有条件使用隔离环境的同志。这些设置的所有变量系统默认都是空的。因此对上卖弄每一个设置的环境变量进行unset NAME就能清空这些环境变量。

比如说:
json-c  0.17
glib 2.80.0

由于几乎系统除了json-c库,其实几乎只提供了std库和libc库,所以大部分款本自己开心就好了。
注意,系统镜像额外提供了glib。但是和2.80能兼容

对于绝大多数情况,这点库文件消耗内存不足一提。因此加载自己的独立库系统也可以。但是记住别一个APP加载一套。因为这个处理器的内存实在是太捉襟见肘了。

  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #define LOG_TAG "main"
  8. #include
  9. #include
  10. #include "IdHelper.h"
  11. gboolean callback(gpointer argv){
  12.     log_i("run a task");
  13.     return true;
  14. }
  15. int main(int argc, char* argv[]){
  16. //    setbuf(stdout, NULL);
  17.     elog_init();
  18.     elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
  19.     elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
  20.     elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
  21.     elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
  22.     elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
  23.     elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
  24.     elog_set_text_color_enabled(true);
  25.     elog_start();
  26.     g_print("Hello World glib!\n");
  27.     log_a("Hello EasyLogger Assert!");
  28.     log_e("Hello EasyLogger Error!");
  29.     log_w("Hello EasyLogger Warning!");
  30.     log_i("Hello EasyLogger Info!");
  31.     log_d("Hello EasyLogger Debug!");
  32.     log_v("Hello EasyLogger Verbose!");
  33. //        elog_raw("Hello EasyLogger!");
  34.     // 必须检查g_thread是否能用。否则将无法进入main_loop。新版本glib自动做了这部分呢
  35.     //if (g_thread_supported() == 0){
  36.     //    g_thread_init(NULL);
  37.     //}else{
  38.     //    return EXIT_FAILURE;
  39.     //} // if g_thread_supported
  40.     GMainLoop* loop = g_main_loop_new(NULL, false);
  41.     g_timeout_add(1000, callback, NULL);
  42.     g_main_loop_run(loop);
  43.     GString* test_str = g_string_new("test glib string\n");
  44.     g_string_append(test_str, "test append str\n");
  45.     g_print("test g_print: %s\n", test_str->str);
  46.     g_string_free(test_str, true);
  47.     GString* str = Get_Id_64();
  48.     g_print("create2 nanoid: %s\n", str->str);
  49.     g_print("list size: %04ld\n", Get_64_list_size());
  50.     Clear_64_list();
  51.     g_print("list size: %04ld\n", Get_64_list_size());
  52.   
  53.   for (int index=0; index<1; index++) {
  54.           struct json_object *obj = json_object_new_object();
  55.     json_object_object_add(obj, "name", json_object_new_string("xiaoming"));
  56.     json_object_object_add(obj, "age", json_object_new_int(20));
  57.     log_d(json_object_to_json_string(obj));
  58.     json_object_put(obj);
  59.     sleep(1);
  60.   }
  61.     g_main_loop_unref(loop);
  62.     return EXIT_SUCCESS;
  63. }

上面示例代码太简单了。如果真有零基础的,有问题直接问。留言私信都行!偶尔没看到每次来耍测评区的时候绝对会看到!我的其他测评部分也能看到这部分讲解。

上面用到的库是easyLogger。直接去github搜就能搜到这个库,挺完善维护挺久的一个库。找最大的那个基本上不会被迷惑。
本项目我使用cmake做的。如果想用makefile也没问题。甚至因为涉及到你之前编译过glib,你直接用meson也可以。不过这个系统谷歌私有,用起来确实麻烦,效果大部分系统没感觉像宣传的那么简单。好多环境变量设置做的还不够完善,尤其是对交叉编译不太友好,因为谷歌给他用在了通用组件编译上。其交叉编译系统就像是个补丁一样贴在上面。


举报

范嘉琦

2024-3-26 11:10:24
硬件开发及串口
一、系统架构图
系统架构图

系统架构图

二、基本调度块
本程序暂时没有图形界面。整个程序通过glib库的main_loop调度。
之所以主程序选择C/GLIB的组合,不是因为别的,就是因为这颗芯片只有区区256M的内存和单个跑Linux的核。尽管它明明有俩大核,但是它竟然只能同时跑一个。

同时:因为官方的freertos库和8051的系统接口没做完,导致底层调度任务并没法交给单片机层去调度。只能让linux自己处理了。

当然,这里其实也不是为了测试它最深层次的性能。测试其性能会单独跑一个程序。这里先完成它的主程序。
本文GLIB使用了2.80.0版本。对于旧版本的main_loop是要先检查thread的支持度的。但是新版程序这个功能直接集成到了main_loop创建函数中。所以无需单独处理了。
  1. GMainLoop* loop;
  2. loop = g_main_loop_new(NULL, false);
  3. g_main_loop_run(loop);
整个程序会被阻塞在g_main_loop_run函数中。同时这个函数的阻塞成本非常低,它内部运行了一套独立的的任务管理程序。但是这不是本测评的重点。一般的任务框架放到这个架构中跑是一点都没问题的。
即使是后面要使用TPU处理数据,这里也是通用的。

这里之所以要独立出loop的创建,是为了方便在局部中使用这个变量。否则这个loop标志将无法逃脱主程序的范围。而实际上真正的任务管理是一个单独的类。

这里之所以不用C++类,而是用C的模拟类,还是考虑到它仅有256的内存。

这里不是说自己写的代码就高效,也不是说C就高效。仅仅是在限制应用规模。
  1. __attribute__((constructor)) void Single_Task_Module_Single_Task_Init(){
  2.     g_print("init Single Task\n");
  3.     // 创建一个时间的阻塞对象
  4.     time_task_cond = (GCond*)g_malloc0(sizeof(GCond));
  5.     g_cond_init(time_task_cond);
  6.     time_task_cond_mutex = (GMutex*)g_malloc0(sizeof(GMutex));
  7.     g_mutex_init(time_task_cond_mutex);
  8. }
  9. __attribute__((destructor)) void Single_Task_Module_Single_Task_Deinit(){
  10.     // 释放时间阻塞
  11.     g_cond_clear(time_task_cond);
  12.     g_free(time_task_cond);
  13.     g_mutex_clear(time_task_cond_mutex);
  14.     g_free(time_task_cond_mutex);
  15.     g_print("exit Single Task\n");
  16. }
程序在启动和销毁时总要执行一些处理和释放资源的行为。这里就是在程序被加载时(或者库),自动运行的一些内容

下面一段代码有条件的请在RTC下使用。本处的对齐工作仅仅是仗着大核的性能高:
  1. { // 创建基础的定时任务
  2.         // 打印当前毫秒数
  3.         struct timeval tv;
  4.         gettimeofday(&tv, NULL);
  5.         long time_in_mill = (tv.tv_usec / 1000); // 秒转毫秒 + 微秒转毫秒
  6.         log_i("Current time in milliseconds: %ld\n", time_in_mill);
  7.         int sec = 1010 - time_in_mill;
  8.         g_timeout_add(sec, Time_Task_Callback, NULL);
  9.         //g_mutex_unlock(&time_task_mutex);
  10.     } // 创建基础的定时任务
这里其实在获取一个当前毫秒数。然后去计算任务时差,再去设置下次任务启动时间。
同时这里其实是直接丢弃了启动后的第一秒,就是为了对齐工作。
  1. // 串口管理类
  2. typedef struct _Tag_Uart_Base_Def {
  3.     gboolean inited;
  4.     //GSList* 485_ch_list;
  5.     T_485_Ch_Def* ch_obj;
  6.     /** \brief 创建一个串口连接。
  7.       * 目前仅支持一个串口对象
  8.       * @o_port: 串口号。win和linux不一样
  9.       * @o_speed: 串口速率。大部分情况是定值。一般是B9600或者B115200
  10.       * @o_data_bit: 数据位宽度。一般只有7和8两个选择
  11.       * @o_stop_bit: 停止位宽度。false为1位,true为2位。
  12.       * @o_parity: 校验位。1为奇校验,2位偶校验。其余通通为无校验
  13.       * @return: 返回是否成功。目前校验比较简单
  14.     */
  15.     gboolean (*Init_Port)(struct _Tag_Uart_Base_Def* self,
  16.             GString* o_port, speed_t o_speed,
  17.             tcflag_t o_data_bit, gboolean o_stop_bit,
  18.             tcflag_t o_parity);
  19.     /** \brief发送一个串口数据。
  20.       *警告,data资源会被彻底释放
  21.       * [url=home.php?mod=space&uid=1141835]@Return[/url] 返回实际发送了多少个字符.
  22.       */
  23.     size_t (*Write_Data)(struct _Tag_Uart_Base_Def* self, GString* data);
  24. }Uart_Base_Def;
这个接口旨在统一地管理串口功能,将串口的设置细节屏蔽掉。
  1. // 串口通道管理类
  2. typedef struct _Tag_T_485_Ch_Def{
  3.     int fd;
  4.     GString* port;
  5.     speed_t speed;
  6.     tcflag_t data_bit;
  7.     // 如果为true为双停止位,false为1位停止位
  8.     gboolean stop_bit;
  9.     // 校验位。0为无校验。1为奇校验,2为偶校验。其余都为无校验
  10.     tcflag_t parity;
  11.     struct termios* options;
  12. }T_485_Ch_Def;
这个类对于串口参数进行存储。而真正用来配置的是options







举报

范嘉琦

2024-3-31 21:59:21
自制测试底板

一、底板设计逻辑

首先,任何设计都是有成本的。因此需要合理考虑成本。
本处用一个SD3078的RTC芯片作为GPIO的秒中断输出。也作为一个高精度RTC存在。
然后用三颗3PEAK的TPT7487串口转485芯片
六个TPSMA12AHE3_B/I作为TVS防雷管。
整个板子不大。除了I2C要等长。其他没啥任何要求。485在这个小小的距离产生的长度误差甚至还不如导线的干扰大。再加上引脚粗零件少,整体布线0.553mm(21.8mil)。此时对于这个简单小板板来说,已经不存在什么干扰困难了。
整体覆铜。简单!


这里点名批评一下官方。竟然freerots和8051这两部分的库还没做完就放出来了。直接用大核吧。



二、测试逻辑流程
Image4.png

三、能够测试什么?
首先解释一下这个板子为啥这么画,是因为这整个板子是一个大的测试项目的一部分。上卖面还有别的东西。否则不至于把整个板子上面都空着。

这个板子的大头是上面的tpu,不过哪个不需要额外的接口。直接拉流就行。因为板子里面集成了gstreamer。关于这个库,可以去找我历史的测评记录。曾经测试DPU的时候提到过这个库。

但是毕竟这个板子的数据加速能力是不错的。因此,可以在上面干很多事情。比如说将一些数据通过485拉回来后,做一些运算。毕竟这种芯片计算FFT什么的不在话下。

还可以通过lvgl写点图形界面。毕竟它自带屏幕和触摸。就是它官方的接口设计的过于扭曲,自己弄个支架吧。否则你的屏幕根本没法好好用。它的两根排线都在拧麻花。排线被折坏是迟早的事情。





举报

更多回帖

×
20
完善资料,
赚取积分