蓝桥杯嵌入式第三届省赛——“里程仪”旧板标准库

一、赛题分析

        “里程仪”具有即时速度、平均速度、行车时间、行驶里程显示及超速报警等功能。所用模块:LCD,LED,KEY,EEPROM,PWM输出,输入捕获。从这开始,博主会对每个用到的模块进行讲解。

二、程序设计

1、LED初始化

        初始化结构体,使能时钟,配置引脚初始化参数,LED初始化引脚,锁存器配置,锁存器初始化引脚,LED初始化灭。

#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"
#include "delay.h"

#define LED1    GPIO_Pin_8
#define LED2    GPIO_Pin_9
#define LED3    GPIO_Pin_10
#define LED4    GPIO_Pin_11
#define LED5    GPIO_Pin_12
#define LED6    GPIO_Pin_13
#define LED7    GPIO_Pin_14
#define LED8    GPIO_Pin_15
#define LEDALL	GPIO_Pin_All

//led
void LED_Init(void);

void LED_Control(u16 LEDx, u8 state);

#endif

#include "led.h"


void LED_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,  ENABLE);
	
  GPIO_InitStruct.GPIO_Pin = 0xff00;
  GPIO_InitStruct.GPIO_Speed =	GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	
  GPIO_Init(GPIOC, &GPIO_InitStruct);
	
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	
  GPIO_Init(GPIOD, &GPIO_InitStruct);
		
  GPIO_SetBits(GPIOD,GPIO_Pin_2);
  GPIO_SetBits(GPIOC,LEDALL);
  GPIO_ResetBits(GPIOD,GPIO_Pin_2);	
}


void LED_Control(u16 LEDx, u8 state)
{
  if(state == 1)
  {
    GPIO_SetBits(GPIOD,GPIO_Pin_2);
    GPIO_ResetBits(GPIOC,LEDx);
    GPIO_ResetBits(GPIOD,GPIO_Pin_2);		
  }
  else
  {
    GPIO_SetBits(GPIOD,GPIO_Pin_2);
    GPIO_SetBits(GPIOC,LEDx);
    GPIO_ResetBits(GPIOD,GPIO_Pin_2);	
  }		
}

 2、KEY初始化

        用正点原子的按键思想编写。

#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"

#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define KEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define KEY3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define KEY4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)

#define KEY1_VALUE 1
#define KEY2_VALUE 2
#define KEY3_VALUE 3
#define KEY4_VALUE 4

void KEY_Init(void);
u8 key_scan(void);

#endif
#include "key.h"

void KEY_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		
  GPIO_Init(GPIOA, &GPIO_InitStruct);
	
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		
  GPIO_Init(GPIOB, &GPIO_InitStruct);
}

u8 key_scan(void)
{
  static u8 key_up = 1;
  if(key_up&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))
  {
    key_up = 0;
    if(KEY1==0) return 1;
    else if(KEY2==0) return 2;
    else if(KEY3==0) return 3;
    else if(KEY4==0) return 4;
  }
  else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)   
    key_up=1;
  return 0;
}

3、IIC程序

        这里不贴全部代码,只贴i2c_read(),i2c_write().有一点要提醒,i2c_write()最好不要放在while(1)里面直写入,一直写入的话很大概率会出错。

unsigned char i2c_read(unsigned char add)
{
  unsigned char data;
	
  I2CStart();	
  I2CSendByte(0xa0);	
  I2CWaitAck(); 	
	
  I2CSendByte(add);	
  I2CWaitAck(); 
	
  I2CStart();
  I2CSendByte(0xa1); 
  I2CWaitAck();

  data = I2CReceiveByte(); 	
  I2CWaitAck();
  I2CStop();	
	
  return(data);	
}

void i2c_write(unsigned char add,unsigned char data)
{
  I2CStart(); 
	
  I2CSendByte(0xa0); 
  I2CWaitAck(); 
	
  I2CSendByte(add);	
  I2CWaitAck(); 
	
  I2CSendByte(data); 
  I2CWaitAck(); 
	
  I2CStop();
}

4、TIM4中断配置

        TIM定时时间计算方法在很多地方都能查到。此处定时1ms。

#ifndef __TIMER_H
#define __TIMER_H

#include "stm32f10x.h"

void TIM4_Init(void);

#endif

#include "timer.h"
#include "stm32f10x_tim.h"
#include "misc.h"

void TIM4_Init(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the TIM4 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);		
  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 1000 - 1;				
  TIM_TimeBaseStructure.TIM_Prescaler = 71;				
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
	
  /* TIM IT enable */
  TIM_ITConfig(TIM4, TIM_IT_Update , ENABLE);

  /* TIM4 enable counter */
  TIM_Cmd(TIM4, ENABLE);
} 

4、PWM输出配置

        要产生0-20HZ低频信号,用中断输出比较好。

#ifndef _PWM_H
#define _PWM_H

#include "stm32f10x.h"

void TIM3_PWM_Init(void);

extern uint16_t CCR1_Val;

#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 CCR1_Val = 1000;

void TIM3_PWM_Init(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
    
  uint16_t PrescalerValue = 0;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
  PrescalerValue = (uint16_t) (SystemCoreClock / 2000) - 1;
  /* Time base configuration */
  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: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);

  TIM_Cmd(TIM3, ENABLE);
  TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
}

void TIM3_IRQHandler(void)
{
  uint16_t capture = 0;
  /* TIM3_CH1 toggling with frequency = 1000/speed Hz */
  if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
    capture = TIM_GetCapture1(TIM3);
    TIM_SetCompare1(TIM3, capture + CCR1_Val );
  }
}

5、PWM输入捕获模块配置

#ifndef __PWM_CAPTURE_H
#define __PWM_CAPTURE_H

#include "stm32f10x.h"

extern uint16_t Time2_IC2ReadValue1;
extern uint16_t Time2_IC2ReadValue2;
extern uint8_t Time2_CaptureNumber;
extern uint32_t Time2_Capture;
extern uint32_t TIM2Freq;
extern uint32_t total_meter_now[2];
extern u8 x;

void Time2_InputCapture_Init(void);

#endif
#include "pwm_capture.h"
#include "misc.h"
#include "stm32f10x_tim.h"

uint16_t Time2_IC2ReadValue1 = 0;
uint16_t Time2_IC2ReadValue2 = 0;
uint8_t Time2_CaptureNumber = 0;
uint32_t Time2_Capture = 0;
uint32_t TIM2Freq = 0;
uint16_t Tim2_Update_Cnt = 0;
uint32_t total_meter_now[2] = {0,0}; //用于记录总里程。
u8 x;

void Time2_InputCapture_Init(void)
{
  TIM_ICInitTypeDef  TIM_ICInitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);

  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0x0;
  TIM_ICInit(TIM2, &TIM_ICInitStructure);
  
  TIM_Cmd(TIM2, ENABLE);
  TIM_ITConfig(TIM2, TIM_IT_CC2 | TIM_IT_Update, ENABLE);
}

void TIM2_IRQHandler(void)
{ 
  if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) 
  {
    /* Clear TIM2 Capture compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    Tim2_Update_Cnt++;
  }
  if(TIM_GetITStatus(TIM2, TIM_IT_CC2) == SET) 
  {
    total_meter_now[x]++;
    /* Clear TIM2 Capture compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    if(Time2_CaptureNumber == 0)
    {
      /* Get the Input Capture value */
	  Tim2_Update_Cnt = 0;
      Time2_IC2ReadValue1 = TIM_GetCapture2(TIM2);
      Time2_CaptureNumber = 1;
    }
    else if(Time2_CaptureNumber == 1)
    {
      /* Get the Input Capture value */
      Time2_IC2ReadValue2 = TIM_GetCapture2(TIM2); 
      
      Time2_Capture = ( Tim2_Update_Cnt * (0xFFFF) + Time2_IC2ReadValue2 - Time2_IC2ReadValue1);
      
      /* Frequency computation */ 
      TIM2Freq = (uint32_t) ((SystemCoreClock * 1.0 / Time2_Capture) + 0.5);
      Time2_CaptureNumber = 0;
    }
  }
}

6、主函数程序设计

        按照题目要求,比较简单。考虑到可能中途停车,所以设置了total_meter_now[2],T[2]用于记录停止时的行驶路程,和时间。这样,再次开始行驶时,就可以重新计算平均速度和开始计时。在存储总里程时,考虑到如果行驶总里程大于255的情况,用了两个存储单元,每满255公里进一,设计总里程小于65536。

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

float Vim = 0,Vavg = 0,S = 0;
u16 T[2] = {0,0},Total = 0,S_SAVE = 0;
u8 key_flag = 0,led_flag = 0,i2c_flag = 0;
u8 string[20];
u8 speed = 0;

void key_in(void);
void Velocity_range_analysis(void);
void display_content(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();
  i2c_init();
	
  TIM3_PWM_Init();
  Time2_InputCapture_Init();
  TIM_Cmd(TIM3, DISABLE);
//i2c_write(0x01,0);Delay_Ms(2); //此处存储内容清0
//i2c_write(0x02,0);Delay_Ms(2);
  S_SAVE = i2c_read(0x01)+i2c_read(0x02)*255;
  while(1)
  {
    key_in();
    Velocity_range_analysis();
    display_content();
    CCR1_Val = 1000/speed;
  }
}

void key_in(void)
{
  key_flag = key_scan();
  if(key_flag == 1&&speed<20)
  {
    speed++;
    if(speed == 1)TIM_Cmd(TIM3, ENABLE);
  }
  if(key_flag == 2&&speed>0)
  {
    speed--;
    if(speed==0)
    {
      TIM_Cmd(TIM3, DISABLE);
      TIM2Freq = 0;
      total_meter_now[!x]=total_meter_now[x];
      T[!x] = T[x];
    }
  }
}

void Velocity_range_analysis(void)
{
  Vim = speed * 2 * 3.6;
  if(speed == 0)
    Vavg = 0;
  else 
    Vavg = (total_meter_now[x]-total_meter_now[!x]) * 2 * 3.6 / (T[x]-T[!x]);
  S = total_meter_now[x] * 2/1000.0;
	
  LED_Control(LED1,led_flag);
  if(Vim<90)led_flag = 0;
	
  Total = S + S_SAVE;
	
  if(i2c_flag == 1)
  {
    i2c_write(0x01,Total%255);Delay_Ms(10);
    i2c_write(0x02,Total/255);Delay_Ms(10);
    i2c_flag = 0;
  }
}

void display_content(void)
{
  memset(string,0,sizeof(string));
  sprintf((char*)string,"  Vim(km/h):%.1f  ",Vim);
  LCD_DisplayStringLine(Line1,string);

  memset(string,0,sizeof(string));
  sprintf((char*)string,"  Vave(km/h):%.1f  ",Vavg);
  LCD_DisplayStringLine(Line3,string);

  memset(string,0,sizeof(string));
  sprintf((char*)string,"  T:%02d:%02d:%02d  ",T[x]/3600,T[x]%3600/60,T[x]%3600%60);
  LCD_DisplayStringLine(Line5,string);	

  memset(string,0,sizeof(string));
  sprintf((char*)string,"  S(km):%.1f  ",S);
  LCD_DisplayStringLine(Line7,string);
	
  memset(string,0,sizeof(string));
  sprintf((char*)string,"       Total(km):%d  ",Total);
  LCD_DisplayStringLine(Line9,string);	
}

void TIM4_IRQHandler(void)
{
  static u16 led_cnt = 0,sec_flag = 0,i2c_cnt = 0;
  if(TIM_GetITStatus(TIM4,TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    if(speed>0)
    {
      if(++sec_flag>=1000)
      {
        T[x]++;
        sec_flag = 0;
      }
    }
    if(Vim>90)
    {
      if(++led_cnt>=1000)
      {
        led_cnt = 0;
        led_flag =! led_flag;
      }
    }
    if(++i2c_cnt>=100)
    {
      i2c_cnt = 0;
      i2c_flag = 1;
    }
  }
}

三、附上工程

链接:https://pan.baidu.com/s/1Fo9ama--gpUXTVTIlqh_7Q 
提取码:2h90

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