目录
0 前言
由于时间关系,暂停详细更新,本文章中,只会记录重要代码,关于cubmx的配置以及引脚配置,请自行下载文件配置
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();
}
}
}