文章目录:
一:阵列键盘
1.阵列键盘测试程序
KEYPAD4x4.h
KEYPAD4x4.c
main.c
2.键盘中断测试程序
NVIC.h
NVIC.c
main.c
二:舵机控制
1.延时函数驱动舵机程序
SG90.h
SG90.c
main.c
2.PWM(脉冲宽度调制 脉宽调制/占空比)驱动舵机程序
pwm.h
pwm.c
main.c
三:DHT11芯片(DHT11温湿度显示程序)
dht11.h
dht11.c
main.c
四:MPU6050(六轴加速度和陀螺仪传感器芯片)
MPU6050.h
MPU6050.c
main.c
文章来源:https://uudwc.com/A/xkVMm
一:阵列键盘
对跳线进行设置
第一步:把ADC输入的两个跳线断开 第二步:将模拟摇杆的三个跳线断开 第三步:将触摸按键的四个跳线断开 第四步:将旋转编码器的三个跳线断开
1.阵列键盘测试程序
按下时:红线蓝线短接
阵列键盘
新建文件夹文章来源地址https://uudwc.com/A/xkVMm
Hardware文件夹——>KEYPAD4x4文件夹——>KEYPAD4x4.c KEYPAD4x4.h
KEYPAD4x4.h
#ifndef __KEYPAD4x4_H #define __KEYPAD4x4_H #include "sys.h" #include "delay.h" #define KEYPAD4x4PORT GPIOA //定义IO接口组 #define KEY1 GPIO_Pin_0 //定义IO接口 #define KEY2 GPIO_Pin_1 //定义IO接口 #define KEY3 GPIO_Pin_2 //定义IO接口 #define KEY4 GPIO_Pin_3 //定义IO接口 #define KEYa GPIO_Pin_4 //定义IO接口 #define KEYb GPIO_Pin_5 //定义IO接口 #define KEYc GPIO_Pin_6 //定义IO接口 #define KEYd GPIO_Pin_7 //定义IO接口 void KEYPAD4x4_Init(void);//初始化 void KEYPAD4x4_Init2(void);//初始化2(用于IO工作方式反转) u8 KEYPAD4x4_Read (void);//读阵列键盘 #endif
KEYPAD4x4.c
#include "KEYPAD4x4.h" void KEYPAD4x4_Init(void){ //微动开关的接口初始化 GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO的初始化枚举结构 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Pin = KEYa | KEYb | KEYc | KEYd; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻 GPIO_Init(KEYPAD4x4PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2 | KEY3 | KEY4; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 //上拉电阻 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(KEYPAD4x4PORT,&GPIO_InitStructure); } void KEYPAD4x4_Init2(void){ //微动开关的接口初始化2(用于IO工作方式反转) GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO的初始化枚举结构 GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2 | KEY3 | KEY4; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻 GPIO_Init(KEYPAD4x4PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = KEYa | KEYb | KEYc | KEYd; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 //上拉电阻 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(KEYPAD4x4PORT,&GPIO_InitStructure); } u8 KEYPAD4x4_Read (void){//键盘处理函数 u8 a=0,b=0;//定义变量 KEYPAD4x4_Init();//初始化IO GPIO_ResetBits(KEYPAD4x4PORT,KEY1|KEY2|KEY3|KEY4); GPIO_SetBits(KEYPAD4x4PORT,KEYa|KEYb|KEYc|KEYd); if(!GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYa) || //查寻键盘口的值是否变化 !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYb) || !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYc) || !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYd)){ delay_ms (20);//延时20毫秒 if(!GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYa) || //查寻键盘口的值是否变化 !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYb) || !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYc) || !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEYd)){ a = GPIO_ReadInputData(KEYPAD4x4PORT)&0xff;//键值放入寄存器a } KEYPAD4x4_Init2();//IO工作方式反转 GPIO_SetBits(KEYPAD4x4PORT,KEY1|KEY2|KEY3|KEY4); GPIO_ResetBits(KEYPAD4x4PORT,KEYa|KEYb|KEYc|KEYd); b = GPIO_ReadInputData(KEYPAD4x4PORT)&0xff;//将第二次取得值放入寄存器b a = a|b;//将两个数据相或 switch(a){//对比数据值 case 0xee: b = 16; break;//对比得到的键值给b一个应用数据 case 0xed: b = 15; break; case 0xeb: b = 14; break; case 0xe7: b = 13; break; case 0xde: b = 12; break; case 0xdd: b = 11; break; case 0xdb: b = 10; break; case 0xd7: b = 9; break; case 0xbe: b = 8; break; case 0xbd: b = 7; break; case 0xbb: b = 6; break; case 0xb7: b = 5; break; case 0x7e: b = 4; break; case 0x7d: b = 3; break; case 0x7b: b = 2; break; case 0x77: b = 1; break; default: b = 0; break;//键值错误处理 } while(!GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEY1) || //等待按键放开 !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEY2) || !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEY3) || !GPIO_ReadInputDataBit(KEYPAD4x4PORT,KEY4)); delay_ms (20);//延时20毫秒 } return (b);//将b作为返回值 }
main.c
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "relay.h" #include "oled0561.h" #include "KEYPAD4x4.h" int main (void){//主程序 u8 s; delay_ms(500); //上电时等待其他器件就绪 RCC_Configuration(); //系统时钟初始化 RELAY_Init();//继电器初始化 I2C_Configuration();//I2C初始化 OLED0561_Init(); //OLED初始化 OLED_DISPLAY_8x16_BUFFER(0," YoungTalk "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(3," KEYPAD4x4 TEST "); //显示字符串 KEYPAD4x4_Init();//阵列键盘初始化 while(1){ s=KEYPAD4x4_Read();//读出按键值 //有按键被按下 if(s!=0){ //如按键值不是0,也就是说有按键操作,则判断为真 //-------------------------"----------------" OLED_DISPLAY_8x16_BUFFER(6," KEY NO. "); //显示字符串 OLED_DISPLAY_8x16(6,8*8,s/10+0x30); //显示十位 偏移量 OLED_DISPLAY_8x16(6,9*8,s%10+0x30); //显示个位 } } }
2.键盘中断测试程序
嵌套:即是在进入一个中断处理程序之后,还能在中断之内再次产生中断 NVIC嵌套向量中断控制器 cortex-m3支持256个中断,其中包含了16个内核中断,240个外部中断 stm32只有84个中断,包括16个内核中断和68个可屏蔽中断 stm32f103上只有60个可屏蔽中断 优先级的设置(设置值越小,优先级越高) NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级 NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级 抢占优先级 又称:主优先级 在嵌套时,抢占优先级较高的可以在较低的中断内嵌套中断 同一抢占优先级不能嵌套,必须前一个中断处理完成才能进入下一个 不同抢占优先级下,响应优先级没有意义 响应优先级 又称:子优先级/亚优先级/次优先级/副优先级 同一抢占优先级的中断同时产生时,响应优先级较高的先处理 同一抢占优先级不能嵌套
新建文件夹
Basic文件夹——>nvic文件夹——>NVIC.c NVIC.h
NVIC.h
#ifndef __NVIC_H #define __NVIC_H #include "sys.h" extern u8 INT_MARK; //中断标志位 void KEYPAD4x4_INT_INIT (void); //中断初始化函数 #endif
NVIC.c
#include "NVIC.h" u8 INT_MARK;//中断标志位 void KEYPAD4x4_INT_INIT (void){ //按键中断初始化 NVIC_InitTypeDef NVIC_InitStruct; //定义结构体变量 EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //启动GPIO时钟 (需要与复用时钟一同启动) RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);//配置端口中断需要启用复用时钟 //第1个中断 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4); //定义 GPIO 中断 EXTI_InitStruct.EXTI_Line=EXTI_Line4; //定义中断线 EXTI_InitStruct.EXTI_LineCmd=ENABLE; //中断使能 EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式为 中断 EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发 EXTI_Init(& EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel=EXTI4_IRQn; //中断线 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级 2 NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级 2 NVIC_Init(& NVIC_InitStruct); //第2个中断 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5); //定义 GPIO 中断 EXTI_InitStruct.EXTI_Line=EXTI_Line5; //定义中断线 EXTI_InitStruct.EXTI_LineCmd=ENABLE; //中断使能 EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式为 中断 EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发 EXTI_Init(& EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel=EXTI9_5_IRQn; //中断线 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级 2 NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级 2 NVIC_Init(& NVIC_InitStruct); //第3个中断 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6); //定义 GPIO 中断 EXTI_InitStruct.EXTI_Line=EXTI_Line6; //定义中断线 EXTI_InitStruct.EXTI_LineCmd=ENABLE; //中断使能 EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式为 中断 EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发 EXTI_Init(& EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel=EXTI9_5_IRQn; //中断线 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级 2 NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级 2 NVIC_Init(& NVIC_InitStruct); //第4个中断 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7); //定义 GPIO 中断 EXTI_InitStruct.EXTI_Line=EXTI_Line7; //定义中断线 EXTI_InitStruct.EXTI_LineCmd=ENABLE; //中断使能 EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式为 中断 EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发 EXTI_Init(& EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel=EXTI9_5_IRQn; //中断线 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级 2 NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级 2 NVIC_Init(& NVIC_InitStruct); } void EXTI4_IRQHandler(void){ if(EXTI_GetITStatus(EXTI_Line4)!=RESET){//判断某个线上的中断是否发生 INT_MARK=1;//标志位置1,表示有按键中断 EXTI_ClearITPendingBit(EXTI_Line4); //清除 LINE 上的中断标志位 } } void EXTI9_5_IRQHandler(void){ if(EXTI_GetITStatus(EXTI_Line5)!=RESET){//判断某个线上的中断是否发生 INT_MARK=2;//标志位置1,表示有按键中断 EXTI_ClearITPendingBit(EXTI_Line5); //清除 LINE 上的中断标志位 } if(EXTI_GetITStatus(EXTI_Line6)!=RESET){//判断某个线上的中断是否发生 INT_MARK=3;//标志位置1,表示有按键中断 EXTI_ClearITPendingBit(EXTI_Line6); //清除 LINE 上的中断标志位 } if(EXTI_GetITStatus(EXTI_Line7)!=RESET){//判断某个线上的中断是否发生 INT_MARK=4;//标志位置1,表示有按键中断 EXTI_ClearITPendingBit(EXTI_Line7); //清除 LINE 上的中断标志位 } }
main.c
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "relay.h" #include "oled0561.h" #include "KEYPAD4x4.h" #include "NVIC.h" int main (void){//主程序 u8 s; delay_ms(500); //上电时等待其他器件就绪 RCC_Configuration(); //系统时钟初始化 RELAY_Init();//继电器初始化 I2C_Configuration();//I2C初始化 OLED0561_Init(); //OLED初始化 OLED_DISPLAY_8x16_BUFFER(0," YoungTalk "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(3," KEYPAD4x4 TEST "); //显示字符串 INT_MARK=0;//标志位清0 NVIC_Configuration();//设置中断优先级 KEYPAD4x4_Init();//阵列键盘初始化 KEYPAD4x4_INT_INIT();//阵列键盘的中断初始化 while(1){ //其他程序内容 if(INT_MARK){ //中断标志位为1表示有按键中断 INT_MARK=0;//标志位清0 s=KEYPAD4x4_Read();//读出按键值 if(s!=0){ //如按键值不是0,也就是说有按键操作,则判断为真 //-------------------------"----------------" OLED_DISPLAY_8x16_BUFFER(6," KEY NO. "); //显示字符串 OLED_DISPLAY_8x16(6,8*8,s/10+0x30);// OLED_DISPLAY_8x16(6,9*8,s%10+0x30);// } } } }
二:舵机控制
第一步:将舵机连接到开发板上(舵机接口) 第二步:将触摸按键的四个跳线帽短接
注意事项 1.舵机上如果有负载需要更大的驱动电流 2.必须连续不断地向PWM线发出角度波形,直到达到对应角度 3.一般情况下,180度舵机的最大角度可达190度,甚至200度 4.要按指定型号舵机的波形与角度对应关系做调试,不盲信理论值
新建文件夹
Hardware文件夹——>SG90文件夹——>SG90.c SG90.h
1.延时函数驱动舵机程序
延时函数利用滴答计时器,虽然可以事项该功能,但实际开发中,这种效率很低
SG90.h
#ifndef __SG90_H #define __SG90_H #include "sys.h" #include "delay.h" #define SE_PORT GPIOA //定义IO接口 #define SE_OUT GPIO_Pin_15 //定义IO接口 void SG90_Init(void);//SG90舵机初始化 void SG90_angle(u8 a);//舵机角度设置 #endif
SG90.c
#include "SG90.h" void SG90_Init(void){ //舵机接口初始化 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Pin = SE_OUT; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(SE_PORT, &GPIO_InitStructure); GPIO_WriteBit(SE_PORT,SE_OUT,(BitAction)(0)); //接口输出高电平1 } void SG90_angle(u8 a){ //舵机角度控制设置(参数值0~180)对应角度0~180度 u8 b=100;//角度校正偏移量 GPIO_WriteBit(SE_PORT,SE_OUT,(BitAction)(1)); //接口输出高电平1 delay_us(500+a*10+b); //延时 GPIO_WriteBit(SE_PORT,SE_OUT,(BitAction)(0)); //接口输出高电平1 delay_us(19500-a*10-b); //延时 }
main.c
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "relay.h" #include "oled0561.h" #include "SG90.h" #include "touch_key.h" int main (void){//主程序 delay_ms(500); //上电时等待其他器件就绪 RCC_Configuration(); //系统时钟初始化 RELAY_Init();//继电器初始化 I2C_Configuration();//I2C初始化 OLED0561_Init(); //OLED初始化 OLED_DISPLAY_8x16_BUFFER(0," YoungTalk "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(3," SG90 TEST "); //显示字符串 TOUCH_KEY_Init();//按键初始化 SG90_Init();//SG90舵机初始化 SG90_angle(0);//舵机初步为0(最小值) while(1){ if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 0 "); //显示字符串 SG90_angle(0);//舵机初步为0(最小值) } if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 45 "); //显示字符串 SG90_angle(45);//舵机初步为0(最小值) } if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 90 "); //显示字符串 SG90_angle(90);//舵机初步为0(最小值) } if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 180 "); //显示字符串 SG90_angle(180);//舵机初步为0(最小值) } } }
2.PWM(脉冲宽度调制 脉宽调制/占空比)驱动舵机程序
使用定时器产生PWM(定时器是独立工作的) 不管用户是否按键,PWM都会处在工作状态,一直输出脉冲来控制舵机(舵机会有抖动)
PWM:是利用微处理器的数字输出 来对模拟电路进行控制的一种非常有效的技术 应用:在从测量、通信到功率控制与变换的许多领域中 必须有一个完整周期(高低电平 高电平决定亮度) 改变高低电平的长度比例,从而达到调节亮度的效果 PWM的产生:可由STM32中的定时器产生,包括1个高级定时器TIM1和3个普通定时器TIM2、TIM3、TIM4
为了连接定时器的相关输出:在核心版上通过一条导线连接P415和PB0 舵机连接:接P415端口 定时器的输出:PB0端口
按A键亮度最暗....按D键亮度最亮(使LED显示占空比使亮度发生变化)
新建文件夹
Basic文件夹——>pwm文件夹——>pwm.c pwm.h
pwm.h
#ifndef __PWM_H #define __PWM_H #include "sys.h" void TIM3_PWM_Init(u16 arr,u16 psc); #endif
pwm.c
#include "pwm.h" void TIM3_PWM_Init(u16 arr,u16 psc){ //TIM3 PWM初始化 arr重装载值 psc预分频系数 GPIO_InitTypeDef GPIO_InitStrue; TIM_OCInitTypeDef TIM_OCInitStrue; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3和相关GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟(LED在PB0引脚) RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟(定时器3通道3需要重映射到BP5引脚) GPIO_InitStrue.GPIO_Pin=GPIO_Pin_0; // TIM_CH3 GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽 GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz; //设置最大输出速度 GPIO_Init(GPIOB,&GPIO_InitStrue); //GPIO端口初始化设置 // GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //映射,重映射只用于64、100、144脚单片机 //当没有重映射时,TIM3的四个通道CH1,CH2,CH3,CH4分别对应PA6,PA7,PB0,PB1 //当部分重映射时,TIM3的四个通道CH1,CH2,CH3,CH4分别对应PB4,PB5,PB0,PB1 (GPIO_PartialRemap_TIM3) //当完全重映射时,TIM3的四个通道CH1,CH2,CH3,CH4分别对应PC6,PC7,PC8,PC9 (GPIO_FullRemap_TIM3) TIM_TimeBaseInitStrue.TIM_Period=arr; //设置自动重装载值 TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数 TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数器向上溢出 TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //时钟的分频因子,起到了一点点的延时作用,一般设为TIM_CKD_DIV1 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue); //TIM3初始化设置(设置PWM的周期) TIM_OCInitStrue.TIM_OCMode=TIM_OCMode_PWM1; // PWM模式1:CNT < CCR时输出有效电平 TIM_OCInitStrue.TIM_OCPolarity=TIM_OCPolarity_High;// 设置极性-有效电平为:高电平 TIM_OCInitStrue.TIM_OutputState=TIM_OutputState_Enable;// 输出使能 TIM_OC3Init(TIM3,&TIM_OCInitStrue); //TIM3的通道3 PWM 模式设置 TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器 TIM_Cmd(TIM3,ENABLE); //使能TIM3 }
main.c
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "relay.h" #include "oled0561.h" #include "SG90.h" #include "touch_key.h" #include "pwm.h" int main (void){//主程序 delay_ms(500); //上电时等待其他器件就绪 RCC_Configuration(); //系统时钟初始化 RELAY_Init();//继电器初始化 I2C_Configuration();//I2C初始化 OLED0561_Init(); //OLED初始化 OLED_DISPLAY_8x16_BUFFER(0," YoungTalk "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(3," SG90 TEST2 "); //显示字符串 TOUCH_KEY_Init();//按键初始化 TIM3_PWM_Init(59999,23); //设置频率为50Hz,公式为:溢出时间Tout(单位秒)=(arr+1)(psc+1)/Tclk 20MS = (59999+1)*(23+1)/72000000 //Tclk为通用定时器的时钟,如果APB1没有分频,则就为系统时钟,72MHZ //PWM时钟频率=72000000/(59999+1)*(23+1) = 50HZ (20ms),设置自动装载值60000,预分频系数24 while(1){ if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 0 "); //显示字符串 TIM_SetCompare3(TIM3,1500); //改变比较值TIM3->CCR2达到调节占空比的效果(1500为0度) } if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 45 "); //显示字符串 TIM_SetCompare3(TIM3,3000); //改变比较值TIM3->CCR2达到调节占空比的效果 } if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 90 "); //显示字符串 TIM_SetCompare3(TIM3,4500); //改变比较值TIM3->CCR2达到调节占空比的效果 } if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){ //读触摸按键的电平 OLED_DISPLAY_8x16_BUFFER(6," Angle 180 "); //显示字符串 TIM_SetCompare3(TIM3,7500); //改变比较值TIM3->CCR2达到调节占空比的效果 } } }
三:DHT11芯片(DHT11温湿度显示程序)
温湿度传感器
引脚说明
串行接口
通信过程
每一个波形的事件长度
数字0 1信号表示方法
新建文件夹
Hardware文件夹——>DHT11文件夹——>dht11.c dht11.h
通过程序对波形图的高低电平进行采样判断:有些部分需要输出,有些部分需要输入,通过while循环不断判断高低电平的开始位置,再通过延时函数判断数据位是"1"还是"0"
最终将5个字节数据读取进来进行数据校验,校验好后再放入到对应的变量当中,在主函数当中将读到的温度湿度数据以十进制数的方式进行显示,最终就达到了程序效果
dht11.h
#ifndef __DHT11_H #define __DHT11_H #include "sys.h" #include "delay.h" #define DHT11PORT GPIOA //定义IO接口 #define DHT11_IO GPIO_Pin_15 //定义IO接口 void DHT11_IO_OUT (void); void DHT11_IO_IN (void); void DHT11_RST (void); u8 Dht11_Check(void); u8 Dht11_ReadBit(void); u8 Dht11_ReadByte(void); u8 DHT11_Init (void); u8 DHT11_ReadData(u8 *h); #endif
dht11.c
#include "dht11.h" //切换端口的工作状态(因为有输出也有输入) void DHT11_IO_OUT (void){ //端口变为输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = DHT11_IO; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(DHT11PORT, &GPIO_InitStructure); } void DHT11_IO_IN (void){ //端口变为输入 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = DHT11_IO; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 GPIO_Init(DHT11PORT, &GPIO_InitStructure); } void DHT11_RST (void){ //DHT11端口复位,发出起始信号(IO发送) DHT11_IO_OUT(); GPIO_ResetBits(DHT11PORT,DHT11_IO); // delay_ms(20); //拉低至少18ms GPIO_SetBits(DHT11PORT,DHT11_IO); // delay_us(30); //主机拉高20~40us } u8 Dht11_Check(void){ //等待DHT11回应,返回1:未检测到DHT11,返回0:成功(IO接收) u8 retry=0; DHT11_IO_IN();//IO到输入状态 while (GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//DHT11会拉低40~80us retry++; delay_us(1); } if(retry>=100)return 1; else retry=0; while (!GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//DHT11拉低后会再次拉高40~80us retry++; delay_us(1); } if(retry>=100)return 1; return 0; } u8 Dht11_ReadBit(void){ //从DHT11读取一个位 返回值:1/0 u8 retry=0; while(GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//等待变为低电平 retry++; delay_us(1); } retry=0; while(!GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//等待变高电平 retry++; delay_us(1); } delay_us(40);//等待40us //用于判断高低电平,即数据1或0 if(GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO))return 1; else return 0; } u8 Dht11_ReadByte(void){ //从DHT11读取一个字节 返回值:读到的数据 u8 i,dat; dat=0; for (i=0;i<8;i++){ dat<<=1; dat|=Dht11_ReadBit(); } return dat; } u8 DHT11_Init (void){ //DHT11初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设时钟使能 DHT11_RST();//DHT11端口复位,发出起始信号 return Dht11_Check(); //等待DHT11回应 } u8 DHT11_ReadData(u8 *h){ //读取一次数据//湿度值(十进制,范围:20%~90%) ,温度值(十进制,范围:0~50°),返回值:0,正常;1,失败 u8 buf[5]; u8 i; DHT11_RST();//DHT11端口复位,发出起始信号 if(Dht11_Check()==0){ //等待DHT11回应 for(i=0;i<5;i++){//读取5位数据 buf[i]=Dht11_ReadByte(); //读出数据 } if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]){ //数据校验 *h=buf[0]; //将湿度值放入指针1 h++; *h=buf[2]; //将温度值放入指针2 } }else return 1; return 0; }
main.c
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "relay.h" #include "oled0561.h" #include "dht11.h" int main (void){//主程序 u8 b[2]; delay_ms(1000); //上电时等待其他器件就绪 RCC_Configuration(); //系统时钟初始化 RELAY_Init();//继电器初始化 I2C_Configuration();//I2C初始化 OLED0561_Init(); //OLED初始化 OLED_DISPLAY_8x16_BUFFER(0," YoungTalk "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(2," DHT11 TEST "); //显示字符串 if(DHT11_Init()==0){ //DHT11初始化 返回0成功,1失败 OLED_DISPLAY_8x16_BUFFER(4,"Humidity: % "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(6,"Temperature: C"); //显示字符串 }else{ OLED_DISPLAY_8x16_BUFFER(4,"DHT11INIT ERROR!"); //显示字符串 } delay_ms(1000);//DHT11初始化后必要的延时(不得小于1秒) while(1){ if(DHT11_ReadData(b)==0){//读出温湿度值 指针1是湿度 20~90%,指针2是温度 0~50C,数据为十进制 OLED_DISPLAY_8x16(4,9*8,b[0]/10 +0x30);//显示湿度值 OLED_DISPLAY_8x16(4,10*8,b[0]%10 +0x30);// OLED_DISPLAY_8x16(6,12*8,b[1]/10 +0x30);//显示温度值 OLED_DISPLAY_8x16(6,13*8,b[1]%10 +0x30);// }else{ OLED_DISPLAY_8x16_BUFFER(6,"DHT11READ ERROR!"); //显示字符串 } delay_ms(1000); //延时,刷新数据的频率(不得小于1秒) } }
四:MPU6050(六轴加速度和陀螺仪传感器芯片)
可以实现对自身位移和旋转角度的感知
加速度传感器:检测位移
陀螺仪传感器:检测方向和旋转的运动
MPU6050原始数据显示程序
新建文件夹
Hardware文件夹——>MPU6050文件夹——>MPU6050.c MPU6050.h
MPU6050.h
#ifndef __MPU6050_H #define __MPU6050_H #include "sys.h" #include "i2c.h" //i2c总线通信 #include "delay.h" #define MPU6050_ADD 0xD0 //器件地址(AD0悬空或低电平时地址是0xD0,为高电平时为0xD2,7位地址:1101 000x) #define MPU6050_RA_XG_OFFS_TC 0x00 #define MPU6050_RA_YG_OFFS_TC 0x01 #define MPU6050_RA_ZG_OFFS_TC 0x02 #define MPU6050_RA_X_FINE_GAIN 0x03 #define MPU6050_RA_Y_FINE_GAIN 0x04 #define MPU6050_RA_Z_FINE_GAIN 0x05 #define MPU6050_RA_XA_OFFS_H 0x06 #define MPU6050_RA_XA_OFFS_L_TC 0x07 #define MPU6050_RA_YA_OFFS_H 0x08 #define MPU6050_RA_YA_OFFS_L_TC 0x09 #define MPU6050_RA_ZA_OFFS_H 0x0A #define MPU6050_RA_ZA_OFFS_L_TC 0x0B #define MPU6050_RA_XG_OFFS_USRH 0x13 #define MPU6050_RA_XG_OFFS_USRL 0x14 #define MPU6050_RA_YG_OFFS_USRH 0x15 #define MPU6050_RA_YG_OFFS_USRL 0x16 #define MPU6050_RA_ZG_OFFS_USRH 0x17 #define MPU6050_RA_ZG_OFFS_USRL 0x18 #define MPU6050_RA_SMPLRT_DIV 0x19 #define MPU6050_RA_CONFIG 0x1A #define MPU6050_RA_GYRO_CONFIG 0x1B #define MPU6050_RA_ACCEL_CONFIG 0x1C #define MPU6050_RA_FF_THR 0x1D #define MPU6050_RA_FF_DUR 0x1E #define MPU6050_RA_MOT_THR 0x1F #define MPU6050_RA_MOT_DUR 0x20 #define MPU6050_RA_ZRMOT_THR 0x21 #define MPU6050_RA_ZRMOT_DUR 0x22 #define MPU6050_RA_FIFO_EN 0x23 #define MPU6050_RA_I2C_MST_CTRL 0x24 #define MPU6050_RA_I2C_SLV0_ADDR 0x25 #define MPU6050_RA_I2C_SLV0_REG 0x26 #define MPU6050_RA_I2C_SLV0_CTRL 0x27 #define MPU6050_RA_I2C_SLV1_ADDR 0x28 #define MPU6050_RA_I2C_SLV1_REG 0x29 #define MPU6050_RA_I2C_SLV1_CTRL 0x2A #define MPU6050_RA_I2C_SLV2_ADDR 0x2B #define MPU6050_RA_I2C_SLV2_REG 0x2C #define MPU6050_RA_I2C_SLV2_CTRL 0x2D #define MPU6050_RA_I2C_SLV3_ADDR 0x2E #define MPU6050_RA_I2C_SLV3_REG 0x2F #define MPU6050_RA_I2C_SLV3_CTRL 0x30 #define MPU6050_RA_I2C_SLV4_ADDR 0x31 #define MPU6050_RA_I2C_SLV4_REG 0x32 #define MPU6050_RA_I2C_SLV4_DO 0x33 #define MPU6050_RA_I2C_SLV4_CTRL 0x34 #define MPU6050_RA_I2C_SLV4_DI 0x35 #define MPU6050_RA_I2C_MST_STATUS 0x36 #define MPU6050_RA_INT_PIN_CFG 0x37 #define MPU6050_RA_INT_ENABLE 0x38 #define MPU6050_RA_DMP_INT_STATUS 0x39 #define MPU6050_RA_INT_STATUS 0x3A #define MPU6050_RA_ACCEL_XOUT_H 0x3B #define MPU6050_RA_ACCEL_XOUT_L 0x3C #define MPU6050_RA_ACCEL_YOUT_H 0x3D #define MPU6050_RA_ACCEL_YOUT_L 0x3E #define MPU6050_RA_ACCEL_ZOUT_H 0x3F #define MPU6050_RA_ACCEL_ZOUT_L 0x40 #define MPU6050_RA_TEMP_OUT_H 0x41 #define MPU6050_RA_TEMP_OUT_L 0x42 #define MPU6050_RA_GYRO_XOUT_H 0x43 #define MPU6050_RA_GYRO_XOUT_L 0x44 #define MPU6050_RA_GYRO_YOUT_H 0x45 #define MPU6050_RA_GYRO_YOUT_L 0x46 #define MPU6050_RA_GYRO_ZOUT_H 0x47 #define MPU6050_RA_GYRO_ZOUT_L 0x48 #define MPU6050_RA_EXT_SENS_DATA_00 0x49 #define MPU6050_RA_EXT_SENS_DATA_01 0x4A #define MPU6050_RA_EXT_SENS_DATA_02 0x4B #define MPU6050_RA_EXT_SENS_DATA_03 0x4C #define MPU6050_RA_EXT_SENS_DATA_04 0x4D #define MPU6050_RA_EXT_SENS_DATA_05 0x4E #define MPU6050_RA_EXT_SENS_DATA_06 0x4F #define MPU6050_RA_EXT_SENS_DATA_07 0x50 #define MPU6050_RA_EXT_SENS_DATA_08 0x51 #define MPU6050_RA_EXT_SENS_DATA_09 0x52 #define MPU6050_RA_EXT_SENS_DATA_10 0x53 #define MPU6050_RA_EXT_SENS_DATA_11 0x54 #define MPU6050_RA_EXT_SENS_DATA_12 0x55 #define MPU6050_RA_EXT_SENS_DATA_13 0x56 #define MPU6050_RA_EXT_SENS_DATA_14 0x57 #define MPU6050_RA_EXT_SENS_DATA_15 0x58 #define MPU6050_RA_EXT_SENS_DATA_16 0x59 #define MPU6050_RA_EXT_SENS_DATA_17 0x5A #define MPU6050_RA_EXT_SENS_DATA_18 0x5B #define MPU6050_RA_EXT_SENS_DATA_19 0x5C #define MPU6050_RA_EXT_SENS_DATA_20 0x5D #define MPU6050_RA_EXT_SENS_DATA_21 0x5E #define MPU6050_RA_EXT_SENS_DATA_22 0x5F #define MPU6050_RA_EXT_SENS_DATA_23 0x60 #define MPU6050_RA_MOT_DETECT_STATUS 0x61 #define MPU6050_RA_I2C_SLV0_DO 0x63 #define MPU6050_RA_I2C_SLV1_DO 0x64 #define MPU6050_RA_I2C_SLV2_DO 0x65 #define MPU6050_RA_I2C_SLV3_DO 0x66 #define MPU6050_RA_I2C_MST_DELAY_CTRL 0x67 #define MPU6050_RA_SIGNAL_PATH_RESET 0x68 #define MPU6050_RA_MOT_DETECT_CTRL 0x69 #define MPU6050_RA_USER_CTRL 0x6A #define MPU6050_RA_PWR_MGMT_1 0x6B //电源管理专用寄存器 #define MPU6050_RA_PWR_MGMT_2 0x6C #define MPU6050_RA_BANK_SEL 0x6D #define MPU6050_RA_MEM_START_ADDR 0x6E #define MPU6050_RA_MEM_R_W 0x6F #define MPU6050_RA_DMP_CFG_1 0x70 #define MPU6050_RA_DMP_CFG_2 0x71 #define MPU6050_RA_FIFO_COUNTH 0x72 #define MPU6050_RA_FIFO_COUNTL 0x73 #define MPU6050_RA_FIFO_R_W 0x74 #define MPU6050_RA_WHO_AM_I 0x75 / void MPU6050_Init(void); void MPU6050_READ(u16* n); #endif
MPU6050.c
#include "MPU6050.h" void MPU6050_Init(void){ //初始化MPU6050 I2C_SAND_BYTE(MPU6050_ADD,MPU6050_RA_PWR_MGMT_1,0x80);//解除休眠状态 最高位的数据为1,从而进入复位状态 delay_ms(1000); //等待器件就绪 I2C_SAND_BYTE(MPU6050_ADD,MPU6050_RA_PWR_MGMT_1,0x00);//解除休眠状态 I2C_SAND_BYTE(MPU6050_ADD,MPU6050_RA_SMPLRT_DIV,0x07);//陀螺仪采样率 I2C_SAND_BYTE(MPU6050_ADD,MPU6050_RA_CONFIG,0x06); I2C_SAND_BYTE(MPU6050_ADD,MPU6050_RA_ACCEL_CONFIG,0x00);//配置加速度传感器工作在16G模式 I2C_SAND_BYTE(MPU6050_ADD,MPU6050_RA_GYRO_CONFIG,0x18);//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) } void MPU6050_READ(u16* n){ //读出X、Y、Z三轴加速度/陀螺仪原始数据 //n[0]是AX,n[1]是AY,n[2]是AZ,n[3]是GX,n[4]是GY,n[5]是GZ u8 i; u8 t[14]; I2C_READ_BUFFER(MPU6050_ADD, MPU6050_RA_ACCEL_XOUT_H, t, 14); //读出连续的数据地址,包括了加速度和陀螺仪共12字节 for(i=0; i<3; i++) //整合加速度 n[i]=((t[2*i] << 8) + t[2*i+1]); for(i=4; i<7; i++) //整合陀螺仪 n[i-1]=((t[2*i] << 8) + t[2*i+1]); }
main.c
#include "stm32f10x.h" //STM32头文件 #include "sys.h" #include "delay.h" #include "relay.h" #include "oled0561.h" #include "MPU6050.h" int main (void){//主程序 u16 t[6]={0}; delay_ms(500); //上电时等待其他器件就绪 RCC_Configuration(); //系统时钟初始化 RELAY_Init();//继电器初始化 I2C_Configuration();//I2C初始化 OLED0561_Init(); //OLED初始化 OLED_DISPLAY_8x16_BUFFER(0," MPU6050 TEST "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(2,"X: X: "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(4,"Y: Y: "); //显示字符串 OLED_DISPLAY_8x16_BUFFER(6,"Z: Z: "); //显示字符串 MPU6050_Init(); //MPU6050初始化 while(1){ MPU6050_READ(t); //加速度 //其中t[0~2]是加速度ACCEL,t[3~5]是陀螺仪GYRO OLED_DISPLAY_8x16(2,2*8,t[0]/10000 +0x30);//显示 OLED_DISPLAY_8x16(2,3*8,t[0]%10000/1000 +0x30);//显示 OLED_DISPLAY_8x16(2,4*8,t[0]%1000/100 +0x30);// OLED_DISPLAY_8x16(2,5*8,t[0]%100/10 +0x30);// OLED_DISPLAY_8x16(2,6*8,t[0]%10 +0x30);// OLED_DISPLAY_8x16(2,11*8,t[3]/10000 +0x30);//显示 OLED_DISPLAY_8x16(2,12*8,t[3]%10000/1000 +0x30);//显示 OLED_DISPLAY_8x16(2,13*8,t[3]%1000/100 +0x30);// OLED_DISPLAY_8x16(2,14*8,t[3]%100/10 +0x30);// OLED_DISPLAY_8x16(2,15*8,t[3]%10 +0x30);// OLED_DISPLAY_8x16(4,2*8,t[1]/10000 +0x30);//显示 OLED_DISPLAY_8x16(4,3*8,t[1]%10000/1000 +0x30);//显示 OLED_DISPLAY_8x16(4,4*8,t[1]%1000/100 +0x30);// OLED_DISPLAY_8x16(4,5*8,t[1]%100/10 +0x30);// OLED_DISPLAY_8x16(4,6*8,t[1]%10 +0x30);// OLED_DISPLAY_8x16(4,11*8,t[4]/10000 +0x30);//显示 OLED_DISPLAY_8x16(4,12*8,t[4]%10000/1000 +0x30);//显示 OLED_DISPLAY_8x16(4,13*8,t[4]%1000/100 +0x30);// OLED_DISPLAY_8x16(4,14*8,t[4]%100/10 +0x30);// OLED_DISPLAY_8x16(4,15*8,t[4]%10 +0x30);// OLED_DISPLAY_8x16(6,2*8,t[2]/10000 +0x30);//显示 OLED_DISPLAY_8x16(6,3*8,t[2]%10000/1000 +0x30);//显示 OLED_DISPLAY_8x16(6,4*8,t[2]%1000/100 +0x30);// OLED_DISPLAY_8x16(6,5*8,t[2]%100/10 +0x30);// OLED_DISPLAY_8x16(6,6*8,t[2]%10 +0x30);// OLED_DISPLAY_8x16(6,11*8,t[5]/10000 +0x30);//显示 OLED_DISPLAY_8x16(6,12*8,t[5]%10000/1000 +0x30);//显示 OLED_DISPLAY_8x16(6,13*8,t[5]%1000/100 +0x30);// OLED_DISPLAY_8x16(6,14*8,t[5]%100/10 +0x30);// OLED_DISPLAY_8x16(6,15*8,t[5]%10 +0x30);// delay_ms(200); //延时(决定刷新速度) } }