#引入串口
from machine import UART,Timer
from fpioa_manager import fm
#映射串口引脚
fm.register(6, fm.fpioa.UART1_RX, force=True)
fm.register(7, fm.fpioa.UART1_TX, force=True)
#串口初始化
uart = UART(UART.UART1, 115200, timeout=1000 , read_buf_len=4096)
#引入KPU和摄像头
import sensor,image,lcd,time
import KPU as kpu
#LCD初始化
lcd.init(freq=15000000)
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) #设置帧大小
sensor.set_vflip(1) #摄像头后置方式
sensor.run(1) #图像捕抓控制
sensor.set_windowing((224, 224)) #设置LCD显示屏大小
sensor.set_hmirror(1)
#标签
classes = ['1', '2', '3', '4', '5', '6', '7', '8']
task = kpu.load("/sd/m.kmodel") #模型SD卡上,kpu.load()调用模型的方法,并赋值给对象
#网络参数
anchor = [1.40625, 1.8125000000000002, 5.09375, 5.28125, 3.46875, 3.8124999999999996, 2.0, 2.3125, 2.71875, 2.90625]
a = kpu.init_yolo2(task, 0.68, 0.3, 5, anchor) #KPU.init_yolo2(kpu_net, threshold, nms_value, anchor_num, anchor)
#参数解析:
#kpu_net:模型加载的对象,一般是KPU.load()的返回值;threshold:概率阈值,大于这个值才会输出结果;nms_value:两个框重合的比率大于这个值,就取概率大的值。换言之,这个值越小越好。
#anchor_num:锚点数,len(anchor)//2;anchor:锚点参数与模型参数一致,同一个模型这个参数是固定的,和模型绑定的(训练模型时即确定了), 不能改成其它值。
header = bytearray([0xFF]) #包头
Left_Direction = bytearray([0x3A]) #左转
Right_Direction = bytearray([0x3B]) #右转
footer = bytearray([0xFE]) #包尾
while(True):
img = sensor.snapshot()
code = kpu.run_yolo2(task, img) #kpu.run_yolo2(kpu_net,image_t)kpu_net: kpu_load 返回的 kpu_net 对象;image_t:从 sensor 采集到的图像;返回值:kpu_yolo2_find 的列表
#运行yolo2网络。kpu.run_yolo2()返回一个二维列表,每个子列表代表一个识别到的目标物体,目标物体信息列表包含以下6个数据:x, y, w, h:代表目标框的左上角x,y坐标,以及框的宽w高h.class: 类别序号.prob : 概率值,范围:[0, 1]
if code:
for i in code:
a=img.draw_rectangle(i.rect()) #i.rect()这个方法调用返回一个代表检测到的对象边界框的元组。这个元组通常包含四个元素(x,y,w,h)
a = lcd.display(img) #识别到后,立马画矩形框
#kpu.run_yolo2(task, img) 的返回值是一个列表,列表中的元素为字典。每个字典表示检测到的一个对象的相关信息,
#包括矩形框左上角的元素位置(x、y)、矩形的宽度和高度(w、h)、该对象的概率值(value)、类别 ID(classid)、索引(index)以及对象的数量(objnum)等。
x1,y1,x2,y2 = i.rect() #解包,将其赋值给x1,y1,x2,y2
x_center = (x1 + x2) // 2
if x_center < 80:
lcd.draw_string(100, 20, "left", lcd.RED, lcd.WHITE)
uart.write(header+classes[i.classid()]+Left_Direction++footer)
#uart.write(header+classes[i.classid()]+footer)
print(classes[i.classid()])
else:
lcd.draw_string(200, 20, "right", lcd.RED, lcd.WHITE)
uart.write(header+classes[i.classid()]+Right_Direction++footer)
#uart.write(header+classes[i.classid()]+footer)
print(classes[i.classid()])
for i in code:
lcd.draw_string(i.x(), i.y(), classes[i.classid()], lcd.BLUE, lcd.WHITE)
lcd.draw_string(i.x(), i.y()+12, '%f'%i.value(), lcd.BLUE, lcd.WHITE)
#uart.write('0xb3'+'0xb3'+classes[i.classid()]+classes[i.classid()]+classes[i.classid()]+'0x5b')
else:
a = lcd.display(img)
#uart_A.deinit()
#del uart_A
#a = kpu.deinit(task)
上面的代码是k210识别标签后给stm32发送标签数字'1', '2', '3', '4', '5', '6', '7', '8'。但是在主控里的usart使用的是下面这行代码,依旧能接收。不仅如此,而且是字符S ,A等,其他字符均可接收。
uint16_t Serial_RxPacket[4];
当发送的字符为A时,代码如下
import sensor, image, lcd, time
from machine import UART
from fpioa_manager import fm
# 针对 Maix 系列开发板的引脚映射
fm.register(7, fm.fpioa.UART1_TX, force=True)
fm.register(6, fm.fpioa.UART1_RX, force=True)
uart = UART(UART.UART1, 9600, 8, 0, 1, timeout=1000, read_buf_len=4096)
header = bytearray([0xFF])
footer = bytearray([0xFE])
label = 'A'
#label = 'S'
while True:
a=uart.write(header + label.encode() + footer)
print(a)
A
串口助手在
十六进制下为: FF 41 FE
文本模式下为:?A?A?A?A?A?A?
S
串口助手在
十六进制下为: FF 53 FE
文本模式下为:S?S?S?S?
那么为什么明明是字符,主控用无符号整形也能接受呢?
分情况讨论:
接收在十六进制下,包头包尾本身就是十六进制。所以在十六进制的模式下,直接显示FF和FE。那么字符S和A呢?他们又不是十六进制,为什么可以被接收?那就得讲讲ASCII码了
ASCII(美国标准信息交换码,American Standard Code for Information Interchange)是一种字符编码标准,用于表示文本数据。每个字符都被映射到一个唯一的整数值,范围从 0
到 127
。这使得计算机可以用整数来表示和处理字符。
- 控制字符:
0x00
到0x1F
(如换行符、回车符) - 可打印字符:
0x20
到0x7E
(如字母、数字、标点符号) - 删除符:
0x7F
- ASCII 范围:ASCII 字符集仅包含从
0x00
到0x7F
的字符。
https://blog.csdn.net/u010033786/article/details/126275465
在这个链接里,你可以明白为什么是范围从 0
到 127,为什么0x00
到 0x7F
,且两者是映射的。
为什么字符能在 STM32 的整形数组中被接收?
在 STM32 这样的嵌入式系统中,接收字符数据并将其存储在整形数组中是因为:
-
字符是整数:在计算机中,字符实际上是整数。每个字符都有一个对应的整数值(即 ASCII 码)。例如,字符
'S'
的 ASCII 码是83
(十六进制0x53
),在整形数组中就是83
。 -
整形数组存储字节:在 STM32 中,整形数组可以存储字节(
uint8_t
)、半字(uint16_t
)、字(uint32_t
)等。字符数据(即其 ASCII 码)可以直接存储在这些数组中。因为每个字符的 ASCII 码都是一个 8 位的值(即一个字节),可以用uint8_t
(无符号 8 位整数)来存储。 -
数据传输:串口通信等数据传输方式通常以字节为单位传输数据。在 STM32 中,通过 UART 接收数据时,接收到的字节数据可以存储在整形数组中。接收到的数据可以是字符(ASCII 码)、数字或其他二进制数据。处理数据时,可以将这些字节解释为对应的字符或其他数据类型。
是的,你可以这样理解。让我们详细分解一下这个过程:
1. 转换字符为 ASCII 码
当你传输字符 'S'
时,它首先被转换为其 ASCII 码。对于 'S'
,其 ASCII 码是 83
(十进制),十六进制表示为 0x53
,二进制表示为 01010011
。
2. 传输数据
在传输过程中,字符 'S'
实际上被转化为其 ASCII 码 0x53
的字节数据。传输的是字节数据,而不是字符本身。
3. 主控接收数据
主控(如 STM32)接收到的实际上是一个字节(0x53
),它能够理解和处理这个字节。主控不关心这个字节代表什么字符,只是按照字节流的方式接收数据。
4. 显示数据
- 十进制模式:在十进制模式下,字节值
0x53
会显示为83
。 - 十六进制模式:在十六进制模式下,字节值
0x53
会显示为53
。 - 文本模式:在文本模式下,字节
0x53
会显示为字符'S'
。
ASCII码是人与计算机之间的桥梁,十六进制是一种表现方式(也可以说是HEX模式),
文本模式也是一种模式,只不过这种 模式主要是为人类服务的,是给人看的。但是只能看到0x20到0x7E的字符,其他的则是乱码。
为什么不能转化为两个 ASCII 字符
0xFF
和 0xFE
是单个字节的数据,超出了标准 ASCII 字符集的范围:
-
单字节表示:
0xFF
和0xFE
都是 8 位数据(即一个字节)。在 ASCII 编码中,一个字符始终由一个字节表示。如果一个字符的 ASCII 码超出了 ASCII 编码范围(0x00
到0x7F
),它不能被直接解释为标准的 ASCII 字符。即使在扩展 ASCII 中,这些值也可能不对应于可打印字符。
-
不能拆分为两个 ASCII 字符:
- ASCII 编码是基于 7 位或 8 位的单字节表示,每个字符都是独立的一个字节。
0xFF
和0xFE
本身是两个单独的字节,它们并不代表两个 ASCII 字符,而是一个单独的值。 - 如果你将
0xFF
或0xFE
拆分成两个 4 位的半字节(nibbles),你得到的值(0xF
和0xF
或0xF
和0xE
)不能直接映射到标准 ASCII 字符集,因为 ASCII 字符集不是以这种方式设计的。
- ASCII 编码是基于 7 位或 8 位的单字节表示,每个字符都是独立的一个字节。