MCU向CH9141发送AT指令
沁恒的BLE模块CH914x系列中,CH9141和CH9343支持通过UART串口识别AT指令。注意CH9140不支持AT指令。使用电脑作为上位机时,各类串口助手可以提供保存和发送自定义字符串的功能,不过很多应用场景下都是通过板子上另一片MCU来为BLE透传模块配置AT指令。以下提供帮助MCU快速使用9141和9143。
本文以CH9141手册中的AT命令为准,整理一份二维数组,可按需增补删改。
const uint8_t ATCMD_STD[4][4] = { //标准指令前后缀 "AT+", "?\r\n", "=", "\r\n", }; const uint8_t ATCMD_9141[52][10] = { //51条指令 "", "AT...", //序号1 //进AT配置模式 "RESET", "VER", //获取芯片版本号 "HELLO", "RELOAD", //序号5 "SHOW", "SAVE", "EXIT", //退出AT配置模式 "GPIO", "INITIO", //序号10 "UART", "MAC", "TPL", "BLESTA", "DISCONN", //序号15 "BLEMODE", "CCADD", "NAME", "PNAME", "PASEN", //序号20 "PASS", "SYSID", "MODNAME", "SERINUM", "FIRMREV", //序号25 "HARDREV", "SOFTREV", "MANUNAME", "PNPID", "ADVEN", //序号30 "ADVDAT", "LINK", "CONN", "SCAN", "CONADD", //序号35 "CLRCONADD", "RSSI", "ADC", "SLEEP", "BAT", //序号40 "BDSP", "BLECFGEN", "BCCH", "ADVINTER", "CONNINTER", //序号45 "LSICALI", "RFCALI", "TNOW", "BSTA", "AFEC", //序号50 "IOEN", };
当然也可以把“AT+”放在ATCMD_9141[]数组中,不过数组的大小就要配置为52x13,多占用一百多个字节。
在发出指令前,可以利用sprintf库函数,将多个字符串按顺序拼接到一起。调用strcat或其他函数做拼接均可,不过考虑到sprintf可以将十进制数据添加到字符串中,还是使用sprintf好了。
将三串字符串拼接到TxBuff中,再通过打印函数或者串口输出,可以得到期望的字符串,最后清空一下TxBuff以便下次使用:
sprintf(TxBuff, "%s%s%s", ATCMD_STD[0], ATCMD_9141[8], ATCMD_STD[3]); PRINT("%s", TxBuff); memset(TxBuff, 0, sizeof(TxBuff));
为了方便使用,可以再封装一层宏定义,调用起来更方便。
//PRINT()即printf() #define ENTER_AT_CFG \ { \ sprintf(TxBuff, "%s%s", ATCMD_9141[1], ATCMD_STD[3]); \ PRINT("%s", TxBuff); \ memset(TxBuff, 0, sizeof(TxBuff)); \ } //进入AT配置模式 #define EXIT_AT_CFG \ { \ sprintf(TxBuff, "%s%s%s", ATCMD_STD[0], ATCMD_9141[8], ATCMD_STD[3]); \ PRINT("%s", TxBuff); \ memset(TxBuff, 0, sizeof(TxBuff)); \ } //退出AT配置模式 #define COMMAND_AT(MACRO_X) \ { \ sprintf(TxBuff, "%s%s%s", ATCMD_STD[0], (MACRO_X), ATCMD_STD[3]); \ PRINT("%s", TxBuff); \ memset(TxBuff, 0, sizeof(TxBuff)); \ } //不带问号(即AT+X?格式),不带配置参数(即AT+X=n格式)的指令 #define QUERY_AT(MACRO_X) \ { \ sprintf(TxBuff, "%s%s%s", ATCMD_STD[0], (MACRO_X), ATCMD_STD[1]); \ PRINT("%s", TxBuff); \ memset(TxBuff, 0, sizeof(TxBuff)); \ } //带问号的查询指令 #define SET_STR_AT(MACRO_X1, MACRO_X2) \ { \ sprintf(TxBuff, "%s%s%s%s%s", ATCMD_STD[0], (MACRO_X1), ATCMD_STD[2], (MACRO_X2), ATCMD_STD[3]); \ PRINT("%s", TxBuff); \ memset(TxBuff, 0, sizeof(TxBuff)); \ } //需要配置字符串的命令,比如开/关广播、设置厂商名称等指令 #define SET_NUM_AT(MACRO_X1, MACRO_X2) \ { \ sprintf(TxBuff, "%s%s%s%d%s", ATCMD_STD[0], (MACRO_X1), ATCMD_STD[2], (MACRO_X2), ATCMD_STD[3]); \ PRINT("%s", TxBuff); \ memset(TxBuff, 0, sizeof(TxBuff)); \ } //需要配置十进制数字的命令,比如设置发射功率等指令 /*以下为函数中的调用代码*/ ENTER_AT_CFG; DelayMs(500); COMMAND_AT(ATCMD_9141[3]); DelayMs(500); QUERY_AT(ATCMD_9141[4]); DelayMs(500); SET_STR_AT(ATCMD_9141[30], "OFF"); DelayMs(500); SET_NUM_AT(ATCMD_9141[13], 3); DelayMs(500); EXIT_AT_CFG; DelayMs(500);
以上宏只涵盖了部分指令。有个别指令比较长,比如配置波特率、奇偶校验位等参数的一串指令;还有部分指令格式比较特殊,比如配置GPIO的指令,这些需要再自行封装,或者写变参数的函数/宏去封装,调用起来更方便。
代码中没有判断CH9141是否回复了“OK\r\n”,实际使用中需要确认CH9141收到指令后再发下一条指令。