要求:
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报警器
实验效果如下图