25/1/13 算法笔记<嵌入式> 继续学习Esp32
PWM(Pulse Width Modulation,脉宽调制) 是一种通过快速切换高低电平来模拟中间电压值的技术。它广泛应用于控制 LED 亮度、电机速度、音频生成等场景。
analogWrite函数:用于在微控制器(如 Arduino)上生成模拟信号。
使用analogWrite改变亮度。
#define POT 26
#define LED 13
//初始化电位计输入信号
int pot_value;
int led_value;
void setup(){
pinMode(POT,INPUT);
pinMode(POT,INPUT);
}
void loop(){
//读取电位计模拟输入值
pot_value=analogRead(POT);
//吧电位计模拟输入值转换为LED模拟输出
led_value = pot_value/16;
analogWrite(LED,led_value);
delay(50);
}
使用LEDC外设
#define POT 26
#define LED 13
#define CHANNEL 0
#define RESOLUTION 12
#define FREQ 1000
//初始化电位计输入信号
int pot_value;//用于存储电位计读取的模拟值
int led_value;
void setup(){
//设置ADC分辨率
analogReadResolution(12);
//配置输入衰减
analogSetAttenuation(ADC_11db);
//建立LEDC通道,配置LEDC分辨率
ledcSetup(CHANNEL,1000,12);
//关联GPIO与LEDC通道
ledcAttachPin(LED,CHANNEL);
}
void loop(){
//读取电位计模拟输入值
pot_value=analogRead(POT);
//输出PWM
ledcWrite(CHANNEL,pot_value);
delay(50);
}
I2C控制LED1602
设计理念是:信号线尽量少并且速率要尽量高,信号线少,可以减少引脚占用,这对早期的芯片(引脚很少)的很重要。
标准的I2C需要两根信号线:
SCL:时钟线,时钟都是有master提供的
SDA:双向数据线,发数据或者收数据(收发不能同时)
简单来说,只需要2根线,就可以对多台设备传输大量数据,减少单片机上IO口的占用。
TIP:需要将LiquidCrystal_I2C.h和.cpp文件放在一个目录下
库的使用:
显示文字在液晶屏上。
#include "LiquidCrystal_I2C.h"
//构造LCD对象,设置地址,列数,行数
LiquidCrystal_I2C lcd(0x27,16,2);
void setup(){
//初始化LCD对象
lcd.init();
//打印内容
lcd.backlight();
lcd.print("Hello,world!");
}
void loop(){
}
通过串口显示
Serial.available()//判断串口缓冲区有没有数据,若大于0就读取了数据
Serial.read()//
从串口读取一个字节的数据,并将其写入 LCD 显示屏。这通常用于将通过串口接收到的字符显示在 LCD 上。
Adafruit_SSD1306的使用
上述知识均来自小破站的
18. SPI 控制 OLED 液晶屏_哔哩哔哩_bilibili这里面。
使用步骤:
1.初始化OLED,调用构造函数,调用begin方法
2.初始化完成后,调用绘制类函数,当然可以设置颜色,字体
3.绘制完毕,调用显示类函数display。
#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define WIDTH 128
#define HEIGH 64
#define OLED_MOSI 13
#define OLED_SCK 18
#define OLED_DC 2
#define OLED_CS 4
#define OLED_RESET 15
Adafruit_SSD1306 oled(WIDTH,HIGH,OLED_MOSI,OLED_SCK,
OLED_DC,OLED_RESET,OLED_CS);
//初始化进度
int progress=0;
void setup() {
//OLED初始化
oled.begin();
//清除显示
oled.clearDisplay();
//绘制水平线
oled.drawFastHLine(32,5,48,SSD1306_WHITE);
//绘制斜线
oled.drawLine(32,5,48,30,SSD1306_WHITE);
//绘制矩形
oled.drawRect(5,5,10,25,SSD1306_WHITE);
//绘制实心矩阵
oled.fillRect(75,5,10,30,SSD1306_WHITE);
//设置光标位置
oled.setCursor(5,50);
//设置文本颜色
oled.setTextColor(SSD1306_WHITE);
//显示文字
oled.println("Hello,world!");
//显示内容
oled.display();
}
void loop() {
//清楚显示
oled.clearDisplay();
//设置光标位置
oled.setCursor(25,40);
//显示文字
oled.println("Progress");
//显示进度条边框
oled.drawRoundRect(0,10,128,20,5,SSD1306_WHITE);
//显示进度
oled.fillRoundRect(5,15,progress,10,2,SSD1306_WHITE);
//进度递增
if (progress<118)
{
progress++;
}
else
{
progress=0;
}
//显示内容
oled.display();
//延时
delay(50);
}
U8G2库控制OLED
平台支持性好,支持中文
#include <Arduino.h>
#include <U8g2lib.h>
//构造对象
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0,18,13,4,2,15);
void setup() {
//初始化OLED对象
u8g2.begin();
//开启中文字符集支持
u8g2.enableUTF8Print();
//设置字体
u8g2.setFont(u8g2_font_wqy12_t_chinese2);
}
void loop() {
//清除缓存区内容
u8g2.clearBuffer();
//绘制内容
u8g2.setCursor(0,15);
u8g2.print("Hello,World!");
u8g2.setCursor(0,40);
u8g2.print("你好,ESP32");
//发送缓存区内容到OLED显示
}
按键控制菜单
#include <Arduino.h>
#include <U8g2lib.h>
#define BUTTON_UP 12
#define BUTTON_DOWN 14
void display_menu();
//定义菜单列表
char *menu[4] = {"Item 1","Item 2","Item 3","Item 4"};
//构造对象
U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI u8g2(U8G2_R0,18,13,4,2,15);
//初始化按键列表
unsigned int order = 0;
void setup() {
//初始化OLED对象
u8g2.begin();
u8g2.setFont(u8g2_font_6x12_tr);
//配置输出按键
pinMode(BUTTON_UP,INPUT_PULLUP);
pinMode(BUTTON_DOWN,INPUT_PULLUP);
}
void loop()
{
//判断按键是否按下,并记录位置
if(!digitalRead(BUTTON_UP)){
order = (order - 1 + 4)%4;
}
if(!digitalRead(BUTTON_DOWN)){
order = (order + 1)%4;
}
display_menu(order);
delay(80);
}
void display_menu(){
u8g2.firstPage();
do
{
//绘制页面内容
u8g2.drawStr(0,12,"Menu");
u8g2.drawHLinie(0,14,128);
for(int i=0;i<4;i++){
if(i==index){
u8g2.drawStr(5,(i+2)*12+2,">");
u8g2.drawStr(20,(i+2)*12+2,menu[i]);
}else{
u8g2.drawStr(5,(i+2)*12+2,menu[i]);
}
}
}while(u8g2.nextPage());
}
中断分类:
硬件中断,也称外部中断,一键中断响应外部硬件时间而发生,
软件中断:当触发软件事件时,会发生这种类型的中断。
在中断执行函数中不能用delay()函数
delay()
的工作原理
-
delay()
函数依赖于 Arduino 的millis()
函数,后者通过定时器中断来更新时间。 -
在
delay()
执行期间,程序会不断检查millis()
的值,直到达到指定的延时时间。 -
如果在 ISR 中使用
delay()
,会导致以下问题:-
阻塞其他中断:
delay()
依赖于定时器中断,但在 ISR 中,定时器中断被禁用,导致millis()
无法更新,delay()
会一直阻塞。 -
影响系统实时性:ISR 应该尽快执行完毕,
delay()
会延长 ISR 的执行时间,影响系统的响应能力。
-
简单来说delay是一个阻塞函数,他会暂停代码的执行,会中断积压。
定时器中断
ESP32通过定时器可以完成各种预设好的任务,ESP32定时器达到指定时间后也会产生中断,然后在回调函数内执行所需功能。
#define LED 2
#define LED_ONCE 4
hw_timer_t *timer = NULL;
hw_timer_t *timer_once = NULL;
// 定时中断函数:用于 LED 闪烁
void IRAM_ATTR timer_interrupt() {
digitalWrite(LED, !digitalRead(LED)); // 切换 LED 状态
}
// 一次性定时中断函数:用于点亮 LED_ONCE
void IRAM_ATTR timer_once_interrupt() {
digitalWrite(LED_ONCE, HIGH); // 点亮 LED_ONCE
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(LED_ONCE, OUTPUT);
// 初始化定时器
timer = timerBegin(0, 80, true); // 使用定时器 0,分频系数 80,向上计数
timer_once = timerBegin(1, 80, true); // 使用定时器 1,分频系数 80,向上计数
// 配置定时器中断
timerAttachInterrupt(timer, &timer_interrupt, true); // 绑定 timer_interrupt 到定时器 0
timerAttachInterrupt(timer_once, &timer_once_interrupt, true); // 绑定 timer_once_interrupt 到定时器 1
// 设置定时器周期(单位:微秒)
timerAlarmWrite(timer, 1000000, true); // 定时器 0 每 1 秒触发一次
timerAlarmWrite(timer_once, 5000000, false); // 定时器 1 在 5 秒后触发一次(仅一次)
// 启用定时器报警
timerAlarmEnable(timer);
timerAlarmEnable(timer_once);
}
void loop() {
// 主循环无需操作
}
软件定时器
#include <Ticker.h>
#define LED 2
#define LED_ONCE 4
//定义定时器对象
Ticker timer;
Ticker timer_once;
//切换指定引脚的电平状态
void toggle(int pin){
digitalWrite(pin,!digitalRead(pin));
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(LED_ONCE, OUTPUT);
//配置定时器
timer.attach(0.5,toggle,LED);
timer_once.once(3,toggle,toggle,LED_ONCE);
}
void loop() {
// 主循环无需操作
}
舵机转动
CHANNEL
是 ESP32 的 LEDC(LED PWM 控制器) 通道编号。ESP32 的 LEDC 模块提供了多个独立的 PWM 通道,可以用来生成 PWM 信号,控制设备如 LED 亮度、舵机角度等。
-
通道 0 用于生成 PWM 信号,控制连接到
SERVO
引脚(GPIO 13)的设备(如舵机)。
#define FREQ 50
#define CHANNEL 0
#define RESOLUTION 8
#define SERVO 13
int calculatePWM(int degree){
int min_width=0.5/20*pow(2,RESOLUTION);
int max_width=2.5/20*pow(2,RESOLUTION);
return (max_width-min_width)*degree/180+min_width;
}
void setup(){
//建立LEDC通道
ledcSetup(CHANNEL,FREQ,RESOLUTION);
//关联GPIO口与LEDC通道
ledcAttachPin(SERVO,CHANNEL);
}
void loop(){
for (int i=0;i<=180;i+=10){
//输出PWM,设置占空比
ledcWrite(CHANNEL,calculatePWM(i));
delay(500);
}
}
ok!明天见!