电赛备赛日记(一):K210与STM32串口通信

2023-05-16

   拖更了n久的备赛日记终于来啦,最近实现了关于K210图像识别并将所需数据(即目标类别,目标在图像中的加权坐标)其中,加权坐标指K210识别到的目标并框出的框的宽和高与框左上顶点的坐标加权,希望以此来判断目标所处的位置并方便后续进行诸如寻迹,目标跟随等任务。其中涉及包括YOLO网络的训练,上位机K210进行目标检测并利用串口对数据进行发出。下位机STM32则要接收到K210传出的数据,对数据进行解码,存入对应数组便于后续引用,在此次实验中再利用串口二将接收数据传给PC端利用串口助手进行数据显示。

   我们一步一步来,从K210的YOLO网络训练开始讲起,K210中常用的YOLO网络训练包括利用spieed公司的线上训练平台,本地训练中除了深度学习大佬依旧可以利用之前常用的训练方法进行训练外,身为初学者,没有那么多的经历也没有能力完成深度学习环境的搭建与代码的编写,可以选择大佬开发好的MX-YOLO进行训练。本次实验就是采用MX-YOLO进行本地训练(跑例程的mask检测),开发的模型,在本次日记中不做过多赘述,后续会和相关训练方法同期更新出。在模型运行中遇到了诸如内存不足的问题(感觉是运行内存不足),在重刷了spieed的最小固件后得以解决。也建议大家在使用K210跑深度学习模型的时候刷最小固件来执行程序,这样能容纳稍微大一点的模型。一般能支持2兆到3兆左右(模型存至sd卡)。

   模型导入进行目标识别后,我们需要启动K210的串口将数据发出,要注意K210的串口不能单独发出数字,所以此处选择定义了一个一个元组参数来统一发送数据。为了让STM32更好的接收数据,我们定义了一个简单的通信协议。长这样

帧头1:0xfe
帧头2: 0xfd
数据1:classid
数据2:cx
数据3:cy
帧尾:0xff

其中之所以选择0xfe 0xfd 0xff作为帧头,是因为我们的捕捉的图像大小为224*224,皆小于0xfd,不会出现加权后数据大于0xff从而影响通信的情况。

下面就是我们K210端的全部代码啦,烧即用,只需要修改对应模型以及对应的anchor文件和lable文件即可。(注意我选用的spieed的固件包,选用canmv或者其它固件包大概率会无法使用喔)

#此代码中0为未带口罩,1为戴口罩
import sensor
import image
import lcd
import KPU as kpu
from machine import UART
from fpioa_manager import fm
import ustruct
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.set_vflip(1)#设置摄像头后置即所见即所得
sensor.run(1)
#初始化串口,pin6为串口1RX口,pin7为串口1TX口
fm.register(6,fm.fpioa.UART1_RX)
fm.register(7,fm.fpioa.UART1_TX)
def sending_data(cc,cx,cy):
    global uart;
    data = ustruct.pack("<bbhhhb",              #格式为俩个字符俩个短整型(2字节)
                   0xfe,#帧头1
                   0xfd,#帧头2
                   int(cc), #数据1为传入类型 #四字节
                   int(cx), #数据2为框与顶点坐标的加权的长#四字节
                   int(cy), #数据3为框与顶点坐标的加权的高#四字节
                   0xff)
    uart = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=4096)
    uart.write(data);   #必须要传入一个字节数组
task = kpu.load("/sd/mask.kmodel")
info_list = kpu.netinfo(task)
f=open("mask.anchors.txt","r")
anchor_txt=f.read()
L=[]
for i in anchor_txt.split(","):
    L.append(float(i))
anchor=tuple(L)
f.close()
a = kpu.init_yolo2(task, 0.6, 0.3, 5, anchor)
f=open("mask.lable.txt","r")
labels_txt=f.read()
labels = labels_txt.split(",")
f.close()
while(True):
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    if code:#识别到对应目标
        for i in code:
            #print(i.w())
            obj_x=(i.x()+i.w())/2
            obj_y=(i.y()+i.h())/2#数据加权平均计算x,y
            sending_data(i.classid(),obj_x,obj_y)#发送数据
            if i.classid():#检测目标为mask,仅适用于二分类问题运用此行
               a=img.draw_rectangle(i.rect(),(0,255,0),2)
               a = lcd.display(img)
               for i in code:
                 lcd.draw_string(i.x()+45, i.y()-20, labels[i.classid()]+" "+'%.2f'%i.value(), lcd.WHITE,lcd.GREEN)
            else:#检测目标为un_mask
               a=img.draw_rectangle(i.rect(),(255,0,0),2)
               a = lcd.display(img)
               for i in code:
                 lcd.draw_string(i.x()+45, i.y()-20, labels[i.classid()]+" "+'%.2f'%i.value(), lcd.WHITE,lcd.RED)
    else:#没有要检测的目标出现
        a = lcd.display(img)
a = kpu.deinit(task)

  K210的数据会按ASCII形式发出(其实无所谓,对应都有十六进制形式),这里要留意,一个整形是四字节,也就是比如我classid为01,但实际发出的数据是 00 01

   然后就是stm32的部分了,为了方便移植和开发,本程序中选用hal库开发,对于较底层的解读后续更新。

   目前用到的所有初始化都是最基础的cubemx初始化串口的步骤,并无特殊操作,要注意需要使能串口一的中断用于接收中断。同时将串口模式设置为asynchronous(异步)。下面简单介绍一下具体配置过程。

    

 

首先我们需要配置系统的RCC,将高速时钟配置成Crystal/Ceramic Resonator:外部无源晶振(陶瓷晶振

  编辑时钟树  配置对应串口

 

这里只展示了配置串口1,串口2同串口1一致,注意串口1要打开中断,即NVIC那块enable

 

中断优先级可以不动,让系统自动按中断号跑就行。

 

初始化就是这样,我们就可以生成代码啦。

将我们这里需要的一些定义代码写在此处,注意一定一定要把代码写在user code里,这样重新配置文件的时候不会被覆盖掉。这里有如此多的hello world主要是为了测试程序是否正常执行。可以进行删除。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuffer[9] = {0};		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t hello[]={"hello,world\r\n"};
uint8_t hello1[]={"hello1,world\r\n"};
uint8_t hello2[]={"hello2,world\r\n"};
uint8_t hello3[]={"hello3,world\r\n"};
uint8_t hello4[]={"hello4,world\r\n"};
uint8_t hello5[]={"hello5,world\r\n"};
uint8_t hello6[]={"hello6,world\r\n"};
uint8_t error3[]={"please check the end of the frame\r\n"};
uint8_t error2[]={"please check the end of the header1\r\n"};
uint8_t error1[]={"please check the end of the header1\r\n"};
uint8_t classid;
uint8_t obj_x;
uint8_t obj_y;
uint8_t jieguo[3]={0};
/* USER CODE END PTD */

下面是main函数中的部分,要记得在main函数中除了生成的初始化函数,我们还要开启对应的接收中断。

  HAL_UART_Transmit(&huart2, hello,sizeof(hello) , 1000);
  HAL_UART_Receive_IT(&huart1,Uart1_RxBuffer, 9);

这里将串口1接收的数据直接存储进数组Uart1_RxBuffer中要注意这里其实需要的是一个地址,但是我们知道数组的第一位可以代表他整个数组的地址,所以这里只需要这么写,9代表接收数据的长度为9个字节(参考上述的通信协议,总共是九个字节)

串口接收满9个字节的数据后会引起中断,本程序目前while循环中无任何代码,如果有需要也可以将数据解析任务放置在main函数的while循环中。

引起串口中断后会执行串口中断服务函数,在服务函数中调用串口中断回调函数

此处串口中断回调函数代码如下:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */

	//HAL_UART_Transmit(&huart2, hello, sizeof(hello),0xFFFF);
		HAL_UART_Transmit(&huart2, hello1, sizeof(hello1),0xFFFF);
		if(Uart1_RxBuffer[0]==0XFE)//判断帧头1
		 {
			 HAL_UART_Transmit(&huart2, hello2, sizeof(hello2),0xFFFF);
			 if(Uart1_RxBuffer[1]==0XFD)//判断帧头2
			 {
				HAL_UART_Transmit(&huart2, hello3, sizeof(hello2),0xFFFF);
				 jieguo[0]=Uart1_RxBuffer[3];
				 jieguo[1]=Uart1_RxBuffer[5];
				 jieguo[2]=Uart1_RxBuffer[7];
				 if(Uart1_RxBuffer[8]==0xFF) //判断帧尾
					{
						HAL_UART_Transmit(&huart2, hello4, sizeof(hello4),0xFFFF);
					}
				 else
				 {
						HAL_UART_Transmit(&huart2, error3, sizeof(error3), 1000);
				 }
			 }
			 else
			  {
				 HAL_UART_Transmit(&huart2, error2, sizeof(error2), 1000);
			  }
		 }
	    else
	     {
	       HAL_UART_Transmit(&huart2, error1, sizeof(error1), 1000);
		 }
    HAL_UART_Transmit(&huart2, jieguo, sizeof(jieguo), 1000);
    memset(jieguo,0x00,sizeof(jieguo)); //清空数组
	memset(Uart1_RxBuffer,0x00,sizeof(Uart1_RxBuffer)); //清空数组
    HAL_UART_Transmit(&huart2, Uart1_RxBuffer, sizeof(Uart1_RxBuffer), 1000);
	HAL_UART_Receive_IT(&huart1,Uart1_RxBuffer, 9);   //再开启接收中断
	HAL_UART_Transmit(&huart2, hello5, sizeof(hello5), 1000);
}

此处要注意必须用memset清除数组,如果不用memset清除数组,则串口接收数据无法更新(我也不懂,明明进中断了但是不会覆盖第一次的数据结果。有懂的大佬麻烦给我讲一下),同时一定要在中断回调函数中再次使能接收中断。

进行完上述所有,就可正常接收K210传输的数据并将接收数据通过串口2利用USB转TTL传输回PC机串口助手显示。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

电赛备赛日记(一):K210与STM32串口通信 的相关文章

  • openmv识别红色物体并返回坐标给stm32单片机,通过pid控制舵机云台

    本人搜索了有关于舵机云台pid控制的代码 xff0c 但是都没有搜到想要的结果 xff0c 现在自己写出来了代码 xff0c 所以就将自己写的代码分享出来 xff0c 和大家一起学习进步 1 openmv识别红色物体 43 返回中心坐标的的
  • vs解决报错:C++ qualified name is not allowed(E0283)

    我们看 把在GCC下编译过关的c 43 43 程序放在vs下却不能过 仅给出部分代码 其他以此类推 先不要慌着改 看下详细信息 看上去都是语法错误 但这真的没任何语法错误啊 百度上查找下 报错信息都不一样 别人是类里面多加限定符 我这是正常
  • Linux下的man命令

    目录 一 man是什么 xff1f 二 man命令的使用1 通过man man查看man手册2 通过man来进行查询 四 总结 一 man是什么 xff1f man所代表的的是英文单词manual xff0c 也就是帮助手册的意思 xff0
  • IO多路复用之select

    目录 一 IO多路复用二 select函数三 select实现socket服务器 xff08 1 xff09 流程图 xff1a xff08 2 xff09 代码讲解 xff1a 四 总结代码示例 xff1a 一 IO多路复用 IO多路复用
  • curl 和 wget 命令下载

    curl 和 wget 命令下载 一 wget下载1 wget介绍2 wget下载方法 二 curl下载1 curl介绍2 curl下载方法 三 wget下载sqlite实例总结 一 wget下载 1 wget介绍 wget 是一个从网络上
  • Linux下载安装和使用SQLite

    Linux安装SQLite 一 SQLite下载二 SQLite安装三 SQLite的使用1 解决无法直接用sqlit3命令2 解决无法编译的问题 总结 一 SQLite下载 首先 xff0c 前往SQLite官网下载页面找到包含confi
  • 解决:‘config.status: error: Something went wrong bootstrapping makefile fragments......’问题

    解决 xff1a config status error Something went wrong bootstrapping makefile fragments 问题 一 问题二 解决方法 一 问题 首先我们来看安装sqlite时报的这
  • TFTP服务器搭建与使用

    文章目录 一 TFTP协议二 TFTP服务器搭建1 安装TFTP服务器2 创建TFTP服务文件夹3 配置tftp文件4 配置tftpd hpa文件 三 TFTP服务器使用 一 TFTP协议 TFTP xff08 Trivial File T
  • 深入探讨Linux驱动开发:Linux设备树

    文章目录 一 设备树介绍二 设备树框架1 设备树框架2 节点基本格式3 节点部分属性简介 总结 一 设备树介绍 设备树 xff08 Device Tree xff0c 简称 DT xff09 是一种在嵌入式系统中描述硬件设备的一种数据结构和
  • 深入探讨Linux驱动开发:驱动介绍与hello驱动实例

    文章目录 前言一 Linux驱动介绍1 用户态和内核态2 内核功能介绍3 驱动程序介绍 二 驱动程序分类与注意事项1 驱动程序分类2 内核驱动开发注意事项 三 hello驱动开发1 驱动模块2 模块加载和卸载函数3 编写hello模块4 M
  • ROS中使用乐视 奥比中光(Astra Pro)深度相机显示彩色和深度图像

    环境 UbuntuROS Kinect or Melodic 奥比中光ROS驱动包安装地址 xff1a https github com orbbec ros astra camera 1 安装ROS 2 安装依赖 span class t
  • 深入探讨Linux驱动开发:字符设备驱动开发与测试

    文章目录 一 字符设备驱动介绍1 设备驱动介绍 二 设备号1 设备号介绍2 分配与释放设备编号 dev t类型 静态分配设备号 动态分配设备号 释放主次设备号 手动创建设备节点 自动创建设备节点 删除设备节点 三 字符设备注册1 cdev结
  • ZED-深度感知使用

    文章目录 1 深度感知配置2 得到深度数据2 1 得到深度值 3 展示深度图4 获取点云数据4 1 从点云数据中计算距离 5 得到法线图像6 调整深度分辨率 1 深度感知配置 可以在初始化时使用InitParameters xff0c 在运
  • Linux系统之 开机自启动程序脚本 编写

    Linux系统启动加载程序 最近完成了项目 xff0c 来个开机自启运行 找到已编译好的程序 xff08 以下是我编译的house xff09 span class token function ls span l span class t
  • 算法时间复杂度、空间复杂度分析

    算法时间复杂度 在计算机程序编写前 xff0c 依据统计方法对算法进行估算 xff0c 经过总结 xff0c 我们发现一个高级语言编写的程序程序在计算机上运行所消耗的时间取决于下列因素 1 算法采用的策略和方案 编译产生的代码质量 3 问题
  • Linux 选择题一百道

    cron 后台常驻程序 daemon 用于 xff1a A 负责文件在网络中的共享 B 管理打印子系统 C 跟踪管理系统信息和错误 D 管理系统日常任务的调度 在大多数Linux发行版本中 xff0c 以下哪个属于块设备 block dev
  • 程序返回return与系统退出exit

    程序返回return与系统退出exit return是语言级别的 xff0c 它表示了调用堆栈的返回 xff1b exit则是系统调用级别的 xff0c 它表示了一个进程的结束 return是返回函数调用 xff0c 如果返回的是main函
  • 结构体、结构体变量、结构体指针、字符串

    结构体 数组是用于保存一组相同类型数据的 而结构体是用于保存一组不同类型数组的 在使用结构体之前必须先定义结构体类型 因为C语言不知道你的结构体中需要存储哪些类型数据 我们必须通过定义结构体类型来告诉C语言 我们的结构体中需要存储哪些类型的
  • linux下脚本实现 切换root用户并执行

    借助一个工具 expect sudo apt span class token operator span get install expect 编写脚本 xff1a vim root sh span class token operato
  • csdn极客江南

    零基础学会 C 语言课程学习突破 1500 人 xff1a 学习地址 xff1a https edu csdn net course detail 31452 spm 61 1001 2014 3001 5507TS TS 专栏文章更新至第

随机推荐

  • 音视频编码格式认知

    编码格式对应的就是音频编码和视频编码 xff0c 音频编码标准和视频编码标准 xff0c 每种编码标准都对应的编码算法 xff0c 其目的是通过一定编码算法实现数据的压缩 减少数据的冗余 视频编码指的是通过特定的压缩技术 xff0c 将某个
  • TensorFlow、PyTorch各版本对应的CUDA、cuDNN关系

    TensorFlow PyTorch各版本对应的CUDA cuDNN关系 xff08 截止2021年4月7日 xff09 TensorFowLinuxCPUGPU macOSCPUGPU WindowsCPUGPU PyTorchCPUGP
  • Android源码新大陆

    vold amp av http aospxref com android 13 0 0 r3 xref system vold model http aospxref com android 13 0 0 r3 xref framewor
  • QT读取GPS模块时,显示不完全,一条指令只能ReadAll32个字节,数据被分开

    在用QT读取GPS模块的时候 xff0c 发现读取到的数据总是显示不出来经度 xff0c 用debug调试 xff0c 发现数据被分开 xff0c 一条指令只能读到N那里 xff0c GNRMC 064401 65 A 3110 47069
  • STM32CubeMX配置串口DMA传输实现不定长数据收发

    串口简介 串口是全双工的串行通信协议 串口通信指串口按位 xff08 bit xff09 发送和接收字节 xff08 一个字节有8位 xff09 尽管比特字节 xff08 byte xff09 的串行通信慢 xff0c 但是串口可以在使用一
  • 单片机串口不够用怎么办?

    扩展串口 一 为什么要扩展串口 xff1f 一块单片机的串口是有限的 xff0c 一般2 4个 当我们做一个项目时需要连接多个外设时跟单片机通讯时 xff0c 且通讯都是以串口形式 那么我们只能去扩展串口来满足我们的应用需求 二 解决方法
  • C语言:自定义实现strcat函数

    include lt stdio h gt include lt assert h gt char My Strcat char str1 const char str2 assert str1 amp amp str2 指针不为空 cha
  • 了解串口协议,及完成STM32的USART串口通讯程序,并用keil观察波形

    文章目录 前言一 串口协议1 RS 2322 485标准 二 RS 232 485和TTL电平1 RS 232电平2 4853 TTL电平4 区别 三 USB TTL转2321 CH3402 发送接收3 USB转RS 232 四 完成一个S
  • UART RS232 RS485协议原理及应用

    一 URAT UART Universal Asynchronous Receiver Transmitter 通用异步收发传输器 xff0c 简称串口 xff0c 是设备间进行异步通信的模块 UART负责处理数据总线和串行口之间的串 并
  • 2022年电赛E题声源定位跟踪系统

    我们组本来是奔着视觉题去的 xff0c 可是到比赛的时候突然发现好像就无人机比较合适 xff0c 但是我们都没玩过无人机 xff0c 本想转战小车 xff0c 可是材料突然发现要两辆小车 xff0c 材料也不够来不及买 xff0c 于是我们
  • Python中requests库使用方法详解

    python中requests库使用方法详解 前言 xff1a 一 什么是Requests二 安装Requests库三 各种请求方式1 基本的GET请求2 带参数的GET请求3 解析json4 获取二进制数据5 添加headers6 基本P
  • File Browser的安装(适用于Kali/Ubuntu/Debian)

    filebrowser是一个基于Go开发的开源轻量级免费的文件浏览器 xff0c 可以把电脑上的文件快速传到其他设备上 1 下载 下载地址 xff1a https github com filebrowser filebrowser rel
  • Ubuntu 挂载硬盘到 /home 目录下扩容

    实验室提供的 NVIDIA Jetson NX 内存容量太小 xff0c 只有16G xff0c 刷完机装上系统和 CUDA 等安装包后 xff0c 系统只剩下一点多个G xff0c 后面完全是不够用的 xff0c 因此需要加硬盘并挂载才能
  • vue-element-admin 二次开发 报错修改

    安装 GitHub git clone https github com PanJiaChen vue element admin git Gitee https gitee com panjiachen vue element admin
  • 常用字符串函数

    1 strlen 函数 strlen 函数用来计算字符串长度 用法 xff1a 结果 xff1a 2 strcpy 函数 用来将一个数组的内容拷贝到另一个数组 用法 xff1a 结果 xff1a 注意 xff1a 如果a1中的比a2中的长
  • C++ 学习日志——STL中的容器 Vector

    本文简单的介绍下STL下的容器 vector的简单的存放和遍历方法 目录 一 vector存放和访问方式 二 三种遍历方式 三 vector存放对象 四 vector存放指针 难点 一 vector存放和访问方式 迭代器 xff1a vec
  • ARM汇编程序入门实践

    一 stm32程序 1 新建工程 1 xff09 双击打开keil xff0c 点击菜单栏Project gt New Vision Project xff0c 新建项目 xff0c 在弹窗中设置工程项目的名称和路径 xff0c 在这里 x
  • 【STM32学习笔记】(5)—— STM32工程添加源文件和头文件

    向工程目录里创建头文件和源文件 STM32的工程文件构成较为复杂 xff0c 同时为STM32工程文件添加源文件和头文件也是较为复杂的 xff0c 下面就由此文章来介绍怎么给STM32工程添加源文件 xff08 c xff09 与头文件 x
  • 在STM32中使用printf函数

    一 目的 利用printf函数使得输出打印到串口中 二 工作原理 我们在C语言中为什么能够调用printf函数打印数据到控制台中 xff0c 那是因为printf函数又调用了fputc 函数 那么我们我们可不可以说printf函数之所以能够
  • 电赛备赛日记(一):K210与STM32串口通信

    拖更了n久的备赛日记终于来啦 xff0c 最近实现了关于K210图像识别并将所需数据 xff08 即目标类别 xff0c 目标在图像中的加权坐标 xff09 其中 xff0c 加权坐标指K210识别到的目标并框出的框的宽和高与框左上顶点的坐