智能家居系统(基于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.rhkb.cn/news/417301.html

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

相关文章

Meta关闭Spark AR平台:未来规划与影响分析

Meta宣布将关闭其移动AR创作平台Spark AR&#xff0c;这一消息在业界引起了广泛关注。尽管Snap和TikTok在AR滤镜领域取得了巨大成功&#xff0c;但Meta却选择了另一条发展道路。本文将探讨这一决策背后的可能原因及其对未来的影响。 关闭Spark AR平台的背后 硬件为主&#xff…

计算机网络(三) —— 简单Udp网络程序

目录 一&#xff0c;初始化服务器 1.0 辅助文件 1.1 socket函数 1.2 填充sockaddr结构体 1.3 bind绑定函数 1.4 字符串IP和整数IP的转换 二&#xff0c;运行服务器 2.1 接收 2.2 处理 2.3 返回 三&#xff0c;客户端实现 3.1 UdpClient.cc 实现 3.2 Main.cc 实现 …

【Mysql】系统服务启动访问报错问题处理:this is incompatible with sql_mode=only_full_group_by

一、背景&#xff1a; 本来已经正常运行的平台&#xff0c;突然有一天由于对服务器进行部分操作迁移&#xff0c;发现jar可以正常启动&#xff0c;但是访问功能一直报错&#xff0c;监控后台日志后&#xff0c;发现了问题&#xff1a; 报错的具体信息如下&#xff1a; Caused…

Linux编译器--gcc/g++使用

目录 一、预编译指令 1.1预处理功能 1.2指令 1.3问题扩展 二、编译&#xff08;生成汇编&#xff09; 三、汇编&#xff08;生成二进制机器语言&#xff09; 四、链接&#xff08;生成可执行文件或库文件&#xff09; 4.1库文件 4.2目标文件和库的链接 4.3动态库和静态…

【Django-Minio-Storage 使用教程】

Django-Minio-Storage 使用教程 安装 Django-Minio-Storage配置 Django 项目官方文档 安装 Django-Minio-Storage 使用 pip 安装 Django-Minio-Storage pip install django-minio-storage配置 Django 项目 在 Django 项目的 settings.py 文件中进行以下配置 INSTALLED_APPS…

【mysql】mysql修改sql_mode之后无法启动

现象&#xff1a;修改后mysql无法启动&#xff0c;不报错 原因&#xff1a;MySQL在8以后sql_mode已经取消了NO_AUTO_CREATE_USER这个关键字。去掉这个关键字后&#xff0c;启动就可以了 修改前&#xff1a; sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR…

Bootstrap前端框架Glyphicons字体图标

115工具网收集提供Bootstrap前端框架Glyphicons字体图标库对照表​​​​​​​&#xff0c;Bootstrap前端UI,Glyphicons字体图标调用,Bootstrap按钮字体图标对照表,包括250多个来自Glyphicon Halflings的字体图标.项目中引用Bootstrap相关文件后即可直接调用下列图标class&quo…

Linux CentOS安装PySpark3.5(单机版)详细教程及机器学习实战

目录 一、安装须知 二、安装Spark 1、下载安装包 2、修改配置文件spark-env.sh 3、验证Spark是否安装成功 三、安装py4j 四、配置环境变量 五、基于PySpark的机器学习实战 1、将数据文件上传HDFS 2、创建代码文件 3、提交应用程序 一、安装须知 前置依赖&#xff1…

Acrobat Pro DC 2023 for Mac/Win:全能型PDF编辑器深度解析

Adobe Acrobat Pro DC 2023作为一款跨平台的PDF编辑器&#xff0c;无论是对于Mac还是Windows用户&#xff0c;都提供了极为全面且强大的PDF处理功能。该软件凭借其卓越的性能和丰富的特性&#xff0c;成为了全球范围内用户处理PDF文档的首选工具。 一、强大的编辑功能 Acroba…

【2024高教社杯全国大学生数学建模竞赛】ABCDEF题 问题分析、模型建立、参考文献及实现代码

【2024高教社杯全国大学生数学建模竞赛】ABCDEF题 问题分析、模型建立、参考文献及实现代码 1 比赛时间 北京时间&#xff1a;2024年9月5日 18:00-2024年9月8日20:00 2 思路内容 2.1 往届比赛资料 【2022高教社杯数学建模】C题&#xff1a;古代玻璃制品的成分分析与鉴别方案…

HBase 部署及shell操作

HBase 数据库 一、HBase 概述1.1 HBase 是什么HBase 的特点 二、HBase 模型及架构2.1 HBase 逻辑模型2.2 HBase 数据模型2.3 HBase 物理模型2.3.1 列簇物理模型2.3.2 Rowkey 字段排序2.3.3 Region 存储到不同节点2.3.4 Region 结构 2.4 HBase 基本架构 三、搭建 HBase 分布式集…

Claude的小白入门指南

要想快速上手Claude AI&#xff0c;其实并没有那么复杂。作为新一代的AI助手&#xff0c;Claude致力于为用户提供高效、无害、透明的交互体验。这篇入门指南将从Claude AI的特点、主要功能和如何实际操作等几个方面为大家做一个详细的介绍。 Claude AI是什么&#xff1f; Claud…

【SRC挖掘】越权漏洞——burp插件被动检测越权漏洞,一个插件让挖洞效率翻倍!Autorize

越权与未授权漏洞 越权漏洞什么是越权漏洞&#xff1f;Autorize插件安装使用步骤拦截过滤器 越权漏洞 什么是越权漏洞&#xff1f; 越权漏洞是指应用程序未对当前用户操作的身份权限进行严格校验&#xff0c;导致用户可以操作超出自己管理权限范围的功能&#xff0c;从而操作…

大模型笔记01--基于ollama和open-webui快速部署chatgpt

大模型笔记01--基于ollama和open-webui快速部署chatgpt 介绍部署&测试安装ollama运行open-webui测试 注意事项说明 介绍 近年来AI大模型得到快速发展&#xff0c;各种大模型如雨后春笋一样涌出&#xff0c;逐步融入各行各业。与之相关的各类开源大模型系统工具也得到了快速…

UnityShader自定义属性特性

前言&#xff1a; 在编写UnityShader时&#xff0c;我们常常会使用特性来更换材质球面板的属性外观&#xff0c;除此之外&#xff0c;还可以使用自定义的扩展脚本来实现自定义的材质球界面&#xff0c;参考我之前的文章UnityShaderUI编辑器扩展 但是自定义扩展每次都要单独写…

性能测试经典案例解析——远程培训系统

各位好&#xff0c;我是 道普云 一站式云测试SaaS平台。一个在软件测试道路上不断折腾十余年的萌新。 欢迎关注我的专栏和我的主页 道普云 文章内容具有一定门槛&#xff0c;建议先赞再收藏慢慢学习&#xff0c;有不懂的问题欢迎私聊我。 希望这篇文章对想提高软件测试水平…

5.sklearn-朴素贝叶斯算法、决策树、随机森林

文章目录 环境配置&#xff08;必看&#xff09;头文件引用1.朴素贝叶斯算法代码运行结果优缺点 2.决策树代码运行结果决策树可视化图片优缺点 3.随机森林代码RandomForestClassifier()运行结果总结 本章学习资源 环境配置&#xff08;必看&#xff09; Anaconda-创建虚拟环境…

Keil下载烧录程序到单片机提示flash outtime超时

Flash Timeout.Reset the Target and try it again. Error:Flash Download failed - “Cortex-M4” 解决方法&#xff1a; 1.检查杜邦线 2.RESET按键按一下再下载(或者一直按着下载)&#xff0c;这样能让单片机进入烧录模式。 3.有外部看门狗&#xff0c;外部看门狗用跳帽屏…

『功能项目』DOTween动态文字【26】

打开上一篇25协程生成怪物模型的项目&#xff0c; 本章要做的事情是用DOTween插件做一个动态文字效果 首先在资源商店中免费下载一个DOTween插件 新建脚本&#xff1a;DowteenFlicker.cs 编写脚本&#xff1a; using DG.Tweening; using UnityEngine; using UnityEngine.UI;pu…

2024 年高教社杯全国大学生数学建模竞赛题目-C 题 农作物的种植策略

根据乡村的实际情况&#xff0c;充分利用有限的耕地资源&#xff0c;因地制宜&#xff0c;发展有机种植产业&#xff0c;对乡村经济 的可持续发展具有重要的现实意义。选择适宜的农作物&#xff0c;优化种植策略&#xff0c;有利于方便田间管理&#xff0c;提 高生产效益&#…