195 lines
6.0 KiB
C
195 lines
6.0 KiB
C
/* ble_at.c - HLK-B40 蓝牙透传模块 AT 配置
|
||
*
|
||
* 接线:模块 PC5(功能按键输入) 接单片机 PB0。
|
||
* 进 AT 模式:PB0 拉低 1s 后释放(手册要求 0.5~3s);
|
||
* 回透传模式:AT 模式下发送 AT+TS=1。
|
||
* 上电默认透传模式。
|
||
*/
|
||
|
||
#include "ble_at.h"
|
||
#include "uart.h"
|
||
#include "log.h"
|
||
#include "main.h"
|
||
#include "system.h"
|
||
#include "24c02.h"
|
||
#include "stdio.h"
|
||
#include "string.h"
|
||
|
||
#define BLE_PC5_PORT GPIOB
|
||
#define BLE_PC5_PIN GPIO_PIN_0
|
||
#define BLE_PC5_LOW() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_RESET)
|
||
#define BLE_PC5_HIGH() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_SET)
|
||
|
||
/* EEPROM 缓存区:已配置好的蓝牙名(132~157,26B,避开其它占用) */
|
||
#define BLE_CACHE_ADDR 132
|
||
#define BLE_POWER_ADDR 158 /* EEPROM:已配置的发射功率(132~157 名称缓存, 160 日志等级) */
|
||
#define BLE_CACHE_LEN 26
|
||
|
||
/* PB0 开漏输出:释放时由模块 PC5 内部上拉保持高电平 */
|
||
void ble_at_init(void)
|
||
{
|
||
GPIO_InitTypeDef g = {0};
|
||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||
BLE_PC5_HIGH();
|
||
g.Pin = BLE_PC5_PIN;
|
||
g.Mode = GPIO_MODE_OUTPUT_OD;
|
||
g.Pull = GPIO_NOPULL;
|
||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
||
HAL_GPIO_Init(BLE_PC5_PORT, &g);
|
||
BLE_PC5_HIGH();
|
||
}
|
||
|
||
static void ble_delay(uint32_t ms)
|
||
{
|
||
while (ms >= 100) {
|
||
HAL_Delay(100);
|
||
IWDG_Feed();
|
||
ms -= 100;
|
||
}
|
||
if (ms) HAL_Delay(ms);
|
||
}
|
||
|
||
/* 清空 USART1 待处理行 */
|
||
static void ble_flush_rx(void)
|
||
{
|
||
while (USART1_GetCommand() != NULL) { }
|
||
}
|
||
|
||
/* 发送 AT 命令并等待期望应答(按行匹配子串),0=成功 */
|
||
static int ble_at_cmd(const char *cmd, const char *expect, int timeout_ms)
|
||
{
|
||
char line[64];
|
||
int n = snprintf(line, sizeof(line), "%s\r\n", cmd);
|
||
ble_flush_rx();
|
||
HAL_UART_Transmit(&huart1, (uint8_t *)line, n, 500);
|
||
|
||
int t = timeout_ms / 50;
|
||
while (t-- > 0) {
|
||
ble_delay(50);
|
||
const char *resp = USART1_GetCommand();
|
||
if (resp) {
|
||
if (strstr(resp, expect)) {
|
||
log_info("> BLE AT: %s", resp);
|
||
return 0;
|
||
}
|
||
if (strstr(resp, "ERROR")) {
|
||
log_warn("> BLE AT 应答错误: %s", resp);
|
||
return -1;
|
||
}
|
||
}
|
||
}
|
||
log_warn("> BLE AT 超时: %s", cmd);
|
||
return -1;
|
||
}
|
||
|
||
/* 上电执行一次:进 AT 模式 → 蓝牙名称查询/设置为 BLE_NAME → 需要时重启模块生效。
|
||
* 先查询后设置:名字已正确则整个流程跳过(不留 AT 窗口,避免 APP 数据撞进 AT 命令) */
|
||
void ble_setup_name(void)
|
||
{
|
||
char cmd[48];
|
||
|
||
/* EEPROM 缓存命中(名称+功率都对)则跳过整个 AT 流程(零窗口,蓝牙 APP 不断联) */
|
||
char cache[BLE_CACHE_LEN] = {0};
|
||
eepromReadData(BLE_CACHE_ADDR, cache, BLE_CACHE_LEN);
|
||
cache[BLE_CACHE_LEN - 1] = 0;
|
||
uint8_t pwr_cache = 0xFF;
|
||
eepromReadData(BLE_POWER_ADDR, &pwr_cache, 1);
|
||
if (strcmp(cache, BLE_NAME) == 0 && pwr_cache == BLE_TX_POWER) {
|
||
log_info("> BLE: 配置已缓存(%s/功率%d),跳过设置", BLE_NAME, BLE_TX_POWER);
|
||
return;
|
||
}
|
||
|
||
/* AT 窗口内静音日志:UART1 与模块共用,日志字节会污染模块的 AT 命令解析 */
|
||
log_mute(1);
|
||
|
||
/* 进 AT 模式并查询当前名称:无应答则重新拉 PC5 再试,最多 3 轮 */
|
||
const char *cur = NULL;
|
||
for (int round = 0; round < 3 && !cur; round++) {
|
||
BLE_PC5_LOW();
|
||
ble_delay(1100); /* 拉低 1s 进 AT 模式 */
|
||
BLE_PC5_HIGH();
|
||
ble_delay(600); /* 等模块切换到 AT 模式 */
|
||
|
||
ble_flush_rx();
|
||
HAL_UART_Transmit(&huart1, (uint8_t *)"AT+NAME=?\r\n", 12, 500);
|
||
int t = 30;
|
||
while (t-- > 0 && !cur) {
|
||
ble_delay(50);
|
||
cur = USART1_GetCommand();
|
||
}
|
||
}
|
||
|
||
if (!cur) {
|
||
log_mute(0);
|
||
log_warn("> BLE: 3 轮 AT 均无应答(模块未接/被手机占用/PC5 未拉低)");
|
||
return;
|
||
}
|
||
|
||
if (strstr(cur, BLE_NAME) && pwr_cache == BLE_TX_POWER) {
|
||
/* 名称与功率都正确:直接回透传,不动任何设置 */
|
||
ble_at_cmd("AT+TS=1", "OK", 1500);
|
||
log_mute(0);
|
||
log_info("> BLE: 名称已是 %s,无需设置", BLE_NAME);
|
||
return;
|
||
}
|
||
if (strstr(cur, BLE_NAME)) {
|
||
/* 名称对但功率变了:只重设发射功率 */
|
||
snprintf(cmd, sizeof(cmd), "AT+RFPOWER=%d", BLE_TX_POWER);
|
||
ble_at_cmd(cmd, "OK", 1500);
|
||
ble_at_cmd("AT+REBOOT=1", "OK", 1500);
|
||
uint8_t p = BLE_TX_POWER;
|
||
eepromWriteData(BLE_POWER_ADDR, &p, 1);
|
||
log_mute(0);
|
||
log_info("> BLE: 发射功率已设置为 %d", BLE_TX_POWER);
|
||
return;
|
||
}
|
||
|
||
/* 需要设置:发 AT+NAME(最多重试 3 次)。
|
||
* 注意:本模块设置类参数重启后才生效,当场回查查到的仍是旧名,
|
||
* 不能用作校验——改完直接重启,下次开机的查询才是真正的校验 */
|
||
int ok = -1;
|
||
for (int i = 0; i < 3 && ok != 0; i++) {
|
||
snprintf(cmd, sizeof(cmd), "AT+NAME=%s", BLE_NAME);
|
||
ok = ble_at_cmd(cmd, "OK", 1500);
|
||
}
|
||
|
||
if (ok == 0) {
|
||
#if BLE_UUID_CUSTOM
|
||
/* 顺带设置透传 GATT UUID(FFE0/FFE1),通用 BLE 串口 APP 可连 */
|
||
snprintf(cmd, sizeof(cmd), "AT+UUIDS=%s", BLE_UUID_SERVICE);
|
||
ble_at_cmd(cmd, "OK", 1500);
|
||
snprintf(cmd, sizeof(cmd), "AT+UUIDR=%s", BLE_UUID_TX);
|
||
ble_at_cmd(cmd, "OK", 1500);
|
||
snprintf(cmd, sizeof(cmd), "AT+UUIDW=%s", BLE_UUID_RX);
|
||
ble_at_cmd(cmd, "OK", 1500);
|
||
#endif
|
||
/* 顺带设置发射功率 */
|
||
snprintf(cmd, sizeof(cmd), "AT+RFPOWER=%d", BLE_TX_POWER);
|
||
ble_at_cmd(cmd, "OK", 1500);
|
||
|
||
/* 手册说明:设置类参数重启后才生效,重启模块使新名称生效;
|
||
* 模块重启后默认就是透传模式,无需再发 AT+TS=1 */
|
||
for (int i = 0; i < 2; i++) {
|
||
if (ble_at_cmd("AT+REBOOT=1", "OK", 1500) == 0) break;
|
||
}
|
||
eepromWriteData(BLE_CACHE_ADDR, (void *)BLE_NAME, strlen(BLE_NAME) + 1);
|
||
{
|
||
uint8_t p = BLE_TX_POWER;
|
||
eepromWriteData(BLE_POWER_ADDR, &p, 1);
|
||
}
|
||
log_mute(0);
|
||
log_info("> BLE: 名称/UUID/功率已设置完成");
|
||
} else {
|
||
ble_at_cmd("AT+TS=1", "OK", 1500); /* 确保回到透传 */
|
||
log_mute(0);
|
||
log_warn("> BLE: 设置名称失败(模块未接或应答异常)");
|
||
}
|
||
}
|
||
|
||
/* 强制重新执行蓝牙设置(蓝牙 BLESET 命令触发):清缓存后重跑 */
|
||
void ble_force_setup(void)
|
||
{
|
||
eepromWriteData(BLE_CACHE_ADDR, "", 1);
|
||
ble_setup_name();
|
||
}
|