【Follow me第二季第2期】+ 基础任务【驱动LED矩阵+DAC正弦波+放大信号+ADC数据采集】
[复制链接]
本帖最后由 御坂10032号 于 2024-9-7 21:19 编辑
前言
在上一个章节中我们学习到了如何使用R4来Blink 和 使用串口输出数据, 那么本章节我们来研究以下如何驱动R4的LED矩阵和使用DAC生成正弦波,然后用OPAMP放大DAC信号。最后用ADC采集并且打印数据到串口等其他接口可上传到上位机显示曲线。
一、驱动LED矩阵
R4上有一个LED矩阵,根据官方的文档得知, 如果我们想使用LED矩阵主要为以下几步。
1- 引入头文件
#include "Arduino_LED_Matrix.h"
2-定义matrix对象
ArduinoLEDMatrix matrix;
3-在setup中调用matrix的start函数
matrix.begin();
代码如下所示
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
void setup() {
Serial.begin(115200);
matrix.begin();
}
那么上述的起始工作我们已经完成了,但是具体怎么来驱动矩阵呢? 由于这个LED矩阵是一个8*12的, 所以每一个LED灯都占用了一个bit用来存储led的状态。 如下代码所示
byte frame[8][12] = {
{ 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
上面的矩阵是一个爱心的形状, 如果想让这个形状更加清晰的话, 可以使用ctrl + f 然后搜索1, 此时你会更清晰的看到这个爱心。 如下图所示
紧接着我们便可以通过 matrix.renderBitmap(frame, 8, 12); 加载这个矩阵数组。 如下代码所示
// To use ArduinoGraphics APIs, please include BEFORE Arduino_LED_Matrix
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
byte frame[8][12] = {
{ 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
void setup() {
Serial.begin(115200);
matrix.begin();
matrix.renderBitmap(frame, 8, 12);
}
void loop() {
}
实验现象如下:
我们也可以换一种方式来表示LED的状态, 比如说使用16进制
unsigned long frame[] = {
0x3184a444,
0x42081100,
0xa0040000
};
为什么它可以表示LED的状态呢? 首先我们需要把他转换成2进制便得到如下数据
110001100001001010010001000100
1000010000010000001000100000000
10100000000001000000000000000000
然后32位对齐(高位补0保持原本数据不变)
00110001100001001010010001000100
01000010000010000001000100000000
10100000000001000000000000000000
之后呢, 再将其分成8*12的原始矩阵,便得到了如下数据
001100011000
010010100100
010001000100
001000001000
000100010000
000010100000
000001000000
000000000000
它还是上面我们定义的爱心矩阵。之后我们便可以通过matrix.loadFrame() 函数来加载这个定义的矩阵, 代码如下所示
// To use ArduinoGraphics APIs, please include BEFORE Arduino_LED_Matrix
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
const uint32_t heart[] = {
0x3184a444,
0x44042081,
0x100a0040
};
void setup() {
Serial.begin(115200);
matrix.begin();
matrix.loadFrame(heart);
}
void loop() {
}
实验现象如下所示
那么基础的原理理解的话,我们便可以看一下今天写的第一个代码, 通过数据帧的方式打印Hello EEworld and Digikey
1 - 首先定义一个头文件eeworld.h 用来存储每一个字母的状态
const uint32_t animation[][4] = {
{
0x8808,
0x880f80,
0x88088088,
},
{
0xf808,
0x800f80,
0x800800f8,
},
{
0x8008,
0x800800,
0x800800f8,
},
{
0x8008,
0x800800,
0x800800f8,
},
{
0x6009,
0x900900,
0x90090060,
},
{
0xf808,
0x800f80,
0x800800f8,
},
{
0xf808,
0x800f80,
0x800800f8,
},
{
0xf808,
0x800f80,
0x800800f8,
},
{
0x40,
0x12221540,
0x88000000,
},
{
0x6009,
0x900900,
0x90060000,
},
{
0xf009,
0x900f00,
0xc00a0090,
},
{
0x8008,
0x800800,
0x800f8000,
},
{
0xc00a,
0x900900,
0xa00c0000,
},
{
0x00,
0x00,
0x00,
},
{
0x400a,
0xa00e00,
0xa00a0000,
},
{
0xe00a,
0xa00a00,
0xa00a0000,
},
{
0xc00a,
0x900900,
0xa00c0000,
},
{
0x00,
0x00,
0x00,
},
{
0xc00a,
0x900900,
0xa00c0000,
},
{
0xe004,
0x400400,
0x400e0000,
},
{
0x1e012,
0x1201601,
0x101f0000,
},
{
0xe004,
0x400400,
0x400e0000,
},
{
0x900a,
0xc00a00,
0x90000000,
},
{
0xf008,
0x800f00,
0x800800f0,
},
{
0x1100a,
0x400400,
0x40040000,
},
{
0x00,
0x00,
0x00,
},
{
0x00,
0x00,
0x00,
}
};
2- 在主程序中引入头文件
#include "eeworld.h"
3- 完整代码如下所示
#include "eeworld.h"
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
const int frameCount = sizeof(animation) / sizeof(animation[0]); // 获取总帧数
int currentFrame = 0; // 当前帧计数器
void setup() {
Serial.begin(115200);
matrix.begin();
}
void loop() {
matrix.loadFrame(animation[currentFrame]); // 加载并显示当前帧
delay(500); // 延迟 500 毫秒
// 更新帧计数器以显示下一个帧
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0; // 如果达到最后一帧,则返回到第一帧
}
}
实验现象如下所示:
9月7日 (1)
附件代码:
当然还有第二种方式, 比如说使用Arduino 官方提供的LED滚屏的效果, 代码如下所示
// To use ArduinoGraphics APIs, please include BEFORE Arduino_LED_Matrix
#include "ArduinoGraphics.h"
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
void setup() {
Serial.begin(115200);
matrix.begin();
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
// add some static text
// will only show "UNO" (not enough space on the display)
const char text[] = "UNO r4";
matrix.textFont(Font_4x6);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText();
matrix.endDraw();
delay(2000);
}
void loop() {
// Make it scroll!
matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(50);
// add the text
const char text[] = " Hello EEWorld and DigiKey! ";
matrix.textFont(Font_5x7);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(text);
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
}
实验现象如下:
9月7日 (1)(1)
下面我再简单的介绍一下动画的矩阵是怎么实现的。我们可以借助官方的LED tool 来快速自定义我们自己的动画(删除掉持续时间的话就是加载单独帧)
我们可以使用笔刷直观的绘制我们想要的动画, 比如说我这里绘制了一个正方形(起始我们可以使用这个工具直接把绘制的动画烧录到Arduino中,但是谷歌好像不支持这个工具,并且我使用了火狐也没有成功)
绘制完成后点击右上角, 把代码下载下来。
把这个文件放到你Arduino的工程目录下, 并且引入到项目里。 代码如下
#include "Arduino_LED_Matrix.h" //Include the LED_Matrix library
#include "animation2.h"
// Create an instance of the ArduinoLEDMatrix class
ArduinoLEDMatrix matrix;
void setup() {
Serial.begin(115200);
// you can also load frames at runtime, without stopping the refresh
matrix.loadSequence(animation2);
matrix.begin();
matrix.play(true);
}
void loop() {
}
实验现象如下
cab33554025ea7066c0f3c540fe2d9a3
二、DAC 输出正弦波,放大信号,并且使用ADC采集显示在串口绘图中
这个任务主要分为三个部分, 分别是使用DAC输出正弦波, 然后使用信号放大器放大DAC输出,然后使用ADC采集输入.
1- DAC 输出
根据官方文档,得知 实现DAC比较简单, 我们只需要使用下面的代码并且按照图示搭建好电路
/*
SineWave
Generates a pre-generated sawtooth-waveform.
See the full documentation here:
https://docs.arduino.cc/tutorials/uno-r4-wifi/dac
*/
#include "analogWave.h" // Include the library for analog waveform generation
analogWave wave(DAC); // Create an instance of the analogWave class, using the DAC pin
int freq = 10; // in hertz, change accordingly
void setup() {
Serial.begin(115200); // Initialize serial communication at a baud rate of 115200
wave.sine(freq); // Generate a sine wave with the initial frequency
}
void loop() {
// Read an analog value from pin A5 and map it to a frequency range
freq = map(analogRead(A5), 0, 1024, 0, 10000);
// Print the updated frequency to the serial monitor
Serial.println("Frequency is now " + String(freq) + " hz");
wave.freq(freq); // Set the frequency of the waveform generator to the updated value
delay(1000); // Delay for one second before repeating
}
那么程序就会读取滑动变阻器的输出端(ADC)动态调整A0 (蜂鸣器输出的频率)
串口助手输出如下所示
由于比较拮据手头没有示波器,便使用了STC的试验箱,烧录了老梁示波器的代码。用来测量A0的输出
虚拟示波器输出如下所示
之后我们使用 opamp 对输出的数据进行放大。 参考官方文档, 我们可以使用下面的电路进行两倍的输出放大。
简单的搭建一下电路, 注意R0,也就是接地的电阻需要再10K欧姆, 另一个电阻则为30K欧姆
代码如下(此时我们已经完成了正弦波和放大信号,那么现在我们将使用ADC来读取放大后的信号)
#include "analogWave.h" // Include the library for analog waveform generation
#include <OPAMP.h>
analogWave wave(DAC); // Create an instance of the analogWave class, using the DAC pin
int freq = 10; // in hertz, change accordingly
void setup() {
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
Serial.begin(115200); // Initialize serial communication at a baud rate of 115200
wave.sine(freq); // Generate a sine wave with the initial frequency
}
void loop() {
// Read an analog value from pin A5 and map it to a frequency range
freq = map(analogRead(A5), 0, 1024, 0, 10000);
// Print the updated frequency to the serial monitor
Serial.println("Frequency is now " + String(freq) + " hz");
wave.freq(freq); // Set the frequency of the waveform generator to the updated value
delay(50); // Delay for one second before repeating
}
示波器输出如下所示(4V左右,滑动变阻器仍然可以调整输出频率):
参考官方文档, 我们可以使用如下API来配置ADC功能
1- analogReadResolution()
2- analogRead()
如果不需要修改分辨率的话, 可以不使用第一个API。我们在我们的代码上稍微修改下,使其可以把输出被串口绘图正确解析,同时可以获取A4的ADC输入
#include "analogWave.h" // Include the library for analog waveform generation
#include <OPAMP.h>
analogWave wave(DAC); // Create an instance of the analogWave class, using the DAC pin
int freq = 10; // in hertz, change accordingly
int reading = 0;
void setup() {
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
Serial.begin(115200); // Initialize serial communication at a baud rate of 115200
analogWriteResolution(14);
wave.sine(freq); // Generate a sine wave with the initial frequency
}
void loop() {
// Read an analog value from pin A5 and map it to a frequency range
freq = map(analogRead(A5), 0, 1024, 0, 10000);
// Print the updated frequency to the serial monitor
Serial.println("Frequency is now " + String(freq) + " hz");
reading = analogRead(A4);
Serial.print(reading);
wave.freq(freq); // Set the frequency of the waveform generator to the updated value
delay(50); // Delay for one second before repeating
}
电路如下
串口绘图工具输出如下(蓝色的为ADC读取的值,黄色的为当前的频率)
代码如下:
|