第11届蓝桥杯嵌入式国赛代码及详解
一、前言
比赛已经过去三个月了,现在忙完毕设了,闲下来把当初比赛总结写一写,以后读研可能就玩STM32很少了,因为导师方向是雷达的,会多做些信号处理的内容了。在拿到省一后我才开始准备国赛,大概准备了不到一个月的时间,主要是去学习扩展板的模块内容,今年疫情种种原因加上自身发生了很多事导致几乎想要退赛,最后也是拿到了国二。STM32算是单片机中性价比最高的了,操作不难,但实现功能很强大,由于网上可参考的国赛资料太少,故做此篇希望可以帮到大家。废话不多说,直接素质三连:上图,上题,上代码!喜欢的麻烦大家点赞评论转发三连走起哈!
二、国赛试题
三、代码详解
1、模块化分析
国赛题使用的模块重点考查扩展板上的PWM波输出,捕获以及ADC的双通道产生电压值,还有一些简单的如温度传感器的使用,以及数码管的显示等。
本题考查
(1)扩展板ADC模拟输出两路电压
(2)双通道测量信号频率
(3)跟随PULS1或PULS2输出信号
(4)LED灯控制
(5)按键输入
(6)操作界面编写
2、模块初始化
拿到题目首先要确定使用到了哪些模块并对其进行初始化(注:初始化需要自己编写,比赛不会提供,所以需要大家大量练习)
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 0xff00;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOC, &GPIO_InitStructure);
GPIOC->ODR = 0xff00; //LED0~8初始化熄灭
GPIOD->ODR |= (1<<2);
GPIOD->ODR &=~(1<<2);
}
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitStructure);
}
void ADC1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_13Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_13Cycles5);
ADC_AutoInjectedConvCmd(ADC1,ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
/****************************************************
/*获取两个通道的电压值的功能函数不能公用一个函数,会出现电压最大到不了3.30V的BUG,
故写了两个函数用来使用
******************************************************/
unsigned int ADC1_Conv(void) //AO1的ADC电压
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
return ADC_GetConversionValue(ADC1);
}
unsigned int ADC1_InjConv(void) //AO2的ADC电压
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_JEOC));
ADC_ClearFlag(ADC1,ADC_FLAG_JEOC);
return ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_1);
}
void TIM3_OUTPUT(u16 Fre,u8 Duty,u8 status,u8 enable) //脉冲信号输出初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
if(status)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure);
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);
TIM_TimeBaseInitStructure.TIM_Period = 0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler = 71;
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseInitStructure.TIM_CounterMode = 0x0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
}
CH2_Val = 1000000 / Fre;
CH2_Duty = CH2_Val * Duty / 100;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
if(enable)
{
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
}else
{
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
}
TIM_OCInitStructure.TIM_Pulse = CH2_Val;
TIM_OC2Init( TIM3,&TIM_OCInitStructure);
TIM_SetCounter(TIM3, 0x0);
TIM_SetCompare2(TIM3,0x0);
if(status)
{
TIM_Cmd( TIM3, ENABLE);
TIM_ITConfig( TIM3, TIM_IT_CC2,ENABLE);
}
}
void PWM2_INPUT(void) //定时器测量内部输出频率初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_TimeBaseInitStructure.TIM_Period=0xffff;
TIM_TimeBaseInitStructure.TIM_Prescaler=71;
TIM_TimeBaseInitStructure.TIM_ClockDivision=0x0;
TIM_TimeBaseInitStructure.TIM_CounterMode=0x0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
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_ICInitStructure.TIM_Channel=TIM_Channel_3;
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_CC3,ENABLE);
}
3、中断函数
void TIM3_IRQHandler(void)
{
u16 capture;
if(TIM_GetITStatus(TIM3,TIM_IT_CC2) == 1)
{
TIM_ClearITPendingBit( TIM3, TIM_IT_CC2);
capture = TIM_GetCapture2( TIM3);
if(TIM3_CH2_Flag)
{
TIM_SetCompare2(TIM3,capture + CH2_Duty);
}else
{
TIM_SetCompare2(TIM3,capture + CH2_Val - CH2_Duty);
}
TIM3_CH2_Flag ^= 1;
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_CC2)==1)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);
if(capture_Flag)
{
switch(TIM2_CH2_CAPTURE_MODE)
{
case 0: TIM2_CH2_CAPTURE_H=0;
TIM2_CH2_CAPTURE_HL=0;
TIM2_CH2_CAPTURE_MODE=1;
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);
TIM_SetCounter(TIM2,0x0);
break;
case 1: TIM2_CH2_CAPTURE_H=TIM_GetCounter(TIM2);
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
TIM2_CH2_CAPTURE_MODE=2;
break;
case 2: TIM2_CH2_CAPTURE_HL=TIM_GetCounter(TIM2);
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
TIM2_CH2_CAPTURE_MODE=3;
break;
}
}else{
TIM2_CH2_CAPTURE_MODE=0;
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
}
}
if(TIM_GetITStatus(TIM2,TIM_IT_CC3)==1)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_CC3);
if(!capture_Flag)
{
switch(TIM2_CH3_CAPTURE_MODE)
{
case 0: TIM2_CH3_CAPTURE_H=0;
TIM2_CH3_CAPTURE_HL=0;
TIM2_CH3_CAPTURE_MODE=1;
TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Falling);
TIM_SetCounter(TIM2,0x0);
break;
case 1: TIM2_CH3_CAPTURE_H=TIM_GetCounter(TIM2);
TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising);
TIM2_CH3_CAPTURE_MODE=2;
break;
case 2: TIM2_CH3_CAPTURE_HL=TIM_GetCounter(TIM2);
TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising);
TIM2_CH3_CAPTURE_MODE=3;
break;
}
}else{
TIM2_CH3_CAPTURE_MODE=0;
TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising);
}
}
}
void SysTick_Handler(void)
{
static u8 Set_PWM_Num=0;
static u8 LED_Num=0;
static u8 KEY_Num=0;
static u8 ADC_Num=0;
static u8 Display_Num=0;
static u16 capture_Num=0;
TimingDelay--;
if(++LED_Num==200)
{
LED_Flag=1;
LED_Num=0;
}
if(++KEY_Num==50)
{
KEY_Flag=1;
KEY_Num=0;
}
if(++Display_Flag==100)
{
Display_Flag=1;
Display_Num=0;
}
if(++ADC_Num==100)
{
ADC_Flag=1;
ADC_Num=0;
}
if(++capture_Num==500)
{
capture_Flag^=1;
capture_Num=0;
}
if(++Set_PWM_Num==100)
{
Set_PWM_Flag=1;
Set_PWM_Num=0;
}
}
4、按键控制函数
void KEY_Read(void)
{
static u8 KEY1_Num=0;
static u8 KEY2_Num=0;
static u8 KEY3_Num=0;
static u8 KEY4_Num=0;
if(KEY1==0)
{
KEY1_Num++;
if(KEY1_Num==1)
{
Set_Flag^=1;
if(Set_Flag==0)
{
LED_Volt=LED_Volt_Vector;
LED_PWM=LED_PWM_Vector;
LED_MODE=0xff00;
}
LCD_ClearLine(Line1);
LCD_ClearLine(Line2);
LCD_ClearLine(Line3);
LCD_ClearLine(Line4);
LCD_ClearLine(Line5);
LCD_ClearLine(Line6);
LCD_ClearLine(Line7);
}
}else{
KEY1_Num=0;
}
if(KEY2==0)
{
KEY2_Num++;
if(KEY2_Num==1)
{
if(Set_Flag==1)
{
LED_Volt_Vector++;
if(LED_Volt_Vector>8)
{
LED_Volt_Vector=1;
}
if(LED_Volt_Vector==LED_PWM_Vector)
{
LED_Volt_Vector++;
if(LED_Volt_Vector>8)
{
LED_Volt_Vector=1;
}
}
}
}
}else{
KEY2_Num=0;
}
if(KEY3==0)
{
KEY3_Num++;
if(KEY3_Num==1)
{
if(Set_Flag==1)
{
LED_PWM_Vector++;
if(LED_PWM_Vector>8)
{
LED_PWM_Vector=1;
}
if(LED_PWM_Vector==LED_Volt_Vector)
{
LED_PWM_Vector++;
if(LED_PWM_Vector>8)
{
LED_PWM_Vector=1;
}
}
}
}
}else{
KEY3_Num=0;
}
if(KEY4==0)
{
KEY4_Num++;
if(KEY4_Num==1)
{
Follow_PLUS^=1;
}
}else{
KEY4_Num=0;
}
}
5、变量定义
#include "stm32f10x.h"
#include "lcd.h"
#include "stdio.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)
u32 TimingDelay = 0;
void Delay_Ms(u32 nTime);
void LED_Init(void);
void KEY_Init(void);
void KEY_Read(void);
void ADC1_Init(void);
void TIM3_OUTPUT(u16 Fre,u8 Duty,u8 status,u8 enable);
unsigned int ADC1_Conv(void);
unsigned int ADC1_InjConv(void);
void PWM2_INPUT(void);
void LED_Work(void);
/****定时器2变量******/
u8 capture_Flag=0;
u8 TIM2_CH2_CAPTURE_MODE=0;
u32 TIM2_CH2_CAPTURE_H=0;
u32 TIM2_CH2_CAPTURE_HL=0;
u8 TIM2_CH3_CAPTURE_MODE=0;
u32 TIM2_CH3_CAPTURE_H=0;
u32 TIM2_CH3_CAPTURE_HL=0;
/***定时器3变量*****/
u16 CH2_Val;
u16 CH2_Duty;
u8 TIM3_CH2_Flag=1;
/***系统中断函数标志变量****/
u8 LED_Flag=0;
u8 KEY_Flag=0;
u8 ADC_Flag=0;
u8 Display_Flag=0;
u8 Set_PWM_Flag=0;
/***界面标志变量****/
u8 Set_Flag;
u8 Change_Flag;
/****函数所用到的变量******/
u8 LED_Volt=1;
u8 LED_PWM=2;
u8 LED_Volt_Vector=1;//电压灯中间变量
u8 LED_PWM_Vector=2;//频率灯中间变量
u8 Follow_PLUS=0;
u8 string[20];
float AO1,AO2;
u16 TIM2_CH2_Fre;
u16 TIM2_CH3_Fre;
u16 LED_MODE=0xff00;
u16 Follow_Fre;
u16 TIM2_CH2_Fre_Last;
u16 TIM2_CH3_Fre_Last;
6、主函数
//Main Body
int main(void)
{
STM3210B_LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
SysTick_Config(SystemCoreClock/1000);
LED_Init();
KEY_Init();
ADC1_Init();
PWM2_INPUT();
TIM3_OUTPUT(1000,60,1,1);
while(1)
{
if(LED_Flag)
{
LED_Flag=0;
if(AO1>AO2)
{
LED_MODE&=~(1<<(LED_Volt+7));
}else{
LED_MODE|=(1<<(LED_Volt+7));
}
if(TIM2_CH2_Fre>TIM2_CH3_Fre)
{
LED_MODE&=~(1<<(LED_PWM+7));
}else{
LED_MODE|=(1<<(LED_PWM+7));
}
GPIOC->ODR = LED_MODE;
GPIOD->ODR |= (1<<2);
GPIOD->ODR &=~(1<<2);
}
if(KEY_Flag)
{
KEY_Flag=0;
KEY_Read();
}
if(ADC_Flag)
{
ADC_Flag=0;
AO1=(float)ADC1_Conv()*3.3/4095.0;
AO2=(float)ADC1_InjConv()*3.3/4095.0;
}
if(TIM2_CH2_CAPTURE_MODE==3)
{
TIM2_CH2_Fre=1000000/TIM2_CH2_CAPTURE_HL;//Hz
TIM2_CH2_CAPTURE_MODE=0;
}
if(TIM2_CH3_CAPTURE_MODE==3)
{
TIM2_CH3_Fre=1000000/TIM2_CH3_CAPTURE_HL;//Hz
TIM2_CH3_CAPTURE_MODE=0;
}
if(Display_Flag)
{
Display_Flag=0;
if(Set_Flag==0)
{
LCD_SetTextColor(White);
sprintf((char*)string," DATA ");
LCD_DisplayStringLine(Line2,string);
sprintf((char*)string," V1:%.1fV ",AO1);
LCD_DisplayStringLine(Line4,string);
sprintf((char*)string," V2:%.1fV ",AO2);
LCD_DisplayStringLine(Line5,string);
sprintf((char*)string," F1:%dHz ",TIM2_CH2_Fre);
LCD_DisplayStringLine(Line6,string);
sprintf((char*)string," F2:%dHz ",TIM2_CH3_Fre);
LCD_DisplayStringLine(Line7,string);
}else if(Set_Flag==1)
{
LCD_SetTextColor(White);
sprintf((char*)string," PARA ");
LCD_DisplayStringLine(Line2,string);
sprintf((char*)string," VD:LD%d ",LED_Volt_Vector);
LCD_DisplayStringLine(Line4,string);
sprintf((char*)string," FD:LD%d ",LED_PWM_Vector);
LCD_DisplayStringLine(Line5,string);
}
}
if(Set_PWM_Flag)
{
Set_PWM_Flag=0;
if(Follow_PLUS==0)
{
Follow_Fre=TIM2_CH2_Fre;
if(TIM2_CH2_Fre != TIM2_CH2_Fre_Last) //如果频率与历史值有变化,则重新设置,否则则跳过
{
TIM3_OUTPUT(Follow_Fre,50,0,1); //对响应的通道进行操作
TIM2_CH2_Fre_Last = TIM2_CH2_Fre; //保存作为历史值
}
}else if(Follow_PLUS==1)
{
Follow_Fre=TIM2_CH3_Fre;
if(TIM2_CH2_Fre != TIM2_CH2_Fre_Last) //如果频率与历史值有变化,则重新设置,否则则跳过
{
TIM3_OUTPUT(Follow_Fre,50,0,1); //对响应的通道进行操作
TIM2_CH3_Fre_Last = TIM2_CH3_Fre; //保存作为历史值
}
}
}
}
}
注:所有的代码都写在主函数中,所以变量就用如上定义的这些,因为比赛要上交代码文件,这样放在一起的好处是不会漏交文件,但在平时项目时模块化分开比较好。
四、完整题目代码
总结
总的来说,本次国赛程序题难度不大,当时比赛时候客观题做的很一般,比赛结束老师问我咋样时候,我说客观题做的好国一,做的不好国二,以后参加国赛的同学们记住,最后影响你拿国一的是客观题,因为能进国赛的同学编程题不会太差的。
有技术问题的可以私信我,也可以加我QQ:2395277840
喜欢的话点个赞支持一下哈!