发帖
9 2 1

【电子DIY作品】BW21-CBV-Kit 扩展板设计

无垠的广袤
论坛元老

36

主题

50

回帖

4028

积分

论坛元老

积分
4028
QQ
电子DIY 245 9 2025-6-19 17:26:54
[i=s] 本帖最后由 无垠的广袤 于 2025-6-23 17:27 编辑 [/i]

【电子DIY作品】BW21-CBV-Kit 开发套件扩展板设计

本文介绍了安信可 BW21-CBV-Kit 开发套件 扩展板 及其 外壳 的项目设计。

项目介绍

主控为 安信可 BW21 模组 开发板,得益于该模组的 强劲性能完善的生态建设 ,可实现诸如

  • 人脸识别
  • 手势识别
  • 物体识别
  • ……

等 AI 相关项目,为智能设备和产品的快速开发和部署提供了极大便利。

外观

board_3D.jpg

扩展板工程详见:智能交互与识别机器人 - 立创开源硬件平台 .

核心板工程详见:BW21-CBV-Kit Ai视觉识别开发板 - 立创开源硬件平台 .

参数特点

  • 所有模组 IO 引出,便于外设连接与开发调试;
  • 增加多组 5V 、3.3V 和 GND 扩展引脚,为外设开发提供便利;
  • 增加 TP4057 充电管理电路,支持 3.7V 锂电池供电输入;
  • 充电管理包括充电指示灯与 XH2.54 电源插接母座,模块化设计,便于快速组装测试;
  • 包括 DHT11 单总线传感器接口,信号引脚带上拉电阻,可直插传感器或模块;
  • IIC 接口,可连接 OLED 显示屏;
  • SPI 通信接口,可连接 TFT 彩色显示屏;

功能定义

包括排针扩展接口、电源输出扩展、充电指示灯、状态指示灯、XH2.54 电池供电接口、DHT11传感器接口、IIC OLED接口、SPI/TFT屏接口等。

pinout_def.jpg

原理图

SCH_Shell_update.jpg

3D外壳

  • 四角 M3 螺丝定位孔设计,便于 DIY 安装与拆卸;
  • 扩展排针和电源排针设计,便于外部模块连接;
  • TFT 和 OLED 屏排母接口设计,便于工程快速验证与显示测试;
  • BW21 核心板模组位置下方挖槽,增强散热;
  • 天线位置预留凹槽设计,便于天线延展,使无线通信更加畅通无阻;
  • 侧边精准挖槽,便于Type-C充电接口连接,充电指示灯显示,以及 DHT11 模块接入;

3D_Shell.png

实物展示

expansion_shell_project.jpg

功能测试

包括扩展板和核心板的板载资源测试,如 OLED 单色屏、TFT 彩屏、DHT11 传感器等。

OLED 显示

介绍了 IIC 通信的 SSD1306 驱动 OLED 屏显示。

shell_SCH_oled.jpg

根据原理图,SCL 和 SDA 分别对应 E3 (13)和 E4(12) 引脚。

测试代码

新建 Arduino 工程,添加如下代码

#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>

#define SCREEN_WIDTH  128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64     // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
#define OLED_RESET     -1      // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C    ///< See datasheet for Address
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define NUMFLAKES 10    // Number of snowflakes in the animation example

#define LOGO_HEIGHT_FLAKES 16
#define LOGO_WIDTH_FLAKES  16
static const unsigned char PROGMEM logo_bmp_flake[] =
    {0b00000000, 0b11000000,
     0b00000001, 0b11000000,
     0b00000001, 0b11000000,
     0b00000011, 0b11100000,
     0b11110011, 0b11100000,
     0b11111110, 0b11111000,
     0b01111110, 0b11111111,
     0b00110011, 0b10011111,
     0b00011111, 0b11111100,
     0b00001101, 0b01110000,
     0b00011011, 0b10100000,
     0b00111111, 0b11100000,
     0b00111111, 0b11110000,
     0b01111100, 0b11110000,
     0b01110000, 0b01110000,
     0b00000000, 0b00110000};

void setup()
{
    Serial.begin(115200);

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ;    // Don't proceed, loop forever
    }

    // Clear the buffer
    display.clearDisplay();

    // Show the display buffer on the screen. You MUST call display() after
    // drawing commands to make them visible on screen!
    display.display();
    delay(500);

    testanimate(logo_bmp_flake, LOGO_WIDTH_FLAKES, LOGO_HEIGHT_FLAKES);    // Animate bitmaps
}

void loop()
{
}

#define XPOS   0    // Indexes into the 'icons' array in function below
#define YPOS   1
#define DELTAY 2

void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h)
{
    int8_t f, icons[NUMFLAKES][3];

    // Initialize 'snowflake' positions
    for (f = 0; f < NUMFLAKES; f++) {
        icons[f][XPOS] = random(1 - LOGO_WIDTH_FLAKES, display.width());
        icons[f][YPOS] = -LOGO_HEIGHT_FLAKES;
        icons[f][DELTAY] = random(1, 6);
        Serial.print(F("x: "));
        Serial.print(icons[f][XPOS], DEC);
        Serial.print(F(" y: "));
        Serial.print(icons[f][YPOS], DEC);
        Serial.print(F(" dy: "));
        Serial.println(icons[f][DELTAY], DEC);
    }

    for (;;) {                     // Loop forever...
        display.clearDisplay();    // Clear the display buffer

        // Draw each snowflake:
        for (f = 0; f < NUMFLAKES; f++) {
            display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
        }

        display.display();    // Show the display buffer on the screen
        delay(200);           // Pause for 1/10 second

        // Then update coordinates of each flake...
        for (f = 0; f < NUMFLAKES; f++) {
            icons[f][YPOS] += icons[f][DELTAY];
            // If snowflake is off the bottom of the screen...
            if (icons[f][YPOS] >= display.height()) {
                // Reinitialize to a random position, just off the top
                icons[f][XPOS] = random(1 - LOGO_WIDTH_FLAKES, display.width());
                icons[f][YPOS] = -LOGO_HEIGHT_FLAKES;
                icons[f][DELTAY] = random(1, 6);
            }
        }
    }
}

效果

expansion_board_oled_test.gif

SPI 显示

介绍了 SPI 通信的 SSD1306 驱动 OLED 屏显示图片和文字。

shell_SCH_spi_oled.jpg

根据原理图,SPI 通信 OLED 引脚对应关系如下

SPI OLED BW21
SCL E3 (13)
SDA E4(12)
RST F8(5)
DC F11(4)
CS F12(3)

将目标图片尺寸调整至合适大小,转换为 XBM格式(单色位图)

在线 XBM 转化工具:XBM Converter .

测试代码

新建 Arduino 工程,添加如下代码

这里使用了 U8g2 库,注意引脚定义

#include <U8g2lib.h>
#include <SPI.h>
#include "logo.h"
// 初始化 U8g2(SPI 模式)
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(
  U8G2_R0, 
  /* clock=*/ 13,  // SCK
  /* data=*/ 12,   // MOSI (SDA)
  /* cs=*/ 3,      // Chip Select
  /* dc=*/ 4,      // Data/Command
  /* reset=*/ 5   // RESET
);

void setup() {
  u8g2.begin();
  u8g2.setFont(u8g2_font_helvB10_tr);
}

void loop() {
  u8g2.clearBuffer();
  // 1. 显示文字(上方区域,128x19像素)
  u8g2.setCursor(0, 12);          // 文字起始位置(x,y)
  u8g2.print("Hello, Ai-Thinker"); // 要显示的文字
  // 2. 显示图片(下方区域,128x45像素)
  u8g2.drawXBMP(0, 19, logo_width, logo_height, logo);
  u8g2.sendBuffer();// 刷新显示
  delay(2000);
}

上传代码至开发板,复位运行。

效果

SPI_oled_test.gif

DHT11传感器

dht11_module_1.jpg

详见:DHT11 模块 - 立创开源硬件平台 .

根据原理图可知,模块数据引脚对应 F5(0)引脚。

测试代码

新建 Arduino 工程,添加如下代码

#include "BLEDevice.h"
#include "DHT.h"
/* OLED */
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#include <ArduinoJson.h>

#define SCREEN_WIDTH  128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64     // OLED display height, in pixels
#define OLED_RESET     -1      // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C    ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define UART_SERVICE_UUID      "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

#define STRING_BUF_SIZE 100

// The digital pin we're connected to.
#define DHTPIN 0

// Uncomment whatever type you're using!
#define DHTTYPE DHT11    // DHT 11
// #define DHTTYPE DHT22   // DHT 22 (AM2302), AM2321
// #define DHTTYPE DHT21   // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);

BLEService UartService(UART_SERVICE_UUID);
BLECharacteristic Rx(CHARACTERISTIC_UUID_RX);
BLECharacteristic Tx(CHARACTERISTIC_UUID_TX);
BLEAdvertData advdata;
BLEAdvertData scndata;
bool notify = false;

void writeCB(BLECharacteristic* chr, uint8_t connID)
{
    printf("Characteristic %s write by connection %d :\n", chr->getUUID().str(), connID);
    if (chr->getDataLen() > 0) {
        Serial.print("Received string: ");
        Serial.print(chr->readString());
        Serial.println();
    }
}

void notifCB(BLECharacteristic* chr, uint8_t connID, uint16_t cccd)
{
    if (cccd & GATT_CLIENT_CHAR_CONFIG_NOTIFY) {
        printf("Notifications enabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
        notify = true;
    } else {
        printf("Notifications disabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
        notify = false;
    }
}

void setup()
{
    Serial.begin(115200);

    advdata.addFlags();
    advdata.addCompleteName("AMEBA_BLE_DEV");
    scndata.addCompleteServices(BLEUUID(UART_SERVICE_UUID));

    Rx.setWriteProperty(true);
    Rx.setWritePermissions(GATT_PERM_WRITE);
    Rx.setWriteCallback(writeCB);
    Rx.setBufferLen(STRING_BUF_SIZE);
    Tx.setReadProperty(true);
    Tx.setReadPermissions(GATT_PERM_READ);
    Tx.setNotifyProperty(true);
    Tx.setCCCDCallback(notifCB);
    Tx.setBufferLen(STRING_BUF_SIZE);

    UartService.addCharacteristic(Rx);
    UartService.addCharacteristic(Tx);

    BLE.init();
    BLE.configAdvert()->setAdvData(advdata);
    BLE.configAdvert()->setScanRspData(scndata);
    BLE.configServer(1);
    BLE.addService(UartService);

    BLE.beginPeripheral();
    dht.begin();

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ;    // Don't proceed, loop forever
    }
    delay(1000);
    display.clearDisplay();
    display.setTextColor(WHITE);
}

void loop()
{
    float h = dht.readHumidity();
    float t = dht.readTemperature();

    if (isnan(h) || isnan(t)) {
        Serial.println("Failed to read from DHT sensor!");
        return;
    }

    // Create JSON payload
    DynamicJsonDocument doc(256);
    doc["temperature"] = String(t,2);
    doc["humidity"] = String(h,2);

    char json_string[256];
    serializeJson(doc, json_string);
  
    Serial.print("Publishing: ");
    Serial.println(json_string);

    //String msg = ("Humidity: " + String((int)h) + "%\t" + "Temperature: " + String((int)t) + "°C\n");
    String msg = (json_string);

    Tx.writeString(msg);
    if (BLE.connected(0) && notify) {
        Tx.notify(0);
    }

    // clear display
    display.clearDisplay();
    // display temperature
    display.setTextSize(1);
    display.setCursor(0,0);
    display.print("Temperature: ");
    display.setTextSize(2);
    display.setCursor(0,17);
    display.print(String(t,2));
    display.print(" ");
    display.setTextSize(1);
    display.cp437(true);
    display.write(167);
    display.setTextSize(2);
    display.print("C");

    // display humidity
    display.setTextSize(1);
    display.setCursor(0, 35);
    display.print("Humidity: ");
    display.setTextSize(2);
    display.setCursor(0, 45);
    display.print(String(h,2));
    display.print(" %"); 
  
    display.display(); 
    delay(2000);
}

上传代码至开发板,复位运行。

效果

shell_oled_dht11.jpg

动态演示

shell_oled_dht11.gif

DS18B20传感器

ds18b20_module.jpg

详见:DS18B20模块 - 立创开源硬件平台 .

根据原理图可知,模块数据引脚对应 F5(0)引脚。

测试代码

#include "BLEDevice.h"
#include <DallasTemperature.h>
/* OLED */
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#include <ArduinoJson.h>

#define SCREEN_WIDTH  128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64     // OLED display height, in pixels
#define OLED_RESET     -1      // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C    ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define UART_SERVICE_UUID      "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

#define STRING_BUF_SIZE 100

// DS18B20 configuration
#define ONE_WIRE_BUS 0
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

BLEService UartService(UART_SERVICE_UUID);
BLECharacteristic Rx(CHARACTERISTIC_UUID_RX);
BLECharacteristic Tx(CHARACTERISTIC_UUID_TX);
BLEAdvertData advdata;
BLEAdvertData scndata;
bool notify = false;

void writeCB(BLECharacteristic* chr, uint8_t connID)
{
    printf("Characteristic %s write by connection %d :\n", chr->getUUID().str(), connID);
    if (chr->getDataLen() > 0) {
        Serial.print("Received string: ");
        Serial.print(chr->readString());
        Serial.println();
    }
}

void notifCB(BLECharacteristic* chr, uint8_t connID, uint16_t cccd)
{
    if (cccd & GATT_CLIENT_CHAR_CONFIG_NOTIFY) {
        printf("Notifications enabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
        notify = true;
    } else {
        printf("Notifications disabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
        notify = false;
    }
}

void setup()
{
    Serial.begin(115200);

    advdata.addFlags();
    advdata.addCompleteName("AMEBA_BLE_DEV");
    scndata.addCompleteServices(BLEUUID(UART_SERVICE_UUID));

    Rx.setWriteProperty(true);
    Rx.setWritePermissions(GATT_PERM_WRITE);
    Rx.setWriteCallback(writeCB);
    Rx.setBufferLen(STRING_BUF_SIZE);
    Tx.setReadProperty(true);
    Tx.setReadPermissions(GATT_PERM_READ);
    Tx.setNotifyProperty(true);
    Tx.setCCCDCallback(notifCB);
    Tx.setBufferLen(STRING_BUF_SIZE);

    UartService.addCharacteristic(Rx);
    UartService.addCharacteristic(Tx);

    BLE.init();
    BLE.configAdvert()->setAdvData(advdata);
    BLE.configAdvert()->setScanRspData(scndata);
    BLE.configServer(1);
    BLE.addService(UartService);

    BLE.beginPeripheral();
    sensors.begin(); // 初始化传感器

    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ;    // Don't proceed, loop forever
    }
    delay(1000);
    display.clearDisplay();
    display.setTextColor(WHITE);
}

void loop()
{
    sensors.requestTemperatures(); // 获取温度数据
    float t = sensors.getTempCByIndex(0); // 获取第一个传感器的温度

    if (isnan(t)) {
        Serial.println("Failed to read from DS18B20 sensor!");
        return;
    }

    // Create JSON payload
    DynamicJsonDocument doc(256);
    doc["temperature"] = String(t,2);

    char json_string[256];
    serializeJson(doc, json_string);
  
    Serial.print("Publishing: ");
    Serial.println(json_string);

    String msg = (json_string);

    Tx.writeString(msg);
    if (BLE.connected(0) && notify) {
        Tx.notify(0);
    }

    // clear display
    display.clearDisplay();
    // display temperature
    display.setTextSize(1);
    display.setCursor(0,0);
    display.print("Temperature: ");
    display.setTextSize(2);
    display.setCursor(0,17);
    display.print(String(t,2));
    display.print(" ");
    display.setTextSize(1);
    display.cp437(true);
    display.write(167);
    display.setTextSize(2);
    display.print("C");
  
    display.display(); 
    delay(1000);
}

上传代码至开发板,复位运行。

效果

ds18b20_ble_oled_ha.gif

同时使用 Bluefruit Connect 应用APP连接设备蓝牙,打开串口,即可获取温度信息

ble_ds18b20_app_uart.jpg

同时通过蓝牙 UART 转 MQTT,实现 Home Assistant 数据接收

ds18b20_ha.jpg

具体方案见帖子:【BW21-CBV-Kit 开发套件测评】OLED蓝牙温湿度计与Home Assistant连接 .

锂电池供电

这里使用 200mAh 锂电池进行充放电测试

battery_charge.jpg

注意充电或供电需使用扩展板 Type-C 接口

充电

standby 指示灯熄灭,charge 指示灯常亮,充满则熄灭

charge_LED.gif

放电

charge 指示灯熄灭,standby 指示灯常亮

discharging.gif

无电池

charge 指示灯闪烁,standby 指示灯常亮

no_battery.gif

更新日志

🎨2025年6月18日更新

  • 优化排针布局,增加排针与排母的间隔,使之与开发板更加匹配,且不影响杜邦线连接;
  • 优化 3D 外壳沉头孔高度,使适配效果更佳;
  • 优化 3D 外壳板载天线部分,移动侧边挖槽的位置,使布局更合理;
  • 优化电路设计,增加 MOS 管充电防倒灌电路;

✨2025年6月19日更新

  • 优化充电模块电路设计,底板增加 Type-C 电源充电接口,实现电池的充放电管理;
  • 3D 外壳侧边挖槽位置优化,更新 3D 外壳附件;

🚀2025年6月23日更新

  • 增加功能测试,包括 IIC OLED、SPI OLED显示、DHT11传感器测试、DS18B20测试、锂电池充放电测试等;
  • 优化扩展板功能定义介绍内容、PCB效果显示内容等,提升浏览体验;

总结

本文介绍了安信可 BW21-CBV-Kit 开发套件扩展板及其外壳的项目设计,并根据板载资源进行了相关功能的测试,为后续 DIY 设计与项目开发作铺垫,也为该模组的快速开发和产品应用提供了参考。

──── 2人觉得很赞 ────

使用道具 举报

2025-6-19 17:30:15
大佬这么迅速的嘛 😂,板子画的也很漂亮。
2025-6-19 18:06:15
WT_0213 发表于 2025-6-19 17:30
大佬这么迅速的嘛 😂,板子画的也很漂亮。

嘿嘿,酝酿好几天了,功能还没测试完,后面会接着补充
2025-6-19 18:06:52
厉害
2025-6-19 22:59:48
可以可以,还有外壳
2025-6-20 08:46:57
不错不错~
2025-6-20 09:59:37

大佬这么迅速的嘛
一眼嘉立创
优秀!
厉害
您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 29086 个
  • 话题数: 41888 篇