FuShenxiao

  • 2024-12-19
  • 回复了主题帖: 测评入围名单: NXP MCX A系列 FRDM-MCXA156开发板

    个人信息无误,确认可以完成测评分享计划

  • 2024-11-27
  • 发表了主题帖: 《RISC-V 体系结构编程与实践(第2版)》——一些阅读前的准备工作

    本帖最后由 FuShenxiao 于 2024-11-27 09:52 编辑 感谢EEWORLD提供的《RISC-V 体系结构编程与实践(第2版)》书籍测评机会。由于目前考研在即,这里就写一些我的准备工作,以及一些资料网站等,具体阅读任务将在12月底考研完成后进行,并在寒假参与中科院的“一生一芯”项目。   相关操作环境配置: 操作系统:Ubuntu Linux 20.04 GCC版本:9(riscv64-linux-gnu-gcc) QEMU版本:4.2.1 GDB版本:gdb-multiarch   一些官方网站: RISC-V官网:RISC-V International – RISC-V: The Open Standard RISC Instruction Set Architecture (riscv.org) 香山处理器文档:香山 XiangShan (openxiangshan.cc) “一生一芯”项目官网:一生一芯 (oscc.cc) “甲辰计划”官网:甲辰计划 (RISC-V Prosperity 2036) | 甲辰计划 (rv2036.org)   一些RISC-V相关的资料: RISC-V指令集手册   RISC-V体系结构手册   GCC官方手册   汇编器(AS)官方手册   链接器(LD)手册   RVV手册   RISC-V高速缓存维护指令扩展手册   PLIC手册   RISC-V ABI接口手册   RISC-V SBI接口手册   一些视频教程: [完结] 循序渐进,学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春_哔哩哔哩_bilibili “一生一芯”概述 [第六期“一生一芯”计划 - P1]_哔哩哔哩_bilibili    

  • 2024-11-20
  • 回复了主题帖: 读书入围名单:《RISC-V 体系结构编程与实践(第2版)》

    个人信息无误,确认可以完成阅读计划和打卡任务

  • 2024-11-06
  • 回复了主题帖: 【求助】小学六年级的题目,我硬是不会做

    okhxyyo 发表于 2024-11-6 17:23 嘿,你这个图画的好。我感觉一定不是这么算,一定是哪里有个小窍门。我想想啊 这几天在考研,这种是二重积分极坐标法的经典题型:Sad:

  • 回复了主题帖: 【求助】小学六年级的题目,我硬是不会做

    okhxyyo 发表于 2024-11-6 16:07 不知道怎么入手了都 感觉像是考研难度的,得用到二重积分

  • 2024-11-02
  • 加入了学习《DIY作品演示》,观看 阿尔达H-30T高温休眠版恒温电烙铁试用体验

  • 2024-10-24
  • 回复了主题帖: STM32H7S78-DK测评(四)——RTC测试

    freebsder 发表于 2024-10-24 19:19 S又是个主打哪个方向的型号? 我觉得主要是它的高性能图像显示能力吧,主要吸引人的是它DMA2D,也就是GPU功能,这块开发板的屏幕有480*800,已经挺大的了。

  • 发表了主题帖: STM32H7S78-DK测评(四)——RTC测试

    本帖最后由 FuShenxiao 于 2024-10-24 16:58 编辑 STM32的RTC指的是实时时钟(Real-Time Clock),它是一种用于计时和日期记录的硬件模块。在STM32微控制器中,RTC模块是由一个32位的计数器和一组用于保存日期和时间的寄存器组成的。由于RTC具有较高的精度和稳定性,从而能保持准确的日期和时间信息,因此它常用于需要精确定时和实时数据处理的应用场景。 本来以为RTC实现挺简单的,没想到这里还有那么多坑,主要是CubeMX代码生成存在一些问题。 在CubeMX中配置 选择STM32H7S78-DK模板 配置RTC      为了能输出时间和日期,还需要配置UART4   代码编写 生成RTC初始化代码如下 void MX_RTC_Init(void) { /* USER CODE BEGIN RTC_Init 0 */ /* USER CODE END RTC_Init 0 */ RTC_PrivilegeStateTypeDef privilegeState = {0}; RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef sDate = {0}; /* USER CODE BEGIN RTC_Init 1 */ /* USER CODE END RTC_Init 1 */ /** Initialize RTC Only */ hrtc.Instance = RTC; hrtc.Init.HourFormat = RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv = 127; hrtc.Init.SynchPrediv = 255; hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE; hrtc.Init.BinMode = RTC_BINARY_NONE; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } privilegeState.rtcPrivilegeFull = RTC_PRIVILEGE_FULL_NO; privilegeState.backupRegisterPrivZone = RTC_PRIVILEGE_BKUP_ZONE_NONE; privilegeState.backupRegisterStartZone2 = RTC_BKP_DR0; privilegeState.backupRegisterStartZone3 = RTC_BKP_DR0; if (HAL_RTCEx_PrivilegeModeSet(&hrtc, &privilegeState) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN Check_RTC_BKUP */ /* USER CODE END Check_RTC_BKUP */ /** Initialize RTC and set the Time and Date */ sTime.Hours = 0x15; sTime.Minutes = 0x0; sTime.Seconds = 0x0; sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; sTime.StoreOperation = RTC_STOREOPERATION_RESET; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } sDate.WeekDay = RTC_WEEKDAY_THURSDAY; sDate.Month = RTC_MONTH_OCTOBER; sDate.Date = 0x24; sDate.Year = 0x24; if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN RTC_Init 2 */ /* USER CODE END RTC_Init 2 */ } 编写printf重定向 int fputc(int ch,FILE *p) { char c=ch; HAL_UART_Transmit(&huart4,(unsigned char *)&c,1,50); return ch; } void UART4_SendByte(char c) { HAL_UART_Transmit(&huart4,(unsigned char *)&c,1,50); } void UART4_SendData(char *p,int len) { HAL_UART_Transmit(&huart4,(unsigned char *)p,len,50); } 编写输出RTC日历的代码 static void RTC_CalendarShow(uint8_t *showtime, uint8_t *showdate) { RTC_DateTypeDef sdatestructureget; RTC_TimeTypeDef stimestructureget; /* Get the RTC current Time */ HAL_RTC_GetTime(&hrtc, &stimestructureget, RTC_FORMAT_BIN); /* Get the RTC current Date */ HAL_RTC_GetDate(&hrtc, &sdatestructureget, RTC_FORMAT_BIN); /* Display time Format : hh:mm:ss */ sprintf((char *)showtime, "%2d:%2d:%2d", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds); /* Display date Format : mm-dd-yyyy */ sprintf((char *)showdate, "%2d-%2d-%2d", sdatestructureget.Month, sdatestructureget.Date, 2000 + sdatestructureget.Year); printf("%s\r\n", showtime); printf("%s\r\n", showdate); } 初始化时间/日期字符串 uint8_t aShowTime[16] = "hh:ms:ss"; uint8_t aShowDate[16] = "mm-dd-yyyy"; 在主函数循环中加入如下代码 RTC_CalendarShow(aShowTime, aShowDate); HAL_Delay(1000); 但是,这样还是有问题的。 问题出在 HAL_StatusTypeDef RTC_EnterInitMode(RTC_HandleTypeDef *hrtc) 其代码如下 HAL_StatusTypeDef RTC_EnterInitMode(RTC_HandleTypeDef *hrtc) { uint32_t tickstart; HAL_StatusTypeDef status = HAL_OK; /* Check if the Initialization mode is set */ if (READ_BIT(RTC->ICSR, RTC_ICSR_INITF) == 0U) { /* Set the Initialization mode */ SET_BIT(RTC->ICSR, RTC_ICSR_INIT); tickstart = HAL_GetTick(); /* Wait till RTC is in INIT state and if Time out is reached exit */ while ((READ_BIT(RTC->ICSR, RTC_ICSR_INITF) == 0U) && (status != HAL_TIMEOUT)) { if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE) { /* New check to avoid false timeout detection in case of preemption */ if (READ_BIT(RTC->ICSR, RTC_ICSR_INITF) == 0U) { status = HAL_TIMEOUT; /* Change RTC state */ hrtc->State = HAL_RTC_STATE_TIMEOUT; } else { break; } } } } return status; } 问题出现在执行 SET_BIT(RTC->ICSR, RTC_ICSR_INIT); 之后,寄存器的初始化位并没有置1。 阅读手册可知需要对DBP置1才能使能写RTC寄存器。   在进入RTC之前会调用 HAL_RTC_MspInit(hrtc);   于是我们就可以进入 HAL_RTC_MspInit 中使能写RTC寄存器,添加一句 HAL_PWR_EnableBkUpAccess(); 用于允许访问备份区。 void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle) { RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(rtcHandle->Instance==RTC) { /* USER CODE BEGIN RTC_MspInit 0 */ HAL_PWR_EnableBkUpAccess(); /* USER CODE END RTC_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC; PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* RTC clock enable */ __HAL_RCC_RTC_ENABLE(); __HAL_RCC_RTCAPB_CLK_ENABLE(); /* USER CODE BEGIN RTC_MspInit 1 */ /* USER CODE END RTC_MspInit 1 */ } } 于是RTC就可以正常使用了。 结果展示     完整工程代码

  • 2024-10-13
  • 回复了主题帖: STM32H7S78-DK测评(三)——OV5640摄像头测试

    lugl4313820 发表于 2024-10-13 19:41 楼主是太历害了呀,可以分享开发板的设计吗? 这块开发板是ST官方的开发板,ST官网上能找到开发板资料的。如果你说的是OV5640转接板,我已经在嘉立创开源了。

  • 2024-10-08
  • 回复了主题帖: STM32H7S78-DK测评(三)——OV5640摄像头测试

    cc1989summer 发表于 2024-10-8 14:00 楼主。ST官方手册有说的,配套摄像头模块型号是MB1683,Mouser上售价400+元人民币。 抱歉我没仔细读文档,不过这个摄像头也是基于OV5640的,我看e络盟要将近300块,但是感觉都挺贵的,还是自己画个转接板划算  

  • 2024-10-07
  • 发表了主题帖: STM32H7S78-DK测评(三)——OV5640摄像头测试

    本帖最后由 FuShenxiao 于 2024-10-7 11:55 编辑 转接板设计 观察开发板原理图摄像头接口可以发现,接口居然有30pin,这和软排线连接的OV5640(24pin)或者OV5640模块(2*9pin)显然是不相同的。 恰好我手头有一块正点原子OV5640摄像头模块,于是我就针对它完成转接板的设计。 观察正点原子OV5640摄像头模块原理图可以轻易地与STM32H7S78-DK的摄像头接口相对应。 I2C1_SCL   --->   OV_SCL I2C1_SDA   --->   OV_SDA DCMI_D0 ~ DMCI_D7   --->   OV_D0 ~ OV_D7 RSTI   --->   OV_RESET PWR_EN   --->   OV_PWDN DCMI_VSYNC   --->   OV_VSYNC DCMI_HSYNC   --->   OV_HREF DCMI_PIXCLK   --->   OV_PCLK  在嘉立创中绘制原理图如下 绘制PCB如下。为了保证相关电学特性稳定,需要在OV5640供电端加入100nF的去耦电容,在SCL和SDA两个IIC接口上加入4.7kΩ的上拉电阻。 最终得到PCB正反面如下图所示     具体工程文件参见STM32H7S78-DK摄像头转接板 - 立创开源硬件平台 (oshwhub.com) 引脚配置 首先在CubeMX中配置时钟(这里不再赘述) 接着分别配置DCMIPP(摄像头接口)和LTDC(显示屏接口)   配置两个指示灯 完成以上操作后,生成代码 代码编写 OV5640初始化代码,需要注意的是,OV5640默认原始图像是上下翻转的,所以要使用OV5640_FLIP将图像翻转 static uint32_t OV5640_Config(uint32_t Resolution, uint32_t PixelFormat) { OV5640_IO_t IOCtx; uint32_t id; uint32_t ret = OV5640_OK; static OV5640_Object_t OV5640Obj; /* Configure the Camera driver */ IOCtx.Address = CAMERA_OV5640_ADDRESS; IOCtx.Init = BSP_I2C1_Init; IOCtx.DeInit = BSP_I2C1_DeInit; IOCtx.ReadReg = BSP_I2C1_ReadReg16; IOCtx.WriteReg = BSP_I2C1_WriteReg16; IOCtx.GetTick = BSP_GetTick; /* Register Bus IO */ if(OV5640_RegisterBusIO (&OV5640Obj, &IOCtx) != OV5640_OK) { ret = OV5640_ERROR; } /* Read ID */ if(OV5640_ReadID(&OV5640Obj, &id) != OV5640_OK) { ret = OV5640_ERROR; } if(id == OV5640_ID) { /* Initialize the camera Module */ Camera_Drv = (CAMERA_Drv_t *) &OV5640_CAMERA_Driver; OV5640_DeInit(&OV5640Obj); if(Camera_Drv->Init(&OV5640Obj, Resolution, PixelFormat) != OV5640_OK) { ret = OV5640_ERROR; } else if(Camera_Drv->MirrorFlipConfig(&OV5640Obj, OV5640_FLIP) != OV5640_OK) { ret = OV5640_ERROR; } } return ret; } 在主函数中使用如下代码完成OV5640初始化 if(OV5640_Config(OV5640_R480x272, OV5640_RGB565) != OV5640_OK) { /* Camera Module Config KO */ Error_Handler(); } DCMIPP接口获取数据信息 HAL_StatusTypeDef HAL_DCMIPP_PIPE_Start(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe, uint32_t DstAddress, uint32_t CaptureMode) { assert_param(IS_DCMIPP_PIPE(Pipe)); assert_param(IS_DCMIPP_CAPTURE_MODE(CaptureMode)); /* Check pointer validity */ if ((hdcmipp == NULL) || ((DstAddress & 0xFU) != 0U)) { return HAL_ERROR; } /* Check DCMIPP pipe state */ if (hdcmipp->PipeState[Pipe] != HAL_DCMIPP_PIPE_STATE_READY) { return HAL_ERROR; } /* Set Capture Mode and Destination address for the selected pipe */ DCMIPP_SetConfig(hdcmipp, Pipe, DstAddress, CaptureMode); /* Enable Capture for the selected Pipe */ DCMIPP_EnableCapture(hdcmipp, Pipe); return HAL_OK; } 在主函数中用如下代码完成DCMIPP接口数据接收,将接收到的图像信息传到CAMERA_FRAME_BUFFER中。CAMERA_FRAME_BUFFER后面的传参为当前运行模式,除了可以选择DCMIPP_MODE_CONTINUOUS,还可以使用DCMIPP_MODE_SNAPSHOT if(HAL_DCMIPP_PIPE_Start(&phdcmipp, DCMIPP_PIPE0, (uint32_t)CAMERA_FRAME_BUFFER , DCMIPP_MODE_CONTINUOUS) != HAL_OK) { Error_Handler(); } 也可以使用双缓冲模式,代码如下 HAL_StatusTypeDef HAL_DCMIPP_PIPE_DoubleBufferStart(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe, uint32_t DstAddress0, uint32_t DstAddress1, uint32_t CaptureMode) { assert_param(IS_DCMIPP_PIPE(Pipe)); assert_param(IS_DCMIPP_CAPTURE_MODE(CaptureMode)); /* Check pointer validity */ if ((hdcmipp == NULL) || ((DstAddress0 & 0xFU) != 0U) || ((DstAddress1 & 0xFU) != 0U)) { return HAL_ERROR; } /* Check DCMIPP pipe state */ if (hdcmipp->PipeState[Pipe] != HAL_DCMIPP_PIPE_STATE_READY) { return HAL_ERROR; } /* Set Capture Mode and Destination addresses for the selected pipe */ DCMIPP_SetDBMConfig(hdcmipp, Pipe, DstAddress0, DstAddress1, CaptureMode); /* Enable Capture for the selected Pipe */ DCMIPP_EnableCapture(hdcmipp, Pipe); return HAL_OK; } 最后在显示屏上显示图像,在这一步中,需要将CAMERA_FRAME_BUFFER的数据显示到屏幕的指定区域内 static void MX_LTDC_Init(void) { /* USER CODE BEGIN LTDC_Init 0 */ /* USER CODE END LTDC_Init 0 */ LTDC_LayerCfgTypeDef pLayerCfg = {0}; /* USER CODE BEGIN LTDC_Init 1 */ /* USER CODE END LTDC_Init 1 */ hltdc.Instance = LTDC; hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL; hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL; hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL; hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC; hltdc.Init.HorizontalSync = 3; hltdc.Init.VerticalSync = 3; hltdc.Init.AccumulatedHBP = 11; hltdc.Init.AccumulatedVBP = 11; hltdc.Init.AccumulatedActiveW = 811; hltdc.Init.AccumulatedActiveH = 491; hltdc.Init.TotalWidth = 819; hltdc.Init.TotalHeigh = 499; hltdc.Init.Backcolor.Blue = 0; hltdc.Init.Backcolor.Green = 0; hltdc.Init.Backcolor.Red = 0; if (HAL_LTDC_Init(&hltdc) != HAL_OK) { Error_Handler(); } pLayerCfg.WindowX0 = xpos; pLayerCfg.WindowX1 = xpos + ImageWidth; pLayerCfg.WindowY0 = ypos; pLayerCfg.WindowY1 = ypos + ImageHeight; pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; pLayerCfg.Alpha = 255; pLayerCfg.Alpha0 = 0; pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA; pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA; pLayerCfg.FBStartAdress = (uint32_t)CAMERA_FRAME_BUFFER; pLayerCfg.ImageWidth = ImageWidth; pLayerCfg.ImageHeight = ImageHeight; pLayerCfg.Backcolor.Blue = 0; pLayerCfg.Backcolor.Green = 0; pLayerCfg.Backcolor.Red = 0; if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN LTDC_Init 2 */ /* USER CODE END LTDC_Init 2 */ } 结果展示 当程序正常运行时,LD1会持续闪烁,OV5640中的内容也会实时传输到屏幕上,传输显示的过程还是较为流畅的。 不过噪声的问题似乎有些严重,面对比较白和亮的地方会产生一些紫色的点点,但是对着暗色调的就不会产生这个问题(比如我的书包,桌子在椅背的阴影部分)。考虑PCB绘制部分,网上对OV5640的建议是信号线之间的线长差距小于100mil,这个我符合要求。再考虑软排线的长度,10cm在诸多网友看来似乎确实有些长了,下一步尝试换短一点的软排线再试试。 不过让我费解的是,官方的板子既然做了这个接口,为什么不顺便再做一个适配的摄像头呢,就像STM32MP135F-DK开发板还配套摄像头模块。而且这个30pin的接口根本无法适配OV5640,还得自己画转接板。       [localvideo]6ff2fc34925b327c32ce635fc53741fe[/localvideo]  程序源码:

  • 2024-10-03
  • 回复了主题帖: STM32H7S78-DK测评(二)——用串口实现手写数字体识别(失败)

    cc1989summer 发表于 2024-10-3 10:35 比较有创意的设计。   STM32H7S78-DK有触摸屏,要是能直接脱离电脑直接在开发板上运行就好了( ... 对的,我下一步就想这么做了,不过深度学习的办法似乎行不通,我得尝试用别的识别手段

  • 2024-10-02
  • 发表了主题帖: STM32H7S78-DK测评(二)——用串口实现手写数字体识别(失败)

    本帖最后由 FuShenxiao 于 2024-10-2 22:49 编辑 本次测试基于b站教程:教程来了!!STM32手写数字识别!!!_哔哩哔哩_bilibili up主工程开源地址:https://github.com/colin2135/STM32G070_AI_TEST.git 上位机测试软件地址:https://github.com/colin2135/HandWriteApp.git 模型训练与保存 作为深度学习的入门教程,现在网上介绍MNIST手写数字体识别的教程已经很多了。这里贴一段用keras生成.h5文件的代码,不过为了和跟随up主的教程,我最后用了GitHub上的.tflite模型文件。 from keras.datasets import mnist import matplotlib.pyplot as plt from keras.models import Sequential from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense from keras.utils import np_utils import tensorflow as tf config = tf.compat.v1.ConfigProto() config.gpu_options.allow_growth = True sess = tf.compat.v1.Session(config=config) # 设定随机数种子,使得每个网络层的权重初始化一致 # np.random.seed(10) # x_train_original和y_train_original代表训练集的图像与标签, x_test_original与y_test_original代表测试集的图像与标签 (x_train_original, y_train_original), (x_test_original, y_test_original) = mnist.load_data() """ 数据可视化 """ # 原始数据量可视化 print('训练集图像的尺寸:', x_train_original.shape) print('训练集标签的尺寸:', y_train_original.shape) print('测试集图像的尺寸:', x_test_original.shape) print('测试集标签的尺寸:', y_test_original.shape) """ 数据预处理 """ # 从训练集中分配验证集 x_val = x_train_original[50000:] y_val = y_train_original[50000:] x_train = x_train_original[:50000] y_train = y_train_original[:50000] # 打印验证集数据量 print('验证集图像的尺寸:', x_val.shape) print('验证集标签的尺寸:', y_val.shape) print('======================') # 将图像转换为四维矩阵(nums,rows,cols,channels), 这里把数据从unint类型转化为float32类型, 提高训练精度。 x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32') x_val = x_val.reshape(x_val.shape[0], 28, 28, 1).astype('float32') x_test = x_test_original.reshape(x_test_original.shape[0], 28, 28, 1).astype('float32') # 原始图像的像素灰度值为0-255,为了提高模型的训练精度,通常将数值归一化映射到0-1。 x_train = x_train / 255 x_val = x_val / 255 x_test = x_test / 255 print('训练集传入网络的图像尺寸:', x_train.shape) print('验证集传入网络的图像尺寸:', x_val.shape) print('测试集传入网络的图像尺寸:', x_test.shape) # 图像标签一共有10个类别即0-9,这里将其转化为独热编码(One-hot)向量 y_train = np_utils.to_categorical(y_train) y_val = np_utils.to_categorical(y_val) y_test = np_utils.to_categorical(y_test_original) """ 定义网络模型 """ def CNN_model(): model = Sequential() model.add(Conv2D(filters=16, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) model.add(Conv2D(filters=32, kernel_size=(5, 5), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) model.add(Flatten()) model.add(Dense(100, activation='relu')) model.add(Dense(10, activation='softmax')) print(model.summary()) return model """ 训练网络 """ model = CNN_model() # 编译网络(定义损失函数、优化器、评估指标) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # 开始网络训练(定义训练数据与验证数据、定义训练代数,定义训练批大小) train_history = model.fit(x_train, y_train, validation_data=(x_val, y_val), epochs=10, batch_size=32, verbose=2) # 模型保存 model.save('model.h5') # 定义训练过程可视化函数(训练集损失、验证集损失、训练集精度、验证集精度) def show_train_history(train_history, train, validation): plt.plot(train_history.history[train]) plt.plot(train_history.history[validation]) plt.title('Train History') plt.ylabel(train) plt.xlabel('Epoch') plt.legend(['train', 'validation'], loc='best') plt.show() show_train_history(train_history, 'accuracy', 'val_accuracy') show_train_history(train_history, 'loss', 'val_loss') CubeMX配置 安装CubeAI   在CubeMX上方Software Packs下拉选择Select Components,选择其中的X-CUBE-AI   在左侧菜单栏选择Middleware and Software Packs,选择其中的X-CUBE-AI,导入模型并分析。如果这个模型过大,超过了flash的大小,可能还需要对模型进行压缩,并配置外部flash。   这里可能有人会问为什么用的是7.3.0版本的CubeAI,而不用更高版本的CubeAI。我尝试了8.1.0和9.0.0两个版本的CubeAI,在模型验证阶段均出现tool error: 'gbk' codec can't encode character的报错,可能高版本的CubeAI对gbk的适配能力不太好吧。 串口配置 观察开发板原理图可以发现,PD0和PD1可以做虚拟串口使用,对应的是UART4。   开启UART4并设置为异步模式。由于需要串口收发,所以还要使能串口接收中断。   最后使能DEBUG功能   代码编写 由于此次测试不需要TouchGFX(keil对TouchGFX的适配不是很好,总是缺文件),所以用的keil编写。 首先包含相关头文件 #include "stdio.h" #include "string.h" #include "ai_platform.h" #include "network.h" #include "network_data.h" 由于需要串口收发数据,因此需要对printf进行重定向,并在魔术棒里开启microLIB /* USER CODE BEGIN 0 */ /** * @brief 重定向c库函数printf到USARTx * @retval None */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, 0xffff); return ch; } /** * @brief 重定向c库函数getchar,scanf到USARTx * @retval None */ int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart4, &ch, 1, 0xffff); return ch; } 定义AI模型相关参数,并声明后续使用到的一些函数 ai_handle network; float aiInData[AI_NETWORK_IN_1_SIZE]; float aiOutData[AI_NETWORK_OUT_1_SIZE]; ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE]; ai_buffer * ai_input; ai_buffer * ai_output; static void AI_Init(void); static void AI_Run(float *pIn, float *pOut); void PictureCharArrayToFloat(uint8_t *srcBuf,float *dstBuf,int len); void Uart_send(char * str); #define UART_BUFF_LEN 1024 #define ONE_FRAME_LEN 1+784+2 uint16_t uart_rx_length = 0; uint8_t uart_rx_byte = 0; uint8_t uart_rx_buffer[UART_BUFF_LEN]; volatile uint8_t goRunning = 0; /* USER CODE END 0 */ 定义串口中断回调函数 /* USER CODE BEGIN 4 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) { if(goRunning ==0) { if (uart_rx_length < UART_BUFF_LEN) { uart_rx_buffer[uart_rx_length] = uart_rx_byte; uart_rx_length++; if (uart_rx_byte == '\n') { goRunning = 1; } } else { //rt_kprintf("rx len over"); uart_rx_length = 0; } } HAL_UART_Receive_IT(&huart4, (uint8_t *)&uart_rx_byte, 1); } 定义串口发送函数 void Uart_send(char * str) { HAL_UART_Transmit(&huart4, (uint8_t *)str, strlen(str),0xffff); } 定义AI模型初始化函数 static void AI_Init(void) { ai_error err; /* Create a local array with the addresses of the activations buffers */ const ai_handle act_addr[] = { activations }; /* Create an instance of the model */ err = ai_network_create_and_init(&network, act_addr, NULL); if (err.type != AI_ERROR_NONE) { printf("ai_network_create error - type=%d code=%d\r\n", err.type, err.code); Error_Handler(); } ai_input = ai_network_inputs_get(network, NULL); ai_output = ai_network_outputs_get(network, NULL); } 定义AI模型运行函数 static void AI_Run(float *pIn, float *pOut) { char logStr[100]; int count = 0; float max = 0; ai_i32 batch; ai_error err; /* Update IO handlers with the data payload */ ai_input[0].data = AI_HANDLE_PTR(pIn); ai_output[0].data = AI_HANDLE_PTR(pOut); batch = ai_network_run(network, ai_input, ai_output); if (batch != 1) { err = ai_network_get_error(network); printf("AI ai_network_run error - type=%d code=%d\r\n", err.type, err.code); Error_Handler(); } for (uint32_t i = 0; i < AI_NETWORK_OUT_1_SIZE; i++) { sprintf(logStr,"%d %8.6f\r\n",i,aiOutData[i]); Uart_send(logStr); if(max<aiOutData[i]) { count = i; max= aiOutData[i]; } } sprintf(logStr,"current number is %d\r\n",count); Uart_send(logStr); } 定义将串口收到的uint8_t类型数据转换为float类型函数 void PictureCharArrayToFloat(uint8_t *srcBuf,float *dstBuf,int len) { for(int i=0;i<len;i++) { dstBuf[i] = srcBuf[i];//==1?0:1; } } /* USER CODE END 4 */ 主函数部分,需要完成外设初始化以及模型运行逻辑的书写 int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Update SystemCoreClock variable according to RCC registers values. */ SystemCoreClockUpdate(); /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_CRC_Init(); MX_FLASH_Init(); MX_UART4_Init(); /* USER CODE BEGIN 2 */ __HAL_RCC_CRC_CLK_ENABLE(); AI_Init(); memset(uart_rx_buffer,0,784); HAL_UART_Receive_IT(&huart4, (uint8_t *)&uart_rx_byte, 1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ char str[10]; if(goRunning>0) { if(uart_rx_length == ONE_FRAME_LEN) { PictureCharArrayToFloat(uart_rx_buffer+1,aiInData,28*28); AI_Run(aiInData, aiOutData); } memset(uart_rx_buffer,0,784); goRunning = 0; uart_rx_length = 0; } } /* USER CODE END 3 */ } 至此,代码部分就完成了。但是当我烧录运行程序的时候,发生了如下报错,定位到报错的函数为 ai_network_init_and_create() ,说明模型创建失败。 ai_network_create error - type=51 code=65 在网上诸多类似问题的帖子中,存在类似问题的大多都是H7系列芯片,而一般建议的方案都是检查是否开启CRC,以下两张图是ST员工对该问题的回复     然而,当我在AI模型初始化在 AI_Init() 之前加入 __HAL_RCC_CRC_CLK_ENABLE() 用于CRC开启使能,还是发生了如上的报错。 如果有了解这个问题的大佬,还请指导一下,我将不胜感激。 整个工程如下: STM32G431RBT6实现 依照该up主的做法,我看网上已有用F4,F7实现的,而该up主用的是G0的芯片。我宿舍里刚好有一块蓝桥杯嵌入式的板子,上面搭载的是STM32G431RBT6,接下来就尝试一下G4是否能实现手写数字体识别。 AI模型导入流程与上文一致,引脚配置如下。其中PF0和PF1用于晶振信号输入,PA9和PA10用于串口收发,PA13和PA14用于DEBUG。     代码部分也与上文一致,仅需将huart4改为huart1即可。 编写烧录,运行成功!说明G4也是能部署AI模型用于MNIST手写数字体识别的。  

  • 2024-10-01
  • 发表了主题帖: STM32CubeAI中 ai_network_create_and_init 不成功

    本帖最后由 FuShenxiao 于 2024-10-1 10:59 编辑 我将模型导入CubeMX中的CubeAI,生成代码后进行模型初始化 定义相关参数 ai_handle network; float aiInData[AI_NETWORK_IN_1_SIZE]; float aiOutData[AI_NETWORK_OUT_1_SIZE]; ai_u8 activations[AI_NETWORK_DATA_ACTIVATIONS_SIZE]; 模型初始化 static void AI_Init(void) { ai_error err; /* Create a local array with the addresses of the activations buffers */ const ai_handle act_addr[] = { activations }; /* Create an instance of the model */ err = ai_network_create_and_init(&network, act_addr, NULL); if (err.type != AI_ERROR_NONE) { printf("ai_network_create error - type=%d code=%d\r\n", err.type, err.code); Error_Handler(); } ai_input = ai_network_inputs_get(network, NULL); ai_output = ai_network_outputs_get(network, NULL); } 这个代码是原封不动抄自GitHub的代码,但是模型初始化失败,并报错 ai_network_create error - type=51 code=65 网上大多建议开启CRC,但是我已经在 AI_Init() 之前加入 __HAL_RCC_CRC_CLK_ENABLE(),但是模型初始化依然失败 请问是否有遇到过类似问题的,如果能帮忙解答我将感激不尽。

  • 2024-09-17
  • 发表了主题帖: STM32H7S78-DK测评(一)——TouchGFX控制板载LED

    本帖最后由 FuShenxiao 于 2024-9-17 17:34 编辑 非常感谢EEWORLD和ST提供的测评机会。这块开发板搭载的STM32H7S7L8是一颗相当高性能的MCU,这块板子的做工也是十分精美。大家可以观看b站的介绍视频,无论是计算性能还是图形显示能力都是相当厉害的【新品速递】STM32H7R/S高性能MCU:高安全性,大存储和优异图显_哔哩哔哩_bilibili 作为ST官方的Discovery系列开发板,TouchGFX在这上面的开发就显得轻而易举了。 软件准备: TouchGFX Designer(这个在官网下载最新版本,里面才有STM32H7S78-DK的模板) STM32CubeIDE(须安装好STM32H7RS的芯片固件包,不选用MDK-ARM可能是不太适配,试了好久总是缺文件,而CubeIDE就能正常编译) STM32CubeMX(须安装好STM32H7RS的芯片固件包) 在TouchGFX中绘制UI 在TouchGFX中选择对应的开发板     绘制UI,包括标题与四个控制按键。四个按键用于控制板载LD1-LD4四个用户自定义灯。   分别配置四个按键的反转触发。配置完成后点击屏幕右下角的代码生成即可生成代码。   生成文件目录如下     在CubeIDE中烧录 选择生成的STM32CubeIDE文件夹下的工程,对生成的代码进行编译和烧录,即可在开发板的屏幕上观察到在TouchGFX中绘制的画面。       配置用户灯 观察开发板原理图可以发现,LD1和LD2为高电平点亮,而LD3和LD4为低电平点亮。   进入CubeMX进行引脚配置   TouchGFX按键代码编写 进入STM32H7S78-DK_Appli/Application/User/generated/Screen1ViewBase.cpp,可以看到已经有了对于反转LED灯的函数调用,此处的四个ledToggle()均为虚函数。 void Screen1ViewBase::buttonCallbackHandler(const touchgfx::AbstractButton& src) { if (&src == &toggleButton1) { //Interaction1 //When toggleButton1 clicked call virtual function //Call led1Toggle led1Toggle(); } if (&src == &toggleButton2) { //Interaction2 //When toggleButton2 clicked call virtual function //Call led2Toggle led2Toggle(); } if (&src == &toggleButton3) { //Interaction3 //When toggleButton3 clicked call virtual function //Call led3Toggle led3Toggle(); } if (&src == &toggleButton4) { //Interaction4 //When toggleButton4 clicked call virtual function //Call led4Toggle led4Toggle(); } } 进入gui/Screen1View.cpp,添加以下代码 #include "main.h" void Screen1View::led1Toggle() { HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin); } void Screen1View::led2Toggle() { HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); } void Screen1View::led3Toggle() { HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin); } void Screen1View::led4Toggle() { HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin); } 在gui/Screen1View.hpp中添加void ledToggle()成员函数定义,这个函数继承自Screen1ViewBase。 #ifndef SCREEN1VIEW_HPP #define SCREEN1VIEW_HPP #include <gui_generated/screen1_screen/Screen1ViewBase.hpp> #include <gui/screen1_screen/Screen1Presenter.hpp> class Screen1View : public Screen1ViewBase { public: Screen1View(); virtual ~Screen1View() {} virtual void setupScreen(); virtual void tearDownScreen(); void led1Toggle(); void led2Toggle(); void led3Toggle(); void led4Toggle(); protected: }; #endif // SCREEN1VIEW_HPP 再次编译烧录,即可实现用TouchGFX按键控制用户灯亮灭。 [localvideo]ef761969489ae72df89af24b9feaee44[/localvideo] 工程源代码: https://download.eeworld.com.cn/detail/FuShenxiao/634343

  • 上传了资料: STM32H7S78-DK测评(一)——TouchGFX控制板载LED_源代码

  • 2024-09-09
  • 回复了主题帖: 测评入围名单:STM32H7R/S 高性能MCU 开启全新的创新机遇!纵享ST 2024高性能新品!

    个人信息无误,确认可以完成评测计划

  • 2024-09-05
  • 回复了主题帖: 中兴面试题的问题

    %*d相当于跳过,这样子10输入到a,20输入的时候跳过,30输入到b,然后没有输入了,c一开始定义是0,所以最后a=10,b=30,c=0输出

  • 2024-08-30
  • 回复了主题帖: 小熊派BearPi-Pico H2821星闪开发板测评(七)——传输与功耗测试

    Jacktang 发表于 2024-8-30 07:45 12Mbps时丢包率约为25%,这个有点大吧 因为我是1ms发送一个数据,然后算起来一秒钟是12Mb,所以这个丢包率可能是会大的。如果能几百微秒发个数据,那应该就可以了。

  • 2024-08-28
  • 发表了主题帖: 小熊派BearPi-Pico H2821星闪开发板测评(七)——传输与功耗测试

    本帖最后由 FuShenxiao 于 2024-8-28 22:30 编辑 官方案例中已经编写了测试传输速率的代码。 配置测速跑流模式 Server和Client的配置与SLE透传测试的配置一致,除了配置SLE之外,还需要将运行模式设置为测速跑流模式。 具体流程为:进入Application->选择SLE UART Sample Configuration->选择Select SLE UART sample mode->选择Select sle uart sample type->选择Enable SLE UART low lantensy sample->退出选择Set the current sample in PERFORMANCE mode 完成以上配置后退出保存,分别烧录到Server和Client中即可           打开串口助手,可以看到实现的是由Server端发送数据到Client端,Client端显示传输用时和传输速率。这里应该是传输1000条数据,平均每条数据用时500us,传输速率大约4000kbps(不知道我理解的对不对)   SLE功耗测试 官方说明默认发射功率为6dBm,大概是1.26mW。 无论是Server发送数据还是Client接收数据,还是二者无发送/接收任务时,功耗均保持在0.15W-0.16W之间,这说明功耗的绝大部分都在芯片和外围电路上了,天线上的功耗几乎可以忽略不计了。     SLE传输极限测试 这一步是我突发奇想的方式,我也不知道是否合理。如果想用标准手段,可以参照@不爱胡萝卜的仓鼠大佬的文章[BearPi-Pico H2821]测评 ⑥丢包及连接稳定性测试 - RF/无线 - 电子工程世界-论坛 (eeworld.com.cn) 这里我直接用了正点原子串口调试助手的自动循环发送功能。   比如说这里我连续发送了五个1,那么左侧的Server发送端就会显示发送了5个字节,右边的Client客户端就比较麻烦,它的1每一次接收都要显示两遍,所以我们可以把这个日志信息复制下来,然后使用word的查找功能找出总共有多少个1,除以2就得到接收到的个数了,这样就能测试传输速率和丢包率。Client发送Server也是同理。 当然,正点原子的自动循环发送的间隔最小只有1ms,如果想测试传输的极限,那么我们可以发送更长的字符串(这个过程可能还是无法测试传输速率的极限,而且这种方法需要依赖UART,因此可能会受限于UART的传输速率,但是我个人认为实际上不会受限,因为串口的传输速率远大于SLE)   这里我们直接把两块开发板靠在一块,试试它传输的极限在哪里。 最终测试结果为在8Mbps左右出现丢包,12Mbps时丢包率约为25%,考虑到我的实验方法可能不太正确,官方标注的星闪最大12Mbps的传输速率我认为是存在的。 传输距离 官方在8月11日的时候进行了星闪的拉距测试得到的结果是 棒状天线:断连:921米;重连:789米 板载PCB天线:断连:832米;重连:811米 详细可以看小熊派公众号视频。 发射功率6dBm,这个传输距离我个人认为还是挺夸张的。

统计信息

已有78人来访过

  • 芯积分:379
  • 好友:--
  • 主题:14
  • 回复:14

留言

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


现在还没有留言