Proteus仿真基于stm32的PM2.5报警器

要求:

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报警器

实验效果如下图
在这里插入图片描述

发布了19 篇原创文章 · 获赞 22 · 访问量 6089

猜你喜欢

转载自blog.csdn.net/qq_44622367/article/details/105192237