0 前言
🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩 单片机stm32智能路灯智能灯控系统 - LoRa远程通信
🥇学长这里给一个题目综合评分(每项满分5分)
- 难度系数:4分
- 工作量:4分
- 创新点:3分
🧿 毕设项目分享:见文末!
1 简介
本项目设计实现了基于stm32和lora模块的智能灯系统,使用LoRa串口透传无线收发的大棚监控系统。可以实时检测光照,温湿度,烟雾浓度,环境噪音,是否有人。
将数据通过lora模块发送到主机进行监控。主机可以设置灯的工作时间,将工作时间发给从机。
从机在工作时根据亮度自动调整led亮度,当有人时灯开到最亮。
2 主要器件
- STM32F103C8T6
- lora模块
- 温湿度模块
- 烟雾检测模块
- 光敏模块
- OLED屏
- led灯
3 实现效果
- 屏幕显示
- 温湿度、烟雾、光照检测
- 调整LED亮度
4 设计原理
4.1 Lora模块
简单来讲,LoRa就是一种低功耗远程无线通信技术。它是基于Semtech公司SX1276/1278芯片开发的无线数传模块,这种芯片集成规模小、效率高,从而让LoRa模块拥有高接收灵敏度。那么它相比于我们常用的蓝牙和WiFi有什么优势呢?总结而言,就是低功耗、远距离、抗干扰。相同条件下,LoRa模块比WIFI模块传输距离更远。多见的WIFI、蓝牙等近距离无线通信技术,通信距离一般也就只有几十米左右。如果要覆盖某个地区一个城市的网络,部署的成本会很高,不划算。而作为低功耗广域网的LoRa技术,无线通信距离可以达到几公里,甚至十几公里,相对WIFI模块而言,距离要远得多。而这些优势,使得LoRa在现在的物联网中应用广泛,得到了很快的发展。
引脚描述如下
接线
传输方式
LORA模块通信的传输方式主要有以下三种:
- 透明传输:即透传数据,例如:A设备发5字节数据AABBCCDDEE到B设备,B设备就可以收到数据AABBCCDDEE。(透明传输,针对设备相同地址、相同的通信信道,用户数据可以是字符或16进制数据形式)
- 定向传输:即定点传输,例如:A设备(地址为:0x1400,信道为0x17(23信道、433Mhz))需要向B设备(地址为:0x1234,信道为0x10(16信道、426Mhz))发送数据AABBCC,其通信格式为:123410AABBCC,其中1234为模块B的地址,10为信道,则模块B可以收到AABBCC。同理,如果B设备需要向A设备发送数据AABBCC,其通信格式为:140017AABBCC,则A设备可以收到AABBCC。(定向传输,针对设备间地址和通信信道不同,数据格式为16进制,发送格式:高位地址+低位地址+信道+用户数据)
- 广播与数据监听:将模块地址设置为0xFFFF(即65535),可以监听相同信道上的所有模块的数据传输,发送的数据,可以被相同信道上任意地址的模块收到,从而起到广播和监听的作用。
三者的区别:
简单的说,当只需要点对点通信(即两个LORA相互通信)时,可使用前两种方式。
其中,透明传输可以以字符或16进制数据形式发送,需要两个LORA模块的地址、信道相同,而定向传输只能以16进制的特定格式发送,无需二者地址或信道相同。(由于LORA模块的地址和信道有十分多的选择,一般不会出现冲突的可能,所以个人推荐使用透明传输更方便。)
当需要多个LORA通信时,选用第三种传输方式。
4.2 DHT11温湿度传感器
简介
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为 4 针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。
接线
DHT11编码步骤
- 单片机上点后1s内不读取(不重要)
- 主机(单片机)发送起始信号:
- 主机先拉高data
- 拉低data延迟18ms
- 拉高data(通过此操作将单片机引脚设置为输入)。
- 从机(DHT11)收到起始信号后进行应答:
- 从机拉低data,主机读取到data线被拉低持续80us后从机拉高data线, 持续80us,直到高电平结束,意味着主机可以开始接受数据。
- 主机开始接收数据:
- 主机先把data线拉高(io设置为输入)
- 从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)
- 从机拉高data线后,延迟40us左右(28~70us之间)主机再次读取data线电平,如果为低电平,则为“0”,如果为高电平,则为“1”。
- 继续重复上述1,2步骤累计40次。
4.3 光照传感器
简介
光敏传感器是最常见的传感器之一,它的种类繁多,主要有:光电管、光电倍增管、光敏电阻、光敏三极管、太阳能电池、红外线传感器、紫外线传感器、光纤式光电传感器、色彩传感器、CCD和CMOS图像传感器等。光传感器是目前产量最多、应用最广的传感器之一,它在自动控制和非电量电测技术中占有非常重要的地位。光敏传感器是利用光敏元件将光信号转换为电信号的传感器,它的敏感波长在可见光波长附近,包括红外线波长和紫外线波长。光传感器不只局限于对光的探测,它还可以作为探测元件组成其他传感器,对许多非电量进行检测,只要将这些非电量转换为光信号的变化即可。
总结:照射光敏二极管的光强不同,通过光敏二极管的电流大小就不同,所以可以通过检测电流大小,达到检测光强的目的。利用这个电流变化,我们串接一个电阻,就可以转换成电压的变化,从而通过ADC读取电压值,判断外部光线的弱。
优点
- 采用灵敏型光敏电阻传感器
- 比较器输出,信号干净,波形好,驱动能力超过15mA。
- 配可调电位器可调节检测光线亮度
- 工作电压3.3V-5V
- 输出形式 :数字开关量输出(0和1)
- 设有固定螺栓孔,方便安装
原理图
5 部分核心代码
#include "lora.h"
#include "sys.h"
#include "delay.h"
#include "usart3.h"
#include "string.h"
#include "stdio.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
//设备参数初始化(具体设备参数见lora_cfg.h定义)
_LoRa_CFG LoRa_CFG=
{
.addr = LORA_ADDR, //设备地址
.power = LORA_POWER, //发射功率
.chn = LORA_CHN, //信道
.wlrate = LORA_RATE, //空中速率
.wltime = LORA_WLTIME, //睡眠时间
.mode = LORA_MODE, //工作模式
.mode_sta = LORA_STA, //发送状态
.bps = LORA_TTLBPS , //波特率设置
.parity = LORA_TTLPAR //校验位设置
};
//全局参数
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//设备工作模式(用于记录设备状态)
u8 Lora_mode=0;//0:配置模式 1:接收模式 2:发送模式
//记录中断状态
static u8 Int_mode=0;//0:关闭 1:上升沿 2:下降沿
//usmart支持部分
//将收到的AT指令应答数据返回给电脑串口
//mode:0,不清零USART3_RX_STA;
// 1,清零USART3_RX_STA;
void lora_at_response(u8 mode)
{
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
printf("%s",USART3_RX_BUF); //发送到串口
if(mode)USART3_RX_STA=0;
}
}
//lora发送命令后,检测接收到的应答
//str:期待的应答结果
//返回值:0,没有得到期待的应答结果
//其他,期待应答结果的位置(str的位置)
u8* lora_check_cmd(u8 *str)
{
char *strx=0;
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
strx=strstr((const char*)USART3_RX_BUF,(const char*)str);
}
return (u8*)strx;
}
//lora发送命令
//cmd:发送的命令字符串(不需要添加回车了),当cmd<0XFF的时候,发送数字(比如发送0X1A),大于的时候发送字符串.
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果)
// 1,发送失败
u8 lora_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
u8 res=0;
USART3_RX_STA=0;
if((u32)cmd<=0XFF)
{
while((USART3->SR&0X40)==0);//等待上一次数据发送完成
USART3->DR=(u32)cmd;
}else u3_printf("%s\r\n",cmd);//发送命令
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(10);
if(USART3_RX_STA&0X8000)//接收到期待的应答结果
{
if(lora_check_cmd(ack))break;//得到有效数据
USART3_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
//AUX中断设置
//mode:配置的模式 0:关闭 1:上升沿 2:下降沿
void Aux_Int(u8 mode)
{
if(!mode)
{
EXTI_InitStructure.EXTI_LineCmd = DISABLE;//关闭中断
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
}else
{
if(mode==1)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿
else if(mode==2)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
}
Int_mode = mode;//记录中断模式
EXTI_Init(&EXTI_InitStructure);
NVIC_Init(&NVIC_InitStructure);
}
//LoRa模块初始化
//返回值: 0,检测成功
// 1,检测失败
u8 LoRa_Configure(void)
{
u8 retry=0;
u8 temp=1;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG,从而PA15可以做普通IO使用,否则PA15不能做普通IO!!!
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //LORA_MD0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //LORA_AUX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.4
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource4);
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = DISABLE; //中断线关闭(先关闭后面再打开)
EXTI_Init(&EXTI_InitStructure);//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //LORA_AUX
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; //关闭外部中断通道(后面再打开)
NVIC_Init(&NVIC_InitStructure);
LORA_MD0=0;
LORA_AUX=0;
while(LORA_AUX)//确保LORA模块在空闲状态下(LORA_AUX=0)
{
delay_ms(600);
}
usart3_init(115200);//初始化串口3
LORA_MD0=1;//进入AT模式
delay_ms(40);
retry=3;
while(retry--)
{
if(!lora_send_cmd("AT","OK",70))
{
temp=0;//检测成功
break;
}
}
if(retry==0) temp=1;//检测失败
return temp;
}
void LoRa_Init(void)
{
while(LoRa_Configure())//初始化ATK-LORA-01模块
{
delay_ms(300);
}
LoRa_Set();
}
//Lora模块参数配置
void LoRa_Set(void)
{
u8 sendbuf[20];
u8 lora_addrh,lora_addrl=0;
usart3_set(LORA_TTLBPS_115200,LORA_TTLPAR_8N1);//进入配置模式前设置通信波特率和校验位(115200 8位数据 1位停止 无数据校验)
usart3_rx(1);//开启串口3接收
while(LORA_AUX);//等待模块空闲
LORA_MD0=1; //进入配置模式
delay_ms(40);
Lora_mode=0;//标记"配置模式"
lora_addrh = (LoRa_CFG.addr>>8)&0xff;
lora_addrl = LoRa_CFG.addr&0xff;
sprintf((char*)sendbuf,"AT+ADDR=%02x,%02x",lora_addrh,lora_addrl);//设置设备地址
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+WLRATE=%d,%d",LoRa_CFG.chn,LoRa_CFG.wlrate);//设置信道和空中速率
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+TPOWER=%d",LoRa_CFG.power);//设置发射功率
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+CWMODE=%d",LoRa_CFG.mode);//设置工作模式
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+TMODE=%d",LoRa_CFG.mode_sta);//设置发送状态
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+WLTIME=%d",LoRa_CFG.wltime);//设置睡眠时间
lora_send_cmd(sendbuf,"OK",50);
sprintf((char*)sendbuf,"AT+UART=%d,%d",LoRa_CFG.bps,LoRa_CFG.parity);//设置串口波特率、数据校验位
lora_send_cmd(sendbuf,"OK",50);
LORA_MD0=0;//退出配置,进入通信
delay_ms(40);
while(LORA_AUX);//判断是否空闲(模块会重新配置参数)
USART3_RX_STA=0;
Lora_mode=1;//标记"接收模式"
usart3_set(LoRa_CFG.bps,LoRa_CFG.parity);//返回通信,更新通信串口配置(波特率、数据校验位)
Aux_Int(1);//设置LORA_AUX上升沿中断
}
u8 Dire_Date[]={0x11,0x22,0x33,0x44,0x55};//定向传输数据
u8 date[30]={0};//定向数组
u32 obj_addr = 0;//记录用户输入目标地址
u8 obj_chn = 0;//记录用户输入目标信道
u8 wlcd_buff[10]={0}; //LCD显示字符串缓冲区
//Lora模块发送数据
void LoRa_SendData(u8 Data[])
{
static u8 num=0;
u16 addr;
u8 chn;
u16 i=0;
if(LoRa_CFG.mode_sta == LORA_STA_Tran)//透明传输
{
u3_printf("%s\r\n",Data);
num++;
if(num==255) num=0;
}else if(LoRa_CFG.mode_sta == LORA_STA_Dire)//定向传输
{
addr = (u16)obj_addr;//目标地址
chn = obj_chn;//目标信道
date[i++] =(addr>>8)&0xff;//高位地址
date[i++] = addr&0xff;//低位地址
date[i] = chn;//无线信道
for(i=0;i<Dire_DateLen;i++)//数据写到发送BUFF
{
date[3+i] = Dire_Date[i];
}
for(i=0;i<(Dire_DateLen+3);i++)
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET);//循环发送,直到发送完毕
USART_SendData(USART3,date[i]);
}
//将十六进制的数据转化为字符串打印在lcd_buff数组
sprintf((char*)wlcd_buff,"%x %x %x %x %x %x %x %x",
date[0],date[1],date[2],date[3],date[4],date[5],date[6],date[7]);
Dire_Date[4]++;//Dire_Date[4]数据更新
}
}
u8 rlcd_buff[10]={0}; //LCD显示字符串缓冲区
//Lora模块接收数据
u8* LoRa_ReceData(void)
{
u16 len=0;
//有数据来了
if(USART3_RX_STA&0x8000)
{
LED1 =0;
memset((char*)USART3_RX_BUF,0x00,len);//串口接收缓冲区清0
len = USART3_RX_STA&0X7FFF;
USART3_RX_BUF[len]=0;//添加结束符
USART3_RX_STA=0;
if(LoRa_CFG.mode_sta==LORA_STA_Tran)//透明传输
{
}else if(LoRa_CFG.mode_sta==LORA_STA_Dire)//定向传输
{
//将十六进制的数据转化为字符串打印在lcd_buff数组
sprintf((char*)rlcd_buff,"%x %x %x %x %x",
USART3_RX_BUF[0],USART3_RX_BUF[1],USART3_RX_BUF[2],USART3_RX_BUF[3],USART3_RX_BUF[4]);
}
}
return USART3_RX_BUF;
}
//LORA_AUX中断服务函数
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4))
{
if(Int_mode==1)//上升沿(发送:开始发送数据 接收:数据开始输出)
{
if(Lora_mode==1)//接收模式
{
USART3_RX_STA=0;//数据计数清0
}
Int_mode=2;//设置下降沿触发
LED0=0;//DS0亮
}
else if(Int_mode==2)//下降沿(发送:数据已发送完 接收:数据输出结束)
{
if(Lora_mode==1)//接收模式
{
USART3_RX_STA|=1<<15;//数据计数标记完成
}else if(Lora_mode==2)//发送模式(串口数据发送完毕)
{
Lora_mode=1;//进入接收模式
}
Int_mode=1;//设置上升沿触发
LED0=1;//DS0灭
}
Aux_Int(Int_mode);//重新设置中断边沿
EXTI_ClearITPendingBit(EXTI_Line4); //清除LINE4上的中断标志位
}
}
//DHT11温湿度传感器部分
#include "reg52.h"
#include "LCD1602.h"
#include "intrins.h"
//typedef unsigned char uchar;
//typedef unsigned int uint;
//定义变量
sbit Data=P3^6;
uchar rec_dat[13];//用于保存接收到的数据组
void DHT11_delay_us(uchar n)
{
while(--n);
}
void DHT11_delay_ms(uint z)
{
uint i,j;
for(i=z;i>0;i--)
for(j=110;j>0;j--);
}
void DHT11_start()
{
Data=1;
DHT11_delay_us(2);
Data=0;
DHT11_delay_ms(20); //延时18ms以上
Data=1;
DHT11_delay_us(30);
}
uchar DHT11_rec_byte() //接收一个字节
{
uchar i,dat=0;
for(i=0;i<8;i++) //从高到低依次接收8位数据
{
while(!Data); //等待50us低电平过去
DHT11_delay_us(8); //延时60us,如果还为高则数据为1,否则为0
dat<<=1; //移位使正确接收8位数据,数据为0时直接移位
if(Data==1) //数据为1时,使dat加1来接收数据1
dat+=1;
while(Data); //等待数据线拉低
}
return dat;
}
void DHT11_receive() //接收40位的数据
{
uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise;
DHT11_start();
if(Data==0)
{
while(Data==0); //等待拉高
DHT11_delay_us(40); //拉高后延时80us
R_H=DHT11_rec_byte(); //接收湿度高八位
R_L=DHT11_rec_byte(); //接收湿度低八位
T_H=DHT11_rec_byte(); //接收温度高八位
T_L=DHT11_rec_byte(); //接收温度低八位
revise=DHT11_rec_byte(); //接收校正位
DHT11_delay_us(25); //结束
if((R_H+R_L+T_H+T_L)==revise) //校正
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;
}
/*数据处理,方便显示*/
rec_dat[0]=RH/10+'0';
rec_dat[1]=(RH%10)+'0';
rec_dat[2]='%';
rec_dat[3]='R';
rec_dat[4]='H';
rec_dat[5]=' ';
rec_dat[6]=' ';
rec_dat[7]=(TH/10)+'0';
rec_dat[8]=(TH%10)+'0';
rec_dat[9]='^';
rec_dat[10]='C';
}
}
void main()
{
//使用lcd1602显示数据
DHT11_receive();
lcd1602(rec_dat);
}
🧿 毕设项目分享:见文末!