嵌入式开发中我们要时刻保持代码的高效与整洁看之前,先点赞
好习惯,要养成

一、前言

嵌入式开发中我们要时刻保持代码的高效与整洁。在第一节中“NB-IOT开发|nbiot开发教程《一》AT指令类模组驱动解析”我们说到AT指令模组最好的驱动-状态机。本节我们就开始编写状态机。

目前网上可以看到的状态机如下图:

u8 NB_IoT_ack_chack(u8 *str)
{delay_ms(10);if(USART2_RX_STA!=0){USART2_RX_STA=0;if(strstr((const char*)USART2_RX_BUF,(const char*)str))//符合预期{ memset(USART2_RX_BUF,0, sizeof USART2_RX_BUF);    return 0;}else //不符合预期  { memset(USART2_RX_BUF,0, sizeof USART2_RX_BUF);    return 1;}                                } else { memset(USART2_RX_BUF,0, sizeof USART2_RX_BUF);  //清空数组          return  1;}
}u8 NB_IoT_ZDFW()
{u8  x=0;cmd1: send_NB_IoT("AT+NCONFIG=AUTOCONNECT,TRUE\r\n") ;  if(!NB_IoT_ack_chack("OK"))  x++ ;else   goto cmd1;cmd2: send_NB_IoT("AT+CFUN=1\r\n") ;     if(!NB_IoT_ack_chack("OK"))  x++ ;else   goto cmd2;       cmd3: send_NB_IoT("AT+NRB\r\n") ; if(!NB_IoT_ack_chack("REBOOTING"))  x++ ;else   goto cmd3;
}

delay_ms(10); 对,你没有看错就是delay,死等,多么可怕的应用。而且死等10ms就一定能收到数据吗,有些模组中的指令返回时间并不是固定的,可见delay并不是很合适的使用,如果延时1s呢?????,如果此时有按键或者有屏幕刷新,1s的延时能接受吗??,显然是不能的。本次状态机拒绝使用死等!

二、代码实现

举例:

状态:

1.发送AT确认模组是否正常;2.配置模组参数;3.发送数据;(暂定三个状态)

动作:

1.发送AT确认模组是否正常->通过串口发送AT\r\n,模组正常会返回OK,模组不正常返回非OK数据或者不返回。

2.配置模组参数->通过串口发送AT+PARAM=10\r\n,模组正常会返回OK,模组不正常返回非OK数据或者不返回。

3.发送数据->通过串口发送AT+SEND=2030559498473738292929394\r\n,模组正常会返回OK,模组不正常返回非OK数据或者不返回。

事件:

状态机进行状态切换需要事件驱动。

事件1:状态强制切换事件(event_change_state),用于将状态机强制切换到某个指定状态或者下一状态。

事件2:串口接收到完整数据包事件(event_uart_data),模组返回数据。

事件3:超时事件(event_timeout),例如发送AT后模组在1s或者指定时间内没有返回任何数据。

状态:

typedef enum
{STATE_HAL_RESET= 0x01,  /*模组复位*/STATE_AT,               /*发送AT指令测试*/STATE_SET_PARAM,        /*设置参数*/STATE_SEND_DATA,        /*发送数据*/STATE_IDEL,             /*空闲状态*/
} nbiot_state_e;

结合上图1中状态机我们用语言描述下AT指令。例如发送AT模组返回OK,首先这个具有两个状态,一个是当前状态,一个是下一状态;具有的动作就是发送AT,模组返回OK,两个执行动作;在实际使用时可能会存在模组不回复或者回复错误的时候,所以要尝试,尝试要记录重试次数try_cnt;发送完AT后模组要有一个反应时间,就是MCU等待时间,这个时间不确定,每条指令等待时间不一样,计做wait_time;

现在用C语言结构体描述上述文字

typedef struct
{nbiot_state_e cur_state;    /*当前状态*/nbiot_state_e next_state;   /*下一个状态*/int  try_cnt;               /*重试次数*/int  wait_time;             /*等待时间*/int (*action1)(void);       /*动作1:发送AT*/int (*action2)(const void *arg,int len); /*动作2:判断接收到的数据是否为OK或者未接收到*/
} nbiot_fsm_state_t;

状态描述完了,还需要有一个游动指针指向当前正在执行的状态。

typedef struct
{int cur_state;  /*当前状态*/int trycnt;     /*当前状态已经重试的次数*/uint32_t init;  /*当前状态是执行action1还是执行action2*/const nbiot_fsm_state_t *fsm_state; /*当前状态是功能参数*/
} nbiot_fsm_state_index_t;
static nbiot_fsm_state_index_t nbiot_fsm_state_index;

状态中的两个执行动作C语言描述:action1->发送AT

action2:->判断接收到的数据是否为OK,如果是则执行下一状态,如果不是则等待,如果超时未收到数据就重试。

/** AT*/
static int at_action1(void)
{const char *cmd = "AT\r\n";return cola_device_write(uart_dev,0,cmd,strlen(cmd));
}
static int at_action2(const void *arg,int len)
{if(!len)return STATE_RETRY;char *pt = memmem(arg, len, "OK", strlen("OK"));if (pt) {return STATE_NEXT;}return STATE_WAIT;
}

每一个状态都写两个执行动作。

全部的状态列表:

static const nbiot_fsm_state_t nbiot_state_list[] =
{{STATE_HAL_RESET,STATE_AT,       1,  300,nbiot_reset_action1,nbiot_reset_action2},{STATE_AT,       STATE_SET_PARAM,3,  3000,at_action1,         at_action2         },{STATE_SET_PARAM,STATE_SEND_DATA,4,  3000,set_param_action1,  set_param_action2  },{STATE_SEND_DATA,STATE_IDEL,     5,  3000,send_data_action1,  send_data_action2  },{STATE_IDEL,     STATE_IDEL,     100,3000,state_idel_action1, state_idel_action2 },
};

状态写完了接下来还需要一个状态管理的函数,负责管理是执行action1还是执行action2;

状态管理函数:状态管理函数中等待模组返回数据采用的是软定时器,该方式很好的避免了死等问题。

状态和执行动作完成了,接下来就是事件,驱动状态机运行的驱动力--------事件。

事件1:状态强制切换事件

更新游动状态指针,并且给任务发送状态改变的信号cola_set_event(&nbiot_task,SIG_CHANGE_STATE);

static void nbiot_change_state(nbiot_state_e state)
{const nbiot_fsm_state_t *fsm_state = nbiot_get_state(state);if(fsm_state){nbiot_fsm_state_index.init = ACTION1;if(nbiot_fsm_state_index.cur_state != state) {nbiot_fsm_state_index.cur_state = state;nbiot_fsm_state_index.trycnt    = 0;nbiot_fsm_state_index.fsm_state = fsm_state;}cola_set_event(&nbiot_task,SIG_CHANGE_STATE);}
}

事件2:串口接收到完整数据包事件

上一节“NB-IOT开发|nbiot开发教程《二》AT指令类模组驱动-STM32串口实现接收不定长度数据”中提到的,不明白可以再看下。

串口接收到完整数据包后通知nbiot任务,cola_set_event(uart1_dev.owner,SIG_DATA);

static void uart_timer_cb(uint32_t event)
{if(uart1_dev.owner){cola_set_event(uart1_dev.owner,SIG_DATA);}
}

事件3:超时事件

static void nbiot_timer_cb(uint32_t event)
{cola_set_event(&nbiot_task,SIG_TIMEOUT);
}

三个驱动状态机运行的时间就完成了,既然这三个事件是驱动力,那么就需要在任务中管理这三个驱动事件。

事件管理

static void nbiot_task_cb(uint32_t event)
{int err = 0;if(event & SIG_CHANGE_STATE){nbiot_fsm_schedule(NULL,0);}if(event & SIG_TIMEOUT){nbiot_fsm_schedule(NULL,0);}if(event & SIG_DATA){err = cola_device_read(uart_dev,0,g_buf,sizeof(g_buf));if(err){nbiot_fsm_schedule(g_buf,err);}}
}

三、调试

状态机已经跑起来了,确实按照我们状态列表中编写的在跑,首先运行第一个状态硬件复位,300ms后执行AT指令发送,发送一次AT,尝试3次,而且等待时间为3000ms,如果尝试超过最大次数,则从新复位nbiot模组。如果接收到OK则执行下一个状态。

四、代码下载

        重要的就是代码下载了,不知道有木有人在看呀,等评论超100再上传吧,要不然没有动力写下去了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

NB-IOT开发|nbiot开发教程《三》AT指令类模组驱动-STM32实现AT指令状态机相关推荐

  1. NB-IOT开发|nbiot开发教程《五》nbiot(移远BC26)电信物联网平台实现MCU固件在线升级(STM32L051+BC26)

    嵌入式开发中我们要时刻保持代码的高效与整洁看之前,先点赞 好习惯,要养成 一.前言 嵌入式开发中我们要时刻保持代码的高效与整洁.为什么要有在线升级和为什么要进行在线升级,在这不多说,产品的开发中,在线 ...

  2. NB-IoT(7)---开发环境搭建及模组驱动开发

    NB-IoT(7)---开发环境搭建及模组驱动开发 STM32CubeMX安装及使用 1.STM32CubeMX介绍 2.STM32CubeMX安装 3.STM32CubeMX使用 STM32Cube ...

  3. 华为云IoT智慧物流案例10 | 广和通L610模组FOTA升级(服务端FileZilla Server客户端FileZilla)

    华为云IoT智慧物流案例10 | 广和通L610模组FOTA升级(服务端FileZilla Server客户端FileZilla) 第一章 使用FileZilla Server搭建FTP服务器 第二章 ...

  4. Air700E开发板|移芯EC618|4G Cat.1模组:概述及PinOut

    文章目录 基础资料 概述 主要功能 外设分布 PinOut(管脚定义) 管脚功能说明 固件升级 正常开机模式: 下载模式: IO 电平选择 基础资料 Air700E文档中心 概述 EVB-Air700 ...

  5. 2G退网百天倒计时,汽车定位器NB-IOT or LTE CAT1,芯片 or 模组,来自e老板的选择!

    近期有一位长期关注"eSIM物联工场"的朋友反馈生意难做,压力很大,在考虑转行,在2020年,这样的声音层出不穷,但其实每年都有这样的声音,只是每年的人不一样. 这位朋友我们简称e ...

  6. IoT黑板报:英特尔公布LTE通讯模组XMM 7560

    IoT黑板报,阅尽物联网新鲜事! 关注物联网领域,寻求报道或投稿请邮件联系 jiawd@csdn.net 微信联系 jiaweidi1214,备注"姓名+公司+职位"(以便更快审核 ...

  7. Qwt开发教程(三)—Qwt常见类简介#F0222

    本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载. 接下来,我们开始学习Qwt控件的使用来,使用前,先来简单了解下常见的Qwt类. 一.QwtPlot 它继承自 ...

  8. 《嵌入式系统 - RT-Thread开发笔记》 第三部分 RT-Thread 移植与设备驱动开发 - 第1章 RT-Thread 开发环境搭建 (Ubuntu)

    开发环境: Ubuntu:16.04 LTS 开发板:stm32f746-st-nucleo 开发板MCU:STM32F746ZG 1.1准备工作 1.1.1下载源码与安装环境 更新软件源: $ su ...

  9. 《嵌入式系统 - RT-Thread开发笔记》 第三部分 RT-Thread 移植与设备驱动开发 - 第1章 RT-Thread 开发环境搭建(RT-Thread Studio)

    开发环境: RT-Thread版本:4.0.4 操作系统:Windows 10 RT-Thread Studio版本:2.1.4 开发板MCU:STM32F746ZG 1.1 RT-Thread St ...

最新文章

  1. android应用程序开发_Kotlin与Flutter:Android跨平台应用程序开发,到底选择哪个?...
  2. 未曾秋高气爽,亦然爬山去也
  3. JavaFX触屏事件
  4. 这10个功能模块,手把手教你从零设计电商系统
  5. Linux进阶之Jenkins持续集成介绍及安装演示
  6. MVC 下拉列表三级联动
  7. JavaScript之WebSocket 技术
  8. PFC2D学习笔记——geometry的使用
  9. java 代码控制键盘_Java Robot实现控制鼠标和键盘的代码实例
  10. bootice添加linux_用BOOTICE工具在U盘上实现SYSLINUX与GRUB4DOS双启动 - 图文
  11. 关于C#GB2312编码问题
  12. off-by-one error
  13. 分析开源三大CMS中WordPress相比Drupal与Joomla是怎样摘取皇冠的
  14. uva_10066 The Twin Towers
  15. QQ群反向昵称、恶搞昵称的原理
  16. TTP223使用说明
  17. QQ浏览器X5内核问题汇总
  18. overleaf 只能用jpg格式?visio生成jpg文件太大怎么办?
  19. 欧拉筛法原理C语言,素数筛法
  20. x64dbg 调试 EXCEPTION_ACCESS_VIOLATION C0000005

热门文章

  1. ImageConverter引起的 invalid address or address of corrupt block 0xb7feab58 passed to dlfree
  2. 回顾并总结关于复利计算器的三次实验
  3. 计算机游戏英文文献,JAVA游戏英文文献翻译
  4. Windows驱动之IRP PENDING
  5. CMDN Club #23 开放平台和O2O移动产品开发
  6. oracle12c rac恢复,oracle 12c rac恢复votedisk碰到问题了(asm)
  7. gateway和openfeign依赖冲突
  8. 第一章 数据资产的定义
  9. 特征方程求数列的通项公式(二阶线性递推式)
  10. 用html语言设计李白的一首诗,李白诗《赠汪伦》教学设计