title: 十一届蓝桥杯嵌入式省赛
date: 2021-12-04 15:49:03
tags: stm32
categories: 比赛项目
前言
由于本人乃菜鸡,而且这个项目只是我拿来给自己练习的,所以并没有想过做教学使用,大家可以参考一下,如果各位大佬垂阅,可以给出一下建议,我将不甚感激。
之前些32代码都是全部放到main.c里面,这次是第一次把主要的程序都放到其他.c中,然后调用,这样逻辑是会比较清晰的,而且以后和别人一起做项目,这样才能更好的分工和耦合,以及对接,但是我可能是因为第一次这样做吧,所以做的时候还是有点理不清,还需要进一步熟悉这种分模块的写法。
题目
分析
这一年的主观题还是比较简单的,主要难点在于如何发出两个PWM波,由于规定了PWM的输出引脚,所以如果使用定时器来产生PWM波,不太好搞,生成不一样频率的PWM,然而如果使用软件模拟PWM的话,就比较简单了。
代码
1.主函数模块
main.c
#include "stm32f10x.h"
#include "lcd.h"
#include "UI.h"
#include "key.h"
#include "stdio.h"
u32 TimingDelay = 0;
void Delay_Ms(u32 nTime);
extern int duty1,duty2;
int main(void)
{
SysTick_Config(SystemCoreClock/100000); //一次中断10us,1khz是1ms100个中断,2khz是200个中断
Key_init();
Led_init();
Led_all_close();
ADC_Config();
STM3210B_LCD_Init();
LCD_Clear(White);
LCD_SetBackColor(White);
LCD_SetTextColor(Black);
while(1){
Key_Set(); //按键控制PWM
UUII(); //LCD显示
}}
void Delay_Ms(u32 nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
2.显示和adc配置模块
adc.c
#include "lcd.h"
#include "UI.h"
#include "stdio.h"
#include "stm32f10x.h"
#include "key.h"
char read[9];
int ADC_Flag;
extern int flag_key_1;
extern int flag_key_4;
int Mode;
int UI_q; //切换数据显示或者参数显示
int duty1,duty2;
void UUII(){
if(flag_key_1%2 == 0){
if((flag_key_4%2 ==0)){
LCD_DisplayStringLine(Line0,(unsigned char *)" Data");
sprintf((char*)read," V: %.2fv",Read_ADC()*3.3/100);
LCD_DisplayStringLine(Line3,(unsigned char *)read);
LCD_DisplayStringLine(Line6,(unsigned char *)" Mode: AUTO");
duty1=(int)Read_ADC();
duty2=(int)Read_ADC(); //现在的adc输出的是真实电压的10/3.3倍,所以可以可以直接赋值
Led_all_close();
GPIO_ResetBits(GPIOC,GPIO_Pin_8);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
else{
LCD_DisplayStringLine(Line0,(unsigned char *)" Data");
sprintf((char*)read," V: %.2fv",Read_ADC()*3.3/100);
LCD_DisplayStringLine(Line3,(unsigned char *)read);
LCD_DisplayStringLine(Line6,(unsigned char *)" Mode: MANU");
Led_all_close();
GPIO_ResetBits(GPIOC,GPIO_Pin_9);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
UI_q=0;
}
else{
if((flag_key_4%2 ==0)){ //自动模式
duty1=(int)Read_ADC();
duty2=(int)Read_ADC();
}
else{ //手动模式
if(RB2 == KEY_ON){
LCD_Clear(White);
duty1=duty1+10;
if(duty1>90){duty1=0;}
}
if(RB3 == KEY_ON){
LCD_Clear(White);
duty2=duty2+10;
if(duty2>90){duty2=0;}
}
}
LCD_DisplayStringLine(Line0,(unsigned char *)" Para");
sprintf((char*)read," PA6:%d ",duty1);
LCD_DisplayStringLine(Line3,(unsigned char *)read);
sprintf((char*)read," PA7:%d ",duty2);
LCD_DisplayStringLine(Line6,(unsigned char *)read);
UI_q=1;}
if(UI_q == 0){
}
}
float Read_ADC(void)
{
float ADC_VALUE;
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
ADC_VALUE = ADC_GetConversionValue(ADC1)*100/0xfff;
return ADC_VALUE;
}
void ADC_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//PB0-ADC channel 8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// ADC1 工作模式配置
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_8, 1, ADC_SampleTime_13Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
}
3、按键&LED配置
#include "key.h"
#include "stm32f10x.h"
#include "lcd.h"
int flag_key_1;
int flag_key_4;
uint16_t Channel2Pulse = 0, Channel3Pulse = 0;
void Key_init(){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//B1、B2按键引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//B3、B4按键引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
uint16_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON)
{
while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
return KEY_ON;
}
else
return KEY_OFF;
}
void Key_Set(void){
if(RB1 == KEY_ON){
LCD_Clear(White);
flag_key_1++;
}
if(RB4 == KEY_ON){
LCD_Clear(White);
flag_key_4++;
}
}
void Led_init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//LED引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8| GPIO_Pin_9| GPIO_Pin_10| GPIO_Pin_11| GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//锁存器引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void Led_all_close(){
GPIO_SetBits(GPIOC,GPIO_Pin_8| GPIO_Pin_9| GPIO_Pin_10| GPIO_Pin_11| GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
4、systick中断模拟PWM
分频比
首先设置systick中断函数多久发送的周期,这个函数的形参数,是分频比
SysTick_Config(SystemCoreClock/100000);
中 断 频 率 = 主 频 率 / 分 频 比 中断频率=主频率/分频比 中断频率=主频率/分频比
$$
=SystemCoreClock / (SystemCoreClock/100000)
=100000HZ
T=10us
$$
这样题目需要的1KHZ,即周期为1ms的PWM,和走起为500us的PWM就都能得到了。
中断函数
#include "stm32f10x_it.h"
int cnt1,cnt2;
extern u32 TimingDelay;
extern int duty1,duty2;
void SysTick_Handler(void)
{ cnt1++;
cnt2++;
TimingDelay--;
if(cnt1<duty1){
GPIO_SetBits(GPIOA,GPIO_Pin_6);
}
if(cnt1>duty1){
GPIO_ResetBits(GPIOA,GPIO_Pin_6);
}
if(cnt1==100){
cnt1=0;}
if(cnt2<(duty2/2)){
GPIO_SetBits(GPIOA,GPIO_Pin_7);
}
if(cnt2>(duty2/2)){
GPIO_ResetBits(GPIOA,GPIO_Pin_7);
}
if(cnt2==50){
cnt2=0;}
}
结果
初始状态
波形
总结:
这次的题目还是比较简单的,难点就在与如何产生两个不同频率的PWM,如果只想到定时器产生PWM的话,由于题目规定的两个PWM的引脚是同一个定时器的两个端口,所以要设置成不同频率还是有点难度的,但如果软件模拟PWM,还是比较简单的。除此之外,这道题几乎没有难度,个人需要提升的点在于:需要行一步熟悉各个模块的配置过程。因为在正式比赛的时候是没有例程给你使用的。