国产自研RISC-VMCU哪家强?我们让工程师评测了几款

原创 电子工程世界 2023-10-24 09:01
提到国产开发板,就不得不提到RISC-V,作为开源指令集,它被视为国产自研的突破口,除此之外,它简单、开源、易移植、模块化、经济、稳定,因此备受青睐。
 
近年来,国产厂商纷纷推出RISC-V指令集的开发板,从高端到高性价比,业界呈现百花齐放的态势。
 
仅仅用文字来表达,也略显苍白无力,那么国产自研 RISC-V MCU 究竟有多强?跟着我一起看看工程师们的评测吧。
 
本文包括三种开发板的评测——包括赛昉科技的高性能RISC-V单板计算机、先楫高性能RISC-V MCU、沁恒高性价比RISC-V MCU。
电子工程世界(ID:EEWorldbbs)丨出品

 国产RISC-V Linux板 昉·星光VisionFive 


原贴地址:http://bbs.eeworld.com.cn/elecplay/content/ba84beb2

开箱
对赛昉科技的这块国产RISC-V Linux板 昉·星光VisionFive早有耳闻,号称首个高性能RISC-V单板计算机,一直心心念念想要把玩;这次电子工程师和赛昉科技开展试用活动,第一时间就报了名,没想到运气这么好,选两个人就中了,真的是运气爆棚了。得知中选后,确认完地址,就期盼着早日收到板子。端午节前,板子如约送达,做为端午礼物,再好不过了。
 
打开快递包括,一个小巧的盒子引入眼帘:


打开包装盒后,里面是一个简洁的塑料盒,板子就躺在里面:

 
这块板子不大,但是接口非常的多,毕竟要对得起下面的这个官方宣传:

通过官方文档,可以了解所具有的配置接口:
 
为了方便开发者,特别提供了一个40针的接口:


查阅官方文档,可以了解上面这40针的具体定义:



这40针接口对于开发者了来说,是非常非常有用非常非常方便的,后续将会给大家分享40针的相关使用,包括GPIO 点灯、I2C读取温湿度数据等:


 

GPIO开发基础:从原理到实战

昉·星光VisionFive开发板上,提供了40Pin IO口,可以供我们在实际开发中使用。


这些IO口的具体功能定义,可以通过官方的资料了解:


在Linux系统中,GPIO驱动启用后,对应的挂载点在/sys/class/gpio
这里需要说明一下,在Linux系统上,对于常规的文件,用文件路径来访问文件,这个很好理解。

同样的,对于内外设备,Linux系统上,也把这些各种设备,当成特殊形式的文件来访问。

例如,要查看cpu的信息,那么cat /proc/cpuinfo即可。

而GPIO设备的话,其挂载点就是/sys/class/gpio。
 
通过官方40Pin的详细文档,我们可以了解每个引脚具体的挂载点:


从上图中,我们可以看到,GPIO0引脚,其对应的sys为448,那么,在系统中,其对应的挂载点,就是 /sys/class/gpio/gpio448 依次类推,我们可以得到,GPIO2引脚,其对应的sys为450,那么其挂载点,就是/sys/class/gpio/gpio450。
 
但是在Linux系统中,具体GPIO的引脚,可能不会开机自动挂载,需要我们先激活,才能使用。

要激活具体的GPIO,也需要使用到一个特殊文件,那就是 /sys/class/gpio/export如果要激活GPIO1,也就是/sys/class/gpio/gpio448,我们只需要执行下面的命令,即可激活:

  • echo 448 > /sys/class/gpio/export

该命令相当于告诉 /sys/class/gpio/export,请帮我激活gpio448

执行完该命令后,ls -l /sys/class/gpio,就可以看到gpio448存在了。

提醒:上述命令,需要在root权限下执行,否则执行时会提示没有权限。
 
激活GPIO0引脚后,我们还需要设置该引脚的功能,是输入,还是输出,那么使用下面的命令,操作/sys/class/gpio/gpio448/direction这个特殊文件即可:如果是输出,也就是要输出高低电平,例如点亮LED,就使用:

  • echo out > /sys/class/gpio/gpio448/direction

上面的命令,就相当于告诉 /sys/class/gpio/gpio448/direction ,我要把你gpio448设置为out,也就是输出了。
 
如果是输入,例如接按键,获取按键状态,就使用:

  • echo in > /sys/class/gpio/gpio448/direction

上面的命令,就相当于告诉 /sys/class/gpio/gpio448/direction ,我要把你gpio448设置为in,也就是输入了。
 
设置好了GPIO0对应的功能后,我们就能具体使用了。

这个时侯,我们又要使用到 /sys/class/gpio/gpio448/value 这个特殊文件了。

例如,如果设置好输出,已经在GPIO0上接好了LED,现在要点亮LED了,就执行:

  • echo 1 > /sys/class/gpio/gpio448/value

这样就表示输出高电平
 
如果要熄灭对应的LED,就执行:

  • echo 0 > /sys/class/gpio/gpio448/value

这样就表示输出低电平了
 
如果设置好输入,在GPIO0上接好了普通按键,现在要获取按键输入状态,就执行:

  • cat /sys/class/gpio/gpio448/value

那么,显示1,表示按键按下;显示0,则表示按键松开。
 
在上面的讲解中,我们一共用到了下面的文件:
  • /sys/class/gpio/export:通知激活GPIO引脚对应的sys挂载点
  • /sys/class/gpio/gpio448/direction:通知该GPIO是输入in还是输出out
  • /sys/class/gpio/gpio448/value:输出或者输入高低电平

我们对这几个特殊文件的操作,也就是使用了基础的echo、cat指令,这样的指令,对任何一个普通文件,也可以操作。

所以本质上,对Linux而言,普通文件是文件,而这些GPIO挂载点也是文件,只不过,具体的功能有所不同罢了。
 
那么,如果你还懂一点bash脚本,会循环的,我命而已通过下面的方式,来闪烁GPIO0上连接的LED,具体指令如下:

  • # 激活GPIO0 - 448
  • echo 448 > /sys/class/gpio/export

  • # 设置GPIO0 - 448 为输出
  • echo out > /sys/class/gpio/gpio448/direction

  • # 循环10次:点亮LED,延时1秒,在关闭LED,再延时1秒
  • for i in 1 2 3 4 5 6 7 8 9 10
  • do
  • echo 1 /sys/class/gpio/gpio448/value
  • sleep 1
  • echo 0 /sys/class/gpio/gpio448/value
  • sleep 1
  • done

如果已经连接好LED到GPIO0,那么执行后,就能看到实际效果了。
 
在上面的讲解中,我们在bash环境下,用echo来写入数据到对应的GPIO对应的文件中,从而操控LED引脚。

那么,到了C语言中,我们要如何操作呢?

实际上,也非常简单,你就把他们当作普通的文件,打开,然后写入或者读取内容就好了。

以下为一段通过GPIO0来闪烁LED的代码,基本功能和上面演示的bash脚本闪烁LED类似:

  • #include
  • #include
  • #include
  • #include
  • #include //define O_WRONLY and O_RDONLY

  • //芯片复位引脚: P1_16
  • #define SYSFS_GPIO_EXPORT "/sys/class/gpio/export"
  • #define SYSFS_GPIO_RST_PIN_VAL "448"
  • #define SYSFS_GPIO_RST_DIR "/sys/class/gpio/gpio448/direction"
  • #define SYSFS_GPIO_RST_DIR_VAL "OUT"
  • #define SYSFS_GPIO_RST_VAL "/sys/class/gpio/gpio448/value"
  • #define SYSFS_GPIO_RST_VAL_H "1"
  • #define SYSFS_GPIO_RST_VAL_L "0"

  • int main()
  • {
  • int fd;

  • //打开端口/sys/class/gpio# echo 448 > export
  • fd = open(SYSFS_GPIO_EXPORT, O_WRONLY);
  • if(fd == -1)
  • {
  • printf("ERR: Radio hard reset pin open error.\n");
  • return EXIT_FAILURE;
  • }
  • write(fd, SYSFS_GPIO_RST_PIN_VAL ,sizeof(SYSFS_GPIO_RST_PIN_VAL));
  • close(fd);

  • //设置端口方向/sys/class/gpio/gpio448# echo out > direction
  • fd = open(SYSFS_GPIO_RST_DIR, O_WRONLY);
  • if(fd == -1)
  • {
  • printf("ERR: Radio hard reset pin direction open error.\n");
  • return EXIT_FAILURE;
  • }
  • write(fd, SYSFS_GPIO_RST_DIR_VAL, sizeof(SYSFS_GPIO_RST_DIR_VAL));
  • close(fd);

  • //输出复位信号: 拉高>100ns
  • fd = open(SYSFS_GPIO_RST_VAL, O_RDWR);
  • if(fd == -1)
  • {
  • printf("ERR: Radio hard reset pin value open error.\n");
  • return EXIT_FAILURE;
  • }
  • while(1)
  • {
  • write(fd, SYSFS_GPIO_RST_VAL_H, sizeof(SYSFS_GPIO_RST_VAL_H));
  • usleep(1000000);
  • write(fd, SYSFS_GPIO_RST_VAL_L, sizeof(SYSFS_GPIO_RST_VAL_L));
  • usleep(1000000);
  • }
  • close(fd);

  • printf("INFO: Radio hard reset pin value open error.\n");
  • return 0;

  • }

上面C代码的步骤,和我们在bash脚本中操作的步骤一致,都是操作前面说的三个特殊GPIO挂载点对应的文件。
  • 首先,是open了/sys/class/gpio/export,然后写入448,表示要激活GPIO0对应的sys挂载点
  • 其次,是open了/sys/class/gpio/gpio448/direction,写入了OUT,表示设置GPIO0为输入
  • 然后,是open了/sys/class/gpio/gpio448/value,准备循环写入0和1,来点亮和熄灭LED
  • 最后,是循环;再循环中,先写入1,点亮LED,然后延时1秒(usleep 1000000微秒),再写入0,熄灭LED,再延时1秒

编译该c代码,并执行,就能看到和之前bash脚本执行,同样的结果了。
 
通过上面的讲解,我们能够学习到,如果控制GPIO0,同样的方法,你也可以控制GPIO2、GPIO4等所有你能控制的GPIO,他们无非是不同的文件而已。

不管是在bash脚本中,还是在C语言、C++语言,或者是Python中,都可以按照同样的操作文件的方式来进行操作即可。

C语言编程控制WS2812B炫彩灯珠
这次试用的这块RISC-V单板计算机上,为我们提供了40Pin接口,其中就有SPI接口。

今天要分享的,就是在Debian下,使用SPI来驱动ws2812B炫彩灯珠。
 
WS2812B可是个好东西,应用的场合非常之多:


节假日,很多商场、娱乐场所、小区等,都安装了这种炫彩灯带装点节日的气氛。
 
除了条带状的,下面这种点阵的也很常用:


商家可以在这种点阵上,输出图案或者文字来做宣传
作为玩板子的宅男,在2月14日或者农历7月7日,给心爱的她做一个心形的炫彩灯板,一定能打动她。
 
此外,有不少开发板,也提供了一颗WS2812B灯珠,例如ESP32-C3上,比常见的LED可控性强多了。

 
WS2812B可以用于单颗、点阵、条带等,根据实际需要,可以任意定制。
因为,它是由一颗颗小灯珠给连起来的,可以用柔性材料给保护起来,防水防潮。
用得少的,可以单颗使用,用得多的,也可以上千颗使用,连连连就成。
今天的分享,咱们先不玩复杂的,先驱动WS2812B灯板上的第一颗。
 
第一步,我们要参考官方文档,启用SPI以便可以在后面的编程中使用:


成功启用后,登录系统,可以查看该设备的挂载点:


然后设置,需要设置其可被普通用户使用,否则可能遇到权限问题:

单颗WS2812B灯珠需要的电流不是很大,但是,后续可能要控制整个板子,那就需要不小的电流了。

40Pin虽然可以输出5V,但是其电流非常有限。所以,请使用单独的5V供电,不要使用40Pin的接口,避免产生副作用。

在我的实际使用中,我用了一个USB2TTL,把其中的5V用于给WS2812B供电。


 
参考官方中关于40Pin的说明,具体的连线如下:


仔细连好线,别连错了:


准备妥当,现在可以开始使用c语言,对spidev进行编程,从而通过spi控制WS2812B灯珠了。

查看了网上提供的WS2812B资料,可以了解到,要控制WS2812B的灯珠,需要让灯珠控制器收到24bits的数据,每8Bits为一组,分别用于控制GRB三种颜色。

但是控制设备发送数据的时候,确需要24bytes的数据,具体如下:

在上面的数据中,每24bytes数据,我分成了3行。
每一行,WS2812B自身的控制器收到后,会将每byte解析为0或者1,以上0x80解析为0,0xf8解析为1。
如果第一行,全部为0xf8,则最终为11111111,表示G的亮度为0xff,也就是255。
如果第一行,全部为0x80,则最终为00000000,表示G的亮度为0x00,也就是0,熄灭。
以上的0x80和0xf8为经验数值,大家可以查看网上的资料了解。
 
除了发送的数据,SPI还需要设置发送的速度,经验值为6.4MHz,也就是6400000。

通过学习Linux下spidev的编程,最终的代码如下:


上面的代码,看起来不少,重点不多:
  1. 把挂载点/dev/spi0.0打开,当作文件打开即可
  2. 设置各项参数,特别是速率
  3. 发送需要的数据
循环部分,就是交替发送并解码为[0,0,0]和[1,0,0]来控制第一颗灯珠GRB,从而形成亮灭效果了。

在上面定义的发送数据中:

前面说了,可以通过设置0x80和0xf8,来控制G的亮度。

同样的,还可以设置第二行、第三行,来控制GRB三中颜色的力度,从而达到炫彩的目的。

具体的颜色数目:255 * 255 * 255 = 16581375,160多万,真得很炫彩。
以上展示的,是控制第一颗灯珠,需要24bytes的数据;而发送2组24bytes数据,就可以控制两颗;以此类推,要控制更多的,就发送24倍数组bytes了。

星光板上的nodejs开发

在官方提供的资料中,有专门说明nodejs应用的:


nodejs是一门非常简单易学易用的语言,因为其核心就是javascript,通过强大的npm扩展,让nodejs拥有无限的扩展性。

这次先做一个入门分享,用nodejs点灯。
 
首先通过ssh,或者串口终端,登录到开发板。
然后新建一个nodejs测试目录:
mkdir -p ~/projects/nodetest
cd ~/projects/nodetest
 
后续的代码,就在这个目录下面编写了。

通过官方手册,我们可以了解到这个板子上面40Pin的详细信息:

GPIO0比较方便使用 ,我们就用这个引脚。在之前 的文章中,我讲过448的含义,在系统 调用的时候,就是gpio448。
 
然后,进过了解,可以使用nodejs的onoff模块,来直接操作gpio,所以先安装onoff模块。
mkdir node_modules
npm install onoff 
先建立node_modules目录的目的,是为了让npm把模块安装在 当前目录下,不对当前用户的环境产生影响。

然后,经过阅读onoff的说明,编写了 如下的nodejs程序:
  • const Gpio = require('onoff').Gpio;
  • const led = new Gpio(448, 'out');
  • let stopBlinking = false;
  • let led_status = 0;


  • const blinkLed = _ => {
  • if (stopBlinking) {
  • return led.unexport();
  • }

  • led.read((err, value) => { // Asynchronous read
  • if (err) {
  • throw err;
  • }

  • led_status = led_status ? 0 : 1;
  • led.write(led_status, err => { // Asynchronous write
  • if (err) {
  • throw err;
  • }
  • });
  • });

  • setTimeout(blinkLed, 1000);
  • };

  • blinkLed();


  • setTimeout(_ => stopBlinking = true, 10000);
 
上述代码的基本逻辑说明:
  1. 引用了 onoff模块
  2. 定义了GPIO 448引脚控制LED
  3. 设置了退出变量和LED状态变量
  4. blinkLed为控制LED的主函数
    1. 将LED状态置反
    2. 输出LED状态
    3. 检查是否停止,是的话就停止继续执行
    4. 读取LED信息
    5. 定时1秒继续执行
  5. 运行blinkLed
  6. 定时10秒后,设置停止状态
将LED连接到GIO0:


然后用nodejs运行上述代码:
sudo node blink.js
 
因为操作gpio需要root权限,所以用sudo执行。
执行后,可以看到 LED每秒闪动一次,10秒后不再继续 闪动。

星光板上的nodejs开发2:web服务器

再上一篇文章中,分享了星光板上基础的nodejs开发,实现了用nodejs控制LED闪烁。

这一次,再分享,在星光板上建立一个 基础的web服务,并通过web服务,来控制LED。
 
首先,nodejs自带了http模块,通过该模块,就能建立http服务端,对外提供web服务;也可以建立http客户端,获取其他web服务器提供的数据。

通过查看nodejs官方http的文档:http 超文本传输协议 | Node.js API 文档 (nodejs.cn)了结了 nodejs的http的开发资料,并编写如下的程序:
  • var http = require('http');
  • var fs = require('fs');
  • var url = require('url');


  • // 创建服务器
  • http.createServer( function (request, response) {
  • // 解析请求,包括文件名
  • var pathname = url.parse(request.url).pathname;

  • // 输出请求的文件名
  • console.log("Request for " + pathname + " received.");

  • response.writeHead(404, {'Content-Type': 'text/html'});
  • response.write(pathname);
  • response.end();

  • }).listen(8080);

  • // 控制台会输出以下信息
  • console.log('Server running at http://*.*.*.*:8080/');
 
将上述代码保存为web_server.js,然后使用nodejs执行:


执行后,显示 “Server running at http://*.*.*.*:8080/” 表示可以访问了。 
然后访问如下的网址:(IP请根据自己开发板实际获取的设定)


可以看到,该web server能够收到我们的请求。
然后,我们可以结合之前nodejs控制LED的分享,实现通过网页,来控制LED:
  • var http = require('http');
  • var fs = require('fs');
  • var url = require('url');

  • const Gpio = require('onoff').Gpio;
  • const led = new Gpio(448, 'out');

  • // 创建服务器
  • http.createServer( function (request, response) {
  • // 解析请求,包括文件名
  • var pathname = url.parse(request.url).pathname;

  • // 输出请求的文件名
  • console.log("Request for " + pathname + " received.");

  • if(pathname=="/on") {
  • led.write(1);
  • } else if(pathname=="/off") {
  • led.write(0);
  • }
  • response.writeHead(404, {'Content-Type': 'text/html'});
  • response.write(pathname);
  • response.end();

  • }).listen(8080);

  • // 控制台会输出以下信息
  • console.log('Server running at http://*.*.*.*:8080/');
 
将上述代码保存为web_led.js,然后执行:


再访问/on和/off对应的网址,LED就能通过网页控制了。
 
参考类似的方法,你还可以学习控制其他类型的外设,以及读取显示数据了。

使用USB摄像头建立MJPEG推流服务

之前一直使用的是Debian系统,结果上周末,SD卡出问题了,系统没了。
还好手头还有一张卡,于是下载了Ubuntu系统,写到SD卡,能够正常拍了。

Ubuntu下载地址:Ubuntu 22.04 LTS has a RISC-V version
 
下载之后,第一件事情,就是接上USB摄像头,然后查看系统信息中,是否成功识别了。

1. 查看dmesg信息:
  • sudo dmesg
 
[  477.372298] videodev: Linux video capture interface: v2.00
[  477.539509] usbcore: registered new interface driver snd-usb-audio
[  477.703831] usb 1-1.4: Found UVC 1.00 device HIK 720P Camera (2bdf:0280)
[  477.712220] input: HIK 720P Camera: HIK 720P Camer as /devices/platform/soc/104c0000.usb/xhci-hcd.0.auto/usb1/1-1/1-1.4/1-1.4:1.0/input/input0
[  477.713148] usbcore: registered new interface driver uvcvideo

看到以上信息,说明识别成功。
 
2. 查看设备:
  • sudo ls -lh /dev/video*
 
HonestQiao@ubuntu-star5:~$ sudo ls -lh /dev/video*
crw-rw---- 1 root video 81, 0 Aug  9 06:45 /dev/video0
crw-rw---- 1 root video 81, 1 Aug  9 06:45 /dev/video1

看到以上信息,说明系统成功挂载了对应的设备了。
 
然后,可以安装v4l工具,查看USB摄像头的相关信息:

  • sudo apt install v4l-utils
 
安装后,可以用下面的指令查看摄像头的信息:

  • # 查看当前挂载的设备
  • sudo v4l2-ctl --list-devices

  • # 查看只是的视频格式
  • sudo v4l2-ctl -d /dev/video0 --list-formats

  • # 查看支持的分辨率
  • sudo v4l2-ctl --list-framesizes=MJPG -d /dev/video0


 
命令实际执行,结果如下:
1. 挂载的设备:


2. 支持的格式:


确定其中支持MJPEG格式 
3. 支持的分辨率:


可以看到,MJPEG模式,支持640*480、1280*720。
有了这些信息,就可以准备建立MJPEG服务了。
 
要建立视频推流,可以用nginx + rtmp,也可以用mjpeg_streamer,后者专用于mjpeg,小巧有方便,就选它了。
 
先下载并安装mjpeg_streamer:
  • sudo apt install make cmake libjpeg9-dev

  • git clone https://github.com/jacksonliam/mjpg-streamer.git

  • cd mjpg-streamer-master/mjpg-streamer-experimental/

  • make all

  • sudo make install
 
如果中途提示缺少什么,就安装什么。因为提前执行了安装libjpeg9-dev的命令,所以不会提示libjpeg的问题。
 
正常make all的结果:


正常sudo make install的结果:

 
安装完成后,启动就能使用了:


 
 然后通过下面的网址即可访问:192.168.1.217:8080/?action=stream

该网址,可以在网页中,使用img标志直接播放,也可以使用python 读取进行处理,非常方便。

Python点亮炫彩灯环

在之前的分享中,我讲过使用C语言,直接操作spi设备,来控制WS2812B灯珠。

这一次,我们使用Python语言来控制。

得益于Python的可扩展性,已经有很多爱好者,做出了各种WS2812B的控制模块,经过一番研究,我选择了ws2812-spi,其官网地址为:joosteto/ws2812-spi: python routines to program the WS2812 RGB LED chips on the raspberry, using the hardware SPI MOSI. (github.com)

ws2812-spi模块,本来是给树莓派写的,但是在星光派上面,也能够正常使用。
 
首先是接线,还是参考上次的接线:

但我这次用的是炫彩灯环:

 
不过接线方式完全一样,因为WS2812B系列,都是VCC、GND、DIN的。
同样注意,需要使用额外的电源,给WS1812B供电,不要用板载的5V。
 
然后,要先安装下面的模块,以便python能够操作spi设备:

  • git clone https://github.com/doceme/py-spidev.git
  • cd py-spidev
  • make
  • make install

安装好以后,就可以下载ws2812-spi的代码了:

  • git clone https://github.com/joosteto/ws2812-spi.git
  • cd ws2812-spi

再编写一个下面的Python程序:
  • import spidev
  • import sys
  • import time

  • sys.path.append("./")
  • import ws2812

  • spi = spidev.SpiDev()
  • spi.open(0,0)

  • # write2812=write2812_pylist8

  • nLED=24
  • ledArray = [[0,0,0]]*nLED
  • print(ledArray)
  • ws2812.write2812(spi, ledArray)
  • time.sleep(1)

  • #write 4 WS2812's, with the following colors: red, green, blue, yellow
  • # ws2812.write2812(spi, [[10,0,0], [0,10,0], [0,0,10], [10, 10, 0]])

  • for n in range(0,100):
  • for i in range(0,nLED):
  • if i > 0:
  • ledArray[i-1] = [0, 0, 0]
  • # ws2812.write2812(spi, ledArray)
  • # time.sleep(0.5)
  • else:
  • ledArray[nLED-1] = [0, 0, 0]

  • if i % 7 ==0:
  • ledArray[i] = [100, 0, 0]
  • if i % 7 ==1:
  • ledArray[i] = [0, 100, 0]
  • if i % 7 ==2:
  • ledArray[i] = [0, 0, 100]
  • if i % 7 ==3:
  • ledArray[i] = [100, 100, 0]
  • if i % 7 ==4:
  • ledArray[i] = [100, 0, 100]
  • if i % 7 ==5:
  • ledArray[i] = [0, 100, 100]
  • if i % 7 ==6:
  • ledArray[i] = [100, 100, 100]

  • print(ledArray)
  • ws2812.write2812(spi, ledArray)
  • time.sleep(0.05)

将上述代码,保存为test.py,以便后续执行。
 
在上述代码中,其逻辑如下:
  1. 先打开spi设备,使用:
    spi = spidev.SpiDev()
    spi.open(0,0)
  2. 再将所有的灯熄灭:
    nLED=24
    ledArray = [[0,0,0]]*nLED
    ws2812.write2812(spi, ledArray)
  3. 然后循环,依次点亮下灯,并关闭上一课;根据循环的灯珠,来自动设定颜色
    ws2812.write2812(spi, ledArray)的第二个参数,就是一个颜色数组:[r, g, b],取值为0-255

编写完成后,使用`python test.py执行`,就能点亮了灯环了。

通过以上的代码,就能够控制这个炫彩灯环了。

如果进一步研究,你还可以考虑,根据简谱中的1234567,来设置点亮的灯珠个数,实现跟着音乐跳动,效果会更棒!

 先楫HPM6750测评 


原贴地址:http://bbs.eeworld.com.cn/thread-1210101-1-1.html

两种IDE(SES和RS)开发平台体验

SES是segger公司所出的segger embedded studio的开发平台,而RS是rtthread所出的rtthread studio。两个开发平台均支持hpm系列开发,并且是可以进行商业开发。先辑在这方面的合作生态还是值得肯定的。

楼主在项目上使用更多的是RS开发,而对于SES则是第一次接触,楼主在此贴更多得是记录SES新建工程的坑,再则记录RS的开发。相比两种IDE而言,楼主更绝对RS更容易开发,集成化也很高,不过编译和调试上并没有SES快。

先辑官方的SDK是支持SES开发平台,不过需要进行env配置,然后生成对应的SES工程,生成的工程耦合SDK太高,楼主并不喜欢这种方式,所以使用SES直接生成对应的最小工程。

 一、SES平台新建基础工程

网上其他记录SES新建工程大多比较简单,而且也不太够详细,比如SES工程支持debug和release版本,这两个版本的工程配置是可以继承于common版本,这样可以做到同等配置继承又可以实现差异化配置。

这里新建的时候需要安装下pack包,在Tool里看到package mangage即可进行安装。

  

新建工程时候,会弹出此窗口,这些配置都是共同的配置,debug和release版本均可继承。
       
进而在选择文件到工程的时候,楼主选择了全部不勾选,这样可以自己自由去选择文件。

这样新建的空工程即可完成

coremark跑分验证(外部flash xip运行和内部sram运行比较)

由于国内大多半导体厂家对于外部存储xip内存映射运行性能很差(也就是分所谓的零等待和非零等待区域),往往跑分宣传都是在SRAM(零等待区域)运行,导致用户在实际体验时候(代码区超过非零等待)也大受折扣。
  
先辑这块MCU,支持代码运行在FLASH XIP,同样也支持SRAM运行。那么两者差距有多大,这也是本贴需要验证的地方。
 
在实际验证当中,SRAM和FLASH XIP运行性能差距不大,相差150左右,代码放在外部flash运行,也是足够性能强大。
 
下图为在SRAM运行,跑分为4702分,比官方宣传的3965还要高些。
 

下图为FLASH XIP运行,跑分为4570分。与SRAM运行跑分相比,相差132分,性能差距不大,值得肯定。
 
 
下面楼主介绍coremark移植优化过程,在楼主上贴 [先楫HPM6750测评之一]两种IDE(SES和RS)开发平台体验 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn) 中的基础工程进行添加。
 
这里的debug版本和release版本分别设置为flash xip运行配置和sram运行配置。

 
根据官方SDK的env生成的工程配置进行移植对比。需要注意几个点。
  1、修改相关RV扩展

 
  2、编译器优化选项添加

 
  3、链接文件添加,flash xip和sram需要不同修改

   flash xip

 
   SRAM

 
  4、宏定义修改,flash xip和sram需要不同修改

   FLASH XIP运行需要加入FLASH_XIP=1

 
   SRAM则不需要,继承于common

 
至此修改完毕,需要注意的是BOOT拨码的设置,如下图说明:
 由此可知,当需要在SRAM运行时,需要拨码如下:

当需要在FLASH XIP运行时,需要拨码如下:


双核应用启动分析

HPM6750支持双核,除了主从核心之分,其他的配置均一相同,这对于应用来说相当重要。
   
官方SDK中有个双核的helloworld例子,在multicore\hello中,原先使用的mbx例子,core1跑在ram中,只有在调试的时候才会两核运行,手册中,core1复位启动需要core0加载并且copy到core1的ILM区域中,并且唤醒。见手册说明
 

先看下core0的代码,主要核心在于需要把core1的flash镜像拷贝加载到core1的ILM区域,然后再启动core1
 

在secondary_core_image_load_and_run这个函数中,sysctl_is_cpu1_released判断core1是否启动,判断是否启动主要看系统控制模块 SYSCTL 的HALT休眠位
 

由官方文档可知,从核启动管理需要这么做。
     

接下来看到的core_local_mem_to_sys_address,是core1根据SEC_CORE_IMG_START的定义地址去查找core1的ILM指令存储器的镜像地址,大小为256K,也就是说core1的指令程序空间可以达到256K。
 

接下来就是把memcpy((void *)sec_core_img_sys_addr, sec_core_img, sec_core_img_size);   sec_core_img的flash镜像拷贝到sec_core_img_sys_addr  core1的ILM地址中,做好相关的cache,启动core1,这样双核就启动了。
   
那么问题来了,sec_core_img这个C数组,也就是core1的程序代码是怎么生成的?
   
这里需要做一个bin转c的批处理文件,刚好官方也做了,在脚本文件夹中
 
E:/personal/software/risc-v/HPM6750EVKMINI/sdk_env_v0.9.0/tools/python3/python3.exe $(HPM_SDK_BASE)/scripts/bin2c.py $(OutDir)/$(ProjectName).bin sec_core_img > E:/personal/software/risc-v/HPM6750EVKMINI/sdk_env_v0.9.0/hpm_sdk/samples/multicore/hello/core0/src/sec_core_img.c"   
 
 

     

   
  如此,烧录core0代码就可以看到RGB闪烁和相关打印信息了。
 
 
SPI外设验证两种刷屏性能
此贴是为了后续的lvgl移植做的SPI显示屏驱动接口,由于没有RGB屏幕,只能使用SPI接口的显示屏作为UI显示。查看了官方的SDK代码的SPI例子,发现并没有SPI DMA的例子,只有poll例子。
  
查看了下手册,HPM6750的所有SPI接口均可达到最大80M频率,由400M频率的PLL1CLK1时钟源,最大分频为5分频得到。




根据扩展接口定义,本贴使用的是SPI2,加之SPI例子中也是使用SPI2,所以工程中就以SPI2作为显示屏接口。
  
官方的SPI例子移植为SPI显示屏显示需要注意几个点:
 
一、时钟源和分频改变
   
官方使用的SPI时钟源是CLK_24M,也就是SPI最高只能24M频率,这对于能快速刷屏而言,24M频率是肯定不够的,结合上述的时钟分配,可以找到以下的SPI时钟初始化接口board_init_spi_clock,根据以下解释便可得到SPI的频率,最大分频为5,也就是80M,这里的SPI显示屏根据手册最大也只能达到70多M,很明显楼主使用6分频即可。

       
当为默认的24M频率时候,帧率大概为19fps
 

当设置为66M频率时候,还没使用DMA,SPI轮询方式可达到30fps+. 这个速度相对其他国产的MCU而言,同等频率可高太多了。


细说SPI外设遇到的小曲折
自从上次的贴子调试不通SPI DMA后,一直搁置到现在,官方也刚好发布了V0.11.0版本,这回又重拾了SPI DMA。

很奇怪的是,把官方的SPI DMA移植过来之后,一直都是不成功,也没法触发到DMA完成中断,用分析仪抓了下波形,也是迟迟没有看到时序的发生。
 
于是照着官方的例子试下,果不其然,官方的demo是可以运行,通过DMA发送数据。看到了官方的例子有对命令和地址寄存器赋值,于是照着搞个temp的地址和命令对着赋值。果不其然,真的发出去了。数据也完全可以对的上。
 
按以往的开发经验来说,只需要传输数据的话,禁用掉地址段和命令段,以及也不需要去操作地址和命令寄存器。

但实际上,虽然命令段和地址段都禁用了,这CMD和ADDR两个寄存器是需要进行赋值的。
官方的驱动当中,不管禁止不禁止,都会对相应的寄存器进行赋值

现在可以发送了,但是大数据在分包发送的时候,分包大于512以上就是发送不成功。再次查看了寄存器。TRANSCTRL的数据长度是9Bit,也就是只能512长度。这个SPI IP确实有点独特,以往的开发大多是16位以上的,这也就标志着在进行DMA传输时,也只能发送512分包。

验证一下:
SPI DMA传输,O3代码优化,大概可以45fps


而SPI poll传输,也是O3代码优化,大概可以41fps

这样看来,确实DMA对于SPI的传输帮助并没那么大,主要受限与SPI的数据寄存器最大只能512.

于此,对于使用HPM6750的SPI DMA外设,需要注意以下两点:

1、SPI DMA一次传输最大512字节,超过需要进行分包
2、使用SPI DMA时候,在配置发送的时候,需要对命令和地址寄存器进行赋值,随便一个值都可以。当然,询问了官方,后续这些问题都会有驱动层进行解决,以后的SDK更新也不会有此类问题暴露给用户操作。

细说性能提升的优化方法

在之前的coremark跑分贴子上,在flash和ram运行的性能大致一样,主要的原因还是代码空间小于32K,这刚好是cache的空间范围内,hpm6570有32K ICACHE和32K DCACHE,性能上是最高的,所以跑分上,两者并没有太大的差距。

但是,如果代码空间超过了32K,这时候cache总会有用满的时候,也会有不命中的情况下,这时候需要考虑的正是系统资源和编译整合利用。

 下面以littlevgl的benchmark跑分例子要进行性能提升的一个验证方法,当然这仅仅作为参考,并不能决定大多数应用场景。

由于上个贴子说明了SPI的一点缺陷,会导致DMA的辅助功能提升并不大,在实际跑lvgl的时候,code放在flash,编译器使用segger,代码缺省优化,也其实没优化的情况下,生成的代码如下:


 那么按照这样烧录进去,weightied fps大概是120多左右。

 
  
这是有点低了,先从lvgl的配置上去优化,lvgl的刷新周期,从30fps最大刷新率改为100fps刷新率,提升上也并不是很大,大概在160左右变动。

那么开O3优化的效果又是如何,再次烧录进去,weightied fps大概是174多左右。
当然也试了以下方法,实验过程也忘了拍照,但是其实效果性能并没有提升多少,也就180左右变动
1、改为全尺寸双缓冲,但是其实这种对MCU屏幕有用,对于SPI屏幕上,效果并没多少。
2、改为非全尺寸双缓冲,大概五分之一局部刷新。
3、改为单缓冲局部刷新和单缓冲全尺寸刷新,效果均不大。
  
于是试着找了官方的技术,放假期间的,技术也在中午跟着我远程调试了下,换为GCC编译器,以及开启了相关优化,优化提升也不明显,大概也是180fps变动。
  
在调试的过程中,有个idea让楼主茅塞顿开,也就是官方技术建议就是把中断isr放在ram运行,但实际提升也不大。

于是楼主照着这个思路来看下性能有没有增加,也就是把核心的代码加载到ram中运行。好在与hpm6750有足够的RAM来加载,根据手册可知道,两核心有SLV各512K,SRAM一共1M,这是足够加载很多核心代码。

说干就干,在代码上去实现的话,可以使用ATTR_RAMFUNC修饰符放在定义的函数前面,这样编译的时候就会加载到RAM运行。
在实际调试中,单纯几个函数的修饰并不能解决问题。也不可能去手动一个一个修饰,好在与SES可以可视化去操作加载。从ATTR_RAMFUNC,Link文件可看到。

ATTR_RAMFUNC是把函数放在了section的.fast中,

 
从Link可看到,fast是放在了ILM_SLV的256K空间中。


于是我们可以参考Link,自己在copy个link,把fast放在更大的RAM上,也就是SRAM上


那么ses如何去加载这些函数到RAM上了,跟keil类似
右键点击需要加载的文件夹,选择options

选择code段改为.fast,这样就可以一次搞定加载所有需要到RAM运行的函数。

     
根据之前的调试性能,再加载核心的放在RAM中运行,烧录代码进去,奇迹的时刻,从122fps提升到286,整整提升了两倍性能,这已经对于SPI这个稍微缺陷IP,足够有帮助了。

于此总结:
1、在从代码优化,编译器优化上,可以提高性能。
2、在1的基础上,随着代码空间的增多,32k cache总有用完的时候,xip flash 也会有所损失性能,最好就是可以把主要的代码加载到RAM中运行,更可提高性能。
3、除了32K cache的加持,内部RAM整合也有足够2M,对于系统而言,是足够性能整合的。

 先楫HPM6750运行边缘AI框架——TFLM基准测试 


原帖地址:http://bbs.eeworld.com.cn/thread-1208270-1-1.html

 

本篇将会介绍TFLM是什么,然后介绍TFLM官方的基准测试,以及如何在HPM6750上运行TFLM基准测试,并和树莓派3B+上的基准测试结果进行对比。

TFLM是什么?

你或许都听说过TensorFlow——由谷歌开发并开源的一个机器学习库,它支持模型训练和模型推理。

今天介绍的TFLM,全称是TensorFlow Lite for Microcontrollers,翻译过来就是“针对微控制器的TensorFlow Lite”。那TensorFlow Lite又是什么呢?

TensorFlow Lite(通常简称TFLite)其实是TensorFlow团队为了将模型部署到移动设备而开发的一套解决方案,通俗的说就是手机版的TensorFlow。下面是TensorFlow官网上关于TFLite的一段介绍:

TensorFlow Lite 是一组工具,可帮助开发者在移动设备、嵌入式设备和 loT 设备上运行模型,以便实现设备端机器学习。

而我们今天要介绍的TensorFlow Lite for Microcontrollers(TFLM)则是 TensorFlow Lite的微控制器版本。这里是官网上的一段介绍:

TensorFlow Lite for Microcontrollers (以下简称TFLM)是 TensorFlow Lite 的一个实验性移植版本,它适用于微控制器和其他一些仅有数千字节内存的设备。它可以直接在“裸机”上运行,不需要操作系统支持、任何标准 C/C++ 库和动态内存分配。核心运行时(core runtime)在 Cortex M3 上运行时仅需 16KB,加上足以用来运行语音关键字检测模型的操作,也只需 22KB 的空间。

这三者一脉相承,都出自谷歌,区别是TensorFlow同时支持训练和推理,而后两者只支持推理。TFLite主要用于支持手机、平板等移动设备,TFLM则可以支持单片机。从发展历程上来说,后两者都是TensorFlow项目的“支线项目”。或者说这三者是一个树形的发展过程,具体来说,TFLite是从TensorFlow项目分裂出来的,TFLite-Micro是从TFLite分裂出来的,目前是三个并行发展的。在很长一段时间内,这三个项目的源码都在一个代码仓中维护,从源码目录的包含关系上来说,TensorFlow包含后两者,TFLite包含tflite-micro。

TFLM开源项目


2021年6月,谷歌将TFLM项目的源代码从TensorFlow主仓中转移到了一个独立的代码仓中。

但截至目前(2022年6月),TFLite的源代码仍然以TensorFlow项目中的一个子目录进行维护。这也可以看出谷歌对TFLM的重视。

TFLM代码仓链接:https://github.com/tensorflow/tflite-micro

下载命令:git clone https://github.com/tensorflow/tflite-micro.git

TFLM主要业务代码位于tensorflow\lite\micro子目录:

TFLM官方支持make和bazel构建。

TFLM基准测试

TFLM代码仓顶层的README.md中给出了基准测试文档链接:
https://github.com/tensorflow/tflite-micro/blob/main/tensorflow/lite/micro/benchmarks/README.md

该文档篇幅不长:

通过这个目录我们可以知道,TFLM提供了两个基准测试(实际有三个),分别是:

  • 关键词基准测试

    • 关键词基准测试使用的是程序运行时生产的随机数据作为输入,所以它的输出是没有意义的

  • 人体检测基准测试

    • 人体检测基准测试使用了两张bmp图片作为输入

    • 具体位于tensorflow\lite\micro\examples\person_detection\testdata子目录


下载依赖的软件

在PC的Linux系统上,运行TFLM基准测试之前,需要先安装依赖的一些工具:

sudo apt install git unzip wget python3 python3-pip


基准测试命令


参考”Run on x86”,在x86 PC上运行关键词基准测试的命令是:

  • make -f tensorflow/lite/micro/tools/make/Makefile run_keyword_benchmark

在PC上运行人体检测基准测试的命令是:

  • make -f tensorflow/lite/micro/tools/make/Makefile run_person_detection_benchmark

执行这两个命令,会依次执行如下步骤:

  1. 调用几个下载脚本,下载依赖库和数据集;

  2. 编译测试程序;

  3. 运行测试程序;

  • tensorflow/lite/micro/tools/make/Makefile

代码片段中,可以看到调用了几个下载脚本: 

flatbuffers_download.sh和kissfft_download.sh脚本第一次执行时,会将相应的压缩包下载到本地,并解压,具体细节参见代码内容;

pigweed_download.sh脚本会克隆一个代码仓,再检出一个特定版本:

这里需要注意的是,代码仓https://pigweed.googlesource.com/pigweed/pigweed 国内一般无法访问(因为域名googlesource.com被禁了)。将此连接修改为我克隆好的代码仓:https://github.com/xusiwei/pigweed.git 可以解决因为国内无法访问googlesource.com而无法下载pigweed测试数据的问题。

基准测试的构建规则

  • tensorflow/lite/micro/tools/make/Makefile

  • 文件是Makefile总入口文件,该文件中定义了一些makefile宏函数,并通过include引入了其他文件,包括定义了两个基准测试编译规则的tensorflow/lite/micro/benchmarks/Makefile.inc

文件:

KEYWORD_BENCHMARK_SRCS := \\tensorflow/lite/micro/benchmarks/keyword_benchmark.cc
KEYWORD_BENCHMARK_GENERATOR_INPUTS := \\tensorflow/lite/micro/models/keyword_scrambled.tflite
KEYWORD_BENCHMARK_HDRS := \\tensorflow/lite/micro/benchmarks/micro_benchmark.h
KEYWORD_BENCHMARK_8BIT_SRCS := \\tensorflow/lite/micro/benchmarks/keyword_benchmark_8bit.cc
KEYWORD_BENCHMARK_8BIT_GENERATOR_INPUTS := \\tensorflow/lite/micro/models/keyword_scrambled_8bit.tflite
KEYWORD_BENCHMARK_8BIT_HDRS := \\tensorflow/lite/micro/benchmarks/micro_benchmark.h
PERSON_DETECTION_BENCHMARK_SRCS := \\tensorflow/lite/micro/benchmarks/person_detection_benchmark.cc
PERSON_DETECTION_BENCHMARK_GENERATOR_INPUTS := \\tensorflow/lite/micro/examples/person_detection/testdata/person.bmp \\tensorflow/lite/micro/examples/person_detection/testdata/no_person.bmp
ifneq ($(CO_PROCESSOR),ethos_u) PERSON_DETECTION_BENCHMARK_GENERATOR_INPUTS += \\ tensorflow/lite/micro/models/person_detect.tfliteelse # Ethos-U use a Vela optimized version of the original model. PERSON_DETECTION_BENCHMARK_SRCS += \\ $(GENERATED_SRCS_DIR)tensorflow/lite/micro/models/person_detect_model_data_vela.ccendif
PERSON_DETECTION_BENCHMARK_HDRS := \\tensorflow/lite/micro/examples/person_detection/model_settings.h \\tensorflow/lite/micro/benchmarks/micro_benchmark.h
# Builds a standalone binary.$(eval $(call microlite_test,keyword_benchmark,\\$(KEYWORD_BENCHMARK_SRCS),$(KEYWORD_BENCHMARK_HDRS),$(KEYWORD_BENCHMARK_GENERATOR_INPUTS)))
# Builds a standalone binary.$(eval $(call microlite_test,keyword_benchmark_8bit,\\$(KEYWORD_BENCHMARK_8BIT_SRCS),$(KEYWORD_BENCHMARK_8BIT_HDRS),$(KEYWORD_BENCHMARK_8BIT_GENERATOR_INPUTS)))
$(eval $(call microlite_test,person_detection_benchmark,\\$(PERSON_DETECTION_BENCHMARK_SRCS),$(PERSON_DETECTION_BENCHMARK_HDRS),$(PERSON_DETECTION_BENCHMARK_GENERATOR_INPUTS)))

从这里可以看到,实际上有三个基准测试程序,比文档多了一个 keyword_benchmark_8bit ,应该是 keword_benchmark的8bit量化版本。另外,可以看到有三个tflite的模型文件。

Keyword基准测试


关键词基准测试使用的模型较小,比较适合在STM32 F3/F4这类主频低于100MHz的MCU。

这个基准测试的模型比较小,计算量也不大,所以在PC上运行这个基准测试的耗时非常短:

可以看到,在PC上运行关键词唤醒的速度非常快,10次时间不到1毫秒。

模型文件路径为:./tensorflow/lite/micro/models/keyword_scrambled.tflite

模型结构可以使用Netron软件查看。

Person detection基准测试


人体检测基准测试的计算量相对要大一些,运行的时间也要长一些: 

xu@VirtualBox:~/opensource/tflite-micro$ make -f tensorflow/lite/micro/tools/make/Makefile run_person_detection_benchmarktensorflow/lite/micro/tools/make/downloads/flatbuffers already exists, skipping the download.tensorflow/lite/micro/tools/make/downloads/kissfft already exists, skipping the download.tensorflow/lite/micro/tools/make/downloads/pigweed already exists, skipping the download.g++ -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wmissing-field-initializers -Wstrict-aliasing -Wno-unused-parameter  -DTF_LITE_USE_CTIME -Os -I. -Itensorflow/lite/micro/tools/make/downloads/gemmlowp -Itensorflow/lite/micro/tools/make/downloads/flatbuffers/include -Itensorflow/lite/micro/tools/make/downloads/ruy -Itensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/ -Itensorflow/lite/micro/tools/make/downloads/kissfft -c tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/examples/person_detection/testdata/person_image_data.cc -o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/examples/person_detection/testdata/person_image_data.og++ -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wmissing-field-initializers -Wstrict-aliasing -Wno-unused-parameter  -DTF_LITE_USE_CTIME -Os -I. -Itensorflow/lite/micro/tools/make/downloads/gemmlowp -Itensorflow/lite/micro/tools/make/downloads/flatbuffers/include -Itensorflow/lite/micro/tools/make/downloads/ruy -Itensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/ -Itensorflow/lite/micro/tools/make/downloads/kissfft -c tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/examples/person_detection/testdata/no_person_image_data.cc -o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/examples/person_detection/testdata/no_person_image_data.og++ -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wmissing-field-initializers -Wstrict-aliasing -Wno-unused-parameter  -DTF_LITE_USE_CTIME -Os -I. -Itensorflow/lite/micro/tools/make/downloads/gemmlowp -Itensorflow/lite/micro/tools/make/downloads/flatbuffers/include -Itensorflow/lite/micro/tools/make/downloads/ruy -Itensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/ -Itensorflow/lite/micro/tools/make/downloads/kissfft -c tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/models/person_detect_model_data.cc -o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/models/person_detect_model_data.og++ -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -Werror -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wmissing-field-initializers -Wstrict-aliasing -Wno-unused-parameter  -DTF_LITE_USE_CTIME -I. -Itensorflow/lite/micro/tools/make/downloads/gemmlowp -Itensorflow/lite/micro/tools/make/downloads/flatbuffers/include -Itensorflow/lite/micro/tools/make/downloads/ruy -Itensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/ -Itensorflow/lite/micro/tools/make/downloads/kissfft -o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/bin/person_detection_benchmark tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/benchmarks/person_detection_benchmark.o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/examples/person_detection/testdata/person_image_data.o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/examples/person_detection/testdata/no_person_image_data.o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/obj/core/tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/genfiles/tensorflow/lite/micro/models/person_detect_model_data.o tensorflow/lite/micro/tools/make/gen/linux_x86_64_default/lib/libtensorflow-microlite.a -Wl,--fatal-warnings -Wl,--gc-sections -lmtensorflow/lite/micro/tools/make/gen/linux_x86_64_default/bin/person_detection_benchmark non_test_binary linuxInitializeBenchmarkRunner took 192 ticks (0 ms).
WithPersonDataIterations(1) took 32299 ticks (32 ms)DEPTHWISE_CONV_2D took 895 ticks (0 ms).DEPTHWISE_CONV_2D took 895 ticks (0 ms).CONV_2D took 1801 ticks (1 ms).DEPTHWISE_CONV_2D took 424 ticks (0 ms).CONV_2D took 1465 ticks (1 ms).DEPTHWISE_CONV_2D took 921 ticks (0 ms).CONV_2D took 2725 ticks (2 ms).DEPTHWISE_CONV_2D took 206 ticks (0 ms).CONV_2D took 1367 ticks (1 ms).DEPTHWISE_CONV_2D took 423 ticks (0 ms).CONV_2D took 2540 ticks (2 ms).DEPTHWISE_CONV_2D took 102 ticks (0 ms).CONV_2D took 1265 ticks (1 ms).DEPTHWISE_CONV_2D took 205 ticks (0 ms).CONV_2D took 2449 ticks (2 ms).DEPTHWISE_CONV_2D took 204 ticks (0 ms).CONV_2D took 2449 ticks (2 ms).DEPTHWISE_CONV_2D took 243 ticks (0 ms).CONV_2D took 2483 ticks (2 ms).DEPTHWISE_CONV_2D took 202 ticks (0 ms).CONV_2D took 2481 ticks (2 ms).DEPTHWISE_CONV_2D took 203 ticks (0 ms).CONV_2D took 2489 ticks (2 ms).DEPTHWISE_CONV_2D took 52 ticks (0 ms).CONV_2D took 1222 ticks (1 ms).DEPTHWISE_CONV_2D took 90 ticks (0 ms).CONV_2D took 2485 ticks (2 ms).AVERAGE_POOL_2D took 8 ticks (0 ms).CONV_2D took 3 ticks (0 ms).RESHAPE took 0 ticks (0 ms).SOFTMAX took 2 ticks (0 ms).
NoPersonDataIterations(1) took 32148 ticks (32 ms)DEPTHWISE_CONV_2D took 906 ticks (0 ms).DEPTHWISE_CONV_2D took 924 ticks (0 ms).CONV_2D took 1762 ticks (1 ms).DEPTHWISE_CONV_2D took 446 ticks (0 ms).CONV_2D took 1466 ticks (1 ms).DEPTHWISE_CONV_2D took 897 ticks (0 ms).CONV_2D took 2692 ticks (2 ms).DEPTHWISE_CONV_2D took 209 ticks (0 ms).CONV_2D took 1366 ticks (1 ms).DEPTHWISE_CONV_2D took 427 ticks (0 ms).CONV_2D took 2548 ticks (2 ms).DEPTHWISE_CONV_2D took 102 ticks (0 ms).CONV_2D took 1258 ticks (1 ms).DEPTHWISE_CONV_2D took 208 ticks (0 ms).CONV_2D took 2473 ticks (2 ms).DEPTHWISE_CONV_2D took 210 ticks (0 ms).CONV_2D took 2460 ticks (2 ms).DEPTHWISE_CONV_2D took 203 ticks (0 ms).CONV_2D took 2461 ticks (2 ms).DEPTHWISE_CONV_2D took 230 ticks (0 ms).CONV_2D took 2443 ticks (2 ms).DEPTHWISE_CONV_2D took 203 ticks (0 ms).CONV_2D took 2467 ticks (2 ms).DEPTHWISE_CONV_2D took 51 ticks (0 ms).CONV_2D took 1224 ticks (1 ms).DEPTHWISE_CONV_2D took 89 ticks (0 ms).CONV_2D took 2412 ticks (2 ms).AVERAGE_POOL_2D took 7 ticks (0 ms).CONV_2D took 2 ticks (0 ms).RESHAPE took 0 ticks (0 ms).SOFTMAX took 2 ticks (0 ms).
WithPersonDataIterations(10) took 326947 ticks (326 ms)
NoPersonDataIterations(10) took 352888 ticks (352 ms)

可以看到,人像检测模型运行10次的时间是三百多毫秒,一次平均三十几毫秒。这是在配备AMD标压R7 4800 CPU的Win10虚拟机下运行的结果。

模型文件路径为:./tensorflow/lite/micro/models/person_detect.tflite

同样,可以使用Netron查看模型结构。

HPM SDK中的TFLM


TFLM中间件


HPM SDK中集成了TFLM中间件(类似库,但是没有单独编译为库),位于hpm_sdk\middleware子目录:

这个子目录的代码是由TFLM开源项目裁剪而来,删除了很多不需要的文件。

TFLM 示例


HPM SDK中也提供了TFLM示例,位于hpm_sdk\samples\tflm子目录:

示例代码是从官方的persion_detection示例修改而来,添加了摄像头采集图像和LCD显示结果。

由于我手里没有配套的摄像头和显示屏,所以本篇没有以这个示例作为实验。

在HPM6750上运行TFLM基准测试


接下来以person detection benchmark为例,讲解如何在HPM6750上运行TFLM基准测试。

将person detection benchmark源代码添加到HPM SDK环境


按照如下步骤,在HPM SDK环境中添加person detection benchmark源代码文件:

  1. 在HPM SDK的samples子目录创建tflm_person_detect_benchmark目录,并在其中创建src目录;

  2. 从上文描述的已经运行过person detection benchmark的tflite-micro目录中拷贝如下文件到src目录:

    1. tensorflow\lite\micro\benchmarks\person_detection_benchmark.cc

    2. tensorflow\lite\micro\benchmarks\micro_benchmark.h

    3. tensorflow\lite\micro\examples\person_detection\model_settings.h

    4. tensorflow\lite\micro\examples\person_detection\model_settings.cc

  3. 在src目录创建testdata子目录,并将tflite-micro目录下如下目录中的文件拷贝全部到testdata中:

    1. tensorflow\lite\micro\tools\make\gen\linux_x86_64_default\genfiles\tensorflow\lite\micro\examples\person_detection\testdata

  4. 修改person_detection_benchmark.cc、model_settings.cc、no_person_image_data.cc、person_image_data.cc 文件中部分#include预处理指令的文件路径(根据拷贝后的相对路径修改);

  5. person_detection_benchmark.cc文件中,main函数的一开始添加一行board_init();、顶部添加一行#include "board.h”

添加CMakeLists.txt和app.yaml文件

在src平级创建CMakeLists.txt文件,内容如下:

cmake_minimum_required(VERSION 3.13)
set(CONFIG_TFLM 1)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})project(tflm_person_detect_benchmark)set(CMAKE_CXX_STANDARD 11)
sdk_app_src(src/model_settings.cc)sdk_app_src(src/person_detection_benchmark.cc)sdk_app_src(src/testdata/no_person_image_data.cc)sdk_app_src(src/testdata/person_image_data.cc)
sdk_app_inc(src)sdk_ld_options("-lm")sdk_ld_options("--std=c++11")sdk_compile_definitions(__HPMICRO__)sdk_compile_definitions(-DINIT_EXT_RAM_FOR_DATA=1)# sdk_compile_options("-mabi=ilp32f")# sdk_compile_options("-march=rv32imafc")sdk_compile_options("-O2")# sdk_compile_options("-O3")set(SEGGER_LEVEL_O3 1)generate_ses_project()

在src平级创建app.yaml文件,内容如下:

dependency:
- tflm

编译和运行TFLM基准测试


接下来就是大家熟悉的——编译运行了。

首先,使用generate_project生产项目:

接着,将HPM6750开发板连接到PC,在Embedded Studio中打卡刚刚生产的项目:

这个项目因为引入了TFLM的源码,文件较多,所以右边的源码导航窗里面的Indexing要执行很久才能结束。

然后,就可以使用F7编译、F5调试项目了:

编译完成后,先打卡串口终端连接到设备串口,波特率115200。启动调试后,直接继续运行,就可以在串口终端中看到基准测试的输出了:

============================== hpm6750evkmini clock summary==============================cpu0:            816000000Hzcpu1:            816000000Hzaxi0:            200000000Hzaxi1:            200000000Hzaxi2:            200000000Hzahb:             200000000Hzmchtmr0:         24000000Hzmchtmr1:         1000000Hzxpi0:            133333333Hzxpi1:            400000000Hzdram:            166666666Hzdisplay:         74250000Hzcam0:            59400000Hzcam1:            59400000Hzjpeg:            200000000Hzpdma:            200000000Hz==============================
----------------------------------------------------------------------$$\\ $$\\ $$$$$$$\\ $$\\ $$\\ $$\\$$ | $$ |$$ __$$\\ $$$\\ $$$ |\\__|$$ | $$ |$$ | $$ |$$$$\\ $$$$ |$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\$$$$$$$$ |$$$$$$$ |$$\\$$\\$$ $$ |$$ |$$ _____|$$ __$$\\ $$ __$$\\$$ __$$ |$$ ____/ $$ \\$$$ $$ |$$ |$$ / $$ | \\__|$$ / $$ |$$ | $$ |$$ | $$ |\\$ /$$ |$$ |$$ | $$ | $$ | $$ |$$ | $$ |$$ | $$ | \\_/ $$ |$$ |\\$$$$$$$\\ $$ | \\$$$$$$ |\\__| \\__|\\__| \\__| \\__|\\__| \\_______|\\__| \\______/----------------------------------------------------------------------InitializeBenchmarkRunner took 114969 ticks (4 ms).
WithPersonDataIterations(1) took 10694521 ticks (445 ms)DEPTHWISE_CONV_2D took 275798 ticks (11 ms).DEPTHWISE_CONV_2D took 280579 ticks (11 ms).CONV_2D took 516051 ticks (21 ms).DEPTHWISE_CONV_2D took 139000 ticks (5 ms).CONV_2D took 459646 ticks (19 ms).DEPTHWISE_CONV_2D took 274903 ticks (11 ms).CONV_2D took 868518 ticks (36 ms).DEPTHWISE_CONV_2D took 68180 ticks (2 ms).CONV_2D took 434392 ticks (18 ms).DEPTHWISE_CONV_2D took 132918 ticks (5 ms).CONV_2D took 843014 ticks (35 ms).DEPTHWISE_CONV_2D took 33228 ticks (1 ms).CONV_2D took 423288 ticks (17 ms).DEPTHWISE_CONV_2D took 62040 ticks (2 ms).CONV_2D took 833033 ticks (34 ms).DEPTHWISE_CONV_2D took 62198 ticks (2 ms).CONV_2D took 834644 ticks (34 ms).DEPTHWISE_CONV_2D took 62176 ticks (2 ms).CONV_2D took 838212 ticks (34 ms).DEPTHWISE_CONV_2D took 62206 ticks (2 ms).CONV_2D took 832857 ticks (34 ms).DEPTHWISE_CONV_2D took 62194 ticks (2 ms).CONV_2D took 832882 ticks (34 ms).DEPTHWISE_CONV_2D took 16050 ticks (0 ms).CONV_2D took 438774 ticks (18 ms).DEPTHWISE_CONV_2D took 27494 ticks (1 ms).CONV_2D took 974362 ticks (40 ms).AVERAGE_POOL_2D took 2323 ticks (0 ms).CONV_2D took 1128 ticks (0 ms).RESHAPE took 184 ticks (0 ms).SOFTMAX took 2249 ticks (0 ms).
NoPersonDataIterations(1) took 10694160 ticks (445 ms)DEPTHWISE_CONV_2D took 274922 ticks (11 ms).DEPTHWISE_CONV_2D took 281095 ticks (11 ms).CONV_2D took 515380 ticks (21 ms).DEPTHWISE_CONV_2D took 139428 ticks (5 ms).CONV_2D took 460039 ticks (19 ms).DEPTHWISE_CONV_2D took 275255 ticks (11 ms).CONV_2D took 868787 ticks (36 ms).DEPTHWISE_CONV_2D took 68384 ticks (2 ms).CONV_2D took 434537 ticks (18 ms).DEPTHWISE_CONV_2D took 133071 ticks (5 ms).CONV_2D took 843202 ticks (35 ms).DEPTHWISE_CONV_2D took 33291 ticks (1 ms).CONV_2D took 423388 ticks (17 ms).DEPTHWISE_CONV_2D took 62190 ticks (2 ms).CONV_2D took 832978 ticks (34 ms).DEPTHWISE_CONV_2D took 62205 ticks (2 ms).CONV_2D took 834636 ticks (34 ms).DEPTHWISE_CONV_2D took 62213 ticks (2 ms).CONV_2D took 838212 ticks (34 ms).DEPTHWISE_CONV_2D took 62239 ticks (2 ms).CONV_2D took 832850 ticks (34 ms).DEPTHWISE_CONV_2D took 62217 ticks (2 ms).CONV_2D took 832856 ticks (34 ms).DEPTHWISE_CONV_2D took 16040 ticks (0 ms).CONV_2D took 438779 ticks (18 ms).DEPTHWISE_CONV_2D took 27481 ticks (1 ms).CONV_2D took 974354 ticks (40 ms).AVERAGE_POOL_2D took 1812 ticks (0 ms).CONV_2D took 1077 ticks (0 ms).RESHAPE took 341 ticks (0 ms).SOFTMAX took 901 ticks (0 ms).
WithPersonDataIterations(10) took 106960312 ticks (4456 ms)
NoPersonDataIterations(10) took 106964554 ticks (4456 ms)

可以看到,在HPM6750EVKMINI开发板上,连续运行10次人像检测模型,总体耗时4456毫秒,每次平均耗时445.6毫秒。

在树莓派3B+上运行TFLM基准测试


在树莓派上运行TFLM基准测试


树莓派3B+上可以和PC上类似,下载源码后,直接运行PC端的make命令:

make -f tensorflow/lite/micro/tools/make/Makefile

一段时间后,即可得到基准测试结果:

可以看到,在树莓派3B+上的,对于有人脸的图片,连续运行10次人脸检测模型,总体耗时4186毫秒,每次平均耗时418.6毫秒;对于无人脸的图片,连续运行10次人脸检测模型,耗时4190毫秒,每次平均耗时419毫秒。

HPM6750和AMD R7 4800H、树莓派3B+的基准测试结果对比

这里将HPM6750EVKMINI开发板、树莓派3B+和AMD R7 4800H上运行人脸检测模型的平均耗时结果汇总如下:


树莓派3B+

HPM6750EVKMINI

AMD R7 4800H

有人脸平均耗时(ms)

418.6

445.6

32.6

无人脸平均耗时(ms)

419

445.6

35.2

CPU最高主频(Hz)

1.4G

816M

4.2G

可以看到,在TFLM人脸检测模型计算场景下,HPM6750EVKMINI和树莓派3B+成绩相当。虽然HPM6750的816MHz CPU频率比树莓派3B+搭载的BCM2837 Cortex-A53 1.4GHz的主频低,但是在单核心计算能力上平没有相差太多。

这里树莓派3B+上的TFLM基准测试程序是运行在64位Debian Linux发行版上的,而HPM6750上的测试程序是直接运行在裸机上的。由于操作系统内核中任务调度器的存在,会对CPU的计算能力带来一定损耗。所以,这里进行的并不是一个严格意义上的对比测试,测试结果仅供参考。

参考链接

更多内容可以参考TFLM官网和项目源码。

  1. TFLite指南:https://tensorflow.google.cn/lite/guide?hl=zh-cn

  2. TFLM介绍:https://tensorflow.google.cn/lite/microcontrollers/overview?hl=zh-cn

  3. TensorFlow官网:https://tensorflow.google.cn/


 先楫HPM6750 CoreMark跑分测试 


原贴地址:http://bbs.eeworld.com.cn/thread-1203215-1-1.html

 

上篇帖子中,我们完成了Embedded Studio开发环境搭建,对Hello World示例进行了编译和调试。

本篇将使用Embedded Studio编译CoreMark程序,并进行coremark跑分测试,同时对HPM6750的跑分结果和STM32部分型号的跑分结果进行对比。

CoreMark简介


什么是CoreMark?

来自CoreMark首页的解释是:

CoreMark is a simple, yet sophisticated benchmark that is designed specifically to test the functionality of a processor core. Running CoreMark produces a single-number score allowing users to make quick comparisons between processors.

翻译一下就是:

CoreMark是一个简单而又精密的基准测试程序,是专门为测试处理器核功能而设计的。运行CoreMark会产生一个“单个数字”的分数,(从而)允许用户在(不同)CPU之间进行快速比较。

简单来说,就是一个测试CPU性能的程序,类似PC上的Cinebench、CPU-Z之类的CPU性能测试工具。
了解了CoreMark是什么之后,接下来我们尝试在HPM6750开发板上跑一下CoreMark,看看分数是多少。

在HPM6750上运行CoreMark

创建CoreMark项目

HPM SDK样例中已经附带了CoreMark,因此我们直接使用SDK中的coremark程序就行了。
首先,根据上篇帖子中的介绍,使用generate_project命令生成Embedded Studio的项目:

generate_project命令执行完毕后,打开生成的hpm6750evkmini_build\segger_embedded_studio子目录,可以看到项目文件已经创建完成了:


双击coremark.emProject文件,默认会使用Embedded Studio打开项目(Embedded Studio安装成功的话):

 

可以看到,coremark主要的源文件只有6个.c文件。

编译CoreMark项目


点击Embedded Studio的【Build】→【Build coremark】菜单,即可触发编译;稍等一段实际后,编译完成,可以在Output窗口看到Build complete:

运行CoreMark跑分

开始运行之前,我们可以使用串口调试助手(或者其他类似的工具),连接开发板的串口设备。我这里使用的是MobaXterm,Putty或者sscom之类也是可以的。

串口配置是:

  • 波特率115200,

  • 8位数据位,

  • 1位停止位,

  • 无校验位。

点击Embedded Studio的【Debug】→【Go】菜单,即可运行coremark程序:


不需要单步执行,直接点绿色三角形图标(Continue Execution),让程序直接运行。

点击运行按钮后,立刻可以看到串口输出:

 

这段输出是由CoreMark程序启动时调用board_init输出的,所以在测试刚刚开始就会输出。


运行一段时间后(10秒左右),可以看到跑分结果输出了:

 

图中的HPMicro字符画是测试刚开始时输出的,下面的部分是最终输出。

最总跑分:4698.857421


细心的朋友可能会发现,这里的分数并不像新闻里面报道的9220分。这是为什么呢?


经过简单的分析coremark项目的代码,不难得出答案。原来,示例程序里面的coremark项目,只是用了HPM6750的一个CPU核,而HPM6750是有两个同样的CPU核的。


那么,双核同时运行CoreMark测试,分数会翻倍吗?会是官方公布的9220分吗?这里我们暂且保留悬念,后面的帖子中我们将进行双核CoreMark实验。


不同存储模式的CoreMark对比


前面的跑分结果是使用-t flash_xip生成的项目得到的,接下来我们尝试使用不同-t选项进行10轮测试:


 

可以看到,release参数的执行平均分数最高,单核达到了4701.68分。

观察generate_project命令的输出,可以发现不同-t参数,项目使用的链接脚本不同:

通过对比链接脚本的内容,我们可以知道,不同链接脚本使用的存储配置不同。上一篇帖子的最后,也有一个表格做了总结,这里再次贴出来:

调试版

发布版(更小)

程序代码

运行内存

debug

release

片内SRAM

片内SRAM

flash_xip

flash_xip_release

FLASH芯片

片内SRAM

flash_sdram_xip

flash_sdram_xip_release

FLASH芯片

DRAM芯片


和其他芯片的CoreMark跑分对比


CoreMark跑分榜


CoreMark首页的Scores页面中,有一些已经测试过的CPU、MCU的跑分记录。


CoreMark跑分查询


我们可以在Processor Name Match框中输入STM32,点击Apply进行过滤。过滤出结果后,我们可以按照分数从高到底排序:

可以看到,STM32H745的跑分是3223.82分,STM32H743的跑分是2020.55分。相比HPM6750单个CPU核的4698.86分差的都比较多。


不过这里查询到的数据都比较老了,STM32H7系列产品也在不断更新。因此,我从STM官网上找来了关于STM32H743和STM32H745的CoreMark跑分,以及HPM6750官方公布跑分数据,对比如下:



STM32H743

STM32H745

HPM6750

处理器架构

ARM Cortex-M7

ARM Cortex-M7+M4

双32位RISC-V核

CPU最高频率(MHz)

480

480+240

816+816

CoreMark跑分(官方数据)

2424

3224

9220


参考链接


  1. HPM6750EVKMINI用户手册(网盘资料夹中的文件,没有独立链接);

  2. HPM6750的CPU核心是晶心科技的D45,具体信息详见晶心D45介绍页:http://www.andestech.com/en/products-solutions/andescore-processors/riscv-d45/

  3. STM32H743产品介绍页:https://www.st.com/zh/microcontrollers-microprocessors/stm32h743-753.html

  4. STM32H745产品介绍页:https://www.st.com/zh/microcontrollers-microprocessors/stm32h745-755.html

  5. CoreMark项目首页:https://www.eembc.org/coremark/


 沁恒CH549开发板评测 


原贴地址:http://bbs.eeworld.com.cn/thread-1080782-1-1.html

第1篇 硬件电路对比分析
这次拿到的CH549评估板采用了底板+核心板的方式。   
        

这样的好处在于可以方便的评估同一系列不同型号的MCU,甚至可以评估不同系列的MCU。而外设不需改动。
 
底板上有单独的RS232电路和插座,满足了一些工业环境应用的需求。一般情况下是用不到的。
 
此外底板上还有TF卡槽和SPI接口的FLASHRAM,通过跳线来选择。   
 
电源供电电路
        
最初的CH554评估板是没有3.3V板上供电的,某些情况下使用起来有些不便,于是我在之前评估贴中给沁恒提了些建议(【CH554评测】第5篇 供电,评估板的板上3.3V供电讨论  http://bbs.eeworld.com.cn/thread-582706-1-1.html),沁恒在新的开发板中采纳了我的建议,增加了5V转3.3V的LDO,在板上提供3.3V电压。但在第二次CH554评估板评测时发现,虽然增加了板上3.3V供电,但是还是有一些不太合理的地方,虽然不影响使用评估,但总是有些隐患和不爽(【新版CH554评测】---1、电源改进测试评估    http://bbs.eeworld.com.cn/thread-642276-1-1.html )。这次拿到CH549评估板,惊喜地发现,这些问题好像都已经解决了,板上电源灯在上电开关打开前不会亮了。
 
USB防护。每个USB端口都增加了自恢复保险丝(下图中的F1、F3、F4、F5),这样,我在上面第二篇帖子里提到的“两个USB插座同时供电的情况,在极端情况下可能出现电源供电冲突,导致USB电源烧毁。”的隐患可以有效的得到防护。


另外,还增加了USB ESD防护电路,防止热拔插损坏或静电损坏。

容易忽略的地方:
      
CH549评估板板子上面有4个USB口,从左到右分别是:USB TYPE-C、USB TYPE-A(真USB口) 、 USB MicroB 、USB TYPE-A(串口转USB)。这里要注意的是USB TYPE-C的D+、D-线并没有连到MCU的 D+、D-信号引脚上(在上次CH554评板上是连接了的,不过没有焊USB TYPE-C插座)。另外要提醒大家的是,如果用USB下载程序,一定要选从左数第二个USB TYPE-A(真USB口) 或 USB MicroB,这两个才真正连接MCU的USB。我开始弄错了,弄成了最右边的USB TYPE-A(串口转USB),怎么也下载不了。
      
USB TYPE-C的VUSB可以通过MCU的P11引脚控制Q1(P-MOSFET)是否导通来决定是否通过USB TYPE-C给外部单板供电。为什么其他USB接口没这个控制电路呢?这是因为其他USB的VUSB电压都是从外部输入,并不输出。


       
感觉这个电源控制电路有些简单了。USB TYPE-C是可以由设备确定自己是电源输出还是电源输入。如果设备是电源输入,但外部电源又高于5V时怎么办?简单的直接输入是会烧板子的。后面具体用到时再详细分析吧。

第2篇 程序烧录工具评测
  
以前用过CH554的网友需要注意,由于沁恒新增加了MCU系列(CH549就是新的CH54X系列,CH554是原来的CH55X系列),而且估计内部的BOOTloader程序也做了升级,所以以前的下载程序需要更新到现在最新版本的下载程序WCHISPTool。下载程序WCHISPTool支持沁恒的的32位MCU-CH56X系列、CH57X系列和8位MCU-CH55X系列、CH54X系列。这样使用沁恒不同系列MCU时,只用安装下载程序WCHISPTool就可以了。
       
最新版本的下载程序WCHISPTool中集成了沁恒MCU在KEIL中的器件型号库,不像以前需要单独安装沁恒MCU在KEIL中的器件型号库(不过好像只支持了8位MCU-CH55X系列、CH54X系列,32位MCU-CH56X系列、CH57X系列没看到)。具体操作是:打开下载程序WCHISPTool,点击功能菜单下最下面一行的“添加WCH MCU到KEIL器件库”:


      
接下来再打开KEIL,此时在里面找芯片的型号就出现了WCH系列的各个型号了:


关于下载程序WCHISPTool的使用,我在之前“【CH554评测】第3篇 烧录,固件下载环境评测 http://bbs.eeworld.com.cn/thread-570795-1-1.html”帖子里说的很明白了,这里只强调一下在CH549评估板上的注意事项:

下载程序WCHISPTool支持USB下载和串口下载两种方式.
       
如果用USB下载程序,一定要选从左数第二个USB TYPE-A(真USB口) 或 USB MicroB,这两个才真正连接MCU的USB。最左边的USB TYPE-C的D+、D-线并没有连到MCU的 D+、D-信号引脚上,所以不能用来做USB下载。
       
如果用串口下载程序,一定要选最右边的USB TYPE-A,这个是通过CH340做的一个串口转USB(虚拟串口)。除了用作串口下载程序外,另一个重要作用是在调试时进行串口输出调试信息。
 
       
另外一个注意事项是,如果你的板上MCU(例如之前的CH554)的BOOTloader是2.30以前的版本,在使用新版本下载程序WCHISPTool时,需要点击"功能"菜单下倒数第二行的“bootload2.30之前版本”,使能该选项(前面打勾)。如果用的MCU内部的BOOTloader是2.30之后的版本,记得取消该选项。


        
最后给沁恒提个小建议:下载程序WCHISPTool退出时记住当前选择的MCU系列和型号,以及相关设置。不要每次打开都需要重头选择设置一遍。

第3篇 底层驱动软件对比评测-SPI接口LCD驱动

关于软件开发环境和相关设置,我在之前的帖子“【CH554评测】第2篇 编译,软件开发环境评测http://bbs.eeworld.com.cn/thread-567677-1-1.html”中说的很详细,感兴趣的网友可以看看。
        
在这里我想详细的对比一下CH554和CH549的底层驱动软件,以及相关调试。
        
因为后续会用到LCD显示,所以这一贴就先说说后续准备用到的SPI接口的LCD的驱动调试。
        
沁恒的SPI接口底层驱动程序写的还是不错的,我这次用的的SPI接口的LCD之前已经在其他芯片上驱动过了,已经有比较完善的驱动程序,只需把SPI接口的驱动程序移植一下就好。实际结果也是这样的。只是重新封装了一下SPI接口的写函数就可以了。
       
原来的SPI写函数:

  • static void SPIx_Write(uint16_t Value)
  • {
  • *(__IO uint8_t*)&SPI1 -> DR = (uint8_t)Value;
  • while(SPI1 -> SR & SPI_SR_BSY);
  • }
      
CH549上的SPI写函数:

  • unsigned char SPI_RW(unsigned char byte)
  • {
  • CH549SPIMasterWrite(byte);
  • return byte;
  • }

        
原来的SPI片选和指令和数据选择宏定义:

  • /* Chip Select macro definition */
  • #define LCD_CS_LOW() GPIOA -> BSRR = GPIO_BSRR_BR_0
  • #define LCD_CS_HIGH() GPIOA -> BSRR = GPIO_BSRR_BS_0

  • /* Set WRX High to send data */
  • #define LCD_CD_LOW() GPIOA -> BSRR = GPIO_BSRR_BR_1
  • #define LCD_CD_HIGH() GPIOA -> BSRR = GPIO_BSRR_BS_1
      
CH549上的SPI片选和指令和数据选择宏定义:

  • sbit LCD_CD =P1^3;
  • sbit LCD_CS =P1^4;
       
主程序部分,注意做好SPI接口模式和时钟设置:

  • // 配置SPI //
  • SPIMasterModeSet(3); //SPI主机模式设置,模式3
  • SPI_CK_SET(2); //试2分频
       
再调用LCD的初始化函数LCD_Init()(这个都不需要修改);

  • LCD_Init();
       
移植完成。
       
整个过程下来,感觉沁恒CH549的SPI底层驱动写得很好,移植起来很方便。只是以前芯片是ARM Cortex-M3内核,现在改为增强51内核,有些小细节需要注意一下。
       
首先,CH549虽然是增强51内核,但毕竟还是51内核。直接可用(直接寻址)的RAM只有128字节,不像许多ARM Cortex-M3内核的MCU,最少也有个4K字节 RAM。好在CH549毕竟是增强51内核,RAM也扩展了2K字节片内 xRAM,不过不能直接寻址,要用的话还需做些设置。

       
上图提示地址空间溢出。仔细看可以发现 data = 190.5,而xdata = 0。   
在Keil的项目设置中如下图选择使用扩展的2K字节片内 xRAM:

再编译就可以发现:
            
此外就是当LCD显示用的字库太大时,也会出现下面错误报警:

     
此时,把字库空间定义到程序FLASHRAM中就好了,毕竟有64K不是!
原来的代码:

  • const unsigned char asc2_1206[95][12]={
  • {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
  • 。。。。。。
  • };

加关键字 code 

  • code const unsigned char asc2_1206[95][12]={
  • {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
  • 。。。。。。。。。。
  • };

这样,移植就顺利地完成了。看一下效果吧:  


这里还要注意一下,不同的MCU配合LCD时,提取字模的顺序可能不同。在CH549上配合ILI9341驱动的LCD,顺序如下图:

接下来显示一下我做的初步测试界面:
白色底:


黑色底:


实际效果感觉黑色更好一些。
感觉整个过程要数字模最花时间了。
 
第4篇 底层驱动软件评测-触摸按键驱动
 
在上一篇帖子《底层驱动软件对比评测-SPI接口LCD驱动》中我完成了SPI接口的LCD的显示。因为后续DIY项目中要用到按键,以便对相关参数进行设置,以及人机交互时输入。正好CH549评估板上有4个触摸按键可以用到,所以接下来评测一下沁恒CH549评估板的触摸按键驱动。
      
CH549支持16通道触摸按键,CH0~CH15 分别对应引脚 P1.0~P1.7 和 P0.0~P0.7。目前CH549评估板中使用了的4个触摸按键对应CH549引脚如下图,使用了 P0.0~P0.3引脚,对应着CH8~CH11通道。


沁恒CH549的底层驱动做的很好,很方便调用。我们一起来看一下触摸按键是怎么用起来的。
       
首先是把触摸按键TouchKey的底层驱动代码的头文件TouchKey.H加入自己的主程序中:

  • #include "..\Interface\TouchKey\TouchKey.H"
       
接下来在主程序中先定义后面触摸按键要用到的变量。

  • #pragma NOAREGS
  • UINT16 PowerValue[16]; //保存触摸按键上电未触摸值
  • UINT8 PressedKey;
  • volatile UINT16 Press_Flag = 0; //按下标志位
  • UINT8C CPW_Table[16] = { 30,30,30,30, 30,30,30,30, //与板间电容有关的参数,分别对应每个按键
  • 30,30,30,30, 30,30,30,30,} };

     
由于要比较检测触摸前后的检测值的大小,所以需要一个绝对值函数(沁恒例子程序中已经有了,拿来用就可以了)

  • /*******************************************************************************
  • * Function Name : ABS
  • * Description : 求两个数差值的绝对值
  • * Input : a,b
  • * Output : None
  • * Return : 差值绝对值
  • *******************************************************************************/
  • UINT16 ABS(UINT16 a,UINT16 b)
  • {
  • if(a>b)
  • {
  • return (a-b);
  • }
  • else
  • {
  • return (b-a);
  • }
  • }
     
接下来在主程序中定义一些存储通道数、按键按下标志、触摸通道检测值、比较差值等临时变量,同时对触摸按键做初始化,检测按键未触摸时的初值并存储,以便后续比较。

  • UINT8 ch;
  • UINT16 value;
  • UINT16 err;
  • //触摸按键初始化
  • TouchKey_Init();
  • Press_Flag = 0; //无按键按下
  • /* 获取按键初值 */
  • for(ch = 8; ch!=12; ch++)
  • {
  • PowerValue[ch] = TouchKeySelect( ch,CPW_Table[ch] );
  • }

接下来就是主循环或定时中断里的按键检测程序了:

  • // 按键检测 //
  • for(ch = 8; ch!=12; ch++)
  • {
  • value = TouchKeySelect( ch,CPW_Table[ch] );
  • err = ABS(PowerValue[ch],value);
  • if( err > DOWM_THRESHOLD_VALUE ) //差值大于阈值,认为按下
  • {
  • if((Press_Flag & (1<0) //说明是第一次按下
  • {
  • // 按键按下处理程序 //
  • }
  • Press_Flag |= (1<

  • }
  • else if( err < UP_THRESHOLD_VALUE ) //说明抬起或者未按下
  • {
  • if(Press_Flag & (1<//刚抬起
  • {
  • Press_Flag &= ~(1<
  • // 按键释放处理程序 //
  • PressedKey = ch;
  • }
  • }
  • }
       
再接下来就是主循环或定时中断里的按键处理程序了:

  • // 按键处理 //
  • switch (PressedKey)
  • {
  • case 8:
  • LCD_ShowNum(145,70,1,16,0);
  • PressedKey=0;
  • break;
  • case 9:
  • LCD_ShowNum(145,70,2,16,0);
  • PressedKey=0;
  • break;
  • case 10:
  • LCD_ShowNum(145,70,3,16,0);
  • PressedKey=0;
  • break;
  • case 11:
  • LCD_ShowNum(145,70,4,16,0);
  • PressedKey=0;
  • break;
  • }
      
至于按键效果,比如说长按、短按、按下、抬起等就要靠自己编程了。
      
总的来说,沁恒CH549的触摸按键驱动还是让开发人员很容易上手的,大家只需专注在自己要实现的功能上就好了。
      
最后,看一下触摸按键实现的触摸效果吧:


· END ·










电子工程世界 关注EEWORLD电子工程世界,即时参与讨论电子工程世界最火话题,抢先知晓电子工程业界资讯。
评论
  • Supernode与艾迈斯欧司朗携手,通过Belago红外LED实现精准扫地机器人避障;得益于Belago出色的红外补光功能,使扫地机器人能够大大提升其识别物体的能力,实现精准避障;Belago点阵照明器采用迷你封装,兼容标准无铅回流工艺,适用于各种3D传感平台,包括移动设备、物联网设备和机器人。全球领先的光学解决方案供应商艾迈斯欧司朗(瑞士证券交易所股票代码:AMS)近日宣布,与国内领先的多行业三维视觉方案提供商超节点创新科技(Supernode)双方联合推出采用艾迈斯欧司朗先进Belago红
    艾迈斯欧司朗 2024-12-20 18:55 83浏览
  • 随着工业自动化和智能化的发展,电机控制系统正向更高精度、更快响应和更高稳定性的方向发展。高速光耦作为一种电气隔离与信号传输的核心器件,在现代电机控制中扮演着至关重要的角色。本文将详细介绍高速光耦在电机控制中的应用优势及其在实际工控系统中的重要性。高速光耦的基本原理及优势高速光耦是一种光电耦合器件,通过光信号传递电信号,实现输入输出端的电气隔离。这种隔离可以有效保护电路免受高压、电流浪涌等干扰。相比传统的光耦,高速光耦具备更快的响应速度,通常可以达到几百纳秒到几微秒级别的传输延迟。电气隔离:高速光
    晶台光耦 2024-12-20 10:18 146浏览
  •         不卖关子先说感受,真本书真是相见恨晚啊。字面意思,见到太晚了,我刚毕业或者刚做电子行业就应该接触到这本书的。我自己跌跌撞撞那么多年走了多少弯路,掉过多少坑,都是血泪史啊,要是提前能看到这本书很多弯路很多坑都是可以避免的,可惜这本书是今年出的,羡慕现在的年轻人能有这么丰富完善的资料可以学习,想当年我纯靠百度和论坛搜索、求助啊,连个正经师傅都没有,从软件安装到一步一布操作纯靠自己瞎摸索,然后就是搜索各种教程视频,说出来都是泪啊。  &
    DrouSherry 2024-12-19 20:00 112浏览
  • 汽车行业的变革正愈演愈烈,由交通工具到“第三生活空间”。业内逐渐凝聚共识:汽车的下半场在于智能化。而智能化的核心在于集成先进的传感器,以实现高等级的智能驾驶乃至自动驾驶,以及更个性、舒适、交互体验更优的智能座舱。毕马威中国《聚焦电动化下半场 智能座舱白皮书》数据指出,2026年中国智能座舱市场规模将达到2127亿元,5年复合增长率超过17%。2022年到2026年,智能座舱渗透率将从59%上升至82%。近日,在SENSOR CHINA与琻捷电子联合举办的“汽车传感系列交流会-智能传感专场”上,艾
    艾迈斯欧司朗 2024-12-20 19:45 107浏览
  • 光耦固态继电器(SSR)作为现代电子控制系统中不可或缺的关键组件,正逐步取代传统机械继电器。通过利用光耦合技术,SSR不仅能够提供更高的可靠性,还能适应更加复杂和严苛的应用环境。在本文中,我们将深入探讨光耦固态继电器的工作原理、优势、挑战以及未来发展趋势。光耦固态继电器:如何工作并打破传统继电器的局限?光耦固态继电器通过光电隔离技术,实现输入信号与负载之间的电气隔离。其工作原理包括三个关键步骤:光激活:LED接收输入电流并发出与其成比例的光信号。光传输:光电传感器(如光电二极管或光电晶体管)接收
    腾恩科技-彭工 2024-12-20 16:30 55浏览
  • //```c #include "..\..\comm\AI8051U.h"  // 包含头文件,定义了硬件寄存器和常量 #include "stdio.h"              // 标准输入输出库 #include "intrins.h"         &n
    丙丁先生 2024-12-20 10:18 84浏览
  • 耳机虽看似一个简单的设备,但不仅只是听音乐功能,它已经成为日常生活和专业领域中不可或缺的一部分。从个人娱乐到专业录音,再到公共和私人通讯,耳机的使用无处不在。使用高质量的耳机不仅可以提供优良的声音体验,还能在长时间使用中保护使用者听力健康。耳机产品的质量,除了验证产品是否符合法规标准,也能透过全面性的测试和认证过程,确保耳机在各方面:从音质到耐用性,再到用户舒适度,都能达到或超越行业标准。这不仅保护了消费者的投资,也提升了该公司在整个行业的产品质量和信誉!客户面临到的各种困难一家耳机制造商想要透
    百佳泰测试实验室 2024-12-20 10:37 163浏览
  • 百佳泰特为您整理2024年12月各大Logo的最新规格信息。——————————USB▶ 百佳泰获授权进行 USB Active Cable 认证。▶ 所有符合 USB PD 3.2 标准的产品都有资格获得USB-IF 认证——————————Bluetooth®▶ Remote UPF Testing针对所有低功耗音频(LE Audio)和网格(Mesh)规范的远程互操作性测试已开放,蓝牙会员可使用该测试,这是随时测试产品的又一绝佳途径。——————————PCI Express▶ 2025年
    百佳泰测试实验室 2024-12-20 10:33 118浏览
  • 汽车驾驶员监控系统又称DMS,是一种集中在车辆中的技术,用于实时跟踪和评估驾驶员状态及驾驶行为。随着汽车产业智能化转型,整合AI技术的DMS逐渐成为主流,AI模型通过大量数据进行持续训练,使得驾驶监控更加高效和精准。 驾驶员监测系统主要通过传感器、摄像头收集驾驶员的面部图像,定位头部姿势、人脸特征及行为特征,并通过各种异常驾驶行为检测模型运算来识别驾驶员的当前状态。如果出现任何异常驾驶行为(如疲劳,分心,抽烟,接打电话,无安全带等),将发出声音及视觉警报。此外,驾驶员的行为数据会被记录
    启扬ARM嵌入式 2024-12-20 09:14 99浏览
  • 光耦合器,也称为光隔离器,是用于电气隔离和信号传输的多功能组件。其应用之一是测量电路中的电压。本文介绍了如何利用光耦合器进行电压测量,阐明了其操作和实际用途。使用光耦合器进行电压测量的工作原理使用光耦合器进行电压测量依赖于其在通过光传输信号的同时隔离输入和输出电路的能力。该过程包括:连接到电压源光耦合器连接在电压源上。输入电压施加到光耦合器的LED,LED发出的光与施加的电压成比例。光电二极管响应LED发出的光由输出侧的光电二极管或光电晶体管检测。随着LED亮度的变化,光电二极管的电阻相应减小,
    腾恩科技-彭工 2024-12-20 16:31 73浏览
  • ALINX 正式发布 AMD Virtex UltraScale+ 系列 FPGA PCIe 3.0 综合开发平台 AXVU13P!这款搭载 AMD 16nm 工艺 XCVU13P 芯片的高性能开发验证平台,凭借卓越的计算能力和灵活的扩展性,专为应对复杂应用场景和高带宽需求而设计,助力技术开发者加速产品创新与部署。随着 5G、人工智能和高性能计算等领域的迅猛发展,各行业对计算能力、灵活性和高速数据传输的需求持续攀升。FPGA 凭借其高度可编程性和实时并行处理能力,已成为解决行业痛点的关
    ALINX 2024-12-20 17:44 91浏览
  • 国产数字隔离器已成为现代电子产品中的关键部件,以增强的性能和可靠性取代了传统的光耦合器。这些隔离器广泛应用于医疗设备、汽车电子、工业自动化和其他需要强大信号隔离的领域。准确测试这些设备是确保其质量和性能的基本步骤。如何测试数字隔离器测试数字隔离器需要精度和正确的工具集来评估其在各种条件下的功能和性能。以下设备对于这项任务至关重要:示波器:用于可视化信号波形并测量时序特性,如传播延迟、上升时间和下降时间。允许验证输入输出信号的完整性。频谱分析仪:测量电磁干扰(EMI)和其他频域特性。有助于识别信号
    克里雅半导体科技 2024-12-20 16:35 71浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦