STM32 port state machine (sequential logic simulation)

  Here, first of all thank CSDN Seamless quiet rain, his blog has given me great inspiration, paste the URL of his blog: https://blog.csdn.net/wuhenyouyuyouyu/article/details/52585835

  I always learn off of school for half a year and then turn STM32 do FPGA, FPGA for a year and then come back with the STM32, before the concept of the microcontroller is used to do some simple things, the most important is the ability to configure Hershey register drive peripherals, but now control a robotic arm to pick up to do, and I flew to rip off, so many manipulator posture, in order to automatically crawl then action must be to follow a certain process, special action, if simple peripheral drive obviously can not complete this arduous task, in addition, in order to receive instructions PYNQ sent to the serial port, these instructions can tell me when to begin to grasp the STM32, the target steering angle is how much, how much is the target location, and many more. We used to do the instruments commonly used SPI, write data through the first to write a very easy way to modify the address register variables, but the serial port, you can only create their own agreement, and according to remind his teammates and the knowledge to do the FPGA, I used a state machine wording (C language version), and ultimately this custom serial protocols, and so that my C levels increase a little.

  Ado, secondary flowchart of the code written as follows:

  

   Of course, this is a flow chart of the serial port my game, I finally sorted out the code re-write, so the assignment is different.

  For embedded, the peripheral drivers are basic skills, here I am driven to write my UART1 agreement, and drive the RGB lights to verify here directly posted my driver code:

<LED.h>

#ifndef INC_LED_H_
#define INC_LED_H_

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

#define LED_Pin_Port      GPIOB
#define LED_Green_Pin     GPIO_Pin_0
#define LED_Blue_Pin       GPIO_Pin_1
#define LED_Red_Pin        GPIO_Pin_5

#define LED_Green_ON    GPIO_ResetBits(LED_Pin_Port,LED_Green_Pin)
#define LED_Green_OFF    GPIO_SetBits(LED_Pin_Port,LED_Green_Pin)

#define LED_Blue_ON        GPIO_ResetBits(LED_Pin_Port,LED_Blue_Pin)
#define LED_Blue_OFF    GPIO_SetBits(LED_Pin_Port,LED_Blue_Pin)

#define LED_Red_ON        GPIO_ResetBits(LED_Pin_Port,LED_Red_Pin)
#define LED_Red_OFF        GPIO_SetBits(LED_Pin_Port,LED_Red_Pin)

void LED_Init(void);

#endif /* INC_LED_H_ */

<LED.c>

#include "LED.h"

void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    GPIO_InitStructure.GPIO_Pin = LED_Green_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_Pin_Port,&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = LED_Blue_Pin;
    GPIO_Init(LED_Pin_Port,&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = LED_Red_Pin;
    GPIO_Init(LED_Pin_Port,&GPIO_InitStructure);
}

<uart.h>

#ifndef INC_UART_H_
#define INC_UART_H_

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

void Uart1_Init(void);
void Uart1_SendByte(u8 Data);
u8   Uart1_ReceiveByte(void);
void Uart1_SendString(char *str);

#endif /* INC_UART_H_ */

<uart.c>

#include "uart.h"

void Uart1_Init(void)
{
    GPIO_InitTypeDef    Uart_GPIO_InitStructure;
    USART_InitTypeDef    Uart1_InitStructure;
    NVIC_InitTypeDef    NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
    Uart_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    Uart_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    Uart_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&Uart_GPIO_InitStructure);

    Uart_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    Uart_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    Uart_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&Uart_GPIO_InitStructure);

    Uart1_InitStructure.USART_BaudRate = 115200;
    Uart1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    Uart1_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    Uart1_InitStructure.USART_Parity = USART_Parity_No;
    Uart1_InitStructure.USART_StopBits = USART_StopBits_1;
    Uart1_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1,&Uart1_InitStructure);

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);

    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    USART_Cmd(USART1,ENABLE);
}

void Uart1_SendByte(u8 Data)
{
    USART_SendData(USART1,Data);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}

void Uart1_SendString(char *str)
{
    while((*str)!='\0')
    {
        Uart1_SendByte(*str);
        str++;
    }
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
}

u8   Uart1_ReceiveByte(void)
{
    u8 Receive_Data;
    Receive_Data = USART_ReceiveData(USART1);
//    while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
    return Receive_Data;
}

  To here, I can drive good serial port 1 and LED lights, the next step is to focus on where the FSM

 

   Pirates a map, this is the classic block diagram of the sequential logic circuit, so using Verilog description of a state machine, I like to use three-state machine, because it has the taste, well reflect the structure of the sequential logic circuit, If the period is not the state machine Verilog parallel state, it is not earlier, while the C language, EMM, originally executed sequentially, there will be some problems, like this:

if(state == Idle)
{
    ........  
}
else if(state == State1)
{
    ........
}
else if(state == State2)
{
    ........
}
else
{
    .........
}

  As we all know, C language, if else if statement is the first from the beginning to determine if they comply with the conditions of the line is always behind the lines, then they would have executed many times and more each time if ..... but if a judge statement in parentheses to the operation is performed, even if the MCU single-cycle instruction, performing multiplication and division operations so need to consume more clock cycles, therefore, performed once every plurality Analyzing waste least one clock cycle or more, so , like this state machine is undoubtedly inefficient, terror, the online a lot of so-called C language state machine are using this approach, so I started to have Mongolian circle for a long time.

  Later, I also saw a post, qiuri2008 of https://www.cnblogs.com/jiangzhaowei/p/9129024.html , his FSM do a little improvement on the basis of the original structure is used for encapsulating each state information, and the use of function pointers approach to the nuclear structure of the state machine, in imitation of my code when it felt progress but still very confused, and so I said above there a difference?

Theft at his code:

 

 

 

 

 

 

 

 

 

This section defines the equivalent of a data control block, similar to the OS TCB

 

 And here is the use of an array of data constitute a control block table, information is recorded state machine

 

 

 

 Well, this is his core code it, is still very worth learning, this is a state of mind machine, use a table to store state information, and to determine whether the state jumps to execute the function through a function pointer by a combination of logic, it can perform different functions in different states. But when I imitate its code written, but found the same deadly thing --Event!

Can be seen from the table, the current state of the different next state of the same EVENT is not the same value, being the case, according to the definition of the state machine to EVENT is determined what needs to write:

void EventJudge(u8 ReceiveData)
{
      switch(CurState)  
     {
          case Idle: if(ReceiveData == '1')
                           .....
                         else 
                             ......
          case State1: if(ReceiveData == '2'))
                          .......
           default: ......
     }
}    

那么这样子写的意义又何在呢?加上之前的For查找Event并且比对,其实这样子写效率什么还没有直接的if else if高呢

而CSDN的无痕幽雨却这样构造状态机核:

typedef State(*Procedure)(void*);
Procedure Steps[] = {Idle_Fun , State1_Fun.......};
typedef struct{
  u8 Target1;
  u8 Target2;
}SM_Arg_T
void BestStateMachine(void * invar)
{
    static State NS = s_init; //定义下一状态
    NS = Steps[NS](invar);
}

这里采用函数内static,静态局部变量,这样做可以让函数每一次重新进入该函数的时候使用上一次修改的值,类似于全局变量,有利于函数模块化。而使用函数指针数组则可以直接通过索引找到对应函数,因此,若我们每一个状态的函数返回一个状态参量,那么就可以直接索引至对应的函数指针,这样的查找表方式,相较于for循环查找表可谓快了很多很多倍,因此,每一个状态函数都应该这样写

State Uart_Idle_Fun(void *arg)
{
    Uart_SM_Arg_t* Uart_SM_Arg = (Uart_SM_Arg_t*)arg;
    if(Uart_SM_Arg->ReceiveData == 0xAA)
    {
        return Type_Judge;
    }
    else
    {
        return Uart_Idle;
    }
}

注意这里的入口参数,采用指针当入口参数,这样子函数内就可以很方便的修改以及读取参数,并且不用占用大量的栈空间,FreeRTOS的邮箱也是一样的道理,使用了消息队列,也是传的指针,而不是传一大块数据

最后,贴下自己的FSM代码实现吧

<UART1_FSM.h>

#ifndef INC_UART1_FSM_H_
#define INC_UART1_FSM_H_

#define Instruction_Type    1
#define Data_Type            2

#include "uart.h"

typedef u8 State;
enum{
    Uart_Idle,
    Type_Judge,
    InstructionAssignment,
    DataAttributionJudge,
    DataAssignment
};
typedef State(*Uart_Procedure)(void*);
typedef struct{
    u8    ReceiveData;
    u8    Type;
    u8    Instruction;
    u8     DataAttribution;
    u8    Data;
}Uart_SM_Arg_t;

State Uart_Idle_Fun(void *arg);
State Uart_Type_Judge_Fun(void* arg);
State Uart_InstructionAssignment_Fun(void* arg);
State Uart_DataAttributionJudge_Fun(void* arg);
State Uart_DataAssignment_Fun(void* arg);
void UartStateMachine(Uart_SM_Arg_t *arg);

extern Uart_SM_Arg_t    Uart1_SM_Arg;
extern u8 Target1,Target2,Target3;

#endif /* INC_UART1_FSM_H_ */

<UART1_FSM.c>

#include "UART1_FSM.h"

u8 Target1,Target2,Target3;


Uart_Procedure Uart_Steps[] = { Uart_Idle_Fun, Uart_Type_Judge_Fun, Uart_InstructionAssignment_Fun, Uart_DataAttributionJudge_Fun, Uart_DataAssignment_Fun};

Uart_SM_Arg_t    Uart1_SM_Arg ={
        .Data = 0,
        .DataAttribution = 0,
        .Instruction = 0,
        .ReceiveData = 0,
        .Type = 0
};

State Uart_Idle_Fun(void *arg)
{
    Uart_SM_Arg_t* Uart_SM_Arg = (Uart_SM_Arg_t*)arg;
    if(Uart_SM_Arg->ReceiveData == 0xAA)
    {
        return Type_Judge;
    }
    else
    {
        return Uart_Idle;
    }
}

State Uart_Type_Judge_Fun(void* arg)
{
    Uart_SM_Arg_t* Uart_SM_Arg = (Uart_SM_Arg_t*)arg;
    if(Uart_SM_Arg->ReceiveData == 0x0A)
    {
        Uart_SM_Arg->Type = Instruction_Type;
        return InstructionAssignment;
    }
    else if(Uart_SM_Arg->ReceiveData == 0x0F)
    {
        Uart_SM_Arg->Type = Data_Type;
        return DataAttributionJudge;
    }
    else
    {
        return Uart_Idle;
    }
}

State Uart_InstructionAssignment_Fun(void* arg)
{
    Uart_SM_Arg_t* Uart_SM_Arg = (Uart_SM_Arg_t*)arg;
    if(Uart_SM_Arg->Type == Instruction_Type)
    {
        Uart_SM_Arg->Instruction = Uart_SM_Arg->ReceiveData;
        return Uart_Idle;
    }
    else
    {
        return Uart_Idle;
    }
}

State Uart_DataAttributionJudge_Fun(void* arg)
{
    Uart_SM_Arg_t* Uart_SM_Arg = (Uart_SM_Arg_t*)arg;
    if(Uart_SM_Arg->Type == Data_Type)
    {
        Uart_SM_Arg->DataAttribution = Uart_SM_Arg->ReceiveData;
        return DataAssignment;
    }
    else
    {
        return Uart_Idle;
    }
}

State Uart_DataAssignment_Fun(void* arg)
{
    Uart_SM_Arg_t* Uart_SM_Arg = (Uart_SM_Arg_t*)arg;
    if(Uart_SM_Arg->DataAttribution == 0xA1)
    {
        Uart_SM_Arg->Data = Uart_SM_Arg->ReceiveData;
//        Uart_SM_Arg->MachineBi.RealSense_Target.RealSense_TargetBeta = Uart_SM_Arg->Data;
        Target1 = Uart_SM_Arg->Data;
        return Uart_Idle;
    }
    else if(Uart_SM_Arg->DataAttribution == 0xA2)
    {
        Uart_SM_Arg->Data = Uart_SM_Arg->ReceiveData;
//        Uart_SM_Arg->MachineBi.RealSense_Target.RealSense_TargetX = Uart_SM_Arg->Data;
        Target2 = Uart_SM_Arg->Data;
        return Uart_Idle;
    }
    else if(Uart_SM_Arg->DataAttribution == 0xA3)
    {
        Uart_SM_Arg->Data = Uart_SM_Arg->ReceiveData;
//        Uart_SM_Arg->MachineBi.RealSense_Target.RealSense_TargetY = Uart_SM_Arg->Data;
        Target3 = Uart_SM_Arg->Data;
        return Uart_Idle;
    }
    else
    {
        return Uart_Idle;
    }
}

void UartStateMachine(Uart_SM_Arg_t *arg)
{
    static State Next_State = Uart_Idle;
    Next_State = Uart_Steps[Next_State](arg);
}

验证代码

int main(void)
{
  Uart1_Init();
  LED_Init();
  /* TODO - Add your application code here */

  /* Infinite loop */
  while (1)
  {
      if(Target1 == 0xA0)
      {
          LED_Red_ON;
          LED_Green_OFF;
          LED_Blue_OFF;
      }
      else if(Target2 == 0x0F)
      {
          LED_Red_OFF;
          LED_Green_ON;
          LED_Blue_OFF;
      }
      else
      {
          LED_Red_OFF;
          LED_Green_OFF;
          LED_Blue_ON;
      }
  }
}

初始状态:

 

 

 

串口调试助手输入:

 

 实验现象:

 

 大功告成!

Guess you like

Origin www.cnblogs.com/whulaolao/p/11921247.html