- 2025-01-12
-
加入了学习《运算放大器视频教程》,观看 运算放大器负反馈电路,同相输入电路基础解析
-
加入了学习《运算放大器视频教程》,观看 运算放大器的应用电路,加法器电路详细解析
- 2025-01-10
-
加入了学习《运算放大器视频教程》,观看 运算放大器的应用电路,反相加法运算器,比例加法器
-
加入了学习《运算放大器视频教程》,观看 运算放大器的应用电路,比例运算器,乘法与除法运算?
-
加入了学习《运算放大器视频教程》,观看 运算放大器负反馈电路的补充,什么是电压跟随器
- 2025-01-08
-
加入了学习《运算放大器视频教程》,观看 运放负反馈电路,“虚短”和“虚断”进阶解析及电路分析
-
加入了学习《运算放大器视频教程》,观看 运算放大器负反馈电路,初步了解“虚短”和“虚断”
-
加入了学习《运算放大器视频教程》,观看 什么是理想的运算放大器,运放应该如何使用
-
加入了学习《运算放大器视频教程》,观看 运放内部电路最终章,改善交越失真电路实验
-
加入了学习《运算放大器视频教程》,观看 Vbe倍增电路是如何改善交越失真问题的,运放输出级最后一块骨头
-
加入了学习《运算放大器视频教程》,观看 如何改善交越失真,甲乙类功放电路详细解析
-
加入了学习《运算放大器视频教程》,观看 甲乙类功放电路如何克服交越失真问题,电路元器件的选择有何讲究
-
加入了学习《运算放大器视频教程》,观看 乙类功放电路的交越失真实验,正负电源应该如何接
-
加入了学习《运算放大器视频教程》,观看 运放互补输出电路,乙类功放电路的交越失真是什么
- 2025-01-06
-
加入了学习《运算放大器视频教程》,观看 运放中间级电路,达林顿电路放大原理,什么叫复合三极管
- 2024-12-28
-
上传了资料:
【Follow me第二季第4期】_任务实现Arduino代码
- 2024-12-27
-
发表了主题帖:
【Follow me第二季第4期】任务汇总_By CJ
本帖最后由 andy11112 于 2024-12-29 21:48 编辑
大家好,以下是此次Follow me第二季第4期的任务汇总,欢迎提出宝贵意见并交流。
【视频汇总】:
必做任务一,0:13
必做任务二,1:55
必做任务三,2:57
选做任务一,3:37
【源码链接】:https://download.eeworld.com.cn/detail/andy11112/635456
【总体心得】:
有参加Follow me的活动,此次使用的板子是Nano RP2040 Connect,特点是尺寸小,集成度高,这次任务使用到了速度计、陀螺仪、RGB LED和麦克风模块,另外板上还有任务中未涉及的蓝牙和WiFi模块。通过此次活动任务,学习了在Arduino环境中进行相关功能的开发。
必做任务一:搭建环境并开启第一步Blink三色LED / 串口打印Hello DigiKey & EEWorld!
1、【任务介绍】
首先搭建开发环境,然后是让三色LED闪烁,同时在串口中打印“Hello DigiKey & EEWorld!”。
1.1【开发环境搭建】
首先,下载编辑器。此次计划用Arduino进行开发,所以需要使用对应的编辑器。有桌面版和云端版两种选择,考虑避免网络稳定性的影响,此次我选择用桌面版。
从相关资料中(ABX00053-datasheet.pdf中的最后一页)可以找到桌面版编辑器的对应连接:https://www.arduino.cc/en/software
在页面中找到和自己系统匹配的下载链接。
下载后,运行安装包,完成后打开编辑器。可见如下界面,常用的快捷按钮包括:
a、【代码上传】将当前代码进行编译并上传到开发板中。
b、【串口绘图仪】将串口打印的信息,转换为坐标图形显示
c、【串口监视器】输入和输出串口信息
d、【项目文件夹】新建项目
e、【开发板管理器】安装和管理对应开发板所需要的内核包
f、【库管理器】安装和管理所需要的库
1.2【开发板连接和配置】
所需物料:这次活动中的全部任务,所需的物料只有开发板和Micro USB数据线。
将开发板通过USB线连接电脑后,在编辑器中选择对应开发板Nano RP2040 Connect。
编辑器会根据开发板状态,提示是否需要安装对应的Arduino Mbed OS RP2040 Boards内核包。
可以在菜单栏的 文件—>示例 中找到很多代码示例,用于实现特定功能的参考。
1.3【RGB闪烁和串口打印】
可以在网页上找到RGB的操作方法以及代码示例。链接:https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-01-technical-reference/#rgb
操作RGB时,需要注意:
a、RGB是由WiFi模块直接控制,所以要引入WiFiNINA库,在代码头部包含如下代码:
#include <WiFiNINA.h>
b、在void setup()中定义3种颜色灯的引脚为输出。
c、在void loop()中,可以用digitalWrite控制RGB,High表示点亮,Low表示熄灭;也可以用analogWrite控制RGB,设置引脚输出范围255~0,其中0表示最亮,255表示最暗。
串口输出,需要注意:
a、可以在示例中参考相关代码(文件—>示例—>01.Basics—>AnalogReadSerial)。
b、在void setup()中对串口做初始化。在void loop()中用Serial.println进行串口输出。
2、【软件流程图】
3、【代码片段】
#include <WiFiNINA.h>
void setup() {
//定义输出引脚
pinMode(LEDR, OUTPUT); //定义红灯控制引脚
pinMode(LEDG, OUTPUT); //定义绿灯控制引脚
pinMode(LEDB, OUTPUT); //定义蓝灯控制引脚
Serial.begin(9600); //设置串口波特率
}
void loop() {
//数字引脚方式写入
digitalWrite(LEDR, HIGH); //RED ON
delay(1000); //延时1秒
digitalWrite(LEDR, LOW); //RED OFF
digitalWrite(LEDG, HIGH); //GREEN ON
delay(1000);
digitalWrite(LEDG, LOW); //GREEN OFF
digitalWrite(LEDB, HIGH); //BLUE ON
delay(1000);
digitalWrite(LEDB, LOW); //BLUE OFF
delay(1000);
Serial.println("Hello DigiKey & EEWorld!"); //串口打印内容
}
4、【功能展示】
RGB分别循环亮起红色、绿色和蓝色灯。
串口循环打印“Hello DigiKey & EEWorld!”。
必做任务二:学习IMU基础知识,调试IMU传感器,通过串口打印六轴原始数据
1、【任务介绍】
1.1【IMU】学习
在https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-01-technical-reference/#imu中,可以找到IMU相关信息。
IMU,全称Inertial Measurement Unit。型号:LSM6DSOXTR。包含3D数字加速计和3D数字陀螺仪,以及温度传感器。
IMU具有许多功能,其中包括一个机器学习核心,可用于运动检测,如自由落体、步数检测器、步数计数器、计步器。另外还内置了温度传感器。
【加速度计】是一种用于测量加速度的机电设备。这种力可以是静态的,如连续的重力,也可以是动态的,如许多移动的情况,以感知运动或振动。可以能够通过向上、向下、向左或向右倾斜板来读取板的相对位置以及角度。
【陀螺仪传感器】是一种可以测量物体方向和角速度的设备。陀螺仪比加速度计更先进,因为它可以测量物体的倾斜和横向方向,而加速度计只能测量线性运动。陀螺仪传感器也称为“角速率传感器”或“角速度传感器”。可使用陀螺仪作为施加到板上的力方向的指示器。
1.2【IMU】使用
安装LSM6DSOX库。
在代码开头,引入LSM6DSOX库。
#include <Arduino_LSM6DSOX.h>
在void setup()中,用IMU.begin()做初始化,用IMU.accelerationSampleRate()设置采样频率。
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
1.3【IMU】读取数据
可参考https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-imu-basics/,完成IMU数据读取和打印。
读取加速计的各方向数据。
float x, y, z;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);
}
读取陀螺仪的各方向数据。
float x, y, z;
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(x, y, z);
}
2、【软件流程图】
3、【代码片段】
#include <Arduino_LSM6DSOX.h> //导入IMU库
float Ax, Ay, Az; //定义加速度计变量
float Gx, Gy, Gz; //定义陀螺仪变量
void setup() {
Serial.begin(9600); //初始化串口通信,波特率为9600
while(!Serial); //等待串口连接
if (!IMU.begin()) { //初始化IMU
Serial.println("Failed to initialize IMU!"); //如果初始化失败,打印错误信息
while (1); //停止程序运行
}
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate()); //打印加速度计采样率,单位为Hz
Serial.println("Hz");
Serial.println();
Serial.print("Gyroscope sample rate = ");
Serial.print(IMU.gyroscopeSampleRate()); //打印陀螺仪采样率,单位为Hz
Serial.println("Hz");
Serial.println();
}
void loop() {
if (IMU.accelerationAvailable()) { //检查加速度计数据是否可用,如果可用,则读取数据,否则跳过
IMU.readAcceleration(Ax, Ay, Az); //读取加速度计数据,将数据存储到Ax,Ay,Az变量中
Serial.println("Accelerometer data: ");
Serial.print(Ax);
Serial.print('\t');
Serial.print(Ay);
Serial.print('\t');
Serial.println(Az);
Serial.println();
}
if (IMU.gyroscopeAvailable()) { //检查陀螺仪数据是否可用,如果可用,则读取数据,否则跳过
IMU.readGyroscope(Gx, Gy, Gz); //读取陀螺仪数据,将数据存储到Gx,Gy,Gz变量中
Serial.println("Gyroscope data: ");
Serial.print(Gx);
Serial.print('\t');
Serial.print(Gy);
Serial.print('\t');
Serial.println(Gz);
Serial.println();
}
delay(500);
}
4、【成果展示】
4.1【加速计】
开发板处于不同的静止姿态,仅有重力作用,可以从加速计中读出不同方向上的数据。
晃动开发板,可以从陀螺仪读出相应数据。
必做任务三:学习PDM麦克风技术知识,调试PDM麦克风,通过串口打印收音数据和音频波形
1、【任务介绍】
1.1【PDM】学习
可以在https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-01-technical-reference/#microphone中,学习PDM相关知识。
型号:MP34DT06JTR。这是是一款紧凑型、低功耗、带IC接口的全向数字MEMS麦克风。使用PDM(脉冲密度调制)用二进制信号表示模拟信号。如下是关键参数。
信噪比:64dB
灵敏度:-26dBFS±3dB
温度范围:-40至85°C
PDM库包含在Arduino Mbed OS Nano Board Package中,无需额外安装PDM库。
1.2【PDM使用】
菜单栏 文件 > 示例 > PDM > PDMSerialPlotter ,可以找到对应的参考代码。
首先设置声道和采样率。PDM通过回调函数将接收到的声音数据写入缓存中,然后读取数据,通过串口打印出来以及绘图仪显示出来。
由于绘图仪的刷新率较快,为了更方便观察波形,每次输出之间增加延时语句delay(100)。
2、【软件流程图】
3、【代码片段】
#include <PDM.h> //导入PDM库
static const char channels = 1; //单声道,如果使用双声道,则将1改为2
static const int frequency = 16000; //采样频率,默认为16000Hz
short sampleBuffer[512]; //缓冲区,用于读取采样数据,每个采样数据是16位,即2字节,所以缓冲区大小为512*2=1024字节
volatile int samplesRead; //采样数据读取计数器,用于记录已经读取的采样数据数量,该变量是volatile的,因为它是被中断服务程序修改的
void setup() {
Serial.begin(9600); //初始化串口通信,波特率为9600
while (!Serial); //等待串口连接
PDM.onReceive(onPDMdata); //设置在准备读取新PDM数据时调用的回调函数,onPDMdata函数将在PDM数据接收中断时会被调用
if (!PDM.begin(channels, frequency)) { //初始化PDM,设置采样通道数和采样频率,如果初始化失败,则输出错误信息并停止程序
Serial.println("Failed to start PDM!");
while (1);
}
}
void loop() {
if (samplesRead) { //如果已经读取了采样数据,则进行处理
for (int i = 0; i < samplesRead; i++) { //遍历采样数据,将每个采样数据输出到串口
if(channels == 2) { //如果使用双声道,则将每个采样数据分为左右两个声道,分别输出
Serial.print("L:"); //输出左声道采样数据
Serial.print(sampleBuffer[i]); //输出左声道采样数据
Serial.print(" R:"); //输出右声道采样数据
i++; //右声道采样数据在左声道采样数据之后,所以需要将i加1
}
Serial.println(sampleBuffer[i]); //输出采样数据
delay(100); //延时,降低串口打印频率,是串口绘图仪的刷新速率降低,方便查看
}
samplesRead = 0; //重置采样数据读取计数器
}
}
void onPDMdata() { // PDM数据接收回调函数
int bytesAvailable = PDM.available(); //获取当前可用的采样数据字节数
PDM.read(sampleBuffer, bytesAvailable); //将采样数据读取到缓冲区中,缓冲区大小为bytesAvailable字节,即bytesAvailable/2个采样数据
samplesRead = bytesAvailable / 2; //更新采样数据读取计数器,采样数据数量为bytesAvailable/2个
}
4、【成果展示】
串口打印麦克风采样数据。
绘图仪显示音频波形。
选做任务一:通过RGB LED不同颜色、亮度显示PDM麦克风收到的声音大小
1、【任务介绍】
此次考虑在必做任务三的基础上,通过RGB的亮度,来显示接收声音的大小。
思路可以参考https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-microphone-basics/。
麦克风采样得到的数据,范围在-32700~32700之间,为了映射到RGB的亮度上,做了映射处理。
brightness = map(constrain(abs(sampleBuffer[i]),0,32700), 0, 32700, 255, 0); //将采样数据映射到255-0的范围,用于控制RGB灯的亮度
//麦克风采样值有正和负,所以取绝对值
analogWrite(LEDR, brightness); //将亮度值写入RGB灯的红色通道,控制红色通道的亮度
analogWrite(LEDG, brightness); //将亮度值写入RGB灯的绿色通道,控制绿色通道的亮度
analogWrite(LEDB, brightness); //将亮度值写入RGB灯的蓝色通道,控制蓝色通道的亮度
2、【软件流程图】
3、【代码片段】
#include <PDM.h> //导入PDM库
#include <WiFiNINA.h> //导入WiFiNINA库,用于RGB的控制
static const char channels = 1; //单声道,如果使用双声道,则将1改为2
static const int frequency = 16000; //采样频率,默认为16000Hz
short sampleBuffer[512]; //缓冲区,用于读取采样数据,每个采样数据是16位,即2字节,所以缓冲区大小为512*2=1024字节
volatile int samplesRead; //采样数据读取计数器,用于记录已经读取的采样数据数量,该变量是volatile的,因为它是被中断服务程序修改的
int brightness = 0; //RGB灯的亮度,用于控制RGB灯的亮度
void setup() {
Serial.begin(9600); //初始化串口通信,波特率为9600
while (!Serial); //等待串口连接
pinMode(LEDR, OUTPUT); //设置RGB灯的引脚为输出模式
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
PDM.onReceive(onPDMdata); //设置在准备读取新PDM数据时调用的回调函数,onPDMdata函数将在PDM数据接收中断时会被调用
if (!PDM.begin(channels, frequency)) { //初始化PDM,设置采样通道数和采样频率,如果初始化失败,则输出错误信息并停止程序
Serial.println("Failed to start PDM!");
while (1);
}
}
void loop() {
if (samplesRead) { //如果已经读取了采样数据,则进行处理
for (int i = 0; i < samplesRead; i++) { //遍历采样数据,将每个采样数据输出到串口
if(channels == 2) { //如果使用双声道,则将每个采样数据分为左右两个声道,分别输出
Serial.print("L:"); //输出左声道采样数据
Serial.print(sampleBuffer); //输出左声道采样数据
Serial.print(" R:"); //输出右声道采样数据
i++; //右声道采样数据在左声道采样数据之后,所以需要将i加1
}
Serial.println(sampleBuffer); //输出采样数据
brightness = map(constrain(abs(sampleBuffer[i]),0,32700), 0, 32700, 255, 0); //将采样数据映射到255-0的范围,用于控制RGB灯的亮度
//麦克风采样值有正和负,所以取绝对值
analogWrite(LEDR, brightness); //将亮度值写入RGB灯的红色通道,控制红色通道的亮度
analogWrite(LEDG, brightness); //将亮度值写入RGB灯的绿色通道,控制绿色通道的亮度
analogWrite(LEDB, brightness); //将亮度值写入RGB灯的蓝色通道,控制蓝色通道的亮度
Serial.print("The brightness of RGB is: ");
Serial.println(brightness); //输出RGB灯的亮度值
delay(100);
}
samplesRead = 0; //重置采样数据读取计数器
}
}
void onPDMdata() { // PDM数据接收回调函数
int bytesAvailable = PDM.available(); //获取当前可用的采样数据字节数
PDM.read(sampleBuffer, bytesAvailable); //将采样数据读取到缓冲区中,缓冲区大小为bytesAvailable字节,即bytesAvailable/2个采样数据
samplesRead = bytesAvailable / 2; //更新采样数据读取计数器,采样数据数量为bytesAvailable/2个
}
4、【成果展示】
串口输出麦克风采样值,以及RGB亮度设置值(255~0范围,数值越小,亮度越高)。
RGB亮度随周围环境声音大小而改变。
- 2024-08-31
-
上传了资料:
【Follow me第二季第1期】_任务实现CircuitPython代码
- 2024-08-29
-
发表了主题帖:
【Follow me第二季第1期】任务汇总_By CJ
本帖最后由 andy11112 于 2024-9-1 17:43 编辑
大家好,以下是此次Follow me第二季第一期的任务汇总,欢迎交流。
【视频汇总】:
入门任务,0:12
基础任务一,1:10
基础任务二,1:42
基础任务三,2:25
进阶任务,3:32
创意任务二,4:16
【源码链接】:https://download.eeworld.com.cn/detail/andy11112/634234
【总体心得】:
关于此次活动,从中学到了Adafruit Circuit Playground Express(CPX)的使用,具备多种传感器是这个板子的特点。
此次使用CircuitPython,大部分设计的思路,可以通过官网以及lib中的example中找到。
入门任务(必做):开发环境搭建,板载LED点亮
1、【任务介绍】首先搭建开发环境,然后尝试点亮板载的红色LED指示灯。
搭建开发环境。
首先,使用MicroUSB连线,将CPX和电脑连接在一起,此时可以在资源管理器中,看到CPX的盘符。
此次计划使用CircuitPython编写代码,所以下载最新UF2文件(https://circuitpython.org/board/circuitplayground_express/)。
然后将UF2文件拖放到CPX盘符中,盘符会短暂消失,然后以“CIRCUITPY”名称出现。
下载最新的库包(https://circuitpython.org/libraries)。后续用于放入通用库和特定库,并参考案例代码。
此次选用Mu Editor,下载并安装(https://codewith.mu/)。Mu Editor编辑器的特点,在于可以同时查看CPX的串口输出,方便确认打印信息和做调试。
用到的硬件和配件,包括CPX板子,舵机(用于创意任务),鳄鱼夹连接线,电池盒,扭蛋等。
入门任务需要将红灯点亮,使用基础库,对cp.red_led赋值,即可实现。
3、【代码片段】
from adafruit_circuitplayground import cp
while True:
cp.red_led = True #赋值,True为点亮,False为熄灭
4、【功能展示】
运行代码后,红色指示灯常亮。
基础任务一(必做):控制板载炫彩LED,跑马灯点亮和颜色变换
1、【任务介绍】
任务要求,控制板上的彩灯,实现颜色变换和跑马灯效果。
查看官网,可以在其中找到关于彩灯控制的介绍和代码(https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-neopixel)。
参考官网介绍,导入colorwheel和neopixel库,用于实现颜色的变化和彩灯的控制。
2、【软件流程图】
3、【代码片段】
import time
from rainbowio import colorwheel
from adafruit_circuitplayground import cp
cp.pixels.brightness = 0.1 #设置彩灯的亮度,0为最暗(熄灭),1为最亮
cp.pixels.auto_write = False #设置False不会立即生效,需要调用pixels.show()
pixels_on = 1 #点亮彩灯,1为开启,0为关闭
pixels_color = 0 #彩灯颜色变换,1为开启,0为关闭
pixels_cycle = 0 #彩灯跑马灯,1为开启,0为关闭
#定义不同颜色
RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
OFF = (0, 0, 0)
def turn_on(wait): #定义点亮彩灯函数
cp.pixels[0] = (RED) #第1颗彩灯点亮,颜色为红色
cp.pixels.show()
time.sleep(wait) #间隔特定时间
cp.pixels.fill((YELLOW)) #全部彩灯点亮,颜色为黄色
cp.pixels.show()
time.sleep(wait) #间隔特定时间
cp.pixels.fill((OFF))
cp.pixels.show()
def color_change(wait): #定义颜色变换函数
for i in range(0,255,51): #颜色变化步进,一轮循环5个颜色
cp.pixels.fill((colorwheel(i & 255))) #colorwheel函数可以将一个HSV颜色值转换为RGB颜色值。idx & 255表示将idx除以256取余数,可得一个0-255之间的整数
cp.pixels.show()
time.sleep(wait)
def rainbow_cycle(wait): #定义跑马灯函数
for j in range(255):
for i in range(cp.pixels.n):
idx = int((i * 256 / cp.pixels.n) + j) #10个彩灯,彩色轮盘上的颜色值等间隔分配给各灯,同步变换颜色
cp.pixels[i] = colorwheel(idx & 255)
cp.pixels.show()
time.sleep(wait)
while True:
if pixels_on:
turn_on(1)
if pixels_color:
color_change(1)
if pixels_cycle:
rainbow_cycle(0.001)
4、【功能展示】
彩灯可以变换不同的颜色,以及实现不同颜色跑马灯效果(如下图)。
基础任务二(必做):监测环境温度和光线,通过板载LED展示舒适程度
1、【任务介绍】
该任务的关键,是要获得温度和光强数据,这需要用到板上的温度传感器和光传感器。
光传感器,从官网中能找到相关信息和代码示例(https://learn.adafruit.com/adafruit-circuit-playground-express/playground-light-sensor)。也可以在下载的库文件夹中的example路径下,找到光传感器的示例(路径:adafruit-circuitpython-bundle-9.x-mpy-20240820\examples\circuitplayground\circuitplayground_light.py),即可以简便的通过cp.light读取光传感器输入值。
温度传感器,官网中有介绍(https://learn.adafruit.com/adafruit-circuit-playground-express/playground-temperature)。同时库文件夹中,也有相关示例(上述同路径下的circuitplayground_temperature.py),通过cp.temperature读取温度值。
读取光强和温度值后,通过判断语句,在温度和光强超过舒适范围时,彩灯显示红灯以作警示;否则彩灯显示绿灯,表示环境条件在舒适范围。
2、【软件流程图】
3、【代码片段】
import time
from adafruit_circuitplayground import cp
def scale_range(value): #定义函数,将光线传感器读数范围(0~320)转换为0~9
return round(value / 320 * 9)
def light_monitor():
l = scale_range(cp.light)
print("Light level:", l)
if l >= 1 and l < 6:
return True
else:
return False
def temperature_monitor():
t = cp.temperature
print("Temperature:", t)
if t > 17 and t < 30:
return True
else:
return False
cp.pixel_brightness = 0.1
cp.pixels.auto_write = False
RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
OFF = (0, 0, 0)
while True:
if light_monitor():
print("Light is normal!")
if temperature_monitor():
print("Temperature is normal!")
if light_monitor() and temperature_monitor():
cp.pixels.fill(GREEN)
cp.pixels.show()
else:
cp.pixels.fill(RED)
cp.pixels.show()
time.sleep(1)
4、【功能展示】
光线充足时,显示绿灯;用手遮挡,光线不足时,显示红灯。
温度不便于调整,暂不做展示。
基础任务三(必做):接近检测——设定安全距离并通过板载LED展示,检测到入侵时,发起声音报警
1、【任务介绍】
该任务,要求检测距离,通过LED展示距离情况,当物体和板子距离非常近时,发出声音报警。
重点在于距离探测。通常的探测手段有红外和微波,CPX集成了红外收发模块,所以考虑基于红外做距离探测。
找到irremote库,其中有transmit功能,不过需要做脉冲和编码配置,对于此次测距来说,似乎有些冗余。
在网络上做搜索,找到了一个视频,介绍adafruit板子做IR Proximity,其中展示了Arduino代码实现。
视频中可以看出当中的逻辑:设置红外发射引脚,先关闭发射。循环【开启发射,延时10ms,关闭发射,读取接收引脚值,延时10ms】。间断开启红外发射,应该是避免红外发射管持续发射而烧坏。
由于我是用CircuitPython,需要将Arduino转换为CircuitPython,可以参照官网说明进行两种语言之间的代码改写(https://learn.adafruit.com/arduino-to-circuitpython/analog-input)。
同时可以参考官网资料(https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-analog-in)和CircuitPython中的模块说明(https://docs.circuitpython.org/en/latest/shared-bindings/digitalio/#digitalio.DigitalInOut),编写模拟和数字引脚的代码。
最终实现效果:物体距离板子3~4cm左右,彩灯开始亮绿灯做指示,彩灯亮起数量越多,表明物体距离越近;物体距离板子1~2cm左右,彩灯亮起数量超过5个,显示颜色由绿色变成红色,同时发出警报音。
2、【软件流程图】
3、【代码片段】
import time
import board
import digitalio
import analogio
from adafruit_circuitplayground import cp
# 定义红外接近传感器引脚
IR_TX_PIN = board.IR_TX # 指定红外发射引脚
PROX_PIN = board.IR_PROXIMITY # 指定红外接近传感器引脚
# 初始化引脚
ir_tx = digitalio.DigitalInOut(IR_TX_PIN) # 配置红外发射引脚
ir_tx.direction = digitalio.Direction.OUTPUT # 设置红外发射引脚为输出模式
ir_tx.value = False # 设置初始值为低电平,关闭红外发射
prox_in = analogio.AnalogIn(PROX_PIN) # 配置红外接近传感器引脚
cp.pixels.auto_write = False # 关闭自动写入,以便手动控制 NeoPixel
cp.pixels.brightness = 0.1 # 设置 NeoPixel 亮度
def distance_convert(value): # 将红外接近传感器的值转换为距离
return round((value-30000) / (42000-30000) * 10) # 将红外接近传感器的值转换为距离
def pixels_indicator(distance): # 设置 NeoPixel 指示灯
if distance <5: # 如果距离小于5
color = (0,255,255) # 设置颜色为蓝绿色
else:
color = (255,0,0) # 距离大于5,设置颜色为红色,用于警示
for i in range(10): #用于粗略量化指示距离情况
if i <= distance:
cp.pixels[i] = (color)
else:
cp.pixels[i] = (0, 0, 0)
cp.pixels.show()
def alarm(distance): # 定义报警函数
if distance > 5: # 如果距离小于5
cp.play_tone(262, 0.0001) # 播放音调
# 主循环
while True:
total = 0 # 重置总距离
for i in range(10):
ir_tx.value = True # 打开红外发射
time.sleep(0.01) # 等待一段时间,使红外发射器发射红外光
ir_tx.value = False # 关闭红外发射
#time.sleep(0.01) # 等待一段时间,使红外发射器停止发射红外光
prox = prox_in.value # 读取红外接近传感器的值
distance = distance_convert(prox) # 将红外接近传感器的值转换为距离
print("proximity:",prox,"distance:",distance,"temperature:",cp.temperature) # 打印红外接近传感器的值
total =total + distance # 计算总距离
time.sleep(0.01) #等待一段时间
avg = total / 10 # 计算平均距离
print("average distance:",avg) # 打印平均距离
pixels_indicator(avg) # 设置 NeoPixel 指示灯
alarm(avg) # 调用报警函数
4、【功能展示】
手指逐渐靠近板子,起初亮绿灯,随着距离越来越近,亮灯数量越多,超过5个灯亮起时,变成红灯,并且发出警报声。
进阶任务(必做):制作不倒翁——展示不倒翁运动过程中的不同灯光效果
1、【任务介绍】
通过运动传感器,探测CPX的倾斜活动,用彩灯实现指示效果。
重点在于读取运动传感器的数据,换算成角度信息,并和彩灯位置对应。
库文件夹中example路径(\examples\circuitplayground\circuitplayground_advanced_examples\circuitplayground_gravity_pulls_pixel.py),有类似的实现代码,可作为参考,在此基础上稍作修改,加上不同倾斜位置播放不同频率的音调。
2、【软件流程图】
3、【代码片段】
涉及数学运算,导入math库
import time
import math
from adafruit_circuitplayground import cp
PIXEL_SPACING_ANGLE = 30 # 像素之间,间隔30°
STANDARD_GRAVITY = 9.81 # 标准重力加速度
BACKGROUND_COLOR = 0, 0, 64 # 背景颜色,蓝色
MIN_BRIGHTNESS = 15 # 最小亮度,15
LIGHTING_ARC_LENGTH = 45 # 照明弧长,45°
def compute_pixel_angles(): # 计算像素角度,排除第5和第11个像素(像素从0开始计数)
return [
(300 + PIXEL_SPACING_ANGLE * n) % 360 for n in range(12) if n not in (5, 11)
] # % 360 是为了确保角度在0到360之间,因为角度是循环的
def degrees_between(a1, a2): # 计算两个角度之间的最小度数差
smaller = min(a1, a2) # 取两个角度中的较小值
larger = max(a1, a2) # 取两个角度中的较大值
return min(larger - smaller, 360 + smaller - larger) # 返回两个角度之间的最小度数差
def pixel_brightness(distance_from_down, accel_magnitude):
'''
计算像素亮度,
distance_from_down表示像素距离地面的距离,
accel_magnitude表示加速度计测量的重力加速度的大小。
'''
half_lighting_arc_length = LIGHTING_ARC_LENGTH / 2 # 照明弧长的一半
if accel_magnitude < 0.1 or distance_from_down > half_lighting_arc_length: # 如果加速度小于0.1或者距离超过一半的照明弧长,则返回None
return None
normalized_nearness = 1 - distance_from_down / half_lighting_arc_length # 计算归一化的接近度
scale_factor = (255 - MIN_BRIGHTNESS) * accel_magnitude # 计算缩放因子
color_part = MIN_BRIGHTNESS + round(normalized_nearness * scale_factor) # 计算颜色部分
return color_part
def angle_in_degrees(x, y): # 计算点(x, y)的角度,返回-180到180之间的角度
return math.atan2(y, x) / math.pi * 180 # math.atan2(y, x)返回的是弧度,需要转换为角度
def positive_degrees(angle): # 将-180到180的角度转换为0到360的角度
return (angle + 360) % 360 # (angle + 360) % 360 是为了确保角度在0到360之间,因为角度是循环的
cp.pixels.brightness = 0.1 # 设置像素亮度为0.1
pixel_positions = compute_pixel_angles() # 计算像素角度
while True:
debug = cp.switch #滑动开关,启用调试模式
accel_x, accel_y = cp.acceleration[:2] # Ignore z,cp.acceleration[:2]获取加速度计的加速度值,并转换为列表
down_angle = positive_degrees(angle_in_degrees(accel_x, accel_y)) # 计算地面的角度
magnitude_limit = STANDARD_GRAVITY # 设置加速度计测量的重力加速度的大小为标准重力加速度
normalized_magnitude = (
min(math.sqrt(accel_x * accel_x + accel_y * accel_y), magnitude_limit)
/ magnitude_limit
) # 计算归一化的重力加速度的大小
pixels_lit = [] #创建一个空列表,用于存储被照亮的像素
for i, pixel_position in enumerate(pixel_positions): # 遍历每个像素
pe = pixel_brightness(
degrees_between(pixel_position, down_angle), normalized_magnitude
) # 计算像素亮度
cp.pixels[i] = (pe, 0, 0) if pe else BACKGROUND_COLOR # 设置像素颜色
if pe:
pixels_lit.append((i, pe)) # 如果像素被照亮,则将其添加到pixels_lit列表中
cp.play_tone(262+i*150, 0.1) # 播放音调,音调频率为262+i*150,持续时间为0.1秒
if debug:
lit_formatted = ", ".join(("{}: {:>3d}".format(p, i) for p, i in pixels_lit))
print(
"x: {:>6.2f}, y: {:>6.2f}, angle: {:>6.2f}, mag: {:>3.2f}, pixels: [{}]".format(
accel_x, accel_y, down_angle, normalized_magnitude, lit_formatted
)
)
time.sleep(0.5)
4、【功能展示】
将CPX、电池盒放在扭蛋中,制成简易【不倒翁】。
CPX水平状态,全部彩灯为蓝色。
CPX倾斜后,倾斜一侧的彩灯变为红色,并播放对应频率的声音。
创意任务二:章鱼哥——章鱼哥的触角根据环境声音的大小,章鱼哥的触角可舒展或者收缩
1、【任务介绍】
根据任务要求,需要探测环境声音的强弱,然后控制电机转动,实现章鱼触角的伸缩。
探测声音,可用板上的麦克风来实现。但CPX上麦克风无法直接读取声音的模拟输入大小,所以找到了lib的example中的circuitplayground_sound_meter.py,用于实现读取环境音量大小,量化到0~10范围后,并用彩灯来指示。
控制电机,参考官网介绍(https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-servo),实现电机控制。此次使用的是【FS90R】,属于连续舵机,参考Continuous Servo Code。
添加条件判断:小音量,不做任何操作;中音量,触角缓慢小幅收缩;大音量,触角快速大幅收缩;按下按键,触角缓慢小幅伸展。
连续舵机的前转、后转的速度和行程,不太一致,短时间内没有添加详细的触角伸展逻辑,后续有待补充完善。
2、【软件流程图】
3、【代码片段】
涉及声音采集,导入audiobusio库
涉及舵机控制,导入pwmio库和adafruit_motor中的servo库(将lib中的adafruit_motor文件夹添加到CPX的lib路径中)
import array #导入array模块
import math #导入math模块
import board #导入board模块
import audiobusio #导入audiobusio模块
import pwmio
import time
from adafruit_motor import servo #导入servo模块
from adafruit_circuitplayground import cp
def constrain(value, floor, ceiling):
return max(floor, min(value, ceiling)) #将value限制在floor和ceiling之间,如果value小于floor,则返回floor,如果value大于ceiling,则返回ceiling,否则返回value
def log_scale(input_value, input_min, input_max, output_min, output_max): #定义一个log_scale函数,用于将输入值映射到输出值
normalized_input_value = (input_value - input_min) / (input_max - input_min) #将输入值归一化到0-1之间,即输入值减去最小值除以最大值减去最小值
return output_min + math.pow(normalized_input_value, 0.630957) * (
output_max - output_min
) #将归一化后的输入值映射到输出值,即输出最小值加上归一化后的输入值乘以输出最大值减去输出最小值,再乘以0.630957
def normalized_rms(values):
minbuf = int(sum(values) / len(values)) #计算输入信号的均值,即输入信号的总和除以输入信号的长度,并将结果转换为整数,作为输入信号的底值
return math.sqrt(
sum(float(sample - minbuf) * (sample - minbuf) for sample in values)
/ len(values)
) # 计算输入信号的均方根值,即输入信号的总和除以输入信号的长度,并将结果转换为浮点数,再乘以输入信号的总和除以输入信号的长度,最后取平方根
mic = audiobusio.PDMIn(
board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16
) #创建一个PDMIn对象,用于从麦克风读取音频数据,采样率为16000,位深度为16,时钟引脚为MICROPHONE_CLOCK,数据引脚为MICROPHONE_DATA,并将结果赋值给mic
samples = array.array("H", [0] * 160) #创建一个array对象,用于存储采样数据,长度为160,类型为H,即16位无符号整数,并将结果赋值给samples
mic.record(samples, len(samples)) #开始录音,将录音数据存储到samples中,长度为160
input_floor = normalized_rms(samples) + 10 #计算输入信号的均方根值,并加上10作为输入信号的底值
sensitivity = 500 # 设置灵敏度,即输入信号的底值与输入信号的顶值之间的差值
input_ceiling = input_floor + sensitivity #计算输入信号的顶值,即输入信号的底值加上灵敏度
pwm = pwmio.PWMOut(board.A1, frequency=50) #创建一个PWMOut对象,用于控制舵机,频率为50Hz,引脚为A1,并将结果赋值给pwm
my_servo = servo.ContinuousServo(pwm) #创建一个ContinuousServo对象,用于控制舵机,并将结果赋值给my_servo
peak = 0 #初始化峰值,即输入信号的顶值,用于在NeoPixels上显示峰值
while True:
mic.record(samples, len(samples)) #开始录音,将录音数据存储到samples中,长度为160
magnitude = normalized_rms(samples) #计算输入信号的均方根值,并将结果赋值给magnitude
print((magnitude,)) #打印输入信号的均方根值
c = log_scale(
constrain(magnitude, input_floor, input_ceiling),
input_floor,
input_ceiling,
0,
10,
) #将输入信号的均方根值映射到0-10之间,并将结果赋值给c
print(("c =", c))
cp.pixels.fill((0, 0, 0)) #将NeoPixels填充为黑色
for i in range(10): #遍历NeoPixels,用于指示音量的大小
if i < c:
cp.pixels[i] = (i * (255 // 10), 50, 0)
if c >= peak:
peak = min(c, 10 - 1)
elif peak > 0:
peak = peak - 1
if peak > 0:
cp.pixels[int(peak)] = (80, 0, 255)
cp.pixels.show()
if c >= 5 and c < 10: #中等音量,舵机向前缓慢转动
print("forward_slow")
my_servo.throttle = 0.1
time.sleep(0.05)
print("stop")
my_servo.throttle = 0.0
time.sleep(1)
if c >= 10: #高音量,舵机向前快速转动
print("forward_fast")
my_servo.throttle = 1
time.sleep(0.7)
print("stop")
my_servo.throttle = 0.0
time.sleep(1)
if cp.button_a or cp.button_b: #按下A或B按钮,舵机向后缓慢转动,用于手动回位
print("backward_slow")
my_servo.throttle = -0.1
time.sleep(0.1)
print("stop")
my_servo.throttle = 0.0
time.sleep(1)
4、【功能展示】
用一次性纸杯,纸条和棉线,配合舵机制成简易的章鱼哥。
探测环境声音,用彩灯指示音量大小。根据响应音量,控制章鱼哥触角收缩速度和幅度。
音量大于5(换算后的量化值),触角缓慢小幅收缩;音量大于9,触角快速大幅收缩。