第十一届蓝桥杯嵌入式省赛题
准备经历
从今年的寒假疫情在家开始准备蓝桥杯,一边写驱动程序,一边准备考研,后来驱动成熟了,开始做各届的模拟题省赛题。因为由于考研的原因,到大四就没有那么长时间准备了,所以当得知比赛推迟举办的时候,其实心里是不开心的。后来第一次省赛疫情原因学校没有组织比赛,就只能参加第二次省赛了。想想一路走来,虽然过程曲折,但是还是获得了比较满意的结果。最重要的是对STM32的理解更深了。
比赛感受
比赛还是五小时,从上午九点到下午两点,(比软件类多一小时,呜呜呜呜),总结一句话,编程两小时,仿真三小时。
比赛完,食堂都没饭了,为了不打扰舍友考研休息,我在阳台吃着泡面聊天搜着客观题答案,搜完觉得省一应该差不多了。编程题决定你获不获奖,客观题决定你能不能拿省一。废话不多说上题。
第十一届嵌入式省赛题
功能分析
本次省赛用到的模块不是很多,不过都是属于经典要考的内容。LED灯点亮,KEY按键控制,定时器双通道输出PWM,ADC模拟电压。以及界面显示等。总体来说初始化函数不难写。
源码分析
声明:为比赛上交文件方便原因,我把初始化程序和功能函数全写在Main.c里,就为了最后不会漏交文件,但这不是一个好习惯,在平时做项目时候建议大家文件模块化,条理更加清晰。
变量定义
#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;
//****定时器变量******//
u16 TIM3_CH1_Fre;
u16 TIM3_CH1_Duty;
u8 TIM3_CH1_Flag=0;
u16 TIM3_CH2_Fre;
u16 TIM3_CH2_Duty;
u8 TIM3_CH2_Flag=0;
//*******************//
void Delay_Ms(u32 nTime);
void LED_Init(void);
void KEY_Init(void);
void ADC1_Init(void);
void PWM3_OUT(u16 Frequency1,u8 Duty1,u16 Frequency2,u8 Duty2);
void KEY_Read(void);
float ADC_Read(void);
u8 zhankong1=10;//占空比10
u8 zhankong2=10;//占空比10
u8 biankong1=10;
u8 biankong2=10;
u8 Set_Flag=0;//界面跳转标志
u8 Control_Flag;//模式跳转标志
//******中断标志变量*********//
u8 KEY_Flag=0;
u8 ADC_Flag=0;
u8 Display_Flag;
u8 LED_Flag=0;
u8 Time_Flag=0;
//**************************//
int ADC_PWM;
float temp;//电压值
u8 string[20];
模块初始化
初始化很常规了,注意PWM使用比较输出。
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD,ENABLE);
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);
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);
GPIOC->ODR=0xff00;
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_GPIOB|RCC_APB2Periph_ADC1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
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_8, 1, ADC_SampleTime_55Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
float ADC_Read(void)
{
float temp;
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
Delay_Ms(5);
temp=ADC_GetConversionValue(ADC1)*3.3/0xfff;
return temp;
}
void PWM3_OUT(u16 Frequency1,u8 Duty1,u16 Frequency2,u8 Duty2)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
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);
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);
TIM3_CH1_Fre=1000000/Frequency1;
TIM3_CH1_Duty=TIM3_CH1_Fre*Duty1/100;
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_Pulse=TIM3_CH1_Fre;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC1Init(TIM3,&TIM_OCInitStructure);
TIM3_CH2_Fre=1000000/Frequency2;
TIM3_CH2_Duty=TIM3_CH2_Fre*Duty2/100;
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_Pulse=TIM3_CH2_Fre;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC2Init(TIM3,&TIM_OCInitStructure);
TIM_Cmd(TIM3,ENABLE);
TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2,ENABLE);
}
按键控制部分
这个按键程序进行了消抖处理。
void KEY_Read(void)
{
static u8 KEY1_Num=0,KEY2_Num=0,KEY3_Num=0,KEY4_Num=0;
if(KEY1==0)
{
KEY1_Num++;
if(KEY1_Num==1)
{
Set_Flag^=1;
LCD_ClearLine(Line1);
LCD_ClearLine(Line3);
LCD_ClearLine(Line5);
}
}else{
KEY1_Num=0;
}
if(KEY2==0)
{
KEY2_Num++;
if(KEY2_Num==1)
{
if(Set_Flag==1)
{
biankong1+=10;//PA6占空比手动模式+10
if(biankong1>90)
{
biankong1=10;
}
}
}
}else{
KEY2_Num=0;
}
if(KEY3==0)
{
KEY3_Num++;
if(KEY3_Num==1)
{
if(Set_Flag==1)
{
biankong2+=10;//PA7占空比手动模式+10
if(biankong2>90)
{
biankong2=10;
}
}
}
}else{
KEY3_Num=0;
}
if(KEY4==0)
{
KEY4_Num++;
if(KEY4_Num==1)
{
if(Control_Flag==0)
{
Control_Flag=1;
}else if(Control_Flag==1)
{
Control_Flag=0;
}
}
}else{
KEY4_Num=0;
}
}
中断函数
#include "stm32f10x_it.h"
extern u32 TimingDelay;
extern u8 KEY_Flag;
extern u8 ADC_Flag;
extern u8 Display_Flag;
extern u8 LED_Flag;
extern u8 Time_Flag;
void SysTick_Handler(void)
{
static u8 Key_Num=0;
static u16 ADC_Num=0;
static u8 Display_Num=0;
static u16 LED_Num=0;
static u16 Time_Num=0;
TimingDelay--;
if(++Key_Num==50)
{
Key_Num=0;
KEY_Flag=1;
}
if(++ADC_Num==50)
{
ADC_Num=0;
ADC_Flag=1;
}
if(++Display_Num==50)
{
Display_Num=0;
Display_Flag=1;
}
if(++LED_Num==1000)
{
LED_Num=900;
LED_Flag=1;
}
if(++Time_Num==500)
{
Time_Num=0;
Time_Flag=1;
}
}
void TIM3_IRQHandler(void)
{
u16 capture1;
u16 capture2;
if(TIM_GetITStatus(TIM3,TIM_IT_CC1)==1)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
capture1=TIM_GetCapture1(TIM3);
if(TIM3_CH1_Flag==1)
{
TIM_SetCompare1(TIM3,capture1+TIM3_CH1_Duty);
}
if(TIM3_CH1_Flag==0)
{
TIM_SetCompare1(TIM3,capture1+TIM3_CH1_Fre-TIM3_CH1_Duty);
}
TIM3_CH1_Flag^=1;
}
if(TIM_GetITStatus(TIM3,TIM_IT_CC2)==1)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
capture2=TIM_GetCapture2(TIM3);
if(TIM3_CH2_Flag==1)
{
TIM_SetCompare2(TIM3,capture2+TIM3_CH2_Duty);
}
if(TIM3_CH2_Flag==0)
{
TIM_SetCompare2(TIM3,capture2+TIM3_CH2_Fre-TIM3_CH2_Duty);
}
TIM3_CH2_Flag^=1;
}
}
主程序
//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();
PWM3_OUT(10000,zhankong1,5000,zhankong2);
while(1)
{
if(Display_Flag)
{
if(Set_Flag==0)
{
sprintf((char*)string," Data");
LCD_DisplayStringLine(Line1,string);
sprintf((char*)string," V:%.2fV",temp);
LCD_DisplayStringLine(Line3,string);
if(Control_Flag==0)
{
sprintf((char*)string," Mode:AUTO");
}else if(Control_Flag==1)
{
sprintf((char*)string," Mode:MANU");
}
LCD_DisplayStringLine(Line5,string);
}
if(Set_Flag==1)
{
sprintf((char*)string," Para");
LCD_DisplayStringLine(Line1,string);
sprintf((char*)string," PA6:%d%%",biankong1);
LCD_DisplayStringLine(Line3,string);
sprintf((char*)string," PA7:%d%%",biankong2);
LCD_DisplayStringLine(Line5,string);
}
}
if(ADC_Flag)
{
ADC_Flag=0;
temp=ADC_Read();
ADC_PWM=temp/3.3*100; // 得到自动模式下的PWM占空比
}
if(Time_Flag)
{
Time_Flag=0;
if(Control_Flag==0)
{
PWM3_OUT(10000,ADC_PWM,5000,ADC_PWM);
if(temp==3.30)
{
PWM3_OUT(10000,100,5000,100);
}
if(temp==0)
{
PWM3_OUT(10000,0,5000,0);
}
}else if(Control_Flag==1)
{
PWM3_OUT(10000,biankong1,5000,biankong2);
}
}
if(KEY_Flag)
{
KEY_Flag=0;
KEY_Read();
}
if(LED_Flag)
{
LED_Flag=0;
if(Control_Flag==0)
{
GPIOC->ODR^=(1<<8);
GPIOC->ODR|=(1<<9);
GPIOD->ODR|=(1<<2);
GPIOD->ODR&=~(1<<2);
}else if(Control_Flag==1)
{
GPIOC->ODR|=(1<<8);
GPIOC->ODR^=(1<<9);
GPIOD->ODR|=(1<<2);
GPIOD->ODR&=~(1<<2);
}
}
}
}
赛后总结
总的来说本次省赛题目考的模块相对来说不难,没有考到I2C问题,也没考到输入捕获。具体I2C细节处理请看我的另一篇文章I2C细节设置。所以想拿奖不难,想拿省一的小伙伴们需要加油把客观题练好叭。最后特别感谢一位CSDN博主嵌入式飞哥感谢他写的那么棒的博客,清晰地代码风格帮助了我蓝桥杯比赛的学习。