只需要课程大纲或进度表+wokwi +大模型工具,就可以完全掌握嵌入式系统基础实验的所有核心点。
LCD
cpp
// Learn about the ESP32 WiFi simulation in
// https://docs.wokwi.com/guides/esp32-wifi
https://wokwi.com/projects/321525495180034642
#include <WiFi.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, 16, 2);
#define NTP_SERVER "pool.ntp.org"
#define UTC_OFFSET 0
#define UTC_OFFSET_DST 0
void spinner() {
static int8_t counter = 0;
const char* glyphs = "\xa1\xa5\xdb";
LCD.setCursor(15, 1);
LCD.print(glyphs[counter++]);
if (counter == strlen(glyphs)) {
counter = 0;
}
}
void printLocalTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
LCD.setCursor(0, 1);
LCD.println("Connection Err");
return;
}
LCD.setCursor(8, 0);
LCD.println(&timeinfo, "%H:%M:%S");
LCD.setCursor(0, 1);
LCD.println(&timeinfo, "%d/%m/%Y %Z");
}
void setup() {
Serial.begin(115200);
LCD.init();
LCD.backlight();
LCD.setCursor(0, 0);
LCD.print("Connecting to ");
LCD.setCursor(0, 1);
LCD.print("WiFi ");
WiFi.begin("Wokwi-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(250);
spinner();
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
LCD.clear();
LCD.setCursor(0, 0);
LCD.println("Online ^_^");
LCD.setCursor(0, 1);
LCD.println("Updating time...");
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
}
void loop() {
printLocalTime();
delay(250);
}
实验目的
本实验旨在通过结合使用ESP32的WiFi功能、RTC(实时时钟)和LCD(液晶显示屏)模块,实现一个能够显示当前时间并通过网络同步时间的系统。具体目标包括:
- 掌握ESP32的WiFi连接配置及基本使用方法。
- 学习如何在Arduino环境下使用NTP(网络时间协议)服务来同步时间。
- 熟悉LCD液晶显示屏的基本操作,包括初始化、显示文本等。
- 综合运用ESP32、LCD和WiFi技术,构建一个具有实时时间显示功能的系统。
实验步骤
- 硬件连接 :
- 将ESP32开发板与LCD液晶显示屏(通过I2C接口)连接。
- 确保ESP32能够接入网络(可以是路由器、热点等)。
- 软件环境配置 :
- 在Arduino IDE中安装ESP32的开发板支持包。
- 引入必要的库文件,如
WiFi.h
、Wire.h
和LiquidCrystal_I2C.h
。
- 编写代码 :
- 初始化LCD显示屏和WiFi连接。
- 配置NTP服务器,设置时区偏移量。
- 编写函数来检查WiFi连接状态,并在LCD上显示状态信息。
- 编写函数从NTP服务器获取当前时间,并在LCD上格式化显示。
- 代码上传与调试 :
- 将编写好的代码上传到ESP32开发板。
- 观察LCD显示屏上的时间是否随时间更新,检查网络连接状态。
- 调整代码中的NTP服务器和时区设置,确保时间显示的准确性。
- 优化与扩展 :
- 考虑加入用户输入功能,允许用户设置时区或选择NTP服务器。
- 实现时间的夏令时自动调整。
- 优化WiFi连接过程的用户反馈,提高用户体验。
实验原理
- WiFi连接:ESP32通过WiFi模块连接到无线网络,利用TCP/IP协议与服务器进行通信。
- NTP时间同步:ESP32通过NTP协议向NTP服务器发送请求,获取当前的网络时间,并根据本地时区进行转换。
- LCD显示:LCD液晶显示屏通过I2C接口与ESP32连接,ESP32向LCD发送控制命令和显示数据,实现文本的显示和更新。
实验内容
- ESP32 WiFi模块的配置与连接。
- NTP服务器的选择与时区设置。
- LCD液晶显示屏的初始化与文本显示。
- 时间获取与显示的逻辑实现。
思考题
- 如何在不改变硬件连接的情况下,将LCD显示屏的显示内容从时间改为其他信息(如天气、新闻标题等)?
- 如果NTP服务器无法连接或时间同步失败,如何设计备用方案以保证系统时间的准确性?
- 如何优化WiFi连接过程的用户体验,例如添加连接状态指示灯或增加用户反馈?
- 如果想要将时间显示改为24小时制,需要在代码中做哪些修改?
- 如何利用ESP32的其他功能(如蓝牙、ADC等)来扩展这个实时时间显示系统的功能?
仿真
代码
cpp
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#define TFT_DC 35
#define TFT_CS 3
#define TFT_MOSI 37
#define TFT_CLK 36
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK);
void setup() {
Serial.begin(115200);
Serial.println("Welcome to Wokwi, CORE-S3");
tft.begin();
// Configure the direction of the display
const uint8_t mode = 0xc8;
tft.sendCommand(ILI9341_MADCTL, &mode, 1);
tft.setCursor(88, 60);
tft.setTextColor(ILI9341_RED);
tft.setTextSize(3);
tft.println("CORE-S3");
}
const uint32_t colors[] = {
ILI9341_GREEN,
ILI9341_CYAN,
ILI9341_MAGENTA,
ILI9341_YELLOW,
};
uint8_t colorIndex = 0;
void loop() {
tft.setTextSize(2);
tft.setCursor(36, 106);
tft.setTextColor(colors[colorIndex++ % 4]);
tft.println("Welcome to Wokwi!Hello");
delay(250);
}
实验目的
本实验旨在通过使用Arduino和Adafruit_ILI9341库,在TFT显示屏上实现文本显示和颜色变换,以熟悉Arduino的图形用户界面编程和SPI通信。
实验步骤
- 硬件连接 :
- 将TFT显示屏的DC引脚连接到Arduino的35号引脚。
- 将TFT显示屏的CS引脚连接到Arduino的3号引脚。
- 将TFT显示屏的MOSI引脚连接到Arduino的37号引脚。
- 将TFT显示屏的CLK引脚连接到Arduino的36号引脚。
- 确保TFT显示屏的电源和地线分别连接到Arduino的5V和GND。
- 软件准备 :
- 在Arduino IDE中安装所需的库:SPI.h, Adafruit_GFX.h, Adafruit_ILI9341.h。
- 将提供的代码复制到Arduino IDE中,并上传到Arduino板。
- 代码运行 :
- 上传代码后,打开Arduino IDE的串口监视器,设置波特率为115200。
- 观察TFT显示屏上的显示内容,验证文本是否正确显示,并且颜色是否按预期变换。
实验原理
- SPI通信:Serial Peripheral Interface(SPI)是一种同步串行通信接口,用于微控制器与各种外围设备(如显示器、传感器等)之间的通信。本实验中,Arduino通过SPI接口与TFT显示屏通信,发送显示数据和命令。
- Adafruit_ILI9341库:这是一个专门用于控制ILI9341 TFT显示屏的Arduino库。它提供了丰富的API,用于初始化显示屏、设置显示参数、绘制图形和文本等。
- 文本显示和颜色变换:通过调用Adafruit_ILI9341库的相关函数,可以在TFT显示屏上指定位置显示文本,并设置文本的颜色。本实验中,通过循环改变颜色的索引值,实现文本颜色的变换。
实验内容
- 初始化TFT显示屏,包括设置SPI通信参数和显示屏的显示方向。
- 在显示屏上指定位置显示文本"CORE-S3",并设置文本颜色为红色。
- 在循环中,改变文本的颜色,使其在绿色、青色、品红色和黄色之间循环变换,每次变换间隔250毫秒。
思考题
- SPI通信的原理是什么?
- SPI通信是一种同步串行通信方式,通过主设备(Master)和从设备(Slave)之间的时钟信号(CLK)和数据信号(MOSI/MISO)进行数据传输。主设备产生时钟信号,并控制数据传输的时机和方向。从设备根据时钟信号接收或发送数据。
- Adafruit_ILI9341库提供了哪些功能?
- Adafruit_ILI9341库提供了初始化显示屏、设置显示参数(如亮度、对比度、颜色等)、绘制基本图形(如点、线、矩形、圆形等)和文本显示等功能。这些功能使得用户可以方便地控制ILI9341 TFT显示屏进行各种图形和文本的显示。
- 如何修改代码以在显示屏上显示不同的文本或改变文本的位置?
- 要在显示屏上显示不同的文本,可以修改
tft.println("CORE-S3");
语句中的字符串内容。要改变文本的位置,可以修改tft.setCursor(88, 60);
语句中的坐标值。这两个函数分别用于设置要显示的文本内容和文本在显示屏上的位置。
- 要在显示屏上显示不同的文本,可以修改
- 如果希望加快或减慢文本颜色的变换速度,应该如何修改代码?
- 要加快或减慢文本颜色的变换速度,可以修改
delay(250);
语句中的延迟时间值。减小该值会加快颜色变换速度,增大该值会减慢颜色变换速度。
- 要加快或减慢文本颜色的变换速度,可以修改
cpp
/*
* Use of MAX72XX, DS1307 and DTH22 components to
* print some information on the display.
*
* for more examples:
* https://github.com/MajicDesigns/MD_Parola/tree/main/examples
* https://github.com/MajicDesigns/MD_MAX72XX/tree/main/examples
*/
// Header file includes
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <DHT.h>
#include <SPI.h>
#include <Wire.h>
#include "Font7Seg.h"
// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 4 // Define the number of displays connected
#define CLK_PIN 13 // CLK or SCK
#define DATA_PIN 11 // DATA or MOSI
#define CS_PIN 10 // CS or SS
#define SPEED_TIME 75 // Speed of the transition
#define PAUSE_TIME 0
#define MAX_MESG 20
// These are for the clock
#define DS1307_ADDRESS 0x68
// These are for the temperature
#define DHTPIN 2
#define DHTTYPE DHT22
#define TIMEDHT 1000
// Global variables
uint8_t wday, mday, month, year;
uint8_t hours, minutes, seconds;
char szTime[9]; // mm:ss\0
char szMesg[MAX_MESG + 1] = "";
float humidity, celsius, fahrenheit;
uint8_t degC[] = { 6, 3, 3, 56, 68, 68, 68 }; // Deg C
uint8_t degF[] = { 6, 3, 3, 124, 20, 20, 4 }; // Deg F
uint8_t clear = 0x00;
uint32_t timerDHT = TIMEDHT;
DHT dht(DHTPIN, DHTTYPE);
// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
void beginDS1307()
{
// Read the values (date and time) of the DS1307 module
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(clear);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 0x07);
seconds = bcdToDec(Wire.read());
minutes = bcdToDec(Wire.read());
hours = bcdToDec(Wire.read() & 0xff);
wday = bcdToDec(Wire.read());
mday = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
}
uint8_t decToBcd(uint8_t value)
{
return ((value / 10 * 16) + (value % 10));
}
uint8_t bcdToDec(uint8_t value)
{
return ((value / 16 * 10) + (value % 16));
}
// Code for reading clock time
void getTime(char *psz, bool f = true)
{
sprintf(psz, "%02d%c%02d", hours, (f ? ':' : ' '), minutes);
}
// Code for reading clock date
void getDate(char *psz)
{
char szBuf[10];
sprintf(psz, "%d %s %04d", mday , mon2str(month, szBuf, sizeof(szBuf) - 1), (year + 2000));
}
// Code for get Temperature
void getTemperature()
{
// Wait for a time between measurements
if ((millis() - timerDHT) > TIMEDHT) {
// Update the timer
timerDHT = millis();
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
humidity = dht.readHumidity();
// Read temperature as Celsius (the default)
celsius = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
fahrenheit = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again)
if (isnan(humidity) || isnan(celsius) || isnan(fahrenheit)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
}
}
// Get a label from PROGMEM into a char array
char *mon2str(uint8_t mon, char *psz, uint8_t len)
{
static const __FlashStringHelper* str[] =
{
F("Jan"), F("Feb"), F("Mar"), F("Apr"),
F("May"), F("Jun"), F("Jul"), F("Aug"),
F("Sep"), F("Oct"), F("Nov"), F("Dec")
};
strncpy_P(psz, (const char PROGMEM *)str[mon - 1], len);
psz[len] = '\0';
return (psz);
}
char *dow2str(uint8_t code, char *psz, uint8_t len)
{
static const __FlashStringHelper* str[] =
{
F("Sunday"), F("Monday"), F("Tuesday"),
F("Wed_Hello"), F("Thursday"), F("Friday"),
F("Saturday")
};
strncpy_P(psz, (const char PROGMEM *)str[code - 1], len);
psz[len] = '\0';
return (psz);
}
void setup(void)
{
Wire.begin();
P.begin(2);
P.setInvert(false);
P.setZone(0, MAX_DEVICES - 4, MAX_DEVICES - 1);
P.setZone(1, MAX_DEVICES - 4, MAX_DEVICES - 1);
P.displayZoneText(1, szTime, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, 0, PA_PRINT , PA_NO_EFFECT);
P.addChar('$', degC);
P.addChar('&', degF);
dht.begin();
}
void loop(void)
{
static uint32_t lastTime = 0; // Memory (ms)
static uint8_t display = 0; // Current display mode
static bool flasher = false; // Seconds passing flasher
beginDS1307();
getTemperature();
P.displayAnimate();
if (P.getZoneStatus(0))
{
switch (display)
{
case 0: // Temperature deg Celsius
P.setPause(0, 1000);
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_UP);
display++;
dtostrf(celsius, 3, 1, szMesg);
strcat(szMesg, "$");
break;
case 1: // Temperature deg Fahrenheit
P.setTextEffect(0, PA_SCROLL_UP, PA_SCROLL_DOWN);
display++;
dtostrf(fahrenheit, 3, 1, szMesg);
strcat(szMesg, "&");
break;
case 2: // Humidity
P.setTextEffect(0, PA_SCROLL_DOWN, PA_SCROLL_LEFT);
display++;
dtostrf(humidity, 3, 0, szMesg);
strcat(szMesg, "%UR");
break;
case 3: // Clock
P.setFont(0, numeric7Seg);
P.setTextEffect(0, PA_PRINT, PA_NO_EFFECT);
P.setPause(0, 0);
if ((millis() - lastTime) >= 1000)
{
lastTime = millis();
getTime(szMesg, flasher);
flasher = !flasher;
}
if ((seconds == 00) && (seconds <= 30)) {
display++;
P.setTextEffect(0, PA_PRINT, PA_WIPE_CURSOR);
}
break;
case 4: // Day of week
P.setFont(0, nullptr);
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dow2str(wday, szMesg, MAX_MESG);
break;
default: // Calendar
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display = 0;
getDate(szMesg);
break;
}
P.displayReset(0); // Rest zone zero
}
}
实验步骤
- 准备硬件 :
- 确保你有一块Arduino开发板(如Arduino Uno)和一个连接电脑的数据线(通常是USB线)。
- 找到开发板上的LED_BUILTIN引脚。对于大多数Arduino板,这个内置LED通常连接在D13引脚上。
- 连接开发板 :
- 使用USB线将Arduino开发板连接到电脑。
- 软件设置 :
- 打开Arduino IDE(集成开发环境)。
- 将上述提供的代码复制到Arduino IDE的代码窗口中。
- 确认正确选择了你的Arduino板型号和端口(通常在"工具"菜单中设置)。
- 上传代码 :
- 点击IDE中的"上传"按钮,将代码烧录到Arduino开发板上。
- 观察IDE下方的状态栏,确认代码成功上传且没有错误信息。
- 观察结果 :
- 观察Arduino板上的内置LED,它应该会每5秒闪烁一次(亮5秒,灭5秒)。
实验原理
本实验利用了Arduino的基本数字输出功能来控制LED的开关状态。通过pinMode()
函数将D13引脚(LED_BUILTIN)设置为输出模式,然后使用digitalWrite()
函数改变该引脚的电平状态(HIGH或LOW),从而控制LED的亮灭。delay()
函数用于在LED状态改变之间提供时间延迟,本例中每次延迟5000毫秒(即5秒)。
实验内容
- 学习如何使用Arduino IDE编写和上传代码。
- 理解
pinMode()
、digitalWrite()
和delay()
函数的基本用法。 - 通过实践观察LED的闪烁现象,理解数字信号对物理设备的控制原理。
思考题
- 代码修改 :如果希望LED闪烁的频率加快,应该如何修改代码?
- 答:可以减少
delay()
函数的参数值。例如,将delay(5000);
改为delay(1000);
,则LED会以1秒为周期闪烁。
- 答:可以减少
- 多LED控制 :如果要在Arduino上控制多个LED同时或交替闪烁,需要如何扩展代码?
- 答:需要定义更多引脚为输出,并在
loop()
函数中分别对这些引脚使用digitalWrite()
进行控制。例如,可以使用pinMode(9, OUTPUT);
和pinMode(10, OUTPUT);
来设置两个额外的LED引脚,然后在循环中分别控制它们的亮灭。
- 答:需要定义更多引脚为输出,并在
- 故障排查 :如果LED不闪烁,可能的原因有哪些,应如何排查?
- 答:可能的原因包括:代码未成功上传、引脚连接错误、开发板未正确供电、LED损坏等。排查步骤可以包括:确认代码上传无误、检查USB连接、检查引脚是否正确设置且无短路、尝试更换LED或使用其他已知工作正常的引脚进行测试。
- 实际应用 :列举一些利用类似原理的实际应用场景。
- 答:交通信号灯控制、家用电器的开关控制、广告牌灯光效果、简单的报警系统指示等。
视频
ESP32等单片机实验报告快速完成-机器人控制器设计与编程基础实验高效版本
实验目的
本实验旨在通过使用Arduino和Adafruit_ILI9341库,在TFT显示屏上实现文本显示和颜色变换,以熟悉Arduino的图形用户界面编程和SPI通信。
实验步骤
硬件连接:
将TFT显示屏的DC引脚连接到Arduino的35号引脚。
将TFT显示屏的CS引脚连接到Arduino的3号引脚。
将TFT显示屏的MOSI引脚连接到Arduino的37号引脚。
将TFT显示屏的CLK引脚连接到Arduino的36号引脚。
确保TFT显示屏的电源和地线分别连接到Arduino的5V和GND。
软件准备:
在Arduino IDE中安装所需的库:SPI.h, Adafruit_GFX.h, Adafruit_ILI9341.h。
将提供的代码复制到Arduino IDE中,并上传到Arduino板。
代码运行:
上传代码后,打开Arduino IDE的串口监视器,设置波特率为115200。
观察TFT显示屏上的显示内容,验证文本是否正确显示,并且颜色是否按预期变换。
实验原理
SPI通信:Serial Peripheral Interface(SPI)是一种同步串行通信接口,用于微控制器与各种外围设备(如显示器、传感器等)之间的通信。本实验中,Arduino通过SPI接口与TFT显示屏通信,发送显示数据和命令。
Adafruit_ILI9341库:这是一个专门用于控制ILI9341 TFT显示屏的Arduino库。它提供了丰富的API,用于初始化显示屏、设置显示参数、绘制图形和文本等。
文本显示和颜色变换:通过调用Adafruit_ILI9341库的相关函数,可以在TFT显示屏上指定位置显示文本,并设置文本的颜色。本实验中,通过循环改变颜色的索引值,实现文本颜色的变换。
实验内容
初始化TFT显示屏,包括设置SPI通信参数和显示屏的显示方向。
在显示屏上指定位置显示文本"CORE-S3",并设置文本颜色为红色。
在循环中,改变文本的颜色,使其在绿色、青色、品红色和黄色之间循环变换,每次变换间隔250毫秒。
思考题
SPI通信的原理是什么?
SPI通信是一种同步串行通信方式,通过主设备(Master)和从设备(Slave)之间的时钟信号(CLK)和数据信号(MOSI/MISO)进行数据传输。主设备产生时钟信号,并控制数据传输的时机和方向。从设备根据时钟信号接收或发送数据。
Adafruit_ILI9341库提供了哪些功能?
Adafruit_ILI9341库提供了初始化显示屏、设置显示参数(如亮度、对比度、颜色等)、绘制基本图形(如点、线、矩形、圆形等)和文本显示等功能。这些功能使得用户可以方便地控制ILI9341 TFT显示屏进行各种图形和文本的显示。
如何修改代码以在显示屏上显示不同的文本或改变文本的位置?
要在显示屏上显示不同的文本,可以修改tft.println("CORE-S3");语句中的字符串内容。要改变文本的位置,可以修改tft.setCursor(88, 60);语句中的坐标值。这两个函数分别用于设置要显示的文本内容和文本在显示屏上的位置。
如果希望加快或减慢文本颜色的变换速度,应该如何修改代码?
要加快或减慢文本颜色的变换速度,可以修改delay(250);语句中的延迟时间值。减小该值会加快颜色变换速度,增大该值会减慢颜色变换速度。