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

本文主要是介绍智能家居系统(基于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串口助手开发

这篇关于智能家居系统(基于STM32F103C8T6标准库+FreeRTOS+Qt串口开发实现)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141018

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象