hollyedward

  • 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); }  

  • 2024-03-21
  • 发表了主题帖: 【GD32VW553-EVAL试用评测】LCD屏幕与随机数模块TRNG使用

    本帖最后由 hollyedward 于 2024-3-21 22:28 编辑  1、SPI LCD屏幕         GD32VW553H-EVAL 开发板上有一个TFT LCD 显示屏,它支持SPI 接口。在这个Demo 中,分别进行了文字测试、数字测试、画图测试和颜色测试,最终在LCD 屏上显示。         通过 SPI 协议与外部设备进行通信。 串行外设接口(Serial Peripheral Interface,缩写为 SPI)提供了基于 SPI 协议的数据发送和 接收功能,可以工作于主机或从机模式。SPI 接口支持具有硬件 CRC 计算和校验的全双工和 单工模式。 SPI 主要特征         具有全双工、半双工和单工模式的主从操作;         16位宽度,独立的发送和接收缓冲区;         8位或16位数据帧格式;         低位在前或高位在前的数据位顺序;         软件和硬件NSS管理;         硬件CRC计算、发送和校验;         发送和接收支持DMA模式;         支持SPI TI模式;                                              烧录LCD测试程序         运行程序后所有led闪烁一次,会依次进行文字测试、数字测试、画图测试和颜色测试                    但是好像有问题,我看font_test程序里有打印汉字,屏幕没有显示出来           尝试打印4*4的格子,显示不同的颜色,但是有问题                   没有显示别的颜色,字体也没显示,再琢磨看看 void draw_test(void) { __O uint8_t x=0; __O uint8_t y=0; lcd_clear(WHITE); const uint16_t colors[] = { YELLOW, RED, GREEN, }; for(y = 0; y <= 3; y ++){ for(x = 0; x <= 3; x ++){ gui_box(x * 60, y * 60,60,60,0); delay_1ms(100); gui_rect(x * 60, y * 60,60,60,colors[(x + y) % 3]); delay_1ms(100); } } gui_draw_font_gbk16(250,50,RED,WHITE,"Average Temperature Is: "); /* gui box2 */ // gui_box2(20,160,200,120,0); // gui_box2(25,165,205,125,0); // gui_box2(30,170,210,130,0); // /* gui rect */ // gui_rect(10,50,110,70,YELLOW); delay_1ms(1000); }         默认的画图测试 void draw_test(void) { __O uint8_t i=0; lcd_clear(BLUE); gui_draw_font_gbk16(2,20,WHITE, BLUE," GUI Draw Test "); /* gui box */ gui_box(60,90,160,160,GREEN); /* gui box2 */ gui_box2(20,160,200,120,0); gui_box2(25,165,205,125,0); gui_box2(30,170,210,130,0); /* gui rect */ gui_rect(10,50,110,70,YELLOW); /* gui circle */ gui_circle(120,160,80,RED); delay_1ms(1000); }         不懂"__0"这个写法,问了gpt 在C语言中,__0是一个编译器指示器,它告诉编译器将指定的变量放在内存的特定位置。在您的代码中,__O指示器用于将变量i放在内存的第一个字节处。 之所以要这样做,是因为uint8_t类型的变量只能存储8个比特的数据。这意味着i的可能值范围为0到255。如果将i放在内存的第一个字节处,则可以确保i的值不会与其他变量冲突。 2、TRNG 随机数模块         真随机数发生器模块(TRNG)能够通过连续模拟噪声生成一个 32 位的随机数值。(那从环境里收集的噪声数据处理后算不算真随机数呢)                                          随机数种子由模拟电路实现。模拟种子信号输出到一个线性反馈移位寄存器(LFSR)之后在 该寄存器中转化成一个 32 位宽度的随机数。 该模拟种子由几个环形振荡器的输出生成。LFSR 由可配置的 TRNG_CLK 时钟(参考 RCU 相 关章节)驱动,因此随机数质量仅与 TRNG_CLK 时钟有关,与 HCLK 频率无关。 当有足够数量的种子被输入 LFSR 之后,LFSR 会输出 32 位数据到 TRNG_DATA 寄存器。同时,系统会监视模拟种子和 TRNG_CLK 时钟。一旦模拟种子发生错误或者时钟产生错误, TRNG_STAT 寄存器的相关状态位将被置 1,如果 TRNG_CTL 寄存器的 IE 位同时被置 1 还将 产生中断。         烧录相关例程         使用支持Hex输入的串口工具连接         当程序运行时,串口助手将显示初始信息。通过串口助手输入期望的最小值与最大值,之后会自动生成输入范围内的随机数并通过串口助手显示。         但是我这里生成了两个随机数,不应该是生成一个吗                             代码查看         初始化USART接口以及TRNG外设,若TRNG三次仍未初始成功则打印错误信息至USART,进入死循环。         调用trng_random_range_get()方法生成随机数 int main(void) { uint8_t min,max; uint8_t retry = 0; /* USART configuration */ gd_eval_com_init(EVAL_COM0); printf("\r\n /==============Gigadevice TRNG test=============/ \r\n"); /* configure TRNG module */ while((ERROR == trng_configuration()) && retry < 3) { printf("TRNG init fail \r\n"); printf("TRNG init retry \r\n"); retry++; } if(3 == retry) { /* if TRNG initialization is failed */ printf("TRNG init error \r\n"); while(1) { } } /* TRNG initialization is successful */ printf("TRNG init ok \r\n"); while(1) { /* get the random number range */ printf("Please input min num (hex format, the range is 0~0xFF): \r\n"); min = usart_data_get(); printf("The input min num is 0x%x \r\n", min); printf("Please input max num hex format, the range is 0~0xFF): \r\n"); max = usart_data_get(); printf("The input max num is 0x%x \r\n", max); /* input value is invalid */ if(min > max) { printf("Please input correct num \r\n"); continue; } printf("Generate random num1 is 0x%x \r\n", trng_random_range_get(min, max)); printf("Generate random num2 is 0x%x \r\n", trng_random_range_get(min, max)); } }         trng_random_range_get继续调用trng_get_true_random_data方法,trng_get_true_random_data 返回的是寄存器TRNG_DATA的值 /* registers definitions */ #define TRNG_CTL REG32(TRNG + 0x00000000U) /*!< control register */ #define TRNG_STAT REG32(TRNG + 0x00000004U) /*!< status register */ #define TRNG_DATA REG32(TRNG + 0x00000008U) /*!< data register */  

  • 2024-03-16
  • 发表了主题帖: 【GD32VW553-EVAL试用评测】UART用户指令使用

    本帖最后由 hollyedward 于 2024-3-17 16:09 编辑 GD32VW553-EVAL学习笔记(二) 将测试pc机与开发板连接,使用uart指令调试需要两根usb线,一根供电,一根连接uart通信接口。 开发板上电正常启动后,使用相应的uart工具可以接收开发板发送的数据,以及给开发板发送相应的指令。   我在windows平台,使用的是mobaXterm这个软件,支持很多种通信连接。   session-serail选项 选择相应的com端口,以及bps速率,进行连接   有一个usb串口,和两个蓝牙串口,蓝牙uart输出     连接后默认打印信息,烧录的是带蓝牙wifi的msdk,实现了更完整的协议栈   回车后有一个#号,是命令提示符,命令格式和提示符类似linux的。 参考官方文档《GD32VW553 基本指令用户指南》   区分大小写,<>内为必填,[]为option 1、help指令,打印所有指令   2、ble_help 打印和ble有关的指令   3、cpu_stats 打印任务task的cpu占用率和runtime状态运行时间 IDLE应该是python的解释器?   4、sys_ps 设置模式1为cpu深度睡眠模式,当CPU 处于空闲状态时,将自动进入deep sleep,之后可由 wifi/ble 自动唤醒或是通过 uart rx 事件主动唤醒。     WIFI指令 1、wifi_open、wifi_scan 使能wifi功能,开发板上电默认使能 打印开发板扫描到的AP信息,好像只能扫描2.4Ghz的     一些初学问题: 使用uart指令里的wifi_scan 很多时候会出现uart串口掉线的问题,而有的时候又可以,比较玄学 文档里说不能在softap模式下运行wifi_scan,但是设备一直是在STA模式下(station) 感觉可能是扫描wifi对cpu的负载占用太高了。   在IDLE占用97%的时候可以正常执行wifi_scan   有的时候按下重启reset按钮也会掉串口   如果开始一直一运行wifi_scan就掉线串口,按reset或者重连串口还是没用。此时拔掉电源有的时候起作用   2、wifi_connect <ssid> [paaword] wifi名字必须中间不能有空格    换了一个串口调试工具 SecureCRT   连接的时候还是掉了下串口,但是发现指令是被正常执行了,开发板通过dhcp协议从ap那里获取到了分配的ip地址 感觉上面执行wifi_scan的时候应该也执行了,但是串口掉了没能打印出信息   3、ping 命令测试连接连通性 先获取下ping百度的ip地址   在开发板上执行ping命令   查看此时的cpu占用   蓝牙指令 1、ble_help 可以看到msdk_fd是实现了更多的蓝牙协议栈的   同时打开蓝牙和wifi后经常会掉串口   cpu占用也变得更大了     2、ble_adv 1         开发板开启蓝牙广播,使得别的设备可以扫描到与配对 连接蓝牙   3、ble_courier_wifi 蓝牙配网   使用微信小程序“GD32蓝牙配网”,可以搜索到设备   连接成功   但是配网的过程中点击扫描一直没反应,感觉是和前面的wifi_scan的问题有关,输入ssid以及密码也不能连接       初学小白难免会出错,还有很多问题亟待解决,望大佬不吝赐教      

  • 2024-03-08
  • 回复了主题帖: 【安信可BW16-Kit开发板】非接触式温度传感器D6T传感器接入测试

    wangerxian 发表于 2024-3-8 16:55 这一个传感器要多少米? 得捷180,不过我是免费拿的样品

最近访客

< 1/3 >

统计信息

已有73人来访过

  • 芯积分:165
  • 好友:--
  • 主题:16
  • 回复:21

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言