蓝桥杯嵌入式第三届省赛——“里程仪”旧板标准库
一、赛题分析
“里程仪”具有即时速度、平均速度、行车时间、行驶里程显示及超速报警等功能。所用模块: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
有不对的地方,请多指教...