看了很久的江科大课程了,陆陆续续也学了不少东西,就想着做个类似课设的小项目,尽量多的用上学到的东西。因为是类似课设的小项目,所以做的东西比较简单;没有商业项目经验,所以代码写的很烂,漏洞和Bug想必只会多不会少。新手小白,如果被大佬看到了不顺眼的地方请多多海涵,也请多给我提建议,谢谢大家。
内容
定时器定时,每秒读取一次MPU6050,并通过ESP8266将数据上传到服务器,使用DMA转运串口数据。服务器接受到数据后,查看Y轴加速度数据,当大于2000时为安全状态,返回"Safe",否则返回"Warn"。当接收到串口数据后,若接收到"Warn",则打开蜂鸣器和LED小灯,否则关闭。
在OLED的第一行和第二行显示系统所在的工作状态和标志位,第3、4行将显示串口发送和接受的数据
总体设计
懒得画电路图了,请欣赏灵魂画手带来的作品
原理图
数据收发(ESP8266)部分
ESP8266使用串口通讯。
首先初始化转运数据所用的DMA1_Channel5,将串口接收到的数据转运到自定义的数组中。
接着初始化所使用的串口USART1,写好底层的串口收发代码,开启串口空闲中断,便于接受和判断ESP8266或服务器发来的数据。定义好相关标志位,在收到特定信息后,改变一些标志位便于主程序使用。
在ESP8266的代码文件include定义好的DMA和USART文件,并且基于此写ESP的初始化、连接WIFI、连接服务器、收发数据以及进行相关配置的代码。
传感器(MPU6050)部分
MPU6050使用I2C通讯。直接在初始化函数中初始化所使用的I2C2,并写好MPU6050的初始化、读写寄存器函数。
反馈部分
使用PB8 PB9 软件模拟I2C控制OLED
使用TIM2输出的PWM波控制蜂鸣器发声。在初始化函数中初始化所使用的TIM2,并写好MPU6050的初始化、读写寄存器函数。
使用空闲的PB12引脚控制LED小灯
主函数
在主函数中依次初始化上述所用的外设以及内部寄存器,使用TIM3定时一秒,每经过1秒就获取MPU6050数据并发送到服务器。
硬件设计
数据收发(ESP8266)部分
将USART1所在的PA9(Tx)、PA10(Rx)连接到ESP8266的Rx、Tx引脚,并使用PA8引脚连接到8266的RST便于进行硬件重置。
传感器(MPU6050)部分
将I2C2所在的PB10(SCL)、PB11(SDA)连接到MPU6050的SCL、SDA引脚
反馈部分
将蜂鸣器的I/O连接到TIM2的PWMI输出的PA0引脚。
OLED使用的软件模拟I2C,和LED一样寻找空闲引脚即可。
软件设计
数据收发(ESP8266)部分
DMA_for_USART.h
#ifndef __DMA_FOR_USART_H
#define __DMA_FOR_USART_H
#include "stm32f10x.h" // Device header
//保存搬运的数据
extern char RxData[];
void DMA_for_USART_Init(void);
#endif
DMA_for_USART.c
/*
使用DMA1将串口接收的数据转运到RxData数组中暂存
*/
#include "DMA_for_USART.h"
char RxData[2048];
void DMA_for_USART_Init(void)
{
//打开DMA1时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//DMA初始化结构体
DMA_InitTypeDef DMA_InitStructure;
//数据源站点
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);//源地址为USART1的数据寄存器
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度为一字节
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//不自增
//数据目的站点
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxData;//目的地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据宽度为一字节
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//自增
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//转运方向 源地址->目的地址
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//循环装填
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//硬件触发
DMA_InitStructure.DMA_BufferSize = 2048;//转运最大值为2048位
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//初始化DMA1的通道5
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
//使能DMA1的通道5
DMA_Cmd(DMA1_Channel5, ENABLE);
}
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h" // Device header
#include "string.h"
#include "OLED.h"
#include "DMA_for_USART.h"
#include "LED.h"
#include "Buzzer.h"
//接受buffer
extern uint8_t ESP_Ready_flag;
extern uint8_t ESP_OK_flag;
extern uint8_t Rx_length;
extern uint8_t Serial_RxFlag;
extern uint8_t Safe_flag;
void Serial_Init(void);
void Serial_SendByte(unsigned char Byte);
void Serial_SendArray(uint8_t *Array, uint16_t length);
void Serial_SendString(char *String);
#endif
Serial.c
#include "Serial.h"
/*
串口收发数据控制ESP8266
利用串口空闲中断 和 String中的 strstr确认命令和返回的关键值
*/
//接收数据的长度
uint8_t Rx_length = 0;
//接收完成标志位
uint8_t Serial_RxFlag = 0;
//是否安全
uint8_t Safe_flag = 0;
//ESP8266处理完成标志
uint8_t ESP_Ready_flag = 0;
uint8_t ESP_OK_flag = 1;
void Serial_Init(void)
{
//初始化转运数据用DMA
DMA_for_USART_Init();
//开启串口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//开启GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//GPIOA初始化
GPIO_InitTypeDef GPIO_InitStructure;
//复用推挽输出为串口发送
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//PA9引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//上拉输入为串口接收
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//PA10引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化
USART_InitTypeDef USART_InitStructure;
//波特率
USART_InitStructure.USART_BaudRate = 115200;
//硬件流控 设置为不控制
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//串口模式设置为输出
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
//校验 设置为无校验
USART_InitStructure.USART_Parity = USART_Parity_No;
//停止位长度设置 设置为1位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//字长设置 无校验 设置为8位
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
//设置中断优先组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//初始化NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//开启USART串口空闲中断IDLE到NVIC的输出
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
//开启USART串口到DMA触发的硬件连接
USART_DMACmd(USART1, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);
//使能串口USART1
USART_Cmd(USART1, ENABLE);
}
void Serial_SendByte(uint8_t Byte)
{
//发送数据
USART_SendData(USART1, Byte);
//判断是否发送完成
while(
//获取USART1的发送数据寄存器空标志位USART_FLAG_TXE
USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET
);
/*
由手册得知 在写入发送数据寄存器后
TXE标志位会自动清0 所以不需要手动清0
*/
}
//发送队列
void Serial_SendArray(uint8_t *Array, uint16_t length)
{
for(uint16_t i = 0; i < length; i++)
{
Serial_SendByte(Array[i]);
OLED_ShowString(2,1,"send");
OLED_ShowNum(3,1,Array[i],1);
}
}
//发送字符串
void Serial_SendString(char *String)
{
OLED_ShowString(2,1,"send");
OLED_ShowString(3,1,String);
for(uint8_t i = 0; String[i] != '\0' ; i++)
Serial_SendByte(String[i]);
}
//串口空闲中断函数
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET)
{
//串口接收完成标志位置1
Serial_RxFlag = 1;
//读接收数据长度
Rx_length = 2048 - DMA1_Channel5->CNDTR;
//使能DMA1的通道5
DMA_Cmd(DMA1_Channel5, DISABLE);
//重置DMA1C5计数器
DMA_SetCurrDataCounter(DMA1_Channel5, 2048);
//使能DMA1的通道5
DMA_Cmd(DMA1_Channel5, ENABLE);
//清除标志位
USART1->SR; // 读取状态寄存器
USART1->DR; // 读取数据寄存器
//清除IDLE中断标志位
USART_ClearITPendingBit(USART1, USART_IT_IDLE);
Serial_RxFlag = 1;
RxData[Rx_length]='\0';
if(ESP_Ready_flag == 1)
OLED_ShowString(4,1,RxData);
//ESP8266未返回ready
if(ESP_Ready_flag == 0)
{
if(strstr(RxData,"ready") != NULL)
{
ESP_Ready_flag = 1;
OLED_ShowString(3,9,"RD:1");
}
}
//ESP8266未返回OK
if(ESP_OK_flag == 0)
{
if(strstr(RxData,"OK") != NULL)
{
ESP_OK_flag = 1;
}
}
//处于危险状态 判断服务器是否返回Safe
if(Safe_flag == 0)
{
if(strstr(RxData,"Safe") != NULL)
{
OLED_ShowString(1,8,"ITSAFE");
Safe_flag = 1;
Buzzer_Turn_Off();
LED_Turn_Off();
}
}
//处于安全状态 判断服务器是否返回Warn
if(strstr(RxData,"Warn") != NULL)
{
OLED_ShowString(1,8,"ITWARN");
Safe_flag = 0;
Buzzer_Turn_On();
LED_Turn_On();
}
}
}
ESP8266.h
#ifndef __ESP8266_H
#define __ESP8266_H
#include "Serial.h"
#include "delay.h"
void ESP8266_Init(void);
void ESP8266_Connect_Wifi(char * wifi_name, char * wifi_pswd);
void ESP8266_Config(void);
void ESP8266_Connect_Server(char * ip_mode, char * ip_address, char * post);
void ESP8266_OpenSocket(void);
void ESP8266_CloseSocket(void);
void ESP8266_SendData(char *data);
#endif
ESP8266.c
#include "ESP8266.h"
//初始化
void ESP8266_Init(void)
{
//串口初始化
Serial_Init();
//打开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//PA8引脚用于重置ESP8266
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//重置ESP8266
GPIO_SetBits(GPIOA, GPIO_Pin_8);
Delay_ms(20);
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
Delay_ms(20);
GPIO_SetBits(GPIOA, GPIO_Pin_8);
while(ESP_Ready_flag == 0);
OLED_ShowString(2,8,"Rest");
//设置ESP8266为STA模式
ESP_OK_flag = 0;
Serial_SendString("AT+CWMODE=1\r\n");
while(ESP_OK_flag == 0);
OLED_ShowString(2,8,"Init");
}
//连接wifi
void ESP8266_Connect_Wifi(char * wifi_name, char * wifi_pswd)
{
ESP_OK_flag = 0;
Serial_SendString("AT+CWJAP=\"");
Serial_SendString(wifi_name);
Serial_SendString("\",\"");
Serial_SendString(wifi_pswd);
Serial_SendString("\"\r\n");
while(ESP_OK_flag == 0);
OLED_ShowString(2,8,"Wifi");
}
//设置为单连接和透传模式
void ESP8266_Config(void)
{
ESP_OK_flag = 0;
Serial_SendString("AT+CIPMUX=0\r\n");
while(ESP_OK_flag == 0);
ESP_OK_flag = 0;
Serial_SendString("AT+CIPMODE=1\r\n");
while(ESP_OK_flag == 0);
OLED_ShowString(2,8,"Conf");
}
//使用TCP/UDP模式连接到服务器的指定端口
void ESP8266_Connect_Server(char * ip_mode, char * ip_address, char * post)
{
ESP_OK_flag = 0;
Serial_SendString("AT+CIPSTART=\"");
Serial_SendString(ip_mode);
Serial_SendString("\",\"");
Serial_SendString(ip_address);
Serial_SendString("\",");
Serial_SendString(post);
Serial_SendString("\r\n");
while(ESP_OK_flag == 0);
OLED_ShowString(2,8,"Serv");
}
//打开透传发送模式
void ESP8266_OpenSocket(void)
{
Serial_SendString("AT+CIPSEND\r\n");
OLED_ShowString(2,8,"Open");
}
//关闭透传发送模式
void ESP8266_CloseSocket(void)
{
Serial_SendString("+++");
OLED_ShowString(2,8,"Clos");
}
//发送字符串数据
void ESP8266_SendData(char *data)
{
Serial_SendString(data);
OLED_ShowString(2,8,"Send");
}
传感器(MPU6050)部分
MPU6050_Reg.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
/*
预定义 MPU6050的相关命令
*/
#define MPU6050_SMPLRT_DIV 0x19
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C
#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
#define MPU6050_WHO_AM_I 0x75
#endif
MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_H
#include "stm32f10x.h" // Device header
#include "MPU6050_Reg.h"
//定义结构体用于传输6轴数据
typedef struct
{
int16_t AccX;
int16_t AccY;
int16_t AccZ;
int16_t GyroX;
int16_t GyroY;
int16_t GyroZ;
} MPU6050_Data;
//初始化
void MPU6050_Init(void);
//写寄存器
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
//读寄存器
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
uint8_t MPU6050_GetID(void);
//读6轴数据
MPU6050_Data MPU6050_GetDATA(void);
#endif
MPU6050.c
#include "MPU6050.h"
/*
使用硬件I2C2对MPU6050进行读写控制
I2C2 中 SCL->PB10 SDA->PB11
*/
#define MPU6050_ADDRESS 0xD0
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timeout;
Timeout = 10000; //给定超时计数时间
while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) //循环等待指定事件
{
Timeout --; //等待时,计数值自减
if (Timeout == 0) //自减到0后,等待超时
{
/*超时的错误处理代码,可以添加到此处*/
break; //跳出等待,不等了
}
}
}
void MPU6050_Init(void)
{
//手册内容
//MyI2C_Init();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
//复用开漏输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitTypeDef I2C_InitStructure;
//I2C模式
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
//I2C时钟速度 50kHZ
I2C_InitStructure.I2C_ClockSpeed = 50000;
//I2C时钟占空比2:1 ,I2C时钟速度>100kHz时有效
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
//应答位使能
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
//寄存器地址宽度
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
//从机地址 这里作为主机
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C2, &I2C_InitStructure);
//使能I2C
I2C_Cmd(I2C2, ENABLE);
//电源管理
//解除睡眠 选择陀螺仪时钟
MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
//6轴均不待机
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
//MPU6050配置
//采样分频为10
MPU6050_WriteReg(MPU6050_SMPLRT_DIV , 0x09);
//配置外部同步 滤波最大
MPU6050_WriteReg(MPU6050_CONFIG , 0x06);
//陀螺仪配置为最大量程
MPU6050_WriteReg(MPU6050_GYRO_CONFIG , 0x18);
//加速度仪配置为最大量程
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}
//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
//通讯开始
//MyI2C_Start();
I2C_GenerateSTART(I2C2, ENABLE);
//等待事件EV5发生
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
//指定设备地址
//MyI2C_SendByte(MPU6050_ADDRESS);
I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
//接受应答位
//MyI2C_ReceiveAck();
//等待事件EV6发生->设置为发送模式
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
//指定寄存器地址
//MyI2C_SendByte(RegAddress);
I2C_SendData(I2C2, RegAddress);
//接受应答位
//MyI2C_ReceiveAck();
//等待事件EV8发生->发送中
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
//写数据
//MyI2C_SendByte(Data);
I2C_SendData(I2C2, Data);
//不接受应答位
//MyI2C_ReceiveAck();
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//通讯结束
//MyI2C_End();
I2C_GenerateSTOP(I2C2, ENABLE);
}
//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
//通讯开始
//MyI2C_Start();
I2C_GenerateSTART(I2C2, ENABLE);
//等待事件EV5发生
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
//指定设备地址
//MyI2C_SendByte(MPU6050_ADDRESS);
I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
//接受应答位
//MyI2C_ReceiveAck();
//等待事件EV6发生->设置为发送模式
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
//指定寄存器地址
//MyI2C_SendByte(RegAddress);
I2C_SendData(I2C2, RegAddress);
//接受应答位
//MyI2C_ReceiveAck();
//等待事件EV8发生->发送中
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//开始时序
//MyI2C_Start();
I2C_GenerateSTART(I2C2, ENABLE);
//等待事件EV5发生
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
//指定设备地址 改成读地址
//MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);
//接受应答位
//MyI2C_ReceiveAck();
//等待事件EV6发生->设置为接受模式
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
//提前给Ack置0 STOP置1
I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);
//等待事件EV7发生->接受一个字节
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);
//缓存数据
//uint8_t Data = MyI2C_ReceiveByte();
uint8_t Data = I2C_ReceiveData(I2C2);
//ACK置1
I2C_AcknowledgeConfig(I2C2, ENABLE);
// //不需要其他数据 不返回应答
// MyI2C_SendAck(1);
// //通讯结束
// MyI2C_End();
return Data;
}
uint8_t MPU6050_GetID(void)
{
return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
//从MPU6050读取6轴数据
MPU6050_Data MPU6050_GetDATA(void)
{
MPU6050_Data Data;
Data.AccX =
(MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H)<<8)
| MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
Data.AccY =
(MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H)<<8)
| MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
Data.AccZ =
(MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H)<<8)
| MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
Data.GyroX =
(MPU6050_ReadReg(MPU6050_GYRO_XOUT_H)<<8)
| MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
Data.GyroY =
(MPU6050_ReadReg(MPU6050_GYRO_YOUT_H)<<8)
| MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
Data.GyroZ =
(MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H)<<8)
| MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
return Data;
}
反馈部分
OLED
这部分不是本人原创,B站关注江协科技
LED.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h" // Device header
void LED_Init(void);
void LED_Turn_On(void);
void LED_Turn_Off(void);
#endif
LED.c
#include "LED.h" // Device header
/*
使用PB12控制LED亮灭
*/
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
//低电平开灯
void LED_Turn_On(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
//高电平熄灯
void LED_Turn_Off(void)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
Buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H
#include "stm32f10x.h" // Device header
void Buzzer_Init(void);
void Buzzer_Turn_On(void);
void Buzzer_Turn_Off(void);
#endif
Buzzer.c
#include "Buzzer.h"
/*
使用TIM2即PA0引脚输出PWM波 使蜂鸣器发出响声
*/
#define Buzzer_Prot GPIOA
#define Buzzer_Pin GPIO_Pin_0
void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = Buzzer_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(Buzzer_Prot, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStructure);
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100-1;//计数 ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;//分频 PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式设置为模式PWM1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较级性设置为不反相
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse = 50;//设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, DISABLE);
}
void Buzzer_Turn_On(void)
{
TIM_Cmd(TIM2, ENABLE);
}
void Buzzer_Turn_Off(void)
{
TIM_Cmd(TIM2, DISABLE);
TIM_SetCounter(TIM2, 0);
}
定时器
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h" // Device header
extern uint8_t Get_Data_Timer_Flag;
void Timer_Init(void);
void Timer_Turn_On(void);
void Timer_Turn_Off(void);
#endif
Timer.c
#include "Timer.h"
/*
Timer3产生1秒信号触发中断向服务器发信息
*/
uint8_t Get_Data_Timer_Flag = 0;
void Timer_Init(void)
{
//配置定时器
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能APB1总线上的TIM3定时器
TIM_InternalClockConfig(TIM3);//输入使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//时基单元初始化结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;//输入信号滤波采样设置为1分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式设置为向上计数
//定时频率为CK_CNT_OV=CK_PSC/(PSC+1)/(ARR+1);
TIM_TimeBaseInitStructure.TIM_Period = 10000 -1;//10kHz进行10000次计数得到1Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//将72MHz分频7200为10kHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器 只有高级定时器拥有
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//时基单元初始化
TIM_ClearFlag(TIM3, TIM_FLAG_Update);//解决复位时会立即进入中断函数的问题
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);//中断设置为更新中断
//配置NVIC中断控制器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;//中断通道为TIM3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//响应优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//抢占优先级
NVIC_Init(&NVIC_InitStructure);
//关闭定时器
TIM_Cmd(TIM3, DISABLE);
}
void Timer_Turn_On(void)
{
//开启定时器
TIM_Cmd(TIM3, ENABLE);
}
void Timer_Turn_Off(void)
{
//关闭定时器
TIM_Cmd(TIM3, DISABLE);
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)//中断是否来自TIM3的更新事件
{
TIM_Cmd(TIM3, DISABLE);
Get_Data_Timer_Flag = 1;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);//中断处理程序完成 清除中断标志位
TIM_Cmd(TIM3, ENABLE);
}
}
主程序
main.c
#include "stm32f10x.h" // Device header
#include <string.h>
#include <stdio.h>
#include "Delay.h"
#include "OLED.h"
#include "ESP8266.h"
#include "MPU6050.h"
#include "Timer.h"
static uint8_t send_count = 0;
MPU6050_Data data;
//wifi名
char wifi_name[] = "******";
//wifi密码
char wifi_pswd[] = "***********";
char ip_mode[] = "TCP";
//服务器ip
char server_addr[] = "*************";
//服务器开启的端口
char server_port[] = "26";
int main(void)
{
Timer_Init();
OLED_Init();
MPU6050_Init();
ESP8266_Init();
Buzzer_Init();
LED_Init();
ESP8266_Connect_Wifi(wifi_name, wifi_pswd);
ESP8266_Config();
ESP8266_Connect_Server(ip_mode, server_addr, server_port);
//暂存转为字符串后的数据
char temp_char[20];
Timer_Turn_On();
//打开TCP连接
ESP8266_OpenSocket();
while(1)
{
if(Get_Data_Timer_Flag != 0)
{
// //打开TCP连接
// ESP8266_OpenSocket();
OLED_ShowString(2,1,"Send");
//清除自定义标志位
Get_Data_Timer_Flag =0;
//获取数据
data = MPU6050_GetDATA();
//发送数据
sprintf(temp_char,"%d",data.AccX);
ESP8266_SendData(temp_char);
ESP8266_SendData(",");
sprintf(temp_char,"%d",data.AccY);
ESP8266_SendData(temp_char);
ESP8266_SendData(",");
sprintf(temp_char,"%d",data.AccZ);
ESP8266_SendData(temp_char);
ESP8266_SendData(",");
sprintf(temp_char,"%d",data.GyroX);
ESP8266_SendData(temp_char);
ESP8266_SendData(",");
sprintf(temp_char,"%d",data.GyroY);
ESP8266_SendData(temp_char);
ESP8266_SendData(",");
sprintf(temp_char,"%d",data.GyroZ);
ESP8266_SendData(temp_char);
send_count ++;
OLED_ShowNum(2,4,send_count,2);
// //关闭TCP连接
// ESP8266_CloseSocket();
}
}
}
服务器代码
记得开启在防火墙开启26端口(当然可以自定义为想要的端口啦)
import socket
import re
server = socket.socket()
server.bind(('0.0.0.0',26))
server.listen(1)
data_send = ""
while True:
client, addr = server.accept()
data_recv = client.recv(1024)
#接收数据
data_recv = data_recv.decode()
print(f"Received: ")
print(data_recv)
#从数据中提取数字信息
numbels = re.findall(r'\d+',data_recv)
print(numbels)
#判断是否安全
if int(numbels[2]) < 2000:
data_send = "Warn"
else :
data_send = "Safe"
client.sendall(data_send.encode())
print(data_send)
client.close()
结果图
水平放置时,Y_Acc大于2000,返回Safe,蜂鸣器不响灯不亮
倾斜放置时,Y_Acc小于2000,返回Warn,蜂鸣器响灯亮