文章目录
- 前言
- 一、CubeMX配置(第十三届模拟题完整版)
- 二、代码相关定义、声明
-
- 三、主要函数
- 1.按键扫描
- 2.配置模式
- 3.LCD显示
- 4.频率检测(TIM2输入捕获中断函数)
- 5.PWM输出(TIM3)
- 6.Main函数
- 四、实验结果
-
- 五、源码(转载请注明出处)
- 总结
前言
相关说明:
开发板:CT117E-M4(STM32G431RB 蓝桥杯嵌入式比赛板)
开发环境: CubeMX+Keil5
涉及题目:第十三届蓝桥杯嵌入式模拟题
题目难点:根据输入的PWM,实时更新输出PWM,R37电压值为0V时如何输出持续的低电平,电压为3.3V时如何输出持续的高电平。
CubeMX配置、主要函数代码及说明:
一、CubeMX配置(第十三届模拟题完整版)
1.使能外部高速时钟:
2.配置时钟树:
3.GPIO:
4.ADC(默认配置即可):
5.TIM2(输入捕获,检测输入信号的频率):
6.TIM3(PWM输出):
7.NVIC(输入捕获中断配置):
二、代码相关定义、声明
1.函数声明
main.c
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
void LCD_Init_Show(void);
void LCD_Refresh(void);
gpio.h
void KEY_Scan(void);
void LED_AllClose(uint8_t *LED_Close);
void LED_Change(void);
adc.h
double ADC_GetValue(void);
time.h
void PWM_Out(double R37_V,uint32_t FRQ,uint8_t R);
2.宏定义
#define LED_GPIO_PORT GPIOC
#define LED1_GPIO_PIN GPIO_PIN_8
#define LED2_GPIO_PIN GPIO_PIN_9
#define LED3_GPIO_PIN GPIO_PIN_10
#define LED4_GPIO_PIN GPIO_PIN_11
#define LED5_GPIO_PIN GPIO_PIN_12
#define LED6_GPIO_PIN GPIO_PIN_13
#define LED7_GPIO_PIN GPIO_PIN_14
#define LED8_GPIO_PIN GPIO_PIN_15
#define ON GPIO_PIN_RESET
#define OFF GPIO_PIN_SET
#define LED1(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED1_GPIO_PIN,a)
#define LED2(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED2_GPIO_PIN,a)
#define LED3(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED3_GPIO_PIN,a)
#define LED4(a) HAL_GPIO_WritePin(LED_GPIO_PORT,LED4_GPIO_PIN,a)
#define KEY1_GPIO_PORT GPIOB
#define KEY1_GPIO_PIN GPIO_PIN_0
#define KEY2_GPIO_PORT GPIOB
#define KEY2_GPIO_PIN GPIO_PIN_1
#define KEY3_GPIO_PORT GPIOB
#define KEY3_GPIO_PIN GPIO_PIN_2
#define KEY4_GPIO_PORT GPIOA
#define KEY4_GPIO_PIN GPIO_PIN_0
3.变量定义
char str[30];
uint32_t FRQ;
uint32_t TIM_Clock=1000000;
double R37_V;
uint8_t R=4;
uint8_t R_step=2;
uint8_t R_max=10;
uint8_t R_min=2;
uint8_t LED_Close[5]={0,0,1,0,0};
uint8_t Page=0;
三、主要函数
1.按键扫描
尽量将按键实现的功能封装为独立的函数,降低函数耦合度。
void KEY_Scan()
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
Page=1;
LCD_Refresh();
LED_Change();
LED_AllClose(LED_Close);
Setting_Mode();
}
}
else if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY4_GPIO_PORT,KEY4_GPIO_PIN)==GPIO_PIN_RESET);
LED_ban=!LED_ban;
if(LED_ban)
{
LED_Close[1]=1;
LED_Close[2]=1;
LED_Close[3]=1;
LED_Close[4]=1;
}
else
{
LED_Close[1]=0;
LED_Close[2]=0;
LED_Close[3]=0;
LED_Close[4]=0;
}
}
}
}
2.配置模式
用到两个函数对数据进行更改:
1.Dat_change(uint16_t mode),参数为数据更改方式(加/减)。
2.Setting_Mode(),按键按下后调用函数即可对数据进行更改操作,更改完后再更新显示。
void Dat_change(uint16_t mode)
{
switch(mode)
{
case ADD:
R+=R_step;
if(R>R_max)
R=R_max;
break;
case SUB:
R-=R_step;
if(R<R_min)
R=R_min;
break;
}
}
void Setting_Mode()
{
while(1)
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==GPIO_PIN_RESET);
Page=0;
LCD_Refresh();
break;
}
}
else if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==GPIO_PIN_RESET);
Dat_change(ADD);
LCD_Refresh();
}
}
else if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET)
{
while(HAL_GPIO_ReadPin(KEY3_GPIO_PORT,KEY3_GPIO_PIN)==GPIO_PIN_RESET);
Dat_change(SUB);
LCD_Refresh();
}
}
}
}
3.LCD显示
共有两个函数:
1.LCD_Init_Show(),在上电启动后对LCD进行初始化显示操作。
2.LCD_Refresh(),LCD更新显示,数据更新后需要实时进行更新显示。
void LCD_Init_Show()
{
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LCD_DisplayStringLine(Line1,(unsigned char*)" Data ");
sprintf(str," FRQ:%dHz ",FRQ);
LCD_DisplayStringLine(Line3,(unsigned char*)str);
sprintf(str," R37:%.2fV ",R37_V);
LCD_DisplayStringLine(Line5,(unsigned char*)str);
}
void LCD_Refresh()
{
if(Page==0)
{
LCD_DisplayStringLine(Line1,(unsigned char*)" Data ");
sprintf(str," FRQ:%dHz ",FRQ);
LCD_DisplayStringLine(Line3,(unsigned char*)str);
sprintf(str," R37:%.2fV ",R37_V);
LCD_DisplayStringLine(Line5,(unsigned char*)str);
}
else if(Page==1)
{
LCD_DisplayStringLine(Line1,(unsigned char*)" Para ");
sprintf(str," R:%d ",R);
LCD_DisplayStringLine(Line3,(unsigned char*)str);
LCD_DisplayStringLine(Line5,(unsigned char*)" ");
}
}
4.频率检测(TIM2输入捕获中断函数)
检测输入信号频率分为五步:
1.配置定时器相应通道功能为输入捕获并使能中断,上升沿捕获或下降沿捕获均可。
2.开启输入捕获中断:HAL_TIM_IC_Start_IT(&htimX,TIM_CHANNEL_X);
3.在进入中断函数后,获取定时器的计数值,该计数值/定时器时钟频率即为输入信号周期。
4.频率=1/周期,即频率是周期的倒数,则输入信号频率=定时器时钟频率/计数值。
5.计数值清零。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
uint32_t count;
count=TIM2->CNT;
FRQ=TIM_Clock/count;
TIM2->CNT=0;
}
5.PWM输出(TIM3)
PWM输出的代码看似很长,但起始有一大段是GPIO结构体的配置,该题配置PWM输出共分为四步:
1.根据R37电压值的不同,将PA7输出分为三种方式:a.电压值为0V;b.电压值为3.3V;c.0V<电压值<3.3V;ab两种情况对应持续的低电平和高电平,c对应PWM输出。
2.ab两种情况时先关闭PWM,并将GPIO引脚输出方式更改为通用推挽输出(否则PA7无法正常输出持续的高低电平),重新初始化GPIO后调用HAL_GPIO_WritePin即可正常输出。
3.c则根据输入信号频率以及R37电压值来配置PWM参数。首先要知道两条公式:
PWM输出频率=定时器时钟频率/重装载值。
占空比=Pulse/重装载值*100%。
现在知道的数据是输出信号频率(输入信号频率/R值)和占空比(R37电压值/3.3V),则可以得出:
重装载值=(定时器时钟频率/输出信号频率)-1。
Pulse=R37电压值/3.3V×重装载值。
4.根据计算得出的数值直接对寄存器进行配置:
TIM3->ARR=Period;TIM3->CCR2=Pulse;
最后再重启启动PWM即可。
void PWM_Out(double R37_V,uint32_t FRQ,uint8_t R)
{
uint32_t Period;
uint32_t Pulse;
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
if(R37_V<0.01)
{
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
GPIO_InitStruct.Mode =GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);
}
else if(R37_V>3.29)
{
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
GPIO_InitStruct.Mode =GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET);
}
else if(R37_V>0 && R37_V<3.3)
{
Period=TIM_Clock/(FRQ/R)-1;
Pulse=R37_V/3.3*Period-1;
TIM3->ARR=Period;
TIM3->CCR2=Pulse;
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
}
}
6.Main函数
注意起始时要将TIM2中断标志位清零:TIM2->SR=0;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC2_Init();
MX_TIM2_Init();
MX_TIM3_Init();
LCD_Init();
LCD_Init_Show();
TIM2->SR=0;
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
while (1)
{
KEY_Scan();
R37_V=ADC_GetValue();
LCD_Refresh();
LED_Change();
LED_AllClose(LED_Close);
PWM_Out(R37_V,FRQ,R);
FRQ=0;
}
}
四、实验结果
1.输入频率检测
此处用的是另一块开发板为该板提供信号,频率为1KHz;并调节R37使其电压为1.65V,预期占空比为1.65/3.3=50%。
2.R值
默认R值为4,作用为将输入信号进行分频处理;预期输出频率为1K/4=250Hz。
3.输出频率
这里直接用示波器检测输出PWM,频率为250Hz,占空比为50%,符合预期。
五、源码(转载请注明出处)
总结
以上就是全部内容,如有错误请批评指正。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)