- 2024-12-16
-
发表了主题帖:
《RISC-V 体系结构编程与实践(第2版)》阅读体会一--初遇
感谢EEWORLD提供的《RISC-V 体系结构编程与实践(第2版)》书籍测评机会。
准备先熟悉下模拟器环境,开始学习各种指令
- 2024-11-20
-
回复了主题帖:
读书入围名单:《RISC-V 体系结构编程与实践(第2版)》
个人信息无误,确认可以完成阅读计划和打卡任务
- 2024-10-01
-
发表了主题帖:
【2024 DigiKey 创意大赛】 1 得捷物料开箱贴
总算收到了得捷寄来的物料,接下来会尽快上手开始开发项目
- 2024-06-01
-
回复了主题帖:
【AI挑战营第二站】算法工程化部署打包成SDK
onnx是一种保存模型的开放格式,适配很多框架,可以相互转换pth格式啥的
rknn时rockchip针对其下设备npu优化的神经网络推理框架,支持常用的算子
#AI挑战营第二站# 基于RKNN toolkit的模型转换与部署 - ARM技术 - 电子工程世界-论坛 (eeworld.com.cn)
-
回复了主题帖:
【AI挑战营终点站】应用落地:部署手写数字识别应用到幸狐RV1106开发板
打卡,完善中#AI挑战营终点站# luckfox手写体识别部署rknn - ARM技术 - 电子工程世界-论坛 (eeworld.com.cn)
- 2024-05-31
-
发表了主题帖:
#AI挑战营终点站# luckfox手写体识别部署rknn
本帖最后由 hollyedward 于 2024-5-31 22:19 编辑
镜像烧录
按照官方教程,将最新的开发板镜像烧录https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-quick-start
需要安装驱动,使用烧录软件。
安装SDK
有几种安装方式,也有docker打包的环境镜像
在服务器上的虚拟机内下载官方的sdk
相关文件目录
首先要,导出编译链地址至环境变量
然后编译的时候cmake才能找到
export LUCKFOX_SDK_PATH=/home/user/luckfox-pico
执行命令对项目进行编译,生成最后的可执行文件
// 创建build目录
mkdir build
cd build
// 生成makefile文件
cmake ..
// 使用make工具进行编译
make && make install
烧录镜像
通过ssh连接开发板
ssh root@172.32.0.93
用scp将可执行文件和相关库传至开发板
scp -r ./luckfox_mnist_demo root@172.32.0.93:/root
给可执行文件或者文件夹赋权
chmod -R 777 ./luckfox_mnist_demo
运行脚本关闭系统默认 rkipc 程序
RkLunch-stop.sh
运行可执行文件
./luck_mnist_rtsp_demo ./model.rknn
使用开源软件vlc拉取视频流
参考官方的一些例程
通过 Rockchip 多媒体处理平台 (RKMPPI) 捕获和流式传输 H.264 视频。都有相应的接口。
//h264_frame
VENC_STREAM_S stFrame;
stFrame.pstPack = (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S));
VIDEO_FRAME_INFO_S h264_frame;
VIDEO_FRAME_INFO_S stVpssFrame;
// rkaiq init
RK_BOOL multi_sensor = RK_FALSE;
const char *iq_dir = "/etc/iqfiles";
rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
//hdr_mode = RK_AIQ_WORKING_MODE_ISP_HDR2;
SAMPLE_COMM_ISP_Init(0, hdr_mode, multi_sensor, iq_dir);
SAMPLE_COMM_ISP_Run(0);
// rkmpi init
if (RK_MPI_SYS_Init() != RK_SUCCESS) {
RK_LOGE("rk mpi sys init fail!");
return -1;
}
// rtsp init
rtsp_demo_handle g_rtsplive = NULL;
rtsp_session_handle g_rtsp_session;
g_rtsplive = create_rtsp_demo(554);
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
// vi init
vi_dev_init();
vi_chn_init(0, width, height);
// vpss init
vpss_init(0, width, height);
// bind vi to vpss
MPP_CHN_S stSrcChn, stvpssChn;
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = 0;
stvpssChn.enModId = RK_ID_VPSS;
stvpssChn.s32DevId = 0;
stvpssChn.s32ChnId = 0;
参考官方opencv处理帧的代码
while(1)
{
// get vpss frame
s32Ret = RK_MPI_VPSS_GetChnFrame(0,0, &stVpssFrame,-1);
if(s32Ret == RK_SUCCESS)
{
void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);
cv::Mat frame(height,width,CV_8UC3, data);
// Preprocess the image
cv::Mat gray_frame;
preprocess(frame, gray_frame);
// Run inference
int digit = run_inference(gray_frame);
// Draw rectangle around detected area (for demonstration, let's assume the area is at the center)
int rect_size = 100;
cv::Rect rect((frame.cols - rect_size) / 2, (frame.rows - rect_size) / 2, rect_size, rect_size);
cv::rectangle(frame, rect, cv::Scalar(0, 255, 0), 2);
// Draw the inference result on the frame
char fps_text[16];
sprintf(fps_text, "Digit: %d", digit);
cv::putText(frame, fps_text,
cv::Point(40, 40),
cv::FONT_HERSHEY_SIMPLEX, 1,
cv::Scalar(0, 255, 0), 2);
// Display the frame
cv::imshow("Detection", frame);
cv::waitKey(1);
// Encode and send the frame
RK_MPI_VENC_SendFrame(0, &stVpssFrame,-1);
s32Ret = RK_MPI_VENC_GetStream(0, &stFrame, -1);
if(s32Ret == RK_SUCCESS)
{
if(g_rtsplive && g_rtsp_session)
{
void *pData = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
stFrame.pstPack->u64PTS);
rtsp_do_event(g_rtsplive);
}
RK_U64 nowUs = TEST_COMM_GetNowUs();
fps = (float) 1000000 / (float)(nowUs - stVpssFrame.stVFrame.u64PTS);
}
// Release frame
s32Ret = RK_MPI_VPSS_ReleaseChnFrame(0, 0, &stVpssFrame);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_VI_ReleaseChnFrame fail %x", s32Ret);
}
s32Ret = RK_MPI_VENC_ReleaseStream(0, &stFrame);
if (s32Ret != RK_SUCCESS) {
RK_LOGE("RK_MPI_VENC_ReleaseStream fail %x", s32Ret);
}
}
int sX,sY,eX,eY;
int width = 720;
int height = 480;
char fps_text[16];
float fps = 0;
memset(fps_text,0,16);
所用分辨不需要太高,太高反而影响速度
所以我们所需要做的就是,将摄像头获取到的帧进行前处理,处理为28*28的输入。
一开始使用原来的模型推理精度较低,需要使用更深层以及带卷积的网络层。以及在前处理部分,需要将摄像头获取的图像,尽可能处理到和数据集里的一致,这样就能提高推理的精确度。这也是为何用更粗的线条会提高识别率,而细的笔迹识别率较低一点,所以在前处理的代码可以加入一些膨胀操作,边缘不用很锐化,做平滑过渡的处理。
使用top命令查看手写体任务所占内存
内存只占了三分之一的样子,所以还是有很大优化空间的。
后续可以加入多线程的识别。
代码处理框架参考了论坛内大佬的开源,处理流程都大差不差,主要就是前处理和后处理的区别,很多嵌入式ai板卡跑模型一般都会给好你处理框架以及调用npu啥的驱动代码,自己需要补充和优化前后处理部分的代码。
[localvideo]e531c2ba4846faded3eb65f712a21f86[/localvideo]
- 2024-05-30
-
发表了主题帖:
#AI挑战营第二站# 基于RKNN toolkit的模型转换与验证(续,含代码)
本帖最后由 hollyedward 于 2024-5-30 03:37 编辑
接上一篇文章,可能我使用的服务器的cpu并不支持某些操作,由于rknn也没有开源的sdk,不知道怎么调试,所以我直接租用别的服务器。
首先,安装环境,步骤不用赘述
模型的导出
首先是导出脚本
# filename: onnx2rknn.py
import numpy as np
from rknn.api import RKNN
if __name__ == '__main__':
# 模型部署平台
platform = 'rv1106'
#训练模拟时输入图片大小
Width = 28
Height = 28
# 此处改为自己的模型地址
MODEL_PATH = '/home/ljl/mnist/mnist_cnn_model.onnx'
# 导出模型地址
RKNN_MODEL_PATH = '/home/ljl/mnist/mnist_cnn_model.rknn'
# 创建RKNN对象并在屏幕打印详细的日志信息
rknn = RKNN(verbose=True)
# 模型配置
# mean_values: 输入图像像素均值
# std_values: 输入图像像素标准差
# target_platform: 目标部署平台
# 本模型训练时输入图象为单通道
rknn.config(mean_values=[0], std_values=[255], target_platform=platform)
# 模型加载
print('--> Loading model')
ret = rknn.load_onnx(MODEL_PATH)
if ret != 0:
print('load model failed!')
exit(ret)
print('done')
# 构建 RKNN 模型
print('--> Building model')
#do_quantization:是否对模型进行量化。默认值为 True
ret = rknn.build(do_quantization=True, dataset="./data.txt")
if ret != 0:
print('build model failed.')
exit(ret)
print('done')
# 导出模型
ret = rknn.export_rknn(RKNN_MODEL_PATH)
#释放RKNN模型
rknn.release()
然后要将测试的图片放在文件夹里,路径写在data.txt 文件中
首先要获取mnist的图片用于测试
这里提供导出脚本
# filename: generate_data.py
import torch
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as datasets
from torch.utils.data import DataLoader
import cv2
import os
import numpy as np
# Test set
test_set = datasets.MNIST('dataset/', train=False, transform=transforms.ToTensor(), download=True)
test_loader = DataLoader(dataset=test_set, batch_size=1, shuffle=True)
def mnist_save_png():
for data, i in test_loader:
with torch.no_grad():
image = data.squeeze().numpy() # Remove unnecessary transpose
# Optional: If you need to move channel dimension to the last position
# image = np.transpose(image, (1, 2, 0))
image = cv2.GaussianBlur(image, (9, 9), 0)
# image *= 255 # Scale image to 0-255 range
index = i.numpy()[0]
if not os.path.exists('./mnist_image/'):
os.mkdir('./mnist_image/')
# 每张图片只保存一次
if not os.path.exists('./mnist_image/' + str(index) + '.png'):
cv2.imwrite('./mnist_image/' + str(index) + '.png', image)
if __name__ == '__main__':
mnist_save_png()
导出后的效果,分辨率为28 * 28,代码也对应前面文章的
这里只是保存了10张图片,如果要更多图片测试需要修改代码
准备好后运行脚本即可
模型转换打印的过程。
感觉rknn至少比全志系的好用些(又不开源,还不允许个人使用),虽然不是开源的。
I rknn-toolkit2 version: 2.0.0b0+9bab5682
--> Loading model
I It is recommended onnx opset 19, but your onnx model opset is 17!
I Model converted from pytorch, 'opset_version' should be set 19 in torch.onnx.export for successful convert!
I Loading : 100%|██████████████████████████████████████████████████| 5/5 [00:00<00:00, 24966.10it/s]
done
--> Building model
D base_optimize ...
D base_optimize done.
D
D fold_constant ...
D fold_constant done.
D
D correct_ops ...
D correct_ops done.
D
D fuse_ops ...
W build: Can not find 'idx' to insert, default insert to 0!
D fuse_ops results:
D replace_reshape_gemm_by_conv: remove node = ['/Reshape', '/fc1/Gemm'], add node = ['/fc1/Gemm_2conv', '/fc1/Gemm_2conv_reshape']
D swap_reshape_relu: remove node = ['/fc1/Gemm_2conv_reshape', '/Relu'], add node = ['/Relu', '/fc1/Gemm_2conv_reshape']
D convert_gemm_by_conv: remove node = ['/fc2/Gemm'], add node = ['/fc2/Gemm_2conv_reshape1', '/fc2/Gemm_2conv', '/fc2/Gemm_2conv_reshape2']
D fuse_two_reshape: remove node = ['/fc1/Gemm_2conv_reshape']
D remove_invalid_reshape: remove node = ['/fc2/Gemm_2conv_reshape1']
D fold_constant ...
D fold_constant done.
D fuse_ops done.
D
D sparse_weight ...
D sparse_weight done.
D
I GraphPreparing : 100%|████████████████████████████████████████████| 4/4 [00:00<00:00, 5403.29it/s]
I Quantizating : 100%|███████████████████████████████████████████████| 4/4 [00:00<00:00, 283.34it/s]
D
D quant_optimizer ...
D quant_optimizer results:
D adjust_relu: ['/Relu']
D quant_optimizer done.
D
W build: The default input dtype of 'onnx::Reshape_0' is changed from 'float32' to 'int8' in rknn model for performance!
Please take care of this change when deploy rknn model with Runtime API!
W build: The default output dtype of '15' is changed from 'float32' to 'int8' in rknn model for performance!
Please take care of this change when deploy rknn model with Runtime API!
I rknn building ...
I RKNN: [00:09:32.440] compress = 0, conv_eltwise_activation_fuse = 1, global_fuse = 1, multi-core-model-mode = 7, output_optimize = 1, layout_match = 1, enable_argb_group = 0
I RKNN: librknnc version: 2.0.0b0 (35a6907d79@2024-03-24T02:34:11)
D RKNN: [00:09:32.440] RKNN is invoked
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNExtractCustomOpAttrs
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNExtractCustomOpAttrs
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNSetOpTargetPass
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNSetOpTargetPass
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNBindNorm
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNBindNorm
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNAddFirstConv
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNAddFirstConv
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNEliminateQATDataConvert
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNEliminateQATDataConvert
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNTileGroupConv
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNTileGroupConv
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNTileFcBatchFuse
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNTileFcBatchFuse
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNAddConvBias
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNAddConvBias
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNTileChannel
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNTileChannel
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNPerChannelPrep
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNPerChannelPrep
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNBnQuant
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNBnQuant
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNFuseOptimizerPass
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNFuseOptimizerPass
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNTurnAutoPad
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNTurnAutoPad
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNInitRNNConst
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNInitRNNConst
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNInitCastConst
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNInitCastConst
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNMultiSurfacePass
D RKNN: [00:09:32.442] <<<<<<<< end: rknn::RKNNMultiSurfacePass
D RKNN: [00:09:32.442] >>>>>> start: rknn::RKNNReplaceConstantTensorPass
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNReplaceConstantTensorPass
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNSubgraphManager
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNSubgraphManager
D RKNN: [00:09:32.443] >>>>>> start: OpEmit
D RKNN: [00:09:32.443] <<<<<<<< end: OpEmit
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNLayoutMatchPass
I RKNN: [00:09:32.443] AppointLayout: t->setNativeLayout(64), tname:[/fc1/Gemm_output_0_new]
I RKNN: [00:09:32.443] AppointLayout: t->setNativeLayout(64), tname:[15_conv]
I RKNN: [00:09:32.443] AppointLayout: t->setNativeLayout(0), tname:[15]
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNLayoutMatchPass
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNAddSecondaryNode
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNAddSecondaryNode
D RKNN: [00:09:32.443] >>>>>> start: OpEmit
D RKNN: [00:09:32.443] finish initComputeZoneMap
D RKNN: [00:09:32.443] <<<<<<<< end: OpEmit
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNSubGraphMemoryPlanPass
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNSubGraphMemoryPlanPass
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNProfileAnalysisPass
D RKNN: [00:09:32.443] node: Reshape:/fc2/Gemm_2conv_reshape2, Target: NPU
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNProfileAnalysisPass
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNOperatorIdGenPass
D RKNN: [00:09:32.443] <<<<<<<< end: rknn::RKNNOperatorIdGenPass
D RKNN: [00:09:32.443] >>>>>> start: rknn::RKNNWeightTransposePass
W RKNN: [00:09:32.444] Warning: Tensor /fc2/Gemm_2conv_reshape2_shape need paramter qtype, type is set to float16 by default!
W RKNN: [00:09:32.444] Warning: Tensor /fc2/Gemm_2conv_reshape2_shape need paramter qtype, type is set to float16 by default!
D RKNN: [00:09:32.444] <<<<<<<< end: rknn::RKNNWeightTransposePass
D RKNN: [00:09:32.444] >>>>>> start: rknn::RKNNCPUWeightTransposePass
D RKNN: [00:09:32.444] <<<<<<<< end: rknn::RKNNCPUWeightTransposePass
D RKNN: [00:09:32.444] >>>>>> start: rknn::RKNNModelBuildPass
D RKNN: [00:09:32.446] <<<<<<<< end: rknn::RKNNModelBuildPass
D RKNN: [00:09:32.446] >>>>>> start: rknn::RKNNModelRegCmdbuildPass
D RKNN: [00:09:32.446] ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
D RKNN: [00:09:32.446] Network Layer Information Table
D RKNN: [00:09:32.446] ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
D RKNN: [00:09:32.446] ID OpType DataType Target InputShape OutputShape Cycles(DDR/NPU/Total) RW(KB) FullName
D RKNN: [00:09:32.446] ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
D RKNN: [00:09:32.446] 0 InputOperator INT8 CPU \ (1,1,28,28) 0/0/0 0 InputOperator:onnx::Reshape_0
D RKNN: [00:09:32.446] 1 ConvRelu INT8 NPU (1,1,28,28),(50,1,28,28),(50) (1,50,1,1) 6585/12544/12544 39 Conv:/fc1/Gemm_2conv
D RKNN: [00:09:32.446] 2 Conv INT8 NPU (1,50,1,1),(10,50,1,1),(10) (1,10,1,1) 138/64/138 0 Conv:/fc2/Gemm_2conv
D RKNN: [00:09:32.446] 3 Reshape INT8 NPU (1,10,1,1),(2) (1,10) 7/0/7 0 Reshape:/fc2/Gemm_2conv_reshape2
D RKNN: [00:09:32.446] 4 OutputOperator INT8 CPU (1,10) \ 0/0/0 0 OutputOperator:15
D RKNN: [00:09:32.446] ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
D RKNN: [00:09:32.446] <<<<<<<< end: rknn::RKNNModelRegCmdbuildPass
D RKNN: [00:09:32.446] >>>>>> start: rknn::RKNNFlatcModelBuildPass
D RKNN: [00:09:32.446] Export Mini RKNN model to /tmp/tmpkbgrb68z/check.rknn
D RKNN: [00:09:32.446] >>>>>> end: rknn::RKNNFlatcModelBuildPass
D RKNN: [00:09:32.446] >>>>>> start: rknn::RKNNMemStatisticsPass
D RKNN: [00:09:32.446] ------------------------------------------------------------------------------------------------------------------------------
D RKNN: [00:09:32.446] Feature Tensor Information Table
D RKNN: [00:09:32.446] --------------------------------------------------------------------------------------------+---------------------------------
D RKNN: [00:09:32.446] ID User Tensor DataType DataFormat OrigShape NativeShape | [Start End) Size
D RKNN: [00:09:32.446] --------------------------------------------------------------------------------------------+---------------------------------
D RKNN: [00:09:32.446] 1 ConvRelu onnx::Reshape_0 INT8 NC1HWC2 (1,1,28,28) (1,1,28,28,1) | 0x00027500 0x00027880 0x00000380
D RKNN: [00:09:32.446] 2 Conv /fc1/Gemm_output_0_new INT8 NC1HWC2 (1,50,1,1) (1,4,1,1,16) | 0x00027880 0x000278c0 0x00000040
D RKNN: [00:09:32.446] 3 Reshape 15_conv INT8 NC1HWC2 (1,10,1,1) (1,1,1,1,16) | 0x00027500 0x00027510 0x00000010
D RKNN: [00:09:32.446] 4 OutputOperator 15 INT8 UNDEFINED (1,10) (1,10) | 0x00027580 0x000275c0 0x00000040
D RKNN: [00:09:32.446] --------------------------------------------------------------------------------------------+---------------------------------
D RKNN: [00:09:32.446] -----------------------------------------------------------------------------------------------------
D RKNN: [00:09:32.446] Const Tensor Information Table
D RKNN: [00:09:32.446] -------------------------------------------------------------------+---------------------------------
D RKNN: [00:09:32.446] ID User Tensor DataType OrigShape | [Start End) Size
D RKNN: [00:09:32.446] -------------------------------------------------------------------+---------------------------------
D RKNN: [00:09:32.446] 1 ConvRelu fc1.weight INT8 (50,1,28,28) | 0x00000000 0x00026480 0x00026480
D RKNN: [00:09:32.446] 1 ConvRelu fc1.bias INT32 (50) | 0x00026480 0x00026680 0x00000200
D RKNN: [00:09:32.446] 2 Conv fc2.weight INT8 (10,50,1,1) | 0x00026680 0x00026900 0x00000280
D RKNN: [00:09:32.446] 2 Conv fc2.bias INT32 (10) | 0x00026900 0x00026980 0x00000080
D RKNN: [00:09:32.446] 3 Reshape /fc2/Gemm_2conv_reshape2_shape INT64 (2) | 0x00026980*0x000269c0 0x00000040
D RKNN: [00:09:32.446] -------------------------------------------------------------------+---------------------------------
D RKNN: [00:09:32.446] ----------------------------------------
D RKNN: [00:09:32.446] Total Internal Memory Size: 0.9375KB
D RKNN: [00:09:32.446] Total Weight Memory Size: 154.438KB
D RKNN: [00:09:32.446] ----------------------------------------
D RKNN: [00:09:32.446] <<<<<<<< end: rknn::RKNNMemStatisticsPass
I rknn buiding done.
done
模型验证
我们需要检验模型的输出是否正确
貌似simulator不支持rknn模型验证
报错日志:
官方提供的demo教程也是使用load_onnx,https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-RKNN-Test
所以其实可以在导出rknn模型的时候,也可以验证模型
脚本如下
# filename: rknn_mnist_test.py
import numpy as np
import cv2
from rknn.api import RKNN
# Model conversion parameters
MODEL_PATH = '/root/test/my_model.onnx' # Path to the ONNX model
RKNN_MODEL_PATH = '/root/test/my_model.rknn' # Path to save the RKNN model
# Model inference parameters
input_size = (28, 28) # Define the input size (same as your model's input)
data_file = 'data.txt' # Path to the data file (containing image paths and labels)
rknn = RKNN(verbose=True) # Create RKNN object with verbose logging
rknn.config(mean_values=[0], std_values=[255], target_platform='rv1106') # Set configuration parameters
ret = rknn.load_onnx(MODEL_PATH)
if ret != 0:
print('Load ONNX model failed!')
exit(ret)
print('done')
print('--> Building RKNN model')
ret = rknn.build(do_quantization=True, dataset="./data.txt")
if ret != 0:
print('Build model failed.')
exit(ret)
print('done')
# Model export (optional) #导出rknn模型
ret = rknn.export_rknn(RKNN_MODEL_PATH)
# Model inference
print('--> Performing inference on data')
rknn.init_runtime() # Initialize RKNN runtime
with open(data_file, 'r') as f:
lines = f.readlines()
for line in lines:
# Get image path and label
image_path = line.strip()
# Read the image
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# Preprocess the image
image = image.astype(np.float32)
# image 这时候读取出来是 (28, 28), 需要增加维度
image = np.expand_dims(image, axis=[0,1])
# Run inference
outputs = rknn.inference([image], data_format = 'nchw') #(1, 1, 28, 28) 对应nchw,批次,通道,长,宽
print(f"Ineference Output: {outputs}")
# Check inference results
if outputs is not None:
predicted_label = np.argmax(outputs)
print(f"Image: {image_path}")
print(f"Predicted label: {predicted_label}")
else:
print(f"Inference failed for image: {image_path}")
# Release RKNN resources
rknn.release()
打印的结果
可以看到推理的结果是正确的
- 2024-05-28
-
回复了主题帖:
【安信可BW16-Kit开发板】温湿度与光强传感器接入测试
hollyedward 发表于 2024-5-28 16:54
你使用默认的例程试试,这个就是sdk自带的例程,应该没有问题的。还有就是传感器的针脚确定没接错吧,有 ...
你可以试试别的例程,pa26接个别的pwm,gpio调试的,或者啥
-
回复了主题帖:
【安信可BW16-Kit开发板】温湿度与光强传感器接入测试
HuangDarren 发表于 2024-5-26 14:59
應該不是傳感器的問題,我有再新買一個傳感器但同樣不能用,請問要如何排查針腳?有其他替代針腳可以使用 ...
你使用默认的例程试试,这个就是sdk自带的例程,应该没有问题的。还有就是传感器的针脚确定没接错吧,有的不同厂商针脚顺序不一样,看看它的原理图。其他的就没啥了啊,除非是硬件问题可能
- 2024-05-26
-
回复了主题帖:
【安信可BW16-Kit开发板】温湿度与光强传感器接入测试
HuangDarren 发表于 2024-5-24 13:56
除了PA26以外其他的腳位可以接DATA嗎?我在想會不會是腳位出現故障才無法讀取到傳感器
代码肯定没问题的,我都试过了,你可以排查下是不是板子针脚还是传感器的问题
- 2024-05-21
-
回复了主题帖:
【安信可BW16-Kit开发板】温湿度与光强传感器接入测试
HuangDarren 发表于 2024-5-20 23:53
這是我連接的方式與Output結果(使用您帖子上面相同的Code),因為私信不能傳圖片所以只能 ...
接线没问题吗,接3.3v看看,还有数据线接PA26
- 2024-05-17
-
回复了主题帖:
【安信可BW16-Kit开发板】温湿度与光强传感器接入测试
HuangDarren 发表于 2024-5-15 17:03
您好,我想請問為甚麼我照著上面的步驟連接bw16-kit(腳位PA26)跟DHT11但是卻無法讀取到數值?如果可以的話, ...
可以
- 2024-05-08
-
回复了主题帖:
入围名单公布:嵌入式工程师AI挑战营(初阶),获RV1106 Linux 板+摄像头的名单
本帖最后由 hollyedward 于 2024-5-9 15:02 编辑
个人信息已确认,领取板卡,可继续完成&分享挑战营第二站和第三站任务。
- 2024-05-05
-
发表了主题帖:
#AI挑战营第二站# 基于RKNN toolkit的模型转换与部署
本帖最后由 hollyedward 于 2024-5-7 01:07 编辑
RKNN Toolkit2 是由瑞芯微电子 (Rockchip) 开发的一套用于深度学习模型优化和推理的工具。它主要面向在瑞芯微SoC上进行AI应用开发,但也可以用于PC平台进行模型的转换、量化、推理等操作。
Rockchip RK3566/RK3568 系列, RK3588 系列, K3562 系列, RV1103/RV1106 系列芯片搭载神经网络处理器 NPU, 利用 RKNN 可以帮助用户快速部署 AI 模型到 Rockchip 芯片上使用 NPU 硬件加速模型推理。 为了使用 RKNPU,用户首先需要在 x86 计算机上使用 RKNN-Toolkit2 工具,将训练好的模型转换为 RKNN 格式的模型,然后在开发板上使用 RKNN C API 或 Python API进行推断。
所需工具:
RKNN-Toolkit2 是一个软件开发工具包,供用户在 PC 和 Rockchip NPU 平台上执行模型转换、推断和性能评估。
RKNN-Toolkit-Lite2 为 Rockchip NPU 平台提供了 Python 编程接口,帮助用户部署 RKNN 模型并加速实施AI应用。
RKNN Runtime 为 Rockchip NPU 平台提供了 C/C++ 编程接口,帮助用户部署 RKNN 模型并加速实施AI应用。
RKNPU 内核驱动负责与 NPU 硬件交互。
rknn 软件框图
环境搭建
查看系统版本
cat /proc/version
ubuntu版本为22.04,对应的环境python版本为3.10
安装miniconda
创建虚拟环境
# 下载 RKNN-Toolkit2 仓库
git clone https://github.com/airockchip/rknn-toolkit2.git -b v1.6.0
安装项目需要的依赖文件,使用python 3.10对应的配置文件
pip install -r ./rknn-toolkit2/packages/requirements_cp310-1.6.0.txt
需要下载的依赖
安装 rknn toolkit2
pip install ./rknn-toolkit2/packages/rknn_toolkit2-1.6.0+81f21f4d-cp38-cp38-linux_x86_64.whl
遇到的问题待解决:在服务器的ubuntu系统(版本22.04)上不能正常使用rknn-toolkit2
换版本都试过了没有用
py38+rknn1.6.0
py39+rknn1.6.0
py310+rknn1.6.0
py310+rknn2.0.0
以上均报错Illegal instruction (core dumped)
这是系统原因吗?
这里执行自带的yolov5 example也没有用
python test.py
I rknn-toolkit2 version: 2.0.0b0+9bab5682
--> Config model
done
--> Loading model
I It is recommended onnx opset 19, but your onnx model opset is 12!
I Model converted from pytorch, 'opset_version' should be set 19 in torch.onnx.export for successful convert!
I Loading : 0%| | 0/125 [00:00<?, ?it/I Loading : 100%|██████████████████████████████████████████████| 125/125 [00:00<00:00, 11880.53it/s]
done
--> Building model
D base_optimize ...
D base_optimize done.
D
D fold_constant ...
D fold_constant done.
D
D correct_ops ...
D correct_ops done.
D
D fuse_ops ...
D fuse_ops done.
D
D sparse_weight ...
Illegal instruction (core dumped)
- 2024-04-13
-
回复了主题帖:
【AI挑战营第一站】模型训练:在PC上完成手写数字模型训练,免费申请RV1106开发板
本帖最后由 hollyedward 于 2024-4-13 01:13 编辑
1、用自己的语言描述,模型训练的本质是什么,训练最终结果是什么
可以从函数拟合的角度来看,模型训练的本质就是通过调整模型参数,使模型能够更好地拟合训练数据。我们可以将模型看作是一个函数拟合器,它将输入数据映射到输出数据。模型训练的目标就是找到一组参数,使得模型的输出能够尽可能接近训练数据的真实值。
2、PyTorch是什么?目前都支持哪些系统和计算平台?
PyTorch 是一个开源深度学习框架,它具有广泛的使用以及灵活强大的api,已经打败tensorflow和mxnet等框架,成为研究人员的开发工具首选。此外它支持多种平台,比如linux、windows或者macos系统,它可以部署到各种处理计算平台比如cpu或者gpu上。
3、#AI挑战营第一站# 基于pytorch的minist手写体识别以及模型转换 - 嵌入式系统 - 电子工程世界-论坛 (eeworld.com.cn)
-
回复了主题帖:
免费申请:幸狐 RV1106 Linux 开发板(带摄像头),助力AI挑战营应用落地
#AI挑战营第一站# 基于pytorch的minist手写体识别以及模型转换 - 嵌入式系统 - 电子工程世界-论坛 (eeworld.com.cn)
预期应用:利用所带的摄像头,完成快递单号识别等应用。
-
回复了主题帖:
【AI挑战营第一站】模型训练:在PC上完成手写数字模型训练,免费申请RV1106开发板
1、跟帖回复:用自己的语言描述,模型训练的本质是什么,训练最终结果是什么
从函数拟合的角度来看,模型训练的本质就是通过调整模型参数,使模型能够更好地拟合训练数据。我们可以将模型看作是一个函数拟合器,它将输入数据映射到输出数据。模型训练的目标就是找到一组参数,使得模型的输出能够尽可能接近训练数据的真实值。
2、跟帖回复:PyTorch是什么?目前都支持哪些系统和计算平台?
PyTorch 是由 Facebook 开发的开源深度学习框架,用于自然语言处理、计算机视觉等领域。它以其易用性、灵活性和功能强大而著称,已成为众多研究人员和开发人员的首选框架之一。它支持多种平台,比如linux、windows或者macos,可以部署到cpu或者gpu上,满足各种需求。
3、#AI挑战营第一站# 基于pytorch的minist手写体识别以及模型转换 - 嵌入式系统 - 电子工程世界-论坛 (eeworld.com.cn)
- 2024-04-12
-
发表了主题帖:
#AI挑战营第一站# 基于pytorch的mnist手写体识别以及模型转换
手写体识别,是指计算机从纸张、照片、触摸屏或其他设备中接收并识别人手写的文字等信息的技术。它在我们的生活中有着广泛的应用,例如文档处理、移动设备输入、个性化签名、教育和辅助技术等等。
本教程提供对您提供的 PyTorch 代码的详细解释,逐步指导您完成使用全连接神经网络 训练模型以识别 MNIST 数据集中的手写数字。
1. 数据集下载预处理
mnist手写体数据集下载,存放到./dataset文件夹下
需要将数据集分训练集和测试集
为什么不将所有数据拿来训练?
在机器学习中,通常会将数据集划分成训练集和测试集。虽然直觉上可能认为应该使用所有数据进行训练,但将数据划分成不同的子集对于有效地开发模型至关重要,以下是一些原因:
1. 评估模型泛化能力:
测试集的主要目的是评估训练模型的泛化能力。泛化能力是指模型在新数据上表现良好的能力,这对于实际应用至关重要。
如果使用整个数据集进行训练,包括用于评估的数据,模型可能会简单地记住训练示例而无法泛化到新数据。这种现象称为过拟合。
通过使用单独的测试集,您可以评估模型在从未见过的数据上的表现如何,从而提供更真实的性能估计。
2. 防止过拟合:
过拟合是指模型过于针对特定的训练数据,捕获噪声和无关模式,而不是学习数据中的底层关系。
将数据分成训练集和测试集有助于通过引入验证集来防止过拟合。验证集用于训练过程中监控模型在未见过的数据上的性能。
当模型开始过拟合训练数据时,其在验证集上的性能通常会开始下降。这可以作为停止训练并防止进一步过拟合的早期预警信号。
3. 提高模型选择效率:
在训练过程中,您可能会尝试不同的超参数、模型架构或训练技巧。
使用单独的测试集可以让您在相同的数据上评估不同模型或训练配置的性能,从而客观地选择最适合任务的模型。
4. 保留数据用于未来评估:
在某些情况下,您可能需要保留部分数据用于未来的评估,例如随着时间的推移比较不同的模型或技术。
通过保留一个隔离的测试集,您可以确保即使总体数据集随着时间的推移增长或变化,您也始终拥有用于未来比较的一致且无偏倚的数据。
#训练集和验证集
training_set_full = datasets.MNIST('dataset/', train=True, transform=transforms.ToTensor(), download=True)
#测试集
test_set = datasets.MNIST('dataset/', train=False, transform=transforms.ToTensor(), download=True)
import torch
import torchvision
from torchvision import transforms
# 定义数据预处理操作
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.1307,), (0.3008,)) # 归一化
])
# 加载 MNIST 数据集
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
# 将训练集划分为训练集和验证集
train_size = len(train_dataset)
val_size = int(0.1 * train_size)
train_idx = list(range(train_size))
val_idx = list(range(train_size - val_size, train_size))
random.shuffle(train_idx)
train_sampler = torch.utils.data.sampler.SubsetRandomSampler(train_idx[:-val_size])
val_sampler = torch.utils.data.sampler.SubsetRandomSampler(train_idx[-val_size:])
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, sampler=train_sampler)
val_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, sampler=val_sampler)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64)
数据集的可视化
SAMPLE_IMG_ID = np.random.choice(len(training_set))
junk = plt.imshow(training_set[SAMPLE_IMG_ID][0].squeeze(0), cmap='gray') # "squeeze" removes the first dimension (1,28,28) => (28,28)
junk = plt.title(training_set[SAMPLE_IMG_ID][1])
2. 模型定义
使用类定义一个模型,在初始化里面定义好各个层的定义为一些函数,模型的层与层之间的参数传递实际上就是一堆函数的嵌套。
整个模型相当于一个很多层的嵌套函数,但是函数里面有可调整的参数,这些会影响神经网络的输出结果,训练的过程就是调整这些参数,使得神经网络的输出可以很好地近似到真实的数据值。比如这个案例中是一个十分类问题,神经网络输入是图像的像素点集合本例中是28*28,输出的是10个概率值,表示预测的label的概率,其和为1.
import torch.nn as nn
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.fc1 = nn.Linear(14 * 14 * 64, 1000)
self.fc2 = nn.Linear(1000, 10)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1)
x = self.fc1(x)
x = self.fc2(x)
return x
3. 模型训练
定义一些超参数,例如 EPOCHS 指定训练的轮数,EVALUATION_FREQ 指定每隔多少个批次进行一次模型在验证集上的评估
外部循环用于迭代指定的训练轮数 (EPOCHS)。内部循环将在每个 epoch 中处理训练集的批次数据。
# 循环遍历训练周期 (epoch)
for epoch in range(EPOCHS):
print(f'第 {epoch + 1} 个 epoch')
epoch_acc = [] # 保存每个 epoch 的准确率
training_acc_checkpoint, training_loss_checkpoint = [], [] # 临时保存训练过程中的精度和损失,用于计算平均值
# 遍历训练数据集中的每一个批次
for batch_idx, (data, labels) in enumerate(training_loader):
# 将数据和标签移动到指定设备 (CPU 或 GPU)
data, labels = data.to(device), labels.to(device)
# 评估模型,获得预测结果、准确率和损失值
predictions, acc, loss = evaluate(model, loss_function, data, labels)
training_acc_checkpoint.append(acc)
epoch_acc.append(acc)
training_loss_checkpoint.append(loss.item())
# 反向传播计算梯度
loss.backward()
# 更新模型参数
optimizer.step()
# 清空梯度 (与 optimizer 相关)
optimizer.zero_grad() # 或者 model.zero_grad() (如果所有模型参数都在优化器中)
# 周期性评估验证集
if batch_idx % EVALUATION_FREQ == 0:
# 计算并保存平均训练精度和损失
training_acc_lst.append(np.mean(training_acc_checkpoint))
training_loss_lst.append(np.mean(training_loss_checkpoint))
# 清空临时保存的训练过程数据
training_acc_checkpoint, training_loss_checkpoint = [], []
# 评估验证集 (进入评估模式并关闭梯度追踪)
model.train(mode=False) # 进入评估模式 (参考链接: https://stackoverflow.com/a/55627781/900394)
with torch.no_grad(): # 临时关闭梯度追踪
validation_acc_checkpoint, validation_loss_checkpoint = [], []
validation_predictions = [] # 保存用于之后展示结果的预测值
for val_batch_idx, (val_data, val_labels) in enumerate(validation_loader):
val_data, val_labels = val_data.to(device), val_labels.to(device)
# 评估单个验证批次
val_predictions, validation_acc, validation_loss = evaluate(model, loss_function, val_data, val_labels)
validation_loss_checkpoint.append(validation_loss.item())
validation_acc_checkpoint.append(validation_acc)
validation_predictions.extend(val_predictions) # 扩展 (append 会覆盖) 所有验证预测值
# 计算并保存平均验证精度和损失
validation_acc_lst.append(np.mean(validation_acc_checkpoint))
validation_loss_lst.append(np.mean(validation_loss_checkpoint))
# 重新进入训练模式
model.train(mode=True)
# 打印当前 epoch 的训练和验证结果
print(f'训练精度: {training_acc_lst[-1]:.2f}, 训练损失: {training_loss_lst[-1]:.2f}, 验证精度: {validation_acc_lst[-1]:.2f}, 验证损失: {validation_loss_lst[-1]:.2f}')
4. 模型保存
模型保存是将训练好的模型参数和结构存储到文件中的过程,以便以后加载和使用。这对于以下目的至关重要:
模型部署: 将模型部署到生产环境以进行预测或推理。
模型分享: 与他人分享模型,以便他们可以使用或进行微调。
模型复现: 在以后重新创建训练好的模型。
#保存模型
torch.save(model.state_dict(), 'my_model.pth')
5. 模型转换
什么是 ONNX?
ONNX(Open Neural Network Exchange,开放神经网络交换)是一种用于表示深度学习模型的开放格式。它允许将模型存储在文件中,并在不同框架(例如 PyTorch、TensorFlow、MXNet 等)之间共享和转换。
ONNX 的目标是简化深度学习模型的部署和共享。它使研究人员和开发人员能够专注于构建模型,而无需担心如何将其部署到特定平台或框架。
ONNX 的优点
使用 ONNX 有很多优点,包括:
可移植性: 模型可以轻松地在不同框架之间转换,而无需重新训练或修改代码。
可复用性: 模型可以存储和共享,供其他人使用或在其他项目中进行微调。
互操作性: 不同的框架可以协同工作,使用来自彼此的模型。
简化部署: 模型可以轻松部署到生产环境,而无需考虑底层框架。
所以我们现在基本了解了onnx是一个开放格式,很多框架的模型都可以转换到onnx,或者由onnx转出,转换成onnx可以移植
torch提供了模型转换的方法
其中维度必须要和模型的输入一致,我这里自定义的模型输入为28*28
#导出为onnx模型
dummy_input = torch.randn(1, 1, 784)
torch.onnx.export(model, dummy_input, "my_model.onnx", verbose=False)
使用netron软件查看onnx神经网络结构
netron是一个深度学习模型可视化库,其支持以下格式的模型存储文件:
ONNX (.onnx, .pb)
Keras (.h5, .keras)
CoreML (.mlmodel)
TensorFlow Lite (.tflite)
netron并不支持pytorch通过torch.save方法导出的模型文件,因此在pytorch保存模型的时候,需要将其导出为onnx格式的模型文件,可以利用torch.onnx模块实现这一目标。
对于分析网络结构,以及各个层的参数量,整个可视化效果很好
netron有网页在线版和软件版本,直接拖拽模型加载即能可视化
6. 模型训练与验证结果
由于我们并没有使用卷积神经网络或者循环神经网络,模型的最高准确度也只能达到97%,一些使用了卷积层的神经网络拟合能力更加强大,可以达到99%的准确度
- 2024-03-25
-
回复了主题帖:
【GD32VW553-EVAL试用评测】UART用户指令使用
lugl4313820 发表于 2024-3-18 07:42
初学小白难免会出错,还有很多问题亟待解决,望大佬不吝赐教。
挺用心的啦!
好多没搞懂的,感觉资料还是不详细,得直接看源码才行
-
发表了主题帖:
【GD32VW553-EVAL试用评测】UASRT串口例程学习
GD32VW553-EVAL学习笔记(四)
简介
模块内部框图
1、查看最简单的串口打印例程
首先导入例程至gd32eclipse
右键build project
在项目目录下找到生成的bin文件
使用 GD_LINK_CLI.exe 工具进行烧录
运行结果:USART 将输出“please press the Tamper key”到超级终端。按下按键Tamper 键,串口继续输出“USART printf example”。
查看代码结构
int main(void)
{
systick_config();
/* initialize test status LED */
test_status_led_init();
/* flash LED for test */
flash_led(2);
/* test key initialize */
test_key_init();
/* USART initialize */
gd_eval_com_init(EVAL_COM0);
/* output a message on hyperterminal using printf function */
printf("\n\rplease press the Tamper/Wakeup Key\n\r");
/* the software must wait until TC=1. the TC flag remains cleared during all data
transfers and it is set by hardware at the last frame end of transmission */
while(RESET == usart_flag_get(EVAL_COM0 , USART_FLAG_TC));
while(1){
if(0 == gd_eval_key_state_get(KEY_TAMPER_WAKEUP)){
delay_1ms(50);
if(0 == gd_eval_key_state_get(KEY_TAMPER_WAKEUP)){
delay_1ms(50);
if(0 == gd_eval_key_state_get(KEY_TAMPER_WAKEUP)){
/* turn on LED1 */
gd_eval_led_on(LED1);
delay_1ms(200);
/* output a message on hyperterminal using printf function */
printf("\n\rUSART printf example \n\r");
/* the software must wait until TC=1. the TC flag remains cleared during all data
transfers and it is set by hardware at the last frames end of transmission */
while(RESET == usart_flag_get(USART0 , USART_FLAG_TC));
/* wait until the button is released */
while(0 == gd_eval_key_state_get(KEY_TAMPER_WAKEUP));
}else{
/* turn off LED1 */
gd_eval_led_off(LED1);
}
}else{
/* turn off LED1 */
gd_eval_led_off(LED1);
}
}else{
/* turn off LED1 */
gd_eval_led_off(LED1);
}
}
}
gd_eval_key_state_get(KEY_TAMPER_WAKEUP)方法检测按键状态,延迟50ms按键消抖,连续检测三次以确保按键按下有效。
如果检测为有效,则打开 LED1 (gd_eval_led_on(LED1)),延时 200 毫秒。
再次向上位机输出信息,表明这是一个 USART 打印示例。
等待串口数据发送完成 (while(RESET == usart_flag_get(USART0 , USART_FLAG_TC)))。
循环等待按键松开 (while(0 == gd_eval_key_state_get(KEY_TAMPER_WAKEUP)))
如果按键未检测有效LED1就会熄灭。
2、按键中断USART
烧录例程
运行结果:首先,所有灯亮灭一次用于测试。然后USART0 将输出数组tx_buffer 的内容(从0x00 到0xFF)
到支持hex 格式的超级终端并等待接收由超级终端发送的BUFFER_SIZE 个字节的数据。MCU
将接收到的超级终端发来的数据存放在数组rx_buffer 中。在发送和接收完成后,将比较
tx_buffer 和rx_buffer 的值,如果结果相同,LED1,LED2 点亮,LED3 灭;如果结果不相同,
LED3 点亮,LED1,LED2 灭。
这是串口的自发自收嘛
显示十六进制
代码先初始化 LED 和串口,然后发送一组数据到串口。接着,程序等待接收数据并将其与发送的数据进行比较。如果数据传输成功,则程序会打开 LED1 和 LED2,关闭 LED3;如果数据传输失败,则程序会关闭 LED1 和 LED2,打开 LED3。最后,程序进入一个死循环,等待用户操作。
主程序
int main(void)
{
/* initialize the LEDs */
led_init();
/* USART interrupt configuration */
eclic_irq_enable(USART0_IRQn, 1, 0);
systick_config();
/* flash the LEDs for 1 time */
led_flash(1);
/* initialize the USART */
gd_eval_com_init(EVAL_COM0);
/* enable USART TBE interrupt */
usart_interrupt_enable(EVAL_COM0, USART_INT_TBE);
/* wait until USART send the tx_buffer */
while(tx_count < tx_buffer_size);
while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TC));
/* enable USART RBNE interrupt */
usart_interrupt_enable(EVAL_COM0, USART_INT_RBNE);
/* wait until USART receive the rx_buffer */
while(rx_count < rx_buffer_size);
/* compare the transmitted buffer and the received buffer */
transfer_result = memory_compare(tx_buffer, rx_buffer, BUFFER_SIZE);
if (SUCCESS == transfer_result){
/* if success, turn on LED1 and LED2, turn off LED3*/
gd_eval_led_on(LED1);
gd_eval_led_on(LED2);
gd_eval_led_off(LED3);
}else{
/* if not, turn off LED1 and LED2, turn on LED3*/
gd_eval_led_off(LED1);
gd_eval_led_off(LED2);
gd_eval_led_on(LED3);
}
while(1){
}
}
内存比较
ErrStatus memory_compare(uint8_t* src, uint8_t* dst, uint16_t length)
{
while (length--){
if (*src++ != *dst++){
return ERROR;
}
}
return SUCCESS;
}
3、使用串口DMA功能发送和接收
烧录相关代码
功能和前面接收发送差不多,但使用DMA功能
具体过程:
初始化 LED 和串口。
配置 DMA 控制器。
配置 USART 发送和接收 DMA。
等待 DMA 发送和接收完成。
比较发送和接收数据。
根据比较结果控制 LED:
数据传输成功:打开 LED1 和 LED2,关闭 LED3。
数据传输失败:关闭 LED1 和 LED2,打开 LED3。
进入无限循环,等待用户操作。
int main(void)
{
/* initialize the LEDs */
led_init();
systick_config();
/* flash the LEDs for 3 times */
led_flash(3);
/* initialize the USART */
gd_eval_com_init(EVAL_COM0);
/* configure USART DMA */
dma_config();
/* USART DMA enable for transmission and reception */
usart_dma_transmit_config(EVAL_COM0, USART_TRANSMIT_DMA_ENABLE);
usart_dma_receive_config(EVAL_COM0, USART_RECEIVE_DMA_ENABLE);
/* wait DMA channel7 transfer complete */
while(RESET == dma_flag_get(DMA_CH7, DMA_FLAG_FTF));
/* wait DMA channel2 receive complete */
while(RESET == dma_flag_get(DMA_CH2, DMA_FLAG_FTF));
/* compare the transmitted buffer and the received buffer */
transfer_result = memory_compare(tx_buffer, rx_buffer, BUFFER_SIZE);
if (SUCCESS == transfer_result){
/* if success, turn on LED1 and LED2, turn off LED3 */
gd_eval_led_on(LED1);
gd_eval_led_on(LED2);
gd_eval_led_off(LED3);
}else{
/* if not, turn off LED1 and LED2, turn on LED3 */
gd_eval_led_off(LED1);
gd_eval_led_off(LED2);
gd_eval_led_on(LED3);
}
while(1){
}
}
DMA配置
void dma_config(void)
{
dma_single_data_parameter_struct dma_init_struct;
/* enable the DMA clock */
rcu_periph_clock_enable(RCU_DMA);
/* configure the DMA channe7 */
dma_deinit(DMA_CH7);
dma_single_data_para_struct_init(&dma_init_struct);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
dma_init_struct.memory0_addr = (uint32_t)tx_buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = BUFFER_SIZE;
dma_init_struct.periph_addr = USART0_TDATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA_CH7, &dma_init_struct);
dma_channel_subperipheral_select(DMA_CH7, DMA_SUBPERI4);
dma_circulation_disable(DMA_CH7);
/* enable DMA channel7 */
dma_channel_enable(DMA_CH7);
/* configure the DMA channel2 */
dma_deinit(DMA_CH2);
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.memory0_addr = (uint32_t)rx_buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = BUFFER_SIZE;
dma_init_struct.periph_addr = USART0_RDATA_ADDRESS;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_single_data_mode_init(DMA_CH2, &dma_init_struct);
dma_channel_subperipheral_select(DMA_CH2, DMA_SUBPERI4);
dma_circulation_disable(DMA_CH2);
/* enable DMA channel2 */
dma_channel_enable(DMA_CH2);
}