【米爾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標頭檔案。主要流程為:
開啟串列埠:用open()函式開啟它所對應的裝置檔案。
關閉串列埠:用close()函式關閉串列埠。
傳送資料:使用write()函式可以傳送資料。
讀取資料:使用read()函式可以讀取接收到的資料。
此外,如果對串列埠屬性進行設定需要包含<termios.h>標頭檔案,該檔案包含了POSIX終端屬性描述結構struct termios。
完整的程式碼如下所示:
#include <iostream>
using namespace std;
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <chrono>
#include <thread>
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::sleep_for(chrono::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 [email protected]:/home/qldeng/stm32mp1_uart_test/uart_test ./
複製到板子上的效果:
最後看看實際效果吧: