版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/MemsanGZmInG/article/details/117320456
作者:MemsanGZmInG
日期:2021-05-27
如何在Silicon Labs的EFR32BG22程序中添加AT指令控制
关于EFR32BG22
EFR32BG22 (BG22) 蓝牙低功耗 (LE) 无线 SoC 解决方案是无线 Gecko 系列 2 平台的一部分。BG22 系列同类最优的超低传输和接收功率(4.1 mA TX @ 0 dBm、3.6 mA RX)和高性能、低功耗 Arm® Cortex®-M33 内核(27 µA/MHz 活动、1.2 µA 睡眠)的组合提供业界领先的能源效率,可使钮扣电池寿命延长到多达十年。目标应用包括蓝牙网状网络低功耗节点、智能门锁、个人医疗保健和健身设备。资产跟踪标签、信标和室内导航也将受益于 SoC 的多用途蓝牙到达角 (AOA) 和出发角 (AOD) 功能以及亚米级定位精度。
开发环境
软件:Simplicity Studio 4
芯片:EFR32BG22C224F512IM32(同类BG22系列芯片亦可)
关于Simplicity Studio
Silicon Labs拥有自己的开发工具Simplicity Studio,Simplicity Studio 旨在简化开发人员工作流程,因为它能够智能地识别 Silicon Labs 发布的所有评估和开发套件,从而让用户轻松获得适当的 SDK 和开发资源。 Simplicity Studio 还提供功能丰富的基于 Eclipse 和 GNU 工具链的 IDE,开发人员还可以结合其自身的开发环境使用 Simplicity Studio 的其他功能。
编写
EFR32BG22易于开发的其中一个原因就是拥有非常丰富的API接口,开发者即使没有任何蓝牙堆栈开发的经验也能轻易开发。同时,在Silicon Labs的官网上也有许多例程供我们学习。
以官网的v2.13开发文档中的例程为例(链接在这里,很实用的一个主从透传例程)
进入AT模式前的准备
在透传过程中直接使用AT指令进行控制是不明智的,一是在有数据传输时二者都打印出来容易干扰数据,看起来也很乱,二是在传输数据时修改参数可能会对系统内部堆栈产生不可预知的影响,因此,在使用AT指令控制时,应将其透传功能屏蔽。
1.模块在未连接状态时,进入AT配置模式后应停止扫描(server)/ 广播(client),以免在连接到其他设备时串口打印其他东西干扰参数配置;
2.模块在已连接状态时,屏蔽其透传功能,原因上面已经说了。
进入AT配置模式
uint8_t at_mode_flag = 0;
at_mode_flag = 1;
printLog("\r\n*******************\r\n");
printLog("* AT mode *\r\n");
printLog("*******************\r\n");
gecko_cmd_hardware_set_soft_timer(0, GPIO_POLL_TIMER, 0);//禁用按键轮询扫描
//在spp_server_main.c文件中
if (_main_state == STATE_ADVERTISING)
{
gecko_cmd_le_gap_start_advertising(0, le_gap_non_discoverable, le_gap_non_connectable);//广播不可连接的广播包
}
//在spp_client_main.c文件中
if (_main_state == SCANNING)
{
gecko_cmd_le_gap_end_procedure(); //stop discovery
_main_state = DISCONNECTED;
}
读取串口数据
static void send_spp_data()
{
uint8 len = 0;
uint8 data[256];
uint16 result;
int c;
int timeout = 0;
// Read up to _max_packet_size characters from local buffer
while (len < _max_packet_size) {
c = RETARGET_ReadChar();
if (c >= 0) {
data[len++] = (uint8) c;
} else if (len == 0) {
/* If the first ReadChar() fails then return immediately */
return;
} else {
/* Speed optimization: if there are some bytes to be sent but the length is still
* below the preferred minimum packet size, then wait for additional bytes
* until timeout. Target is to put as many bytes as possible into each air packet */
// Conditions for exiting the while loop and proceed to send data:
if (timeout++ > UART_POLL_TIMEOUT) {
break;
} else if (len >= _min_packet_size) {
break;
}
}
}
if (at_mode_flag == 1)
{
at_commands(_conn_handle, data, len);
}
else if (len > 0) {
// Stack may return "out-of-memory" error if the local buffer is full -> in that case, just keep trying until the command succeeds
do {
result = gecko_cmd_gatt_write_characteristic_value_without_response(_conn_handle, _char_handle, len, data)->result;
_sCounters.num_writes++;
} while (result == bg_err_out_of_memory);
if (result != 0) {
printLog("Unexpected error: %x\r\n", result);
}
else
{
_sCounters.num_pack_sent++;
_sCounters.num_bytes_sent += len;
}
}
len = 0;
memset(data, 0, len);
}
现在,透传功能被屏蔽,我们可以在串口写入AT指令来进行控制了。
简单的几个AT指令
void at_commands(uint8_t connection, char* data, uint8_t len)
{
if (len == 0)
{
return;
}
if (len >= 4 && data[0] == 'A' && data[1] == 'T' && data[len-2] == '\r' && data[len-1] == '\n')
{
//输入测试指令“AT”
if (len == 4)
{
printLog("OK\r\n");
}
//读取模块mac地址,这个函数在生成的例程中有提供
else if (strncmp("AT+LADDR\r\n", data, len) == 0)
{
printLog("+LADDR=");
AT_Search_LADDR();
printLog("\r\n");
}
//修改模块名称,这里被限制名称最多不能超过8个字节,实际是可以不止8个的。可以根据实际情况修改
else if (len > 10 && len < 19 && data[2] == '+' && data[3] == 'N' && data[4] == 'A' && data[5] == 'M' && data[6] == 'E')
{
uint8_t dataname[8] = {0};
printLog("+NAME=");
int len = strlen(data);
int j = 0;
for (int i = 7; i < len-2; i++,j++)
{
dataname[j] = (uint8_t)data[i];
printLog("%c", dataname[j]);
}
dataname[j] = '\0';
gecko_cmd_gatt_server_write_attribute_value(gattdb_device_name, 0, strlen(dataname), dataname);
printLog("\r\nOK\r\n");
}
//断开蓝牙连接
else if (strncmp("AT+DISC\r\n", data, len) == 0)
{
if (connection != 0XFF)
{
gecko_cmd_le_connection_close(connection);
printLog("OK\r\n");
}
}
//修改发射功率
else if (len == 11 && data[2] == '+' && data[3] == 'P' && data[4] == 'O' && data[5] == 'W' && data[6] == 'E' && data[7] == 'R' )
{
printLog("+POWER=");
//修改发射功率时必须暂停系统堆栈进程
gecko_cmd_system_halt(1);
if (data[8] >= '0' && data[8] <= '6')
{
printLog("%d\r\n", gecko_cmd_system_set_tx_power(data[8]*10)->set_power/10);
printLog("\r\nOK\r\n");
}
gecko_cmd_system_halt(0);
}
//退出AT配置模式
else if (strncmp("AT+EXIT\r\n", data, len) == 0)
{
printLog("OK\r\n");
AT_EXIT();
}
}
}
最好不要使用strcmp,否则容易出现AT指令无响应的问题
退出AT配置模式时,上述进入AT配置模式时的操作都要尽数恢复。
void AT_EXIT(void)
{
if (_main_state == DISCONNECTED)
{
gecko_cmd_le_gap_start_discovery(1, le_gap_discover_generic);
}
else if (_main_state == STATE_ADVERTISING)
{
gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_undirected_connectable);
}
at_mode_flag = 0;
printLog("\r\n*******************\r\n");
printLog("* Exit AT mode *\r\n");
printLog("*******************\r\n");
}
进入AT配置模式后可以做很多东西,包括手动扫描、连接设备;读取、修改掉电参数。甚至可以通过修改掉电参数在下次启动后使用新的UUID值或者添加新的服务,让蓝牙连接的条件变的更加灵活。