1. 前言
Proteus是英國著名的EDA工具(仿真軟件),從原理圖布圖、代碼調(diào)試到單片機(jī)與外圍電路協(xié)同仿真,一 鍵切換到PCB設(shè)計(jì),真正實(shí)現(xiàn)了從概念到產(chǎn)品的完整設(shè)計(jì)。是世界上唯一將電路仿真軟件、PCB設(shè)計(jì)軟 件和虛擬模型仿真軟件三合一的設(shè)計(jì)平臺,其處理器模型支持8051、HC11、 PIC10/12/16/18/24/30/DSPIC33、AVR、ARM、8086和MSP430等,2010年又增加了Cortex和DSP系 列處理器,并持續(xù)增加其他系列處理器模型。在編譯方面,它也支持IAR、Keil和MATLAB等多種編譯 器。 前面文章介紹了Proteus的下載,安裝,建立工程,完成LED燈仿真運(yùn)行。這篇文章在這基礎(chǔ)上增加串口打印,DHT11溫濕度檢測。
2. 設(shè)計(jì)程序
先使用keil軟件就將程序設(shè)計(jì)設(shè)計(jì)好,然后生成HEX文件,等待設(shè)計(jì)好原理圖后進(jìn)行仿真測試。
注意: 當(dāng)前使用的芯片是STM32F103。Proteus的版本是8.9
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "dht11.h"
?
/*
(3)溫濕度傳感器: DHT11
VCC--VCC
GND---GND
DAT---PA5
*/
?
#include "stm32f10x.h"
#include
#include
#include "sys.h"
#include
?
#define USART1_RX_LENGTH 1024
extern u8 USART1_RX_BUFFER[USART1_RX_LENGTH]; //保存接收數(shù)據(jù)的緩沖區(qū)
extern u32 USART1_RX_CNT; //當(dāng)前接收到的數(shù)據(jù)長度
extern u8 USART1_RX_FLAG; //1表示數(shù)據(jù)接收完畢 0表示沒有接收完畢
?
#define USART2_RX_LENGTH 1024
extern u8 USART2_RX_BUFFER[USART2_RX_LENGTH]; //保存接收數(shù)據(jù)的緩沖區(qū)
extern u32 USART2_RX_CNT; //當(dāng)前接收到的數(shù)據(jù)長度
extern u8 USART2_RX_FLAG; //1表示數(shù)據(jù)接收完畢 0表示沒有接收完畢
?
#define USART3_RX_LENGTH 1024
extern u8 USART3_RX_BUFFER[USART3_RX_LENGTH]; //保存接收數(shù)據(jù)的緩沖區(qū)
extern u32 USART3_RX_CNT; //當(dāng)前接收到的數(shù)據(jù)長度
extern u8 USART3_RX_FLAG; //1表示數(shù)據(jù)接收完畢 0表示沒有接收完畢
?
void USART1_Init(u32 baud);
void USART2_Init(u32 baud);
void USART3_Init(u32 baud);
void USARTx_StringSend(USART_TypeDef *USARTx,char *str);
void USARTx_DataSend(USART_TypeDef *USARTx,u8 *data,u32 len);
?
//定義按鍵IO口
#define KEY_S3 PAin(1)
?
//函數(shù)聲明
void KEY_Init(void);
u8 KEY_Scan(u8 mode);
?
?
//LED定義
#define LED1 PBout(6)
#define LED2 PBout(7)
#define LED3 PBout(8)
#define LED4 PBout(9)
?
//蜂鳴器IO口定義
#define BEEP PAout(6)
?
//函數(shù)聲明
void LED_Init(void);
void BEEP_Init(void);
?
?
?
//IO方向設(shè)置
#define DHT11_IO_IN() {GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=0x00800000;}
#define DHT11_IO_OUT() {GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=0x00300000;}
////IO操作函數(shù)
#define DHT11_DQ_OUT PAout(5) //數(shù)據(jù)端口 PA5
#define DHT11_DQ_IN PAin(5) //數(shù)據(jù)端口 PA5
?
?
u8 DHT11_Init(void); //初始化DHT11
u8 DHT11_Read_Data(u8 *temp,u8 *humi);//讀取溫濕度
u8 DHT11_Read_Byte(void); //讀出一個字節(jié)
u8 DHT11_Read_Bit(void); //讀出一個位
u8 DHT11_Check(void); //檢測是否存在DHT11
void DHT11_Rst(void); //復(fù)位DHT11
?
//復(fù)位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //SET OUTPUT
DHT11_DQ_OUT=0; //拉低DQ
DelayMs(20); //拉低至少18ms
DHT11_DQ_OUT=1; //DQ=1
DelayUs(30); //主機(jī)拉高20~40us
}
//等待DHT11的回應(yīng)
//返回1:未檢測到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)
{
u8 retry=0;
DHT11_IO_IN();//SET INPUT
while (DHT11_DQ_IN&&retry<100)//DHT11會拉低40~80us
? {
? retry++;
? DelayUs(1);
? };
? if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后會再次拉高40~80us
? {
? retry++;
? DelayUs(1);
? };
? if(retry>=100)return 1;
return 0;
}
//從DHT11讀取一個位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN&&retry<100)//等待變?yōu)榈碗娖?? {
? retry++;
? DelayUs(1);
? }
? retry=0;
? while(!DHT11_DQ_IN&&retry<100)//等待變高電平
? {
? retry++;
? DelayUs(1);
? }
? DelayUs(40);//等待40us
? if(DHT11_DQ_IN)return 1;
? else return 0; ?
?}
??
?//從DHT11讀取一個字節(jié)
?//返回值:讀到的數(shù)據(jù)
?u8 DHT11_Read_Byte(void) ? ?
?{ ? ? ? ?
? ? ?u8 i,dat;
? ? ?dat=0;
? for (i=0;i<8;i++)
? {
? ? dat<<=1;
? ? ?dat|=DHT11_Read_Bit();
? ? } ? ?
? ? ?return dat;
?}
??
??
?//從DHT11讀取一次數(shù)據(jù)
?//temp:溫度值(范圍:0~50°)
?//humi:濕度值(范圍:20%~90%)
?//返回值:0,正常;1,讀取失敗
?u8 DHT11_Read_Data(u8 *temp,u8 *humi) ? ?
?{ ? ? ? ?
? u8 buf[5];
? u8 i;
? DHT11_Rst();
? //printf("------------------------rn");
? if(DHT11_Check()==0)
? {
? for(i=0;i<5;i++)//讀取40位數(shù)據(jù)
? {
? buf[i]=DHT11_Read_Byte();
? }
? if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
? {
? *humi=buf[0];
? *temp=buf[2];
? }
? }else return 1;
? return 0; ? ?
?}
??
??
?//初始化DHT11的IO口 DQ 同時檢測DHT11的存在
?//返回1:不存在
?//返回0:存在 ?
?u8 DHT11_Init(void)
?{
? RCC->APB2ENR|=1<<2; ? ?//使能PORTA口時鐘
? GPIOA->CRL&=0XFF0FFFFF;//PORTA.5 推挽輸出
GPIOA->CRL|=0X00300000;
GPIOA->ODR|=1<<5; ? ? ?//輸出1 ? ?
? DHT11_Rst();
? return DHT11_Check();
?}
??
??
?/*
?函數(shù)功能:按鍵初始化
?硬件連接:PA1
?特性: 按下為低電平---沒按下高電平
?*/
?void KEY_Init(void)
?{
? ? ?//開時鐘
? ? ?RCC->APB2ENR|=1<<2;
? ? ?//配置模式
? ? ?GPIOA->CRL&=0xFFFFFF0F;
GPIOA->CRL|=0x00000080;
//上拉
GPIOA->ODR|=1<<1;
?}
??
??
?/*
?函數(shù)功能:函數(shù)掃描函數(shù)
?函數(shù)參數(shù): mode=1表示使用連續(xù)模式 mode=0使用單擊模式
?返回值: 2 3 4 5 表示具體的按鈕 ? 0表示沒有按下
?*/
?u8 KEY_Scan(u8 mode)
?{
? ? static u8 flag=1; //記錄上一次按下的狀態(tài)
? ? if(mode)flag=1;
? ? if(flag&&(KEY_S3==0))
? ? {
? ? ? ? flag=0;
? ? ? ? delay_ms(20);
? ? ? ? if(KEY_S3==0)return 3;
? ? }
? ? else if(KEY_S3)
? ? {
? ? ? ? flag=1;
? ? }
? ? return 0;
?}
??
??
?/*
?函數(shù)功能: LED初始化
?硬件連接: PB6 PB7 PB8 PB9
?特性: 低電平點(diǎn)亮
?*/
?void LED_Init(void)
?{
? ? ?//開時鐘
? ? ?RCC->APB2ENR|=1<<3;
? ? ?//配置GPIO口
? ? ?GPIOB->CRL&=0x00FFFFFF;
GPIOB->CRL|=0x22000000;
GPIOB->CRH&=0xFFFFFF00;
GPIOB->CRH|=0x00000022;
//上拉
GPIOB->ODR|=1<<6;
? ? ?GPIOB->ODR|=1<<7;
? ? ?GPIOB->ODR|=1<<8;
? ? ?GPIOB->ODR|=1<<9;
?}
??
?/*
?函數(shù)功能: 蜂鳴器初始化
?硬件連接: PA6
?特性: 高電平響
?*/
?void BEEP_Init(void)
?{
? ? RCC->APB2ENR|=1<<2;
? ? GPIOA->CRL&=0xF0FFFFFF;
GPIOA->CRL|=0x02000000;
}
?
?
/*
函數(shù)功能: 串口1的初始化
硬件連接: PA9(TX) 和 PA10(RX)
*/
void USART1_Init(u32 baud)
{
/*1. 開時鐘*/
RCC->APB2ENR|=1<<14; //USART1時鐘
? ? ?RCC->APB2ENR|=1<<2; ?//PA
? ? ?RCC->APB2RSTR|=1<<14; //開啟復(fù)位時鐘
? ? ?RCC->APB2RSTR&=~(1<<14);//停止復(fù)位
? ? ?/*2. 配置GPIO口的模式*/
? ? ?GPIOA->CRH&=0xFFFFF00F;
GPIOA->CRH|=0x000008B0;
/*3. 配置波特率*/
USART1->BRR=72000000/baud;
/*4. 配置核心寄存器*/
USART1->CR1|=1<<5; //開啟接收中斷
? ? ?STM32_SetPriority(USART1_IRQn,1,1); //設(shè)置中斷優(yōu)先級
? ? ?USART1->CR1|=1<<2; //開啟接收
? ? ?USART1->CR1|=1<<3; //開啟發(fā)送
? ? ?USART1->CR1|=1<<13;//開啟串口功能
?}
??
?/*
?函數(shù)功能: 串口2的初始化
?硬件連接: PA2(TX) 和 PA3(RX)
?*/
?void USART2_Init(u32 baud)
?{
? ? ?/*1. 開時鐘*/
? ? ?RCC->APB1ENR|=1<<17; //USART2時鐘
? ? ?RCC->APB2ENR|=1<<2; ?//PA
? ? ?RCC->APB1RSTR|=1<<17; //開啟復(fù)位時鐘
? ? ?RCC->APB1RSTR&=~(1<<17);//停止復(fù)位
? ? ?
? ? ?/*2. 配置GPIO口的模式*/
? ? ?GPIOA->CRL&=0xFFFF00FF;
GPIOA->CRL|=0x00008B00;
/*3. 配置波特率*/
USART2->BRR=36000000/baud;
/*4. 配置核心寄存器*/
USART2->CR1|=1<<5; //開啟接收中斷
? ? ?STM32_SetPriority(USART2_IRQn,1,1); //設(shè)置中斷優(yōu)先級
? ? ?USART2->CR1|=1<<2; //開啟接收
? ? ?USART2->CR1|=1<<3; //開啟發(fā)送
? ? ?USART2->CR1|=1<<13;//開啟串口功能
?}
??
?/*
?函數(shù)功能: 串口3的初始化
?硬件連接: PB10(TX) 和 PB11(RX)
?*/
?void USART3_Init(u32 baud)
?{
? ? ?/*1. 開時鐘*/
? ? ?RCC->APB1ENR|=1<<18; //USART3時鐘
? ? ?RCC->APB2ENR|=1<<3; ?//PB
? ? ?RCC->APB1RSTR|=1<<18; //開啟復(fù)位時鐘
? ? ?RCC->APB1RSTR&=~(1<<18);//停止復(fù)位
? ? ?
? ? ?/*2. 配置GPIO口的模式*/
? ? ?GPIOB->CRH&=0xFFFF00FF;
GPIOB->CRH|=0x00008B00;
/*3. 配置波特率*/
USART3->BRR=36000000/baud;
/*4. 配置核心寄存器*/
USART3->CR1|=1<<5; //開啟接收中斷
? ? ?STM32_SetPriority(USART3_IRQn,1,1); //設(shè)置中斷優(yōu)先級
? ? ?USART3->CR1|=1<<2; //開啟接收
? ? ?USART3->CR1|=1<<3; //開啟發(fā)送
? ? ?USART3->CR1|=1<<13;//開啟串口功能
?}
??
?u8 USART1_RX_BUFFER[USART1_RX_LENGTH]; //保存接收數(shù)據(jù)的緩沖區(qū)
?u32 USART1_RX_CNT=0; ?//當(dāng)前接收到的數(shù)據(jù)長度
?u8 USART1_RX_FLAG=0; //1表示數(shù)據(jù)接收完畢 0表示沒有接收完畢
??
?//串口1的中斷服務(wù)函數(shù)
?void USART1_IRQHandler(void)
?{
? ? ?u8 data;
? ? ?//接收中斷
? ? ?if(USART1->SR&1<<5)
? ? {
? ? ? ? ?TIM1->CNT=0; //清除計(jì)數(shù)器
TIM1->CR1|=1<<0; //開啟定時器1
? ? ? ? ?data=USART1->DR; //讀取串口數(shù)據(jù)
// if(USART1_RX_FLAG==0) //判斷上一次的數(shù)據(jù)是否已經(jīng)處理完畢
{
//判斷是否可以繼續(xù)接收
if(USART1_RX_CNTSR&1<<5)
? ? {
? ? ? ? ?TIM2->CNT=0; //清除計(jì)數(shù)器
TIM2->CR1|=1<<0; //開啟定時器2
? ? ? ? ?data=USART2->DR; //讀取串口數(shù)據(jù)
// if(USART2_RX_FLAG==0) //判斷上一次的數(shù)據(jù)是否已經(jīng)處理完畢
{
//判斷是否可以繼續(xù)接收
if(USART2_RX_CNTSR&1<<5)
? ? {
? ? ? ? ?TIM3->CNT=0; //清除計(jì)數(shù)器
TIM3->CR1|=1<<0; //開啟定時器3
? ? ? ? ?data=USART3->DR; //讀取串口數(shù)據(jù)
// if(USART3_RX_FLAG==0) //判斷上一次的數(shù)據(jù)是否已經(jīng)處理完畢
{
//判斷是否可以繼續(xù)接收
if(USART3_RX_CNTDR=*str++;
while(!(USARTx->SR&1<<7)){}
? ? }
?}
??
?/*
?函數(shù)功能: 數(shù)據(jù)發(fā)送
?*/
?void USARTx_DataSend(USART_TypeDef *USARTx,u8 *data,u32 len)
?{
? ? u32 i;
? ? for(i=0;iDR=*data++;
? ? ? ? while(!(USARTx->SR&1<<7)){}
? ? }
?}
??
?//printf函數(shù)底層函數(shù)接口
?int fputc(int c, FILE* stream)
?{
? ? ?USART1->DR=c;
while(!(USART1->SR&1<<7)){}
? ? ?return c;
?}
??
??
?u8 dht11_temp;
?u8 dht11_humidity;
??
?int main()
?{
? ? u8 key_val;
? ? u32 time=0;
? ? LED_Init();
? ? BEEP_Init();
? ? KEY_Init();
? ? USART1_Init(115200); ? ?//串口1初始化-打印調(diào)試信息
? ? //初始化DHT11
? ? DHT11_Init();
? ? ?
? ? while(1)
? ? {
? ? ? ?key_val=KEY_Scan(0); //PA1
? ? ? ?if(key_val)
? ? ? {
? ? ? ? ? BEEP=!BEEP;
? ? ? ? ? LED1=!LED1; ? //PB6
? ? ? }
? ? ? ?delay_ms(5);
? ? ? ?
? ? ? ?time++;
? ? ? ?if(time>=10)
{
time=0;
LED2=!LED2; //PB7
//讀取溫濕度
if(DHT11_Read_Data(&dht11_temp,&dht11_humidity))
{
printf("溫度讀取失敗.rn");
}
printf("T:%d,H:%drn",dht11_temp,dht11_humidity);
//濕度大于80以上就關(guān)閉插座
if(dht11_humidity>80)
{
LED1=1;
}
}
}
}
復(fù)制代碼
3. 設(shè)計(jì)電路圖
3.1 添加DHT11器件
打開Proteus,搜索DHT11元器件。
鼠標(biāo)選擇空白區(qū)域,點(diǎn)擊鼠標(biāo)右鍵,放置電源和GND。
設(shè)計(jì)好的效果如下:
3.2 添加虛擬串口終端
為了方便查看程序的串口輸出,添加一個串口終端顯示框。
在虛擬儀表模式下,選擇virtual terminal
工具,然后在原理圖空白區(qū)域點(diǎn)擊一下就可以放virtual terminal
工具。
在繪制原理圖的經(jīng)常遇到連線復(fù)雜,或者布線很亂,如果元器件的引腳不方便直接與MCU單片機(jī)連接,可以采用標(biāo)簽的形式或者總線方式布線。這里以串口終端演示,采用標(biāo)簽方式連接IO口。
首先在坐標(biāo)的菜單欄里選擇終端模式,然后鼠標(biāo)點(diǎn)擊DEFAULT
,然后在原理圖的空白區(qū)域,點(diǎn)擊一下鼠標(biāo)左鍵,會出現(xiàn)一個空心的連接線條,將這個連接線條連接到元器件的IO口上就行。
放置好之后,鼠標(biāo)點(diǎn)擊這個接線端子--空心圓圈,彈出對話框,設(shè)置連接的IO口。
然后MCU的PA9和PA10的端子上也設(shè)置好標(biāo)簽名稱。
設(shè)置虛擬串口顯示器的波特率為:115200
如果在調(diào)試仿真時, Virtual Terminal無法自動彈出窗口,可以點(diǎn)擊菜單欄的調(diào)試,選擇恢復(fù)彈出窗口。
設(shè)置STM32芯片的晶振為:71MHZ
3.3 開始仿真
-
Proteus
+關(guān)注
關(guān)注
79文章
1692瀏覽量
106295 -
STM32
+關(guān)注
關(guān)注
2264文章
10854瀏覽量
354314 -
DHT11
+關(guān)注
關(guān)注
19文章
276瀏覽量
57517
發(fā)布評論請先 登錄
相關(guān)推薦
評論