1. 按键控制电机PWM输出
1.1 硬件设计
按键 五向开关
手柄遥控器(按键):
18 下拉输入 – 前按 --转动
19 下拉输入-- 后按 --方向控制Flag
直流电机控制器:
26 数字输出–电机方向控制,按1下PIN19调换转动方向,其他(按2下)方向不变
25 PWM: 模拟输出
注意:G25 和 G26 外接控制电机控制器两根线原本是用来控制电机正反转的,若两根线短接一次就调换一次电机转向;若是两根线断开,不接任何信号,电机一直转动。这就是为什么ESP32扩展板上面没有插上ESP32主控开发板,电机一直转动的原因。
1.2 程序设计
pin26 一低一高 调换电机转动的方向
其实,第一个 pin26 设为 高可以不要,程序就成了以下:
1.3 线性控制
电机启动加速,停止减速的时候不能突然停下来,而是缓慢地降速或加速。
2. ADC测量电压
电阻串联组成分压电路:把29.4V电压分成11份,1K电阻占1份,10K电阻占10份
模数转换器 (ADC),使用 ESP32 读取模拟值意味着您可以测量 0V~29.4V之间的不同电压电平。
模拟输入引脚具有 12 位分辨率。 这意味着当您读取模拟输入时,其范围可能会在 0 到 4095 之间变化。将测量的电压分配给 0 到 4095 (4095 = 2^12 -1)之间的值,其中 0 V 对应于 0,29.4V 对应于 4095。等比例放大 139.2857142857143 。
ADC是非线性的理想情况下,您会期望在使用 ESP32 ADC 引脚时具有线性行为。然而,这不会发生。
意味着您的 ESP32 无法区分 29.3 V 和 29.4 V。两种电压的值相同:4095。对于非常低的电压值也会发生同样的情况:对于 0 V 和 0.1 V,您将获得相同的值:0。使用 ESP32 ADC 引脚时需要牢记这一点。
2.1 剩余电量百分比%检测
通过单片机读取模拟信号 GPIO34, 范围是0~4095
,然后 map函数
映射将电压转换为0~100
后显示在oled屏幕上。
display.print(map(34,0,4095,0,100));
2.2 读取ADC值计算成电压程序
/**
* @brief 通过读取20次ADC并计算平均值后计算出电源电压
* @param chn: ADC的通道 analogRead(ADC_PIN)
* @retval volt: 电压
*/
float getBatteryVoltServer(const int chn){
int adc_temp = 0; // 求和
int average_val = 0; // 平均值
float volt; // 电压
const float revise = 1.05; // 1.052759767579195
for (int t = 0; t < 20; t++){
adc_temp += analogRead(chn); // GPIO 34
delay(5);
}
average_val = adc_temp/20;//求平均值
volt = average_val*3.3*11.0*revise/1.0/4096; // Revise = 1.03 ,2^12(采样率)=4096
return volt;
}
*2.3 ESP32 ADC /DAC
网友:esp32的adc不行,硬件问题,这是向来的坑?
float adc;
adc = analogRead(ADC_PIN) * 4.2 / 4095; // ADC 原始结果计算电压公式:Vout = Dout * Vmax / Dmax
adc = analogReadMilliVolts(ADC_PIN) * 11 / 1000.0; // 10k + 1k 电阻串联分4.2V电压 1V = 1000mV
ESP32的ADC 和DAC 使用要点总结:
- 1.要用
dacWrite()
而非函数,前者 输出模拟信号,后者输出 PWM 信号;analogWrite()
- 2.要使用
analogReadMilliVolts()
直接读取电压,这个函数会结合衰减系数和特征曲线进行计算。 - 3.要把esp32 库升级到
2.0.5版本
以上,否则analogReadMilliVolts()
读数是错误的。 - 4.使用电阻分压,使待测电压降低到2500mV以内。
- 5.根据 ADC原始结果计算电压,可以使用公式:
Vout = Dout * Vmax / Dmax
- Vout :电压
- Dout :ADC 原始数字读数。
- Vmax :最大可测量输入模拟电压,请参阅 ADC 衰减。
- Dmax:输出 ADC 原始数字读取结果的最大值,在 Single Read 模式下为 4095,在 Continuous Read 模式下为 4095。
参考资料:
3. 蜂鸣器
3.1 硬件设计
- 蜂鸣器 C360615
4. OLED屏幕显示
4.1 硬件设计
- GPIO 21 (SDA)
- GPIO 22 (SCL)
4.2 程序设计
有个问题没有解决,在单独用USB数据线给开发板和扩展板连接的时候,OLED屏幕可以正常显示,但是连接在29.4V 和 电机控制器上的时候,OLED屏幕没有显示画面。后来发现是之前版本的esp32的扩展板有问题,跟换扩展板后问题解决。
5. 扩展板设计
问题:
1、 扩展板上的开关再打开的一瞬间,为什么会触发电机转动?
- 原因:参考问题4
2、开发板单独连接USB供电,oled屏幕可以正常显示,但是连接在电机控制器上面就不能正常显示?
- 原因:原扩展的问题
3、板载引出复位按钮,开发板下载程序后,需要按动RST复位按钮启动程序。
- 原因:ESP32-DevKitC开发板设置问题
4、电源 ->扩展板->电机控制,扩展板上面设置电源总开关,这样既控制了扩展板的电源,也控制了电机控制器的电源。不然的话,电机控制器老是跟电源连接,在下载程序或者扩展板上没有插上开发板的时候,接上总电源 电机控制器会控制电机一直转动。
- 原因: G25 和 G26 外接控制电机控制器两根线原本是用来控制电机正反转的,若两根线短接一次就调换一次电机转向;若是两根线断开,不接任何信号,电机一直转动。这就是为什么ESP32扩展板上面没有插上ESP32主控开发板(或者扩展板拨动开关的一瞬间)电机一直转动(或瞬间转动)的原因。
- 解决方法:可以利用扩展板上面的电源总开关,单刀双掷开关。开关拨动一侧,即控制扩展板、开发板或者电机控制器的电源;开关拨动到另一侧,电路连接电池的
GND
和G25管脚
的电机PWM控制线
连接,始终输出0,这样就禁止电机上电转动。参考:CD4053典型应用电路之模拟开关CD4053与单片机的连接。
5.1 LM2956 Buck电路原理
电路原理:由于LM2596属于开关型MOS管,通过一开一关控制输出电压,输出方波,要得到平滑的直流,需要输出端配合电容和电感组成LC低通滤波器。电感防止开关电路时电流的突变,且不消耗能量;电容防止电路中电压的突变。
-
LM2596 开关电源调节器中文手册 : https://wenku.baidu.com/view/44c441c52cc58bd63186bdab.html
-
DC-DC电源芯片 / DC-DC芯片 LM2596T-5.0 / TO-220直插式 立创编号 C398139
(小提示:按照厂商提供的芯片手册设计外围电路以及元器件选型。避免少踩坑。)
问题:
1、ESP32开发板不用29V转5V电源可以正常工作,使用LM2596 to 5V烧坏了两块开发板 ,开关电源输入电源滤波电容可能引起的问题 LM2596通断电之后损坏的原因分析
解决: 使用吸收上电尖峰性能更好的低ESR的铝电解电容或者钽电容来解决电压突变的问题,且合理的布局
5.1.1 元器件选型
参考:【B站@唐老师讲电赛】电源大师12—BUCK降压电路 BOOST升压电路,电感电容参数计算,前馈电容Cff计算
电容
电容和电感组成LC低通滤波器,电感防止电路中电流的突变,电容防止电路中电压的突变。
- 680uF ±20% 50V KF681M050I200A 立创编号 C59363
- 220uf 25V 立创编号C12450
- 0.1uF/100nF ±10% 50V 0805封装 ,并联Cin两端的钽电容 编号C1711
电感
电感防止电路中的电流突变。根据计算选择对应电感值、额定电流以及一体成型的电感。
33uH
±20%1.45A
137mΩ CKCS6045
-33uH/M6045
封装的 电感,立创编号 C354635
33uH
±20%3A
64.8mΩ 表贴 SLH1207
S330MTT 立创编号 C181844
33uH
±20%3A
107mΩ YSPI1050
-330M 立创编号C497900 (推荐使用这种一体成型的电感)
二极管
同右侧的电感、电容组成一条完整的回路,避免电子堆积在电感的一侧烧坏电路。
- 吸纳二极管 限流
- IN5824 肖特基二极管
- SS34 贴片 立创编号 C2944425
以下是 1N5822 40V 3A 525mV@3A 肖特基二极管 ,立创编号C915962
SS34 表贴 二极管
5.1.2 LM2596 PCB Layout 设计
BUCK 开关电源PCB layout 宝典
5.1.3 滑动开关
- SS12D11 G5 2档3脚
- 立创商品编号 C319012 (直插,立式,不带固定柱)
- 立创商品编号 C319010 (直插,卧式,带固定柱版)
- 立创商品编号 C319015 (直插,卧式,不带固定柱版)
RJ-45 以太网连接器
以太网连接器 俗称’水晶头’,专业术语为RJ-45连接器,属于双绞线以太网接口类型。RJ-45插头只能沿固定方向插入,设有一个塑料弹片与RJ-45插槽卡住以防止脱落。
- RJ45 10P/8C 立创商品编号: C386758
注意:RJ-45只是用作导线连接器使用,选择引脚和凹槽内金属触点直通的,不是带网络变压器的连接座。
6. 遥控手柄
小技巧:通过专利检索网站可以快速查找市面上的遥控手柄的设计作参考。例如:【佰腾网】 一种新型电动滑板车遥控器及电动滑板车
- 拨盘电位器
- 无人机/PS2摇杆
6.1 五向按键版
- 10*10多功能五向开关 表贴式 立创编号 C318948
- K1-1535DA-04 直插式 立创编号 C145902
COMMON是公共端,可以根据需要接VCC或者GND,配置上拉或者下拉输入。CENTER是按键按下,其他A B C D分别对应上下左右方向按键。
6.2 摇杆版
- 多功能开关 / 微型指拨摇杆开关 带中心按键 THB001P / 插件 立创商品编号:C2685355
有线RJ-45, 0.96寸oled ,无震动马达,摇杆版
有线RJ-45, 0.91寸oled ,震动马达,摇杆版
项目成果
硬件设计
立创EDA ESP32扩展板
第一版:
第一版烧坏了两块ESP32 WROOM 32 开发板,是因为DC BUCK降压电路没有设计好,PCB layout和电容的选型问题。
第二版:
第三版:
修改后
立创EDA ESP32遥控手柄
第一版:
第二版:
第三版:
第四版:
为了更好设计外壳,去掉了四个耳朵,为了顶面放置电池,将带哪个组放到了底面。
集成ESP32开发板
备注:这一版存在蓝牙了连接通信距离短的问题
软件设计
五向按键版的遥控手柄
Mixly 程序
Mixly 2.0
米思齐2.0版本 软件包比较大,有6G多。 利用blockly
Mixly 米思齐 图形化封装插件源代码路径:
D:\mixly\Mixly2.0-win32-x64\resources\app\board\arduino_esp32\libraries\ThirdParty
Mixly 米思齐 图形化依赖C代码头文件:
D:\mixly\Mixly2.0-win32-x64\arduino-cli\libraries\skateboard
资料下载
ESP32 for Arduino IDE
参考资料:
- ESP32 PWM 与 Arduino IDE(模拟输出):https://randomnerdtutorials.com/esp32-pwm-arduino-ide/
- ESP32 ADC – 使用 Arduino IDE 读取模拟值:https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/
- ESP32 带有 PIR 运动传感器,使用中断和定时器:https://randomnerdtutorials.com/esp32-pir-motion-sensor-interrupts-timers/
- ESP32 0.96寸OLED屏幕显示:https://naiva.blog.csdn.net/article/details/124190441
有线—五向按键版本
// V.04.22
#include <Wire.h> // 使用I2C库 ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h> //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64
#define LEDC_CHANNEL_0 0 // PWM通道 0
#define LEDC_TIMER_13_BIT 13 // 占空比分辨率 13位 注意:使用1至2^13-1的值来控制亮度
#define LEDC_BASE_FREQ 5000 // PWM 信号频率 5000 Hz
//#define PWM_PIN 25 // PWM输出 GPIO 25
const int ADC_PIN = 34; // ADC GPIO 34 (Analog ADC1_CH6)
const int PWM_PIN = 25; // PWM GPIO 5
const int BUZ_PIN = 27; // BUZZER GPIO 27
const int BUT_D_PIN = 15; // BUTTON DOWN GPIO 15 (五向按键按下)
int limitflag = 5000;
int brightness = limitflag; // PWM值变量
int fadeAmount = 5; // PWM累增(减)值
int adcValue = 0; // 存放读取的ADC数值
int buzState = LOW; // 蜂鸣器标识位
unsigned long previousMillis1 = 0; // 记录蜂鸣器上次时间
const long interval = 50; // 间隔时间 1000
boolean oledState = false; // 标识位 Flag
unsigned long previousMillis2 = 0; // 记录oled上次时间
// I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚
// PWM 输出控制函数
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
uint32_t duty = (8191 / valueMax) * min(value, valueMax); // 8191 = 2 ^ 13 - 1
ledcWrite(channel, duty);
}
// 外部中断服务函数
void IRAM_ATTR detectsMovement() {
Serial.println("BUTTON DOWN !!!");
oledState = true; // 标识位 Flag
previousMillis2 = millis();
}
void setup() {
Serial.begin(115200);
pinMode(BUZ_PIN, OUTPUT); //蜂鸣器设置为输出模式
pinMode(BUT_D_PIN, INPUT_PULLDOWN); //按钮设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断
// 中断引脚、中断服务函数、中断触发方式为FALLING
attachInterrupt(digitalPinToInterrupt(BUT_D_PIN), detectsMovement, FALLING);
//设置定时器并将定时器连接PWM引脚
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
//
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 0);//设置显示坐标
display.println(F("Naiva'S TEST"));//
display.setCursor(0, 20);//设置显示坐标
display.println(F("ESP32 for Arduino IDE"));//
display.display(); // 屏幕上实际显示文本
}
void loop() {
unsigned long currentMillis = millis(); // 单片机内部定时器记录 当前时间
// oled 屏幕 外部中断按键控制显示 和 间隔0.1S刷新屏幕
if((oledState || (currentMillis - previousMillis2 >= 500)) && !(adcValue<=2800)){
previousMillis2 = currentMillis;
Serial.println("按键:刷新屏幕显示PWM + ADC ");
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 20);//设置显示坐标
display.print(F("PWM: "));
display.println(brightness);
display.setCursor(0, 35);//设置显示坐标
display.print(F("ADC: "));
display.println(adcValue);
display.display(); // 屏幕上实际显示文本
oledState = false;//
}
// ADC
adcValue = analogRead(ADC_PIN);
Serial.print("adcValue:");Serial.println(adcValue);
// PWM 输出
// ledcAnalogWrite(LEDC_CHANNEL_0, brightness); //在LEDC通道0上设置PWM亮度
ledcWrite(LEDC_CHANNEL_0,brightness);
Serial.print("brightness:");Serial.println(brightness);
brightness = brightness + fadeAmount; // 改变亮度
if (brightness <= limitflag || brightness >= 8000) // 加/减PWM范围5000~ 8000
{fadeAmount = -fadeAmount;}
// 低电压报警 ADC
if(adcValue<=2800){
if (currentMillis - previousMillis1 >= interval) {
previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
Serial.println("屏幕显示:低电压报警!!!");
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 20);//设置显示坐标
display.println(F("LOW WARNING!!!"));
display.setCursor(0, 35);//设置显示坐标
display.print(F("ADC: "));
display.println(adcValue);
display.display(); // 屏幕上实际显示文本
if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
digitalWrite(BUZ_PIN, buzState);
}
}// 关闭蜂鸣器器
else {digitalWrite(BUZ_PIN, LOW);}
delay(100);
}
功能描述:
- 按键输入控制加速,减速,刹车及电机正反转方向: 线性加速–按住五向按键的前进按键不放进行线性累加100,达到线性加速效果;线性减速–松开五向按键的前进按键,线性每次在原来PWM速度上累减100;刹车–按一下五向按键的后退按键实现紧急刹车;正反转控制–按下五向按键的中间按键ENTER,实现电机方向调换。
- 电机PWM控制:初始化PWM通道设置及线性控制电机缓慢加速与减速。
- ADC测量电压:ADC 显示剩余电量百分比%
- 蜂鸣器低电压报警: 当读取到的ADC<=2500(limitLowADC 低电压阈值)时,进入防误判检测 checkState,确认低电压无误后,通过内部定时计数器实现蜂鸣器间隔 50ms 连续警报声。
- 0.96OLED屏幕显示:
millis
定时计数器实现实时刷新显示oled 屏幕,还可以通过按下五向按键的中间按键(ENTER),触发外部中断从而刷新屏幕。
// V.04.24
#include <Wire.h> // 使用I2C库 ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h> //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
// 初始化PWM通道设置
#define SCREEN_WIDTH 128 // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64
#define LEDC_CHANNEL_0 0 // PWM通道 0
#define LEDC_TIMER_13_BIT 13 // 占空比分辨率 13位 注意:使用1至2^13-1的值来控制亮度
#define LEDC_BASE_FREQ 5000 // PWM 信号频率 5000 Hz
//#define PWM_PIN 25 // PWM输出 GPIO 25
// 定义GPIO引脚
const int ADC_PIN = 34; // ADC GPIO 34 (Analog ADC1_CH6)
const int PWM_PIN = 25; // PWM GPIO 25
const int DIR_PIN = 26; // DIRECTION GPIO 26
const int BUZ_PIN = 27; // BUZZER GPIO 27
const int BUT_D_PIN = 15; // BUTTON DOWN GPIO 15 (五向按键-下)
const int BUT_F_PIN = 18; // BUTTON FORWARD GPIO 18 (五向按键-前)
const int BUT_B_PIN = 19; // BUTTON BACK GPIO 19 (五向按键按-后)
// 定义PWM相关变量
int limitfDownPWM = 3000; // PWM (下限)值
int limitfUpPWM = 8000; // PWM (上限)值
int targetPWM; // PWM 目标值
int pwmValue = 3000; // PWM值变量
int fadeAmount = 100; // PWM累增(减)量
// 定义ADC相关变量
int adcValue = 0; // 存放读取的ADC数值
int limitLowADC = 2500; // 低电压变量
// 定义蜂鸣器相关变量
int buzState = LOW; // 蜂鸣器标识位
unsigned long previousMillis1 = 0; // 记录蜂鸣器 上次时间
const long intervalBUZ = 50; // 蜂鸣器鸣叫 间隔时间 50ms
// 定义OLED屏幕相关变量
boolean oledState = false; // 标识位 Flag
unsigned long previousMillis2 = 0; // 记录OLED 上次时间 刷新时间
const long intervalOLED = 200; // 屏幕刷新 间隔时间 200ms
// I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚
// PWM 输出控制函数
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
uint32_t duty = (8191 / valueMax) * min(value, valueMax); // 8191 = 2 ^ 13 - 1
ledcWrite(channel, duty);
}
// 外部中断服务函数 -- 刷新屏幕和控制电机方向
void IRAM_ATTR detectsMovement() {
Serial.println("BUTTON DOWN !!!");
digitalWrite(DIR_PIN,LOW); // 电机 GPIO26 方向控制输出 0
oledState = true; // OLED 标识位
previousMillis2 = millis();
}
void setup() {
Serial.begin(115200);
pinMode(BUZ_PIN, OUTPUT); //GPIO27 蜂鸣器 设置为输出模式
pinMode(DIR_PIN, OUTPUT); //GPIO26 电机方向 设置为输出模式
// 注意:GPIO一定要设置为内部下拉输入模式,否则读取到的数值是0和1来回跳动,导致加减速度时 PWM停滞在某一两个数值之间来回跳动
pinMode(BUT_F_PIN, INPUT_PULLDOWN); //GPIO18 五向按键按-前 设置为下拉输入模式
pinMode(BUT_B_PIN, INPUT_PULLDOWN); //GPIO19 五向按键按-后 设置为下拉输入模式
pinMode(BUT_D_PIN, INPUT_PULLDOWN); //GPIO19 五向按键按-下 设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断
// 中断引脚、中断服务函数、中断触发方式为FALLING
attachInterrupt(digitalPinToInterrupt(BUT_D_PIN), detectsMovement, FALLING);
//设置定时器并将定时器连接PWM引脚
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
//
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 0);//设置显示坐标
display.println(F("Naiva'S TEST"));//
display.setCursor(0, 20);//设置显示坐标
display.println(F("ESP32 for Arduino IDE"));//
display.display(); // 屏幕上实际显示文本
delay(3000);
}
void loop() {
unsigned long currentMillis = millis(); // 定时计数器=当前时间, millis()的返回值为类型unsigned long
// 定时计数器实现 -- 实时刷新oled 屏幕 外部中断按键控制刷新 和 实时刷新屏幕
if((oledState || currentMillis - previousMillis2 >= intervalOLED) && !(adcValue <= limitLowADC)){
previousMillis2 = currentMillis;
// Serial.println("按键:刷新屏幕显示PWM and ADC ");
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 20);//设置显示坐标
display.print(F("PWM: "));
display.println(pwmValue);
display.setCursor(0, 35);//设置显示坐标
display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
display.display(); // 屏幕上实际显示文本
oledState = false; // 用于外部中断标签
}
// ADC
adcValue = analogRead(ADC_PIN);
// Serial.print("adcValue:");Serial.println(adcValue);
// PWM 输出
//GPIO18 BUT_F_PIN 油门
if(digitalRead(BUT_F_PIN)==1){
digitalWrite(DIR_PIN,HIGH); // GPIO26 方向控制 输出1
targetPWM = limitfUpPWM; // 线性加速 targetPWM= 8000 按下五向按键按-前
}else{targetPWM = limitfDownPWM;} // 线性减速 targetPWM= 3000 松开五向按键按-前
Serial.print("digitalRead(BUT_F_PIN):");Serial.println(digitalRead(BUT_F_PIN)); // 串口打印
//GPIO19 BUT_B_PIN 刹车 按下五向按键按-后
if(digitalRead(BUT_B_PIN)==1){
pwmValue = 3000; // 紧急刹车 pwmValue=3000
}
digitalWrite(DIR_PIN,HIGH); // GPIO26 方向控制 输出1
// pwmValue = pwmValue + fadeAmount; // 改变亮度
// if (pwmValue <= limitfDownPWM || pwmValue >= limitfUpPWM) // 加/减PWM范围5000~ 8000
// {fadeAmount = -fadeAmount;}
if (pwmValue < targetPWM ) // 加/减速度,PWM范围是3000~ 8000
{pwmValue = pwmValue + fadeAmount;}else {pwmValue = pwmValue - fadeAmount;}
Serial.print("targetPWM:");Serial.println(targetPWM); // 串口打印
// ledcAnalogWrite(LEDC_CHANNEL_0, pwmValue); //在LEDC通道0上设置PWM亮度
ledcWrite(LEDC_CHANNEL_0,pwmValue); // 设置PWM
Serial.print("pwmValue:");Serial.println(pwmValue); // 串口打印PWM值
// 定时计数器实现 --低电压报警 ADC<=2500
if(adcValue <= limitLowADC){
static int count = 0; // 静态变量 count, 累计记录ADC低电压状态 防止误判关闭总开关时的低电压触发蜂鸣器报警
boolean checkState = false;
count ++;
if(count >= 20){checkState = true;}else{checkState = false;}
Serial.print("count:");Serial.println(count); // 串口打印
if (adcValue <= limitLowADC && currentMillis - previousMillis1 >= intervalBUZ && checkState == true) {
previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
// Serial.println("屏幕:低电压报警!!!");
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 20);//设置显示坐标
display.println(F("LOW WARNING!!!"));
display.setCursor(0, 35);//设置显示坐标
display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 4095 = 2^12bit ADC 显示剩余电量百分比%
display.display(); // 屏幕上实际显示文本
if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
digitalWrite(BUZ_PIN, buzState);
}
}// 关闭蜂鸣器器
else {digitalWrite(BUZ_PIN, LOW);}
delay(100);
}
// V.05.06
#include <Wire.h> // 使用I2C库 ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h> //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
// 初始化PWM通道设置
#define SCREEN_WIDTH 128 // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64
#define LEDC_CHANNEL_0 0 // PWM通道 0
#define LEDC_TIMER_13_BIT 13 // 占空比分辨率 13位 注意:使用1至2^13-1的值来控制亮度
#define LEDC_BASE_FREQ 5000 // PWM 信号频率 5000 Hz
//#define PWM_PIN 25 // PWM输出 GPIO 25
// 定义GPIO引脚
const int ADC_PIN = 34; // ADC GPIO 34 (Analog ADC1_CH6)
const int PWM_PIN = 25; // PWM GPIO 25
const int DIR_PIN = 26; // DIRECTION GPIO 26
const int BUZ_PIN = 27; // BUZZER GPIO 27
const int BUT_D_PIN = 15; // BUTTON DOWN GPIO 15 (五向按键-下)
const int BUT_F_PIN = 18; // BUTTON FORWARD GPIO 18 (五向按键-前)
const int BUT_B_PIN = 19; // BUTTON BACK GPIO 19 (五向按键按-后)
// 定义PWM相关变量
int limitfDownPWM = 3000; // PWM (下限)值
int limitfUpPWM = 8000; // PWM (上限)值
int targetPWM; // PWM 目标值
int pwmValue = 3000; // PWM值变量
int fadeAmount = 100; // PWM累增(减)量
// 定义ADC相关变量
int adcValue = 0; // 存放读取的ADC数值0~4095 -- 0V~29.4V
int limitLowADC = 1390; // 低电压报警阈值变量 1390 -- 10V
boolean checkState = false;
// 定义蜂鸣器相关变量
int buzState = LOW; // 蜂鸣器标识位
unsigned long previousMillis1 = 0; // 记录蜂鸣器 上次时间
const long intervalBUZ = 50; // 蜂鸣器鸣叫 间隔时间 50ms
// 定义OLED屏幕相关变量
boolean oledState = false; // 标识位 Flag
unsigned long previousMillis2 = 0; // 记录OLED 上次时间 刷新时间
const long intervalOLED = 200; // 屏幕刷新 间隔时间 200ms
// I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚
// PWM 输出控制函数
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
uint32_t duty = (8191 / valueMax) * min(value, valueMax); // 8191 = 2 ^ 13 - 1
ledcWrite(channel, duty);
}
// 外部中断服务函数 -- 刷新屏幕和控制电机方向
void IRAM_ATTR detectsMovement() {
Serial.println("BUTTON DOWN !!!");
digitalWrite(DIR_PIN,LOW); // 电机 GPIO26 方向控制输出 0
oledState = true; // OLED 标识位
previousMillis2 = millis();
}
void setup() {
Serial.begin(115200);
pinMode(BUZ_PIN, OUTPUT); //GPIO27 蜂鸣器 设置为输出模式
pinMode(DIR_PIN, OUTPUT); //GPIO26 电机方向 设置为输出模式
// 注意:GPIO一定要设置为内部下拉输入模式,否则读取到的数值是0和1来回跳动,导致加减速度时 PWM停滞在某一两个数值之间来回跳动
pinMode(BUT_F_PIN, INPUT_PULLDOWN); //GPIO18 五向按键按-前 设置为下拉输入模式
pinMode(BUT_B_PIN, INPUT_PULLDOWN); //GPIO19 五向按键按-后 设置为下拉输入模式
pinMode(BUT_D_PIN, INPUT_PULLDOWN); //GPIO19 五向按键按-下 设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断
// 中断引脚、中断服务函数、中断触发方式为FALLING
attachInterrupt(digitalPinToInterrupt(BUT_D_PIN), detectsMovement, FALLING);
//设置定时器并将定时器连接PWM引脚
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
//
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 0);//设置显示坐标
display.println(F("Naiva'S TEST"));//
display.setCursor(0, 20);//设置显示坐标
display.println(F("ESP32 for Arduino IDE"));//
display.display(); // 屏幕上实际显示文本
delay(3000);
}
void loop() {
unsigned long currentMillis = millis(); // 定时计数器=当前时间, millis()的返回值为类型unsigned long
// 定时计数器实现 -- 实时刷新oled 屏幕 外部中断按键控制刷新 和 实时刷新屏幕
if((oledState || currentMillis - previousMillis2 >= intervalOLED) && !(adcValue <= limitLowADC)){
previousMillis2 = currentMillis;
// Serial.println("按键:刷新屏幕显示PWM and ADC ");
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 15);//设置显示坐标
display.print(F("PWM: "));
display.println(pwmValue);
display.setCursor(0, 30);//设置显示坐标
display.print(F("ADC: ")); display.println(adcValue); // 4095 = 2^12bit
display.setCursor(0, 45);//设置显示坐标
display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
display.display(); // 屏幕上实际显示文本
oledState = false; // 用于外部中断标签
}
// ADC
adcValue = analogRead(ADC_PIN);
// Serial.print("adcValue:");Serial.println(adcValue);
// PWM 输出
//GPIO18 BUT_F_PIN 油门
if(digitalRead(BUT_F_PIN)==1){
digitalWrite(DIR_PIN,HIGH); // GPIO26 方向控制 输出1
targetPWM = limitfUpPWM; // 线性加速 targetPWM= 8000 按下五向按键按-前
}else{targetPWM = limitfDownPWM;} // 线性减速 targetPWM= 3000 松开五向按键按-前
Serial.print("digitalRead(BUT_F_PIN):");Serial.println(digitalRead(BUT_F_PIN)); // 串口打印
//GPIO19 BUT_B_PIN 刹车 按下五向按键按-后
if(digitalRead(BUT_B_PIN)==1){
pwmValue = 3000; // 紧急刹车 pwmValue=3000
}
digitalWrite(DIR_PIN,HIGH); // GPIO26 方向控制 输出1
// pwmValue = pwmValue + fadeAmount; // 改变亮度
// if (pwmValue <= limitfDownPWM || pwmValue >= limitfUpPWM) // 加/减PWM范围5000~ 8000
// {fadeAmount = -fadeAmount;}
if (pwmValue < targetPWM ) // 加/减速度,PWM范围是3000~ 8000
{pwmValue = pwmValue + fadeAmount;}else {pwmValue = pwmValue - fadeAmount;}
Serial.print("targetPWM:");Serial.println(targetPWM); // 串口打印
// ledcAnalogWrite(LEDC_CHANNEL_0, pwmValue); //在LEDC通道0上设置PWM亮度
ledcWrite(LEDC_CHANNEL_0,pwmValue); // 设置PWM
Serial.print("pwmValue:");Serial.println(pwmValue); // 串口打印PWM值
// 定时计数器实现 --低电压报警
static int count = 0; // 静态变量 count, 累计记录ADC低电压状态 防止误判关闭总开关时的低电压触发蜂鸣器报警
if(adcValue <= limitLowADC){
count ++;
if(count >= 20 && checkState == false){checkState = true;}
Serial.print("count:");Serial.println(count); // 串口打印
if(adcValue <= limitLowADC && currentMillis - previousMillis1 >= intervalBUZ && checkState == true) {
previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
// Serial.println("屏幕:低电压报警!!!");
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 15);//设置显示坐标
display.println(F("LOW WARNING!!!"));
display.setCursor(0, 30);//设置显示坐标
display.print(F("ADC: ")); display.println(adcValue); // 4095 = 2^12bit
display.setCursor(0, 45);//设置显示坐标
display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 4095 = 2^12bit ADC 显示剩余电量百分比%
display.display(); // 屏幕上实际显示文本
if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
digitalWrite(BUZ_PIN, buzState);
checkState = false;
}
}// 关闭蜂鸣器器
else {digitalWrite(BUZ_PIN, LOW);count = 0;}
delay(100);
}
有线—摇杆开关版本
问题: 摇杆开关版本,去除了中断换向控制,不知道为什么,用中断按下摇杆中间按钮的时候,esp32 老重启
。
// V.05.07
#include <Wire.h> // 使用I2C库 ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h> //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64
// 初始化PWM通道设置
#define LEDC_CHANNEL_0 0 // PWM通道 0
#define LEDC_TIMER_8_BIT 8 // 占空比分辨率 8位 2^8 = 256
#define LEDC_BASE_FREQ 5000 // PWM 信号频率 5000 Hz
// 定义GPIO引脚
const int ADC_PIN = 34; // ADC GPIO 34 (Analog ADC1_CH6) 电源
const int PWM_PIN = 25; // PWM GPIO 25
const int DIR_PIN = 26; // Direction GPIO 26
const int BUZ_PIN = 27; // Buzzer GPIO 27
const int BUT_D_PIN = 18; // Button down GPIO 18 (按键-下)
const int ADC_RS_Y_PIN = 35; // Rocker switch forward&back GPIO 35 (ADC1_CH7) (摇杆-Y-前后)
const int ADC_RS_X_PIN = 15; // Rocker switch L&R GPIO 15 (ADC2_CH3) (摇杆-X-左右)
// 定义电源ADC相关变量 GPIO34
int adcValue = 0; // 存放读取的ADC数值0~4095 -- 0V~29.4V
int limitLowADC = 1390; // 低电压报警阈值变量 1390 -- 10V
boolean checkState = false;
// 定义摇杆Y轴ADC相关变量 GPIO35
int rsyadcValue = 0; // 存放读取的ADC数值0~4095
// 定义PWM相关变量
int dutyCycle = 0; // PWM 占空比
// 定义DIR方向相关变量
int dirState = HIGH;
boolean buttonState = false;
// 定义蜂鸣器相关变量
int buzState = LOW; // 蜂鸣器标识位
unsigned long previousMillis1 = 0; // 记录蜂鸣器 上次时间
const long intervalBUZ = 50; // 蜂鸣器鸣叫 间隔时间 50ms
// 定义OLED屏幕相关变量
boolean oledState = false; // 标识位 Flag
unsigned long previousMillis2 = 0; // 记录OLED 上次时间 刷新时间
const long intervalOLED = 200; // 屏幕刷新 间隔时间 200ms
// I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚
void setup() {
// Serial.begin(115200);
pinMode(BUZ_PIN, OUTPUT); //GPIO27 蜂鸣器 设置为输出模式
pinMode(DIR_PIN, OUTPUT); //GPIO26 电机方向 设置为输出模式
pinMode(BUT_D_PIN, INPUT_PULLDOWN); //GPIO18 按键按-下 设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断
digitalWrite(DIR_PIN,dirState); //GPIO26 电机默认方向 -- HIGH
// 设置定时器并将定时器连接PWM GPIO25号引脚
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_8_BIT);
ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
ledcWrite(LEDC_CHANNEL_0, dutyCycle); // 初始化PWM输出值设为0 禁止电机转动
// OLED 屏幕初始化
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 0);//设置显示坐标
display.println(F("Naiva'S TEST"));//
display.setCursor(0, 20);//设置显示坐标
display.println(F("ESP32 for Arduino IDE"));//
display.display(); // 屏幕上实际显示文本
delay(3000);
}
void loop()
{
unsigned long currentMillis = millis(); // 定时计数器=当前时间, millis()的返回值为类型unsigned long
// ADC 电源
adcValue = analogRead(ADC_PIN);
//Serial.print("adcValue:");Serial.println(adcValue);
// ADC 摇杆-Y-前后
rsyadcValue = analogRead(ADC_RS_Y_PIN);
//Serial.print("rsyadcValue:");Serial.println(rsyadcValue);
// PWM 控制电机速度
dutyCycle = (map(analogRead(ADC_RS_Y_PIN),2047,4095,0,255));
//Serial.print("dutyCycle = ");Serial.println(dutyCycle);
if(dutyCycle <= 0 || dutyCycle > 255){dutyCycle = 0;}
ledcWrite(LEDC_CHANNEL_0,dutyCycle);
// Dir 控制电机方向
if(digitalRead(BUT_D_PIN) == HIGH && buttonState == false)
{
dirState = !digitalRead(DIR_PIN); //GPIO26电机方向控制
digitalWrite(DIR_PIN,dirState); // GPIO26 方向控制 输出HIGH/LOW
buttonState = true;
}
else if (digitalRead(BUT_D_PIN) == LOW && buttonState == true)
{
buttonState = false;
}
// Serial.print("BUT_D_PIN18 = ");Serial.println(digitalRead(BUT_D_PIN));
// Serial.print("DIR_PIN26 = ");Serial.println(digitalRead(DIR_PIN));
// Serial.print("DIR_PINdirState = ");Serial.println(dirState);
// 屏幕显示
if(currentMillis - previousMillis2 >= intervalOLED && !(adcValue <= limitLowADC))
{
previousMillis2 = currentMillis;
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 5);//设置显示坐标
display.print(F("PWM: "));
display.println(dutyCycle);
display.setCursor(0, 20);//设置显示坐标
display.print(F("ADC: ")); display.print(adcValue); display.print(" ");display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
display.setCursor(0, 35);//设置显示坐标
display.print(F("rsyadcValue: ")); display.println(rsyadcValue);
// display.print(F("rsyadcValue: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
display.display(); // 屏幕上实际显示文本
}
// 定时计数器 -- 低电压报警
static int count = 0; // 静态变量 count, 累计记录ADC低电压状态 防止误判关闭总开关时的低电压触发蜂鸣器报警
if(adcValue <= limitLowADC){
count ++;
if(count >= 20 && checkState == false){checkState = true;}
// Serial.print("count:");Serial.println(count); // 串口打印
if(adcValue <= limitLowADC && currentMillis - previousMillis1 >= intervalBUZ && checkState == true) {
previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
display.clearDisplay();// 清除显示
display.setTextSize(1);// 设置文本大小
display.setTextColor(WHITE);// 设置文本颜色
display.setCursor(0, 5);//设置显示坐标
display.print(F("PWM: "));
display.println(dutyCycle);
display.setCursor(0, 20);//设置显示坐标
display.print(F("ADC: ")); display.print(adcValue); display.print(" ");display.print(map(adcValue,0,4095,0,100)); display.println(F("% warning"));// 显示剩余电量百分比%
display.setCursor(0, 35);//设置显示坐标
display.print(F("rsyadcValue: ")); display.println(rsyadcValue);
display.display(); // 屏幕上实际显示文本
if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
digitalWrite(BUZ_PIN, buzState);
checkState = false;
}
}// 关闭蜂鸣器器
else {digitalWrite(BUZ_PIN, LOW);count = 0;}
delay(100);
}
总结:
至此,2022年8月第一版结束。
感觉自己无论是画PCB电路板时元器件选型,PCB 布局还是单片机C语言程序设计时数据类型的使用(byte,u8节省内存;结构体,指针也没有用)都不很专业。有待继续学习!
第一版资料总汇
分割线1