一、题目分析
考察内容:
- led
- 按键(长按/短按)
- adc(模拟输入)
- PWM输出(PA1)
- 脉冲捕获(PA7)
根据PWM输出进行占空比调节,高频与低频切换(步进)
频率测量转换为速度值
三个显示界面
锁定解锁
任务多次执行时长间隔要求
二、按键部分
//放在定时器中断函数
if(htim->Instance == TIM6) //0.01s进入一次
{
key[0].key_sta = HAL_GPIO_ReadPin(B1_PORT,B1_PIN);
key[1].key_sta = HAL_GPIO_ReadPin(B2_PORT,B2_PIN);
key[2].key_sta = HAL_GPIO_ReadPin(B3_PORT,B3_PIN);
key[3].key_sta = HAL_GPIO_ReadPin(B4_PORT,B4_PIN);
for(uint8_t i = 0;i < 4;i ++){
switch(key[i].judge_sta){
case 0:{ //判断按键是否按下
if(key[i].key_sta == 0){
key[i].judge_sta = 1;
key[i].key_time = 0; //按键时间清零
}else{
key[i].judge_sta = 0;
}
}break;
case 1:{ //消抖
if(key[i].key_sta == 0){
key[i].judge_sta = 2;
}else{
key[i].judge_sta = 0;
}
}break;
case 2:{ //判断按键松开
if(key[i].key_sta == 1){
key[i].judge_sta = 0;
if(key[i].key_time < Short_time){
key[i].single_flag = 1; //短按键
key[i].key_time = 0;
}
}else{ //按键没松开
key[i].key_time++; //继续计时
if(key[i].key_time > Long_time){
key[i].long_flag = 1;
key[i].key_time = 0;
}
}
}break;
}
}
}
struct keys{
uint8_t judge_sta; //按键状态判断
uint8_t key_sta; //按键状态
uint8_t single_flag; //按键单击成功1
uint8_t long_flag; //按键长按成功1
uint16_t key_time; //按键按下时长记录
};
/*define-----------------------------------------------------------------------*/
#define B1_PORT GPIOB
#define B2_PORT GPIOB
#define B3_PORT GPIOB
#define B4_PORT GPIOA
#define B1_PIN GPIO_PIN_0
#define B2_PIN GPIO_PIN_1
#define B3_PIN GPIO_PIN_2
#define B4_PIN GPIO_PIN_0
/*define------------------------------------------------------------------------*/
#define Long_time 200 //200*0.1 = 1s //2s进入一次
#define Short_time 30 //单击在0.3s以内
/*include-----------------------------------------------------------------------*/
extern struct keys key[4];
三、LED灯部分
#include "led.h"
void led_set(uint8_t led_dis)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,led_dis<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void led_toggle(void)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_10,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_11,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_12,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_9);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
四、ADC部分
#include "base_adc.h"
double getADC_val(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Start(hadc);
uint32_t val = HAL_ADC_GetValue(hadc);
return val*3.3/4096;
}
五、输入捕获
读取自己捕获寄存器的值称为直接模式
读取另一个通道的捕获寄存器的值称为间接模式
PSC 80-1 得到1us 计数值不用改动
开启中断
/-----------------------------------------------------------------------------------------------------------------------------/
在STM32中,HAL_TIM_ACTIVE_CHANNEL_1通常是指定定时器(TIM)的第一个通道(Channel 1)处于活动状态。HAL是指STM32Cube库中的硬件抽象层(Hardware Abstraction Layer),它提供了一组API函数,用于与STM32微控制器的外设进行交互。TIM是定时器/计数器模块的缩写,它可以用于产生定时中断、测量脉冲宽度、以及生成PWM信号等应用。
HAL_TIM_ACTIVE_CHANNEL_1是在使用STM32Cube库时的一个宏定义,用于指示TIM的第一个通道是否处于活动状态。这通常用于轮询或中断处理程序中,以确定特定通道是否触发了事件。
//单通道频率测量--双通道占空比测量
uint16_t cnt_rising=0, cnt_falling=0;
uint16_t freq=0;
float IC_duty;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim -> Instance == TIM3)
{
if(htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
cnt_rising = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2)+1;
freq = (80000000/80) / cnt_rising;
IC_duty = ((float)cnt_falling/cnt_rising)*100;
}
if(htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
cnt_falling = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1)+1;
}
}
}
六、PWM输出
本题要求改变频率但不改变占空比,改变频率的时候改变PSC比较方便
改变频率
__HAL_TIM_SET_PRESCALER(&htim2,40-1+bujin_time);
改变占空比
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,P*0.01*1000 - 1);
七、定时器部分
定时器中断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
//定时器和中断记得在while(1)前面开启
锁定--解锁
任务执行间隔
以上两点我的处理方法是设置flag或者valid信号
if(htim->Instance == TIM7) //0.125s进入一次
{
if(bujin_flag == 1){
bujin_time ++;
if(M == L){
if(bujin_time == 40){
bujin_time = 0;
bujin_flag = 0;
__HAL_TIM_SET_PRESCALER(&htim2,80-1);
}else{
__HAL_TIM_SET_PRESCALER(&htim2,40-1+bujin_time);
}
}else if(M == H){
if(bujin_time == 40){
bujin_time = 0;
bujin_flag = 0;
__HAL_TIM_SET_PRESCALER(&htim2,40-1);
}else{
__HAL_TIM_SET_PRESCALER(&htim2,80-1-bujin_time);
}
}
}
}
if(htim->Instance == TIM17) //0.1s进入一次
{
static uint16_t i = 0;
static uint16_t j = 0;
if(bujin_flag == 1){
led_toggle();
}
if(sel_en_flag == 0)
{
i++;
if(i >= 490){
i = 0;
sel_en_flag = 1;
}
}
V_2s = V;
j++;
if(j>=19){ //2s_V 要求
j=0;
if(V_2s == V){
V_2s_valid = 1;
}else{
V_2s_valid = 0;
}
}else{
V_2s_valid = 0;
}
}
八、显示页面
初始化
void display_init(void)
{
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
}
显示页面
//阶段2--步进设置100hz -- 40次中断 5s内
//0.125s中断一次
void H_fre_set(void){
__HAL_TIM_SET_PRESCALER(&htim2,40-1);
}
void L_fre_set(void){
__HAL_TIM_SET_PRESCALER(&htim2,80-1);
}
void displaying(uint8_t menu)
{
uint8_t text[30];
if(menu == DATA_menu)
{
sprintf((char *)text," DATA ");
LCD_DisplayStringLine(Line2,text);
if(M == H){
sprintf((char *)text," M=H ");
// H_fre_set();
LCD_DisplayStringLine(Line4,text);
}else if(M == L){
sprintf((char *)text," M=L ");
// L_fre_set();
LCD_DisplayStringLine(Line4,text);
}
P = IC_duty;
sprintf((char *)text," P=%d%% ",P);
LCD_DisplayStringLine(Line5,text);
V = freq*2*3.14*R/(100.0*K);
sprintf((char *)text," V=%.1f ",V);
LCD_DisplayStringLine(Line6,text);
if(M ==H){
if(V_2s_valid){
if(V> MH) {
MH = V;
}
}
}else if(M ==L){
if(V_2s_valid){
ML = V;
}
}
}else if(menu == PARA_menu){
sprintf((char *)text," PARA ");
if(key[1].single_flag == 1){
key[1].single_flag = 0;
}
LCD_DisplayStringLine(Line2,text);
sprintf((char *)text," R=%d ",R);
LCD_DisplayStringLine(Line4,text);
sprintf((char *)text," K=%d ",K);
LCD_DisplayStringLine(Line5,text);
LCD_ClearLine(Line6);
}else if(menu == RECD_menu){
if(M ==H){
if(V_2s_valid){
if(V> MH) {
MH = V;
}
}
}else if(M ==L){
if(V_2s_valid){
ML = V;
}
}
sprintf((char *)text," RECD ");
LCD_DisplayStringLine(Line2,text);
sprintf((char *)text," N=%d ",N);
LCD_DisplayStringLine(Line4,text);
sprintf((char *)text," MH=%.1f ",MH);
LCD_DisplayStringLine(Line5,text);
sprintf((char *)text," ML=%.1f ",ML);
LCD_DisplayStringLine(Line6,text);
V = freq*2*3.14*R/(100.0*K); //R和K参数在其他页面生效
}
}
九、 总结
后面打算只写一个usr.c usr.h
如果自己新建工程的话,记得把配置,我最初按键测试的时候不行,发现是cubemx里没有分配引脚。
每个模块独立会导致extern很多变量——在比赛过程中时间耗费较长