当前位置: 首页 > news >正文

上海协策网站互动营销型网站建设

上海协策网站,互动营销型网站建设,网站生鲜建设市场分析,开个不愁销路的小厂视频演示#xff1a;基于STM32F103C8T6标准库FreeRTOSQt串口开发实现的智能家居项目_哔哩哔哩_bilibili 基于STM32F103C8T6标准库FreeRTOSQt串口开发实现的智能家居项目: https://pan.baidu.com/s/1f41gAfOOnlcQoKoMx3o84A?pwd6j2g 提取码: 6j2g 注#xff1a;本项目为学习完…视频演示基于STM32F103C8T6标准库FreeRTOSQt串口开发实现的智能家居项目_哔哩哔哩_bilibili 基于STM32F103C8T6标准库FreeRTOSQt串口开发实现的智能家居项目: https://pan.baidu.com/s/1f41gAfOOnlcQoKoMx3o84A?pwd6j2g 提取码: 6j2g 注本项目为学习完《江科大STM32教程》实战项目。受限于个人水平还有很多需要改进的地方还请读者见谅如有不当之处还请指证。 01 项目需求 1.主控STM32F103C8T6 2.按键LED模拟开关灯支持本地控制和上位机控制 3.按键直流电机驱动模块直流电机风扇叶片模拟降温系统支持本地控制和上位机控制本地控制实现直流电机转或不转上位机控制带调速功能 4.按键无源蜂鸣器模拟音乐播放系统支持本地控制和上位机控制 5.DHT11温湿度传感器测量温湿度并上报 6.BH1750光照传感器测量光照强度并上报 7.OLED显示屏显示温湿度、光照强度、音乐播放状态 8.Qt串口上位机Qt上位机实现LED、风扇、音乐播放控制以及温湿度采集。 02 硬件连接 注意本电路仅体现个模块之间的电路连接。原理图绘制参看 嘉立创EDA使用流程 本来一开始想用ESP8266使用mQTT协议进行远程控制的不过在做项目过程中ESP8266似乎被玩坏了所以临时改用串口控制使用STM32的串口1上图中ESP8266模块就没用到了后期看情况更新MQTT版本。 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);#endif3.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);#endifMotor.c /** *Encoding:GB2312 *文件功能:直流电机驱动功能实现 **/#include stm32f10x.h // Device header #include PWM.h #include stdbool.hbool 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.hvoid Motor_Init(void); bool Motor_SetSpeed(int8_t Speed);#endif3.2.2 风扇电机按键驱动  Key2.c /** *Encoding:GB2312 *文件功能:按键功能实现 **/ #include stm32f10x.h // Device header #include Delay.h #include Key2.h #include Motor.h #include stdbool.hextern 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);#endifMusic.c /** *Encoding:GB2312 *文件功能:音乐播放软件功能实现 **/#include stm32f10x.h // Device header #include Buzzer.H #include stm32f10x.h #include Delay.h #include OLED.huint8_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(i0;i246;i){ c5;jtm[i]/25;timej*180;if(i49||i67||i178){c0;} Play_Music(a[i],time,c);} }Music.h #ifndef __SOUND_H__ #define __SOUND_H__void B_Music(void);#endif3.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.hextern 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(如果原来大于1MusicState按下暂停后归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.hextern 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(如果原来大于1MusicState按下暂停后归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.huint8_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.hint 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或01表示下一个时序不需要再接收数据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测量时间120msbh1750_Delay(120); // 等待采集完成break;case BH1750_ONE_H_RES_MODE2: // 单次H分辨率模式精度0.5lx测量时间120msbh1750_Delay(120); // 等待采集完成break;case BH1750_ONE_L_RES_MODE: // 单次L分辨率模式精度4lx测量时间16msbh1750_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测量时间120msbh1750_Delay(120); // 等待采集完成break;case BH1750_CON_H_RES_MODE2: // 连续H分辨率模式精度0.5lx测量时间120msbh1750_Delay(120); // 等待采集完成break;case BH1750_CON_L_RES_MODE: // 连续L分辨率模式精度4lx测量时间16msbh1750_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);#endif04 STM32F103移植FreeRTOS 上面我们完成所有外设驱动程序得编写那么为了解决上面提到的问题现在我们进行移植FreeRTOS了。 参考教程 FreeRTOS入门与工程实践-韦东山老师 FreeRTOS实践教程 FreeRTOS书籍-正点原子 上述资料先看韦东山老师的课程韦东山老师讲解了一些必要的理论知识有助于第二个实践课程的理解建议直接刷完两套视频刷视频过程中理解即可不用跟着做然后对着书自己做FreeRTOS项目。 这个版本的智能家居项目仅仅用到了FreeRTOS的任务创建、挂起与恢复以及中断还有很多需要改进的地方缺点也比较明显后期看情况进行迭代。 05 Qt上位机开发 参考 Qt仪表盘开发 Qt串口助手开发
http://www.w-s-a.com/news/925786/

相关文章:

  • 前端设计除了做网站还能做什么江苏高校品牌专业建设工程网站
  • 做二手房产网站多少钱用户权限配置wordpress
  • 做亚马逊网站需要租办公室吗小型企业网站模板
  • 网站全屏视频怎么做个人公司注册网上申请
  • 如何k掉别人的网站搜索引擎优化与关键词的关系
  • 百度推广 网站吸引力做网站开发的薪酬怎么样
  • js网站开发工具软件营销方案
  • 做网站的天空网云南省建设厅网站怎么进不去
  • 天津网站排名提升网络营销推广策略包括哪些
  • 网站建设与管理 ppt网站打开是别人的
  • 图片网站怎么做排名怎么分析一个网站seo
  • 伪原创对网站的影响深圳装修公司排名100强
  • 网站建设公司效果个人可以做医疗信息网站吗
  • 网站使用arial字体下载微网站 建设
  • 文化馆网站建设意义营销型国外网站
  • 公司网站定位建议wordpress怎么用模板
  • 中国十大热门网站排名计算机选什么专业最好
  • 怀化建设企业网站太原网站关键词排名
  • 空间注册网站网站制作是怎么做的
  • 数码家电商城网站源码一个网站的成本
  • 网站伪静态是什么意思麻涌东莞网站建设
  • 理县网站建设公司郑州仿站定制模板建站
  • 手机网站建设网站报价诸城人才网招聘网
  • 一起做网站怎么下单临沂网站制作
  • 公司网站案例企业网站 模版
  • 做的好的响应式网站有哪些网站界面设计案例
  • 上海创意型网站建设icp备案网站信息
  • 网站没收录中山手机网站制作哪家好
  • 代驾软件开发流程wordpress 博客主题 seo
  • 成都的教育品牌网站建设网站广告js代码添加