蓝桥杯嵌入式第八届省赛——“模拟升降控制器”旧板标准库

6 篇文章 0 订阅
6 篇文章 0 订阅

一、赛题分析

        第八届蓝桥杯嵌入式省赛,博主觉得是在前几届中最难的一届。绕来绕去的,前前后后写了三个版本,第一个版本main.c里就写了400多行代码,而且功能不是很完整。第三版终于把代码浓缩成了300多行,功能完全实现(理论没错,可能有些小错误),在这里分享第三版的程序设计方法和思路。

        本届赛题设计到模块有LCD、LED、PWM双路输出、KEY、RTC。其中LED、KEY、RTC均可在往期博客中找到相应代码初始化。附上上一届的链接,如果上一届没有则可在上上届找到:

蓝桥杯嵌入式第七届省赛——“模拟液位检测告警系统”旧板标准库_对愁眠后霜满天的博客-CSDN博客

二、程序设计

1、PA6-7的双路PWM输出程序设计

        直接把PWM输出写出函数void TIM3_PWM_OCToggle(uint8_t PA6,uint8_t PA7)方便控制。只需要在函数中写入占空比即输出占空比,写入0则输出低电平。

#ifndef _PWM_H
#define _PWM_H

#include "stm32f10x.h"

void TIM3_PWM_OCToggle(uint8_t PA6,uint8_t PA7);

extern uint16_t TIM3_CCR1_Val;
extern uint16_t TIM3_CCR2_Val;
extern float TIM3_CH1_duty, TIM3_CH2_duty;

#endif

/*********************************************

PA1--TIM2--CH2   PA2--TIM2--CH3   PA3--TIM2--CH4 
PA6--TIM3--CH1   PA7--TIM3--CH2

*********************************************/
#include "pwm.h"
#include "stm32f10x_tim.h"
#include "misc.h"

uint16_t TIM3_CCR1_Val = 1000;//PA6输出1000HZ
uint16_t TIM3_CCR2_Val = 500;//PA7输出2000HZ

uint16_t TIM3_capture = 0;
_Bool TIM3_CH1_flag=0, TIM3_CH2_flag=0;
float TIM3_CH1_duty=0.3, TIM3_CH2_duty=0.7;

void TIM3_PWM_OCToggle(uint8_t PA6,uint8_t PA7)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  uint16_t PrescalerValue = 0;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  if(PA6 || PA7)
  {
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    PrescalerValue = (uint16_t) (SystemCoreClock /1000000) - 1;
    TIM_TimeBaseStructure.TIM_Period = 65535;
    TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    /* Output Compare Toggle Mode configuration: Channel2 */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  }
  if(PA6 == 0)
  {
    TIM3_CH1_duty = 0;
    TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_Low);
    TIM_ForcedOC1Config(TIM3,TIM_ForcedAction_Active);		
  }else	
  {
    TIM3_CH1_duty = PA6 * 0.01;
    TIM_OCInitStructure.TIM_Pulse = TIM3_CCR1_Val;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
    TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
  }
	
  if(PA7 == 0)
  {
    TIM3_CH2_duty = 0;
    TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_Low);
    TIM_ForcedOC2Config(TIM3,TIM_ForcedAction_Active);		
  }else
  {
    TIM3_CH2_duty = PA7 * 0.01;
    /* Output Compare Toggle Mode configuration: Channel3 */
    TIM_OCInitStructure.TIM_Pulse = TIM3_CCR2_Val;

    TIM_OC2Init(TIM3, &TIM_OCInitStructure);
		
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
    TIM_ITConfig(TIM3,TIM_IT_CC2, ENABLE);
  }
  
  TIM_Cmd(TIM3, ENABLE);
}


void TIM3_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
    TIM3_capture = TIM_GetCapture1(TIM3);
    if(TIM3_CH1_flag == 1)
    {
      TIM_SetCompare1(TIM3, TIM3_capture + (u16)(TIM3_CCR1_Val*TIM3_CH1_duty));
      TIM3_CH1_flag = 0;
    }else
    {
      TIM_SetCompare1(TIM3, TIM3_capture + (u16)(TIM3_CCR1_Val*(1-TIM3_CH1_duty)));
      TIM3_CH1_flag = 1;
    }
  }

  if(TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
    TIM3_capture = TIM_GetCapture2(TIM3);
    if(TIM3_CH2_flag == 1)
    {
      TIM_SetCompare2(TIM3, TIM3_capture + (u16)(TIM3_CCR2_Val*TIM3_CH2_duty));
      TIM3_CH2_flag = 0;
    }else
    {
      TIM_SetCompare2(TIM3, TIM3_capture + (u16)(TIM3_CCR2_Val*(1-TIM3_CH2_duty)));
      TIM3_CH2_flag = 1;
    }
  }
}

2、main.c设计

        主函数存在的各个关键变量,状态标志位作用讲解处:

        枚举STAT表示电梯所在的状态:STOP表示开门停留,OFF表示门在关闭中,ON表示门在打开中,UP表示在向上运行,DOWN表示向下运行。

        LED[4]包括LED5到LED8,用于方便流水灯的操作。

        state表示现在的状态,初始幅值STOP,表示电梯门在开门停留中。

        key_lock为1表示按键锁死。key_lock_flag为1时表示有按键按下,key_lock_cnt开始计时,到1S时按键锁死。

        ld[4]在程序中表示LED1到LED4的亮灭标志位。当按键按下相应位置置1,到达相应楼层之后只需要把相应楼层数减1处的值幅值为0即可灭掉到达相应楼层对应的LED灯。

        arrive_flag初始化为1,上电表示到达。Input_target[4]保存输入目标楼层信息。

        

enum STAT{STOP,OFF,ON,UP,DOWN};
unsigned LED[4]={LED5,LED6,LED7,LED8};

u8 state = STOP;
u8 key_flag = 0,key_lock = 0,key_lock_flag = 0;
u8 now_floor = 1,Input_target[4]={0},target = 0;
u8 ld[4]={0},led_up = 0,led_down = 4,led_flag = 1;
u8 arrive_flag = 1,lcd_flag = 0,shine_flag = 0;
u8 arrive_pre = 0;
u8 temp[4],min[4];
u8 string[20];

u16 key_lock_cnt = 0;
u16 ON_cnt = 0,OFF_cnt = 0,STOP_cnt = 0;

        key_in函数,当key_lock_cnt == 1000时key_lock == 1时按键锁死在这里体现。每次按键按下key_lock_cnt 清0就可以在最后一次按下按键后1s锁死按键。target_floor()--目标楼层读取函数,由高至低,先上后下。按键锁死之后读取,当target_floor()返回值为0时表示此时以到达最终楼层,arrive_flag置1,进行相应的开门操作。

void key_in(void)
{
  if(key_lock == 0) key_flag = key_scan();
  else if(key_lock == 1)
  {	
    key_flag = 0;
    target = target_floor();
  }

  if(key_flag == 1&&now_floor != 1)
  {	
    Input_target[0] = 1;
    ld[0]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
  if(key_flag == 2&&now_floor != 2)
  {
    Input_target[1] = 2;
    ld[1]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
  if(key_flag == 3&&now_floor != 3)
  {
    Input_target[2] = 3;
    ld[2]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
  if(key_flag == 4&&now_floor != 4)
  {
    Input_target[3] = 4;
    ld[3]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
}

        target_floor()函数,在此处用到的i,j,k,m等用到局部变量,因为程序每执行一次他们都会被清0.在程序中temp[0]永远都是大于当前楼层的最小楼层,比如现在按下2、3、4按键它会依次返回2、3、4,做到顺序上升。当temp[0]==0时表示当前没有比当前楼层大的楼层输入,则返回min[0],min[0]同理是小于当前楼层的最大值。做到先上后下,顺序运行。

u8 target_floor(void)
{
  u8 i,j = 0,k = 0,m;
  for(i=0;i<4;i++)//返回大于当前楼层的追小值
  {
    if(now_floor<Input_target[i])
    {
      temp[j] = Input_target[i];
      j++;
    }
    if(now_floor>Input_target[i]&&Input_target[i]!=0)
    {
      min[k] = Input_target[i];
      k++;
    }
  }
  for(m=0;m<k;m++)//返回小于当前楼层的最大值
  {
    if(min[0]<min[m])min[0]=min[m];
  }
  if(temp[0] == 0) return min[0];
  else return temp[0];
}

       LED_content(),LED灯的控制程序。在TIM4,1ms定时器中,初始化led_flag为1,led_flag取反时间为位移led_up或led_down的1/2即可实现向上或向下的流水灯。

void LED_content(void)
{
  LED_Control(LED1,ld[0]);
  LED_Control(LED2,ld[1]);
  LED_Control(LED3,ld[2]);
  LED_Control(LED4,ld[3]);
	
  if(state == UP) LED_Control(LED[led_up],led_flag);//向上流水灯
  else if(state == DOWN) LED_Control(LED[led_down-1],led_flag);//向下流水灯
  else if(state != UP||state != DOWN) LED_Control(LED5|LED6|LED7|LED8,0);
}

        全部代码如下,本届赛题就像一个状态机一样,由一个状态的完成触发下一个状态。这个程序有一个小bug,arrive_flag初始化为1,则上电即判断到达,屏幕上的1会闪两下,然后开门停住。

#include "stm32f10x.h"
#include "lcd.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "rtc.h"
#include "timer.h"
#include "pwm.h"
#include "stdio.h"
#include "string.h"

enum STAT{STOP,OFF,ON,UP,DOWN};
unsigned LED[4]={LED5,LED6,LED7,LED8};

u8 state = STOP;
u8 key_flag = 0,key_lock = 0,key_lock_flag = 0;
u8 now_floor = 1,Input_target[4]={0},target = 0;
u8 ld[4]={0},led_up = 0,led_down = 4,led_flag = 1;
u8 arrive_flag = 1,lcd_flag = 0,shine_flag = 0;
u8 arrive_pre = 0;
u8 temp[4],min[4];
u8 string[20];

u16 key_lock_cnt = 0;
u16 ON_cnt = 0,OFF_cnt = 0,STOP_cnt = 0;

void key_in(void);
u8 target_floor(void);
void show_lcd(void);
void LED_content(void);
void state_judgement(void);
void PWM_STATE(void);
void GPIO_INIT(void);
void gpio_state(void);

//Main Body
int main(void)
{
  STM3210B_LCD_Init();      
  LCD_Clear(Black);         
  LCD_SetBackColor(Black);   
  LCD_SetTextColor(White);  
  SysTick_Config(SystemCoreClock/1000);
	
  TIM4_Init();
  LED_Init();
  KEY_Init();
  GPIO_INIT();
  TIM3_PWM_OCToggle(0,0);
  RTC_Init(12,50,55);
  while(1)
  {
    key_in();
    show_lcd();
    LED_content();
    PWM_STATE();
    state_judgement();
    gpio_state();
  }
}

void key_in(void)
{
  if(key_lock == 0) key_flag = key_scan();
  else if(key_lock == 1)
  {	
    key_flag = 0;
    target = target_floor();
  }

  if(key_flag == 1&&now_floor != 1)
  {	
    Input_target[0] = 1;
    ld[0]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
  if(key_flag == 2&&now_floor != 2)
  {
    Input_target[1] = 2;
    ld[1]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
  if(key_flag == 3&&now_floor != 3)
  {
    Input_target[2] = 3;
    ld[2]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
  if(key_flag == 4&&now_floor != 4)
  {
    Input_target[3] = 4;
    ld[3]=1;
    key_lock_flag = 1;key_lock_cnt = 0;
  }
}

u8 target_floor(void)
{
  u8 i,j = 0,k = 0,m;
  for(i=0;i<4;i++)//返回大于当前楼层的追小值
  {
    if(now_floor<Input_target[i])
    {
      temp[j] = Input_target[i];
      j++;
    }
    if(now_floor>Input_target[i]&&Input_target[i]!=0)
    {
      min[k] = Input_target[i];
      k++;
    }
  }
  for(m=0;m<k;m++)//返回小于当前楼层的最大值
  {
    if(min[0]<min[m])min[0]=min[m];
  }
  if(temp[0] == 0) return min[0];
  else return temp[0];
}

void show_lcd(void)
{
  LCD_DisplayStringLine(Line1,(u8*)"      Now Floor      ");

  memset(string,0,sizeof(string));
  sprintf((char*)string,"         %d          ",now_floor);
  if(lcd_flag == 0) LCD_DisplayStringLine(Line4,string);	
  else LCD_DisplayStringLine(Line4,(u8*)"                    ");
	
  update_time();
  LCD_DisplayStringLine(Line7,time_str);
}

void LED_content(void)
{
  LED_Control(LED1,ld[0]);
  LED_Control(LED2,ld[1]);
  LED_Control(LED3,ld[2]);
  LED_Control(LED4,ld[3]);
	
  if(state == UP) LED_Control(LED[led_up],led_flag);//向上流水灯
  else if(state == DOWN) LED_Control(LED[led_down-1],led_flag);//向下流水灯
  else if(state != UP||state != DOWN) LED_Control(LED5|LED6|LED7|LED8,0);
}

void state_judgement(void)
{
  if(arrive_flag == 1&&arrive_pre == 0)
  {
    state = ON;
    ON_cnt = 2000;
    STOP_cnt = 2000;
    OFF_cnt = 2000;
    arrive_pre = 1;
  }
}

void PWM_STATE(void)
{
  static u8 state_pre = 0;
  if(state_pre != state&&key_lock == 1)
  {
    if(state == OFF) TIM3_PWM_OCToggle(0,50);
    else if(state == UP) TIM3_PWM_OCToggle(80,0);
    else if(state == DOWN) TIM3_PWM_OCToggle(60,0);
    else if(state == ON) TIM3_PWM_OCToggle(0,60);
    else TIM3_PWM_OCToggle(0,0);
    state_pre = state;
  }
}

void GPIO_INIT(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void gpio_state(void)
{
  if(state == UP)GPIOA->ODR |= GPIO_Pin_4;
  if(state == DOWN)GPIOA->ODR &= ~GPIO_Pin_4;
  if(state == ON)GPIOA->ODR |= GPIO_Pin_5;
  if(state == OFF)GPIOA->ODR &= ~GPIO_Pin_5;
  if(state == STOP)
  {
    GPIOA->ODR &= ~GPIO_Pin_5;
    GPIOA->ODR &= ~GPIO_Pin_4;
  }
}

void TIM4_IRQHandler(void)
{
  static u16 elevator_running = 0,led_cnt = 0,lcd_cnt = 1000;
  if(TIM_GetITStatus(TIM4,TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    if(key_lock_flag == 1&&++key_lock_cnt>=1000)
    {
      key_lock_flag = 0;
      key_lock = 1;
      key_lock_cnt = 0;
    }
    if(now_floor<target&&arrive_flag == 0)
    {
      state = UP;
      if(++led_cnt%100 == 0)
      {
        led_flag=!led_flag;
        if(led_cnt%200 == 0) led_up = (led_up + 1) % 4;
      }
      if(++elevator_running>=6000)
      {
        elevator_running = 0;
        now_floor++;
        if(now_floor == target)
        {
          arrive_flag = 1;
          Input_target[now_floor-1] = 0;
          ld[now_floor-1]=0;
          temp[0]=0;
          led_flag = 1;
		                
       if(Input_target[0]==0&&Input_target[1]==0&&Input_target[2]==0&&Input_target[3]==0)
          {
            key_lock = 0;
            target = 0;
          }
        }
      }
    }
    if(now_floor>target&&now_floor>1&&target!=0&&arrive_flag == 0)
    {
      state = DOWN;
      if(++led_cnt%100 == 0)
      {
        led_flag=!led_flag;
        if(led_cnt%200 == 0)
        {
          led_down--;
          if(led_down == 0) led_down = 4;
        }
      }
      if(++elevator_running>=6000)
      {
        elevator_running = 0;
        now_floor--;
        if(now_floor == target)
        {
          arrive_flag = 1;
          Input_target[now_floor-1] = 0;
          ld[now_floor-1]=0;
          min[0]=0;
          led_flag = 1;
					 
       if(Input_target[0]==0&&Input_target[1]==0&&Input_target[2]==0&&Input_target[3]==0)
          {
            key_lock = 0;
            target = 0;
          }
        }
      }
    }
    if(arrive_flag == 1)
    {
      if(shine_flag == 0)
      {
        if(--lcd_cnt%250==0)
        {
          lcd_flag =! lcd_flag;
          if(lcd_cnt == 0)
          {	
            lcd_cnt = 1000;
            shine_flag = 1;
          }
        }
      }
      if(state == ON)
      {
        ON_cnt--;
        if(ON_cnt == 0) state = STOP;
      }
      if(state == STOP&&target!=0)
      {
        STOP_cnt--;
        if(STOP_cnt == 0)	state = OFF;
      }
      if(state == OFF)
      {
        OFF_cnt--;
        if(OFF_cnt == 0)
        {
          arrive_flag = 0;
          shine_flag = 0;
          arrive_pre = 0;
        }
      }
    }
  }
}

三、附上工程

链接:https://pan.baidu.com/s/1Tsy7qyELnBX7Qz8xox-s2A 
提取码:m1s5

有不对的地方,请多指教...

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

对愁眠后霜满天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值