【K210】K210学习笔记五——串口通信
【K210】K210学习笔记五——串口通信
- 前言
- K210如何进行串口通信
- K210串口配置
- K210串口发送相关定义
- K210串口接收相关定义
- K210串口发送接收测试
- 完整源码
前言
本人大四学生,电赛生涯已经走到尽头,一路上踩过不少坑,但运气也不错拿了两年省一,思来想去,决定开始写博客,将电赛经验分享一二,能力有限,高手轻喷。
往期的博客讲述了 K210 的感光元件模块 sensor 的配置,机器视觉模块 image 中部分函数的使用(目前是用 find_blobs 函数实现一些寻找不同颜色的目标点,寻找不同颜色的线,后面会更新更多 image 模块中的函数使用方法),按键、LCD、LED的使用,以及定时器的使用。
sensor 的学习笔记传送门
【K210】K210学习笔记一——sensor
image 的学习笔记传送门
【K210】K210学习笔记二——image
按键、LCD、LED的使用 的学习笔记传送门
【K210】K210学习笔记三——按键、LCD、LED的使用
定时器的使用
【K210】K210学习笔记四——定时器的使用
本文着重于 K210 如何进行串口通信,如果 MCU 之间无法通信,就算在 K210 上识别做的再好也没有用。我之前发过一篇K210和STM32通信、K210和OpenMV通信的博客,但我觉得那一份的代码还不够“通用”,因此我今天将串口通信的代码进行重置,希望能让大家觉得比上次那一期更好用,要是想回顾上一期代码,了解串口发送与接收,传送门如下。
串口通信 传送门
【串口通信】K210与STM32串口通信、K210与OpenMV串口通信
K210如何进行串口通信
K210串口配置
串口配置需要导入串口相关模块,以提供硬件支持。UART、fm、GPIO、math这四个是必不可少的。其他模块是往期代码中的,我没有删除(主要是想让这份代码大家拿去就可以使用,不用一篇篇翻我的博客去添加那些模块的配置,按需删除不需要的部分)。
# UART_V2.0 - By: FITQY - 周三 8 月 24 日 2022
#__________________________________________________________________
# 导入模块
import sensor, time, image # 导入感光元件模块 sensor 跟踪运行时间模块 time 机器视觉模块 image
import utime # 导入延时模块 utime
from fpioa_manager import fm # 从 fpioa_manager 模块中导入 引脚注册模块 fm
from Maix import GPIO # 从 Maix 模块中导入 模块 GPIO
import lcd # 导入 LCD 模块
from machine import Timer, PWM, UART # 从 machine 模块中导入 定时器模块 Timer 脉宽调制模块 PWM 双向串行通信模块 UART
import math # 导入数学函数模块 math
然后就是对 K210 的串口进行设置,这里我设置了两个串口。K210的串口引脚是可以自行映射的,我用的 K210 是 01Studio 的,这一款 K210 的 P9 和 P6 引脚是相邻的,P7 和 P8 引脚是相邻的,因此我就把 P9 映射为 串口1的RX,P6 映射为 串口1的TX,P7 映射为 串口2的RX,P8 映射为 串口2的TX。波特率是设置为 921600,这个是可以更改的,你要使用 115200也是可以的,只要和其他硬件的串口波特率对的上都可以。至于其他设置,数据位8,校验位0,停止位1,一般都是这样设置,这个不建议更改。
需要注意的是,不建议使用K210引脚号数偏大的引脚,比如26以后的,因为这些引脚似乎在接入SD卡、LCD、麦克风阵列等外设的时候会被占用。
#__________________________________________________________________
# 串口的使用
# 串口1 设置 P9 RX P6 TX
fm.register(9, fm.fpioa.UART1_RX, force = True) # 配置 9 脚为 UART1_RX 强制注册
fm.register(6, fm.fpioa.UART1_TX, force = True) # 配置 6 脚为 UART1_TX 强制注册uart1 = UART(UART.UART1, 921600, 8, 0, 1) # 设置 uart1 为 串口1 波特率 921600 数据位 8位 校验位 0位 停止位 1位# 串口2 设置 P7 RX P8 TX
fm.register(7, fm.fpioa.UART2_RX, force = True) # 配置 7 脚为 UART2_RX 强制注册
fm.register(8, fm.fpioa.UART2_TX, force = True) # 配置 8 脚为 UART2_TX 强制注册uart2 = UART(UART.UART2, 921600, 8, 0, 1) # 设置 uart2 为 串口2 波特率 921600 数据位 8位 校验位 0位 停止位 1位
K210串口发送相关定义
这里我定义了一个类,用来保存串口发送的相关信息,方便传入参数。
串口发送数据的核心思想就是将要发送的数据打包成一个数组,然后将这个数组发送出去。如果不打包,接收端很难将数据正常解析出来,帧头的目的就是方便接收端找到数组的“头”,然后从这个“头”开始,逐个接收数据,有效数据长度位控制的是接收多少个有效数据(就是你要发送的数据),加有效数据长度位是方便后续添加新的需要发送的数据,另外接收端可以灵活接收不同长度的数据,最后接收端对接收数据进行校验,如果校验通过,则保存这些数据,这里用的校验方法是校验和。
帧头我都是用 0xAA,因为 0xAA 转换成8位是 1010 1010 电平是有持续跳变的,可以提高点稳定性。
需要注意的是一次最好不要发送太多的数据,我在接收端代码的定义中是定义有效数据长度不超过40个8位,如果要发送x,y,color,shape,flag,虽然好像这里只有五个数据,但x和y是需要使用两个8位来发送的,因此实际有效数据长度是7个8位。关于为什么要用两个8位来发,原因就是我感光元件设置的图像大小是QVGA,x轴坐标变化范围为0到319,y轴坐标变化范围为0到239,x超过了255因此需要用两个8位来发送,一个8位能保存的数据是从0到255,两个8位是可以保存0到65535的。至于像颜色标志位、形状标志位、目标标志位这些一个8位就足够了,比如用1、2、3分别表示红、绿、蓝,也就是在识别到红色的时候,加一句TSTM32.color = 1,以此类推。当然,这些参数都只是一个名字而已,想来保存什么标志位或者什么数据发送到主控,都可以根据个人需求定义,理解了就不难。
还有一点就是,如果接收端经常出现乱序,不妨可以试一下更改帧头。当然,发送端发送的帧头和接收端识别接收的帧头要一并修改,这一点需要牢记。
#__________________________________________________________________
# 串口发送
# 定义 UART 发送类
class UART_Transmit(): # 定义 UART 发送类pack_flag = 0 # 打包方法标志位head1 = 0x00 # uint8_t 帧头1head2 = 0x00 # uint8_t 帧头2x = 0 # uint16_t 目标x轴坐标y = 0 # uint16_t 目标y轴坐标color = 0 # uint8_t 目标颜色标志位shape = 0 # uint8_t 目标形状标志位flag = 0 # uint8_t 目标标志位mode = 0 # uint8_t 模式标志位# 实例化类
TSTM32 = UART_Transmit() # 实例化 UART_Transmit() 为 TSTM32
TSTM32.pack_flag = 1 # 打包方法为 方法1
TSTM32.head1 = 0xAA # TSTM32 的帧头1为 0xAA
TSTM32.head2 = 0xAA # TSTM32 的帧头2为 0xAATOpenMV = UART_Transmit() # 实例化 UART_Transmit() 为 TOpenMV
TOpenMV.pack_flag = 2 # 打包方法为 方法2
TOpenMV.head1 = 0xAA # TOpenMV 的帧头1为 0xAA
TOpenMV.head2 = 0xAA # TOpenMV 的帧头2为 0xAA# 定义打包函数
def Pack_Data(TData):data = UART_Pack_Method(TData) # 根据不同的方法打包发送数据# 数据包的长度data_len = len(data) # 获得数据包总长度data[2] = data_len - 4 # 有效数据的长度 扣去 帧头1 帧头2 有效数据长度位 校验位# 校验和sum = 0 # 和置零for i in range(0,data_len-1):sum = sum + data[i] # 和累加data[data_len-1] = sum # 和赋值 给数组最后一位发送 只保存低8位 溢出部分无效# 返回打包好的数据return data
与之前不同的是,我将打包函数做成了一个可以带入参数的函数,这样就可以方便实现不同的打包需求,不需要每一次有新的需求的时候,将整段代码复制下来,然后改个名字。 如果你有新的打包需求,就只需要实例化一个新的串口发送类,然后将打包方法设置为3,在这里加个
elif TData.pack_flag == 3:
然后在这个 elif 里面写上你新的打包需求即可。
# 串口数据打包方法函数
def UART_Pack_Method(TData):if TData.pack_flag == 1:data = bytearray([TData.head1, # 帧头1TData.head2, # 帧头20x00, # 有效数据长度 0x00 + data_len - 4TData.mode, # 保存目标x轴坐标 高八位0x00]) # 数据和校验位elif TData.pack_flag == 2:data = bytearray([TData.head1, # 帧头1TData.head2, # 帧头20x00, # 有效数据长度 0x00 + data_len - 4TData.x>>8, # 保存目标x轴坐标 高八位TData.x, # 保存目标x轴坐标 低八位TData.y>>8, # 保存目标y轴坐标 高八位TData.y, # 保存目标y轴坐标 低八位TData.color, # 保存目标颜色标志位TData.shape, # 保存目标形状标志位TData.flag, # 保存目标标志位0x00]) # 数据和校验位return data
K210串口接收相关定义
串口接收数据需要用数组来缓冲,因此还需要定义一个类来缓冲串口数据。与之前不同的是,我新增了一点定义,目的是为了让串口接收的函数可以带入参数,就不用在每次有新的接收方法的时候将整段的代码复制下来改名字了。
#__________________________________________________________________
# 串口接收
# 定义 UART 接收类
class UART_Receive(object): # 定义 UART 接收类uart_buf = [] # 串口缓冲区数组data_len = 0 # 有效数据长度data_cnt = 0 # 总数据长度state = 0 # 接收状态buf_len = 0 # 保存串口等待字节的数量head1 = 0x00 # 接收帧头1head2 = 0x00 # 接收帧头2lenmax = 0 # 有效数据最大长度save_flag = 0 # 保存方法标志位# 实例化类
RSTM32 = UART_Receive() # 实例化 UART_Receive() 为 RSTM32
RSTM32.head1 = 0xAA # RSTM32 的帧头1为 0xAA
RSTM32.head2 = 0xAA # RSTM32 的帧头2为 0xAA
RSTM32.lenmax = 40 # RSTM32 的有效数据最大长度为 40
RSTM32.save_flag = 1 # RSTM32 的保存方法标志位为 1ROpenMV = UART_Receive() # 实例化 UART_Receive() 为 ROpenMV
ROpenMV.head1 = 0xAA # ROpenMV 的帧头1为 0xAA
ROpenMV.head2 = 0xAA # ROpenMV 的帧头2为 0xAA
ROpenMV.lenmax = 40 # ROpenMV 的有效数据最大长度为 40
ROpenMV.save_flag = 2 # ROpenMV 的保存方法标志位为 2
首先是串口数据的读取,这个被我制成了可以带入参数的读取函数,每次有新的需求也不需要整段复制改名字。
# 定义串口数据读取函数
def UART_Read(RData, uart):RData.buf_len = uart.any() # 检查 串口 是否有内容需要读取 返回等待的字节数量(可能为0)for i in range(0, RData.buf_len): # 读取 RData.buf_len 个数据Receive_Data(RData, uart.readchar()) # 接收单个数据 uart.readchar() 然后将这个数据传递到函数 Receive_Data() 进行 数据接收
串口数据接收函数的执行流程如下。
默认状态为状态0,若传入的数据等于帧头1,则保存该数据,然后进入状态1。
进入状态1后,若传入的数据等于帧头2,则保存该数据,然后进入状态2。
进入状态2后,判断需要接收的有效数据长度是否小于设定的有效数据最大长度,若小于,则保存该数据以及总数据长度,然后进入状态3。
进入状态3后,开始接收有效数据长度个数据,接收完毕会进入状态4。
进入状态4后,保存最后一位校验位,然后将数组以及数据总长传入解析函数进行解析。
解析完会将状态置0,数组清空,准备下一次接收。
这个同样也被我制成了可以带入参数的接收函数,每次有新的需求也不需要整段复制改名字。
# 定义串口数据接收函数
def Receive_Data(RData, buf):if RData.state == 0 and buf == RData.head1: # 判断帧头1是否符合要求 符合则进入下一个状态RData.state = 1 # 更改状态为 1RData.uart_buf.append(buf) # 将这个数据添加到数组末尾elif RData.state == 1 and buf == RData.head2: # 判断帧头2是否符合要求 符合则进入下一个状态RData.state = 2 # 更改状态为 2RData.uart_buf.append(buf) # 将这个数据添加到数组末尾elif RData.state == 2 and buf < RData.lenmax: # 有效数据长度位 规定有效数据长度小于40 符合则进入下一个状态RData.state = 3 # 更改状态为 3RData.data_len = buf # 获得有效数据长度RData.data_cnt = buf + 4 # 获得总数据长度 总数据长度 = 帧头1 + 帧头2 + 有效数据长度位 + 有效数据 + 校验位RData.uart_buf.append(buf) # 将这个数据添加到数组末尾elif RData.state == 3 and RData.data_len > 0: # 存储有效数据长度个数据RData.data_len = RData.data_len - 1 # 每存储一次 还需要存储的数据个数减1RData.uart_buf.append(buf) # 将这个数据添加到数组末尾if RData.data_len == 0: # 直到存储完毕RData.state = 4 # 进入下一个状态elif RData.state == 4: # 当接收到存储完毕的信息RData.uart_buf.append(buf) # 保存最后一位校验位 将这个数据添加到数组末尾RData.state = 0 # 状态重置为0 调用串口数据解析函数进行数据解析Parse_Data(RData) # 解析数据#print(RData.uart_buf) # 打印接收数组 若接收结果不对 可取消注释查看数组RData.uart_buf = [] # 清空缓冲区 准备下次接收数据else: # 不满足以上条件 视为接收出错 重置状态为0 丢弃所有数据 准备下一次接收数据RData.state = 0 # 重置状态为0RData.uart_buf = [] # 清空缓冲区 准备下一次接收数据
解析函数其实就是判断接收到的数据累加和是否等于接收到的校验位的值,等于则校验成功,保存数据,不等于则退出,不保存数据。与往期不同的是,这个同样也被我制成了可以带入参数的解析函数,每次有新的需求也不需要整段复制改名字。
# 定义串口数据解析函数
def Parse_Data(PData):# 和累加sum = 0 # 和置0i = 0 # 已循环次数置0while i < (PData.data_cnt - 1): # 循环累加sum = sum + PData.uart_buf[i] # 累加求数组和i = i + 1 # 已循环次数自增# 求余 因为 校验和 为 8 位 超出部分无效 因此只校验 低8位 即可sum = sum % 256 # 和对256取余 得低八位# 和校验失败则退出if sum != PData.uart_buf[PData.data_cnt - 1]: # 和取余结果若不等于校验位的值return # 退出# 和校验成功则根据保存方法的不同 接收数据UART_Save_Method(PData)
然后说一下数据的保存方法,这里我定义了两种。
假设发送过来的数据包是
AA AA 01 01 56
数组的下标从0开始,AA是帧头,第一个01是有效数据长度位,第二个01是要被保存的模式,56是校验和,因此数组下标范围0到4。
第二个01在数组中的下标是3,因此数组下标3的数据被保存到K210.mode
需要注意的是,如果用一个8位来发送一个数据,那么这个数据只能是0到255之间的值,如果要发送超过255的值,则需要用两个8位来发送。
比如需要发送数据为 257 258 3 4 5 而一个8位只能保存0到255,那么就需要将257分成两个8位发送,第一个8位是高八位,需要乘以256还原,第二个8位是低8位,因此257 = 256 + 1 也就是 01 01,所以258就是 01 02
因此整个数组为
AA AA 07 01 01 01 02 03 04 05 6C
K210.x 所对应的高8位在数组中下标是3,低8位在数组中下标是4,数组下标3的数据是01,下标4的数据是01,则有
K210.x = 1 * 256 + 1 = 257
以此类推
当然,如果你有新的保存需求,只需要加个 elif PData.save_flag == 3:
然后在这个 elif 中写下你新的保存需求即可
需要注意的是,最好加一个数据长度的判断,PData.uart_buf[2] 是有效数据长度位,比如发送过来1个数据,那么下标 3 和 下标 4(此时的下标4是校验位) 是存在的,但如果设置保存方法标志的时候错误设置成了2,则下标5之后的所有值都是不存在的,运行就会出错。
# 串口数据保存方法函数
def UART_Save_Method(PData):if PData.save_flag == 1 and PData.uart_buf[2] > 0:K210.mode = PData.uart_buf[3]elif PData.save_flag == 2 and PData.uart_buf[2] > 6:K210.x = PData.uart_buf[3]*256 + PData.uart_buf[4]K210.y = PData.uart_buf[5]*256 + PData.uart_buf[6]K210.color = PData.uart_buf[7]K210.shape = PData.uart_buf[8]K210.flag = PData.uart_buf[9]
K210串口发送接收测试
这里就用 K210 进行测试即可,自己发送,自己接收,当然你要是用我之前那篇博客中的测试方法进行测试也是没问题的。后面我回学校了会更新与32通信的测试。
自发自收就很简单了,只需要将 K210 的6脚和9脚,7脚和8脚连到一起就好,用跳线帽将它们短接即可。
然后就是设置一下发送的信息。
# 串口发送测试信息赋值
TSTM32.mode = 1TOpenMV.x = 65535
TOpenMV.y = 65536
TOpenMV.color = 255
TOpenMV.shape = 256
TOpenMV.flag = 3
然后上电运行,在串行终端中将接收到的参数打印出来即可。这里可以看出,如果用两个8位来发送数据,最大可以支持到65535,如果超过这个值,就会变成0了,而用一个8位来发送,就只能支持到255。
完整源码
完整源码如下所示,大家可以复制该源码,进行测试,要是遇到什么问题可以问我哦!我们下期再见~!
现在串口完成啦!那么现在开始,K210这一系列可能不会日更,后面我会着手于写K210跑训练模型,以及怎么训练模型的教程,难度比现在来得大,因此更新的速度会放缓。
但我一定会保持质量,做到句句有注释,句句有原因,思路清晰, 喜欢我的教程的,希望能换到你们的一个关注。
# UART_V2.0 - By: FITQY - 周三 8 月 24 日 2022
#__________________________________________________________________
# 导入模块
import sensor, time, image # 导入感光元件模块 sensor 跟踪运行时间模块 time 机器视觉模块 image
import utime # 导入延时模块 utime
from fpioa_manager import fm # 从 fpioa_manager 模块中导入 引脚注册模块 fm
from Maix import GPIO # 从 Maix 模块中导入 模块 GPIO
import lcd # 导入 LCD 模块
from machine import Timer, PWM, UART # 从 machine 模块中导入 定时器模块 Timer 脉宽调制模块 PWM 双向串行通信模块 UART
import math # 导入数学函数模块 math#__________________________________________________________________
# 感光元件设置
sensor.reset() # 重置并初始化感光元件 默认设置为 摄像头频率 24M 不开启双缓冲模式
#sensor.reset(freq=24000000, dual_buff=True) # 设置摄像头频率 24M 开启双缓冲模式 会提高帧率 但内存占用增加sensor.set_pixformat(sensor.RGB565) # 设置图像格式为 RGB565 (彩色) 除此之外 还可设置格式为 GRAYSCALE 或者 YUV422
sensor.set_framesize(sensor.QVGA) # 设置图像大小为 QVGA (320 x 240) 像素个数 76800 K210最大支持格式为 VGAsensor.set_auto_exposure(0) # 关闭自动曝光
#sensor.set_auto_exposure(0, exposure=120000) # 设置手动曝光 曝光时间 120000 ussensor.set_auto_gain(0) # 关闭画面增益
sensor.set_auto_whitebal(0) # 关闭RGB自动增益(白平衡)
#sensor.set_auto_gain(0, gain_db = 12) # 设置画面增益 17 dB 影响实时画面亮度
#sensor.set_auto_whitebal(0, rgb_gain_db = (0,0,0)) # 设置RGB增益 0 0 0 dB 影响画面色彩呈现效果 在 K210 上无法调节增益 初步判定是感光元件 ov2640 无法支持#sensor.set_contrast(0) # 设置对比度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
#sensor.set_brightness(0) # 设置亮度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释
#sensor.set_saturation(0) # 设置饱和度 0 这个参数无法读取 且调这个参数对画面似乎不会产生影响 暂时注释sensor.set_vflip(1) # 打开垂直翻转 如果是 01Studio 的 K210 不开启会导致画面方向与运动方向相反
sensor.set_hmirror(1) # 打开水平镜像 如果是 01Studio 的 K210 不开启会导致画面方向与运动方向相反sensor.skip_frames(time = 2000) # 延时跳过2s 等待感光元件稳定#__________________________________________________________________
# 创建时钟对象
clock = time.clock() # 创建时钟对象 clock#__________________________________________________________________
# 目标点输入类 举例 2022 年 TI 杯送货无人机 中的目标点输入部分
class Point_Input():point1 = 0 # 目标点 1point2 = 0 # 目标点 2cross = 0 # 穿圈模式标志位send = 0 # 目标点发送标志位point = Point_Input() # 实例化目标点输入类 Point_Input() 为 point# 按键控制下的目标点获取函数
def Point_Control(ckey):if ckey.control == 1: # 按键确认及发送控制标志位为1 即 按键3 按下ckey.control = 0 # 重置标志位if ckey.cs == 0: # 如果当前为模式 0point.send = 1 # 目标点发送标志置为 1 串口开始发送elif ckey.cs == 1: # 如果当前为模式 1point.point1 = ckey.cinput # 将按键输入值赋值给目标点 1elif ckey.cs == 2: # 如果当前为模式 2point.point2 = ckey.cinput # 将按键输入值赋值给目标点 2elif ckey.cs == 3: # 如果当前为模式 3point.cross = ckey.cinput # 将按键输入值赋值给 穿圈模式标志位if ckey.csflag == 1: # 如果检测到按键模式切换ckey.csflag = 0 # 重置按键模式切换标志位ckey.cinput = 0 # 重置按键输入值#__________________________________________________________________
# 按键的使用
# 定义按键控制类
class Key_Control(): # 定义按键控制类cnt = 0 # 按键计数值cs = 0 # 按键模式选择标志位csmax = 0 # 按键模式上限csflag = 0 # 按键模式切换标志位cinput = 0 # 按键输入值保存位control = 0 # 按键确认及发送控制标志位# 实例化按键类
key = Key_Control() # 实例化按键控制类 Key_Control() 为 key
key.csmax = 3 # 按键模式上限为 3 即最多有 4 个模式 (0, 1, 2, 3)# 注册按键引脚
fm.register(16, fm.fpioa.GPIOHS0, force = True) # 配置 16 脚为 KEY0 使用高速 GPIO 口 强制注册
fm.register(18, fm.fpioa.GPIOHS1, force = True) # 配置 18 脚为 KEY1 使用高速 GPIO 口 强制注册
fm.register(19, fm.fpioa.GPIOHS2, force = True) # 配置 19 脚为 KEY2 使用高速 GPIO 口 强制注册
fm.register(20, fm.fpioa.GPIOHS3, force = True) # 配置 20 脚为 KEY3 使用高速 GPIO 口 强制注册# 创建按键对象
KEY0 = GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_UP) # 创建按键对象 KEY0
KEY1 = GPIO(GPIO.GPIOHS1, GPIO.IN, GPIO.PULL_UP) # 创建按键对象 KEY1
KEY2 = GPIO(GPIO.GPIOHS2, GPIO.IN, GPIO.PULL_UP) # 创建按键对象 KEY2
KEY3 = GPIO(GPIO.GPIOHS3, GPIO.IN, GPIO.PULL_UP) # 创建按键对象 KEY3# 中断回调函数 KEY0 控制按键模式选择
def Key0_Switch(KEY0):utime.sleep_ms(10) # 延时 10ms 消除按键抖动if KEY0.value() == 0: # 确认 按键0 按下key.csflag = 1 # 标记按键模式切换if key.cs < key.csmax: # 若当前按键模式值小于按键模式选择上限值key.cs = key.cs + 1 # 控制按键模式选择 自增else: # 若达到上限 则重新从 0 开始key.cs = 0# 中断回调函数 KEY1 按键输入值自增
def Key1_Switch(KEY1):utime.sleep_ms(10) # 延时 10ms 消除按键抖动if KEY1.value() == 0: # 确认 按键1 按下key.cinput = key.cinput + 1 # 按键输入值自增# 中断回调函数 KEY2 按键输入值自减
def Key2_Switch(KEY2):utime.sleep_ms(10) # 延时 10ms 消除按键抖动if KEY2.value() == 0: # 确认 按键2 按下key.cinput = key.cinput - 1 # 按键输入值自减# 中断回调函数 KEY3 按键确认及发送控制标志位
def Key3_Switch(KEY3):utime.sleep_ms(10) # 延时 10ms 消除按键抖动if KEY3.value() == 0: # 确认按键按下key.control = 1 # 按键确认及发送控制标志位# 开启中断 下降沿触发
KEY0.irq(Key0_Switch, GPIO.IRQ_FALLING) # 开启 按键0 外部中断 下降沿触发
KEY1.irq(Key1_Switch, GPIO.IRQ_FALLING) # 开启 按键1 外部中断 下降沿触发
KEY2.irq(Key2_Switch, GPIO.IRQ_FALLING) # 开启 按键2 外部中断 下降沿触发
KEY3.irq(Key3_Switch, GPIO.IRQ_FALLING) # 开启 按键3 外部中断 下降沿触发#__________________________________________________________________
# LED的使用
# 注册 LED 引脚
fm.register(14, fm.fpioa.GPIO2, force = True) # 配置 14 脚为 LED_R 强制注册
fm.register(13, fm.fpioa.GPIO1, force = True) # 配置 13 脚为 LED_G 强制注册
fm.register(12, fm.fpioa.GPIO0, force = True) # 配置 12 脚为 LED_B 强制注册# 创建 LED 对象
LED_R = GPIO(GPIO.GPIO2, GPIO.OUT) # 创建 LED_R 对象
LED_G = GPIO(GPIO.GPIO1, GPIO.OUT) # 创建 LED_G 对象
LED_B = GPIO(GPIO.GPIO0, GPIO.OUT) # 创建 LED_B 对象# LED控制函数
def LED_Control(led_flag): # LED控制函数 根据传入 led_flag 点亮对应的灯 需要注意的是 0为点亮 1为熄灭if led_flag == 0: # 传入参数为 0 所有灯打开LED_R.value(0)LED_G.value(0)LED_B.value(0)elif led_flag == 1: # 传入参数为 1 所有灯关闭LED_R.value(1)LED_G.value(1)LED_B.value(1)elif led_flag == 2: # 传入参数为 2 红灯常亮LED_R.value(0)LED_G.value(1)LED_B.value(1)elif led_flag == 3: # 传入参数为 3 绿灯常亮LED_R.value(1)LED_G.value(0)LED_B.value(1)elif led_flag == 4: # 传入参数为 4 蓝灯常亮LED_R.value(1)LED_G.value(1)LED_B.value(0)else: # 其他情况 紫灯LED_R.value(0)LED_G.value(1)LED_B.value(0)#__________________________________________________________________
# 定时器的使用
# 定义定时器属性类
class Timer_Property():cnt = 0 # 定时器计数值cnt_max = 0 # 定时器计数值上限period = 0 # 定时器周期freq = 0 # 定时器频率# 定时器0 配置_______________________________________________________
# 定时器0 实例化类
timer0 = Timer_Property() # 实例化定时器属性类 Timer_Property() 为 timer0
timer0.cnt_max = 9 # 设定 定时器0 的计数值上限为 9
timer0.period = 100 # 设定 定时器0 的周期为 100# 定时器0 定义回调函数
def Timer0_Back(tim0):if timer0.cnt < timer0.cnt_max: # 若 定时器0 的计数值小于 定时器0 的计数值上限timer0.cnt = timer0.cnt + 1 # 计数值自增else:timer0.cnt = 0 # 超出计数值上限 则计数值重置为0# 定时器0 初始化
tim0 = Timer(Timer.TIMER0, # 定时器编号 定时器0Timer.CHANNEL0, # 定时器通道 通道0mode = Timer.MODE_PERIODIC, # 定时器模式 周期性unit = Timer.UNIT_MS, # 定时器周期单位 msperiod = timer0.period, # 定时器周期 timer0.period 若 unit 为 Timer.UNIT_MS 则周期为 timer0.period mscallback = Timer0_Back) # 定时器触发中断后执行的回调函数 Timer0_Back# 定时器1 配置_______________________________________________________
# 电机类定义
class Motor_Property():motor1 = 0 # 电机1 占空比motor2 = 0 # 电机2 占空比motor3 = 0 # 电机3 占空比motor4 = 0 # 电机4 占空比motor1_pin = 0 # 电机1 引脚motor2_pin = 0 # 电机2 引脚motor3_pin = 0 # 电机3 引脚motor4_pin = 0 # 电机4 引脚control_x = 0 # 被控坐标 xcontrol_y = 0 # 被控坐标 y# 实例化电机类
motor = Motor_Property() # 实例化电机类 Motor_Property() 为 motor
motor.motor1 = 50 # 电机1的占空比 初始设置为 50%
motor.motor2 = 50 # 电机2的占空比 初始设置为 50%
motor.motor1_pin = 14 # 电机1的引脚 14为红灯引脚 这里先用灯的亮灭观察效果
motor.motor2_pin = 13 # 电机2的引脚 13为绿灯引脚 这里先用灯的亮灭观察效果# 定时器1 实例化类
timer1 = Timer_Property() # 实例化定时器属性类 Timer_Property() 为 timer1
timer1.freq = 1000 # 设定 定时器1 的频率为 1000# 定时器1 通道0 初始化
tim1_ch0 = Timer(Timer.TIMER1, # 定时器编号 定时器1Timer.CHANNEL0, # 定时器通道 通道0mode = Timer.MODE_PWM) # 定时器模式 PWM# 定时器1 通道1 初始化
tim1_ch1 = Timer(Timer.TIMER1, # 定时器编号 定时器1Timer.CHANNEL1, # 定时器通道 通道1mode = Timer.MODE_PWM) # 定时器模式 PWM# 创建对象 电机1 通道为 定时器1的通道0 频率为 定时器1的频率 占空比为 电机1的占空比 引脚为 电机1的引脚
motor1 = PWM(tim1_ch0, freq = timer1.freq, duty = motor.motor1, pin = motor.motor1_pin)# 创建对象 电机2 通道为 定时器1的通道1 频率为 定时器1的频率 占空比为 电机2的占空比 引脚为 电机2的引脚
motor2 = PWM(tim1_ch1, freq = timer1.freq, duty = motor.motor2, pin = motor.motor2_pin)# 定义电机占空比控制函数
def Motor_Control(motor, x):val = 0if x < motor.control_x: # 若 当前坐标 小于 被控坐标x 即当前状态小车在目标的 左边val = (motor.control_x - x) * 0.3125 # 获取坐标差值 并转换为 0~50 之间的值motor.motor1 = 50 - val # 减小 电机1 占空比 电机1为左电机 使小车右转motor.motor2 = 50 + val # 增大 电机2 占空比 电机2为右电机 使小车右转elif x > motor.control_x: # 若 当前坐标 大于 被控坐标x 即当前状态小车在目标的 右边val = (x - motor.control_x) * 0.3125 # 获取坐标差值 并转换为 0~50 之间的值motor.motor1 = 50 + val # 增大 电机1 占空比 电机1为左电机 使小车左转motor.motor2 = 50 - val # 减小 电机2 占空比 电机2为右电机 使小车左转motor.motor1 = int(motor.motor1) # 将 电机1占空比 转换为 整数motor.motor2 = int(motor.motor2) # 将 电机1占空比 转换为 整数#__________________________________________________________________
# 串口的使用
# 串口1 设置 P9 RX P6 TX
fm.register(9, fm.fpioa.UART1_RX, force = True) # 配置 9 脚为 UART1_RX 强制注册
fm.register(6, fm.fpioa.UART1_TX, force = True) # 配置 6 脚为 UART1_TX 强制注册uart1 = UART(UART.UART1, 115200, 8, 0, 1) # 设置 uart1 为 串口1 波特率 921600 数据位 8位 校验位 0位 停止位 1位# 串口2 设置 P7 RX P8 TX
fm.register(7, fm.fpioa.UART2_RX, force = True) # 配置 7 脚为 UART2_RX 强制注册
fm.register(8, fm.fpioa.UART2_TX, force = True) # 配置 8 脚为 UART2_TX 强制注册uart2 = UART(UART.UART2, 921600, 8, 0, 1) # 设置 uart2 为 串口2 波特率 921600 数据位 8位 校验位 0位 停止位 1位#__________________________________________________________________
# 串口发送
# 定义 UART 发送类
class UART_Transmit(): # 定义 UART 发送类pack_flag = 0 # 打包方法标志位head1 = 0x00 # uint8_t 帧头1head2 = 0x00 # uint8_t 帧头2x = 0 # uint16_t 目标x轴坐标y = 0 # uint16_t 目标y轴坐标color = 0 # uint8_t 目标颜色标志位shape = 0 # uint8_t 目标形状标志位flag = 0 # uint8_t 目标标志位mode = 0 # uint8_t 模式标志位# 实例化类
TSTM32 = UART_Transmit() # 实例化 UART_Transmit() 为 TSTM32
TSTM32.pack_flag = 1 # 打包方法为 方法1
TSTM32.head1 = 0xAA # TSTM32 的帧头1为 0xAA
TSTM32.head2 = 0xAA # TSTM32 的帧头2为 0xAATOpenMV = UART_Transmit() # 实例化 UART_Transmit() 为 TOpenMV
TOpenMV.pack_flag = 2 # 打包方法为 方法2
TOpenMV.head1 = 0xAA # TOpenMV 的帧头1为 0xAA
TOpenMV.head2 = 0xAA # TOpenMV 的帧头2为 0xAA# 定义打包函数
def Pack_Data(TData):data = UART_Pack_Method(TData) # 根据不同的方法打包发送数据# 数据包的长度data_len = len(data) # 获得数据包总长度data[2] = data_len - 4 # 有效数据的长度 扣去 帧头1 帧头2 有效数据长度位 校验位# 校验和sum = 0 # 和置零for i in range(0,data_len-1):sum = sum + data[i] # 和累加data[data_len-1] = sum # 和赋值 给数组最后一位发送 只保存低8位 溢出部分无效# 返回打包好的数据return data#__________________________________________________________________
# 串口接收
# 定义 UART 接收类
class UART_Receive(object): # 定义 UART 接收类uart_buf = [] # 串口缓冲区数组data_len = 0 # 有效数据长度data_cnt = 0 # 总数据长度state = 0 # 接收状态buf_len = 0 # 保存串口等待字节的数量head1 = 0x00 # 接收帧头1head2 = 0x00 # 接收帧头2lenmax = 0 # 有效数据最大长度save_flag = 0 # 保存方法标志位# 实例化类
RSTM32 = UART_Receive() # 实例化 UART_Receive() 为 RSTM32
RSTM32.head1 = 0xAA # RSTM32 的帧头1为 0xAA
RSTM32.head2 = 0xAA # RSTM32 的帧头2为 0xAA
RSTM32.lenmax = 40 # RSTM32 的有效数据最大长度为 40
RSTM32.save_flag = 1 # RSTM32 的保存方法标志位为 1ROpenMV = UART_Receive() # 实例化 UART_Receive() 为 ROpenMV
ROpenMV.head1 = 0xAA # ROpenMV 的帧头1为 0xAA
ROpenMV.head2 = 0xAA # ROpenMV 的帧头2为 0xAA
ROpenMV.lenmax = 40 # ROpenMV 的有效数据最大长度为 40
ROpenMV.save_flag = 2 # ROpenMV 的保存方法标志位为 2# 定义串口数据读取函数
def UART_Read(RData, uart):RData.buf_len = uart.any() # 检查 串口 是否有内容需要读取 返回等待的字节数量(可能为0)for i in range(0, RData.buf_len): # 读取 RData.buf_len 个数据Receive_Data(RData, uart.readchar()) # 接收单个数据 uart.readchar() 然后将这个数据传递到函数 Receive_Data() 进行 数据接收# 定义串口数据接收函数
def Receive_Data(RData, buf):if RData.state == 0 and buf == RData.head1: # 判断帧头1是否符合要求 符合则进入下一个状态RData.state = 1 # 更改状态为 1RData.uart_buf.append(buf) # 将这个数据添加到数组末尾elif RData.state == 1 and buf == RData.head2: # 判断帧头2是否符合要求 符合则进入下一个状态RData.state = 2 # 更改状态为 2RData.uart_buf.append(buf) # 将这个数据添加到数组末尾elif RData.state == 2 and buf < RData.lenmax: # 有效数据长度位 规定有效数据长度小于40 符合则进入下一个状态RData.state = 3 # 更改状态为 3RData.data_len = buf # 获得有效数据长度RData.data_cnt = buf + 4 # 获得总数据长度 总数据长度 = 帧头1 + 帧头2 + 有效数据长度位 + 有效数据 + 校验位RData.uart_buf.append(buf) # 将这个数据添加到数组末尾elif RData.state == 3 and RData.data_len > 0: # 存储有效数据长度个数据RData.data_len = RData.data_len - 1 # 每存储一次 还需要存储的数据个数减1RData.uart_buf.append(buf) # 将这个数据添加到数组末尾if RData.data_len == 0: # 直到存储完毕RData.state = 4 # 进入下一个状态elif RData.state == 4: # 当接收到存储完毕的信息RData.uart_buf.append(buf) # 保存最后一位校验位 将这个数据添加到数组末尾RData.state = 0 # 状态重置为0 调用串口数据解析函数进行数据解析Parse_Data(RData) # 解析数据#print(RData.uart_buf) # 打印接收数组 若接收结果不对 可取消注释查看数组RData.uart_buf = [] # 清空缓冲区 准备下次接收数据else: # 不满足以上条件 视为接收出错 重置状态为0 丢弃所有数据 准备下一次接收数据RData.state = 0 # 重置状态为0RData.uart_buf = [] # 清空缓冲区 准备下一次接收数据# 定义串口数据解析函数
def Parse_Data(PData):# 和累加sum = 0 # 和置0i = 0 # 已循环次数置0while i < (PData.data_cnt - 1): # 循环累加sum = sum + PData.uart_buf[i] # 累加求数组和i = i + 1 # 已循环次数自增# 求余 因为 校验和 为 8 位 超出部分无效 因此只校验 低8位 即可sum = sum % 256 # 和对256取余 得低八位# 和校验失败则退出if sum != PData.uart_buf[PData.data_cnt - 1]: # 和取余结果若不等于校验位的值return # 退出# 和校验成功则根据保存方法的不同 接收数据UART_Save_Method(PData)#__________________________________________________________________
# 寻找色块
# 定义类
class Color_Property():cx = 0 # 色块 x轴 中心坐标cy = 0 # 色块 y轴 中心坐标flag = 0 # 色块标志位 1 找到 0 未找到color = 0 # 色块颜色标志位 例如 你可以用 1 来表示 黑色density = 0 # 色块密度比 反映色块锁定程度 值越大 锁定程度越好pixels_max = 0 # 色块像素最大值led_flag = 0 # LED标志位 方便调试用color_threshold = (0, 0, 0, 0, 0, 0) # 色块颜色阈值color_roi = (0,0,320,240) # 色块寻找区域(感兴趣区域)color_x_stride = 1 # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度color_y_stride = 1 # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度color_pixels_threshold = 100 # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块color_area_threshold = 100 # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块color_merge = True # 是否合并寻找到的色块 True 则合并 False 则不合并color_margin = 1 # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并# 实例化类
# 黑色
black = Color_Property()
black.color_threshold = (0, 50, -10, 10, -10, 10)
black.color_roi = (0,0,320,240)
black.color_x_stride = 1
black.color_y_stride = 1
black.color_pixels_threshold = 100
black.color_area_threshold = 100
black.color_merge = True
black.color_margin = 1# 红色
red = Color_Property()
red.color_threshold = (0, 100, 20, 127, -10, 127)#red.color_roi = (0,0,320,240)
red.color_roi = (0,110,320,20)red.color_x_stride = 1
red.color_y_stride = 1#red.color_pixels_threshold = 100
#red.color_area_threshold = 100
red.color_pixels_threshold = 10
red.color_area_threshold = 10red.color_merge = True
red.color_margin = 1# 绿色 预留
green = Color_Property()# 蓝色 预留
blue = Color_Property()# 定义寻找色块函数
def K210_Find_Blobs(color,led_flag):color.pixels_max = 0 # 重置 色块 最大像素数量color.flag = 0 # 重置 色块 标志位color.led_flag = 0 # 重置 led 标志位for blobs in img.find_blobs([color.color_threshold], # 色块颜色阈值roi = color.color_roi, # 色块寻找区域(感兴趣区域)x_stride = color.color_x_stride, # 色块 x轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度y_stride = color.color_y_stride, # 色块 y轴 像素最小宽度 色块如果比较大可以调大此参数 提高寻找速度pixels_threshold = color.color_pixels_threshold, # 色块 像素个数阈值 例如调节此参数为100 则可以滤除色块像素小于100的色块area_threshold = color.color_area_threshold, # 色块 被框面积阈值 例如调节此参数为100 则可以滤除色块被框面积小于100的色块merge = color.color_merge, # 是否合并寻找到的色块 True 则合并 False 则不合并margin = color.color_margin): # 色块合并间距 例如调节此参数为1 若上面选择True合并色块 且被找到的色块有多个 相距1像素 则会将这些色块合并img.draw_rectangle(blobs[0:4]) # 圈出找到的色块if color.pixels_max < blobs.pixels(): # 找到面积最大的色块color.pixels_max = blobs.pixels()color.cx = blobs.cx() # 将面积最大的色块的 x轴 中心坐标值 赋值给 colorcolor.cy = blobs.cy() # 将面积最大的色块的 y轴 中心坐标值 赋值给 colorcolor.flag = 1 # 标志画面中有找到色块color.density = blobs.density() # 将面积最大的色块的 色块密度比 赋值给 colorcolor.led_flag = led_flag # 将控制led颜色的标志位的值 赋值给 colorif color.flag == 1: # 标记画面中被找到的最大色块的中心坐标img.draw_cross(color.cx,color.cy, color=127, size = 15)img.draw_circle(color.cx,color.cy, 15, color = 127)#__________________________________________________________________
# 调试区
# 定义 K210 属性类
class K210_Property(object): # 定义 K210 接收类x = 0 # uint16_t 目标x轴坐标y = 0 # uint16_t 目标y轴坐标color = 0 # uint8_t 目标颜色标志位shape = 0 # uint8_t 目标形状标志位flag = 0 # uint8_t 目标标志位mode = 0 # uint8_t 工作模式位# 实例化类
K210 = K210_Property() # 实例化 K210_Property() 为 K210LED_Control(1) # 关闭一下所有灯 再进入 while 循环 使显示结果正确# 串口发送测试信息赋值
TSTM32.mode = 1TOpenMV.x = 65535
TOpenMV.y = 65536
TOpenMV.color = 255
TOpenMV.shape = 256
TOpenMV.flag = 3# 打印信息函数
# 打印 sensor 各参数
def Print_sensor():print("Exposure :", sensor.get_exposure_us(), "Gain:", sensor.get_gain_db(), "RGB:", sensor.get_rgb_gain_db())# 打印 K210 各参数
def Print_K210():print("Mode:", K210.mode, "x:", K210.x, "y:", K210.y, "color:", K210.color, "shape:", K210.shape, "flag:", K210.flag)# 打印 色块 各参数
def Print_Blobs_Property(color,name):print(name,"cx:",color.cx,"cy:",color.cy,"flag:",color.flag,"color:",color.color,"density:",color.density,"led_flag:",color.led_flag)# 打印总函数
def Print_All():print("______________________________________________________________________")Print_sensor() # 打印 sensor 参数Print_Blobs_Property(black,"Black ") # 打印 黑色色块 参数Print_Blobs_Property(red, "Red ") # 打印 红色色块 参数Print_K210() # 打印 K210 参数# LCD
# LCD 初始化
lcd.init() # lcd初始化# LCD 按键信息及目标点信息显示函数
def LCD_Show():lcd.draw_string(0, 0, "key_cs: "+str(key.cs), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 15, "cinput: "+str(key.cinput), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 30, "point1: "+str(point.point1), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 45, "point2: "+str(point.point2), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 60, "cross : "+str(point.cross), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 75, "red_cx: "+str(red.cx), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 90, "motor1: "+str(motor.motor1), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 105,"motor2: "+str(motor.motor2), lcd.BLUE, lcd.WHITE)lcd.draw_string(0, 120,"FPS : "+str(clock.fps()), lcd.BLUE, lcd.WHITE)# 串口数据打包方法函数
def UART_Pack_Method(TData):if TData.pack_flag == 1:data = bytearray([TData.head1, # 帧头1TData.head2, # 帧头20x00, # 有效数据长度 0x00 + data_len - 4TData.mode, # 保存目标x轴坐标 高八位0x00]) # 数据和校验位elif TData.pack_flag == 2:data = bytearray([TData.head1, # 帧头1TData.head2, # 帧头20x00, # 有效数据长度 0x00 + data_len - 4TData.x>>8, # 保存目标x轴坐标 高八位TData.x, # 保存目标x轴坐标 低八位TData.y>>8, # 保存目标y轴坐标 高八位TData.y, # 保存目标y轴坐标 低八位TData.color, # 保存目标颜色标志位TData.shape, # 保存目标形状标志位TData.flag, # 保存目标标志位0x00]) # 数据和校验位return data# 串口数据保存方法函数
def UART_Save_Method(PData):if PData.save_flag == 1 and PData.uart_buf[2] > 0:K210.mode = PData.uart_buf[3]elif PData.save_flag == 2 and PData.uart_buf[2] > 6:K210.x = PData.uart_buf[3]*256 + PData.uart_buf[4]K210.y = PData.uart_buf[5]*256 + PData.uart_buf[6]K210.color = PData.uart_buf[7]K210.shape = PData.uart_buf[8]K210.flag = PData.uart_buf[9]#__________________________________________________________________
# 主函数
while(True):clock.tick() # 跟踪运行时间# 模式选择______________________________________________________if K210.mode == 0x00:img=sensor.snapshot() # 拍摄一张照片elif K210.mode == 0x01:img=sensor.snapshot() # 拍摄一张照片K210_Find_Blobs(black,1) # 找黑色色块 LED标志为1 表示黑色motor.control_x = 160 # 控制目标处于 x轴中心点 160Motor_Control(motor,black.cx) # 电机占空比控制函数获取电机控制占空比motor1.duty(motor.motor1) # 将获取到的电机1占空比 装载motor2.duty(motor.motor2) # 将获取到的电机2占空比 装载elif K210.mode == 0x02:img=sensor.snapshot() # 拍摄一张照片K210_Find_Blobs(red,2) # 找黑色色块 LED标志为1 表示黑色motor.control_x = 160 # 控制目标处于 x轴中心点 160Motor_Control(motor,red.cx) # 电机占空比控制函数获取电机控制占空比motor1.duty(motor.motor1) # 将获取到的电机1占空比 装载motor2.duty(motor.motor2) # 将获取到的电机2占空比 装载elif K210.mode == 0x03:img=sensor.snapshot() # 拍摄一张照片else:img=sensor.snapshot() # 拍摄一张照片# 模式选择______________________________________________________Point_Control(key) # 按键控制下的目标点获取函数lcd.display(img) # LCD 显示图像LCD_Show() # LCD 显示按键信息及目标点信息#LED_Control(red.led_flag) # LED 标记色块识别情况UART_Read(RSTM32,uart1) # 串口1 数据接收UART_Read(ROpenMV,uart2) # 串口2 数据接收if timer0.cnt == 0: # 如果 timer0.cnt 等于 0 此步骤的目的是控制打印周期 不要打印的太快Print_All() # 打印各参数uart1.write(Pack_Data(TSTM32)) # 串口1 数据发送uart2.write(Pack_Data(TOpenMV)) # 串口2 数据发送#__________________________________________________________________
【K210】K210学习笔记五——串口通信相关推荐
- 51单片机学习笔记-6串口通信
6 串口通信 [toc] 注:笔记主要参考B站江科大自化协教学视频"51单片机入门教程-2020版 程序全程纯手打 从零开始入门". 注:工程及代码文件放在了本人的Github仓库 ...
- 51单片机学习笔记(串口通信 LED点阵屏 DS1302)
四.串口通信 1.基本概念 通信中最重要的两个方面: 信息表示和解析方法: 信息的传输方法. 通信双方事先需要约定好信息的表示方法和解析方法,做到一致,否则信息不能有效传递. 信号的传输方法是指经过编 ...
- 嵌入式学习笔记7——串口通信
并行通信和串行通信的区别: 并行通信通常是将数据字节的各位用多条数据线同时进行传送 . 串行通信是将数据字节分成一位一位的形式在一条传输线上逐个地传送. 异步 不要求收发双方时钟的严格一致,实现容易, ...
- C51单片机学习笔记之串口通信
简介 串口通信就是单片机和pc之间的一种通信方式. 通信方式:并行,串行,同步,异步(最常用的) 传输方向:单工,半双工(不同时间),全双工 基本结构 相关寄存器 SCON串口控制寄存器(主要用方 ...
- Arduino学习笔记⑥ 硬件串口通信
文章目录 1.前言 2.常用串口函数 2.1 begin -- 启用串口 2.2 end -- 停用串口 2.3 print.println.printF -- 打印输出数据 2.4 read -- ...
- STM32学习笔记-USART串口通信+与野火STM32F407板载ESP8266进行通信
文章目录 STM32USART介绍 STM32USART框图 第一部分 第二部分 第三部分 发送器 时序图 接收器 第四部分 软件部分: STM32通过USART与板载ESP8266通讯实验 板载WI ...
- stm32学习笔记----双串口同时打开时的printf()问题
stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...
- ROS学习笔记五:理解ROS topics
ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...
- 【STM32】标准库与HAL库对照学习教程八--串口通信详解
[STM32]标准库与HAL库对照学习教程八--串口通信详解 一.前言 二.准备工作 三.通信的基本概念 1.通信方式 2.串行通信与并行通信 (1)串行通信 (2)并行通信 3.异步通信与同步通信 ...
最新文章
- 找工作?该复习了!(转)
- 实现instanceof关键字
- Tomcat源码学习(7)-How Tomcat works(转)
- 归并排序(Merge_Sort)
- C#图形处理系列(一)——最简单第一步:逆反处理、二值处理
- firewallD卸载Linux,在Ubuntu 18.04/16.04系统上安装和使用Firewalld的方法
- 3.9 创建数据类型
- C++设置不定参数方法 简单示例
- python编程(反汇编)
- Elasticsearch基础(一)mapping
- php备份网站程序,使用PHP备份整个网站
- matlab分离实部虚部,MATLAB/simulink中,想让电流表测出的值为复数形式,有实部虚部,将powergui设置为phasors仿真,总是出错...
- 20201002Class not found XXXXTest
- 【Windows Server 2019】DHCP服务器配置与管理——安装和配置DHCP服务 Ⅱ
- 4-渔夫打鱼晒网问题
- WEB短信平台定制 短信系统构建 路由通道搭建 web版短信后台管理系统 概括简介
- 根据UV风计算风速风向,根据风速与风向计算UV风
- 微信小程序API(功能)常用使用地址
- 配置电脑和服务器硬件的参考
- Ajax局部刷新后,重新加载百度分享