【嵌入式学习记录_M4项目】模拟毒气检测系统

前  言

        这是我在嵌入式学习的第二阶段——M4理论的学习过程中完成的一个练习,如果对您有帮助的话,可以点赞,收藏,关注哦,有其他问题可以私聊交流。

        项目还有需要改进优化的地方,也可以添加其他有趣的功能,期待各位大佬的指点和交流。

一、项目简介 

项目名:模拟毒气检测系统(STM32F407)

功    能: 1.光敏电阻每100ms检测光照强度调节LED的亮度(光照强度越强,灯的亮度越亮)

                2.毒气检测每100ms检测毒气浓度,超过20%浓度,通风(电机)启动同时关闭阀门(舵机转180度), 毒气越浓电机越快,(电机用LED4灯的亮度代替)

                3.每100ms通过串口显示芯片内部温度值

                4.在毒气泄露的情况下,100cm内不能有人靠近,如果低于100cm,蜂鸣器报警

                5.通过按键控制RGB彩灯,KEY1控制红灯亮,KEY2控制绿灯亮,KEY3控制蓝灯亮,KEY4控制彩灯随机变色

                6.PC端发送open/close给串口,控制阀门开关

说    明:在学习完ADC,定时器等原理和使用后,我便接触到了这个综合练习,这个项目主要是用于检测毒气浓度,并且附加了一些额外的功能。其中,大部分功能是在之前的的学习中已经完成了的,例如按键扫描函数,串口中断以及各种初始化配置函数。所以,这次项目主要是写ADC和定时器的一些函数,但在文章中我会发整个代码及思路。

二、功能介绍  

1.光敏电阻每100ms检测光照强度调节LED的亮度(光照强度越强,灯的亮度越亮)

        我通过ADC控制器将光照传感器的模拟量改为数字量,然后用输出比较根据这个数字量改变定时器的比较寄存器的值,从而控制LED灯的亮度。同时通过定时中断完成每100ms转化一次。

2.毒气检测每100ms检测毒气浓度,超过20%浓度,通风(电机)启动同时关闭阀门(舵机转180度), 毒气越浓电机越快,(电机用LED4灯的亮度代替)

        我通过ADC控制器将毒气传感器的模拟量改为数字量,当这个数字量大于20%时,阀门(舵机)对应定时器通道的比较寄存器会赋值2500(舵机旋转角度对应比较寄存器的值    0°---500    180°---2500),阀门关闭(舵机旋转180°),电机(LED4)根据数字量改变TIM8-CH2的比较寄存器的值,从而改变转速(LED4亮度)。同时通过定时中断完成每100ms转化一次。

tips:因为电机模块坏了,所以用LED4的亮度代表电机的转速

3.每100ms通过串口显示芯片内部温度值

        通过定时中断完成每100ms转化一次。ADC将温度传感器的模拟量转化为数字量,通过公式计算温度值,并用printf函数将温度值在串口软件打印。

4.在毒气泄露的情况下,100cm内不能有人靠近,如果低于100cm,蜂鸣器报警

        我通过用超声波模块和定时器的输入捕获测距,当毒气浓度大于20%且100cm内有人时,蜂鸣器会报警,若100cm内没人,则蜂鸣器关闭。

5.通过按键控制RGB彩灯,KEY1控制红灯亮,KEY2控制绿灯亮,KEY3控制蓝灯亮,KEY4控制彩灯随机变色

        通过一个定时器的三个通道控制RGB彩灯,按键按下后通过改变对应通道的比较寄存器的值控制不同的灯。

  6.PC端发送open/close给串口,控制阀门开关

        当在PC端通过串口发送字符串时,会进入串口中断服务函数,在函数中与open/close比较,如果比较结果为相同,则会打开/关闭阀门。

三、程序框架

根据功能需求,我们需要先完成一些简单的初始化函数:

        我们需要先配置串口初始化,蜂鸣器初始化,定时器3通道1输出PWM控制LED3初始化,定时器8通道2输出PWM控制电机(LED4)初始化,RGB初始化,舵机初始化,超声波初始化,按键初始化,ADC初始化。接着配置另一个定时器用作定时中断。然后将每个功能的事件写入对应的中断服务函数中。

主函数框架

int main(void)
{
    //串口初始化
    //蜂鸣器初始化
    //定时器3通道1输出PWM控制LED3
    //定时器8通道2输出PWM控制电机(LED4)
    //舵机初始化
    //超声波初始化
    //RGB初始化
    //按键初始化
    //ADC初始化
    while(1)
    {
        //RGB彩灯控制
    }
    
}

四、代码设计

1.串口初始化

/**************************************************************************
* 函数名    :Usart1_Init
* 函数功能  :对串口1初始化配置
* 函数参数  :U32 bps
* 函数返回值:void
* 函数说明  :
*							PA9  ---- USART1_Tx
*							PA10 ---- USART1_Rx
*************************************************************************/
void Usart1_Init(u32 bps)
{
	/*IO口控制寄存器*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 0);
	//端口模式寄存器---复用模式
	GPIOA->MODER &= ~((3 << 18) | (3 << 20));
	GPIOA->MODER |= ((2 << 18) | (2 << 20));
	//端口输出类型寄存器---推挽
	GPIOA->OTYPER &= ~(1 << 9);
	//端口输出速度寄存器---2MHZ
	GPIOA->OSPEEDR &= ~(3 << 18);
	//上拉/下拉寄存器---浮空
	GPIOA->PUPDR &= ~((3 << 18) |(3 << 20));
	//复用功能寄存器
	GPIOA->AFR[1] &= ~((0xf << 4) | (0xf << 8));
	GPIOA->AFR[1] |= ((7 << 4) | (7 << 8));
	
	/*串口控制寄存器*/
	//串口时钟使能---usart1
	RCC->APB2ENR |= (1 << 4);
	//BRR
	USART1->BRR = 84000000 / bps;
	//CR1
	USART1->CR1 &= ~(1 << 15);
	USART1->CR1 &= ~(1 << 12);
	USART1->CR1 |= (1 << 3);
	USART1->CR1 |= (1 << 2);
	//CR2
	USART1->CR2 &= ~(3 << 12);
	
	//优先级分组 --- 主函数
	//优先级编码值
	u32 pri = NVIC_EncodePriority (5,1,2);
	//确定具体中断源
	NVIC_SetPriority(USART1_IRQn,pri);
	//使能NAIC响应通道
	NVIC_EnableIRQ(USART1_IRQn);
	
	//接收中断使能
	USART1->CR1 |= (1 << 5);
	//空闲中断使能
	USART1->CR1 |= (1 << 4);
	//USART1使能
	USART1->CR1 |= (1 << 13);
}

2.蜂鸣器初始化

/**************************************************************************
* 函数名    :Beep_Init
* 函数功能  :对蜂鸣器初始化配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*			BEEP --- PE0 --- 通用输出,输出推挽,速度为2MHZ,无上拉/下拉
*************************************************************************/
void Beep_Init(void)
{
	//打开GPIOE时钟
	RCC->AHB1ENR |= (1 << 4);
	
	//配置PE0
	GPIOE->MODER &= ~(3 << 0);
	GPIOE->MODER |= (1 << 0);
	GPIOE->OTYPER &= ~(1 << 0);
	GPIOE->OSPEEDR &= ~(3 << 0);
	GPIOE->PUPDR &= ~(3 << 0);
	
	//关闭蜂鸣器
	BEEP_OFF;
}

3.定时器3通道1输出PWM控制LED3初始化

/***********************************************
*函数名    :Tim3_ch1_pwm_Init
*函数功能  :定时器3通道1输出PWM控制LED3
*函数参数  :无
*函数返回值:无
*函数描述  :84M  84分频		1/us  T:1ms
*						LED3 --- PC6 --- TIM3_CH1
************************************************/
void Tim3_ch1_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 2);
	//端口模式寄存器 --- 复用
	GPIOC->MODER &= ~(3 << 12);
	GPIOC->MODER |= (2 << 12);
	//端口输出类型寄存器
	GPIOC->OTYPER &= ~(1 << 6);
	//端口输出速度寄存器
	GPIOC->OSPEEDR &= ~(3 << 12);
	//端口上拉下拉寄存器
	GPIOC->PUPDR &= ~(3 << 12);
	//端口复用功能寄存器
	GPIOC->AFR[0] &= ~(0xf << 24);
	GPIOC->AFR[0] |= (2 << 24);
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB1ENR |= (1 << 1);
	// CR1
	TIM3->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM3->CR1 &= ~(3 << 5);						//配置边沿对齐模式
	TIM3->CR1 &= ~(1 << 4);						//计数器递增计数
	TIM3->CR1 &= ~(1 << 3);						//计数器在发生更新事件时不会停止计数
	TIM3->CR1 &= ~(1 << 1);						//使能更新事件
	// SMRC
	TIM3->SMCR &= ~(7 << 0);					//选择内部时钟
	// CCMRx
	TIM3->CCMR1 &= ~(3 << 0);					//CC1 通道配置为输出
	TIM3->CCMR1 |= (1 << 3);					//影子寄存器使能
	TIM3->CCMR1 &= ~(7 << 4);					//PWM 模式 1
	TIM3->CCMR1 |= (6 << 4);
	// CCER
	TIM3->CCER |= (1 << 0);						//开启––在相应输出引脚上输出 OC1 信号
	TIM3->CCER |= (1 << 1);						//OC1 低电平有效
	// PSC
	TIM3->PSC = 84 - 1;  
	// ARR
	TIM3->ARR = 1000 - 1;
	// CCRx
	TIM3->CCR1 = 1000;
	// EGR
	TIM3->EGR |= (1 << 0);						//重新初始化计数器并生成寄存器更新事件
	//计数器使能
	TIM3->CR1 |= (1 << 0);
}

4.定时器8通道2输出PWM控制电机(LED4)初始化

/***********************************************
*函数名    :Tim8_ch2_pwm_Init
*函数功能  :定时器8通道2输出PWM控制电机(LED4)
*函数参数  :无
*函数返回值:无
*函数描述  :168M  168分频		1/us  T:1ms
*			LED4 --- PC7 --- TIM8_CH2
************************************************/
void Tim8_ch2_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 2);
	//端口模式寄存器 --- 复用
	GPIOC->MODER &= ~(3 << 14);
	GPIOC->MODER |= (2 << 14);
	//端口输出类型寄存器
	GPIOC->OTYPER &= ~(1 << 7);
	//端口输出速度寄存器
	GPIOC->OSPEEDR &= ~(3 << 14);
	//端口上拉下拉寄存器
	GPIOC->PUPDR &= ~(3 << 14);
	//端口复用功能寄存器
	GPIOC->AFR[0] &= ~(0xf << 28);
	GPIOC->AFR[0] |= (3 << 28);
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB2ENR |= (1 << 1);
	// CR1
	TIM8->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM8->CR1 &= ~(3 << 5);						//配置边沿对齐模式
	TIM8->CR1 &= ~(1 << 4);						//计数器递增计数
	TIM8->CR1 &= ~(1 << 3);						//计数器在发生更新事件时不会停止计数
	TIM8->CR1 &= ~(1 << 1);						//使能更新事件
	// SMCR
	TIM8->SMCR &= ~(7 << 0);					//选择内部时钟
	// CCMRx
	TIM8->CCMR1 &= ~(3 << 8);					//CC2 通道配置为输出
	TIM8->CCMR1 |= (1 << 11);					//影子寄存器使能
	TIM8->CCMR1 &= ~(7 << 12);					//PWM 模式 1
	TIM8->CCMR1 |= (6 << 12);
	// CCER
	TIM8->CCER |= (1 << 5);						//OC2 低电平有效
	// PSC
	TIM8->PSC = 168 - 1;  
	// ARR
	TIM8->ARR = 1000 - 1;
	// CCRx
	TIM8->CCR2 = 0;
	//BDTR
	TIM8->BDTR |= (1 << 15);   //允许通道输出PWM
	// EGR
	TIM8->EGR |= (1 << 0);						//重新初始化计数器并生成寄存器更新事件
	
	//开启––在相应输出引脚上输出 OC2 信号
	TIM8->CCER |= (1 << 4);	
	//计数器使能
	TIM8->CR1 |= (1 << 0);
}

5.RGB初始化

/***********************************************
*函数名    :RGB_Init
*函数功能  :RGB彩灯初始化函数
*函数参数  :无
*函数返回值:无
*函数描述  :
*			R --- Tim5_ch2_pwm_Init()
*			G --- Tim5_ch3_pwm_Init()
*			B --- Tim5_ch4_pwm_Init()
************************************************/
void RGB_Init(void)
{
	Tim5_ch2_pwm_Init();
	Tim5_ch3_pwm_Init();
	Tim5_ch4_pwm_Init();
}

/***********************************************
*函数名    :Tim5_ch2_pwm_Init
*函数功能  :定时器5通道2输出PWM控制彩灯R
*函数参数  :无
*函数返回值:无
*函数描述  :84M  84分频		1/us  T:1ms
*			R--------PA1-------TIM5_CH2
************************************************/
void Tim5_ch2_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 0);
	//端口模式寄存器 --- 复用
	GPIOA->MODER &= ~(3 << 2);
	GPIOA->MODER |= (2 << 2);
	//端口输出类型寄存器
	GPIOA->OTYPER &= ~(1 << 1);
	//端口输出速度寄存器
	GPIOA->OSPEEDR &= ~(3 << 2);
	//端口上拉下拉寄存器
	GPIOA->PUPDR &= ~(3 << 2);
	//端口复用功能寄存器
	GPIOA->AFR[0] &= ~(0xf << 4);
	GPIOA->AFR[0] |= (2 << 4);
	
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB1ENR |= (1 << 3	);
	// CR1
	TIM5->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM5->CR1 &= ~(3 << 5);						//配置边沿对齐模式
	TIM5->CR1 &= ~(1 << 4);						//计数器递增计数
	TIM5->CR1 &= ~(1 << 3);						//计数器在发生更新事件时不会停止计数
	TIM5->CR1 &= ~(1 << 1);						//使能更新事件
	// SMRC
	TIM5->SMCR &= ~(7 << 0);					//选择内部时钟
	// CCMRx
	TIM5->CCMR1 &= ~(3 << 8);					//CC2 通道配置为输出
	TIM5->CCMR1 |= (1 << 11);					//影子寄存器使能
	TIM5->CCMR1 &= ~(7 << 12);					//PWM 模式 1
	TIM5->CCMR1 |= (6 << 12);
	// CCER
	TIM5->CCER |= (1 << 4);						//开启––在相应输出引脚上输出 OC2+ 信号
	TIM5->CCER |= (1 << 5);						//OC2 低电平有效
	// PSC
	TIM5->PSC = 84 - 1;  
	// ARR
	TIM5->ARR = 1000 - 1;
	// CCRx
	TIM5->CCR2 = 0;
	// EGR
	TIM5->EGR |= (1 << 0);						//重新初始化计数器并生成寄存器更新事件
	//计数器使能
	TIM5->CR1 |= (1 << 0);
}

/***********************************************
*函数名    :Tim5_ch3_pwm_Init
*函数功能  :定时器5通道3输出PWM控制彩灯G
*函数参数  :无
*函数返回值:无
*函数描述  :84M  84分频		1/us  T:1ms
*			G--------PA2-------TIM5_CH3
************************************************/
void Tim5_ch3_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 0);
	//端口模式寄存器 --- 复用
	GPIOA->MODER &= ~(3 << 4);
	GPIOA->MODER |= (2 << 4);
	//端口输出类型寄存器
	GPIOA->OTYPER &= ~(1 << 2);
	//端口输出速度寄存器
	GPIOA->OSPEEDR &= ~(3 << 4);
	//端口上拉下拉寄存器
	GPIOA->PUPDR &= ~(3 << 4);
	//端口复用功能寄存器
	GPIOA->AFR[0] &= ~(0xf << 8);
	GPIOA->AFR[0] |= (2 << 8);
	
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB1ENR |= (1 << 3);
	// CR1
	TIM5->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM5->CR1 &= ~(3 << 5);						//配置边沿对齐模式
	TIM5->CR1 &= ~(1 << 4);						//计数器递增计数
	TIM5->CR1 &= ~(1 << 3);						//计数器在发生更新事件时不会停止计数
	TIM5->CR1 &= ~(1 << 1);						//使能更新事件
	// SMRC
	TIM5->SMCR &= ~(7 << 0);					//选择内部时钟
	// CCMRx
	TIM5->CCMR2 &= ~(3 << 0);					//CC3 通道配置为输出
	TIM5->CCMR2 |= (1 << 3);					//影子寄存器使能
	TIM5->CCMR2 &= ~(7 << 4);					//PWM 模式 1
	TIM5->CCMR2 |= (6 << 4);
	// CCER
	TIM5->CCER |= (1 << 8);						//开启––在相应输出引脚上输出 OC3 信号
	TIM5->CCER |= (1 << 9);						//OC3 低电平有效
	// PSC
	TIM5->PSC = 84 - 1;  
	// ARR
	TIM5->ARR = 1000 - 1;
	// CCRx
	TIM5->CCR3 = 0;
	// EGR
	TIM5->EGR |= (1 << 0);						//重新初始化计数器并生成寄存器更新事件
	//计数器使能
	TIM5->CR1 |= (1 << 0);
}

/***********************************************
*函数名    :Tim5_ch4_pwm_Init
*函数功能  :定时器5通道4输出PWM控制彩灯B
*函数参数  :无
*函数返回值:无
*函数描述  :84M  84分频		1/us  T:1ms
*			B--------PA3-------TIM5_CH4
************************************************/
void Tim5_ch4_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 0);
	//端口模式寄存器 --- 复用
	GPIOA->MODER &= ~(3 << 6);
	GPIOA->MODER |= (2 << 6);
	//端口输出类型寄存器
	GPIOA->OTYPER &= ~(1 << 3);
	//端口输出速度寄存器
	GPIOA->OSPEEDR &= ~(3 << 6);
	//端口上拉下拉寄存器
	GPIOA->PUPDR &= ~(3 << 6);
	//端口复用功能寄存器
	GPIOA->AFR[0] &= ~(0xf << 12);
	GPIOA->AFR[0] |= (2 << 12);
	
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB1ENR |= (1 << 3);
	// CR1
	TIM5->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM5->CR1 &= ~(3 << 5);						//配置边沿对齐模式
	TIM5->CR1 &= ~(1 << 4);						//计数器递增计数
	TIM5->CR1 &= ~(1 << 3);						//计数器在发生更新事件时不会停止计数
	TIM5->CR1 &= ~(1 << 1);						//使能更新事件
	// SMRC
	TIM5->SMCR &= ~(7 << 0);					//选择内部时钟
	// CCMRx
	TIM5->CCMR2 &= ~(3 << 8);					//CC4 通道配置为输出
	TIM5->CCMR2 |= (1 << 11);					//影子寄存器使能
	TIM5->CCMR2 &= ~(7 << 12);					//PWM 模式 1
	TIM5->CCMR2 |= (6 << 12);
	// CCER
	TIM5->CCER |= (1 << 12);						//开启––在相应输出引脚上输出 OC2+ 信号
	TIM5->CCER |= (1 << 13);						//OC2 低电平有效
	// PSC
	TIM5->PSC = 84 - 1;  
	// ARR
	TIM5->ARR = 1000 - 1;
	// CCRx
	TIM5->CCR4 = 0;
	// EGR
	TIM5->EGR |= (1 << 0);						//重新初始化计数器并生成寄存器更新事件
	//计数器使能
	TIM5->CR1 |= (1 << 0);
}

6.舵机初始化

/***********************************************
*函数名    :SG90_Init
*函数功能  :舵机初始化函数
*函数参数  :无
*函数返回值:无
*函数描述  :
************************************************/
void SG90_Init(void)
{
	Tim14_ch1_pwm_Init();
}

/***********************************************
*函数名    :Tim14_ch1_pwm_Init
*函数功能  :定时器13通道1输出PWM控制舵机
*函数参数  :无
*函数返回值:无
*函数描述  :84M  84分频		1/us  T:20ms
*			SG90 --- PA7 --- TIM14_CH1
************************************************/
void Tim14_ch1_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 0);
	//端口模式寄存器 --- 复用
	GPIOA->MODER &= ~(3 << 14);
	GPIOA->MODER |= (2 << 14);
	//端口输出类型寄存器
	GPIOA->OTYPER &= ~(1 << 7);
	//端口输出速度寄存器
	GPIOA->OSPEEDR &= ~(3 << 14);
	//端口上拉下拉寄存器
	GPIOA->PUPDR &= ~(3 << 14);
	//端口复用功能寄存器
	GPIOA->AFR[0] &= ~(0xf << 28);
	GPIOA->AFR[0] |= (9 << 28);
	
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB1ENR |= (1 << 8);
	// CR1
	TIM14->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM14->CR1 &= ~(1 << 1);					//更新中断事件
	
	//CCMRx
	TIM14->CCMR1 &= ~(3 << 0);					//CC1 通道配置为输出
	TIM14->CCMR1 |= (1 << 3);					//影子寄存器使能
	TIM14->CCMR1 &= ~(7 << 4);					//PWM 模式 1
	TIM14->CCMR1 |= (6 << 4);
	
	// CCER
	
	TIM14->CCER &= ~(1 << 1);						//OC1 高电平有效
	// PSC
	TIM14->PSC = 84 - 1;  
	// ARR
	TIM14->ARR = 20000 - 1;
	// CCRx
	TIM14->CCR1 = 500;
	//EGR
	TIM14->EGR |= (1 << 0);
	
	//通道一使能
	TIM14->CCER |= (1 <<0);
	//计数器使能
	TIM14->CR1 |= (1 << 0);
}

7.超声波初始化

/***********************************************
*函数名    :HC_SR04_Init
*函数功能  :HC-SR04超声波测距模块初始化函数
*函数参数  :无
*函数返回值:无
*函数描述  :
************************************************/
void HC_SR04_Init(void)
{
	//Trig---PB8
	RCC->AHB1ENR |= (1 << 1);
	GPIOB->MODER &= ~(3 << 16);
	GPIOB->MODER |= (1 << 16);
	GPIOB->ODR &= ~(1 << 8);
	//Echo
	Tim4_ch1_in_Init();
}

/***********************************************
*函数名    :Tim4_ch1_in_Init
*函数功能  :定时器4通道1输入捕获ECHO超声波电平
*函数参数  :无
*函数返回值:无
*函数描述  :84M  84分频		1/us  ARR:65535
*			HC-SR04 --- PB6 --- Tim4_ch1
************************************************/
void Tim4_ch1_in_Init(void)
{
	/*IO口控制器配置*/
	RCC->AHB1ENR |= (1 << 1);
	GPIOB->MODER &= ~(3 << 12);
	GPIOB->MODER |= (2 << 12);
	//上拉/下拉寄存器
	GPIOB->PUPDR &= ~(3 << 6);
	//复用功能寄存器
	GPIOB->AFR[0] &= ~(0xf << 24);
	GPIOB->AFR[0] |= (2 << 24);
	
	/*定时器控制器配置*/
	RCC->APB1ENR |= (1 << 2);
	TIM4->CR1 &= ~(3 << 8);
	TIM4->CR1 |= (1 << 7);
	TIM4->CR1 &= ~(3 << 5);
	TIM4->CR1 &= ~(1 << 4);
	TIM4->CR1 &= ~(1 << 3);
	TIM4->CR1 &= ~(1 << 2);
	TIM4->CR1 &= ~(1 << 1);
	
	TIM4->SMCR &= ~(7 << 0);
	
	TIM4->DIER |= (1 << 1);
	TIM4->DIER |= (1 << 1);
	
	TIM4->CCMR1 &= ~(3 << 0);
	TIM4->CCMR1 |= (1 << 0);
	TIM4->CCMR1 &= ~(3 << 2);
	TIM4->CCMR1 &= ~(0xf  << 4);
	TIM4->CCMR1 |= (0xf  << 4);
	
	TIM4->CCER &= ~(1 << 1);
	TIM4->CCER &= ~(1 << 3);
	
	TIM4->PSC = 84-1;
	TIM4->ARR = 65535;
	
	//EGR
	TIM4->EGR |= (1 << 0);
	
	/*NVIC控制器配置*/
	//优先级分组 --- 主函数
	//优先级编码值
	u32 pri = NVIC_EncodePriority (5,0,0);
	//确定具体中断源
	NVIC_SetPriority(TIM4_IRQn,pri);
	//使能NAIC响应通道
	NVIC_EnableIRQ(TIM4_IRQn);
	
	//通道使能
	TIM4->CCER |= (1 << 0);
	//计数器使能
	TIM4->CR1 |= (1 << 0);
}

8.按键初始化

/**************************************************************************
* 函数名    :Key_Init
* 函数功能  :对按键初始化配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*							KEY1 --- PA0 --- 通用输入,不用上拉/下拉电阻
*							KEY2 --- PE2 --- 通用输入,不用上拉/下拉电阻
*							KEY3 --- PE3 --- 通用输入,不用上拉/下拉电阻
*							KEY4 --- PE4 --- 通用输入,不用上拉/下拉电阻
*************************************************************************/
void Key_Init(void)
{
	//打开时钟
	RCC->AHB1ENR |= (1 << 0);
	RCC->AHB1ENR |= (1 << 4);
	
	//配置GPIO口
	//PA0
	GPIOA->MODER &= ~(3 << 0);
	GPIOA->PUPDR &= ~(3 << 0);
	//PE2
	GPIOE->MODER &= ~(3 << 4);
	GPIOE->PUPDR &= ~(3 << 4);
	//PE3
	GPIOE->MODER &= ~(3 << 6);
	GPIOE->PUPDR &= ~(3 << 6);
	//PE4
	GPIOE->MODER &= ~(3 << 8);
	GPIOE->PUPDR &= ~(3 << 8);
}

9.ADC初始化

/***********************************************
*函数名    :adc1_ch10_12_16_init
*函数功能  :ADC1通道10,12和16初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :PC0-----ADC1_CH10---毒气
*           PC2-----ADC1_CH12---光敏
*			        ADC1_CH16---温度
************************************************/
void adc1_ch10_12_16_interrupt_init(void)
{
	/*io控制器配置*/
	//时钟使能
	RCC->AHB1ENR |= (1 << 2);
	GPIOC->MODER &= ~(3 << 4);//PC2模拟模式
	GPIOC->MODER |= (3 << 4);
	GPIOC->MODER &= ~(3 << 0);//PC0模拟模式
	GPIOC->MODER |= (3 << 0);
	
	/*ADC控制器配置*/
	//时钟使能
	RCC->APB2ENR |= (1 << 8);
	//CR1
	ADC1->CR1 &= ~(3 << 24);//分辨率:12位 --- 4096份
	ADC1->CR1 |= (1 << 8);//扫描
	
	//CR2
	//CR2-SWSTART
	ADC1->CR2 &= ~(1 << 11);//右对齐
	ADC1->CR2 |= (1 << 10);//在每个规则转换结束时将 EOC 位置 1
	ADC1->CR2 &= ~(1 << 1);//单次转换
	
	//SMPR1
	ADC1->SMPR1 &= ~(7 << 6);
	ADC1->SMPR1 |= (7 << 6);//通道12采样时间480个周期
	ADC1->SMPR1 &= ~(7 << 0);
	ADC1->SMPR1 |= (7 << 0);//通道10采样时间480个周期
	ADC1->SMPR1 &= ~(7 << 18);
	ADC1->SMPR1 |= (7 << 18);//通道16采样时间480个周期
	
	//SQR
	ADC1->SQR1 &= ~(0xf << 20);
	ADC1->SQR1 |= (2<<20);//3次转换
	
	ADC1->SQR3 &= ~(0x1f << 0);
	ADC1->SQR3 |= (10 << 0);		//通道10第一个转换
	ADC1->SQR3 &= ~(0x1f << 5);
	ADC1->SQR3 |= (12 << 5);		//通道12第二个转换
	ADC1->SQR3 &= ~(0x1f << 10);
	ADC1->SQR3 |= (16 << 10);		//通道16第二个转换
	
	//CCR
	ADC->CCR &= ~(3 << 16);//2分频
	ADC->CCR |= (1 << 23);
	
	//优先级分组 --- 主函数
	//优先级编码值
	u32 pri = NVIC_EncodePriority (5,1,1);
	//确定具体中断源
	NVIC_SetPriority(ADC_IRQn,pri);
	//使能NAIC响应通道
	NVIC_EnableIRQ(ADC_IRQn);
	
	
	//EOC中断使能
	ADC1->CR1 |= (1<<5);
	//使能ADC
	ADC1->CR2 |= (1 << 0);
}

10.基本定时器7毫秒级定时中断

/***********************************************
*函数名    :Timer7_interrupt_ms
*函数功能  :基本定时器7毫秒级定时中断
*函数参数  :u16 ms
*函数返回值:无
*函数描述  :84M  8400分频		10/ms   Max:6553ms
************************************************/
void Timer7_interrupt_ms(u16 ms)
{
	/*定时器控制器配置*/
	//时钟使能
	RCC->APB1ENR |= (1 << 5);
	//CR1
	TIM7->CR1 |= (1 << 7);					//影子寄存器 
	TIM7->CR1 &= ~(1 << 3);					//连续计数
	TIM7->CR1 &= ~(1 << 2);					//产生中断条件
	TIM7->CR1 &= ~(1 << 1);					//产生更新事件条件
	//PSC
	TIM7->PSC = 8400 - 1;						//分频
	//ARR
	TIM7->ARR = ms * 10  -1;					//重载值
	//人为产生更新事件UG
	TIM7->EGR |= (1 << 0);					//生成寄存器更新事件
	//清除计数完成标志位
	TIM7->SR &= ~(1 << 0);
	
	//优先级编码值
	u32 pri = NVIC_EncodePriority (5,2,3);
	//确定具体中断源
	NVIC_SetPriority(TIM7_IRQn,pri);
	//使能NAIC响应通道
	NVIC_EnableIRQ(TIM7_IRQn);
	
	//中断使能
	TIM7->DIER |= (1 << 0);
	//使能计数器
	TIM7->CR1 |= (1 << 0);
}

11.定义存储传感器数据的结构体

typedef struct adc
{
	u16 mq2_val;			//毒气数据
	u16 light_val;		//光照数据
	float tem_val;		//温度数据
	float sr;					//超声波测距距离
}ADC1_T;

12.获取毒气数据,光照数据,温度数据

/***********************************************
*函数名    :get_adc1_ch10_12_16_data
*函数功能  :获取ADC1的通道10,12和16的转换数据
*函数参数  :无
*函数返回值:ADC1_T
*函数描述  :
************************************************/
ADC1_T get_adc1_ch10_12_16_data(void)
{
	ADC1_T adc_data;
	
	ADC1->CR2 |= (1 << 30);//开始转换
	
	//毒气数据
	while(!(ADC1->SR & (1 << 1)));
	adc_data.mq2_val = ADC1->DR / 4095.0 * 100;
	
	//光照数据
	while(!(ADC1->SR & (1 << 1)));
	adc_data.light_val = (4095 - ADC1->DR) / 4095.0 * 100;
	
	//温度数据
	while(!(ADC1->SR & (1 << 1)));
	adc_data.tem_val = ((ADC1->DR * 0.805-760)/2.5)+25;
	return adc_data;
}

13.ADC中断服务函数处理数据

/**************************************************************************
* 函数名    : ADC_IRQHandler
* 函数功能  :ADC中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*************************************************************************/
ADC1_T adc1_irqdata;
void ADC_IRQHandler(void)
{
	//清除中断标志位
	//紧急事件
	static u8 adc_count=0;
	
	if(ADC1->SR & (1<<1))
	{
		adc_count++;
		//毒气
		if(adc_count == 1)
		{
			adc1_irqdata.mq2_val = ADC1->DR / 4095.0 * 100;
			//毒气数据控制舵机控制阀门
			if(adc1_irqdata.mq2_val>20)
			{
				SG90_val = 2500;
				TIM8->CCR2 = 200+adc1_irqdata.mq2_val * 10;
			}
			else
			{
				TIM8->CCR2 = 0;
			}
		}
		//光照
		else if(adc_count == 2)
		{
			adc1_irqdata.light_val = (4095 - ADC1->DR) / 4095.0 * 100;
			//光照强度越强,LED越亮
			TIM3->CCR1 = adc1_irqdata.light_val*10;
		}
		//温度
		else if(adc_count == 3)
		{
			adc1_irqdata.tem_val = ((ADC1->DR * 0.805-760)/2.5)+25;
			adc_count=0;
		}
	}
}

14.超声波开始测距函数

/***********************************************
*函数名    :HC_SR04_Star
*函数功能  :HC-SR04超声波开始测距函数
*函数参数  :无
*函数返回值:无
*函数描述  :
************************************************/
void HC_SR04_Star(void)
{
	GPIOB->ODR |= (1 << 8);
	Timer6_Delay_Us(10);
	GPIOB->ODR &= ~(1 << 8);
}

15.通过定时器4完成超声波测距及报警功能

/**************************************************************************
* 函数名    :TIM4_IRQHandler
* 函数功能  :通用定时器TIM4中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*************************************************************************/
void TIM4_IRQHandler(void)
{
	static u16 TIM4_count = 0;
	static u16 val_1;
	static u32 val_2;
	static u32 val;
	float csb_val;
	//如果是更新中断
	if(TIM4->SR & (1 << 0))
	{
		//清除中断标志位
		TIM4->SR &= ~(1 << 0);
		//紧急事件
		TIM4_count++;
	}
	
	//如果是捕获中断
	if(TIM4->SR & (1 << 1))
	{
		//清除中断标志位
		TIM4->SR &= ~(1 << 1);
		//紧急事件
		if(!(TIM4->CCER & (1 << 1)))
		{
			TIM4_count = 0;
			val_1 = TIM4->CCR1;
			TIM4->CCER |= (1 << 1);
		}
		else if(TIM4->CCER & (1 << 1))
		{
			val_2 = TIM4->CCR1;
			val = 65535 * TIM4_count - val_1 +val_2;
			adc1_irqdata.sr = 34 * (val/1000.0) / 2;
			if(adc1_irqdata.mq2_val>20 && adc1_irqdata.sr<=100)
			{
				BEEP_ON;
			}
			else
			{
				BEEP_OFF;
			}
			TIM4->CCER &= ~(1 << 1);
		}
	}
}

16.按键扫描函数

/**************************************************************************
* 函数名    :Key_Scanf
* 函数功能  :对按键扫描函数
* 函数参数  :void
* 函数返回值:u8
* 函数说明  :
*							标志位:用变量的数值控制程序的执行次数
*************************************************************************/
u8 Key_Scanf(void)
{
	u8 key = 0xff;
	static u8 key_flag = 1;
	if((KEY1 || !KEY2 || !KEY3 || !KEY4) && key_flag)
	{
		Delay_Ms(15);
		if(KEY1)
		{
			key = 1;
			key_flag = 0;
		}
		if(!KEY2)
		{
			key = 2;
			key_flag = 0;
		}
		if(!KEY3)
		{
			key = 3;
			key_flag = 0;
		}
		if(!KEY4)
		{
			key = 4;
			key_flag = 0; 
		}
		
	}
	if(!KEY1 && KEY2 && KEY3 && KEY4)
	{
		key_flag = 1;
	}
	
	return key;
}

17.在主函数中完成RGB彩灯指定控制

        u8 key;
        key = Key_Scanf();
		if(key==1)
		{
			RGB_R=1000;
			RGB_G=0;
			RGB_B=0;
			rgb_flag = 0;	//彩灯随机变化标志位
		}
		else if(key==2)
		{
			RGB_R=0;
			RGB_G=1000;
			RGB_B=0;
			rgb_flag = 0;
		}
		else if(key==3)
		{
			RGB_R=0;
			RGB_G=0;
			RGB_B=1000;
			rgb_flag = 0;
		}
		else if(key==4)
		{
			rgb_flag=1; //彩灯随机变化标志位
		}

18.通过串口中断发送字符串控制阀门开关

/**************************************************************************
* 函数名    :USART1_IRQHandler
* 函数功能  :对串口1中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*							接收中断信号触发
*							空闲中断信号触发
*************************************************************************/
u8 usart1_buff[30];
u8 usart1_flag = 0;
void USART1_IRQHandler(void)
{
	static u8 i = 0;
	//判断接收中断信号触发
	if(USART1->SR & (1 << 5))
	{
		//清除中断标志位
		//紧急事件
		usart1_flag = 0;
		usart1_buff[i] = USART1->DR;
		i++;
	}
	//判断空闲中断信号触发
	if(USART1->SR & (1 << 4))
	{
		//清除中断标志位
		USART1->SR;
		USART1->DR;
		//紧急事件
		usart1_buff[i] = '\0';
		i=0;
		usart1_flag = 1;
		if(strcmp((char*)usart1_buff,"open")==0)
		{
			SG90_val=500;
		}
		if(strcmp((char*)usart1_buff,"close")==0)
		{
			SG90_val=2500;
		}
	}
}

19.通过串口打印数据 

/***********************************************************
*函数名    :printf_val
*函数功能  :数据打印函数
*函数参数  :无
*函数返回值:无
*函数描述  :
*************************************************************/
void printf_val(void)
{
	printf("毒气数据:%d%%\r\n",adc1_irqdata.mq2_val);
	printf("光照数据:%d%%\r\n",adc1_irqdata.light_val);
	printf("温度数据:%0.1f°C\r\n",adc1_irqdata.tem_val);
	printf("检测距离:%0.1f\r\n",adc1_irqdata.sr);
}

20.通过定时器7完成定时中断

/**************************************************************************
* 函数名    :TIM7_IRQHandler
* 函数功能  :基本定时器TIM7中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*************************************************************************/
u32 timer7_count[10];
u8 rgb_flag = 0;
void TIM7_IRQHandler(void)
{
	//清除标志位
	TIM7->SR &= ~(1 << 0);
	//紧急事件
	timer7_count[1]++;
	timer7_count[2]++;
	timer7_count[6]++;
	timer7_count[7]++;
	
	
	//RGB彩灯随机变化
	if(timer7_count[1] == 1000)
	{
		timer7_count[1] = 0;
		if(rgb_flag == 1)
		{
			RGB_R += 123;
			RGB_G += 456;
			RGB_B += 789;
			if(RGB_R >= 1000)
			{
				RGB_R = 0;
			}
			if(RGB_G >= 1000)
			{
				RGB_G = 0;
			}
			if(RGB_B >= 1000)
			{
				RGB_B = 0;
			}
		}
	}
	
	//超声波开始测距
	if(timer7_count[2]==100)
	{
		timer7_count[2] = 0;
		HC_SR04_Star();
	}
	
	
	//ADC开始转换
	if(timer7_count[6]==100)
	{
		timer7_count[6] = 0;
		ADC1->CR2 |= (1<<30);//开始转换
	}
	
	//将获取到的数据通过串口打印出来
	if(timer7_count[7]==105)        //等转换完成再打印
	{
		timer7_count[7] = 0;
		printf_val();
	}
}

五、整体代码

可以在PC端打开该文章,可以下载该项目压缩包。(在这个项目之前的练习也在这个程序中,所以会显得有些乱)

六、总结

       这个模拟毒气检测系统项目是在他人带领下完成的,只是作为一个练手的实践项目,这里面用到了单片机的中断,GPIO,UART,ADC,定时器等基础知识。这并不是一个已经完全完善了的代码,需要补足的地方还有很多也可以通过代码写入其他功能,也有可能会遗漏部分代码的分析,你也可以根据自己需要写入合适的功能。

         你们的支持和反馈对我来说非常重要。如果你有任何问题或建议,请通过私聊随时与我联系。别忘了点赞,收藏,关注哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值