要求:
1.使用stm32做主控,芯片自選
2.使用LCD1602顯示PM2.5的上下限值,並顯示PM2.5的當前值
3.使用按鍵調節上下限的值
4.使用滑動變阻器代替PM2.5吸合傳感器
5.使用ADC0832,測量滑動變阻器的電壓代表PM2.5的當前值
6.若PM2.5的值低於下限報警(蜂鳴器)高於上限報警並繼電器吸合
開發工具
- 標準庫
- Keil
- Protues
Protues&&電路圖
Protues這個軟件好久沒用了,很多都忘記了,最近下載了一個8.9版本的,真的挺好看的。
下面說一下,我使用的時候遇到的小問題:
-
使用STM32,首先就是要畫最小系統:晶振電路,復位電路。但是好奇怪,我在芯片上居然看不到電源,後來發現,在工具欄Design裏面有一個configure Power Rails裏面就可以設置了
-
點一盞LED燈我點了一個小時還沒點亮,這是最基本的東西,應該不會錯纔對。
結果發現,LED燈要設置一下不然真的點不亮。雙擊LED燈會出現如下畫面,圈住的那裏,如果是I/O口輸入高低電平去控制LED的亮滅就一定要選擇digital,如果使用繼電器去控制的話就改爲analog
-
後面也沒有遇到什麼問題了,下面是我這個小作品的電路圖
程序
- LCD1602
LCD1602在學51單片機的時候經常使用,但是用在STM32上和51上還是有點小區別,51的時候可以整個P0/P1/P2口一起操作,可是32我不會整個PA/PB…一起操作所以命令只能單個I/O操作,不過也還好不會太麻煩。
顯示字符的話就去查看ACSII碼,然後操作對應的I/O口即可
以下是部分代碼:(太多了不好全部顯示)
/*---------------------------------------LCD引腳初始化-----------------------------------*/
void LCD_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(LCD_CLOCK,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin=LCD_D0_PIN|LCD_D1_PIN|LCD_D2_PIN|LCD_D3_PIN|
LCD_D4_PIN|LCD_D5_PIN|LCD_D6_PIN|LCD_D7_PIN|
LCD_E_PIN|LCD_RW_PIN|LCD_RS_PIN;
GPIO_Init(LCD_GPIO_PORT,&GPIO_InitStruct);
}
/*-----------------------------------清屏命令------------------------------------------*/
void LcdCom_0x01(void)
{
GPIO_ResetBits(LCD_GPIO_PORT,LCD_RS_PIN);//RS = 0; 選擇發送命令
GPIO_ResetBits(LCD_GPIO_PORT,LCD_RW_PIN);//RW = 0; 選擇寫入
GPIO_SetBits(LCD_GPIO_PORT,LCD_D0_PIN);
GPIO_ResetBits(LCD_GPIO_PORT,LCD_D1_PIN|LCD_D2_PIN|LCD_D3_PIN|LCD_D4_PIN|LCD_ D5_PIN|LCD_D6_PIN|LCD_D7_PIN);
Lcd1602_Delay1ms(5); //等待數據穩定
GPIO_SetBits(LCD_GPIO_PORT,LCD_E_PIN);//E = 1;寫入時序
Lcd1602_Delay1ms(25); //保持時間
GPIO_ResetBits(LCD_GPIO_PORT,LCD_E_PIN);//E = 0;關閉使能
}
/*------------------------------字符A---------------------------------*/
void LcdData_A(void)
{
GPIO_SetBits(LCD_GPIO_PORT,LCD_RS_PIN);//RS = 1; 選擇輸入數據
GPIO_ResetBits(LCD_GPIO_PORT,LCD_RW_PIN);//RW = 0; 選擇寫入
GPIO_SetBits(LCD_GPIO_PORT,LCD_D0_PIN|LCD_D6_PIN);//寫入數據
GPIO_ResetBits(LCD_GPIO_PORT,LCD_D4_PIN|LCD_D1_PIN|LCD_D2_PIN|
LCD_D3_PIN|LCD_D5_PIN|LCD_D7_PIN);
Lcd1602_Delay1ms(5);
GPIO_SetBits (LCD_GPIO_PORT,LCD_E_PIN);//E = 1; //寫入時序
Lcd1602_Delay1ms(25); //保持時間
GPIO_ResetBits(LCD_GPIO_PORT,LCD_E_PIN);//E = 0;
}
/*-----------------------------初始化LCD---------------------*/
void LCD_Init(void) //LCD初始化子程序
{
LcdCom_0x38(); //開顯示
Lcd1602_Delay1ms(25);
LcdCom_0x38();
Lcd1602_Delay1ms(25);
LcdCom_0x38();
Lcd1602_Delay1ms(25);
LcdCom_0x0C();//LcdWriteCom(0x0c); //開顯示不顯示光標
LcdCom_0x06();//LcdWriteCom(0x06); //寫一個指針加1
LcdCom_0x01();//LcdWriteCom(0x01); //清屏
Lcd1602_Delay1ms(25);
LcdCom_0x0C();
}
- ADC0832
這塊芯片我也第一次用,很多人在51上使用這塊芯片,他們都是把DO和DI接在一起,一開始我打算移植一下別人的51的代碼就應該可以用,是因爲我太懶,後來發現移植了也不可以用,又不知道有什麼問題,後來還是自己寫了。時許這種東西還是相信自己吧。 - 51 可以把DO/DI接在一起,共用一個I/O口,可是stm32不可以,因爲stm32初始化引腳的時候要設置模式 DI是輸出模式,DO是輸入模式
ADC0832的時序:
1.CS:片選,通信過程中全程保持低電平
2.CLK第一個脈衝下降前DI必須爲低電平,表示起始信號
3.第二第三個脈衝下降前DI應輸入兩位數據用於選擇通道,CH0=10’b CH1=11’b
4.第三個脈衝下降後DI無效,此後利用DO進行數據轉換讀取
5.第四個脈衝下降後,開始又DO端進行數據轉換最高位開始讀,隨後,每一個脈衝的下降沿,DO都會輸出下一位數據,一共八位數據。
6.最後傳輸完成後一定要把CS拉高,不然你讀完的數據,就會出現有數據變動然後數據變爲0,再也不能測數據。
代碼如下:
#ifndef __ADC0832__H
#define __ADC0832__H
#include "stm32f10x.h"
#define AD_CLOCK RCC_APB2Periph_GPIOB
#define AD_GPIO_PORT GPIOB
#define AD_CS_PIN GPIO_Pin_0
#define AD_CLK_PIN GPIO_Pin_1
#define AD_DO_PIN GPIO_Pin_3
#define AD_DI_PIN GPIO_Pin_2
#define ADDO_READ() GPIO_ReadInputDataBit(AD_GPIO_PORT,AD_DO_PIN)
#define ADCLK0 GPIO_ResetBits(AD_GPIO_PORT,AD_CLK_PIN)
#define ADCLK1 GPIO_SetBits(AD_GPIO_PORT,AD_CLK_PIN)
#define ADCS0 GPIO_ResetBits(AD_GPIO_PORT,AD_CS_PIN)
#define ADCS1 GPIO_SetBits(AD_GPIO_PORT,AD_CS_PIN)
#define ADDO0 GPIO_ResetBits(AD_GPIO_PORT,AD_DO_PIN)
#define ADDO1 GPIO_SetBits(AD_GPIO_PORT,AD_DO_PIN)
#define ADDI0 GPIO_ResetBits(AD_GPIO_PORT,AD_DI_PIN)
#define ADDI1 GPIO_SetBits(AD_GPIO_PORT,AD_DI_PIN)
void DELAY_Us(void);
void ADC0832_Config(void);
void ADC0832_start(void);
uint8_t ADC0832_read(void);
#endif
/*-----------------------------ADC0832引腳初始化---------------------------------*/
void ADC0832_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(AD_CLOCK,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin=AD_CS_PIN|AD_CLK_PIN|AD_DI_PIN;
GPIO_Init(AD_GPIO_PORT,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin=AD_DO_PIN;
GPIO_Init(AD_GPIO_PORT,&GPIO_InitStruct);
}
/*--------------------------初始化ADC0832---------------------------*/
void ADC0832_start(void)
{
ADCS0;//片選信號置零,芯片使能
ADDO0;//ADDO爲高阻態
DELAY_Us();
DELAY_Us();
ADCLK0;
DELAY_Us();
DELAY_Us();
ADDI1;//第一個脈衝下降前,DI必須爲1
/************起始信號*************/
ADCLK1;//第一個脈衝上升沿
DELAY_Us();
DELAY_Us();
ADCLK0;//第一個脈衝下降沿
DELAY_Us();
DELAY_Us();
/**********通道選擇**************/
ADDI1;
ADCLK1;//第二個脈衝上升沿
DELAY_Us();
DELAY_Us();
ADCLK0;//第二個脈衝下降沿
DELAY_Us();
DELAY_Us();
ADDI0;//選擇通道0
ADCLK1;//第三個脈衝上升沿
DELAY_Us();
DELAY_Us();
ADCLK0;//第三個脈衝下降沿
DELAY_Us();
DELAY_Us();
}
/*-----------------------啓動ADC0832進行轉換-------------------------------*/
uint8_t ADC0832_read(void)
{
uint8_t temp=0;
uint16_t i;
ADC0832_start();
ADDI0;//DI轉換成高阻態
ADDO1;//使DO脫離高阻態
/*通道選擇結束開始讀取轉換後的二進制數*/
ADCLK1;//第四個脈衝上升沿
DELAY_Us();
ADCLK0;//ADCLK=0;//第四個脈衝下降沿,開始讀數,一下進行判斷和處理,共8次
DELAY_Us();
for(i=0;i<8;i++)
{
ADCLK1;
DELAY_Us();
ADCLK0;
if(ADDO_READ()==1)
{
temp |=0x01;
}
else
{
temp &=0xfe;
}
temp=temp<<1;
DELAY_Us();
}
ADCS1;
ADC_date=temp;
return ADC_date;
}
- 其他的就是普通的控制I/O 沒什麼特別,這樣就完成了,PM2.5報警器
實驗效果如下圖