目录

0 题目介绍

1 题目分析

2 Cubemx配置

4 代码

5 效果显示

 

0 题目介绍

具体要求如下图

 

 

 

1 题目分析

拿到题目咋一看,就是基本操作,实际做起来一堆定时器操作,很容易把人绕晕。

首先看看需要用到的外设

1. GPIO(key/led)

2.LCD

3.输入捕获(TIM3通道2)和PWM(TIM2通道2)

4.ADC(R37)

这里首先得有个概念,碰到定时之类的,应该想到标志位+计数器的组合,用标志位触发定时器计数,把开始的过程和最终执行的过程分离开来,减少代码耦合。

2 Cubemx配置

配置完成如下:

 

时钟树配置如下(确保SYSCLK=80M)

GPIO配置过(LED设置INPUT初始状态高,PD12使能位INPUT,KEY设置位OUTPUT) 

ADC配置如下

 

定时器2  TIM2通道二(PWM输出配置PA1)这里80M预分频79后为1M,初始阶段频率1M/(999+1)=1Khz(题目要求4000,后面代码里具体设置)

 

 定时器3 TIM3通道二(输入捕获输出配置PA7)

这里简单介绍一下:触发源选TI2FP2(因为PA7是TIM3_CH2),选这个后PA7就会变绿,时钟选内部时钟(80M),通道二(Channel)作为主通道(PA7对的是通道二),通道一作为从通道。

主通道(通道2)检测上升沿,从通道(通道1)检测下降沿(计算频率只需要计算上升沿的捕获值,计算占空比根据上升沿和下降沿的比例关系计算)。

预分频还是79(+1)后到1M。

 

 tim2中断打开(整个题目唯一用到的中断)

 生成文件

 

 移植lcd相关文件头文件,创建一个user.c和user.h(个人习惯)放一些功能代码,主要代码全写在main.c文件中(如果缺少.s文件,请自行添加)

 编译保证不报错。

4 代码

自己需要写的文件只有两个

1. user.c和user.h

2.main.c

user文件如下,这个没啥好说的

user.h:

#include "main.h"

void Led_Disp(unsigned char c);

unsigned char Key_Scan(void);

uint16_t getADC2(void);

user.c:

#include "user.h"

#include "adc.h"

//灯

void Led_Disp(unsigned char c)

{

//全部熄灭

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8

|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);

HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);

HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);

//点亮对应位

HAL_GPIO_WritePin(GPIOC, c<<8, GPIO_PIN_RESET);

HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);

HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);

}

//按键扫描

unsigned char Key_Scan(void)

{

unsigned char c;

if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)

{

c = 1;

}

else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)

{

c = 2;

}

else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)

{

c = 3;

}

else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)

{

c = 4;

}

return c;

}

//adc

uint16_t getADC2(void)

{

uint16_t adc = 0;

HAL_ADC_Start(&hadc2);

adc = HAL_ADC_GetValue(&hadc2);

return adc;

}

主函数这里使用了一个100ms执行一次的计数器和50ms执行一次的计数器,100ms主要控制那个灯闪烁的需求,其他定时都放在50ms函数中。

可以根据测试数据来观察定时器的作用

 timer_b2是按键2相关的计时器, timer_b4是按键4相关的计时器,timer_MH是题目那个保持2s需求的计时器

flag是对应的标志位,具体功能见注释

屏幕第九行是adc电压值和cap采集的频率

main.c

#include "main.h"

#include "adc.h"

#include "tim.h"

#include "gpio.h"

#include "lcd.h"

#include "user.h"

#include

void keyPro(void);

void lcdPro(void);

void ledPro(void);

void ADCPro(void);

void timer_100(void);

void timer_50(void);

//tim2 pwm

//tim3 cap

__IO uint32_t uwtick_Key,uwtick_Lcd,uwtick_Led,uwtick_ADC,uwtick_timer_200,uwtick_timer_100,uwtick_timer_50;

//key

unsigned char Key_Value,Key_Old,Key_Up,Key_Dowm;

unsigned char page = 1;

//lcd

unsigned char str[25];

unsigned char test;

//*pwm相关变量

unsigned char PWM_Mode = 0;

unsigned char PWM_p = 0;

float V = 10; //V值

float V_old = 10; //上一次的V值,用于判断V是否稳定

uint16_t CAP1_UP_Count; //上升沿捕获值

uint16_t CAP1_DOWM_Count; //下降沿捕获值

float CAP1_Duty; //捕获占空比

int32_t PWM1_Duty = 10; //PWM输出占空比控制

int32_t PWM1_F = 249; //PWM输出频率控制

unsigned char R = 1; //R

unsigned char K = 1; //K

unsigned char N = 0; //N

float MH = 10; //低频最大值

float ML = 10; //高频最大值

unsigned char select_R_K = 0; //选择R K

unsigned char lock_b4 = 0; //ADC上锁

float adc_value = 0; //ADC值

//timer

int timer_b2 = 0;

int timer_b4 = 0;

int timer_MH = 0;

int timer_ML = 0;

//标志位

unsigned flag_b2 = 0; //1代表正在运行中

int flag_b4 = 0; //0 没有按下 1上升沿 2下降沿

int flag_MHL = 0; //0没超过 1低超过 2高超过

void SystemClock_Config(void);

int main(void)

{

HAL_Init();

SystemClock_Config();

LCD_Init();

MX_GPIO_Init();

MX_ADC2_Init();

MX_TIM2_Init();

MX_TIM3_Init();

//cap

HAL_TIM_Base_Start(&htim3);

HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);

HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);

//pwm

HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

__HAL_TIM_SET_AUTORELOAD(&htim2,124); //4k=249 8k=124

//__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,124/10);

LCD_Clear(Black);

LCD_SetBackColor(Black);

LCD_SetTextColor(White);

while (1)

{

ADCPro();

lcdPro();

keyPro();

ledPro();

timer_100();//灯闪烁时用

timer_50();

}

}

void lcdPro(void)

{

if(uwTick - uwtick_Lcd<100) return;

uwtick_Lcd = uwTick;

if(page == 1) //数据界面

{

sprintf((char *)str," DATA ");

LCD_DisplayStringLine(Line1,str);

if(PWM_Mode == 0)

{

sprintf((char *)str," M=L ");

}

else

{

sprintf((char *)str," M=H ");

}

PWM_p = PWM1_Duty;

LCD_DisplayStringLine(Line3,str);

sprintf((char *)str," P=%02d%% ",PWM_p);

LCD_DisplayStringLine(Line4,str);

sprintf((char *)str," V=%3.1f ",V);

LCD_DisplayStringLine(Line5,str);

}

else if(page == 2) //参数界面

{

sprintf((char *)str," PARA ");

LCD_DisplayStringLine(Line1,str);

if(select_R_K == 0)

{

LCD_SetTextColor(Green);

}

sprintf((char *)str," R=%d ",R);

LCD_DisplayStringLine(Line3,str);

LCD_SetTextColor(White); //恢复

if(select_R_K == 1)

{

LCD_SetTextColor(Green);

}

sprintf((char *)str," K=%d ",K);

LCD_DisplayStringLine(Line4,str);

LCD_SetTextColor(White); //恢复

}

else if (page == 3) //统计界面

{

sprintf((char *)str," RECD ");

LCD_DisplayStringLine(Line1,str);

sprintf((char *)str," N=%d ",N);

LCD_DisplayStringLine(Line3,str);

sprintf((char *)str," MH=%4.1f ",MH);

LCD_DisplayStringLine(Line4,str);

sprintf((char *)str," ML=%4.1f ",ML);

LCD_DisplayStringLine(Line5,str);

}

//测试用 提交时屏蔽

sprintf((char *)str,"t1:%dt2:%dt3:%dt4:%d ",timer_b2,timer_b4,timer_ML,timer_MH);

LCD_DisplayStringLine(Line7,str);

sprintf((char *)str,"b2:%d b4:%d MHL:%d ",flag_b2,flag_b4,flag_MHL);

LCD_DisplayStringLine(Line8,str);

//adc

sprintf((char *)str, "R37:%4.2fV PWM:%d ",adc_value,1000000/CAP1_UP_Count);

LCD_DisplayStringLine(Line9, str);

}

void keyPro(void)

{

if(uwTick - uwtick_Key<50) return;

uwtick_Key = uwTick;

Key_Value = Key_Scan();

Key_Dowm = Key_Value&(Key_Value^Key_Old);

Key_Up = ~Key_Value&(Key_Value^Key_Old);

if(Key_Value==4&&Key_Old==4&&page==1)

{

flag_b4 = 1;

}

else if(Key_Value!=4&&Key_Old==4&&page==1)

{

flag_b4 = 2;

}

else

{

flag_b4 = 0;

}

Key_Old = Key_Value;

if(Key_Dowm == 1)

{

LCD_Clear(Black);

if(page == 1)

{

page = 2;

}

else if(page == 2)

{

page = 3;

}

else

{

page = 1;

}

}

else if(Key_Dowm == 2)

{

if(page == 1) //数据界面

{

if(flag_b2 == 0)

{

flag_b2 =1;

}

}

if(page == 2) //参数界面

{

if(select_R_K == 0)

{

select_R_K = 1;

}

else

{

select_R_K = 0;

}

}

}

else if(Key_Dowm == 3)

{

if(page == 2)

{

if(select_R_K == 0) //R++

{

R++;

if(R>10)

{

R=1;

}

}

else //K++

{

K++;

if(K>10)

{

K=1;

}

}

}

}

else if(Key_Dowm == 4)

{

if(page == 2)

{

if(select_R_K == 0) //R--

{

R--;

if(R==0)

{

R=10;

}

}

else //K--

{

K--;

if(K==0)

{

K=10;

}

}

}

}

}

//定时相关处理 50ms/次

void timer_50(void)

{

if(uwTick - uwtick_timer_50<50) return;

uwtick_timer_50 = uwTick;

//时间累加

if(flag_b4==1)

{

timer_b4+=50;

}

if(flag_b2 == 1)

{

timer_b2+=50;

if(PWM_Mode ==1)//4000-8000对应249-124 125/5次

{

PWM1_F += 2;

if(PWM1_F>249)

{

PWM1_F = 249;

}

}

else

{

PWM1_F -= 2;

if(PWM1_F<124)

{

PWM1_F=124;

}

}

}

if(flag_MHL == 1)

{

timer_ML+=50;

}

else if(flag_MHL == 2)

{

timer_MH+=50;

}

else

{

timer_ML = 0;

timer_MH = 0;

}

//判断执行

//b4长短按2s

if(flag_b4==2)

{

if(page==1&&timer_b4>2000)//长按

{

if(lock_b4 == 0)

{

lock_b4 = 1;

}

}

else //短按

{

if(page==1&&lock_b4 == 1)

{

lock_b4 = 0;

}

}

}

else if(flag_b4==0)

{

timer_b4 = 0;

}

//b2记时5s PWM高低模式切换

if(flag_b2 == 1&&timer_b2>5000)

{

flag_b2 = 0;

timer_b2 = 0;

N++;

if(PWM_Mode == 0)

{

PWM_Mode = 1;

}

else

{

PWM_Mode = 0;

}

}

//MH2s

if(timer_MH>2000)//低频最大值

{

MH = V;

flag_MHL = 0;

}

else if(timer_ML>2000)

{

ML = V;

flag_MHL = 0;

}

}

unsigned char lednum;

void ledPro(void)

{

if(uwTick - uwtick_Led<100) return;

uwtick_Led = uwTick;

if(page == 1)

{

lednum|=0x1;

}

else

{

lednum&=0xfe;

}

if(lock_b4 == 1)

{

lednum|=0x04;

}

else

{

lednum&=0xfb;

}

Led_Disp(lednum);

}

unsigned char led2flag; //闪烁标志

void timer_100(void)

{

if(uwTick - uwtick_timer_100<100) return;

uwtick_timer_100 = uwTick;

if(timer_b2>0) //闪烁

{

if(led2flag ==0)

{

led2flag = 1;

lednum |=0x2;

}

else if(led2flag == 1)

{

led2flag = 0;

lednum &=0xfd;

}

}

else

{

lednum &=0xfd;

}

}

void ADCPro(void)

{

if(uwTick - uwtick_ADC<150) return;

uwtick_ADC = uwTick;

adc_value = getADC2()*3.3/4096;

//ad转换成占空比

if(lock_b4 ==0)

{

if(adc_value<1.0)

{

PWM1_Duty = 10;

}

else if(adc_value>3.0)

{

PWM1_Duty = 85;

}

else

{

PWM1_Duty = 10+ (adc_value-1)*75/2;

}

}

V = (1000000/CAP1_UP_Count)*2*3.14*R/100/K;

//需要判断V值是否保持

if(V==V_old&&PWM_Mode==0&&V>ML) //低频下

{

flag_MHL = 1;

}

else if(V==V_old&&PWM_Mode==1&&V>MH) //高频下

{

flag_MHL = 2;

}

else

{

flag_MHL = 0;

}

V_old = V;

//频率

__HAL_TIM_SET_AUTORELOAD(&htim2,PWM1_F); //4k

//占空比

__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,PWM1_F*PWM1_Duty/100);

}

//中断回调函数

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)

{

if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)

{

CAP1_UP_Count = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;

CAP1_Duty = (float)CAP1_DOWM_Count/CAP1_UP_Count;

}

else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)

{

CAP1_DOWM_Count = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;

}

}

void SystemClock_Config(void)

{

RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

/** Configure the main internal regulator output voltage

*/

HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

/** Initializes the RCC Oscillators according to the specified parameters

* in the RCC_OscInitTypeDef structure.

*/

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

RCC_OscInitStruct.HSEState = RCC_HSE_ON;

RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;

RCC_OscInitStruct.PLL.PLLN = 20;

RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;

RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;

RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

{

Error_Handler();

}

/** Initializes the CPU, AHB and APB buses clocks

*/

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

{

Error_Handler();

}

/** Initializes the peripherals clocks

*/

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;

PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;

if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)

{

Error_Handler();

}

}

void Error_Handler(void)

{

/* USER CODE BEGIN Error_Handler_Debug */

/* User can add his own implementation to report the HAL error return state */

__disable_irq();

while (1)

{

}

/* USER CODE END Error_Handler_Debug */

}

#ifdef USE_FULL_ASSERT

/**

* @brief Reports the name of the source file and the source line number

* where the assert_param error has occurred.

* @param file: pointer to the source file name

* @param line: assert_param error line source number

* @retval None

*/

void assert_failed(uint8_t *file, uint32_t line)

{

/* USER CODE BEGIN 6 */

/* User can add his own implementation to report the file name and line number,

ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

/* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

5 效果显示

抖音同名

 

 

 

 

 

 

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: