了解的知识
需要什么就添加什么,都在对应的文件里,比如gpio就再gpio.c里面,在.c文件里右键跳到头文件查找自己需要的函数。xx.s是汇编的启动文件,里面有中断的向量表。misc.c里面是中断函数。在system_stm32f4xx.c 和stm32f4xx.h里面修改对应的时钟频率。
LED灯使用
1.首先查看开发板对应的原理图引脚,和芯片连接的网络标号,看它在哪一根时钟总线上。
/****************************************
函数名: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和云服务器实现远程控制。
资源已上传,需要的自提。