第十四届蓝桥杯嵌入式省赛

 

led模块:

查看原理图可知,该led模块由PD2所控制,当PD2为高电平时,led模块才能够启用,在这个时候输入是有效的;当为低电平时,led模块会锁住当前PC口的值。所以在你要控制led灯时,就需要操控PD2。

点击pc8

选择输出模式

同样的操作配置好其他所有PC口和PD2口,如图:

这个user label 是自己定义的名字,STM32CubeMX会根据你定义的名字来宏定义这些端口。


void led_off(void)
{
	HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED8_GPIO_Port,LED8_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

void led_control(void)
{
	if(dis_flag == 0){
		HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET);
	}
	else{
		HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
	}
	if(led_flag == 1 && cishu != 0){
		HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET);
	}
	else{
		HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
	}
	if(lock == 1){
		HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET);
	}
	else{
		HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
	}
	HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(LED8_GPIO_Port,LED8_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

需要注意的是在操作led时要先写好端口的值,然后操控开关PD2,这一步骤必须连续,否则回合lcd显示屏模块冲突,因为他们都是用的pc口。

led每个都有每个的要求比如0.1毫秒的闪烁就用定时器来操控,十分的简单。

pwm模块:

选择定时器2通道口2

将通道二选择为pwm输出模式我的主频是80MHZ 就是 80 000 000HZ

题目说用PA1来实现pwm输出。重要的设置点是分频系数和重装载的值。主频除以(分频系数和重装载的值)后要等于
Pwm输出的频率,占空比是指对于高电平的时间和重装载值的比例。重装载值是200,占空比是50%的话就得填100,这里填了进入程序还是会改的,占空比随便是指就行


TIM_OC_InitTypeDef sConfigOC = {0};

//在主程序中初始化加入:
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);//启动pwm
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 100;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
//复制的tim.c文件中的


void pwm_deal(void)
{    
    if(reload_flag == 1){  //处于频率均匀变化期间设置重装载值  
        reload_flag = 0;
        htim2.Init.Period = reload - 1; //重装载值设置
        if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) //设置
        {
            Error_Handler();
        }
    }
    if((int)P!=(int)old_P && lock == 0){
        old_P = P;
        sConfigOC.Pulse = (int)((P/100.0)*reload - 1); //占空比设置
        if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) //设置
        {
            Error_Handler();
        }
    }

}


还有重装载是指不要一直设置。那样会到时平时测量有误,没事的时候就不要一直设置了。占空比设置的值改成int。

输入捕获:

将PA7设置为TIM17的通道1。选择为输入捕获模式。主频设置为 1MHZ这里很重要影响到后续计算。不要忘了打开定时器中断。

// 定时器的回调函数
//主函数内

HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	// 保存TIMx_CCR的值
	uint32_t  cclValue = 0;
	// 定时器3时执行该段
	if(htim->Instance == TIM17)
	{
		cclValue = __HAL_TIM_GET_COUNTER(&htim17);
		__HAL_TIM_SetCounter(&htim17, 0);
		f = 1000000 / cclValue;
		HAL_TIM_IC_Start_IT(&htim17, TIM_CHANNEL_1);
	}
}

定时器中断触发后会调用定时器中断函数。每次进入中断后会读取计数器的值,每记一次都代表了pwm的一个周期。在记完后,用主频频率除以计数值就可以算出被测量频率的值。当然每次进入中断后都要清空计数器的值和重新启动输入捕获装置。

测量出来的频率要进行数据处理,得到速度V.

if(dis_flag != 1){
	V = f * 2 * 3.14 * R /(K * 100.0);
	old_V = V;
	if(reload == 100 ){
		if(MH<V){
			if(max_cishu >= 20){
				MH = V;
				max_cishu = 0;
			}
		}
	}			
	else if(reload == 200){
		if(ML>V){
			if(max_cishu >= 20){
				ML = V;
				max_cishu = 0;
			}
		}
	}
    if((int)old_V != (int)V && (reload == 100 || reload == 200)){
	    max_cishu = 0;
	}
}

adc模块:

这里要选择两个,分别就是这两个,点击PB15选两次就可以了。

记得来到adc2里面激活15输入口,其他的都不用改。


void adc(void)
{
	P = getadc(&hadc2);
	P *= 37.5; 
	if(P < 10.0){
		P = 10.0;
	}
	else if(P > 85.0){
		P = 85.0;
	}
	if(old_P_flag == 1){
		old_P_flag = 0;
		old_P = P;
	}
}


double getadc(ADC_HandleTypeDef *pin)
{
	unsigned int value = 0,i = 0;
	HAL_ADC_Start(pin);
	for(i=0;i<10;++i)
	{
		HAL_ADC_PollForConversion(pin,10);
		value += HAL_ADC_GetValue(pin);
	}
	return value/10*3.3/4096;
}

连续取十次取平均值。因为adc的值是12位的,也就是2^12=4096,取出来的值和他计算出比例,最终算出之后的电压值。按照题目给出的比例计算出响应的占空比值。放到pwm里面设置。

按键模块

利用状态机来做判断并且消抖。题目还要求有长按设计,改一改结构体就好了,计时功能的话就用定时器,我是喜欢用0.1S的定时器每次,记数超过20就是按了2s。按键结构体最好是每个都搞一个长按标志和长按计时的数。这里我只改了一个。代码如下,第二个状态就是用来消抖的。第三个状态松开时就判断按下。

key.h

#ifndef __KEY_H
#define __KEY_H

struct keys
{
	unsigned char judge_sta; //几次状态转换判断
	char key_sta; //扫描端口时用来存放端口值
	char single_flag; //单词按下
	char long_single_flag; //多次按下
}; 

void key_scan(void);

#endif

key.c

#include "key.h"
#include "main.h"

unsigned char long_flag = 0;
int long_key_cishu = 0;
struct keys key[4] = {0,0,0,0};
//只含单击
void key_scan(void)
{
	key[0].key_sta = HAL_GPIO_ReadPin(key1_GPIO_Port,key1_Pin);
	key[1].key_sta = HAL_GPIO_ReadPin(key2_GPIO_Port,key2_Pin);
	key[2].key_sta = HAL_GPIO_ReadPin(key3_GPIO_Port,key3_Pin);
	key[3].key_sta = HAL_GPIO_ReadPin(key4_GPIO_Port,key4_Pin);
	for(int i=0; i < 3; i++){
		switch(key[i].judge_sta){
			case 0:
				if(key[i].key_sta == 0){
					key[i].judge_sta = 1;
				}
				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;
					key[i].single_flag = 1;
				}
				break;
		}
	}
	switch(key[3].judge_sta){
		case 0:
			if(key[3].key_sta == 0){
				key[3].judge_sta = 1;
			}
			break;
		case 1:
			if(key[3].key_sta == 0){
				key[3].judge_sta = 2;
				long_flag = 1;
			}
			else{
				key[3].judge_sta = 0;
			}
			break;
		case 2:
			if(key[3].key_sta == 1 ){
				key[3].judge_sta = 0;
				if(long_key_cishu >= 20){
					key[3].long_single_flag = 1;
				}
				else{
					key[3].single_flag = 1;
				}
				long_flag = 0;
				long_key_cishu = 0;
			}
		break;
	}
}

main.h

void deal_key(void)
{
	if(dis_flag == 1 && qh_flag == 1){
		R_K = 1;
		qh_flag = 0;
	}
	else if(dis_flag != 1 && qh_flag == 0){
		qh_flag = 1;
	}
	if(key[0].single_flag == 1){
		dis_flag = (dis_flag + 1) % 3;
		key[0].single_flag = 0;
	}
	else if(key[1].single_flag == 1){
		switch(dis_flag){
			case 0:if(cishu==0){pwm_flag ^= 1;N+=1;cishu=50;}break;
			case 1: R_K ^= 1;break;
			default: break;
		}
		key[1].single_flag = 0;
	}
	else if(key[2].single_flag == 1){
		if(dis_flag == 1){
			switch(R_K){
				case 0:if(K == 10){K=1;break;} K++;break;
				case 1:if(R == 10){R=1;break;} R++;break;
			}
		}
		key[2].single_flag = 0;
	}
	else if(key[3].single_flag == 1){
		if(dis_flag == 1){
			switch(R_K){
				case 0:if(K == 1){K=10;break;} K--;break;
				case 1:if(R == 1){R=10;break;} R--;break;
			}
		}
		key[3].single_flag = 0;
	}
	else if(key[3].long_single_flag == 1){
		if(dis_flag == 0){
			lock^=1;
			key[3].long_single_flag = 0;
		}
	}
}

//扫描就交给中断了
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM7){
		key_scan();
	}
}

lcd模块

很简单就是用了个0.1s的定时器通过标志位来在while里显示没什么可说的。

void display(void)
{
	if(lcd_flag == 1){
		if(dis_flag == 0){
			sprintf((char *)lcd,"        DATA ");
			LCD_DisplayStringLine(Line2,lcd);
			if(reload == 100){
				sprintf((char *)lcd2,"     M=H ");
			}
			else if(reload == 200){
				sprintf((char *)lcd2,"     M=L ");
			}
			LCD_DisplayStringLine(Line4,lcd2);
			sprintf((char *)lcd,"     P=%.0f%%       ",P);
			LCD_DisplayStringLine(Line5,lcd);
			sprintf((char *)lcd,"     V=%.1f %d ",V,f);
			LCD_DisplayStringLine(Line6,lcd);
		}
		else if(dis_flag == 1){
			sprintf((char *)lcd,"        PARA ");
			LCD_DisplayStringLine(Line2,lcd);
			sprintf((char *)lcd,"     R=%d   ",R);
			LCD_DisplayStringLine(Line4,lcd);
			sprintf((char *)lcd,"     K=%d   ",K);
			LCD_DisplayStringLine(Line5,lcd);
			sprintf((char *)lcd,"               ");
			LCD_DisplayStringLine(Line6,lcd);
		}
		else{
			sprintf((char *)lcd,"        RECD ");
			LCD_DisplayStringLine(Line2,lcd);
			sprintf((char *)lcd,"     N=%d ",N);
			LCD_DisplayStringLine(Line4,lcd);
			sprintf((char *)lcd,"     MH=%.1f ",MH);
			LCD_DisplayStringLine(Line5,lcd);
			sprintf((char *)lcd,"     ML=%.1f ",ML);
			LCD_DisplayStringLine(Line6,lcd);
		}
	lcd_flag = 0;
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值