状态机在编程中很常见,而用好状态机却不容易,在网上也看到很多人有很多种用法,优缺点各有吧,主要配合自己的系统编写出适合自己的状态机,下面我主要记录一下状态机在物联网产品嵌入式开发中的应用。
我指的物联网产品是带无线通信模组的,比如WIFI、BLE、NB_IoT、4G模组等,这些模组在使用过程中需求AT指令驱动,此时个人觉得使用状态机的方式编写驱动代码更好更直观,当然此时需要另外一个知识点,那就是“不停止式延时”,解释一下什么是“不停止式延时”:一般我们使用延时系统的延时函数,其原理是程序暂停等待,这样如果延时的时间长,导致其他代码无法运行,因此在大型项目中,此延时方式不可采用,所以才出现了这种不暂停式延时,具体下一篇详细介绍。
言归正传,继续介绍状态机,具体以实例介绍,比如初始化蓝牙模组过程中设置蓝牙名称、读取蓝牙名称、读取MAC地址等,一个命令可以分为至少两个状态,设置蓝牙名称可以分为如下两个状态:SBLENAME_STAT、SBLENAME_STATACK,前一个状态可以发送具体的设置指令,发送完成后马上跳转到下一个等待应答状态SBLENAME_STATACK,此时可以检测串口中是否有数据返回,若有完整的数据,则进行判断是否为蓝牙返回值,若正确,则进入下一个状态,若错误,重复此设置指令。另外若串口中没有数据返回,则等待超时,重复此命令,再判断次数,达到最大执行次数以后,若还没有成功可能是模组出现问题,此时可以跳转到复位状态。好了,原理差不多就这么简单,贴一段代码;
case BLE_SUBSTAT_SNAME:
BLE_SnameStat();
break;
case BLE_SUBSTAT_SNAMEACK:
BLE_SnameStatACK();
break;
case BLE_SUBSTAT_QMAC:
BLE_QmacStat();
break;
case BLE_SUBSTAT_QMACACK:
BLE_QmacStatACK();
以上代码以设置名称以及读取MAC地址,下面看具体函数:
static void BLE_SnameStat(void)
{
#ifndef HOST
char str[]="TTM:REN-GasSensor";
#else
char str[]="TTM:REN-HostMachine";
#endif
my_log("send-> %s\r\n",str);
Uart_Drv_data.Uart2_sendFun((uint8_t *)&str,strlen(str));
// BLE_Drv_data.Time = Gettick();
BLE_Drv_data.Time = myDelay.gettick();
BLE_Drv_data.Delay = SNAME_TIMEOUT;
BLE_Drv_data.substat = BLE_SUBSTAT_SNAMEACK;
}
static void BLE_SnameStatACK(void)
{
uint8_t packet_tmp[0xFF];
uint8_t ret_tmp[20] = {0}; //蓝牙名称长度,可以调整,这里为GasSensor
if (Uart_Drv_data.WriteRingBuff2Len > 0)
{
//检测有数据的时候进行读数据
my_rb_readbytes(&Uart_Drv_data.Ring2, packet_tmp, Uart_Drv_data.WriteRingBuff2Len, FORCE);
//长度清零,以便下次接收
Uart_Drv_data.WriteRingBuff2Len = 0;
sscanf((char *)packet_tmp,"TTM:%[^\r\n]",ret_tmp);
my_log("Set Name is : %s\r\n",ret_tmp);
if (Strcmp((uint8_t *) ret_tmp, (uint8_t *) "OK", strlen("OK")))
{
//读取成功,进入下一个状态前将CNT清零
BLE_Drv_data.Cnt = 0;
BLE_Drv_data.substat = BLE_SUBSTAT_QNAME;
}
}
else if (myDelay.waitTime(BLE_Drv_data.Time, BLE_Drv_data.Delay))
{
//等待超时,次数增加,若大于限制次数,进行硬件复位
if (BLE_Drv_data.Cnt ++ > DEFAULT_CNT)
{
BLE_Drv_data.Cnt = 0;
BLE_Drv_data.substat = BLE_SUBSTAT_SWREST;
my_log("BLE QNAME ERR,Will Reset BLE Modele!\r\n");
}
else
{
//若没有大于限制次数,重新软件复位
BLE_Drv_data.substat = BLE_SUBSTAT_SNAME;
my_log("BLE Reset TimeOut! Current Cnt : %d \r\n",BLE_Drv_data.Cnt);
}
}
}
static void BLE_QmacStat(void)
{
char str[]="TTM:MAC-?";
my_log("send-> %s\r\n",str);
Uart_Drv_data.Uart2_sendFun((uint8_t *)&str,strlen(str));
BLE_Drv_data.Time = myDelay.gettick();
BLE_Drv_data.Delay = DEFAULT_TIMEOUT;
BLE_Drv_data.substat = BLE_SUBSTAT_QMACACK;
}
static void BLE_QmacStatACK(void)
{
uint8_t packet_tmp[0xFF];
uint8_t mac_tmp[MAC_STR_LEN];
uint8_t mac_hex[MAC_STR_LEN];
if (Uart_Drv_data.WriteRingBuff2Len > 0)
{
//检测有数据的时候进行读数据
my_rb_readbytes(&Uart_Drv_data.Ring2, packet_tmp, Uart_Drv_data.WriteRingBuff2Len, FORCE);
//长度清零,以便下次接收
Uart_Drv_data.WriteRingBuff2Len = 0;
sscanf((char *)packet_tmp,"TTM:MAC-0x%[^\r\n]",mac_tmp);
str_hex(mac_tmp,mac_hex);
mac2str(mac_hex,(char *)BLE_Drv_data.mac_str,MAC_STR_LEN);
my_log("Read BLE MAC is OK! The MAC is : %s\r\n",BLE_Drv_data.mac_str);
//读取成功,进入下一个状态前将CNT清零
BLE_Drv_data.Cnt = 0;
//如果是从设备,则直接准备接收数据
#ifndef HOST
BLE_Drv_data.stat = BLE_RECV_STAT;
//如果是主机需要扫描当前广播的从设备
#else
BLE_Drv_data.substat = BLE_SUBSTAT_QMODE;
#endif
}
else if (myDelay.waitTime(BLE_Drv_data.Time, BLE_Drv_data.Delay))
{
//等待超时,次数增加,若大于限制次数,进行硬件复位
if (BLE_Drv_data.Cnt ++ > DEFAULT_CNT)
{
BLE_Drv_data.Cnt = 0;
BLE_Drv_data.substat = BLE_SUBSTAT_SWREST;
my_log("BLE QNAME ERR,Will Reset BLE Modele!\r\n");
}
else
{
//若没有大于限制次数,重新软件复位
BLE_Drv_data.substat = BLE_SUBSTAT_QMAC;
my_log("BLE Reset TimeOut! Current Cnt : %d \r\n",BLE_Drv_data.Cnt);
}
}
}
好了,具体介绍就到这里,欢迎大家吐槽,一起进步;
源码下载地址:https://download.csdn.net/download/weixin_41003780/12517892