STMf407vet6开发板的使用

了解的知识

需要什么就添加什么,都在对应的文件里,比如gpio就再gpio.c里面,在.c文件里右键跳到头文件查找自己需要的函数。xx.s是汇编的启动文件,里面有中断的向量表。misc.c里面是中断函数。在system_stm32f4xx.c 和stm32f4xx.h里面修改对应的时钟频率。
在这里插入图片描述

LED灯使用

1.首先查看开发板对应的原理图引脚,和芯片连接的网络标号,看它在哪一根时钟总线上。


![在这里插入图片描述](https://img-blog.csdnimg.cn/76b962df199b47649315cb55245e8e52.png
在这里插入图片描述

/****************************************
函数名:void LED_Init(void)
功能:初始化LED灯
参数:无
返回值:无
说明:
	1.通过原理图查询到 
		LED1 ---- PE8
		LED2 ---- PE9
		LED3 ---- PE10
*****************************************/
void LED_Init(void)
{
    
    
	//1.RCC 使能 AHB1 总线上的 GPIOE 总线时钟 
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	
	//2.初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;	//引脚:8|9|10
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;		//模式:通用输出
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;		//类型:推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;	//速度:快速 50MHz 
	GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_NOPULL;	//状态:无上下拉
	
	
	GPIO_Init(GPIOE, &GPIO_InitStruct);				//初始化GPIO
	//3.控制寄存器输出数据 ---- 让LED不亮
	//GPIO_SetBits(GPIOE,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10);	//将LED引脚置位高电平 -- 不亮
	LED1 = LED2 = LED3 = 1;	//将LED引脚置位高电平 -- 不亮
}

2.蜂鸣器,方法同上

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

/****************************************
函数名:void BEEP_Init(void)
功能:BEEP蜂鸣器初始化
参数:无
返回值:无
说明:
	1.通过原理图查询到 
		BEEP --- PB10
*****************************************/
void BEEP_Init(void)	
{
    
    
	//1.RCC 使能 AHB1 总线上的 GPIOB 总线时钟 
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	//2.初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;			//引脚:10
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;		//模式:通用输出
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;		//类型:推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;	//速度:快速 50MHz 
	GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_NOPULL;	//状态:无上下拉
	
	
	GPIO_Init(GPIOB, &GPIO_InitStruct);				//初始化GPIO
	//3.控制寄存器输出数据 ---- 让BEEP响
	//GPIO_SetBits(GPIOB,GPIO_Pin_10);	//将BEEP引脚置位高电平 -- 响
	//GPIO_ResetBits(GPIOB,GPIO_Pin_10);	//将BEEP引脚置位高电平 -- 不响
	
	BEEP = 0;
}

3.按键

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


/****************************************
函数名:void KEY_Init(void)
功能:KEY按键初始化
参数:无
返回值:无
说明:
	1.通过原理图查询到 
		KEY1 --- PE4
		KEY2 --- PE5
		KEY3 --- PE6
		KEY4 --- PC13
*****************************************/
void KEY_Init(void)
{
    
    
	GPIO_InitTypeDef GPIO_InitStruct;
	//1.RCC使能时钟 AHB1总线上的 GPIOE 和 GPIOC 总线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOC, ENABLE);
	
	//2.1 初始化GPIOE
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;	//引脚:4|5|6
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;						//模式:输入模式
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;						//状态:拉高
	
	GPIO_Init(GPIOE, &GPIO_InitStruct);
	
	//2.2 初始化GPIOC
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	
	GPIO_Init(GPIOC, &GPIO_InitStruct);
}

/********************************************************************************
函数名:u8 KEY_Scan(void)
功能:读取KEY按键值
参数:无
返回值:
	KEY1 按下返回 1
	KEY1 按下返回 2
	KEY1 按下返回 3
	KEY1 按下返回 4	
	没有按键按下则返回0
说明:当按键按下时,引脚会被拉低,变为0低电平.
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
1.出现按键抖动问题,使用延时消抖。
2.出现连续出现多次触发,使用不连续按下,定义一个静态变量保存上次按下记录
*********************************************************************************/
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_5)
#define KEY3 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_6)
#define KEY4 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)

#include "delay.h"
u8 KEY_Scan(void)
{
    
    
	static u8 up = 1;	//记录上次按键状态, 1 上次没有任何按键按下,0表示有按键按下
	//检查按键第一次
	if(up == 1 && (KEY1 == 0 || KEY2 == 0 || KEY3 == 0 || KEY4 == 0))
	{
    
    
		up = 0;	//记录按键按下了
		delay_ms(5);	//延时5毫秒进行消抖
		//再次检查按键是否按下
		if(KEY1 == 0) return 1;
		if(KEY2 == 0) return 2;
		if(KEY3 == 0) return 3;
		if(KEY4 == 0) return 4;
	}
	if(KEY1 == 1 && KEY2 == 1 && KEY3 == 1 && KEY4 == 1)
	{
    
    
		up = 1;	//记录没有任何按键按下,可以触发下次检测
	}

	return 0;
}

4.中断

在这里插入图片描述

#include "nvic_key_exit.h"
#include "stm32f4xx.h"                  // Device header
#include "key.h"
/****************************************
函数名:void KEY_EXIT_Init(void)
功能:按键外部中断初始化
参数:无
返回值:无
说明:
	1.通过原理图查询到 
		KEY1 --- PE4		---- E4
		KEY2 --- PE5		---- E5
		KEY3 --- PE6		---- E6
		KEY4 --- PC13		---- C13
*****************************************/
void KEY_EXIT_Init(void)
{
    
    
	EXTI_InitTypeDef EXTI_InitStruct;	//外部中断结构体
	NVIC_InitTypeDef NVIC_InitStruct;	//中断结构体
	//1.外部中断属于外设,也需要时钟使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
	
	//2.初始化GPIO
	KEY_Init();
	
	//3.配置中断引脚 SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource5);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource6);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC,EXTI_PinSource13);
	
	//4.外部中断配置 GPIOE 4|5|6
	EXTI_InitStruct.EXTI_Line = EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line13;	//指定中断线
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;					//使能
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;		//模式:中断
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;	//触发方式:下降沿
	
	EXTI_Init(&EXTI_InitStruct);
	
	//5.配置中断 EXTI4_IRQn
	NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;	//使能中断通道:外部中断4
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;	//抢占优先级2
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;			//响应优先级2
	
	NVIC_Init(&NVIC_InitStruct);
	
	
	//5.配置中断 EXTI9_5_IRQn
	NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;	//使能中断通道:外部中断5~9
	NVIC_Init(&NVIC_InitStruct);
	
	//5.配置中断 EXTI15_10_IRQn
	NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;	//使能中断通道:外部中断10~15
	NVIC_Init(&NVIC_InitStruct);
}

/*************** 中断服务函数 --------- 需要依赖中断向量表 ***************
1.中断短小精干
2.无参无返回值
3.不能有太多耗时操作,例如延时过高
4.尽量不允许有浮点运算
************************************************************************/
#include "beep.h"
void EXTI4_IRQHandler()
{
    
    
	BEEP = !BEEP;//让BEEP蜂鸣器反转
	
	/*------ 清空中断标志位,给下一次做准备 -------*/
	EXTI_ClearITPendingBit(EXTI_Line4);
}

#include "led.h"
void EXTI9_5_IRQHandler()
{
    
    
	//到底是那个中断线
	if(EXTI_GetITStatus(EXTI_Line5) == SET)	//如果返回SET就是中断线五
	{
    
    
		LED1 = LED2 = LED3 = 0;	//亮
		EXTI_ClearITPendingBit(EXTI_Line5);
	}
	
	if(EXTI_GetITStatus(EXTI_Line6) == SET)	//如果返回SET就是中断线六
	{
    
    
		LED1 = LED2 = LED3 = 1;	//亮
		EXTI_ClearITPendingBit(EXTI_Line6);
	}
}

5.串口

看对应的时钟总线。
在这里插入图片描述

/****************************************************
函数名:void USART1_Init(u32 bound)
功能:串口1初始化
参数:
	@bound : 波特率 
返回值:无
说明:
	通过原理图查看串口引脚  
		USART1_TX --- PA9		
		USART1_RX --- PA10
*****************************************************/
void USART1_Init(u32 bound)
{
    
    
	//1.时钟使能  GPIOA -- AHB1 和 USART1 --- APB2
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	//2.GPIO初始化
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;			//模式:复用
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;	//引脚:9|10
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			//类型:推挽
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;		//速度:50MHz
	GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;			//状态:上拉
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//3.设置复用功能选择
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	
	//4.配置UASRT1 串口
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = bound;		//波特率:一般115200
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无流控制
	USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//模式:收|发
	USART_InitStruct.USART_Parity = USART_Parity_No;				//校验位:无校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;				//停止位:1个
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;		//数据位:8位
	
	USART_Init(USART1, &USART_InitStruct);
	
	//5.使能定时器
	USART_Cmd(USART1, ENABLE);
	
	//6.开启中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	//7.配置中断参数
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;	//通道:USART1 串口
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;	//使能
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;	//抢占优先级:3
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;			//响应优先级:3
	
	NVIC_Init(&NVIC_InitStruct);
}

//8.编写中断服务函数 ---- 启动文件(中断向量表)
/**************************************************************
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
***************************************************************/
#include "beep.h"
#include "string.h"

u8 buff[100];	//缓冲区
u8 buff_index = 0;		//点前使用到多少个
u8 buff_flag = 0;		//查看数据是否结尾

/******* 这个部分是中断直接操作部分 ------ 中断直接操作部分有问题(效率,延时,浮点) *****/
#if 0
void USART1_IRQHandler()
{
    
    
	u8 ret;
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)	//如果接收被置1表示有数据可读
	{
    
    
		//读取数据
		ret = USART_ReceiveData(USART1);	//从RDR读取数据
		if(buff_index < 100)
		{
    
    
			buff[buff_index] = ret;
			
			if(buff[buff_index-1] == 0x0d && buff[buff_index] == 0x0a)
			{
    
    
				buff[buff_index-1] = '\0';
				buff_flag = 1;	//数据已经结尾
			}
			buff_index++;
		}
		else
		{
    
    
			buff_index = 0;
		}
	}
}

/********************************************
函数名:int USART1_Read(char *buf,int len,int wait)
功能:提供读取缓存区函数
参数:
	@buf : 保存数据的地址
	@len : 要读取的字节数
	@wait :是否等待数据完成后再读
返回值: 成功读取的字节数
********************************************/
int USART1_Read(char *buf,int len,int wait)
{
    
    
	if(buff_flag == 0 && wait == 1) return 0;
	/***** 已经数据结尾 buff_flag == 1  *****/
	len = len > buff_index ? buff_index :  len;
	memcpy(buf,buff,len);
	
	buff_index = 0;	//读取了缓存区数据,则缓存区清空
	return len;
}
#else 
void USART1_IRQHandler()
{
    
    
	u8 ret;
	//8.1 获取中断状态:有数据可读中断
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)	//如果接收被置1表示有数据可读
	{
    
    
		//读取数据
		ret = USART_ReceiveData(USART1);	//从RDR读取数据
		USART_SendData(USART2,ret);			
	}
	USART_ClearITPendingBit(USART1,USART_IT_RXNE);	//清除中断标志,便于下次中断检测。
}
#endif

/*********************************************
头文件:#include <stdio.h>  
函数名:int fputc(int ch,FILE *fp)
功能:重定向实现printf调用的打印输出功能
参数:
	@ch : 传入的单字符
	@fp : 流对象
返回值:
	返回:输出的内容
说明:在使用的时候一定要将微库加载打开
*********************************************/
#include "stdio.h"
int fputc(int ch,FILE *fp)		//重定向 printf 默认调用 fputc 发送单字符
{
    
    
	while((USART1->SR & 0x40) == 0)
	{
    
    
		;	//进入循环等待
	}
	USART1->DR = ch;
	return ch;
}

/*********************************************
函数名:u8 USART1_Send_Data(u8 *data,u32 len)
功能:发送指定长度数据
参数:
	@data : 数据首地址
	@len  : 数据长度
返回值:
	返回:成功写入的数据长度
*********************************************/
u8 USART1_Send_Data(u8 *data,u32 len)	//相当与系统编程的 write
{
    
    
	while(len--)
	{
    
    
		while((USART1->SR & 0x40) == 0);//进入循环等待
		USART1->DR = *data;
		data++;
	}
	return len;
}


/****************************************************
函数名:void USART2_Init(u32 bound)
功能:串口1初始化
参数:
	@bound : 波特率 
返回值:无
说明:
	通过原理图查看串口引脚  
		USART2_TX --- PD5		
		USART2_RX --- PD6
*****************************************************/
void USART2_Init(u32 bound)
{
    
    
	//1.时钟使能  GPIOD -- AHB1 和 USART2 --- APB1
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	//2.GPIO初始化
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;			//模式:复用
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;	//引脚:9|10
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			//类型:推挽
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;		//速度:50MHz
	GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;			//状态:上拉
	
	GPIO_Init(GPIOD, &GPIO_InitStruct);
	
	//3.设置复用功能选择
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_USART2);
	GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_USART2);
	
	//4.配置UASRT1 串口
	USART_InitTypeDef USART_InitStruct;
	
	USART_InitStruct.USART_BaudRate = bound;		//波特率:一般115200
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无流控制
	USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//模式:收|发
	USART_InitStruct.USART_Parity = USART_Parity_No;				//校验位:无校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;				//停止位:1个
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;		//数据位:8位
	
	USART_Init(USART2, &USART_InitStruct);
	
	//5.使能定时器
	USART_Cmd(USART2, ENABLE);
	
	//6.开启中断
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	
	//7.配置中断参数
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;	//通道:USART1 串口
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;	//使能
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;	//抢占优先级:3
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;			//响应优先级:3
	
	NVIC_Init(&NVIC_InitStruct);
}



u8 USART2_BUFF[200];	//缓冲区
u8 USART2_Index = 0;
#include "wifi.h"
void USART2_IRQHandler()
{
    
    
	static u8 flag = 0;
	u8 ret;
	//8.1 获取中断状态:有数据可读中断
	if(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == SET)	//如果接收被置1表示有数据可读
	{
    
    
		ret = USART_ReceiveData(USART2);	//从RDR读取数据
		USART_SendData(USART1,ret);
		if(ret == 0xaa)	//接收到开始消息 0xaa
		{
    
    
			flag = 1;
			USART2_Index = 0;	//数据下标又从 0 开始
		}
		
		if(flag == 1)	//正常接收
		{
    
    
			USART2_BUFF[USART2_Index] = ret;	//写入到缓存区
			if(ret == 0xbb)						//接收到结尾消息 0xbb
			{
    
    
				WIFI_t wifi;
				memcpy(&wifi,USART2_BUFF,sizeof(WIFI_t));
				switch(wifi.cmd)
				{
    
    
					case 0x10:	BEEP = !BEEP;	break;
				}
				
				/***** 数据处理完之后,清空缓存区 ****/
				memset(USART2_BUFF,0,200);
				flag = 0;
			}
			
			USART2_Index++;	//下标增加
		}
	}
	USART_ClearITPendingBit(USART2,USART_IT_RXNE);	//清除中断标志,便于下次中断检测。
}

/*********************************************
函数名:u8 USART2_Send_Data(u8 *data,u32 len)
功能:发送指定长度数据
参数:
	@data : 数据首地址
	@len  : 数据长度
返回值:
	返回:成功写入的数据长度
*********************************************/
u8 USART2_Send_Data(u8 *data,u32 len)	//相当与系统编程的 write
{
    
    
	while(len--)
	{
    
    
		while((USART2->SR & 0x40) == 0);//进入循环等待
		USART2->DR = *data;
		data++;
	}
	return len;
}

6.独立看门狗

注意:独立看门狗的时钟是不精确的,是一个范围值。一定要记得喂狗,不然系统就会一直重启重启重启。
看对应的时钟总线。

//独立看门狗初始化
void IWDG_Init(uint8_t Prer,uint16_t rlr)
{
    
    
	//1.解除写保护
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	
	//2.设置预分频系数
	IWDG_SetPrescaler(Prer);	
	
	//3.自动重装载值
	IWDG_SetReload(rlr);
	
	//4.重装载计数器
	IWDG_ReloadCounter();
	
	//5.启动看门狗
	IWDG_Enable();
	//这个一般写在主函数的循环里
	IWDG_Init( IWDG_Prescaler_128, 3750);	//记得喂狗,15s左右
}

7.通用定时器、基本定时器、高级定时器。

看对应的时钟总线。

/********************************************************************************************************************
*函数名:void TIM3_Init(u16 arr,u16 psc)
*功能:TIM3初始化
*参数:
	@arr:自动重装载值
	@psc:分频因子
*其他说明:
	超时时间计算规则:Tout= ((arr+1)*(psc+1))/Tclk
		Tclk:TIM3 的输入时钟频率(单位为 Mhz)。
		Tout:TIM3 溢出时间(单位为 us)。
	案例:
		1.设置超时时间为500毫秒,这里我们一般喜欢设置分频因子为,我们知道TIM3在APB1,默认时钟为42MHz,
		  但是会进行2倍频,则是84MHz,由于计数器无法计数这么高,则分频 8400,则分频因子 psc+1 = 8400
			Tout = ((arr+1)*(psc+1))/Tclk  , 可以通过反推 arr+1 = Tout / (psc+1) *Tclk
			结果: arr+1 = 500 / 8400 * 84000 , 则 arr = 4999
********************************************************************************************************************/
void TIM3_Init(u16 arr,u16 psc)
{
    
    
	//1.使能定时器时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	//2.初始化时钟
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;	//分频:1分频	84M
	TIM_TimeBaseInitStruct.TIM_Prescaler = psc;					//分频因子		//例如:8400
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//模式:向上
	TIM_TimeBaseInitStruct.TIM_Period = arr;					//重装载
	
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
	
	//3.使能中断:更新中断
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);	
	
	//4.配置中断
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	
	 NVIC_Init(&NVIC_InitStruct);
	//5.使能 TIM
	TIM_Cmd(TIM3, ENABLE);
}

#include "beep.h"
void TIM3_IRQHandler()
{
    
    
	if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)
	{
    
    
		BEEP = !BEEP;	//反转
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);	//清除更新中断标志
	}
}


/********************************************************************************************************************
*函数名:void TIM2_CH3_Init(u16 arr,u16 psc)
*功能:TIM2_CH3通道做PWM
*参数:
	@arr:自动重装载值
	@psc:分频因子
*其他说明:
	超时时间计算规则:Tout= ((arr+1)*(psc+1))/Tclk
		Tclk:TIM2 的输入时钟频率(单位为 Mhz)。
		Tout:TIM2 溢出时间(单位为 us)。
	案例:
		1.设置超时时间为500毫秒,这里我们一般喜欢设置分频因子为,我们知道TIM2在APB1,默认时钟为42MHz,
		  但是会进行2倍频,则是84MHz,由于计数器无法计数这么高,则分频 8400,则分频因子 psc+1 = 8400
			Tout = ((arr+1)*(psc+1))/Tclk  , 可以通过反推 arr+1 = Tout / (psc+1) *Tclk
			结果: arr+1 = 500 / 8400 * 84000 , 则 arr = 4999
********************************************************************************************************************/
void TIM2_CH3_Init(u16 arr,u16 psc)
{
    
    
	//1.使能时钟总线 TIM2 和 GPIOB
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	//2.初始化GPIO变为复用模式
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;			//引脚:10
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;		//模式复用
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;		//类型:推挽
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;		//状态:上拉
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;	//速度:100MHz
	
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//3.将其引脚复用为TIM定时器功能
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2);
	
	//4.初始化定时器基本功能
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;			//分频:不分频:1
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;		//模式:向上计数
	TIM_TimeBaseInitStruct.TIM_Period = arr;							//重装载计数器
	TIM_TimeBaseInitStruct.TIM_Prescaler = psc;							//分频因子:psc
	
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
	
	//5.设定通道
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;				//PWM模式:PWM1
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;	//输出状态使能
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;		//极性:高电平
	
	TIM_OC3Init(TIM2, &TIM_OCInitStruct);
	
	//6.使能重装载寄存器,并将影子寄存器值移动
	TIM_ARRPreloadConfig(TIM2,ENABLE);
	
	//7.使能定时器
	TIM_Cmd(TIM2, ENABLE);
	
	//8.如果是高级定时器需要设定为主要输出 TIM_CtrlPWMOutputs() 让高级定时器主动输出
}

/********************************************************************************************************************
*函数名:void TIM1_CH1N_Init(u16 arr,u16 psc)
*功能:TIM1_CH1N通道做PWM
*参数:
	@arr:自动重装载值
	@psc:分频因子
*其他说明:
	超时时间计算规则:Tout= ((arr+1)*(psc+1))/Tclk
		Tclk:TIM1 的输入时钟频率(单位为 Mhz)。
		Tout:TIM1 溢出时间(单位为 us)。
	案例:
		1.设置超时时间为500毫秒,这里我们一般喜欢设置分频因子为,我们知道TIM2在APB1,默认时钟为42MHz,
		  但是会进行2倍频,则是84MHz,由于计数器无法计数这么高,则分频 8400,则分频因子 psc+1 = 8400
			Tout = ((arr+1)*(psc+1))/Tclk  , 可以通过反推 arr+1 = Tout / (psc+1) *Tclk
			结果: arr+1 = 500 / 8400 * 84000 , 则 arr = 4999
通过原理图发现
	LED1 ---- PE8 ----> TIM1_CH1N 互补
********************************************************************************************************************/
void TIM1_CH1N_Init(u16 arr,u16 psc)	
{
    
    
	//1.定时器总线使能 和 GPIOE 总线使能
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
	
	//2.初始化GPIOE 8变为复用模式
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;			//引脚:8
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;		//模式复用
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;		//类型:推挽
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;		//状态:上拉
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;	//速度:100MHz
	
	GPIO_Init(GPIOE, &GPIO_InitStruct);
	
	//3.将其引脚复用为TIM定时器功能
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_TIM1);
	GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_TIM1);
	//4.初始化定时器基本功能
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;			//分频:不分频:1
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;		//模式:向上计数
	TIM_TimeBaseInitStruct.TIM_Period = arr;							//重装载计数器
	TIM_TimeBaseInitStruct.TIM_Prescaler = psc;							//分频因子:psc
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
	
	//5.设定通道
	TIM_OCInitTypeDef TIM_OCInitStruct;
	
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;				//PWM模式:PWM1
	TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Enable;	//输出状态使能
	TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_Low;		//互补极性:低电平
	TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Set;	
	
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;	//输出状态使能
	TIM_OCInitStruct.TIM_OCIdleState = TIM_OCPolarity_High;		//互补极性:高电平
	TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCIdleState_Set;

	
	TIM_OC1Init(TIM1, &TIM_OCInitStruct);
	TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);	//使能外设
	//6.使能重装载寄存器,并将影子寄存器值移动
	TIM_ARRPreloadConfig(TIM1,ENABLE);
	
	//7.使能定时器
	TIM_Cmd(TIM1, ENABLE);
	
	//8.如果是高级定时器需要设定为主要输出 TIM_CtrlPWMOutputs() 让高级定时器主动输出
	TIM_CtrlPWMOutputs(TIM1,ENABLE);
}

typedef struct  //高级定时器一定要注意互补输出的参数是否带N,否则会产生其他问题
{
    
    
  uint16_t TIM_OCMode;        /*!< 指定 TIM 模式。
                                   This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */

  uint16_t TIM_OutputState;   /*!< 指定 TIM 输出比较状态。
                                   This parameter can be a value of @ref TIM_Output_Compare_State */

  uint16_t TIM_OutputNState;  /*!< 指定 TIM 互补输出比较状态。
                                   This parameter can be a value of @ref TIM_Output_Compare_N_State
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint32_t TIM_Pulse;         /*!< 指定要加载到捕获/比较寄存器的脉冲值。
                                   This parameter can be a number between 0x0000 and 0xFFFF */

  uint16_t TIM_OCPolarity;    /*!< 指定输出极性。
                                   This parameter can be a value of @ref TIM_Output_Compare_Polarity */

  uint16_t TIM_OCNPolarity;   /*!< 指定互补输出极性。
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint16_t TIM_OCIdleState;   /*!< 指定空闲状态下的 TIM 输出比较引脚状态。
                                   This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint16_t TIM_OCNIdleState;  /*!< 指定空闲状态下的 TIM 输出比较引脚状态。
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                                   @note This parameter is valid only for TIM1 and TIM8. */
} TIM_OCInitTypeDef1;

8.DHT11温湿度传感器

在这里插入图片描述
在这里插入图片描述
一定要按照时序图来看,不然读取到的数据就是错误的。

/***************************************
函数名:void DHT11_Init(void)
功能:DHT11初始化
参数:无
***************************************/
void DHT11_Init(void)
{
    
    
	//1.使能 GPIOA 时钟总线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	//2.初始化GPIOA 3
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;	
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/***************************************
由于是单总线协议,我们既需要发送 | 接收
所以,我们制作两个宏,用于设置 输出 | 输入模式
****************************************/
#define DHT11_Init_IN()  GPIOA->MODER &= ~(0x3 << 6)		//设置为输入模式
#define DHT11_Init_OUT() GPIOA->MODER |= 0x1 << 6			//设置为输出模式
#define DHT11_OUT PAout(3)	//用于电平输出
#define DHT11_IN  PAin(3)	//用于电平读取

/***************************************
函数名:void DHT11_Reset(void)
功能:DHT11 复位信号
参数:无
***************************************/
void DHT11_Reset(void)
{
    
    
	DHT11_Init_OUT();	//设置引脚为输出模式
	DHT11_OUT = 0;		//将电平拉低
	delay_ms(20);		//拉低至少18ms秒,我们选择 20毫秒
	
	DHT11_OUT = 1;		//将电平拉高
	delay_us(30);		//拉高范围 20us 到 40us 秒,我们选择折中方式 30us
}

/***************************************
函数名:
功能:DHT11 响应信号检测
参数:无
返回值:
	响应:0   ---- 存在
	不响应:-1  ---- 不存在
***************************************/
int DHT11_Check(void)
{
    
    
	int timer = 0;	//用于计时
	
	DHT11_Init_IN();	//将引脚设置为输入模式
	
	timer = 0;
	while(DHT11_IN == 1)			//检测DHT11设备电平由高拉低
	{
    
    
		delay_us(1);				//用于计时延时
		timer++;
		if(timer > 100) return -1;	//用于异常检测
	}
	
	timer = 0;
	while(DHT11_IN == 0)			//检测DHT11设备电平由低拉高
	{
    
    
		delay_us(1);				//用于计时延时
		timer++;
		if(timer > 100) return -1;	//用于异常检测
	}
	
	return 0;	//存在
}


/***************************************
函数名:u8 DHT11_Read_Bit(void)
功能:DHT11 读取1bit数据
参数:无
返回值: 比特值
	1 : 高电平
	0 : 低电平
***************************************/
u8 DHT11_Read_Bit(void)
{
    
    
	int timer = 0;	//用于计时
	
	while(DHT11_IN == 1)
	{
    
    
		delay_us(1);
		timer++;
		if(timer > 100) return 0;
	}
	
	delay_us(80);	//延时80微秒
	return DHT11_IN;
}


/***************************************
函数名:u8 DHT11_Read_Byte(void)
功能:DHT11 读取1字节数据
参数:无
返回值: 数值
说明:高位先出,先存储到高位
例如:10101000
接收:
	每次读取则左移
***************************************/
u8 DHT11_Read_Byte(void)
{
    
    
	u8 data = 0;
	for(int i = 0; i < 8;i++)
	{
    
    
		data <<= 1;			//先左移
		data |= DHT11_Read_Bit();
	}
	
	return data;	//返回数据
}



/******************************************************************************
函数名:u8 DHT11_Read_Data(u8 *HumiH,u8 *HumiL,u8 *TempH,u8 *TempL)
参数:
	@HumiH:湿度高位
	@HumiL:湿度低位
	@TempH:温度高位
	@TempL:温度低位
返回值:
	成功:0
	失败:1
*******************************************************************************/
u8 DHT11_Read_Data(u8 *HumiH,u8 *HumiL,u8 *TempH,u8 *TempL)
{
    
    
	DHT11_Reset();	//复位
	if(DHT11_Check() == 0)	//检查DHT11设备存在,则开始读取数据
	{
    
    
		u8 Data[5] = {
    
    0};
		for(int i = 0; i < 5;i++)
		{
    
    
			Data[i] = DHT11_Read_Byte();
		}
		//知道 Data[0] == 湿度高位 , Data[1] == 湿度低位 .... Data[4]  == 校验和
		if(Data[0] + Data[1] + Data[2] + Data[3] == Data[4])	//进行校验和
		{
    
    
			*HumiH = Data[0];
			*HumiL = Data[1];
			*TempH = Data[2];
			*TempL = Data[3];
			
			return 0;
		}
	}
	return 1;
}

9.IIC时序

一定要按照时序图来看

/*************************************
定义一些比较好用的宏
*************************************/
#define I2C_SCL PBout(6)	//时钟线输出
#define I2C_SDA PBout(7)	//数据线输出

#define READ_SDA PBin(7)	//数据线输入

#define I2C_IN()	GPIOB->MODER &= ~(0x3 << 14)		//设置数据线为输入模式
#define I2C_OUT()	GPIOB->MODER |=  (0x1 << 14)		//设置数据线为输出模式

#define I2C_DELAY 2		//延时值,进行错误微调,如果错误,请修改他的取值即可。
/**************************************
函数名:void IIC_Init(void)
功能:IIC总线初始化
参数:无
返回值:无
说明:
	.通过原理图 发现 
			I2C_SCL1 使用的是 PB6
			I2C_SDA1 使用的是 PB7
***************************************/
void IIC_Init(void)
{
    
    
	//1.是能 GPIOB 时钟总线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	//2.初始化GPIOE6 和 7
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;	//引脚:6|7
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;			//模式:输出模式
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			//状态:上拉
	GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;			//类型:开漏
	
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//3.设置电平状态
	I2C_SCL = 1;
	I2C_SDA = 1;
}


/*************************************
函数名:void IIC_Start(void)
功能:IIC 起始信号
参数:无
返回值:无
*************************************/
void IIC_Start(void)
{
    
    
	I2C_OUT();	//将数据线变为输出模式
	/*--------- 开始:都是高电平 ----------------*/
	I2C_SCL = 1;
	I2C_SDA = 1;
	/*--------- 维持 4.7 us 时间 ---------------*/
	delay_us(I2C_DELAY);
	
	/*--------- 后数据线拉低 --------*/
	I2C_SDA = 0;
	/*--------- 维持 4 us 时间 ---------------*/
	delay_us(I2C_DELAY);
	
	/*--------- 时钟线随后也被拉低 ------------*/
	I2C_SCL = 0;
}

/*************************************
函数名:void IIC_Stop(void)
功能:IIC 停止信号
参数:无
返回值:无
*************************************/
void IIC_Stop(void)
{
    
    
	I2C_OUT();	//将数据线变为输出模式
	/*--------- 开始:都是低电平 -------*/
	I2C_SCL = 0;
	I2C_SDA = 0;
	
	/*--------- 低电平延时 跳变------------*/
	delay_us(I2C_DELAY);
	I2C_SCL = 1;
	
	/*--------- 低电平延时------------*/
	delay_us(I2C_DELAY);
	I2C_SDA = 1;
	/*--------- 高电平延时------------*/
	delay_us(I2C_DELAY);
	/******* 进入总线空闲状态 *********/
}

/*************************************
函数名:void IIC_Ack(void)
功能:IIC 主机产生应答信号
参数:无
返回值:无
*************************************/
void IIC_Ack(void)
{
    
    
	I2C_SCL = 0;	//时钟线低电平
	delay_us(I2C_DELAY);
	I2C_OUT();	//将数据线变为输出模式
	/*------- 输出 低电平 0 -------- */
	I2C_SDA = 0;	//数据线
	delay_us(I2C_DELAY);
	/*------- 准备好数据,将时钟线电平拉高发送 ------*/
	I2C_SCL = 1;
	/*--------- 高电平延时------------*/
	delay_us(I2C_DELAY);
	/*-------- 数据已经发完,将时钟线拉低 ---------*/
	I2C_SCL = 0;

}

/*************************************
函数名:void IIC_NAck(void)
功能:IIC 主机产生非应答信号
参数:无
返回值:无
*************************************/
void IIC_NAck(void)
{
    
    
	I2C_OUT();	//将数据线变为输出模式
	
	I2C_SCL = 0;	//时钟线低电平
	delay_us(I2C_DELAY);
	/*------- 输出 高电平 1 -------- */
	I2C_SDA = 1;	//数据线
	delay_us(I2C_DELAY);
	/*------- 准备好数据,将时钟线电平拉高发送 ------*/
	I2C_SCL = 1;
	/*--------- 高电平延时------------*/
	delay_us(I2C_DELAY);
	/*-------- 数据已经发完,将时钟线拉低 ---------*/
	I2C_SCL = 0;
}

/**************************************
函数名:void IIC_Wait_Ack(void)
功能:IIC 主机等待从机应答
参数:无
返回值:
	成功:0
	失败: -1
**************************************/
int IIC_Wait_Ack(void)
{
    
    
	int timer = 0;
	/*----- 先将我的数据线和时钟线都拉高,用于等待应答 ------*/
	I2C_SDA = 1;	//数据线拉高
	delay_us(I2C_DELAY);
	I2C_SCL = 1;	//时钟线拉高
	delay_us(I2C_DELAY);
	/*----- 等待数据线是否拉低 -------*/
	I2C_IN();	//将模式变为输入模式
	while(READ_SDA == 1)	//如果还是高电平,我就一直等
	{
    
    
		timer++;
		if(timer > 250)	//超时了,我要将我变为空闲状态,不占用总线
		{
    
    
			IIC_Stop();		//发出停止信号
			return -1;		//没有应答
		}
	}
	
	I2C_SCL = 0;
	delay_us(I2C_DELAY);	/*---- 同学们,在这里加入一个延时就可以了 -----*/
	return 0;	//应答了
}


/**************************************
函数名:void IIC_Send_Byte(u8 data)
功能:IIC 发送1字节 8Bit 数据
参数:无
返回值:无
**************************************/
void IIC_Send_Byte(u8 data)
{
    
    
	I2C_OUT();	//将数据线变为输出模式
	/***** 将时钟线拉低,准备开始写 *****/
	I2C_SCL = 0;
	
	/***** 循环将 8为数据给发送出去:先传送最高位  ****/
	for(int i = 0; i < 8;i++)
	{
    
    
		I2C_SDA = (data&0x80) >> 7;
		data <<= 1;
		/**** 等待将数据写入到引脚 ****/
		delay_us(I2C_DELAY);
		/**** 将时钟线拉高,表示数据稳定,正在发送数据 ***/
		I2C_SCL = 1;
		
		/**** 需要等待 从机读取(读取需要一定时间) ****/
		delay_us(I2C_DELAY);
		
		/***** 从机取走数据之后,将始终线拉低,继续向从机写入数据 *****/
		I2C_SCL = 0;
		delay_us(I2C_DELAY);
	}
}



/**************************************
函数名:u8 IIC_Read_Byte(void)
功能:IIC 读取1字节 8Bit 数据
参数:无
返回值:数据
**************************************/
u8 IIC_Read_Byte(void)
{
    
    
	u8 data = 0;	//用于存数据
	
	I2C_IN();	//设置数据线为输入模式
	for(int i = 0; i < 8;i++)
	{
    
    
		I2C_SCL = 0;
		delay_us(I2C_DELAY); 
		
		I2C_SCL = 1;	//准备接收数据
		delay_us(I2C_DELAY);
		
		data <<= 1;
		data += READ_SDA;
	}
	return data;
}

10.用EEPROM(AT24C02)存储数据,防止数据掉电丢失

一定要按照时序图来看
在这里插入图片描述

void AT24C02C_Write_Byte(uint8_t wr_addr,uint8_t data)
{
    
    
	IIC_Start();			//起始信号
	IIC_Send_Byte(AT24C02C);	//发送器件地址,0写入数据
	IIC_Wait_Ack();			//等待应答
	
	IIC_Send_Byte(wr_addr);
	IIC_Wait_Ack();			//等待应答
	
	IIC_Send_Byte(data);	//写入数据
	IIC_Stop();				//停止信号
}

void AT24C02C_Write(uint8_t wr_addr,uint8_t *data,uint8_t len)
{
    
    
	IIC_Start();			//起始信号
	IIC_Send_Byte(AT24C02C);	//发送器件地址,0写入数据
	IIC_Wait_Ack();			//等待应答
	
	IIC_Send_Byte(wr_addr);
	IIC_Wait_Ack();			//等待应答
	
    for(int  i = 0 ; i < len ; i++)	//循环发送数据
    {
    
    
        IIC_Send_Byte(data[i]);	//写入数据
		IIC_Wait_Ack();			//等待应答
    }
	IIC_Stop();				//停止信号
}

void AT24C02C_Read(uint8_t wr_addr,uint8_t *data,uint8_t len)
{
    
    
	IIC_Start();				//起始信号
	IIC_Send_Byte(AT24C02C);	//发送器件地址,0写入数据
	IIC_Wait_Ack();				//等待应答
	
	IIC_Send_Byte(wr_addr);		//发送器件地址,0写入数据
	IIC_Wait_Ack();				//等待应答

	IIC_Start();					//起始信号
	IIC_Send_Byte(AT24C02C+0x01);	//发送器件地址,1读入数据
	IIC_Wait_Ack();			//等待应答
	
    for(int i = 0; i < len;i++)
	{
    
    
		data[i] = IIC_Read_Byte();
		if(i != len-1)IIC_Ack();	//应答
		wr_addr++;
	}
	
	IIC_Stop();
}

11.OLED显示屏

本次使用的OLED是8行*128列的点阵
在这里插入图片描述

12.WiFi(ESP8266),AT指令操作

/***************************************
函数名:void WIFI_Set_Mode(int mode)
功能:配置 WIFI 模式
参数:
	@mode : 配置WIFI模式
		1:STA 模式可开热点
		2:AP  可连路由
		3.STA + AP模式
返回值:
说明:参数一般默认填写3
****************************************/
void WIFI_Set_Mode(int mode)
{
    
    
	char buf[50] = "";
	sprintf(buf,"AT+CWMODE=%d\r\n",mode);
	
	USART2_Send_Data((u8 *)buf,strlen(buf));
	delay_ms(400);
}

/**********************************************************
函数名:void WIFI_Connect(char *user,char *pass)
功能:配置 WIFI 连接路由
参数:
	@user : WIFI名称
	@pass : WIFI密码
返回值:
说明:
	后期验证,可以使用 "WIFI CONNECTED" 返回值作为判断条件
***********************************************************/
void WIFI_Connect(char *user,char *pass)
{
    
    
	char buf[50] = "";
	sprintf(buf,"AT+CWJAP=\"%s\",\"%s\"\r\n",user,pass);
	USART2_Send_Data((u8 *)buf,strlen(buf));
	delay_ms(12000);	//基础功能跑一波 ---- 后续再优化
}

/**********************************************************
函数名:void WIFI_Connect_TCP(char *IP,int port)
功能:配置 WIFI 连接路由
参数:
	@IP   : TCP服务器IP地址
	@port : TCP服务器端口号
返回值:
说明:
	后期验证,可以使用 "WIFI CONNECTED" 返回值作为判断条件
***********************************************************/
void WIFI_Connect_TCP(char *IP,int port)
{
    
    
	char buf[50] = "";
	sprintf(buf,"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",IP,port);
	USART2_Send_Data((u8 *)buf,strlen(buf));
	delay_ms(5000);	//基础功能跑一波 ---- 后续再优化
}

/**********************************************************
函数名:void WIFI_Connect_TCP(char *IP,int port)
功能:配置 WIFI 连接路由
参数:
	@IP   : TCP服务器IP地址
	@port : TCP服务器端口号
返回值:
说明:
	后期验证,可以使用 "WIFI CONNECTED" 返回值作为判断条件
***********************************************************/
void WIFI_Send_Data(char *data,int len)
{
    
    
	char buf[50] = "";
	sprintf(buf,"AT+CIPSEND=%d\r\n",len);
	USART2_Send_Data((u8 *)buf,strlen(buf));
	delay_ms(100);	//基础功能跑一波 ---- 后续再优化
	
	USART2_Send_Data((u8 *)data,len);
	delay_ms(500);	//基础功能跑一波 ---- 后续再优化
}

13.SPI

/*******************************************************************
函数名:void SPI1_Init(void)
功能:SPI1总线初始化
参数:无
返回值:无
说明:
	.通过原理图 发现 
	SCK 时钟线 ----- PB3
	MISO接收线 ----- PB4
	MOSI发送线 ----- PB5
	编写初始化(时钟总线,设置IO复用SPI,配置SPI,使能SPI)
*******************************************************************/
void SPI1_Init(void)
{
    
    
	//1.时钟总线 GPIOB 和 SPI1 
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	//2.配置GPIO为复用模式
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
	
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//3.将其GPIO复用为 SPI 功能
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
	
	//4.配置 SPI 参数
	SPI_InitTypeDef SPI_InitStruct;
	
	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;	//时钟分频:32分频一般设备都在 20MHz以内
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;							//极性:0,第一个时钟边沿采集
	SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;								//相位:0,低电平时钟
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;						//数据位:8位
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;							//模式:主机模式
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;		//模式:全双工
	SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;						//先发:高位先发
	SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;								//片选信号:软件触发
	SPI_InitStruct.SPI_CRCPolynomial = 0x02;							//CRC校验码:多项式计算
	
	SPI_Init(SPI1, &SPI_InitStruct);
	
	//5.使能 SPI1 
	SPI_Cmd(SPI1, ENABLE);
}

/*******************************************************************
函数名:u8 SPI1_Write_Read(u8 data)
功能:SPI1总线收发函数
参数:	
	@data:数据
返回值:
	u8 类型结果
说明:
	外设读写操作时同步完成.
	1. 只发送数据则忽略返回值
	2. 只接主机发送空字节 0x00 即可
*******************************************************************/
u8 SPI1_Write_Read(u8 data)
{
    
    
	//1.判断 SPI1 是否为发送空闲
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
	{
    
    
		;	//RESET 非空闲,则循环等待
	}
	
	//2.发送数据
	SPI_I2S_SendData(SPI1,data);
	
	//3.判断 SPI1 是否为有数据接收
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
	{
    
    
		;	//RESET 数据为空,则循环等待
	}
	
	//4.接收数据
	return SPI_I2S_ReceiveData(SPI1);
}

14.W25Q64(flash)

这里我们用的是W25Q64,其实都差不多。
在这里插入图片描述

/****************************************
说明:
	.通过原理图 发现  w25q64 片选 CS 
	SCK 时钟线 ----- PB3
	MISO接收线 ----- PB4
	MOSI发送线 ----- PB5
	CS  片选线 ----- PB2 ---- 该引脚操作
****************************************/
void W25Q64_Init(void)
{
    
    
	//1.时钟总线使能 GPIOB
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
	//2.配置GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
	
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//3.将引脚设置为高电平,非片选
	W25Q64 = 1;
}

/****************************************
功能: 写使能 = 0x06
****************************************/
void W25Q64_Write_Ebale(void)
{
    
    
	W25Q64 = 0;	//片选拉低 
	
	SPI1_Write_Read(0x06);
	
	W25Q64 = 1; //片选拉高
}

/***************************************
功能:检测W25Q64是否忙碌
***************************************/
void W25Q64_Check_Busy(void)
{
    
    
	u8 flag = 0;
	do
	{
    
    
		W25Q64 = 0;	//片选拉低 
		SPI1_Write_Read(0x05);
		flag = SPI1_Write_Read(0xff);	//读取w25q64度状态寄存器
		
		W25Q64 = 1; //片选拉高
	}while(flag &0x1);
}

/***************************************
功能:扇区擦除(4k) = 0x20
***************************************/
void W25Q64_Erasr(u32 addr)
{
    
    
	W25Q64_Write_Ebale();	//使能
	W25Q64 = 0;				//片选拉低 
	
	SPI1_Write_Read(0x20);	//扇区擦除
	
	SPI1_Write_Read(addr >> 16 & 0xff);	//发送高位 23 - 16 位
	SPI1_Write_Read(addr >> 8 & 0xff);	//发送高位 16 - 8 位
	SPI1_Write_Read(addr >> 0 & 0xff);	//发送低8位
	
	W25Q64 = 1; 			//片选拉高
	W25Q64_Check_Busy();
}


/***************************************
函数:int W25Q64_Write_Data(u32 addr,u8 *data,u8 len)
功能:发送数据 = 0x02
参数:
	@addr:要写入到的地址
	@data:数据
	@len:数据长度
返回值:
	成功写入的长度
****************************************/
int W25Q64_Write_Data(u32 addr,u8 *data,u8 len)
{
    
    
	W25Q64_Write_Ebale();	//1.写使能
	W25Q64 = 0;	//片选拉低 
	
	/***** 1.发送命令 *****/
	SPI1_Write_Read(0x02);

	/***** 2.发送地址 *****/
	SPI1_Write_Read(addr >> 16);	//发送高位 23 - 16 位
	SPI1_Write_Read(addr >> 8);		//发送高位 16 - 8 位
	SPI1_Write_Read(addr >> 0);		//发送低8位
	
	/***** 3.发送数据 *****/
	for(int i = 0; i < len;i++)
	{
    
    
		SPI1_Write_Read(data[i]);	//发送数据
	}
	
	W25Q64 = 1; //片选拉高
	W25Q64_Check_Busy();
	
	return len;
}


/***************************************
函数:int W25Q64_Read_Data(u32 addr,u8 *data,u8 len)
功能:接收数据 = 0x03
参数:
	@addr:要读取内存地址
	@data:数据
	@len:数据长度
返回值:
	成功读取的长度
****************************************/
int W25Q64_Read_Data(u32 addr,u8 *data,u8 len)
{
    
    
	W25Q64_Write_Ebale();	//1.写使能
	W25Q64 = 0;	//片选拉低
	
	/***** 1.发送命令 *****/
	SPI1_Write_Read(0x03);
	/*****2.发送地址 ****/
	SPI1_Write_Read(addr >> 16);	//发送高位 23 - 16 位
	SPI1_Write_Read(addr >> 8);		//发送高位 16 - 8 位
	SPI1_Write_Read(addr >> 0);		//发送低8位
	
	/*****3.读取数据 0x00 发送空数据 ****/
	for(int i = 0; i < len;i++)
	{
    
    
		data[i] = SPI1_Write_Read(0xff);	//空指令
	}
	
	W25Q64 = 1; //片选拉高
}

15.ADC初始化

在这里插入图片描述
在这里插入图片描述

/***************************************
函数名:void ADC1_Init(void)
功能:ADC1初始化
参数:无
返回值:无
****************************************/
void ADC1_Init(void)
{
    
    
    //1.使能时钟 ADC1 时钟总线
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    //2.配置ADC公共设置
    ADC_CommonInitTypeDef ADC_CommonInitStruct;		//ADC 公共配置结构体

    ADC_CommonStructInit(&ADC_CommonInitStruct);	//使用ADC默认配置   
    //ADC_CommonInitStruct->ADC_Mode = ADC_Mode_Independent;				      //独立模式
    //ADC_CommonInitStruct->ADC_Prescaler = ADC_Prescaler_Div2;	 //分频模式 2 分频:84 / 2 = 42MHz > 36MHz
    //ADC_CommonInitStruct->ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;		  //DMA模式:失能
    //ADC_CommonInitStruct->ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;  //采样时间:5时钟周期
    //ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4;	//由于默认是 2 分频,太高,我们自己设置为4分频即可 21MHz

    ADC_CommonInit(&ADC_CommonInitStruct);	//ADC 公共配置初始化

    //3.配置ADC参数
    ADC_InitTypeDef ADC_InitStruct;

    ADC_StructInit(&ADC_InitStruct);
	//ADC_InitStruct->ADC_Resolution = ADC_Resolution_12b;	//分辨率:12位
	//ADC_InitStruct->ADC_ScanConvMode = DISABLE;			//扫描模式:失能
	//ADC_InitStruct->ADC_ContinuousConvMode = DISABLE;		//连续转换:失能
	//ADC_InitStruct->ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;	//禁止触发检查,使用软件触发
	//ADC_InitStruct->ADC_DataAlign = ADC_DataAlign_Right;	//对其方式:右对齐
	// ADC_InitStruct->ADC_NbrOfConversion = 1;				//转换序列次数:单次

    ADC_Init(ADC1, &ADC_InitStruct);
	
	//4.使能ADC
	ADC_Cmd(ADC1,ENABLE);
}


/********************************************
函数名:u16 Get_Adc1(u8 ch)
功能:采集ADC通过值
参数:
	@ch:通道
返回值:数值结果
*******************************************/
u16 Get_Adc1(u8 ch)
{
    
    
	//1.指定ADC通道的规则: 规则组通道,一个序列采样时间
	ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_480Cycles);
	
	//2.指定ADC1软件转换器开启使能
	ADC_SoftwareStartConv(ADC1);
	
	//3.获取结果
	while( ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == 0);	//等待转换结果
	
	return ADC_GetConversionValue(ADC1);	//读取转换结果
}

16.光敏电阻的使用

/*******************************************************
函数:void Light_Init(void)
功能:光敏电阻传感器初始化
说明:
	我们的光敏电阻使用的引脚是 PA0 通道 ADC123_IN0
*********************************************************/
void Light_Init(void)
{
    
    
	//1.使能GPIOA总线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	//2.初始化GPIO为模拟输入
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;			//引脚
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;		//模式
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;	//状态:无上下拉
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//3.调用ADC1初始化
	ADC1_Init();
}

/********************************************************
函数:u16 Get_Light(void)
功能:获取光敏值
参数:无
返回值:光敏值
说明:我们ADC默认是单次采集,有可能异常
	可以类似跳水运动员,取平均值

制作一个粗劣范围: 
范围:0 - 100
数值:0   = 最暗
数值:100 = 最亮
********************************************************/
u16 Get_Light(void)
{
    
    
	u32 temp_val = 0;
	
	for(int i = 0; i < 10; i++)
	{
    
    
		temp_val += Get_Adc1(0);	//通道0
	}
	
	temp_val = temp_val / 10;		//求平均值
	
	if(temp_val > 4000) temp_val = 4000;
	
	return 100-(temp_val/40);
}

实现的功能:

下位机(STM32):
1.执行器:LED,蜂鸣器,风扇。
2.交互区: KEY按键,OLED显示屏
3.定时数据上传: 定时器7
4.PWM档位: BEEP 和 风扇
5.掉电保护执环境状态eeprom 和 独立看门狗异常监测
6.无线交互:WIFI esp 12s
7.传感器:温湿度,光照

上位机(Qt界面):
1.制作TCP服务器
2.监控下位机环境
3.控制下位机设备

本来用Qt写了一个温湿度曲线图,把数据存到数据库,再读出来显示。时间轴做x轴,但是格式转换出问题了,是负值,显示不了数据,各位有没有其他办法呢。
出现了点bug,数据显示不出来。

后续打算用MQTT和云服务器实现远程控制。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

资源已上传,需要的自提。

猜你喜欢

转载自blog.csdn.net/qq_52531759/article/details/131822071