这次为了测试微软Surface平板电脑的功耗,我设计了一个功率计,主要硬件是 DFRobot 的 ESP32 FireBeetle(DFR0478), DC-DC降压电源模块5.5~28V转3.3V3A(DFR0570),FireBeetle萤火虫OLED12864显示屏(DFR0507)以及INA226电压电流监控模块(CJMCU-226)。其中 FireBeetle 是主控,提供蓝牙连接功能;INA226是德州仪器出品的INA226高侧/低侧测量、 双向电流/功率监视器芯片,测的总线电压范围:0V至 36V,能够报告电流、电压和功率,精度0.1%增益误差(最大值),10μV 偏移(最大值),10引脚 DGS 超薄小外形尺寸(VSSOP) 封装。美中不足的是这款芯片价格太高,因此我选择淘宝直接购买模块。 从硬件角度来说,基本设计如下:电源经过INA226后提供给设备,主控通过 I2C 和 INA226进行通讯;此外为了方便起见我们直接从电源上取电,经过降压模块之后共给INA226、主控和屏幕来使用。
INA226 的基本原理是:通过在被测试电源上串接一个电阻(通常是电阻很小的精密电阻),后再通过读取两端电压再经过 ADC 即可推算出当前电流,配合测量出来的电压即可得知当前输出端的功耗。
最终电路图设计如下,特别注意降压模块旁边有一个名为3.3VPOWER的条线,在使用USBMicro接口连接 FireBeetle 下载和调试时,板子会提供3.3V电压,这种情况下需要断开这个条线;而当独立工作时,需要将此位置短路。
PCB 设计如下:
上方是电源的输入和输出接口。 焊接之后工作的照片,这里使用MicrosoftSurface 平板电脑适配器(磁吸接口)进行测试,这个适配器内部有三根线:VCCGND 和一个Signal。从测试来看,Signal的作用是标志当前磁吸接口是否连接到电脑上,如果连接上Signal会拉低,适配器VCC会按照15V 输出。这次实验中,我将 Signal 接地让适配器始终按照15V输出。
代码如下:
- #include "DFRobot_OLED12864.h"
-
- #include "INA226.h"
-
- #include "BluetoothSerial.h"
-
-
-
- #define DISPLAYTIMEOUT 30000
-
-
-
- INA226 INA(0x40);
-
- // OLED 地址
-
- const uint8_t OLED_I2C_addr = 0x3c;
-
- const uint8_t OLED_pin_SPI_cs = D2;
-
-
-
- // OLED 对象
-
- DFRobot_OLED12864 OLED(OLED_I2C_addr, OLED_pin_SPI_cs);
-
-
-
- // 声明蓝牙串口
-
- BluetoothSerial SerialBT;
-
- // 蓝牙连接标记
-
- boolean Connected = false;
-
-
-
- unsigned long DisplayTimeOut;
-
- boolean displayOff = false;
-
-
-
- // 蓝牙连接事件
-
- void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
-
- if (event == ESP_SPP_SRV_OPEN_EVT) {
-
- // 蓝牙已连接
-
- Serial.println("Client Connected");
-
- // 标记蓝牙已连接
-
- Connected = true;
-
- }
-
-
-
- if (event == ESP_SPP_CLOSE_EVT ) {
-
- // 蓝牙已断开
-
- Serial.println("Client disconnected");
-
- // 标记蓝牙已断开
-
- Connected = false;
-
- }
-
- }
-
-
-
-
-
- void IRAM_ATTR detectButton() {
-
- Serial.println("Button");
-
- // 如果当前屏幕是关闭的状态,触发之后要打开屏幕
-
- if (displayOff) {
-
- displayOff = false;
-
- DisplayTimeOut = millis();
-
- }
-
- }
-
-
-
- void setup()
-
- {
-
- Serial.begin(115200);
-
- // 注册回调函数
-
- SerialBT.register_callback(callback);
-
-
-
- if (!SerialBT.begin("OxiSensor")) {
-
- Serial.println("An error occurred initializing Bluetooth");
-
- } else {
-
- Serial.println("Bluetooth initialized");
-
- }
-
-
-
- Wire.begin();
-
- if (!INA.begin() )
-
- {
-
- Serial.println("could not connect. Fix and Reboot");
-
- }
-
- INA.setMaxCurrentShunt(1, 0.002);
-
-
-
- // 初始化屏幕
-
- OLED.init();
-
- OLED.flipScreenVertically();
-
-
-
- OLED.clear();
-
-
-
- DisplayTimeOut = millis();
-
-
-
- pinMode(D8, INPUT_PULLUP);
-
- attachInterrupt(digitalPinToInterrupt(D8), detectButton, RISING);
-
-
-
- }
-
-
-
- // 要显示内容的缓冲
-
- char Buffer[40];
-
- // 存放浮点转字符串的缓冲
-
- char FloatToStr[20];
-
-
-
- float Voltage; //当前电压,单位是 V
-
- float Ampere; //当前电流,单位是 mA
-
- float Watt; //当前功率,单位是 mW
-
- unsigned long Elsp;
-
-
-
- void fillchar(char *p, int len) {
-
- char *blank=" ";
-
- strncat(p, blank, len - strlen(p));
-
- }
-
- void loop()
-
- {
-
-
-
- Elsp = millis();
-
- Voltage = INA.getBusVoltage();
-
- Ampere = INA.getCurrent_mA();
-
- Watt = INA.getPower_mW();
-
- Serial.print(Elsp);
-
- Serial.print(",");
-
- Serial.print(Voltage, 3);
-
- Serial.print(",");
-
- Serial.print(Ampere, 3);
-
- Serial.print(",");
-
- Serial.print(Watt, 3);
-
- Serial.println();
-
-
-
- // 如果当前OLED 处于关闭状态,就不对屏幕发送任何显示数据
-
- if (displayOff == false) {
-
- // 打开屏幕
-
- OLED.displayOn();
-
-
-
- dtostrf(Voltage, 1, 3, FloatToStr);
-
- sprintf(Buffer, "V:%sV", FloatToStr);
-
- fillchar(&Buffer[0],16);
-
- OLED.disStr(0, 0, Buffer);
-
-
-
- memset(Buffer, ' ', sizeof(Buffer));
-
- dtostrf(Ampere, 1, 3, FloatToStr);
-
- sprintf(Buffer, "A:%smA", FloatToStr);
-
- fillchar(&Buffer[0],16);
-
- OLED.disStr(0, 16, Buffer);
-
-
-
- memset(Buffer, ' ', sizeof(Buffer));
-
- dtostrf(Watt, 1, 3, FloatToStr);
-
- sprintf(Buffer, "P:%smW", FloatToStr);
-
- fillchar(&Buffer[0],16);
-
- OLED.disStr(0, 32, Buffer);
-
-
-
- OLED.display();
-
-
-
- // 如果屏幕亮超过DISPLAYTIMEOUT指定时间,就关闭屏幕
-
- if (millis() - DisplayTimeOut > DISPLAYTIMEOUT) {
-
- Serial.println("Turn off display");
-
- displayOff = true;
-
- OLED.displayOff();
-
- }
-
- }
-
-
-
- if (Connected) {
-
- SerialBT.print(Elsp);
-
-
-
- memset(Buffer, 0, sizeof(Buffer));
-
- dtostrf(Voltage, 1, 3, FloatToStr);
-
- sprintf(Buffer, ",%s,", FloatToStr);
-
- SerialBT.print(Buffer);
-
-
-
- dtostrf(Ampere, 1, 3, FloatToStr);
-
- sprintf(Buffer, "%s,", FloatToStr);
-
- SerialBT.println(Buffer);
-
- }
-
-
-
- delay(1000);
-
- }
复制代码
为了读取功率数据,使用了 INA226 库,我手上的 INA226 地址是0x40;代码中创建了一个 BLE 的UART,用户可以搜索发现设备,配对之后就可以获得数据。除了蓝牙之外,用户还可以通过USB的 UART来获得当前的电压电流信息。为了保护 OLED, 程序还设计了经过DISPLAYTIMEOUT设定的时间后自动关闭屏幕的功能,按下OLED按钮后会再次唤醒屏幕显示。
|