智能家居系统(基于STM32F103C8T6标准库+FreeRTOS+Qt串口开发实现)

视频演示:基于STM32F103C8T6标准库+FreeRTOS+Qt串口开发实现的智能家居项目_哔哩哔哩_bilibili

基于STM32F103C8T6标准库+FreeRTOS+Qt串口开发实现的智能家居项目: https://pan.baidu.com/s/1f41gAfOOnlcQoKoMx3o84A?pwd=6j2g 提取码: 6j2g

 注:本项目为学习完《江科大STM32教程》实战项目。受限于个人水平,还有很多需要改进的地方,还请读者见谅,如有不当之处,还请指证。

01 项目需求

1.主控:STM32F103C8T6;

2.按键+LED:模拟开关灯,支持本地控制和远程控制;

3.按键+直流电机驱动模块+直流电机+风扇叶片:模拟降温系统,支持本地控制和上位机控制,本地控制实现直流电机转或不转,上位机控制带调速功能;以及温度高于一定阈值时候自动开启风扇,低于一定阈值时候自动关闭风扇;

4.按键+无源蜂鸣器:模拟音乐播放系统,支持本地控制和上位机控制;

5.DHT11温湿度传感器:测量温湿度;

6.BH1750光照传感器:测量光照强度;

7.OLED显示屏:显示温湿度、光照强度;

8.Qt串口上位机:Qt上位机实现。

02 硬件连接

注意:本电路仅体现个模块之间的电路连接。原理图绘制参看:

嘉立创EDA使用流程

03 驱动部分编写

3.1 照明系统

功能:实现本地按键开关灯,远程开关灯。本节先实现本地任务。

硬件:按键+LED

因为测量温度、亮度、播放音乐等任务执行后一直进行,所以需要使用中断来处理开关灯任务。

3.1.1 LED驱动

LED.c

/**
*Encoding:GB2312
*文件功能:LED开关灯功能实现
**/#include "stm32f10x.h"                  // Device header/*** 函    数:LED初始化,LED接在PC13上* 参    数:无* 返 回 值:无*/
void LED_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);		 //开启GPIOC的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);						//将PC13引脚初始化为推挽输出/*设置GPIO初始化后的默认电平*/GPIO_SetBits(GPIOC, GPIO_Pin_13);				          //设置PC13引脚为高电平,不亮,因为我们的引脚正极接高电平,负极接PC13
}/*** 函    数:LED1开启* 参    数:无* 返 回 值:无*/
void LED_ON(void)
{GPIO_SetBits(GPIOC, GPIO_Pin_13);		//设置PC13引脚为低电平
}/*** 函    数:LED1关闭* 参    数:无* 返 回 值:无*/
void LED_OFF(void)
{GPIO_ResetBits(GPIOC, GPIO_Pin_13);		//设置PC13引脚为高电平
}/*** 函    数:LED状态翻转* 参    数:无* 返 回 值:无*/
void LED_Turn(void)
{if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13) == 0)	//获取输出寄存器的状态,如果当前引脚输出低电平{GPIO_SetBits(GPIOC, GPIO_Pin_13);					//则设置PC13引脚为高电平}else													//否则,即当前引脚输出高电平{GPIO_ResetBits(GPIOC, GPIO_Pin_13);					//则设置PC13引脚为低电平}
}

LED.h

#ifndef __LED_H
#define __LED_Hvoid LED_Init(void);
void LED_ON(void);
void LED_OFF(void);	
void LED_Turn(void);#endif

 3.1.2 LED按键驱动

这个项目中一共使用了3个按键,本章先给出LED按键驱动的代码,最后会整合到一起去。按键接在PC15引脚上。

Key1.c

/**
*Encoding:GB2312
*文件功能:按键1功能实现
**/
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"/**
*函数:LED按键1初始化函数
*参数:无
*返回值:无
**/
void Key1_Init(void)
{/*第1步:打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);         //开启GPIOC的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          // 开启AFIO的时钟/*EXTI和NVIC的时钟一直是打开的,不需要开启:EXTI作为一个独立外设,按理说应该是需要开启时钟的但是寄存器里没有EXTI时钟的控制位NVIC是内核的外设,内核的外设是不需要开启时钟的RCC管的都是内核外的外设*//*第2步:配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);						//将PC15引脚初始化为上拉输入	/*第3步:配置AFIO:它的库函数是和GPIO在一个文件里的*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource15);/*第4步:配置EXTI*/EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line = EXTI_Line15;              //指定中断线为第15个线路EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      //指定中断线的模式为中断触发EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  //指定中断触发方式为上升沿和下降沿均触发EXTI_InitStruct.EXTI_LineCmd = ENABLE;                //指定选择的中断线的新状态EXTI_Init(&EXTI_InitStruct);/*第5步:配置NVIC因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,在misc.h文件里*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       /*设置优先级分组方式,需要注意的是,这个分组方式整个芯片只能用一种,所以这个分组代码整个工程只需要执行依次就行了,如果把它放在模块里面进行分组,要确保每个模块分组都是选的同一个,也可以把这个分组代码放在主函数的最开始,这样模块里就不用在再进行分组了*/	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;   //指定中断通道  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;   //将抢占优先级设置为1,优先级是在多个中断源同时申请,产生拥挤的时候才有作用NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;    //将响应优先级设置为1NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //使能中断通道NVIC_Init(&NVIC_InitStruct);
}/*中断函数:中断函数的名字是固定的,在"startup_stm32f10x_md.s"文件里查看*/
void EXTI15_10_IRQHandler(void)   //中断函数的名字都是固定的,并且无参无返回值
{if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_15) == 0)			//读PC15输入寄存器的状态,如果为0,则代表按键1按下{LED_Turn();			    //LED翻转}EXTI_ClearITPendingBit(EXTI_Line15);     //将通道15中断标志位清除 
}	

Key1.h

#ifndef __KEY_H
#define __KEY_Hvoid Key1_Init(void);#endif

通过以上代码,可以实现按键开关灯。需要改进的是,这里按键上升沿下降沿都能触发,因为是在中断函数里实现的LED状态翻转,所以没有进行消抖。

3.2 降温系统

功能:实现本地能够按键开关风扇,远程开关风扇+调速;高于/低于温度阈值自动开启/关闭风扇。本节先实现本地按键开关风扇。

硬件:按键+直流电机驱动模块+直流电机+风扇叶片

3.2.1 直流电机驱动

电路连接,参考江科大教程:

因为要实现电机的调速功能,所以需要使用PWM驱动。

PWM.c

#include "stm32f10x.h"                  // Device header/*** 函    数:PWM初始化* 参    数:无* 返 回 值:无*/
void PWM_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA2引脚初始化为复用推挽输出	//受外设控制的引脚,均需要配置为复用模式/*配置时钟源*/TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;                 //计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;               //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元/*输出比较初始化*/ TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量TIM_OCStructInit(&TIM_OCInitStructure);                         //结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               //输出比较模式,选择PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       //输出极性,选择为高,若选择极性为低,则输出高低电平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //输出使能TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值TIM_OC2Init(TIM2, &TIM_OCInitStructure);                        //将结构体变量交给TIM_OC3Init,配置TIM2的输出比较通道3/*TIM使能*/TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}/*** 函    数:PWM设置CCR* 参    数:Compare 要写入的CCR的值,范围:0~100* 返 回 值:无* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比*           占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare2(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare);		//设置CCR3的值
}

PWM.h

#ifndef __PWM_H
#define __PWM_Hvoid PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);#endif

Motor.c

/**
*Encoding:GB2312
*文件功能:直流电机驱动功能实现
**/#include "stm32f10x.h"                  // Device header
#include "PWM.h"
#include "stdbool.h"bool MotorState;                //定义一个全局变量用来记录电机的状态/**
*函数:直流电机驱动模块初始化函数
*参数:无
*返回值:无
**/
void Motor_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);PWM_Init();
}/**
*函数:直流电机转速设置函数
*参数:Speed设置电机转速,返回state电机状态
*返回值:返回state电机状态:转/不转
**/
bool Motor_SetSpeed(int8_t Speed)
{if (!MotorState){if (Speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare2(Speed);}else{GPIO_ResetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare2(-Speed);}MotorState = true;}return MotorState;                         //如果风扇处于转动过程中返回真,用来处理中断状态翻转
}

Motor.h 

#ifndef __MOTOR_H
#define __MOTOR_H
#include "stdbool.h"void Motor_Init(void);
bool Motor_SetSpeed(int8_t Speed);#endif

3.2.2 风扇电机按键驱动 

Key2.c

/**
*Encoding:GB2312
*文件功能:按键功能实现
**/
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Key2.h"
#include "Motor.h"
#include "stdbool.h"extern bool MotorState;  /**
*函数:风扇按键2初始化函数
*参数:无
*返回值:无
**/
void Key2_Init(void)
{/*第1步:打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);         //开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          // 开启AFIO的时钟/*EXTI和NVIC的时钟一直是打开的,不需要开启:EXTI作为一个独立外设,按理说应该是需要开启时钟的但是寄存器里没有EXTI时钟的控制位NVIC是内核的外设,内核的外设是不需要开启时钟的RCC管的都是内核外的外设*//*第2步:配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1引脚初始化为上拉输入	/*第3步:配置AFIO:它的库函数是和GPIO在一个文件里的*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);/*第4步:配置EXTI*/EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line = EXTI_Line1;              //指定中断线为第1个线路EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      //指定中断线的模式为中断触发EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  //指定中断触发方式为上升沿和下降沿均触发EXTI_InitStruct.EXTI_LineCmd = ENABLE;                //指定选择的中断线的新状态EXTI_Init(&EXTI_InitStruct);/*第5步:配置NVIC因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,在misc.h文件里*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       /*设置优先级分组方式,需要注意的是,这个分组方式整个芯片只能用一种,所以这个分组代码整个工程只需要执行依次就行了,如果把它放在模块里面进行分组,要确保每个模块分组都是选的同一个,也可以把这个分组代码放在主函数的最开始,这样模块里就不用在再进行分组了*/	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;             //指定中断通道  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;   //将抢占优先级设置为1,优先级是在多个中断源同时申请,产生拥挤的时候才有作用NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;    //将响应优先级设置为1NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //使能中断通道NVIC_Init(&NVIC_InitStruct);
}void EXTI1_IRQHandler(void)
{if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键2按下{if(MotorState){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);//按键按下后,如果原来是转的,那么设置PWM为0,电机停止转动MotorState = false;}else{Motor_SetSpeed(60);                                //按下按键后,如果电机原来不转,设置转速默认为60,电机开始转动}}EXTI_ClearITPendingBit(EXTI_Line1);     //将通道1中断标志位清除 
}

Key.h 

#ifndef __KEY_H
#define __KEY_Hvoid Key2_Init(void);#endif

通过以上文件,我们能够实现按键控制直流电机转或不转,当然因为在中断里面使用按键,所以也没有实现避免按键消抖的问题。 

3.3 音乐播放系统 

功能:实现本地能够按键开始或停止播放音乐,远程开始或停止播放音乐。本节先实现本地任务。

硬件:按键+无源蜂鸣器

3.3.1 驱动蜂鸣器实现音乐播放功能

参考连接:

STM32+无源蜂鸣器播放音乐参考

这里我直接使用了以上up的源代码。

Buzzer.c

/**
*Encoding:GB2312
*文件功能:蜂鸣器硬件功能实现
**/#include "stm32f10x.h"                  // Device header
#include "Delay.h"/**
*函数:蜂鸣器所使用外设初始化函数
*参数:无
*返回值:无
**/
void Music_init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);          //开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);         //开启GPIOA的时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;                     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;             //将PA0引脚初始化为复用推挽输出	GPIO_Init(GPIOA, &GPIO_InitStructure);                        //受外设控制的引脚,均需要配置为复用模式	TIM_InternalClockConfig(TIM2);                                //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 99;	TIM_TimeBaseInitStructure.TIM_Prescaler = 1439;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 50;	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 50;		//CCRTIM_OC1Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}/**
*函数:蜂鸣器频率设置
*参数:无
*返回值:无
**/
void Sound_SetHZ(uint16_t a)
{TIM_PrescalerConfig(TIM2,a,TIM_PSCReloadMode_Immediate);
}/**
*函数:播放音乐
*参数:无
*返回值:无
**/
void Play_Music(int a,int b,int c)
{Music_init();Sound_SetHZ(a);Delay_ms(b);Sound_SetHZ(20);Delay_ms(c);
}/**
*函数:暂停音乐
*参数:无
*返回值:无
**/
void Stop_Music(void)
{Play_Music(20,10,10);  //将蜂鸣器频率改为0,实现停止功能
}

Buzzer.h

#ifndef __PLAYMUSIC_H__
#define __PLAYMUSIC_H__void Music_init(void);
void Sound_SetHZ(uint16_t a);
void Play_Music(int a,int b,int c);
void Stop_Music(void);#endif

Music.c

/**
*Encoding:GB2312
*文件功能:音乐播放软件功能实现
**/#include "stm32f10x.h"                  // Device header
#include "Buzzer.H"
#include "stm32f10x.h"           
#include "Delay.h"
#include "OLED.h"uint8_t MusicState = 0;                    // 定义一个全局变量来记录音乐播放的状态 int i,time,j,a[]={                                                           //音调1635,1376, 917,1376,2062,1836,1635, 917,1376,1376,1836, 917,1376,1376,917,1456, 917,1635,1376, 917,1376,2062,1836,1635, 917,1376,1376,1836,917,1376,1376, 917,1836,1376, 917,                             //前奏 3430, 917, 917,1376,1376,1226,1092,                              //故事的小黄花 4130, 917, 917,1376,1376,1226,1092,1226,1376,1836,               //从出生那年就飘着 51  30, 917, 917,1376,1376,1226,1092,                              //童年的荡秋千 58 30,1092,1226,1092,1031,1092,1226,1031,1092,1226,1376,          //随记忆一直晃到现在 691836,1376,1376,1092,1031,1092,1226,                              //Re So So Si Do Si La 761376,1226,1092,1092,1092,1092,1226,1092,1226,1376,               //So La Si Si Si Si La Si La So 86 1836,1376,1226,1092,1031,1092,1226,1376,                         //吹着前奏 望着天空 941226,1092,1092,1092,1092,1226,1092,1226,1376,                    //我想起花瓣试着掉落 103 30,1376,1376,1376,1376,1635,1456,1376, 917,1031,1092,1376,1367,//没想到 失去的勇气我还留着 11630,1376,1376,1376,1376,1092,1376,                               //好想再问一遍 1231635,1456,1376, 917,1031,1092,1376,1226,                          //你会等待还是离开 1311092,1226,1031,1092,1376, 917, 728, 687, 728, 917,1376,           //刮风这天 我试过握着你手 14230,1376, 817, 817,  30, 817, 917, 917,                          //但偏偏 雨渐渐 15030, 917,1031,1092,1226,1092,1031,1092,                          //大到我看你不见 15830,1092,1031, 917,1092,  30,1031, 917, 728, 612, 728, 687, 687, //还要多久 我才能在你身边 171 30, 687, 687, 917, 917, 817, 917,1031,                          //等到放晴的那天 1791226,1092, 1031, 917, 817,1376,817, 728, 728,                     //也许我会比较好一点 1881092,1226,1031,1092,  30,1376, 917, 728, 687, 728, 917,1376,      //从前从前 有个人爱你很久 20030,1376, 817, 817,  30, 817, 917, 917,                          //但偏偏 风渐渐 20830, 917,1031,1092,1226,1092,1031,1092,                          //大到我看你不见 21630,1092,1031, 917,1092,  30,1031, 917, 728, 612, 728, 687, 687, //好不容易 又能再多爱一天 22930,1376, 687, 917, 917, 817, 917,                               //但故事的最后 2361031,1635,1456,1376,1226,1092,1226,30,1092,1376                   //你好像还是说了 拜拜 246};int tm[]={                                                  //音调时长50, 50, 50, 50, 50, 25, 25, 50, 50, 50, 50, 50, 50, 50,50, 50, 50, 50, 50, 50, 50, 50, 25, 25, 50, 50, 50, 50,50, 50, 50, 50, 25, 25, 50,                       //前奏  50, 50, 50, 50,100, 50, 50,                       //故事的小黄花50, 50, 50, 50, 50, 25, 25, 25, 25, 50,           //从出生那年就飘着50, 50, 50, 50,100, 50, 50,                       //童年的荡秋千  50,100, 25, 25, 25, 25, 25, 25, 25, 25, 50,       //随记忆一直晃到现在50, 50, 50, 50, 50, 50, 50,                        //Re So So Si Do Si La25, 25, 50, 50, 50, 50, 25, 25, 50,100,            //So La Si Si Si Si La Si La So 50, 50, 50, 50, 50, 50, 50, 25,                    //吹着前奏 望着天空25, 50, 50, 50, 50, 25, 25, 50, 75,                //我想起花瓣试着掉落 400, 25, 25, 25, 25, 50, 50, 50, 50, 50, 50, 50,125,//没想到 失去的勇气我还留着 50, 25, 25, 25, 25, 50, 50,                        //好想再问一遍50, 50, 50, 50, 50, 50, 50,130,                    //你会等待还是离开50, 50, 50,100, 50, 50, 50, 50, 50, 50, 50,        //刮风这天 我试过握着你手50, 50, 50, 50, 50, 50, 50, 50,                    //但偏偏 雨渐渐 50, 50, 50, 50, 50, 50, 50,125,                    //大到我看你不见75, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,125,//还要多久 我才能在你身边  50, 50, 50, 50, 50, 50, 50, 50,                    //等到放晴的那天50, 50, 50, 50, 50, 50, 75, 25,100,                //也许我会比较好一点50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,    //从前从前 有个人爱你很久50, 50, 50, 50, 50, 50, 50, 50,                    //但偏偏 风渐渐 50, 50, 50, 50, 50, 50, 50,150,                    //大到我看你不见50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,125,//好不容易 又能再多爱一天 50, 50, 50, 50, 50, 50, 50,                        //但故事的最后50, 50, 50, 50, 50, 50,100, 20, 50,200,            //你好像还是说了 拜拜};/*** 函    数:音乐播放* 参    数:无* 返 回 值:无*/				
void B_Music(void)
{MusicState++;int c;for(i=0;i<=246;i++){     c=5;j=tm[i]/25;time=j*180;if(i==49||i==67||i==178){c=0;}	Play_Music(a[i],time,c);}	
}

Music.h

#ifndef __SOUND_H__
#define __SOUND_H__void B_Music(void);#endif

3.3.2 蜂鸣器按键功能实现

Key3.c

/**
*Encoding:GB2312
*文件功能:按键3功能实现
**/#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Key.h"
#include "LED.h"
#include "Buzzer.h"
#include "Music.h"
#include "stdio.h"
#include "usart1.h"extern uint8_t MusicState;/**
*函数:蜂鸣器按键3初始化函数
*参数:无
*返回值:无
**/
void Key3_Init(void)
{/*第1步:打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);         //开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          // 开启AFIO的时钟/*EXTI和NVIC的时钟一直是打开的,不需要开启:EXTI作为一个独立外设,按理说应该是需要开启时钟的但是寄存器里没有EXTI时钟的控制位NVIC是内核的外设,内核的外设是不需要开启时钟的RCC管的都是内核外的外设*//*第2步:配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA0引脚初始化为上拉输入	/*第3步:配置AFIO:它的库函数是和GPIO在一个文件里的*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource7);/*第4步:配置EXTI*/EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line = EXTI_Line7;              //指定中断线为第0个线路EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      //指定中断线的模式为中断触发EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  //指定中断触发方式为上升沿和下降沿均触发EXTI_InitStruct.EXTI_LineCmd = ENABLE;                //指定选择的中断线的新状态EXTI_Init(&EXTI_InitStruct);/*第5步:配置NVIC因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,在misc.h文件里*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       /*设置优先级分组方式,需要注意的是,这个分组方式整个芯片只能用一种,所以这个分组代码整个工程只需要执行依次就行了,如果把它放在模块里面进行分组,要确保每个模块分组都是选的同一个,也可以把这个分组代码放在主函数的最开始,这样模块里就不用在再进行分组了*/	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;   //指定中断通道  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;   //将抢占优先级设置为1,优先级是在多个中断源同时申请,产生拥挤的时候才有作用NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;    //将响应优先级设置为1NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //使能中断通道NVIC_Init(&NVIC_InitStruct);
}/*中断函数:中断函数的名字是固定的,在"startup_stm32f10x_md.s"文件里查看*/
/**
*函数:按键开始或播放音乐
*参数:无
*返回值:无
**/
void EXTI9_5_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line7)==SET){if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7) == 0)			//读PA7输入寄存器的状态,如果为0,则代表按键1按下{printf("MusicState原始状态:%d\r\n",MusicState);if(MusicState){				
//					Delay_ms(50);Stop_Music();                                      //如果本来就在播放音乐,按键按下那就停止播放音乐,并将音乐状态标志清零				MusicState = 0;printf("如果原来大于1,MusicState按下暂停后归0:%d\r\n",MusicState);}else if(MusicState == 0){B_Music();printf("MusicState播放音乐后:%d\r\n",MusicState);}}  EXTI_ClearITPendingBit(EXTI_Line7);     //将通道7中断标志位清除 }	
}

这里我们使用了串口1打印了一下音乐播放状态标志,用来确定一下按键无法正常工作的原因。 

Key3.h

#ifndef __KEY_H
#define __KEY_Hvoid Key3_Init(void);
void Key_StopMusic(void);#endif

通过以上函数,我们实现了按键控制蜂鸣器播放音乐或暂停,但是是有很大缺陷的代码,只能实现按键功能一次性使用,什么意思呢?如果我的音乐上电后初始状态为播放状态,我能够实现暂停以后再开启,然后需要等整首音乐播放完才能通过按键重启,并不能中途暂停;如果我的音乐初始状态非播放,我能通过按键控制开始播放音乐,然后需要等整首音乐播放完才能通过按键重启,并不能中途暂停。这是因为代码是从上往下执行的,而我们用来记录音乐播放状态的变量没办法再音乐播放过程中修改,所以用这种方法没办法实现顺利实现我们想要的功能。我们使用中断进入了一个时间很长的中断程序进入了音乐播放状态,如果能够生效,就相当于我在A中断运行的时候再去触发A中断,这显示是无法实现的。当然,使用定时器定时中断的功能或许能够实现,但是太过复杂,这里我们先不管,后面用FreeRtos解决这个问题。

3.4 合并LED+直流电机+蜂鸣器功能

LED和直流电机、蜂鸣器我们都使用了按键进行控制,我们将它们的按键驱动程序合并到一块。

这时候就会出现一个问题,当我使用中断进入音乐播放功能在工作时,我按下LED和直流电机的按键会发现无效,这是因为中断优先级的问题,因为使用中断进入音乐播放本来就在中断中,我们必须使用比音乐播放更高优先级的中断才能跳出音乐播放。进行如下修改:

Key.c

/**
*Encoding:GB2312
*文件功能:按键功能实现
**/
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Key.h"
#include "LED.h"
#include "Motor.h"
#include "stdbool.h"
#include "Buzzer.h"
#include "Music.h"
#include "Motor.h"
#include "stdio.h"
#include "usart1.h"extern bool MotorState;  
extern uint8_t MusicState;/**
*函数:LED按键1初始化函数
*参数:无
*返回值:无
**/
void Key1_Init(void)
{/*第1步:打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);         //开启GPIOC的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          // 开启AFIO的时钟/*EXTI和NVIC的时钟一直是打开的,不需要开启:EXTI作为一个独立外设,按理说应该是需要开启时钟的但是寄存器里没有EXTI时钟的控制位NVIC是内核的外设,内核的外设是不需要开启时钟的RCC管的都是内核外的外设*//*第2步:配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);						//将PC15引脚初始化为上拉输入	/*第3步:配置AFIO:它的库函数是和GPIO在一个文件里的*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource15);/*第4步:配置EXTI*/EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line = EXTI_Line15;              //指定中断线为第15个线路EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      //指定中断线的模式为中断触发EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  //指定中断触发方式为上升沿和下降沿均触发EXTI_InitStruct.EXTI_LineCmd = ENABLE;                //指定选择的中断线的新状态EXTI_Init(&EXTI_InitStruct);/*第5步:配置NVIC因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,在misc.h文件里*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       /*设置优先级分组方式,需要注意的是,这个分组方式整个芯片只能用一种,所以这个分组代码整个工程只需要执行依次就行了,如果把它放在模块里面进行分组,要确保每个模块分组都是选的同一个,也可以把这个分组代码放在主函数的最开始,这样模块里就不用在再进行分组了*/	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;   //指定中断通道  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;   //将抢占优先级设置为1,优先级是在多个中断源同时申请,产生拥挤的时候才有作用NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;    //将响应优先级设置为2NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //使能中断通道NVIC_Init(&NVIC_InitStruct);
}/**
*函数:风扇按键2初始化函数
*参数:无
*返回值:无
**/
void Key2_Init(void)
{/*第1步:打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);         //开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          // 开启AFIO的时钟/*EXTI和NVIC的时钟一直是打开的,不需要开启:EXTI作为一个独立外设,按理说应该是需要开启时钟的但是寄存器里没有EXTI时钟的控制位NVIC是内核的外设,内核的外设是不需要开启时钟的RCC管的都是内核外的外设*//*第2步:配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB1引脚初始化为上拉输入	/*第3步:配置AFIO:它的库函数是和GPIO在一个文件里的*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);/*第4步:配置EXTI*/EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line = EXTI_Line1;              //指定中断线为第1个线路EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      //指定中断线的模式为中断触发EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  //指定中断触发方式为上升沿和下降沿均触发EXTI_InitStruct.EXTI_LineCmd = ENABLE;                //指定选择的中断线的新状态EXTI_Init(&EXTI_InitStruct);/*第5步:配置NVIC因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,在misc.h文件里*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       /*设置优先级分组方式,需要注意的是,这个分组方式整个芯片只能用一种,所以这个分组代码整个工程只需要执行依次就行了,如果把它放在模块里面进行分组,要确保每个模块分组都是选的同一个,也可以把这个分组代码放在主函数的最开始,这样模块里就不用在再进行分组了*/	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;             //指定中断通道  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;   //将抢占优先级设置为2,优先级是在多个中断源同时申请,产生拥挤的时候才有作用NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;    //将响应优先级设置为1NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //使能中断通道NVIC_Init(&NVIC_InitStruct);
}/**
*函数:蜂鸣器按键3初始化函数
*参数:无
*返回值:无
**/
void Key3_Init(void)
{/*第1步:打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);         //开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          // 开启AFIO的时钟/*EXTI和NVIC的时钟一直是打开的,不需要开启:EXTI作为一个独立外设,按理说应该是需要开启时钟的但是寄存器里没有EXTI时钟的控制位NVIC是内核的外设,内核的外设是不需要开启时钟的RCC管的都是内核外的外设*//*第2步:配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA0引脚初始化为上拉输入	/*第3步:配置AFIO:它的库函数是和GPIO在一个文件里的*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource7);/*第4步:配置EXTI*/EXTI_InitTypeDef EXTI_InitStruct;EXTI_InitStruct.EXTI_Line = EXTI_Line7;              //指定中断线为第0个线路EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      //指定中断线的模式为中断触发EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  //指定中断触发方式为上升沿和下降沿均触发EXTI_InitStruct.EXTI_LineCmd = ENABLE;                //指定选择的中断线的新状态EXTI_Init(&EXTI_InitStruct);/*第5步:配置NVIC因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,在misc.h文件里*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);       /*设置优先级分组方式,需要注意的是,这个分组方式整个芯片只能用一种,所以这个分组代码整个工程只需要执行依次就行了,如果把它放在模块里面进行分组,要确保每个模块分组都是选的同一个,也可以把这个分组代码放在主函数的最开始,这样模块里就不用在再进行分组了*/	NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;   //指定中断通道  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;   //将抢占优先级设置为1,优先级是在多个中断源同时申请,产生拥挤的时候才有作用NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;    //将响应优先级设置为1NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //使能中断通道NVIC_Init(&NVIC_InitStruct);
}/*中断函数:中断函数的名字是固定的,在"startup_stm32f10x_md.s"文件里查看*/
/**
*函数:按键翻转LED状态和音乐播放状态,因为LED和音乐播放共用了同一个中断函数
*参数:无
*返回值:无
**/
void EXTI15_10_IRQHandler(void)   //中断函数的名字都是固定的,并且无参无返回值
{if(EXTI_GetITStatus(EXTI_Line15)==SET){if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_15) == 0)			//读PC15输入寄存器的状态,如果为0,则代表按键1按下{LED_Turn();			    //LED翻转}EXTI_ClearITPendingBit(EXTI_Line15);     //将通道15中断标志位清除 }
}	/*中断函数:中断函数的名字是固定的,在"startup_stm32f10x_md.s"文件里查看*/
/**
*函数:按键翻转风扇状态
*参数:无
*返回值:无
**/
void EXTI1_IRQHandler(void)
{if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态,如果为0,则代表按键2按下{if(MotorState){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);//按键按下后,如果原来是转的,那么设置PWM为0,电机停止转动MotorState = false;}else{Motor_SetSpeed(60);                                //按下按键后,如果电机原来不转,设置转速默认为60,电机开始转动}}EXTI_ClearITPendingBit(EXTI_Line1);     //将通道1中断标志位清除 
}/*中断函数:中断函数的名字是固定的,在"startup_stm32f10x_md.s"文件里查看*/
/**
*函数:按键开始或播放音乐
*参数:无
*返回值:无
**/
void EXTI9_5_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line7)==SET){if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7) == 0)			//读PA7输入寄存器的状态,如果为0,则代表按键1按下{printf("MusicState原始状态:%d\r\n",MusicState);if(MusicState){				Stop_Music();                                      //如果本来就在播放音乐,按键按下那就停止播放音乐,并将音乐状态标志清零				MusicState = 0;printf("如果原来大于1,MusicState按下暂停后归0:%d\r\n",MusicState);}else if(MusicState == 0){B_Music();printf("MusicState播放音乐后:%d\r\n",MusicState);}}  EXTI_ClearITPendingBit(EXTI_Line7);     //将通道7中断标志位清除 }	
}

Key.h

#ifndef __KEY_H
#define __KEY_Hvoid Key1_Init(void);
void Key2_Init(void);
void Key3_Init(void);#endif

修改后,在main.c 中初始化上述几节程序并下载到STM32后,可以发现在播放音乐的同时能够控制LED和直流电机,但是就是音乐播放功能无法达到我们想要的效果。

3.5 DHT11温湿度传感器驱动

DHT11.c

/**
*Encoding:GB2312
*文件功能:温湿度传感器功能实现
**/#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "stdio.h"
#include "DHT11.h"uint8_t DHT11_Result[5] = {0};/**
*函数:DHT11初始化函数
*参数:无
*返回值:无
**/
void DHT11_Init(void)
{/*打开时钟外设*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);         //开启GPIOA的时钟/*配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);						GPIO_SetBits(GPIOA,GPIO_Pin_8);                             //DHT11采用单总线协议进行数据传输,DHT11空闲状态置为高电平               }/**
*函数:DHT11初始化函数
*参数:无
*返回值:无
*注释:总线空闲状态应为高电平,主机把总线拉低等待DHT11响应,且拉低时间必须大于18ms
**/
void DHT11_Start(void)
{GPIO_ResetBits(GPIOA,GPIO_Pin_8);        Delay_ms(20);                           //主机拉低电平至少18ms发出开始信号GPIO_SetBits(GPIOA,GPIO_Pin_8);   Delay_us(20);                           //主机拉高电平延时20~40us等待DHT11发出响应信号
}/**
*函数:DHT11响应函数
*参数:无
*返回值:无
*注释:DHT11接收到主机开始信号后进行响应
**/
uint8_t DHT11_Response(void)
{uint16_t time = 0;while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)&& time < 100)	           // 开始信号发出后处于高电平状态,接收信号后主机应该收到低电平,但是可能不会立即切换,因此用循环消耗一下这个切换时间{Delay_us(1);time++;}	if (time >= 100)return 1;                                                         // 返回1说明响应超时,可能电路出现问题time = 0;while(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)&& time < 100)	      // 真正的DHT11响应信号,80us低电平信号{Delay_us(1);time++;}	if (time >= 100)return 1;                     // 返回1说明响应超时,可能电路出现问题return 0;
}/**
*函数:接收DHT11数据函数
*参数:无
*返回值:无
**/
uint8_t DHT11_Read_Bit(void)
{uint16_t time = 0;while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)&& time < 100)       //DHT11响应信号发出结束后,先输出80us高电平,准备发送数据{Delay_us(1);time++;}if (time >= 100)return 2;                    //返回2说明响应超时,可能电路出现问题	time = 0;while (!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8) && time < 100)      //DHT11发出50us低电平信号后发送传感器测量数据{Delay_us(1);time++;}if (time >= 30)                 //返回2说明响应超时,可能电路出现问题	return 2;Delay_us(30);                   //延时30usif (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8) == 0)     return 0;else                                                     // 如果30us后还是高电平,说明DHT11发送的是1return 1;
}/**
*函数:接收DHT11字节数据函数
*参数:无
*返回值:无
**/
uint8_t DHT11_Read_Byte(void)
{uint8_t data = 0;uint8_t i = 0;for (i = 0; i < 8; i++){data <<= 1;data = data | DHT11_Read_Bit();}return data;
}/**
*函数:将接收到的DHT11数据转换为温湿度
*参数:无
*返回值:无
**/
void DHT11_Read_Data(uint8_t *pData)
{DHT11_Start();if (DHT11_Response())return;uint8_t i;for (i = 0; i < 5; i++){pData[i] = DHT11_Read_Byte();}if (pData[4] != pData[0] + pData[1] + pData[2] + pData[3]){for (i = 0; i < 5; i++){pData[i] = 0;}}
}/**
*函数:变换温湿度数据为常规数据
*参数:接收温湿度数据的结构体
*返回值:温湿度数据结构体
**/
dht11_result DHT11_GetResult(dht11_result *result)
{DHT11_Read_Data(DHT11_Result);result->humi = DHT11_Result[0]+DHT11_Result[1]/10.0;result->temp = DHT11_Result[2]+DHT11_Result[3]/10.0;return *result;
}

DHT11.h

#ifndef __DHT11_H
#define __DHT11_Htypedef struct{float humi;float temp;
}dht11_result;void DHT11_Init(void);
void DHT11_Read_Data(uint8_t *pData);
dht11_result DHT11_GetResult(dht11_result *result);#endif

3.6 OLED驱动 

江科大OLED显示屏教程

我们直接移植江科大写好的驱动即可,我做了下改动,把显示正负号那里改成了空格,效果如下:

 main.c

/**
*Encoding:GB2312
*文件功能:项目功能实现
**/#include "stm32f10x.h"                  // Device header
#include "stdio.h"
#include "String.h"
#include "Delay.h"
#include "usart1.h"                //这个串口是给电脑使用的,用来在串口助手显示信息
#include "usart2.h"                //这个串口是给ESP8266使用的
#include "timer.h"
#include "Key.h"
#include "LED.h"
#include "OLED.h"
#include "Buzzer.h"
#include "Music.h"
#include "Motor.h"
#include "DHT11.h"
//#include "BH1750.h"
//#include "ESP8266.h"int main(void)
{	                   
//	Key1_Init();                         //按键1初始化,用于控制LED灯
//	Key2_Init();                         //按键2初始化,用来控制直流电机,模拟风扇
//	Key3_Init();                         //按键3初始化,用来控制蜂鸣器播放音乐
//	LED_Init();	                         //LED_Init初始化
//	Motor_Init();                        //直流电机初始化
//	B_Music();	                         //音乐播放功能初始化,初始化后直接开始播放音乐Serial_Init();                       //串口1初始化,用于在串口显示数据DHT11_Init();                        //DHT11温湿度传感器初始化OLED_Init();                         //OLED初始化函数
//	bh1750_init();
//	USART2_Init(115200);                 //串口2初始化,波特率为115200,用于ESP8266与STM32交互
//	WIFI_GPIO_Init();
//	Rst_WIFI();
//	WIFI_Init();
//	TIM3_Int_Init(9999,35999);OLED_ShowChinese(0,0,"温度:");OLED_ShowChinese(0,17,"湿度:");OLED_ShowChinese(0,33,"亮度:");OLED_ShowChinese(0,49,"音乐:停止播放");OLED_Update();while (1){dht11_result MyDHT11Result;MyDHT11Result= DHT11_GetResult(&MyDHT11Result);printf("温度:%.1f;湿度:%.1f\r\n",MyDHT11Result.temp,MyDHT11Result.humi);OLED_ShowFloatNum(49, 0, MyDHT11Result.temp, 2, 1, OLED_8X16);OLED_ShowChinese(92,0,"℃");OLED_ShowFloatNum(49, 17, MyDHT11Result.humi, 2, 1, OLED_8X16);OLED_ShowString(92,17, "%RH", OLED_8X16);OLED_Update();	}
}

3.7 BH1750光照传感器

 BH1750驱动参考

BH1750使用I2C传输数据,我移植了上述博主的代码。不过在我这里好像有问题,感觉结果不是很准确,先不管了,我先把系统跑起来再说。

MyI2C.c

/*************************************************************************** 文件名  :MyI2C.c* 描述    :软件模拟IIC程序  
****************************************************************************/#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "Delay.h"/*引脚配置层*//*** 函    数:I2C写SCL引脚电平* 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平*/
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(I2C_GPIO_PORT, I2C_SCL_GPIO_PIN, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平Delay_us(10);												//延时10us,防止时序频率超过要求
}/*** 函    数:I2C写SDA引脚电平* 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平*/
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(I2C_GPIO_PORT, I2C_SDA_GPIO_PIN, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性Delay_us(10);												//延时10us,防止时序频率超过要求
}/*** 函    数:I2C读SDA引脚电平* 参    数:无* 返 回 值:协议层需要得到的当前SDA的电平,范围0~1* 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1*/
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_GPIO_PIN);		//读取SDA电平Delay_us(10);												//延时10us,防止时序频率超过要求return BitValue;											//返回SDA电平
}/*** 函    数:I2C初始化* 参    数:无* 返 回 值:无* 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化*/
void MyI2C_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(I2C_GPIO_CLK, ENABLE);	//开启GPIOB的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出/*设置默认电平*/GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}/*协议层*//*** 函    数:SDA引脚输入输出模式配置* 参    数:mode,定义SDA引脚模式,大于0为输出模式,否则为输入模式* 返 回 值:无*/
void Set_I2C_SDAMode(uint8_t mode)
{GPIO_InitTypeDef GPIO_InitStruct;if(mode > 0){// 设置为输出模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏或推挽,外部需要接上拉电阻}else{// 设置为输入模式GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 浮空或上拉,外部需要接上拉电阻}GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Pin = I2C_SDA_GPIO_PIN;GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStruct);
}/*** 函    数:I2C起始* 参    数:无* 返 回 值:无*/
void MyI2C_Start(void)
{MyI2C_W_SDA(1);							//释放SDA,确保SDA为高电平MyI2C_W_SCL(1);							//释放SCL,确保SCL为高电平Delay_us(5);MyI2C_W_SDA(0);							//在SCL高电平期间,拉低SDA,产生起始信号MyI2C_W_SCL(0);							//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接Delay_us(5);
}/*** 函    数:I2C终止* 参    数:无* 返 回 值:无*/
void MyI2C_Stop(void)
{MyI2C_W_SCL(0);	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平Delay_us(5);MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号Delay_us(5);
}/*** 函    数:I2C发送应答信号* 参    数:ack,主机应答信号,1或0,1表示下一个时序不需要再接收数据,0表示下一个时序继续接收数据* 返 回 值:返回1,表示程序运行正常*/
uint8_t MyI2C_SendAck(int ack)
{Set_I2C_SDAMode(1);               if(ack == 1){// 发送ACkMyI2C_W_SDA(1); }else if(ack == 0){// 发送ACkMyI2C_W_SDA(0); }else{return 0;  // 入参有误,发送失败}  MyI2C_W_SCL(1);	Delay_us(5);MyI2C_W_SCL(0);	Delay_us(5);return 1;     //发送成功返回1为真	
}/*** 函    数:I2C接收应答信号* 参    数:无* 返 回 值:返回1,表示程序运行正常*/
uint8_t MyI2C_ReciveAck(void)
{uint8_t ack = 0;uint8_t timeout = 5;MyI2C_W_SDA(1); Set_I2C_SDAMode(0);  // SDA输入模式MyI2C_W_SCL(1);           Delay_us(5);  while(timeout--)       {// 等待从设备发送ACKif(MyI2C_R_SDA() == 0){// 读到应答信号ack = 1;  }else{// 没有读到应答信号,继续等待Delay_us(1);if(timeout == 0){// 等待超时ack = 0; }}}           MyI2C_W_SCL(0);             Delay_us(5);        Set_I2C_SDAMode(1); // SDA输出模式 return ack;
}/*** 函    数:I2C发送一个字节* 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF* 返 回 值:无*/
uint8_t MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位{if(0x80 & Byte){	MyI2C_W_SDA(1);}	else{MyI2C_W_SDA(0);}Byte <<= 1;MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDADelay_us(5);  MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据Delay_us(5);  }return MyI2C_ReciveAck();
}/*** 函    数:I2C接收一个字节* 参    数:无* 返 回 值:接收到的一个字节数据,范围:0x00~0xFF*/
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0,bit;				//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到Set_I2C_SDAMode(0);                     //SDA输入模式 for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位{Byte <<= 1;MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDADelay_us(5);if (MyI2C_R_SDA() == 1){bit = 0x80;}	else{bit = 0x00;}Byte |= bit; MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDADelay_us(5);}Set_I2C_SDAMode(1);                     //SDA输出模式 return Byte;							//返回接收到的一个字节数据
}/*** 函    数:I2C发送一个字节* 参    数:addr:发送设备地址,* 返 回 值:返回应答信号,返回1说明发送成功,返回0发送失败*/
uint8_t MyI2C_SendBytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{uint8_t i;uint8_t result = 0;MyI2C_Start();if(MyI2C_SendByte(addr << 1))    // 发送设备地址(7bit地址){// 收到应答,发送成功for (i = 0; i < buf_size; i++)  // 发送数据{if(MyI2C_SendByte(buf[i])){// 收到应答,发送成功result = 1;}else{// 没有收到应答,发送失败result = 0; }}}MyI2C_Stop();                   // 发送停止信号return result;
}/* IIC接收多个数据 */
uint8_t MyI2C_ReceiveBytes(uint8_t addr, uint8_t *buf, uint8_t buf_size)
{uint8_t i;    uint8_t result = 0;MyI2C_Start();if(MyI2C_SendByte((addr << 1) | 1))  // 发送设备地址(7bit地址){for (i = 0; i < buf_size; i++)    // 连续读取数据{*buf++ = MyI2C_ReceiveByte(); if (i == buf_size - 1){MyI2C_SendAck(1);        // 最后一个数据需要回NACK}else{        MyI2C_SendAck(0);        // 发送ACK}}result = 1;}MyI2C_Stop();                   // 发送停止信号return result;
}

MyI2C.h

#ifndef __MYI2C_H
#define __MYI2C_H/*这里将软件I2C使用引脚及外设定义在.H文件里,方便以后更换外设时修改,移植时只需要对这四个定义修改即可*/
#define      I2C_GPIO_CLK                	RCC_APB2Periph_GPIOB
#define      I2C_GPIO_PORT                 	GPIOB
#define      I2C_SCL_GPIO_PIN            	GPIO_Pin_13
#define      I2C_SDA_GPIO_PIN               GPIO_Pin_14void MyI2C_W_SCL(uint8_t BitValue);
void MyI2C_W_SDA(uint8_t BitValue);
uint8_t MyI2C_R_SDA(void);
void MyI2C_Init(void);
void Set_I2C_SDAMode(uint8_t mode);
void MyI2C_Start(void);
void MyI2C_Stop(void);	
uint8_t MyI2C_SendAck(int ack);
uint8_t MyI2C_ReciveAck(void);
uint8_t MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
uint8_t MyI2C_SendBytes(uint8_t addr, uint8_t *buf, uint8_t buf_size);
uint8_t MyI2C_ReceiveBytes(uint8_t addr, uint8_t *buf, uint8_t buf_size);#endif

BH1750.c

/*************************************************************************** 文件名  :bh1750.c* 描述    :光强传感模块     
****************************************************************************/
#include "stm32f10x.h"                  // Device header
#include "bh1750.h"
#include "Delay.h"
#include "MyI2C.h"#define LOG_ENABLE#ifdef LOG_ENABLE#include "stdio.h"#define LOG   printf
#else#define LOG(format, ...)    
#endifuint8_t current_mode;    // BH1750的测量模式
float current_light;     // BH1750的测量光照值// BH1750延时函数
void bh1750_Delay(uint16_t ms)
{// ms级延时,BH1750每次测量都需要时间,该函数用于等待测量结果Delay_ms(ms);
}// 写命令
uint8_t bh1750_write_cmd(uint8_t cmd)
{return MyI2C_SendBytes(BH1750_ADDRESS_LOW, &cmd, 1);
}// 写寄存器
uint8_t bh1750_read_regs(uint8_t *buf, uint8_t buf_size)
{return MyI2C_ReceiveBytes(BH1750_ADDRESS_LOW, buf, buf_size);
}// 复位
uint8_t bh1750_reset(void)
{return bh1750_write_cmd(BH1750_RESET);
}
// 打开电源
uint8_t bh1750_power_on(void)
{return bh1750_write_cmd(BH1750_POWER_ON);
}// 关闭电源
uint8_t bh1750_power_down(void)
{return bh1750_write_cmd(BH1750_POWER_DOWN);
}// 设置测量模式
uint8_t bh1750_set_measure_mode(uint8_t mode)
{uint8_t result = 0;if(bh1750_write_cmd(mode)){result =  1;}return result;
}// 单次读取光照值
uint8_t bh1750_single_read_light(uint8_t mode, float *light)
{// 单次测量模式在测量后会自动设置为断电模式   uint8_t temp[2];uint8_t result = 0;if(mode != BH1750_ONE_H_RES_MODE && mode != BH1750_ONE_H_RES_MODE2 && mode != BH1750_ONE_L_RES_MODE){LOG("bh1750 single read measure mode error! mode:0x%02x\r\n", mode);return result;}if(bh1750_set_measure_mode(mode)) // 每次采集前先设置模式{LOG("bh1750 set measure mode success! mode:0x%02x\r\n", mode);current_mode = mode;switch (mode){case BH1750_ONE_H_RES_MODE:  // 单次H分辨率模式(精度1lx,测量时间120ms)bh1750_Delay(120);  // 等待采集完成break;case BH1750_ONE_H_RES_MODE2: // 单次H分辨率模式(精度0.5lx,测量时间120ms)bh1750_Delay(120);  // 等待采集完成break;case BH1750_ONE_L_RES_MODE:  // 单次L分辨率模式(精度4lx,测量时间16ms)bh1750_Delay(16);  // 等待采集完成break;default:break;}if(bh1750_read_regs(temp, 2))  // 读取测量结果{*light = ((float)((temp[0] << 8) + temp[1]) / 1.2); // 换算成光照值result = 1;}else{LOG("bh1750 read light failed!\r\n");}}else{LOG("bh1750 set measure mode failed! mode:0x%02x\r\n", mode);return result;}return result;
}// 连续读取光照值
uint8_t bh1750_continuous_read_light(uint8_t mode, float *light)
{   uint8_t temp[2];uint8_t result = 0;if(mode != BH1750_CON_H_RES_MODE && mode != BH1750_CON_H_RES_MODE2 && mode != BH1750_CON_L_RES_MODE){LOG("bh1750 continuous read measure mode error! mode:0x%02x\r\n", mode);return result;}if(mode != current_mode){// 要使用的测量模式和BH1750当前的模式不同,则配置成相同模式if(bh1750_set_measure_mode(mode)){LOG("bh1750 set measure mode success! mode:0x%02x\r\n", mode);current_mode = mode;}else{// 模式设置失败LOG("bh1750 set measure mode failed! mode:0x%02x\r\n", mode);return result;}switch (mode){case BH1750_CON_H_RES_MODE:  // 连续H分辨率模式(精度1lx,测量时间120ms)bh1750_Delay(120);  // 等待采集完成break;case BH1750_CON_H_RES_MODE2: // 连续H分辨率模式(精度0.5lx,测量时间120ms)bh1750_Delay(120);  // 等待采集完成break;case BH1750_CON_L_RES_MODE:  // 连续L分辨率模式(精度4lx,测量时间16ms)bh1750_Delay(16);  // 等待采集完成break;default:break;}}if(bh1750_read_regs(temp, 2))  // 读取测量结果{*light = ((float)((temp[0] << 8) + temp[1]) / 1.2); // 换算成光照值result = 1;}else{LOG("bh1750 read light failed!\r\n");}return result;
}// BH1750初始化
uint8_t bh1750_init(void)
{uint8_t result = 0;MyI2C_Init();        // IIC初始化result = bh1750_power_on(); // 打开BH1750电源current_mode = 0;return result;
}// 单次读取BH1750例程
void bh1750_read_example(void)
{bh1750_single_read_light(BH1750_ONE_H_RES_MODE2, &current_light);printf("光照强度为:%0.1f\r\n",current_light);Delay_us(100);
}

BH1750.h

#ifndef __BH1750_H__
#define __BH1750_H__#include "stm32f10x.h"#define BH1750_ADDRESS_LOW      0x23    // addr low  7bit:0x23 8bit:0x46
#define BH1750_ADDRESS_HIGH     0x5C    // addr high 7bit:0x5C 8bit:0xB8/*bh1750 registers define */
#define BH1750_POWER_ON			0x01	// power on
#define BH1750_POWER_DOWN   	0x00	// power down
#define BH1750_RESET			0x07	// reset	
#define BH1750_CON_H_RES_MODE	0x10	// Continuously H-Resolution Mode
#define BH1750_CON_H_RES_MODE2	0x11	// Continuously H-Resolution Mode2 
#define BH1750_CON_L_RES_MODE	0x13	// Continuously L-Resolution Mode
#define BH1750_ONE_H_RES_MODE	0x20	// One Time H-Resolution Mode
#define BH1750_ONE_H_RES_MODE2	0x21	// One Time H-Resolution Mode2
#define BH1750_ONE_L_RES_MODE	0x23	// One Time L-Resolution Modeuint8_t bh1750_init(void);
void bh1750_read_example(void);#endif

04 STM32F103移植FreeRTOS

上面我们完成所有外设驱动程序得编写,那么为了解决上面提到的问题,现在我们进行移植FreeRTOS了。

参考教程:

FreeRTOS入门与工程实践-韦东山老师

FreeRTOS实践教程

FreeRTOS书籍-正点原子

上述资料先看韦东山老师的课程,韦东山老师讲解了一些必要的理论知识,有助于第二个实践课程的理解;建议直接刷完两套视频,刷视频过程中理解即可,不用跟着做,然后对着书自己做FreeRTOS项目。

这个版本的智能家居项目仅仅用到了FreeRTOS的任务创建、挂起与恢复以及中断,还有很多需要改进的地方,缺点也比较明显,后期看情况进行迭代。

05 Qt上位机开发

参考:

Qt仪表盘开发

Qt串口助手开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows I/O系统

硬件存储体系 寄存器 处理器内部定义的存储体&#xff0c;它们除了存储功能&#xff0c;往往还兼有其他的能力&#xff0c;比如参与运算&#xff0c;地址解析&#xff0c;指示处理器的状态&#xff0c;等等。寄存器是由处理器内部专门的触发器电路实现的&#xff0c;处理器往…

2024年高教杯国赛(B题)数学建模竞赛解题思路|完整代码论文集合

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

基于物联网的低成本便携式传感器节点用于火灾和空气污染的检测与报警

目录 摘要 引言 材料和方法 传感器节点 IoT 微控制器 颗粒物传感器 环境和气体传感器 MQTT代理 Node-Red监控平台 系统结构 数据存储 工作描述 实验结果 讨论 结论 致谢 参考文献 这篇论文的标题是《Low-cost IoT-based Portable Sensor Node for Fire and Air…

数据要素大市场打开新局面——数字经济背景下中国特色的数据产权三权分置

前言 数字经济飞速发展的大背景下&#xff0c;无论是数据要素大市场还是数据资产入表的发展和落地&#xff0c;都需要以数据产权基本制度为保障。对数据产权给与恰当的权利定位&#xff0c;有助于让数据成为创造和捕获价值的新经济资源。数据的价值在于使用&#xff0c;而传统…

链表leetcode-1

目录 1.常用技巧 1.1引入虚拟头结点 1.2对于变量&#xff0c;不要太吝啬&#xff0c;可以多定义&#xff0c;方便处理 1.3快慢双指针 2.例题 2.1两数相加 2.2两两交换链表中的节点 2.3重排链表 2.4合并K个升序链表 2.5K个一组翻转链表 1.常用技巧 1.1引入虚拟头结点 可…

场景是人工智能第四要素,是垂直领域人工智能的第一要素。

"场景是人工智能的第四要素&#xff0c;与数据、算力、算法同等重要。"拿着技术找场景&#xff0c;还是拿着场景找技术&#xff1f;这个锤子和钉子的问题&#xff0c;一直困扰着各家AI大厂。从近5年的实践来看&#xff0c;拿着场景找技术是更为稳健的&#xff0c;否则…

哪款宠物空气净化器能更好的清理浮毛?希喂、352、IAM测评分享

家里这三只可爱的小猫咪&#xff0c;已然成为了我们生活中不可或缺的家庭成员&#xff0c;陪伴我们度过了说长不长说短不短的五年时光。时常庆幸自己当年选择养它们&#xff0c;在我失落的时候总能给我安慰&#xff0c;治愈我多时。 但这个温馨的背后也有一点小烦恼&#xff0…

ES6语法详解

以下是ES6常用的一些语法和特性&#xff1a; 声明变量的关键字变化&#xff1a;使用let和const、var来声明变量。 箭头函数&#xff1a;使用箭头&#xff08;>&#xff09;定义函数&#xff0c;简化函数的写法。 模板字符串&#xff1a;使用反引号&#xff08;&#xff0…

【java入门】关键字、标识符与变量初识

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

Docker Elasticsearch安装ik分词插件教程

本章教程在通过Docker 安装Elasticsearch,并安装ik分词插件。本文的重点是安装ik分词插件。 一、安装Elasticsearch 安装教程以前写过,参考:https://blog.csdn.net/qq_19309473/article/details/140725121 安装之后,通过http://ip:9200,可以访问,就表示安装成功。 二、安装…

Linux终端简单配置(Vim、oh-my-zsh和Terminator)

文章目录 0. 概述1. 完整Vim配置2. Vim配置方案解释2.1 状态行与配色方案2.2 文件管理与缓存设置2.3 搜索与导航优化2.4 缩进与格式化设置2.5 粘贴模式快捷切换2.6 文件编码与格式2.7 性能优化 3. 安装 Oh My Zsh 及配置3.1 安装 Oh My Zsh3.2 Oh My Zsh 配置 3. Terminator终端…

vscode +STM32 VS CODE EXTENSION

stm32 vs code extersion 1.0.0版本可以直接导入cubeide的工程&#xff0c;之后版本不可以&#xff0c;所以为了省事&#xff0c;使用stm32 vs code extersion 1.0.0插件。 安装完stm32 vs code extersion插件&#xff0c;会默认把相关插件一起安装。但是需要手动安装Ninja&am…

交叉编译概念

交叉编译概念 目录 交叉编译概念1. 什么是交叉编译2. 交叉编译的作用3. 交叉编译器4. 交叉编译工具链5. 交叉编译的一般步骤6. 交叉编译实例 1. 什么是交叉编译 交叉编译是指在一个平台上编译代码&#xff0c;使其能够在另一个不同的平台上运行的过程。这种编译方式通常用于开…

Android12——Launcher3文件夹布局修改调整

文章声明&#xff1a;本文是笔者参考良心大佬作品后结合实际需求进行相应的定制&#xff0c;本篇主要是笔者记录一次解析bug笔记&#xff0c;文中可能会引用大佬文章中的部分图片在此声明&#xff0c;并非盈利目的&#xff0c;如涉嫌侵权请私信&#xff0c;谢谢&#xff01; 大…

什么是函数调用约定?

目录 前言 一、函数调用约定的主要内容 二、常见的函数调用约定 1. __cdecl&#xff08;C Declaration&#xff09; 2. __stdcall&#xff08;Standard Call&#xff09; 3. __fastcall&#xff08;Fast Call&#xff09; 4. __thiscall&#xff08;This Call&#xff0…

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式4629张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4629 标注数量(xml文件个数)&#xff1a;4629 标注数量(txt文件个数)&#xff1a;4629 标注…

【SQL】删除表中重复数据的方法

很久之前我写入一张sql的数据表&#xff0c;它里面有很多重复的内容。然后我想只保留一条原始数据&#xff1a; 例如上面的时间&#xff0c;出现了很多重复值。 我最初用的是这种方法&#xff1a; SELECT * FROM table_name WHERE primary_key IN (SELECT max(primary_key)F…

仕考网:公务员笔试和面试哪个难?

公务员笔试和面试哪个难?二者之间考察的方向不同&#xff0c;难度也是不同的。 笔试部分因其广泛的知识点和有限的考试时间显得难度更高一些&#xff0c;在笔试环节中&#xff0c;考生需在有限的时间内应对各种问题&#xff0c;而且同时还要面对激烈的竞争&#xff0c;在众多…

栈栈栈专题

一、基础 Leetcode 3174. 清除数字 class Solution { public:string clearDigits(string s) {string st; // 用string模拟栈的行为for(auto& v: s){if(isdigit(v)) st.pop_back();else st v;}return st;} }; 二、进阶 三、邻项消除 四、合法括号字符串 五、…

50Kg大载重长航时油电混动多旋翼无人机技术详解

50Kg大载重长航时油电混动多旋翼无人机技术是一项高度复杂且前沿的研究领域&#xff0c;它结合了燃油发动机的高能量密度和电动机的高效性&#xff0c;旨在提高无人机的续航能力和载重能力。以下是对该技术的详细解析&#xff1a; 产品轴距&#xff1a;2320mm 产品尺寸&#x…