本文编写代码,部门C语言复习,参考江科大,侵删!
一、状态机
1、定义:同一时间只能处在某一种固定状态,收到外部触发条件后,切换到另外一种状态,每个状态做固定动作。关键词如下:
有限:状态数量是固定可数的,不会无限多。状态必须提前定义,无法运行时新增。
单一:任意时刻,只有一个当前状态,不能多状态共存。
触发:必须有事件/条件才会切换状态,不会自己乱跳,即状态不会自动跳转;
举例:电灯
状态:1、灯灭;2、灯亮;
触发事件:按开关;
状态流转:
当前 =灯灭 +按开关→切换为灯亮;
当前=灯亮 +按开关→切换为灯灭;
2、四大组成要素
1)状态集合S:所有能存在的模式;(灭,亮)
2)输入/事件结合E:触发状态转换的信号、条件等;(按下开关)
3)状态转移规则T:当前状态+输入 →下一个状态;(灯灭 +按开关→灯亮,灯亮 +按开关→灯灭)
4)输出/动作A :停留在这个状态要执行的行为;(亮状态输出"通电发光",灭状态输出"断电");
3、两种经典状态机
1)Moore状态机:动作/输出只由当前状态决定,和输入无关,比如:灯,只要状态是"亮",就一直发光,不管有没有按键输入;
2)Mealy状态机(常用)
动作/输出由当前状态+当前输入一起决定;比如电梯,状态:停靠1楼;输入:按下3楼按钮;动作:关门上行;即:同一个状态,输入不同,动作不一样。
4、常见逻辑
枚举列举所有状态→定义全局/结构体变量保存当前状态→循环里用switch判断当前状态,每个状态写对应的逻辑。这是最基础轮询式状态机,也是最通用的。
cpp
typedef enum{
State_Idle,//空闲
State_Run,//运行
State_Err//故障
}State;
State Curr_State = State_Idle;//定义全局变量初始化
int main()
{
while(1)
{
switch(Curr_State)
{
case State_Idle:
(*此处写空闲时的动作代码*)
if(触发启动事件)
{
Curr_State = State_Run;//切换状态
}
break;
case State_Run:
(*此处写运行时的动作代码*)
if(故障信号)
{
Curr_State = State_Err;
}
if(运行完成)
Curr_State = State_Idle
break;
case State_Err:
(*此处写故障处理时的动作代码*)
if(复位按键)
{
Curr_State = State_Idle;
}
break;
}
}
}
5、状态机优势
1)替代多层if-else,逻辑分层清晰,易维护;
2)不会出现逻辑卡死,分支混乱;
3)自带异常处理分支,出错统一跳转到错误状态复位;
4)适配中断、定时器、通信协议;
6、状态转移表(以3段式设备举例)
|-------|------|-------|---------|
| 当前状态 | 触发事件 | 下一个状态 | 该状态动作 |
| IDLE | 启动信号 | RUN | 待机、低功耗 |
| RUN | 完成信号 | IDLE | 正常工作 |
| RUN | 故障信号 | ERROR | 停止工作 |
| ERROR | 复位信号 | IDLE | 报警、清理故障 |
7、注意点
1)状态是静态模式 ,事件是动态触发信号;
2)状态不会自动跳转,必须满足条件才切换;
3)任何时刻只有一个当前状态,不存在同时处于两个状态;
4)有限状态机≠无限状态,状态必须提前定义好,不能动态新增;
二、串口接收HEX数据包和文本数据包
1、HEX数据包

1)固定包长:一整个数据包字节数固定不变。
包头:0xFF,起始标记,判断一帧开始;
包尾:0xFE:结束标记,判断一帧结束;
有效数据是中间的4个字节。
接收流程:
逐个字节接收,抓到字节0xFF,开始缓存→持续存入后续字节,直到遇到0xFE,停止接收→有效数据是中间4个字节;
2)可变长度数据包
包长度不固定,只靠包头+包尾来分割。格式:0xFF+任意长数据+0xFE
接收逻辑:收到0xFF,开始缓存数据,一直往后存,直到收到0xFE,就把这一段当成完整数据包;
可变包长坑点:如果有限数据里刚好包含了0xFF或0xFE,解析会错乱。
举例:0xFF,0x01,0xFE,0x02,0x03,0xFE,程序直接把0xFF~0xFE截断成一个包,剩下数据就错位了。即:数据和包头包尾重复,导致分包出错。
解决方法:
①限制有限数据的取值范围:不让数据出现0xFF、0xFE,只使用剩下的字节数据,从根源避免冲突;
②改用固定长度数据包:只要每个数据包字节长度固定,只靠包头+长度对齐,包尾可以不用,就不会错位。
2)程序编写(固定包长,含包头包尾),接收4个字节数据
Serial初始化
cpp
#include "Serial.h"
uint8_t Serial_RxPacket[4];
uint8_t Serial_TxPacket[4];
uint8_t Receive_Flag;
uint8_t Serial_State;
void Serial_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx |USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength =USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);
}
//标志位返回并清零
uint8_t Serial_RxFlag()
{
if(Receive_Flag ==1)
{
Receive_Flag =0;
return 1;
}
return 0;
}
void USART1_IRQHandler()
{
static uint8_t pRxNum;
if(USART_GetITStatus(USART1,USART_IT_RXNE) ==SET)
{
if(Serial_State ==0)
{
if(USART_ReceiveData(USART1) ==0xFF)
{
Serial_State =1;
pRxNum =0;
}
}
else if(Serial_State ==1)
{
Serial_RxPacket[pRxNum] =USART_ReceiveData(USART1);
pRxNum ++;
if(pRxNum >=4)
{
pRxNum =0 ;
Serial_State =2;
}
}
else if(Serial_State ==2)
{
if(USART_ReceiveData(USART1) ==0xFE)
{
Serial_State =0;
Receive_Flag =1;
}
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
main函数:
cpp
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
OLED_Init();
Serial_Init();
OLED_ShowString(3,1,"RxPacket");
while(1)
{
if(Serial_RxFlag() ==1)
{
OLED_ShowHexNum(4,1,Serial_RxPacket[0],2);
OLED_ShowHexNum(4,4,Serial_RxPacket[1],2);
OLED_ShowHexNum(4,7,Serial_RxPacket[2],2);
OLED_ShowHexNum(4,10,Serial_RxPacket[3],2);
}
}
}
2、文本数据包

1)固定包长
整包字节总数严格固定,一整包=1字节包头+2字节数据+2字节包尾,总长:6个字符
接收逻辑:
找到包头@→再接收5个字符→收满6个字节,
2)可变包长(只靠包头+包尾分割)
格式:@+任意长度文本+\r\n,依靠@作为起始,\r\n作为结束切分字符串,但要避免文本内容里自带回车换行\r\n,否则会提前切断,数据乱序。
3)程序编写(可变包长,接收数据)
cpp
#include "Serial.h"
char Serial_RxPacket[100];
uint8_t Receive_Flag;
void Serial_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx |USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength =USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);
}
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1,Byte);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) ==RESET);
}
void Serial_SendString(char* String)
{
while(*String )
{
Serial_SendByte(*String++);
}
}
void Serial_SendArray(uint8_t *Array,uint8_t Length)
{
uint8_t i=0;
for(i=0;i<Length;i++)
{
Serial_SendByte(Array[i]);
}
}
uint8_t Serial_RxFlag()
{
if(Receive_Flag ==1)
{
Receive_Flag =0;
return 1;
}
return 0;
}
void USART1_IRQHandler()
{
static uint8_t pRxNum =0;
static uint8_t Serial_State =0;
if(USART_GetITStatus(USART1,USART_IT_RXNE) ==SET)
{
uint8_t GetData =USART_ReceiveData(USART1);
if(Serial_State ==0)
{
if(GetData =='@')
{
Serial_State =1;
pRxNum =0;
}
}
else if(Serial_State ==1)
{
if(GetData =='\r')
{
Serial_State =2;
}
else
{
Serial_RxPacket[pRxNum] =GetData;
pRxNum ++;
}
}
else if(Serial_State ==2)
{
if(GetData=='\n')
{
Serial_State =0;
Serial_RxPacket[pRxNum]= '\0';
Receive_Flag =1;
}
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
本文完!其他方式还在学习中......