【STM32】综合练习——智能风扇系统

发布于:2025-04-08 ⋅ 阅读:(38) ⋅ 点赞:(0)

目录

0 前言

1 硬件准备

2 功能介绍

3 前置配置

3.1 时钟配置

3.2 文件配置

4 功能实现 

4.1 按键功能

4.2 屏幕功能

4.3 调速功能

4.4 倒计时功能

4.5 摇头功能

4.6 测距待机功能


0 前言

由于时间关系,暂停详细更新,本文章中,只会记录重要代码,关于cubmx的配置以及引脚配置,请自行下载文件配置

【免费】基于STM32的多功能风扇资源-CSDN文库


1 硬件准备

STM32F103C8T6 * 1

面包板 * 1

OLED显示屏(SSD131590) * 1

RGB三色全彩LED模块 * 1

SG90舵机 * 1

HC-SR04超声波模块 * 1

130直流电机 * 1

风扇头 * 1

L298N电机驱动模块 * 1

直插2脚微动按键 * 2

USB转TTL模块-CH340模块 * 1

ST-LINK V2 * 1

杜邦线 (公对公、公对母、母对母)

跳线


2 功能介绍

(1)完成手动调节(按键)调节风扇三档转速(档位转速分别为30%、50%、80%)
(2)实现按键定时功能(短按一次时间加5秒,长按后倒计时开始,倒计时结束后风扇停止转动)
(3)实现风速不同档位显示,一档风速亮白灯,二档风速亮蓝灯,三档风速亮红灯,并通过OLED屏幕显示当前灯的颜色和定时时间(用英文和数字)
(4)实现通过串口显示当前风速
(5)以超声波为总开关控制系统运作(在20厘米内系统才可工作)
(6)利用舵机实现风扇摇头功能,并通过按键进行控制


3 前置配置

3.1 时钟配置

 设置高速外部时钟:晶振

设置时钟频率为72Mhz 


3.2 文件配置

修改文件名称以及选择IDE:MDK-ARM 

为每一个外设单独生成一对.c/.h文件(模块化,方便管理)


4 功能实现 

4.1 按键功能

在interrupt.c中使用回调函数判断定时器2,进行按键扫描(按键具体的功能不在此文件中) 

// interrupt.c

if(htim == &htim2)	// button
	{
		keys[0].clickState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6);	// left BUTTON
		keys[1].clickState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);	// right BUTTON
		
		for(int i=0; i<=1; i++)
		{
			switch(keys[i].stage)
			{
				case 0:
				{
					//judge click
					if(keys[i].clickState == 0) keys[i].stage = 1; //go next stage
				}
				break;
				case 1:
				{
					if(keys[i].clickState == 0)
					{
						// is true click
						// clear time
						keys[i].pressTime = 0;
						// go next stage
						keys[i].stage = 2;
					}
				}
				break;
				case 2:
				{
					if(keys[i].clickState == 1)	// release click
					{
						//judge click which button and its type
						if(keys[i].pressTime < 30){
							// short click
							chooseButton(i, click);
						} else {
							// long click
							chooseButton(i, longClick);
						}
						// reset stage 
						keys[i].stage = 0;
					} else {	// still click
						// time ++
						keys[i].pressTime++;
					}
				}
				break;
			
			}
		
		}
	}

按键具体的功能被拆分在了buttonFunciton.c中

从上到下依次为:

按键1的单击(调速)、按键1的长按(摇头)

按键2的单击(计时器加5)、按键2的长按(倒计时开始)

#include "buttonFunction.h"

// to judge which button is press down
void chooseButton(uint8_t key, uint8_t type){

	switch(key){
		case 0:	// button 1
		{
			if(type == 0){
				// click —— Toggle Speed Mode and Oled Update
					
				//WindSpeed
				speedLevel++;
				speedLevel %= 4;
				SetWindSpeed();
				UsartSpeed();
				HomePage();
				
			} else if (type == 1){
				// long click —— Toggle Shaking Mode
				
				isShake = !isShake;
				if(isShake){
					HAL_TIM_Base_Start_IT(&htim1);
					shakeIsBegin = 1;
				} else {
					HAL_TIM_Base_Stop_IT(&htim1);
					shakeIsBegin = 0;
				}
				
			}
		}
		break;
		case 1:	//	button 2
		{
			if(type == 0){
				// click —— Count Down Number Add and Oled Update
				
				timeCount += 5;
				HomePage();
				
				
			} else if (type == 1){
				// long click	—— Count Down Start
				
				if(timeCount > 0){
					HAL_TIM_Base_Start_IT(&htim4);
					isBegin = 1;
				}
				
			}
		}
		break;
	}
}

4.2 屏幕功能

OLED使用Keysking的模块文件,这里就不展示了,使用方法见【STM32入门教程-2024】第14集 如何在OLED屏幕上挥毫_哔哩哔哩_bilibili

// page.c
#include "page.h"


// ——————————————Init—————————————————
void PageInit(void){
	HAL_Delay(20); // 单片机启动比OLED上电快,需要延迟等待一下
  OLED_Init(); // 初始化OLED
}

// ——————————————View——————————————————
void CopyrightPage(void) {
	OLED_NewFrame();
	OLED_DrawImage(5,1, &logoImg, OLED_COLOR_NORMAL);
	OLED_PrintString(85, 4, "创客", &font16x16, OLED_COLOR_NORMAL);
	OLED_PrintString(85, 24, "中心", &font16x16, OLED_COLOR_NORMAL);
	OLED_PrintString(85, 44, "出品", &font16x16, OLED_COLOR_NORMAL);
	OLED_ShowFrame();
}

void HomePage(void){
	OLED_NewFrame();
	HomePageTitle();
	HomePageSpeedLight();
	if(timeCount > 0){
		HomePageCountDown();
	}
	OLED_ShowFrame();
}

void SleepPage(void){
		OLED_NewFrame();
		OLED_PrintString(32, 10, "待机中...", &font16x16, OLED_COLOR_NORMAL);
		OLED_PrintString(4, 35, "~(p≧ w≦ q)~", &font12x12, OLED_COLOR_NORMAL);
		OLED_ShowFrame();
}

// ————————————Component————————————————

// Home Title
void HomePageTitle(void){
	OLED_PrintString(32, 10, "智能の扇", &font16x16, OLED_COLOR_NORMAL);
}

// Home Body
void HomePageSpeedLight(void){
	StopAllLight();
	switch(speedLevel){
		case 0:
		{
			OLED_PrintString(50, 30, "STOP", &font16x16, OLED_COLOR_NORMAL);
		}
		break;
		case 1:
		{
			OLED_PrintString(46, 30, "WHITE", &font16x16, OLED_COLOR_NORMAL);
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
		}
		break;
		case 2:
		{
			OLED_PrintString(50, 30, "BLUE", &font16x16, OLED_COLOR_NORMAL);
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
		}
		break;
		case 3:
		{
			OLED_PrintString(54, 30, "RED", &font16x16, OLED_COLOR_NORMAL);
			HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
		}
		break;
	}
}

// Home Bottom
void HomePageCountDown(void){
	char arr[40];
	sprintf(arr, "%d", timeCount);
	if(timeCount >= 10000){
		OLED_PrintString(46, 50, arr, &font16x16, OLED_COLOR_NORMAL);
	} else if(timeCount >= 1000){
		OLED_PrintString(50, 50, arr, &font16x16, OLED_COLOR_NORMAL);
	} else if(timeCount >= 100){
		OLED_PrintString(54, 50, arr, &font16x16, OLED_COLOR_NORMAL);
	} else if(timeCount >= 10){
		OLED_PrintString(58, 50, arr, &font16x16, OLED_COLOR_NORMAL);
	} else {
		OLED_PrintString(62, 50, arr, &font16x16, OLED_COLOR_NORMAL);
	}
}

// Stop All Light
void StopAllLight(void){
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
}
// page.h
#ifndef __PAGE_H__
#define __PAGE_H__

#include "main.h"
#include "string.h"
#include "oled.h"
#include "stdio.h"
#include "stdbool.h"

// ——————————————Internal Api————————————————
// view
void PageInit(void);
void CopyrightPage(void);
void HomePage(void);
void SleepPage(void);

// Component
void HomePageTitle(void);
void HomePageSpeedLight(void);
void HomePageCountDown(void);
void StopAllLight(void);
// ——————————————External Api————————————————
// windSpeed
extern uint8_t speedLevel;
extern uint8_t tempSpeedLevel;
// countDown
extern uint16_t timeCount;
// ——————————————————————————————————————————
#endif

4.3 调速功能

// windSpeed.c
#include "windSpeed.h"

void SetWindSpeed(void){
	switch(speedLevel){
		case 0:
		{
			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, StopSpeed);
		}
		break;
		case 1:
		{
			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, LowSpeed);
		}
		break;
		case 2:
		{
			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, MidSpeed);
		}
		break;
		case 3:
		{
			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, HighSpeed);
		}
		break;
	}
}


void UsartSpeed(void){
	
	sprintf(arr, "当前档位为:%d", speedLevel);
	HAL_UART_Transmit(&huart1, (uint8_t *)arr, sizeof(arr), HAL_MAX_DELAY);

}
// windSpeed.h
#ifndef __WINDSPEED_H__
#define __WINDSPEED_H__

#include "main.h"
#include "tim.h"
#include "usart.h"
#include <stdio.h>


// ————————————Internal Api——————————————————

#define StopSpeed 0
#define LowSpeed 300
#define MidSpeed 500
#define HighSpeed 800
uint8_t speedLevel = 0;
uint8_t tempSpeedLevel = 10;
void SetWindSpeed(void);
void UsartSpeed(void);
char arr[99];

// ————————————External Api——————————————————

// 0

// ——————————————————————————————————————————

#endif

4.4 倒计时功能

// interrupt.c
	if(htim == &htim4){	// Time Count Down
		if(timeCount > 1)
		{
			timeCount--;
		} else {
			timeCount--;
			speedLevel = 0;
			SetWindSpeed();
			HAL_TIM_Base_Stop_IT(&htim4);
			isBegin = 0;
			HAL_TIM_Base_Stop_IT(&htim1);
			shakeIsBegin = 0;
			UsartSpeed();
		}
		HomePage();
	
	}

 倒计时功能主要在interrupt中实现,此处只声明了个全局变量 

// countDown.c
#include "countDown.h"
// countDown.h
#ifndef __COUNTDOWN_H__
#define __COUNTDOWN_H__

#include "main.h"
#include "stdbool.h"
// ————————————Internal Api——————————————————

uint16_t timeCount;
bool isBegin = 0;

// ————————————External Api——————————————————

// 0

// ——————————————————————————————————————————

#endif

4.5 摇头功能

舵机教程:【STM32】动画讲解输入捕获 并实现超声波测距_哔哩哔哩_bilibili 

// shake.c
#include "shake.h"

int shakeNumber = 1500;	//500——2500 duty
bool shakeMode = 0;

void shakeApi(void){
	if(shakeMode == 0){
		// forward
		if(shakeNumber < 2500){
			shakeNumber += shakeSpeed;
		} else {
			shakeMode = 1;
		}
		__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, shakeNumber);
	} else {
		// reverse
		if(shakeNumber > 500){
			shakeNumber -= shakeSpeed;
		} else {
			shakeMode = 0;
		}
		__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, shakeNumber);
	}


}
// shake.h
#ifndef __SHAKE_H__
#define __SHAKE_H__

#include "main.h"
#include "tim.h"
#include "stdbool.h"

// ————————————Internal Api——————————————————

#define shakeSpeed 10	// 5(slow) 10(normal) 20(fast) 100(very fast)
void shakeApi(void);
bool shakeIsBegin = 0;

// ————————————External Api——————————————————

// 0

// ——————————————————————————————————————————


#endif

启用中断:

// interrupt.c
if(htim == &htim1){ // Shake
	shakeApi();
}

4.6 测距待机功能

为了使系统正常工作时(20cm以内) ,能继续进行上一次的数据(如倒计时从上一次离开开始,继续倒计时),这里并没有直接修改存储该数据的变量,而是直接将所有功能暂停(具体实现见StopAll函数),恢复时再读取存储数据的变量

// ultrasound.c
#include "ultrasound.h"

void TriggerUltrasound(void){

	// Send trigger signal
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
//	HAL_Delay(1);
	for(uint32_t i=0; i<11; i++);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
	// Reset counter
	__HAL_TIM_SET_COUNTER(&htim3, 0);
	// Wait
	HAL_Delay(50);
}


void MeasurementDistance(void){
	timeCountBefore = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
	timeCountAfter = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_2);
	distance = (timeCountAfter - timeCountBefore) * 0.034 / 2;
}

void StopAll(void){
	// stop all light
	StopAllLight();
	// stop wideSpeed
	__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0);
	// stop count down
	HAL_TIM_Base_Stop_IT(&htim4);
	// stop shake
	HAL_TIM_Base_Stop_IT(&htim1);
	// sleep page
	SleepPage();
}

void StartAll(void){
	// start count down
	if(timeCount > 0 && isBegin){
		HAL_TIM_Base_Start_IT(&htim4);
	}
	// start shake
	if(shakeIsBegin){
		HAL_TIM_Base_Start_IT(&htim1);
	}
	// start wideSpeed
	SetWindSpeed();
	// home page - start all light
	HomePage();
}
 
// ultrasound.h
#ifndef __ULTRASOUND_H__
#define __ULTRASOUND_H__

#include "main.h"
#include "tim.h"
#include "stdbool.h"

// ————————————Internal Api——————————————————

uint16_t timeCountBefore;
uint16_t timeCountAfter;
float distance;
void TriggerUltrasound(void);
void MeasurementDistance(void);
void StopAll(void);
void StartAll(void);

// ————————————External Api——————————————————

// page - light
extern void HomePage(void);
extern void SleepPage(void);
extern void StopAllLight(void);
// windSpeed
extern uint8_t speedLevel;
extern void SetWindSpeed(void);
// count down
extern uint16_t timeCount;
extern bool isBegin;
// shake
extern bool shakeIsBegin;
// ——————————————————————————————————————————

#endif

输入捕获中断:

测距思想:一个通道读上升沿,一组的另一个通道读下降沿,并且在读到下降沿的时候进行中断

教程:【STM32】动画讲解输入捕获 并实现超声波测距_哔哩哔哩_bilibili 

// interrupt.c
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){

	if(htim == &htim3 && htim -> Channel == HAL_TIM_ACTIVE_CHANNEL_2){	// ultrasound
		// Measurement Ultrasound Distance
		MeasurementDistance();
//		sprintf(aeee, "%f", distance);
//		HAL_UART_Transmit(&huart1, (uint8_t *)aeee, sizeof(aeee), HAL_MAX_DELAY);
		if(distance > 22){
			// sleep
			StopAll();
		} else if(distance >= 0 && distance <= 22){
			// normal
			StartAll();
		}
	}

}