01
【米尔MYD-YA15XC-T评测】+开箱上电测试
STM32MP1 是意法半导体推出的首款 MPU,其内部集成了 1-2 颗主频 650MHz 的 Arm Cortex-A7 应用处理器内核和 1 颗运行频率 209MHz 的高性能 Arm Cortex-M4 微 控制器内核。两种内核之间分工明确、配合默契,Cortex-A7 内核专用于开源操作系统, Cortex-M4 内核则专用于实时及低功耗任务处理。这一灵活的异构计算架构在单一芯片 上执行快速数据处理和实时任务,可以实现最高的能效。MYC-YA15XC-T 核心板基于 STM32MP1 系列处理器研制,完美继承了该处理器高 性能、低功耗的优点,且拥有良好的软件开发环境,Cortex-A7 内核支持开源操作系统 Linux,Cortex-M4 内核完美沿用现有的 STM32 MCU 生态系统,有助于开发者轻松实 现各类开发应用。
这次的板卡包装比米尔以往的板卡包装大得多。
包装很用心:含有指导手册
电源适配器赠送了不同国家的插口适配器:
一如既往的黑色沉金
板卡背面
赠送了塑料螺柱
上电效果
接下来就需要下载资料进行交叉编译器和SDK环境的安装了。
02
【米尔MYD-YA15XC-T评测】+ USB UVC摄像头测试
UVC全称为USB Video Class,即:USB视频类,是一种为USB视频捕获设备定义的协议标准。是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,已成为USB org标准之一。米尔MYD-YA15XC-T开发板内核已经开启了UVC驱动,因此插入USB UVC摄像头后可以看到识别信息:
V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。v4l2-ctl是用户空间一组用于测试,配置和使用整个相机子系统的工具,包括外部相机传感器和相机接口。v4l2-ctl 是最有用的实用工具。
v4l2-ctl基本常用的命令如下:
使用 --list-devices 选项列出所有可用的视频设备
v4l2-ctl --list-devices
获得有关特定设备的信息,加上 -D 选项:
v4l2-ctl -d /dev/video1 -D
获取受支持的参数设置接口列表
v4l2-ctl -L -d /dev/video1
其中可以通过 --set-ctrl 选项更改控制值,如:
v4l2-ctl --set-ctrl test_pattern=1
控制值可以动态更改。
设置像素格式,分辨率和帧率,使用 --list-formats-ext 选项可获取受支持的像素格式、分辨率和帧速率:
v4l2-ctl --list-formats-ext -d /dev/video1
查看当前摄像头支持的视频压缩格式
v4l2-ctl -d /dev/video1 --list-formats
查看摄像头所有参数
v4l2-ctl -d /dev/video1 --all
查看摄像头所支持的分辨率
v4l2-ctl --list-framesizes=MJPG -d /dev/video1
GStreamer是用于创建流媒体应用程序的框架,开发板集成了gstreamer系列命令。
单次拍照
gst-launch-1.0 v4l2src device=/dev/video1 num-buffers=1 ! image/jpeg,width=1920,height=1080 ! filesink location=/tmp/capture.jpg
使用 gst-typefind 命令检查图片分辨率
gst-typefind-1.0 /tmp/capture.jpg
初步完成了usb摄像头图像的采集,后续将研究简单的图像处理,视频采集,体验米尔MYD-YA15XC-T开发板的多媒体处理性能。
03
【米尔MYD-YA15XC-T评测】+使用mjpg-streamer搭建网络摄像头
0、mjpg-streamer介绍
mjpg-streamer是一款免费基于IP地址的视频流服务器,它的输入插件从摄像头读取视频数据,这个输入插件产生视频数据并将视频数据复制到内存中,它有多个输出插件将这些视频数据经过处理,其中最重要的输出插件是网站服务器插件,它将视频数据传送到用户浏览器中,mjpg-streamer的工作就是将其中的一个输入插件和多个输出插件绑定在一起,所有的工作都是通过它的各个插件完成的。mjpg-streamer各个文件如下:
(1)input_testpicture.so。这是一个图像测试插件,它将预设好的图像编译成一个头文件,可以在没有摄像头的情况下传输图像,从而方便调试程序。
(2)input_uvc.so。此文件调用USB摄像头驱动程序V4L2,从摄像头读取视频数据。
(3)input_control.so。这个文件实现对摄像头转动的控制接口。
(4)output_http.so。这是一个功能齐全的网站服务器,它不仅可以从单一文件夹中处理文件,还可以执行一定的命令,它可以从输入插件中处理一幅图像,也可以将输入插件的视频文件根据现有M-JPEG标准以HTTP视频数据服务流形式输出。
(5)output_file.so。这个插件的功能是将输入插件的JPEG图像存储到特定的文件夹下,它可以用来抓取图像。
mjpg-streamer优点是对RAM和CPU的消耗比较少,可以快速的传输jpeg流。
一、米尔SDK安装
在终端执行命令:
. /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
将设置好交叉编译工具链,常用的环境变量如CC。
二、libjpeg交叉编译
有些UVC摄像头只支持输出YUV原始图像,此时mjpg-streamer会将其转码为mjpeg,然后进行传输,此时会对CPU的消耗以及实时流的流畅度有影响,特别是分辨率比较大的时候。
因此mjpg-streamer 依赖 libjpeg, libjpeg下载地址:
http://www.ijg.org/
目前最新版本是v9d。libjpeg交叉编译命令:
make clean
. /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
./configure --host=arm-ostl-linux-musl --prefix=$HOME/stm32mp1/build_jpegsrc.v9d
make -j6
make install
写成编译脚本:
编译成功后得到动态库:
米尔MYD-YA15XC-T内核已经配置好了UVC和USB 驱动,因此无需重新配置编译内核。
三、mjpg-streamer交叉编译
mjpeg-streamer下载
https://sourceforge.net/p/mjpg-streamer/code/HEAD/tree/
目前最新版本为r182,本次测试使用版本r63。
在ubuntu可通过2种命令下载代码:
svn checkout svn://svn.code.sf.net/p/mjpg-streamer/code/ mjpg-streamer-code
或者
svn checkout https://svn.code.sf.net/p/mjpg-streamer/code/ mjpg-streamer-code
在编译前需要对源码的Makefile做以下修改:
将源码的所有Makefile(包含其子目录)中的编译工具链修改为米尔开发板的编译工具链,由于在终端执行命令. /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi会设置好交叉编译相关的环境变量,因此去掉Makefile文件中的CC 即可。
本次测试只需要 input_uvc.so 和 output_http.so,在主目录中的Makefile里面只选择这两个,其它的去掉。
插件input_uvc依赖jpeg,因此修改plugins/input_uvc/Makefile文件,指定jpeg库头文件路径:
CFLAGS += -I$HOME/stm32mp1/build_jpegsrc.v9d/include
指定链接jpeg动态库路径:
-ljpeg -L$HOME/stm32mp1/build_jpegsrc.v9d/lib
编译成功之后,在lib目录会生成如下输入、输出动态库文件:
在mjpg-streamer-r63目录生成mjpg-streamer可执行文件:
查看一下mjpg-streamer文件信息,ARM 32位可执行程序:
将上面生成的动态库文件都复制到lib目录:
然后将lib目录里面的文件和mjpg-streamer可执行文件一起打包为app_and_lib.tar.xz,使用SCP命令将它复制到开发板home目录:
解压:
得到如下文件,使用chmod +x mjpg-streamer命令为mjpg-streamer添加可执行权限:
四、测试
插入USB UVC摄像头,正确识别到了视频设备:
手动指定一下库路径:
export LD_LIBRARY_PATH=./
执行下列命令启动mjpg-streamer:
./mjpg_streamer -i "input_uvc.so -r 1920x1080" -o "output_http.so -w ./www"
如果是输出YUV格式的UVC摄像头,则执行命令:
./mjpg_streamer -i "input_uvc.so -y -r 1920x1080" -o "output_http.so -w ./www
查看图片,在浏览器输入:
http://192.168.1.136:8080/?action=snapshot
查看视频,在浏览器输入:
http://192.168.1.136:8080/?action=stream
其中192.168.1.136为开发板IP地址。
附录:
MJPG简介:MJPEG全名为 "Motion Joint Photographic Experts Group",是一种视频编码格式,Motion JPEG技术常用于闭合电路的电视摄像机的模拟视频信号“翻译”成视频流,并存储在硬盘上。典型的应用如数字视频记录器等。MJPEG不像MPEG,不使用帧间编码,因此用一个非线性编辑器就很容易编辑。MJPEG的压缩算法与MPEG一脉相承,功能很强大,能发送高质图片,生成完全动画视频等;从另一个角度说,在某些条件下,MJPEG也许是效率最低的编码/解码器之一。MJPEG与MJPG的区别:mjpeg是视频,就是由系列jpg图片组成的视频。
还有一款MJPG视频流服务开源软件uStreamer:
µStreamer is a lightweight and very quick server to stream MJPG video from any V4L2 device to the net.
https://github.com/pikvm/ustreamer
04
【米尔MYD-YA15XC-T评测】+ 使用ffmpeg音视频处理软件处理多媒体文件(上)
在多媒体音视频处理应用中一个常见的需求就是给视频或者图像叠加水印文字、图片logo等。以下是ffmpeg的介绍:
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。
FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。这个项目最早由Fabrice Bellard发起,2004年至2015年间由Michael Niedermayer主要负责维护。许多FFmpeg的开发人员都来自MPlayer项目,而且当前FFmpeg也是放在MPlayer项目组的服务器上。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。[1] FFmpeg编码库可以使用GPU加速。
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它包括了领先的音/视频编码库libavcodec等。
libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构
和读取音视频帧等功能;
libavcodec:用于各种类型声音/图像编解码;
libavutil:包含一些公共的工具函数;
libswscale:用于视频场景比例缩放、色彩映射转换;
libpostproc:用于后期效果处理;
ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;
ffsever:一个 HTTP 多媒体即时广播串流服务器;
ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;
本次就打算使用ffmpeg进行中英文特殊字符水印、图片水印的叠加测试,首先在电脑端实现功能,然后交叉编译移植到米尔MYD-YA15XC-T平台,顺便定性对比下电脑和米尔MYD-YA15XC-T的多媒体处理性能。
在编译ffmpeg前需要先编译字体处理软件freetype,这里使用了freetype-2.10.2版本。
编译命令如下:
make clean
./configure --prefix=/opt/nrjd/nrjd-olms-0201/apps/aaa/comm/pc/freetype --with-zlib=no --with-png=no --enable-static=no
make -j12
make install
make clean
然后编译ffmpeg,指定freetype库和头文件路径:
make clean
./configure --enable-shared --disable-static --target-os=linux --enable-libx264 --enable-gpl --enable-libfreetype --enable-filter=drawtext --extra-cflags="-I/home/qldeng/ffmpeg_watermark_test/ffmpeg_test_pc/comm/pc/freetype/include/freetype2" --extra-ldflags="-L/home/qldeng/ffmpeg_watermark_test/ffmpeg_test_pc/comm/pc/freetype/lib" --prefix=/home/qldeng/ffmpeg_watermark_test/ffmpeg_test_pc/comm/pc/ffmpeg
make -j12
make install
make clean
项目文件夹内容如下:
qldeng@ubuntu:~/ffmpeg_watermark_test/ffmpeg_test_pc$ tree -L 2
.
├── build
├── CMakeLists.txt
├── comm
│ ├── ffmpeg-4.4
│ ├── freetype-2.10.2
│ └── pc
├── in.jpg
├── logo.png
├── main.cpp
├── out.jpg
├── readme.txt
├── SourceHanSansCN-Bold.ttf
├── toolchain.cmake
└── 捕获.PNG
5 directories, 9 files
测试项目使用CMake构建,CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.17)
#输出可执行文件的文件名
set(ARTIFACT_NAME myapp)
project(${ARTIFACT_NAME})
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 99)
set(MYLIBS_PATH /opt/nrjd/nrjd-olms-0201/apps/ffmpeg_test_pc/comm/pc)
set(INC_DIR
${PROJECT_SOURCE_DIR}
${MYLIBS_PATH}/freetype/include/freetype2
${MYLIBS_PATH}/ffmpeg/include
)
set(LINK_DIR
${MYLIBS_PATH}/freetype/lib
${MYLIBS_PATH}/ffmpeg/lib
)
include_directories(${INC_DIR})
link_directories(${LINK_DIR})
aux_source_directory(${PROJECT_SOURCE_DIR} SOURCE_FILES)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
link_libraries(
freetype
avcodec
avdevice
avfilter
avformat
avutil
postproc
swresample
swscale
)
add_executable(${ARTIFACT_NAME} ${SOURCE_FILES})
target_link_libraries(${ARTIFACT_NAME}
freetype
avcodec
avdevice
avfilter
avformat
avutil
postproc
swresample
swscale
pthread)
main.cpp是测试代码,直接使用system函数调用ffmpeg即可,需要注意的是特殊符号需要转义:
cmd = "ffmpeg -loglevel quiet -i " + string(dest_path) + " -vf drawtext=\"fontcolor=white:fontfile=SourceHanSansCN-Bold.ttf:fontsize=40:x=w-tw-th/2:y=h-th-th::alpha=0.65:box=1:boxcolor=0x000000:text='" + string(s5) + "\" -y " + string(dest_path);
system(cmd.c_str());
float battery_voltage = 12.345;
float battery_current = 0.678;
float device_temperature = 37.2;
s1 = "日期\\:" + string(str) + "\"";
memset(str, 0, sizeof(str));
sprintf(str, "处理器\\:%0.1f\℃ 电源\\:%0.1fV %0.2fA CSQ\\:%d%%", device_temperature, battery_voltage,battery_current,64);
s2 = string(str);
toolchain.cmake内容:
set(CMAKE_CXX_COMPILER g++)
set(CMAKE_C_COMPILER gcc)
进入build目录,打开终端执行以下命令进行编译:
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake ..
make
可以看到图片上叠加了预期的水印内容,处理时间183毫秒:
下一步就是眼睛就如何正确交叉编译ffmpeg到米尔MYD-YA15XC-T平台了,周六晚上折腾了半天,虽然编译出了命令行工具个库,但是放到米尔MYD-YA15XC-T里面无法运行,总是提示命令找不到,还得仔细研究下。
05
【米尔MYD-YA15XC-T评测】+ 使用STM32CubeProgrammer软件更新系统固件
米尔提供的资料01_Documents\User_Manual\Chinese\软件手册\MYD-YA15XC-T_Linux软件开发指南V1.0.0.pdf第四节描述了如何更新系统固件。
MYC-YA15XC-T 开发板采用的是 ST 公司的 STM32MP1 系列微处理器,原生支持使用ST推出的STM32CubeProgrammer软件更新系统固件,只需要连接一根USB线通信即可完成烧录固件,快速方便。按照米尔提供的资料,实际使用中遇到些小问题。
首先选择启动模式,将拨码开关拨到 Download 模式(B2/B1/B0 : 0/0/0)。将开发板的 J5 USB接口与电脑连接,插入电源适配器。如下图所示:
这时候可以在电脑发现USB DFU设备了:
打开 STM32CubeProgrammer 软件,选择 USB烧录方式:
点击 connect按钮连接,连接正常就会显示处理器的信息,如下图:
根据本开发板的处理器选择的烧录包位于目录myir-image-core\256N256D。然后点击Open File,选择nand的 TSV 文件myir-image-core\256N256D\myir-image-core\flashlayout_myir-image-core\trusted\FlashLayout_nand-2-256_stm32mp15xc-ya151c-256d-t-trusted.tsv,如下图所示:
但是按照这个步骤烧录出错了:
根据错误原因提示,将目录myir-image-core\256N256D\myir-image-core下的arm-trusted-firmware、bootloader、myir-image-core-openstlinux-eglfs-myir-ya151c-t_nand_2_256_multivolume.ubi复制到目录myir-image-core\256N256D\myir-image-core\flashlayout_myir-image-core\trusted下即可:
然后就可以烧录成功了:
06
【米尔MYD-YA15XC-T评测】+ epoll串口通信
串口是嵌入式系统中最常用的接口之一,系统终端通常都是串口。除了终端功能之外,实际应用中,Linux系统也经常通过串口与其它设备进行通信和数据传递,如232、485等接口传感器通常底层都是串口。在Linux下的串口编程不像微控制器上那么简单。本帖以米尔MYD-YA15XC-T为例,实现串口基本操作、串口属性设置、如串口数据读写。米尔MYD-YA15XC-T开发板扩展排针接口引出了串口3供我们使用。位置如下图所示:
终端使用ls /dev/tty*命令查看设备文件:
Linux的串口表现为设备文件,Linux的串口设备文件命名一般为/dev/tty*,米尔MYD-YA15XC-T开发板串口设备命名为/dev/ttySTM*。
可以看到串口3对应的设备文件是/dev/ttySTM3,使用一个TTL串口调试助手连接到串口3:
首先使用echo命令测试发送数据,可以看到串口3收到了数据:
接下来通过编写代码的方式实现串口的数据收发。
首先配置网络,因为后面拷贝文件到开发板需要用到。
在编写Linux串口的C程序代码时,需要包含termios.h头文件。主要流程为:
此外,如果对串口属性进行设置需要包含头文件,该文件包含了POSIX终端属性描述结构struct termios。
完整的代码如下所示:
#include
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#include
class MySerial
{
private:
int fd = -1;
string serial_dev;
int serial_baudrate;
string serial_parity;
int serial_databits;
int serial_stopbits;
void* (*rx_cb_fun)(void*);
int epfd;
struct epoll_event event;// 告诉内核要监听什么事件
struct epoll_event wait_event;
public:
MySerial(string serial_dev, int serial_baudrate, string serial_parity, int serial_databits, int serial_stopbits,void* (*rx_cb_fun)(void*))
{
this-> serial_dev= serial_dev;
this-> serial_baudrate= serial_baudrate;
this-> serial_parity= serial_parity;
this-> serial_databits= serial_databits;
this-> serial_stopbits= serial_stopbits;
this->rx_cb_fun = rx_cb_fun;
epfd = epoll_create(10); // 创建一个 epoll 的句柄,参数要大于 0, 没有太大意义
if( -1 == epfd )
{
perror ("epoll_create");
}
};
int OpenSerial()
{
struct termios tios;
int speed;
fd = open(serial_dev.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
if (fd < 0)
{
perror("open");
}
memset(&tios, 0, sizeof(struct termios));
switch (serial_baudrate)
{
case 50:
speed = B50;
break;
case 75:
speed = B75;
break;
case 110:
speed = B110;
break;
case 134:
speed = B134;
break;
case 150:
speed = B150;
break;
case 200:
speed = B200;
break;
case 300:
speed = B300;
break;
case 600:
speed = B600;
break;
case 1200:
speed = B1200;
break;
case 1800:
speed = B1800;
break;
case 2400:
speed = B2400;
break;
case 4800:
speed = B4800;
break;
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
case 57600:
speed = B57600;
break;
case 115200:
speed = B115200;
break;
case 230400:
speed = B230400;
break;
case 460800:
speed = B460800;
break;
case 500000:
speed = B500000;
break;
case 576000:
speed = B576000;
break;
case 921600:
speed = B921600;
break;
case 1000000:
speed = B1000000;
break;
case 1152000:
speed = B1152000;
break;
case 1500000:
speed = B1500000;
break;
case 2000000:
speed = B2000000;
break;
case 2500000:
speed = B2500000;
break;
case 3000000:
speed = B3000000;
break;
case 3500000:
speed = B3500000;
break;
case 4000000:
speed = B4000000;
break;
default:
speed = B9600;
break;
}
if ((cfsetispeed(&tios, speed) < 0) || (cfsetospeed(&tios, speed) < 0))
{
close(fd);
fd = -1;
perror("cfsetispeed or cfsetospeed");
}
tios.c_cflag |= (CREAD | CLOCAL);
tios.c_cflag &= ~CSIZE;
switch (serial_databits)
{
case 5:
tios.c_cflag |= CS5;
break;
case 6:
tios.c_cflag |= CS6;
break;
case 7:
tios.c_cflag |= CS7;
break;
case 8:
default:
tios.c_cflag |= CS8;
break;
}
if (serial_stopbits == 1)
{
tios.c_cflag &= ~CSTOPB;
}
else
{
tios.c_cflag |= CSTOPB;
}
if (serial_parity == "none")
{
tios.c_cflag &= ~PARENB;
}
else if (serial_parity == "even")
{
tios.c_cflag |= PARENB;
tios.c_cflag &= ~PARODD;
}
else if (serial_parity == "odd")
{
tios.c_cflag |= PARENB;
tios.c_cflag |= PARODD;
}
tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
if (serial_parity == "none")
{
tios.c_iflag &= ~INPCK;
}
else
{
tios.c_iflag |= INPCK;
}
tios.c_iflag &= ~(IXON | IXOFF | IXANY);
tios.c_oflag &= ~OPOST;
tios.c_cc[VMIN] = 0;
tios.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &tios) < 0)
{
close(fd);
fd = -1;
perror("tcsetattr");
}
event.data.fd = fd; // 串口描述符
event.events = EPOLLIN; // 表示对应的文件描述符可以读
// 事件注册函数,将描述符fd加入监听事件
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
if(-1 == ret)
{
perror("epoll_ctl");
}
return fd;
}
void loop()
{
int ret;
// 监视并等待多文件(串口)描述符的属性变化(是否可读)
// 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
ret = epoll_wait(epfd, &wait_event, 2, -1);
if(ret == -1) // 出错
{
close(epfd);
perror("epoll");
}
else if(ret > 0) // 准备就绪的文件描述符
{
//char buf[100] = {0};
if((fd == wait_event.data.fd) && (EPOLLIN == wait_event.events & EPOLLIN))
{
rx_cb_fun(&fd);
}
}
else if(0 == ret)
{
printf("time out\n");
}
}
~MySerial()
{
close(fd);
close(epfd);
}
};
void *SerialRxCB(void* arg)
{
int fd = *(int*)arg;
char buf;
read(fd, &buf, 1);
printf("%c\n", buf);
return NULL;
}
void thread_task(void* arg)
{
int fd = *(int*)arg;
while(1)
{
char tx_buf[]="hello eeworld & mier";
this_thread::seconds(1));
write (fd, tx_buf, sizeof(tx_buf));
}
}
int main(int argc,char* argv[])
{
MySerial *ps = new MySerial("/dev/ttySTM3",9600,"none",8,1,SerialRxCB);
int ret = ps->OpenSerial();
if(ret < 0)
{
perror("open serial");
return -1;
}
thread t(thread_task,&ret);
t.detach();
while(1)
{
ps->loop();
}
return 0;
}
程序中使用到了epoll,类似单片机里面的中断,可以实现异步数据的接收,程序中还使用到了c++11的线程库,米尔提供的交叉编译器版本很新,完全支持c++11的各种特性。首先在虚拟机终端执行命令,这样交叉编译环境变量就在该终端生效了:
. /opt/st/myir/3.1-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
可以看到CXX环境变量:
然后执行命令进行程序的编译:
$CXX stm32mp1_uart_test.cpp -o uart_test -lpthread
c++11的线程库底层使用了pthread实现,因此需要加链接参数-lpthread
交叉编译完成后,使用scp命令将程序拷贝到开发板上执行:
scp qldeng@192.168.1.131:/home/qldeng/stm32mp1_uart_test/uart_test ./
复制到板子上的效果:
最后看看实际效果吧:
全部0条评论
快来发表一下你的评论吧 !