- 2025-03-17
-
回复了主题帖:
[STM32H7R/S]测评 ⑨nano edge ai studio 训练一个模型--下
电子烂人 发表于 2025-3-15 16:51
重新研究了下,不太明白为什么要在BOOT下运行?是APP跑不起来吗
是啊,在文章最开始就写到了,让代码跳转到APP运行这个没搞定,退而求其次就让他在boot里运行,主要目的还是让他能跑起来看看效果。当然在boot里运行这个肯定是不合适的
-
回复了主题帖:
[嘉楠 CanMV K230]测评 ⑩自己训练一个分类识别模型
信鸽新鸽 发表于 2025-3-16 16:42
谢谢朋友,我运行image.py也可以进行对数据集里存进文件夹照片的识别,但是运行video.py的时候没有打开终端 ...
如果要在屏幕上看到结果,需要修改代码,增加在屏幕上显示文字的代码才可以
- 2025-03-13
-
加入了学习《键鼠统一管家》,观看 键鼠统一管家
- 2025-02-25
-
回复了主题帖:
[树莓派5测评]⑤安装SSD并从SSD启动+闭坑指南
vincentzl 发表于 2025-2-25 13:54
哪买的海力士ssd那么便宜 淘宝都是大几百
拆机二手啊,而且我这个128G,容量小,所以便宜。
- 2025-02-24
-
回复了主题帖:
[树莓派5测评]⑧外设-SPI:OLED屏
CoderX9527 发表于 2025-2-24 00:59
点赞。但是树莓派5点个oled屏,打炮打蚊子了吧
主要是学习了解一下SPI这个外设在树莓派上如何使用的,OLED只是一个体现的载体啦
- 2025-02-17
-
回复了主题帖:
【Raspberry Pi 5体验】+12.Home Assistant安装与简单测试(zmj)
期待下一期分享一下如何添加新设备到HA
- 2025-02-14
-
发表了主题帖:
[树莓派5测评]11:opencv识别简单形状
本帖最后由 不爱胡萝卜的仓鼠 于 2025-2-14 22:32 编辑
一.目标
这几天初步自学了一点opencv的一些基础知识,鉴于目前没有很深入的学习,我们就把目标定得稍微低一点。
做一个可以识别简单形状的demo,我先准备白底黑字的各种形状(圆形、矩形、三角形、五边形),然后让opencv读取这个图片,最终分辨出这个是什么形状。并且每张图片中有且只有一个形状
二.思路分析及代码
本次这些形状 的特点还是很明显的,不同形状的边、顶点数是不同的,这个可以作为我们识别的切入点
所以整体思路如下:
1.预处理:将图像转为灰度并应用高斯模糊,以减少噪声干扰。
2.边缘检测:使用Canny算法检测边缘。
3.轮廓查找:找到图像中的最大轮廓
4.多边形近似:使用Douglas-Peucker算法近似轮廓,得到顶点数量。
5.形状判断:如果有3个顶点就是三角形;如果有4个顶点就是矩形;如果有5个顶点就是五边形;剩下就用圆度验证是否为圆形;以上都不满足就是未知形状
import cv2
import numpy as np
def detect_shape(image_path):
# 读取图片
image = cv2.imread(image_path)
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 高斯模糊降噪
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 边缘检测
edged = cv2.Canny(blurred, 50, 150)
# 查找轮廓
contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
return "未检测到任何形状"
# 获取面积最大的轮廓
cnt = max(contours, key=cv2.contourArea)
# 多边形近似,找顶点
peri = cv2.arcLength(cnt, True) # 计算周长
approx = cv2.approxPolyDP(cnt, 0.04 * peri, True) # 多边形拟合
vertices = len(approx) # 顶点数,approx是一个包含拟合后的顶点坐标的数组,其长度即为顶点的数量。
# 根据顶点数判断形状
if vertices == 3:
return "三角形"
elif vertices == 4:
return "矩形"
elif vertices == 5:
return "五边形"
else:
# 使用圆度验证圆形
area = cv2.contourArea(cnt) # 计算面积
perimeter = cv2.arcLength(cnt, True) # 计算周长
if perimeter == 0: #如果周长为0,则直接设定圆度为0以避免除以0的错误。
circularity = 0
else: #周长不为0,计算圆度。计算公式为:圆度 = (4 * π * Area) / (Perimeter^2)
circularity = 4 * np.pi * area / (perimeter ** 2)
return "圆形" if circularity > 0.8 else "未知形状" # 圆度大于0.8则返回圆形,否则返回未知形状
#把每张测试图片都检测一遍
image_path = "yuanxing.jpg"
print(f"检测到的形状是:{detect_shape(image_path)}")
image_path = "fangxing.jpg"
print(f"检测到的形状是:{detect_shape(image_path)}")
image_path = "sanjiaoxing.jpg"
print(f"检测到的形状是:{detect_shape(image_path)}")
image_path = "wubianxing.jpg"
print(f"检测到的形状是:{detect_shape(image_path)}")
测试结果:
图片及代码:
三.感悟
首先python版本的opencv安装和使用对新手学习是非常有好的,不用自己编译,也不用学习C++,python的学习真的很轻松。
例如本次的这个demo,使用opencv需要学习一些图片处理的相关知识,使用opencv的一些函数通过一些方法,来实现识别的能力,需要有自己的思考,想办法去实现目标。如果用之前玩过的K230,训练一个模型就好了,对于新手快速实现目标会更容易,还可以有更好的效果(例如我画的五边形如果没有封口,我这个代码就会说不认识他,当然这个肯定还有优化的空间)
当然opencv还是很强大的,还可以在各种硬件平台上使用,如果花时间深入学习,还是有很多使用场景的
- 2025-02-12
-
发表了主题帖:
[树莓派5测评]⑩:安装opencv及初步尝试
一.opencv介绍
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它提供了大量的图像处理和计算机视觉算法,被广泛应用于各种领域,包括但不限于图像识别、物体检测、运动跟踪、机器人导航、增强现实等。
1.1主要特点
丰富的功能模块:OpenCV包含了数百种计算机视觉算法,涵盖了从基础的图像处理到复杂的机器学习模型。
跨平台支持:可以在多种操作系统上运行,包括Windows、Linux、macOS,以及移动平台如Android和iOS,还有嵌入式系统如树莓派。
多语言接口:除了C++原生接口外,还支持Python、Java等多种编程语言接口,使得开发更加灵活。
高性能优化:许多核心算法都经过了高度优化,支持多线程处理,并且可以利用硬件加速(如Intel IPP、CUDA等)来提高性能。
社区活跃:拥有一个庞大而活跃的开发者社区,持续贡献新功能、修复漏洞并提供技术支持。
1.2基本模块
imgproc:图像处理模块,包含滤波、边缘检测、形态学操作等功能。
video:视频分析模块,用于背景建模、对象跟踪等。
calib3d:相机标定与三维重建模块,支持立体匹配、摄像机姿态估计等。
features2d:二维特征描述子和匹配模块,用于关键点检测、描述子计算及匹配。
objdetect:目标检测模块,支持Haar级联分类器、HOG+SVM等方法进行人脸、行人等对象检测。
ml:机器学习模块,提供了SVM、决策树、KNN等多种经典机器学习算法。
dnn:深度神经网络模块,支持加载和推理预训练的深度学习模型(如TensorFlow、Caffe、Darknet等格式)。
1.3应用场景
自动驾驶:通过摄像头捕捉实时路况,进行车道线检测、障碍物识别等。
安防监控:实现智能监控系统,能够识别人脸、行为异常等。
医疗影像分析:辅助医生进行疾病诊断,例如X光片、CT扫描图像分析。
工业自动化:质量检测、机器人引导、产品分类等。
增强现实:在真实世界环境中叠加虚拟信息或对象。
1.4官网链接
https://opencv.org/
二.安装opencv
opencv可以安装在各种平台上(win、linux等),还可以选择编译好的还是选择自己用源码编译,还可以选择不同的编程语言(py、C++等)
官网的这个页面会引导我们安装:https://opencv.org/get-started/?utm_source=opcv&utm_medium=home
我这边就选择使用编译好的固件,使用py。
理论上直接用这个命令即可,但是别忘了IIC那篇遇到的问题,不允许我们在正式环境下使用pip
所以我们需要使用apt来安装
sudo apt install python3-opencv
安装速度还是非常快的,几十秒就安装好了
安装好后,可以运行一下以下py代码,看看能否打印版本号,如果可以打印出来,就表示成功安装了
import cv2
print(cv2.__version__)
三.初步尝试
在刚才的界面中有一段示例代码,是读取一个图片文件,然后显示出来
我下载了一张树莓派的照片,然后把代码中的路径按照这个文件的实际路径进行填写
import cv2 as cv
img = cv.imread("/home/shumeipai5-ssd/Pictures/image.png")
cv.imshow("Display window", img)
k = cv.waitKey(0) # Wait for a keystroke in the window
成功运行
-
发表了主题帖:
[树莓派5测评]⑨:CSI摄像头
本帖最后由 不爱胡萝卜的仓鼠 于 2025-2-13 16:35 编辑
后续有打算用树莓派学习一下opencv,所以把摄像头给安排上了
一.连接
树莓派5使用了新的接口,和以前的接口不一样了,所以还得买一根转接排线
排线和扩展板的坑之前已经讲过了,可以回看一下这篇文章: [树莓派5测评]⑤安装SSD并从SSD启动+闭坑指南
(我这边为了拍照方便,就把东西都拆了,裸板展示(当然又用回了TF卡的系统))
我这边选择的是树莓派官方的V2摄像头
正反面来个高清特写
连接摄像头,有触点的这面朝有摄像头的这个面
与树莓派的连接,我选择了0号camera
整体连接
二.测试摄像头
现在较新的Raspberry Pi OS中(特别是64位的),raspistill和raspivid这两个已经不再默认安装了,因为树莓派官方正逐步转向libcamera项目。所以我们今天就用libcamera这个库来测试摄像头
然后摄像头也不需要再去配置开启或关闭了,现在系统中已经没有camera这个选项了
2.1 拍照
先来拍个照,使用以下指令就可以拍一张照片
libcamera-still --timeout 5000 -o image.jpg
其中“--timeout 5000”部分是可选的,他是用于设置延时拍摄用的,5000是延时时间,单位是ms
然后“image.jpg”是拍摄好后图像的名称,拍好的图像会保存在终端窗口当前的路径
输入指令后,会弹出一个窗口,展示摄像头拍到的图像,因为设置了延时,我们有机会看窗口的预览
拍摄到的照片文件:
终端窗口日志有点多,截图字太小了,看着累,我复制出来了,有兴趣的可以慢慢看:
shumeipai5@shumeipai5:~ $ libcamera-still --timeout 5000 -o image.jpg
[0:23:58.862059035] [2761] INFO Camera camera_manager.cpp:325 libcamera v0.3.2+99-1230f78d
[0:23:58.869441716] [2764] INFO RPI pisp.cpp:695 libpisp version v1.0.7 28196ed6edcf 29-08-2024 (16:33:32)
[0:23:58.886424077] [2764] INFO RPI pisp.cpp:1154 Registered camera /base/axi/pcie@120000/rp1/i2c@88000/imx219@10 to CFE device /dev/media2 and ISP device /dev/media0 using PiSP variant BCM2712_C0
Made X/EGL preview window
Mode selection for 1640:1232:12:P
SRGGB10_CSI2P,640x480/0 - Score: 4504.81
SRGGB10_CSI2P,1640x1232/0 - Score: 1000
SRGGB10_CSI2P,1920x1080/0 - Score: 1541.48
SRGGB10_CSI2P,3280x2464/0 - Score: 1718
SRGGB8,640x480/0 - Score: 5504.81
SRGGB8,1640x1232/0 - Score: 2000
SRGGB8,1920x1080/0 - Score: 2541.48
SRGGB8,3280x2464/0 - Score: 2718
Stream configuration adjusted
[0:23:59.955103451] [2761] INFO Camera camera.cpp:1197 configuring streams: (0) 1640x1232-YUV420 (1) 1640x1232-BGGR_PISP_COMP1
[0:23:59.955246915] [2764] INFO RPI pisp.cpp:1450 Sensor: /base/axi/pcie@120000/rp1/i2c@88000/imx219@10 - Selected sensor format: 1640x1232-SBGGR10_1X10 - Selected CFE format: 1640x1232-PC1B
Mode selection for 3280:2464:12:P
SRGGB10_CSI2P,640x480/0 - Score: 10248.8
SRGGB10_CSI2P,1640x1232/0 - Score: 6744
SRGGB10_CSI2P,1920x1080/0 - Score: 6655.48
SRGGB10_CSI2P,3280x2464/0 - Score: 1000
SRGGB8,640x480/0 - Score: 11248.8
SRGGB8,1640x1232/0 - Score: 7744
SRGGB8,1920x1080/0 - Score: 7655.48
SRGGB8,3280x2464/0 - Score: 2000
Stream configuration adjusted
[0:24:05.034802819] [2761] INFO Camera camera.cpp:1197 configuring streams: (0) 3280x2464-YUV420 (1) 3280x2464-BGGR_PISP_COMP1
[0:24:05.038391882] [2764] INFO RPI pisp.cpp:1450 Sensor: /base/axi/pcie@120000/rp1/i2c@88000/imx219@10 - Selected sensor format: 3280x2464-SBGGR10_1X10 - Selected CFE format: 3280x2464-PC1B
Still capture image received
最后给大家看一下拍到的照片,这个画质真的感人,堪比门锁
2.2 实时视频
如果想要一直看摄像头拍得到的图像,可以使用下面这个指令
libcamera-still -t 0
其中“-t 0”表示永远不结束。
输入指令后界面如下,和拍照的类似
如果想要结束可以在终端按CTRL+C即可
2.3 录制视频
使用以下指令可以录制视频
libcamera-vid --timeout 10000 -o video.h264
这里的“--timeout”表示的就是录制总时间了
终端日志如下:
shumeipai5@shumeipai5:~ $ libcamera-vid --timeout 10000 -o video.h264
[0:33:46.458812234] [2852] INFO Camera camera_manager.cpp:325 libcamera v0.3.2+99-1230f78d
[0:33:46.466143718] [2855] INFO RPI pisp.cpp:695 libpisp version v1.0.7 28196ed6edcf 29-08-2024 (16:33:32)
[0:33:46.482628687] [2855] INFO RPI pisp.cpp:1154 Registered camera /base/axi/pcie@120000/rp1/i2c@88000/imx219@10 to CFE device /dev/media2 and ISP device /dev/media0 using PiSP variant BCM2712_C0
Made X/EGL preview window
Mode selection for 640:480:12:P
SRGGB10_CSI2P,640x480/0 - Score: 1000
SRGGB10_CSI2P,1640x1232/0 - Score: 1444.49
SRGGB10_CSI2P,1920x1080/0 - Score: 1636.67
SRGGB10_CSI2P,3280x2464/0 - Score: 2162.49
SRGGB8,640x480/0 - Score: 2000
SRGGB8,1640x1232/0 - Score: 2444.49
SRGGB8,1920x1080/0 - Score: 2636.67
SRGGB8,3280x2464/0 - Score: 3162.49
Stream configuration adjusted
[0:33:47.546687132] [2852] INFO Camera camera.cpp:1197 configuring streams: (0) 640x480-YUV420 (1) 640x480-BGGR_PISP_COMP1
[0:33:47.547142878] [2855] INFO RPI pisp.cpp:1450 Sensor: /base/axi/pcie@120000/rp1/i2c@88000/imx219@10 - Selected sensor format: 640x480-SBGGR10_1X10 - Selected CFE format: 640x480-PC1B
[libx264 @ 0x5555877bf2a0] using cpu capabilities: ARMv8 NEON
[libx264 @ 0x5555877bf2a0] profile High, level 3.0, 4:2:0, 8-bit
Output #0, h264, to 'video.h264':
Stream #0:0: Video: h264, yuv420p(tv, smpte170m/smpte170m/bt709), 640x480, q=2-31, 30 fps, 30 tbr, 1000k tbn
Halting: reached timeout of 10000 milliseconds.
[libx264 @ 0x5555877bf2a0] frame I:10 Avg QP:19.87 size: 15954
[libx264 @ 0x5555877bf2a0] frame P:146 Avg QP:22.06 size: 8619
[libx264 @ 0x5555877bf2a0] frame B:137 Avg QP:24.46 size: 2694
[libx264 @ 0x5555877bf2a0] consecutive B-frames: 6.5% 93.5%
[libx264 @ 0x5555877bf2a0] mb I I16..4: 8.2% 57.9% 33.9%
[libx264 @ 0x5555877bf2a0] mb P I16..4: 6.3% 41.6% 6.9% P16..4: 39.2% 0.0% 0.0% 0.0% 0.0% skip: 6.0%
[libx264 @ 0x5555877bf2a0] mb B I16..4: 1.9% 8.8% 0.5% B16..8: 27.4% 0.0% 0.0% direct:19.6% skip:41.7% L0:37.6% L1:46.0% BI:16.5%
[libx264 @ 0x5555877bf2a0] 8x8 transform intra:74.5% inter:59.7%
[libx264 @ 0x5555877bf2a0] coded y,uvDC,uvAC intra: 70.4% 63.2% 24.0% inter: 18.1% 30.1% 0.3%
[libx264 @ 0x5555877bf2a0] i16 v,h,dc,p: 35% 18% 15% 32%
[libx264 @ 0x5555877bf2a0] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 21% 16% 15% 4% 9% 8% 9% 8% 9%
[libx264 @ 0x5555877bf2a0] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 26% 20% 15% 5% 8% 6% 7% 8% 7%
[libx264 @ 0x5555877bf2a0] i8c dc,h,v,p: 38% 23% 25% 13%
[libx264 @ 0x5555877bf2a0] kb/s:1463.86
视频保存路径和照片是一样的
录制的视频是.h264格式的,我尝试用SSH把文件拷贝出来到windows下,然后用格式工厂转码成mp4,可惜转好后无法播放(目前还没琢磨明白哪儿有问题)
然后我在树莓派中使用默认安装的vlc,可惜他也无法播放,播放了一瞬间,然后就没有然后了,终端也有报错
vlc video.h264
也尝试过让他用软件解码
vlc --codec avcodec video.h264
最后我换了一个播放器,终于可以播放了(但是很奇怪的是时间没有10s,我自己数数大概就4s左右,这个我现在也还没研究明白为什么)
ffplay video.h264
- 2025-02-09
-
回复了主题帖:
[树莓派5测评]⑦外设-IIC:读取SHT30温湿度传感器数据
秦天qintian0303 发表于 2025-2-9 08:20
使用Adafruit库的库是不是需要用CPY进行开发啊,和python还是有点差异的吧
这个我倒是没有研究过,我一般看文档然后跑代码。目前到没有遇到和普通py有什么不一样
- 2025-02-08
-
发表了主题帖:
[树莓派5测评]⑧外设-SPI:OLED屏
本帖最后由 不爱胡萝卜的仓鼠 于 2025-2-9 21:00 编辑
一.开启SPI
和IIC一样,SPI默认也是关闭的。打开方式和上一篇的IIC一样
二.硬件接线
硬件连线如下:
OLED 树莓派
GND -> GND
VCC -> 3.3V
D0(SCK) -> GPIO 11 (SPI SCLK)
D1 (MOSI) -> GPIO 10 (SPI MOSI)
RST -> GPIO 24
DC -> GPIO 25
CS -> GPIO 8 (SPI CE0)
其中DC和RST是任意GPIO都可以的,CS可以选择GPIO8或7,我们待会儿代码中使用的是0号SPI,就选他
三.点屏
本来想用U8G2的,这个之前在arduino和STM32上我经常用,非常好用的一个库,可以他没有py的版本。那我们还是回到adafruit
但是adafruit我又遇到了一个坑爹的问题,正如上一篇IIC所遇到的,他得在虚拟py环境下才能被安装,可是我这个屏幕的DC、RST需要用到GPIO,在虚拟PY环境下,他貌似无法访问GPIO,报错“RuntimeError: Cannot determine SOC peripheral base address”。这就难受了,正式环境无法用pip安装adafruit,虚拟环境又无法使用GPIO。搞了很久没有搞定,我放弃了,还是手搓算了
手搓的话就需要使用spidev这个库,正式py环境下是已经预装这个库了
那接下来就是开始漫长的手搓,又要回去看各个寄存器了
经过漫长的调试,我在屏幕上显示了中文、英文、画点、画线、画矩形、画圆等,还在右下角动态显示数字
然后本次我还发现一个很棒的库Pillow,有了它我们就不用像单片机那样吧要显示的东西都提前取模,我们只需要加载好字体库,就可以显示各种大小的文字了
import RPi.GPIO as GPIO
import spidev
import time
from PIL import Image, ImageDraw, ImageFont
#定义OLED屏幕尺寸
OLED_WIDTH = 128
OLED_HEIGHT = 64
#设置DC和RST引脚号(BCM号)
DC_PIN = 25
RST_PIN = 24
# init SPI
spi = spidev.SpiDev()
spi.open(0, 0) # SPI bus 0, device 0
spi.max_speed_hz = 8000000 #SPI速度 8M
# init GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(DC_PIN, GPIO.OUT)
GPIO.setup(RST_PIN, GPIO.OUT)
def oled_write_command(cmd):
GPIO.output(DC_PIN, GPIO.LOW)
spi.xfer([cmd])
def oled_write_data(data):
GPIO.output(DC_PIN, GPIO.HIGH)
spi.xfer([data])
def oled_reset():
GPIO.output(RST_PIN, GPIO.LOW)
time.sleep(0.1)
GPIO.output(RST_PIN, GPIO.HIGH)
time.sleep(0.1)
def oled_init():
oled_reset()
oled_write_command(0xAE) # 关闭显示
oled_write_command(0xD5) # 设置显示时钟分频比/振荡器频率
oled_write_command(0x80) # 建议值
oled_write_command(0xA8) # 设置多路复用率
oled_write_command(0x3F) # 1/64 duty
oled_write_command(0xD3) # 设置显示偏移
oled_write_command(0x00) # 无偏移
oled_write_command(0x40 | 0x00) # 设置显示起始行
oled_write_command(0x8D) # 电荷泵设置
oled_write_command(0x14) # 内部电荷泵
oled_write_command(0x20) # 内存地址模式
oled_write_command(0x00) # 水平地址模式
oled_write_command(0xA1) # 段重映射设置
oled_write_command(0xC8) # COM输出扫描方向
oled_write_command(0xDA) # 设置COM引脚硬件配置
oled_write_command(0x12) # 备用COM引脚配置
oled_write_command(0x81) # 设置对比度控制
oled_write_command(0xCF) # 设置对比度值
oled_write_command(0xD9) # 设置预充电周期
oled_write_command(0xF1) # 建议值
oled_write_command(0xDB) # 设置VCOMH取消选择级别
oled_write_command(0x40) # 建议值
oled_write_command(0xA4) # 全局显示开启
oled_write_command(0xA6) # 正常显示
oled_write_command(0xAF) # 开启显示
def oled_clear():
for page in range(8):
oled_set_pos(page, 0)
for _ in range(OLED_WIDTH):
oled_write_data(0x00)
def oled_set_pos(page, column):
#Set page (0-7) and column (0-127)
oled_write_command(0xB0 + page) # 设置page地址
oled_write_command(0x10 | (column >> 4)) # 设置column高4位
oled_write_command(0x00 | (column & 0x0F)) # 设置column低4位
def oled_draw_image(image):
image = image.convert("1").resize((OLED_WIDTH, OLED_HEIGHT))
pixels = image.load()
for page in range(0, OLED_HEIGHT // 8):
oled_set_pos(page, 0)
for column in range(OLED_WIDTH):
byte = 0
for row in range(8):
y = page * 8 + row
if y < OLED_HEIGHT and pixels[column, y] != 0:
byte |= (1 << row)
oled_write_data(byte)
def draw_point(draw, x, y):
draw.point((x, y), fill=1)
def draw_line(draw, x1, y1, x2, y2):
draw.line((x1, y1, x2, y2), fill=1)
def draw_rectangle(draw, x1, y1, x2, y2):
draw.rectangle((x1, y1, x2, y2), outline=1)
def draw_circle(draw, x, y, radius):
draw.ellipse((x - radius, y - radius, x + radius, y + radius), outline=1)
def main():
oled_init()
oled_clear()
#创建一个画布
image = Image.new("1", (OLED_WIDTH, OLED_HEIGHT), color=0)
draw = ImageDraw.Draw(image)
#加载字体文件
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", 16)
except IOError:
print("Font file not found. Using default font.")
font = ImageFont.load_default()
#绘制文本、点、线、矩形和圆形
draw.text((0, 0), "Hello EEworld!", font=font, fill=1)
draw.text((0, 20), "不爱胡萝卜的仓鼠", font=font, fill=1)
draw_point(draw, 5, 40)
draw_line(draw, 10, 40, 50, 40)
draw_rectangle(draw, 10, 45, 50, 60)
draw_circle(draw, 70, 50, 10)
#刷新显示
oled_draw_image(image)
#右下角循环显示数字0-9
while True:
for i in range(10):
#清除一下数字所在区域,否则后续数字来了就会覆盖之前的数字
draw.rectangle((100, 40, 100 + 8, 40 + 16), outline=0, fill=0)
draw.text((100, 40), str(i), font=font, fill=1)
oled_draw_image(image)
time.sleep(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
finally:
spi.close()
GPIO.cleanup()
效果如下
[localvideo]c0ae64b9feab6ee9075f6857c6a20c98[/localvideo]
-
发表了主题帖:
[树莓派5测评]⑦外设-IIC:读取SHT30温湿度传感器数据
本帖最后由 不爱胡萝卜的仓鼠 于 2025-2-8 22:13 编辑
一.开启IIC
默认情况下,树莓派的IIC是关闭的,在使用前我们需要确保IIC是处于打开状态。
我选择使用GUI界面打开它(网上也有使用命令行的方式,我这儿就不用了)
二.硬件连接
掏出我自己画的SHT30,连接树莓派,连线如下
可以使用以下指令检测在IIC上的设备,测试一下SHT30是否成功被识别到
i2cdetect -y 1
这里,1指的是I2C总线编号,在较新的树莓派型号中通常为1。结果将输出一个表格,显示所有已连接设备的地址。
结果如下,成功找到一个IIC地址为0x45的设备,这个就是SHT30(我通过硬件电阻配置,把地址配置成0x45而不是默认的0x44)
三.smbus2实现IIC通讯函数
使用smbus2库可以自己调用IIC读写接口,进行IIC通讯,可以实际操作IIC上设备的各个寄存器,适合自己手动写驱动和底层调试
使用以下命令可以查看python3已经安装的库
pip3 list
可以看到,smbus2已经被安装了(我没有执行过安装的命令,这个也是预装的?那我这儿就不展示如何安装了,如果没有安装的,自行百度吧,教程很多的)
代码如下
import smbus2
import time
# SHT30的I2C地址
SHT30_I2C_ADDR = 0x45
# 初始化I2C总线
bus = smbus2.SMBus(1)
def read_temperature_humidity():
# 发送测量命令(高重复性,时钟拉伸禁用)
bus.write_i2c_block_data(SHT30_I2C_ADDR, 0x2C, [0x06])
# 等待测量完成
time.sleep(0.5)
# 读取数据(6个字节)
data = bus.read_i2c_block_data(SHT30_I2C_ADDR, 0x00, 6)
# 转换数据
temp = -45 + 175 * (data[0] * 256 + data[1]) / 65535.0
humidity = 100 * (data[3] * 256 + data[4]) / 65535.0
return temp, humidity
if __name__ == "__main__":
try:
while True:
temperature, humidity = read_temperature_humidity()
print(f"Temperature: {temperature:.2f}, Humidity: {humidity:.2f}")
time.sleep(2) # 每2秒读取一次数据
except KeyboardInterrupt:
print("Program terminated")
finally:
bus.close()
效果如下:
四.使用Adafruit库函数读取SHT30温湿度数据
使用mbus2库还是太麻烦,还好SHT30使用起来简单,可以自己手搓。如果遇到复杂一点的传感器,那就要弄很久了。所以我一般会去找现成的库。
SHT30我们可以用Adafruit库,他的文档链接:https://docs.circuitpython.org/projects/sht31d/en/latest/?spm=5176.28103460.0.0.297c5d271wgg84
安装库
pip3 install adafruit-circuitpython-sht31d adafruit-blinka
完蛋,报错了
根据他的提示,输入“sudo apt install python3-adafruit-circuitpython-sht31d”,坑爹啊,还是不行
好了,去问问最近很火的deepseek吧(虽然他天天提示:服务器繁忙,请稍后再试,但是可以看到他的思考过程,这就很有意思。而且用到现在他还没有出现一本正经胡说八道的情况。比我以前的通义好多了)
apt里没有adafruit-circuitpython-sht31d这个库,唯一的解决方法就是使用python虚拟环境
先安装python虚拟环境
sudo apt install python3-venv
这玩意儿竟然已经预装了
创建虚拟环境
python3 -m venv myenv
激活虚拟环境
source myenv/bin/activate
命令行前面出现了“(myenv)”,表示现在已经处于虚拟环境中了
(如果要关闭虚拟环境,可以使用deactivate。并且有一点需要注意的是,系统重启后,需要再次激活虚拟环境)
OK,现在我们就可以安装adafruit-circuitpython-sht31d了
pip install adafruit-circuitpython-sht31d
还有最后一步,因为我使用的IDE是geany,他默认的python解释器还是系统的,而不是我们现在的虚拟python的,所以这个还得改一下
这里的路径要根据实际情况来,主要是用户名和虚拟环境名每个人的不一样
代码如下:
import time
import board
import busio
import adafruit_sht31d
# 定义SHT30传感器的I2C地址
SHT30_I2C_ADDRESS = 0x45
# 创建I2C接口
i2c = busio.I2C(board.SCL, board.SDA)
# 使用I2C和指定地址初始化SHT30传感器
sensor = adafruit_sht31d.SHT31D(i2c, address=SHT30_I2C_ADDRESS)
print("SHT30 Sensor Ready")
while True:
# 读取温度和湿度
temperature = sensor.temperature
humidity = sensor.relative_humidity
print(f"Temperature: {temperature:.2f}°C")
print(f"Humidity: {humidity:.2f}%")
time.sleep(2) # 每2秒读取一次数据
效果如下:
使用现成的库就很方便,不需要管底层是如何通讯的,直接调用函数即可获取数据
-
发表了主题帖:
[树莓派5测评]⑥外设-GPIO:点灯、按键检测
本帖最后由 不爱胡萝卜的仓鼠 于 2025-2-8 20:05 编辑
一.查看40Pin引脚定义
树莓派上有一个通用的40pin排针,引出了很多GPIO和供电。在开始接线之前,我需要了解每个引脚的定义
使用下方命令即可查看GPIO定义
gpio readall
查不到,说不认识gpio命令。我查了一下这个指令需要WiringPi这个库,但是这个库已经停止维护了,最后支持到的就是树莓派4,所以他无法支持树莓派5。需要换个方法
换个指令
pinout
这个指令不错,除了引脚定义,还可以看到基础信息以及板子的形状
当然,在官方文档中也有这40Pin引脚的描述,链接:https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#gpio
二.RPi.GPIO库
我们使用python编程,这个语言简单易上手,还需要C那样先编译再运行,写完就可以直接运行。
之前说了WiringPi已经不能支持树莓派5了,所以我们使用RPI.GPIO这个库,这个库已经预先安装在系统中了,不需要我们再手动安装了
RPI.GPIO库的wiki文档:https://sourceforge.net/p/raspberry-gpio-python/wiki/Home/,可以在这里找到各个函数的注释以及如何使用的示例代码
三.创建.py文件与运行
在桌面创建一个py文件
填写文件名称,以.py结尾
文件创建好了
文件创建好后双击打开,树莓派系统已经预先安装了一个IDE。这玩意儿我还是第一次用,好在界面简单,摸索一下很快就上手了。
下图红框区域编写代码,然后点飞机就可以运行,非常简单
四.GPIO Output,点灯
我用杜邦线、直插式的LED灯和直插式的1K电阻(阻值不一定要1K,起一个限流电阻的作用,因为我手上只有1K的,就用它了)串联焊了一个小灯。一头接40P的37号引脚,另一头接GND。硬件连接如下图所示,:
代码如下:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD) #设置引脚序号为板子硬件序号
GPIO.setup(37, GPIO.OUT) #37号引脚设置为GPIO输出模式
try:
while True:
GPIO.output(37, True) #开灯
time.sleep(1) #delay 1s
GPIO.output(37, False) #关灯
time.sleep(1)
except KeyboardInterrupt:
GPIO.cleanup()
视频效果:
[localvideo]b7df6bee43d8d9f19bc410de273dff6c[/localvideo]
五.GPIO input
5.1 使用定时查询方式读取GPIO电平
使用GPIO.input函数可以读取当前GPIO的电平。
我焊接了一个开关,分别连接GND和36号引脚。36号引脚配置为输入模式,并上拉。这样当开关断开时,读取到的是高电平,开关接通时,为低电平。接线如下:
我把之前的代码稍微修改一下,while循环中增加了GPIO读取函数,当读取到高电平,点灯。反之熄灭
代码如下:
import RPi.GPIO as GPIO
import time
LED_PIN = 37 #LED灯引脚号
KEY_PIN = 36 #KEY引脚号
GPIO.setmode(GPIO.BOARD) #设置引脚序号为板子硬件序号
GPIO.setup(LED_PIN, GPIO.OUT)#LED灯引脚设置为GPIO输出模式
GPIO.setup(KEY_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)#KEY引脚设置为GPIO输入模式,上拉
try:
while True:
key_status = GPIO.input(KEY_PIN) #获取当前KEY的值
if key_status == GPIO.HIGH: #如果KEY为高电平
GPIO.output(LED_PIN, True) #开灯
print("LED ON")
else: #如果KEY为低电平
GPIO.output(LED_PIN, False) #关灯
print("LED OFF")
time.sleep(0.1)#delay 0.1s 避免过度占用CPU
except KeyboardInterrupt:
GPIO.cleanup()
效果:
[localvideo]81cc235b1491451d7e0a00835b0afdf7[/localvideo]
5.2 使用中断方式获取GPIO电平变化
对于按键,平时我们都是关注他被按下和松开的那一瞬间,像刚才的方式需要我们在while1中不停的定时查询GPIO电平,严重影响效率,那么经常玩单片机的小伙伴就会使用中断的方式来对按键的变化进行检测
RPI.GPIO提供了三种方式来解决这个问题:
1:wait_for_edge()
这个函数可以以阻塞的方式等待一个边沿变化(上升沿/下降沿/双边沿),还可以设置消抖时间和等待超时时间。在等待期间不会占用CPU性能(他的本质是等待一个事件)。
2:event_detected()
这个函数是非阻塞的,他会告诉你自从上次调用此函数以来是否发生了指定的事件(上升沿/下降沿/双边沿)
3:threaded callback
这个方式和单片机中的中断回调函数很类似,当检测到指定的边沿变化,就会立刻触发注册过的回调函数
3和单片机裸机的运作方式很接近,我就用一下3这个方式吧。我打算在while循环中定时打印日志,用于表示while循环正在运行。然后当按键被按一次后(对应触发下降沿)就把LED灯翻转一次。
硬件部分我把刚才的开关换成一个按键
代码如下:
import RPi.GPIO as GPIO
import time
LED_PIN = 37 #LED灯引脚号
KEY_PIN = 36 #KEY引脚号
def key_pressed_callback(channel):
print("Button pressed!")
# 翻转LED状态
current_state = GPIO.input(LED_PIN)
GPIO.output(LED_PIN, not current_state)
#设置引脚序号为板子硬件序号
GPIO.setmode(GPIO.BOARD)
#LED灯引脚设置为GPIO输出模式
GPIO.setup(LED_PIN, GPIO.OUT)
#KEY引脚设置为GPIO输入模式,上拉
GPIO.setup(KEY_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#注册检测到下降沿的回调函数,并设置消抖时间为200ms
GPIO.add_event_detect(KEY_PIN, GPIO.FALLING, callback = key_pressed_callback, bouncetime = 200)
try:
while True:
print("RUN")
time.sleep(1)#delay 1s 避免过度占用CPU
except KeyboardInterrupt:
GPIO.cleanup()
效果:
[localvideo]cef2315dda74a4bb017a7e4c9c7e20b5[/localvideo]
日志:
- 2025-01-15
-
回复了主题帖:
[树莓派5测评]⑤安装SSD并从SSD启动+闭坑指南
Bymyself 发表于 2025-1-15 14:41
没有风扇散热,纯被动散热?
有风扇的,只是被HAT盖住了,可以看一下我上一篇的测评文章: [树莓派5测评]④安装散热器及温度测试
- 2025-01-12
-
发表了主题帖:
【Follow me第二季第4期】④汇总篇
本帖最后由 不爱胡萝卜的仓鼠 于 2025-1-13 15:44 编辑
1、展示所有任务的视频
任务1:
EE大学堂视频链接:https://training.eeworld.com.cn/video/42146
任务2:
EE大学堂视频链接:https://training.eeworld.com.cn/video/42148
任务3:
EE大学堂视频链接:https://training.eeworld.com.cn/video/42150
汇总:
[localvideo]44dd7708854e5b723818909f8b1de3c5[/localvideo]
2、所使用的物料清单
本次任务我在得捷电子采购的产品有ARDUINO NANO RP2040 CONNECT、SEEEDSTUDIO XIAO ESP32S3
3、展示每个任务
3.1 任务一:搭建环境并开启第一步Blink三色LED / 串口打印Hello DigiKey & EEWorld!
详细内容:【Follow me第二季第4期】①任务一:Blink与串口打印
基础的点灯,串口功能比较基础,有点没意思,我看到RP2040 connect有带BLE和WiFi,因此我额外购买了一块ESP32-S3。我想实现通过WiFi的方式实现点灯,顺便可以学习一下Arduino上如何连接WiFi、启动server、作为client连接server、TCP通讯数据收发等。
RP2040:上电后实现三色LED、LED的闪烁,并串口打印指定内容。随后连接WiFi,启动server,等待client连接,并接收控制指令,执行指令对应的代码。
ESP32:上电后连接WiFi,并以Client的角色连接server,通过TCP通讯发送控制指令给RP2040,控制2040的LED灯
RP2040代码
/***************************/
//RGB LED需要安装WiFiNINA库,
/***************************/
#include <WiFiNINA.h>
#include <SPI.h>
#define WIFI_CTL_LED (1)
#if WIFI_CTL_LED
#include "wifi_info.h"
const char wifi_ssid[] = SECRET_SSID;
const char wifi_password[] = SECRET_PASS;
int wifi_conn_state = WL_IDLE_STATUS;
#define SERVER_PORT (80)
WiFiServer server(SERVER_PORT);
void printWifiStatus()
{
/* 打印WiFi SSID */
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
/* 打印IP地址 */
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
/* 打印信号强度 */
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
#endif
void setup()
{
/* 初始化串口 */
Serial.begin(115200);
/* delay一下,保证串口监视器成功连接 */
delay(3000);
/* 初始化LED灯的GPIO,并开灯 */
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
/* 初始RGB LED灯的GPIO,并开灯 */
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
/* 打印和闪灯,完成指定任务 */
for(uint8_t j = 0; j < 4; j++)
{
Serial.println("Hello DigiKey & EEWorld!");
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, LOW);
delay(1000);
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
delay(1000);
}
#if WIFI_CTL_LED
/* 检查wifi模块状态是否可用 */
if (WiFi.status() == WL_NO_MODULE)
{
Serial.println("Communication with WiFi module failed!");
while (1);
}
/* 检查wifi模块版本号(非必选,可去掉) */
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
{
Serial.println("now wifi firmware = " + fv + ", lastest version is " +
WIFI_FIRMWARE_LATEST_VERSION + ", suggest update!");
}
/* 检查SSID和PSW是否填写 */
if (strlen(wifi_ssid) <= 0)
{
Serial.println("ERROR: Plase enter WiFi SSID in \"wifi_info.h\"");
while(1);
}
if (strlen(wifi_password) <= 0)
{
Serial.println("ERROR: Plase enter WiFi password in \"wifi_info.h\"");
while(1);
}
/* 尝试使用预设的账号密码,连接路由器 */
uint8_t i = 0;
for (i = 0; i < 5; i++) //wifi连接超时时间5 * 10s
{
wifi_conn_state = WiFi.begin(wifi_ssid, wifi_password);
if (WL_CONNECTED == wifi_conn_state)
{
Serial.println("wifi connect success");
break;
}
delay(10000);
}
if (i == 5)
{
Serial.println("wifi connect failed");
while(1);
}
/* 打印wifi信息 */
printWifiStatus();
/* 开启客户端 */
server.begin();
Serial.print("server started,addr:");
Serial.print(WiFi.localIP());
Serial.print(":");
Serial.println(SERVER_PORT);
#endif
}
void loop()
{
#if WIFI_CTL_LED
WiFiClient client = server.available(); // listen for incoming clients
/* 是否有客户端连接我 */
if (client)
{
/* 打印,提示有客户端连接我 */
Serial.println("new client");
/* 从client接收到的数据的buffer */
String recv_buffer = "";
/* 持续监测客户端是否与我断连 */
while (client.connected())
{
/* 客户端是否可用 */
if (client.available())
{
/* 读取客户端发送的数据,并串口打印 */
/* read接口一次只能读一个字节,每读取一个字节后就把它拼到recv_buffer中 */
char c = client.read();
Serial.write(c);
recv_buffer += c;
/* 通过检查字符串结尾和制定的字符串是否一致,来决定是否需要执行开关灯指令 */
if (recv_buffer.endsWith("CTL_RGB_R_ON"))
{
digitalWrite(LEDR, HIGH);
}
if (recv_buffer.endsWith("CTL_RGB_R_OFF"))
{
digitalWrite(LEDR, LOW);
}
if (recv_buffer.endsWith("CTL_RGB_G_ON"))
{
digitalWrite(LEDG, HIGH);
}
if (recv_buffer.endsWith("CTL_RGB_G_OFF"))
{
digitalWrite(LEDG, LOW);
}
if (recv_buffer.endsWith("CTL_RGB_B_ON"))
{
digitalWrite(LEDB, HIGH);
}
if (recv_buffer.endsWith("CTL_RGB_B_OFF"))
{
digitalWrite(LEDB, LOW);
}
if (recv_buffer.endsWith("CTL_LED_ON"))
{
digitalWrite(LED_BUILTIN, HIGH);
}
if (recv_buffer.endsWith("CTL_LED_OFF"))
{
digitalWrite(LED_BUILTIN, LOW);
}
}
}
/* 客户端与我断连,需要关闭client连接,并打印提示信息 */
client.stop();
Serial.println("client disconnected");
/* 断连后灯回复原装 */
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
}
#else
Serial.println("Hello DigiKey & EEWorld!");
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
delay(1000);
Serial.println("Hello DigiKey & EEWorld!");
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, LOW);
delay(1000);
#endif
}
ESP32代码
#include <Arduino.h>
#include <WiFi.h>
#include "wifi_info.h"
const char wifi_ssid[] = SECRET_SSID;
const char wifi_password[] = SECRET_PASS;
int wifi_conn_state = WL_IDLE_STATUS;
const IPAddress serverIP(192,168,31,215); //填写服务器的实际IP地址
uint16_t serverPort = 80; //端口默认80,服务器端也是默认80
WiFiClient client;
void printWifiStatus()
{
/* 打印WiFi SSID */
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
/* 打印IP地址 */
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
/* 打印信号强度 */
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
void wifi_init()
{
/* 设置工作在STA模式 */
WiFi.mode(WIFI_STA);
/* 断开之前的连接 */
WiFi.disconnect(true, true);
}
void wifi_connect_AP()
{
/* 检查SSID和PSW是否填写 */
if (strlen(wifi_ssid) <= 0)
{
Serial.println("ERROR: Plase enter WiFi SSID in \"wifi_info.h\"");
while(1);
}
if (strlen(wifi_password) <= 0)
{
Serial.println("ERROR: Plase enter WiFi password in \"wifi_info.h\"");
while(1);
}
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(wifi_ssid);
/* 尝试使用预设的账号密码,连接路由器 */
uint8_t i = 0;
WiFi.begin(wifi_ssid, wifi_password);
/* 检测是否链接成功 */
for (i = 0; i < 20; i++)
{
delay(500);
if (WiFi.status() != WL_CONNECTED)
{
Serial.println("wifi connect success");
/* delay一会儿,等待IP等信息都拿到了再继续后续操作 */
delay(3000);
break;
}
}
if (i == 20)
{
Serial.println("wifi connect failed");
while(1);
}
/* 打印wifi信息 */
printWifiStatus();
}
void connect_server()
{
if (client.connect(serverIP, serverPort))
{
Serial.println("Connected to server");
}
else
{
Serial.println("Connection failed");
while(1);
}
}
void setup()
{
/* 初始化串口 */
Serial.begin(115200);
/* 初始化wifi模块 */
wifi_init();
/* wifi连接路由器 */
wifi_connect_AP();
/* 连接到服务器 */
connect_server();
}
void loop()
{
String cmd1 = "CTL_LED_ON";
String cmd2 = "CTL_LED_OFF";
/* 如果连接到服务器,就定时发送开关灯指令 */
if (client.connected())
{
Serial.println("ON");
client.write(cmd1.c_str());
delay(1000);
Serial.println("0FF");
client.write(cmd2.c_str());
delay(1000);
}
}
3.2 任务二:学习IMU基础知识,调试IMU传感器,通过串口打印六轴原始数据
详细内容:【Follow me第二季第4期】②任务二:IMU传感器
代码
/***************************/
//IMU需要安装Arduino_LSM6DSOXk库
/***************************/
#include <Arduino_LSM6DSOX.h>
float ACC_x, ACC_y, ACC_z, GYR_x, GYR_y, GYR_z;
void setup()
{
/* 初始化串口 */
Serial.begin(115200);
/* 初始化IMU传感器 */
if (!IMU.begin())
{
Serial.println("Failed to initialize IMU!");
while (1);
}
/* 打印ACC和GYR的采样率 */
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println("Hz");
Serial.print("Gyroscope sample rate = ");
Serial.print(IMU.gyroscopeSampleRate());
Serial.println("Hz");
}
void loop()
{
if (IMU.accelerationAvailable())
{
IMU.readAcceleration(ACC_x, ACC_y, ACC_z);
}
if (IMU.gyroscopeAvailable())
{
IMU.readGyroscope(GYR_x, GYR_y, GYR_z);
}
Serial.print("ACC_x: ");
Serial.print(ACC_x);
Serial.print(", ");
Serial.print("ACC_y: ");
Serial.print(ACC_y);
Serial.print(", ");
Serial.print("ACC_z: ");
Serial.print(ACC_z);
Serial.print(", ");
Serial.print("GYR_x: ");
Serial.print(GYR_x);
Serial.print(", ");
Serial.print("GYR_y: ");
Serial.print(GYR_y);
Serial.print(", ");
Serial.print("GYR_z: ");
Serial.println(GYR_z);
delay(100);
}
3.3 任务三:学习PDM麦克风技术知识,调试PDM麦克风,通过串口打印收音数据和音频波形。
详细内容:【Follow me第二季第4期】③任务三:PDM麦克风
代码;
#include <PDM.h>
/* 输出通道的数量 */
static const char channels = 1;
/* 采样频率 */
static const int frequency = 20000;
/* 采样buffer,每次采样的数据大小为16bit,所以变量类型为short */
short sampleBuffer[512];
/* 已经读到的采样次数 */
volatile int samplesRead;
void onPDMdata()
{
/* 查询一共有多少数据 */
int bytesAvailable = PDM.available();
/* 把采样数据保存到buffer里 */
PDM.read(sampleBuffer, bytesAvailable);
/* 更新采样计数。一次采样的数据大小为16bit,所以采样计数需要除以2 */
samplesRead = bytesAvailable / 2;
}
void setup()
{
/* 初始化串口 */
Serial.begin(9600);
/* 设置PDM数据接收回调函数 */
PDM.onReceive(onPDMdata);
/* 初始化PDM,并开始采样 */
if (!PDM.begin(channels, frequency))
{
Serial.println("Failed to start PDM!");
while (1);
}
}
void loop()
{
/* 是否有采样数据 */
if (samplesRead)
{
/* 打印buffer中的所有数据 */
for (int i = 0; i < samplesRead; i++)
{
Serial.println(sampleBuffer[i]);
}
/* 清零采样计数 */
samplesRead = 0;
}
}
4、对本活动的心得体会
很感谢得捷电子与EEWORLD共同举办的本次活动,这次的开发板非常有意思,板载各种传感器十分丰富,还有WiFi和BLE,可玩性很高。本次活动我学习到麦克风传感器、开启一个Server、client连接server、TCP通讯等新知识,收获颇丰。希望下次有机会继续参加类似的活动。
5.可编译下载的代码包
任务一:EEWORLD论坛下载中心下载链接:https://download.eeworld.com.cn/detail/%E7%9A%84%E5%88%A0%E9%99%A4%E5%A4%B1%E8%B4%A5v/635430
任务二:EEWORLD论坛下载中心下载链接:https://download.eeworld.com.cn/detail/%E7%9A%84%E5%88%A0%E9%99%A4%E5%A4%B1%E8%B4%A5v/635431
任务三:EEWORLD论坛下载中心下载链接:https://download.eeworld.com.cn/detail/%E7%9A%84%E5%88%A0%E9%99%A4%E5%A4%B1%E8%B4%A5v/635432
- 2025-01-10
-
回复了主题帖:
[树莓派5测评]④安装散热器及温度测试
hellokitty_bean 发表于 2025-1-10 13:10
这个天气,树莓派5可以给室内增温了。。。。。。。。。。。。。。。。
做个树莓派暖手宝,手冷了就打开烤鸡模式
-
回复了主题帖:
[树莓派5测评]④安装散热器及温度测试
dcexpert 发表于 2025-1-10 08:58
这个温度已经接近电脑CPU了。
没错没错,这个温度太恐怖了,难怪官方要出散热器,那是真的热啊
-
回复了主题帖:
[树莓派5测评]④安装散热器及温度测试
tagetage 发表于 2025-1-10 07:46
对树莓派5的温度了解了。,室温是13摄氏度左右很冷啊。。
是啊,开空调吹得人难受,只能靠发抖和热水袋取暖
-
回复了主题帖:
[树莓派5测评]③树莓派的4种交互方式
dcexpert 发表于 2025-1-9 20:19
还有 Raspberry Pi Connect 远程访问
学到了,谢谢。这玩意儿好啊,可以远程访问,出门在外也可以操作树莓派啦
-
发表了主题帖:
[树莓派5测评]④安装散热器及温度测试
本帖最后由 不爱胡萝卜的仓鼠 于 2025-1-10 00:57 编辑
树莓派5的主芯片相比4代性能提升不少,但是随之带来的就是温度的上升,可以说变成火炉了(待会儿我也会烤机一下,看看他裸板到底能去到多少温度),官方也是做了散热相关的设计,也推出了散热器配件。
一.安装散热器
散热器这种配件买第三方的就没问题,直接淘宝15块包邮,树莓派官方同款,除了没LOGO,几乎一样。便宜管饱,配件齐全,还送塑料底板
掏出散热器
方面散热垫也预装好了,还标注了各个区域
安装到树莓派上,这个散热器全靠两个角的塑料柱卡主PCB,然后弹簧给压力,安装时要小心,避免碰坏原件,两边最好一起压进去。我心中理想的应该是螺丝+弹簧的组合
然后去掉风扇座子的保护盖(为什么风扇有给,串口和RTC电池的座子就不给?不理解),准备连接散热器风扇
安装完成
二.烤机前的准备
2.1 如何获取温度
使用以下命令可以获取当前树莓派的温度
vcgencmd measure_temp
2.2 如何烤机
烤机的核心思想就是让CPU/GPU满载,可以写个程序疯狂占满CPU的所有核心,也可以使用一些小工具。我这边就使用stress-ng
安装stress-ng
sudo apt-get install stress-ng
使用以下命令即可让开始烤机
sudo stress-ng --cpu 4 --timeout 60s
这里的 --cpu 4 表示使用4个CPU核心,--timeout 60s 表示持续60秒
开始和结束都会有对应的提示
2.3 如何监控CPU的占用
使用htop即可监控CPU4个核心的占用情况
htop
2.4 多合一小工具
本来我是想找一个可以查看CPU频率的小工具的,结果发现了这个,可以一键开始压力测试,同时监视温度、频率、每个core的占用。那么上面哪些就暂时用不上了,但是写都写了,就留着看吧,下面测试我就都用这个工具了
sudo apt install stress s-tui
安装好后输入下面代码运行
s-tui
界面如下
现在处于监控模式(可以看左上角的Modes),如果要开启烤机,按键盘下键,再按空格 ,即可开始烤机。按键盘上键,再按空格就停止烤机
三.烤机测试
现在我的室温是13摄氏度左右
3.1 无散热空载测试
空载状态下树莓派的温度大约45摄氏度左右
3.2 无散热满载测试
烤机状态下温度,温度直线上升,最高干到了86℃,稳定在85度左右。但是他没有降频,稳定2.4GHz
3.3 风扇吹 满载测试
额外增加一下只有风扇吹的场景,看看风扇吹效果如何。我拿出了夏天吹脑袋的小风扇,开到对大档,怼这板子吹
不得不说,还是大力出奇迹啊,65度稳稳地。看来只要你能接受风扇高速转带来的噪音(最高转速可吵了,听得我脑壳疼),再配合树莓派4代淘宝上的那种散热片,也能得到一个不错的散热效果
3.4带散热空载测试
温度大约在35度左右
3.5带散热满载测试
这个散热片的15块钱真是值啊,6分钟了,稳稳压住,平均就在55-60度左右,牛。而且风扇转起来也几乎没有声音,很安静,太爽了
3.6 总结
通过上面的这些场景的测试,我认为树莓派5的发热确实比较厉害,散热片是很推荐购买的,现在是冬天,如果到了夏天,没有散热空载下温度肯定也不得了,15块钱花的还是很值的
四.风扇转速
关于风扇转速,散热风扇会根据当前温度决定是否转,转多快。
静置状态下他都是不转的,依靠散热片的被动散热就足够了,不要以为风扇是坏的或者没接好
我测试当温度超过50度左右,他就开始转在2400PPM左右(几乎没有声音)。到60度左右为4400PPM左右(稍微有一咪咪声音),再高就没遇到过了