程序框架

本小节对程序框架和运行流程进行说明。

打开 MiaowLabs两轮自平衡小车「小霸王Lite」光盘\02、程序源代码\Mwbalanced-stm32-小霸王Lite-firmware-互补滤波-none V3.33\BasicBalance.uvprojx 工程文件,展开工程分组如下图所示:

工程分组
Image 2.16.1 - 工程分组

如图所示,可以看到工程分有 6 个分组,其中 FWlib 分组主要是 HAL 库的文件代码,CMSIS 分组主要是 Cortex-M3 核心的文件代码,Startup 分组是 stm32f10x 的启动文件代码,BSP 分组主要是外设硬件的驱动程序代码,比如 MPU6050、oled 等,SYS 分组主要是滴答定时器和调试相关的配置代码,USER 分组是用户代码。

下面我们先讲解一下 USER 分组的 main.c。main.c 包含了所有硬件初始化和参数初始化代码。初始化代码如下:

int main(void)
{        
    BspInit();       //初始化BSP
    PIDInit();       //初始化PID    
    CarUpstandInit();//初始化系统参数
    SysTick_Init();  //初始化定时器        
    if(IsInfrareOK())//检测是否悬挂红外模块
        g_iGravity_Offset = 1; //如果检测到悬挂红外模块,则更改偏移值。
    ShowHomePageInit();//初始化OLED显示屏主页
    while (1)//进入主循环
    {    
        SecTask();//秒级任务,主要是记录小车运行时间、读取电池电压等非实时任务

        if(SoftTimer[1] == 0)//系统软件定时器1,分辨率为1ms,递减计数
        {// 每隔20ms 执行一次
            SoftTimer[1] = 20;
            ResponseIMU();//上报姿态数据到APP        
            DebugService();    //上位机调试数据发送        
            Parse(Uart3Buffer);    //APP数据解析函数    
        }            

        if(SoftTimer[2] == 0)//系统软件定时器2,分辨率为1ms,递减计数
        {// 每隔20ms 执行一次
            SoftTimer[2] = 20;
            ShowHomePage();//刷新OLED页面
            Read_Distane();//读取超声波测距距离
            if(g_CarRunningMode == ULTRA_FOLLOW_MODE){
                if(IsUltraOK())UltraControl(0);    //设置超声波跟随模式
             }
            if(g_CarRunningMode == ULTRA_AVOID_MODE){
                if(IsUltraOK())UltraControl(1);    //设置超声波避障模式
             }
            else if(g_CarRunningMode == INFRARED_TRACE_MODE){
                TailingControl();//设置红外循迹模式
            }
        }            
    }
}

初始化代码主要是硬件底层驱动和系统参数的初始化。下面我们来讲解一下功能函数的具体作用。

BspInit 主要是硬件底层驱动的初始化,包括 STM32 外设的初始化、外围模块比如 MPU-6050 的初始化。具体见下面代码:

void BspInit(void)
{
    SWDConfig();            //SWD 调试接口配置,使能 SWD,失能 JTAG
    ADCInit();                //ADC 初始化
    USART1Init();            //串口 1 初始化-底板预留下载及调试用
    USART3Init(0);            //串口 3 初始化-用于蓝牙
    TIM1_Cap_Init();        //TIM1初始化-用于超声波跟随功能
    TIM3_PWM_Init();         //PWM初始化
    TIM2_Encoder_Init();    //TIM2 正交解码初始化-用于测速
    TIM4_Encoder_Init();    //TIM4 正交解码初始化-用于测速    
    i2cInit();                 //I2C 初始化    
    InfraredIOInit();        //红外 IO 口初始化
    OLED_Init();            //OLED 初始化
    MPU6050_Init();            //MPU6050 初始化    
    LEDInit();                //指示灯初始化    
    UltraSelfCheck();        //超声模块开机自检
    InfrareSelfCheck();        //红外模块开机自检    
    delay_ms(500);            //延时 0.5s,等待蓝牙模块启动
    Uart3SendStr("\r\nAT+BAUD8\r\n"); //配置蓝牙串口波特率为 115200 ( 原波特率9600 ) 
    USART3Init(1);            //更改 UART3 波特率为 115200
    delay_ms(20);           //延时 20ms,等待波特率稳定
    SetBlueToothName();        //配置蓝牙模块名称
}

PIDInit 主要是小车系统的 PID 参数初始化。

CarUpstandInit 主要是小车系统的各种参数初始化。

SysTick_Init 是系统滴答定时器 SysTick 的初始化,在这里设置为 1ms 中断一次。

if(IsInfrareOK()) g_iGravity_Offset = 1; 检测是否悬挂红外循迹模块,如果有则需要修改重心偏移值。因为在一侧悬挂红外后,小车重心会偏移。

ShowHomePageInit 主要是在 OLED 显示 logo。

while(1) 主循环中主要是执行一些非实时任务(早些迟些执行都无所谓的任务),人为定义一个秒级任务,轮询执行这些任务。这些非实时任务有上报数据、调试数据、解析协议、刷新 OLED 数据、读取距离等。

执行完初始化,代码会由于滴答定时器 SysTick 进入中断而转跳到 SysTick 定时中断服务函数中执行。在 stm32f10x_it.c 中可以找到 SysTick_Handler 定时中断服务函数。代码具体内容如下:

void SysTick_Handler(void)
{  
    SoftTimerCountDown();             //软定时器
    g_u8MainEventCount++;            //主事件计数变量
    g_u8SpeedControlPeriod++;        //速度环控制周期计数变量
    SpeedControlOutput();            //速度环控制输出函数,每1ms执行一次
    if(g_u8MainEventCount>=5)        //5ms进入一次
    {
        g_u8MainEventCount=0;
        GetMotorPulse();             //捕获电机脉冲(速度)函数,每5ms执行一次
    }
    else if(g_u8MainEventCount==1)
    {
        MPU6050_Pose();                 //读取MPU6050数据函数,每5ms执行一次
        AngleCalculate();             //角度环计算函数,每5ms执行一次
    }
    else if(g_u8MainEventCount==2)
    {
        AngleControl();                 //角度环控制函数,每5ms执行一次
    }
    else if(g_u8MainEventCount==3)
    {
        g_u8SpeedControlCount++;
        if(g_u8SpeedControlCount >= 5)//25ms
        {        
              SpeedControl();          //车模速度控制函数,每25ms调用一次
              g_u8SpeedControlCount=0;
                g_u8SpeedControlPeriod=0;
        }
    }
    else if(g_u8MainEventCount==4)
    {
        MotorManage();            //电机使能/失能控制函数,每5ms执行一次
        MotorOutput();             //电机输出函数,每5ms执行一次
    }
}

SoftTimerCountDown 是软定时器在 SysTick 中断中得到精确的递减。

g_u8MainEventCount 是主事件计数变量。我们需要在中断中运行的代码,在 1ms 内运行不完,所以需要主事件计数变量将代码合理分段,放到 5 个 1ms 内运行。

g_u8SpeedControlPeriod 是速度环的控制周期计数变量。

在 5ms 内执行 GetMotorPulse、MPU6050_Pose、AngleCalculate、AngleControl、MotorManage、MotorOutput,而速度环是 25ms 运行一次。