STM32F4的电压实时检测

本人最近课程设计学习STM32F4系列,在此进行学习记录!本次记录完成的结课项目!
课程实训涉及STM32基本知识,GPIO,外部中断EXTI,串口USART,ADC以及DMA,最后贴有注解代码!

一、任务

A:按键控制ADC开始工作
B:通过旋转滑动变阻器改变电压,随着电压的不同,通过点亮LED灯发出警告,当电压达到最大值,蜂鸣器叫。
C:串口输出当前电压值。

二、过程

1、代码风格

考虑该项目相当于于之前的实验来说,体量更大,所以在编写代码的时候,分成了main函数,头文件,函数定义c文件三个部分。使代码更便于阅读与修改。在.h文件中对函数进行声明,在.c文件对所有函数进行定义。
在这里插入图片描述

2、volatile类型理解

volatile是一个类型修饰符(type specifier),在库函数的代码中定义位“__IO”。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
而在使用ADC读取实时读取电压值的时候就应该使用该类型。

3、小问题

1、串口中断标志

只需要发和收,所以在发的时候不需要中断否则会程序无功能。

2、外部变量的使用

应该在主函数中定义变量,然后在其他.c文件中使用extern来使用外部变量进行传参。否则会导致传参失败。

3、RCC的知识

在使用RCC时钟配置函数,应该在main函数中首先调用,否则会出错。

三、成果

圆满完成了项目要求。
在对STM32复位后,初始化ADC是关闭的,串口打印出“ADC DID NOT OPEN”的信息。
在这里插入图片描述

当按下了WAKEUP KEY之后,通过外部中断EXTI响应打开了ADC,串口打印出“OPEN THE ADC”信息,并实时发送电压信息。

图6-5 ADC打开
通过调节电位器旋钮进行LED灯的点亮,其中电压比小于25,点亮一盏灯;电压比小于50,点亮两盏灯;电压比小于75,点亮3盏灯,电压比大于75,四盏灯全亮,当电压比达到98及更高的时候,蜂鸣器鸣叫。

在这里插入图片描述

四、代码

1、main.c文件

#include <stdio.h>
#include "main.h"
#include "project_fun.h"

__IO uint16_t ADC1ConvertedValue = 0;
int flag=0;

int main()
{
	u16 ADCConvertedValueLocalTemp, ADCConvertedValueLocal, Precent = 0, Voltage = 0;
	
	RCC_config();
	led_config();			
	bee_config();
	Res_config();
	GPIO_config();
	NVIC_config();
	exti_config();
	key_init();
	uart_config();
	DMA_Configuration();
	ADC_Config();
	
	Delay_ARMJISHU(6000000);
	
	ADC_SoftwareStartConv(ADC1);
	
	while(1)
	{  
		if(flag==1)
		{
	    ADCConvertedValueLocalTemp = ADC1ConvertedValue;//第一次得到ADC采取到的值
			Delay_ARMJISHU(80000);
			ADCConvertedValueLocal = ADC1ConvertedValue;//第二次得到ADC采取到的值
			//取两个数采样平均值
			ADCConvertedValueLocal = (ADCConvertedValueLocalTemp + ADCConvertedValueLocal)/2;
			
			Precent = (ADCConvertedValueLocal*100/0x1000);
			Voltage = Precent*33;						  
			printf("\r Result:0x%X, Percentage:%d%%, Voltage:%d.%d%dV.  \r", 
			ADCConvertedValueLocal, Precent, Voltage/1000, (Voltage%1000)/100, (Voltage%100)/10);
		
			if(Precent<=25)
			{
				GPIO_ResetBits(GPIOF,GPIO_Pin_10);
				GPIO_SetBits(GPIOF,GPIO_Pin_9);
				GPIO_SetBits(GPIOF,GPIO_Pin_8);	
				GPIO_SetBits(GPIOF,GPIO_Pin_7);
				GPIO_SetBits(GPIOF,GPIO_Pin_6);
			}
			else if(Precent<=50&&Precent>25)
			{
				GPIO_ResetBits(GPIOF,GPIO_Pin_10);
				GPIO_ResetBits(GPIOF,GPIO_Pin_9);
				GPIO_SetBits(GPIOF,GPIO_Pin_8);	
				GPIO_SetBits(GPIOF,GPIO_Pin_7);
				GPIO_SetBits(GPIOF,GPIO_Pin_6);
			}
			else if(Precent<=75&&Precent>50)
			{
				GPIO_ResetBits(GPIOF,GPIO_Pin_10);
				GPIO_ResetBits(GPIOF,GPIO_Pin_9);
				GPIO_ResetBits(GPIOF,GPIO_Pin_8);	
				GPIO_SetBits(GPIOF,GPIO_Pin_7);
				GPIO_SetBits(GPIOF,GPIO_Pin_6);
			}
			else
			{
				GPIO_ResetBits(GPIOF,GPIO_Pin_10);
				GPIO_ResetBits(GPIOF,GPIO_Pin_9);
				GPIO_ResetBits(GPIOF,GPIO_Pin_8);	
				GPIO_ResetBits(GPIOF,GPIO_Pin_7);
				GPIO_SetBits(GPIOF,GPIO_Pin_6);
				if(Precent>=99)
				{
					GPIO_ResetBits(GPIOF,GPIO_Pin_6);
				}					
			}
			Delay_ARMJISHU(6000000);	
		}
		else
		{
			GPIO_SetBits(GPIOF,GPIO_Pin_10);
			GPIO_SetBits(GPIOF,GPIO_Pin_9);
			GPIO_SetBits(GPIOF,GPIO_Pin_8);	
			GPIO_SetBits(GPIOF,GPIO_Pin_7);
			GPIO_SetBits(GPIOF,GPIO_Pin_6);
			Delay_ARMJISHU(10000000);
			printf("\n\t ADC did not open! \n\t");
			Delay_ARMJISHU(60000000);
		}
	}
}


2、project_fun.h文件

#ifndef __PROJECT_FUN_H
#define __PROJECT_FUN_H

#include "main.h"

void Delay_ARMJISHU(__IO uint32_t nCount);
void RCC_config(void);
void led_config(void);
void bee_config(void);
void exti_config(void);
void NVIC_config(void);
void key_init(void);
void Res_config(void);
int fputc(int ch, FILE *f);
int fgetc(FILE *f);
void uart_config(void);
void usart_sendbyte(USART_TypeDef* USARTx,uint16_t ch);
void usart_sendstring(USART_TypeDef* USARTx,char * str);
void GPIO_config(void);
void ADC_Config(void);
void DMA_Configuration(void);


#endif

3、project_fun.c文件

#include <stdio.h>
#include "main.h"

#define ADC_DR_ADDR  ((uint32_t)(0x4001204C)) //ADC1的地址

extern __IO uint16_t ADC1ConvertedValue;//定义变量
extern int flag;


void Delay_ARMJISHU(__IO uint32_t nCount)
{
  for (; nCount != 0; nCount--)
  {
  }
}


//led灯配置
void led_config(void)
{
	//定义初始化结构体
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

	//led初始化函数
	GPIO_Init(GPIOF,&GPIO_InitStruct);
	

	
	//初始化灯先关闭,取反操作
	GPIO_SetBits(GPIOF,GPIO_Pin_10);
	GPIO_SetBits(GPIOF,GPIO_Pin_9);
	GPIO_SetBits(GPIOF,GPIO_Pin_8);	
	GPIO_SetBits(GPIOF,GPIO_Pin_7);
	
}

//蜂鸣器配置
void bee_config(void)
{
	//蜂鸣器对应GPIO口结构体初始化
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	//结构体赋值
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz ;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	
	
	//初始化函数
	GPIO_Init(GPIOF,&GPIO_InitStruct);
	
	//初始化蜂鸣器先关闭
	GPIO_SetBits(GPIOF,GPIO_Pin_6);
	//GPIOF->ODR ^=  GPIO_Pin_6;GPIO_SetBits(GPIOF,GPIO_Pin_6);
}

void Res_config(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOC,&GPIO_InitStruct);
}


//时钟初始化配置
void RCC_config(void)
{
	//时钟线给予GPIOF口确定
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
	
	//需要给予外部中断映射时钟线
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
	
	//给予按键时钟线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); 
	
	//给予串口时钟,因为串口是PA口
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	
	//给予GPIOA口时钟,因为串口USART使用了PA9,PA10口,同样需要给予时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	//给予DMA2时钟线
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	
	
	//给予ADC1时钟线
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	
}



//外部中断配置
void exti_config(void)
{
	//初始化外部中断结构体
	EXTI_InitTypeDef EXTI_InitStruct; 
	
	//输入结构体参数
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	
	
	//外部中断需要端口映射
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
	
	EXTI_Init(&EXTI_InitStruct);	
	
}


//按键初始化配置
void key_init()
{
	//按键对应GPIO口结构体初始化
	GPIO_InitTypeDef  GPIO_InitStruct;	
	
	//结构体参数配置
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

	//初始化函数
	GPIO_Init(GPIOA,&GPIO_InitStruct); 
}

//串口GPIO初始化配置
void GPIO_config(void)
{
	//定义使用串口的GPIO口
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//通过核心板找到对应的串口的引脚
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; 
	//模式的使用是AF复用模式
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	//输出方式使用推挽输出
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	//设置速度
	//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	//初始化GPIO9的引脚
	GPIO_Init(GPIOA,&GPIO_InitStruct); 
	
	
	//初始化GPIO10的引脚,其他都一样
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;	
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct); 
	
	//GPIO口的引脚的复用功能需要该函数进行对应匹配
	//GPIO口的核心板上的引脚也是
	//其不同于中断的sys函数,并不是映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
}


//NVIC中断模块管理初始化
void NVIC_config(void)
{
	//NVIC结构体初始化
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//NVIC模块分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);	
	
	
	//结构体参数配置
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	
	//NVIC初始
	NVIC_Init(&NVIC_InitStruct);  
	
	//选择串口中断通道
	NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	
	//NVIC初始
	NVIC_Init(&NVIC_InitStruct);  
}


//串口配置
void uart_config(void)
{
	//定义串口初始化的结构体
	USART_InitTypeDef USART_InitStruct;
	
	
	//波特率设置
	USART_InitStruct.USART_BaudRate = 115200;
	//硬件流控制
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	//模式选择,包含Tx与Rx
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	//校验位,本实验没有使用校验
	USART_InitStruct.USART_Parity = USART_Parity_No;
	//指定传输的停止位
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	//数据长度
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	
	
	
	//串口初始化函数,参数使用的是USART1。
	USART_Init(USART1,&USART_InitStruct);
	
	
	//对串口进行使能,打开串口外设
	USART_Cmd(USART1,ENABLE);   
	//对串口进行使能,打开串口中断
	//其中参数需要同时有收,发的操作,所以是或操作
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
}



//串口发送数据函数
void usart_sendbyte(USART_TypeDef* USARTx,uint16_t ch)
{
	//发送字符
	USART_SendData(USARTx, ch); 
	//如果没有发送到数据,即是RESET,在此处等待,判定标志
	while(USART_GetITStatus(USARTx,USART_FLAG_TXE) == RESET);
}


//串口发送字符串函数
void usart_sendstring(USART_TypeDef* USARTx,char * str)
{
	//通过字符串指针名自加的操作方式一一打印出字符
	do{
		usart_sendbyte(USARTx,*str);
		str++;
	}while(*str!='\0');
	//如果没有发送到数据,即是RESET,在此处等待
	while(USART_GetITStatus(USARTx,USART_FLAG_TXE) == RESET);
}



//重定向printf函数
//此函数原本是将字符ch打印到文件指针stream流中去,现在是打印到串口1
//使用printf即可以进行串口信息的发送了
int fputc(int ch,FILE *f)
{
			USART_SendData(USART1, ch); 
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//表示发送完成的FLAG
		return ch;
}

//重定向scanf函数
int fgetc(FILE *f)
{
	while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
	return USART_ReceiveData(USART1);
}

//ADC初始化配置
void ADC_Config(void)
{
	//在此情况下的ADC默认采集的是系统的电压,我们即可以通过调节电位器实现不同数据的实现
	//ADC通用结构体初始化
	ADC_CommonInitTypeDef ADC_CommonInitStruct;
	//ADC结构体初始化
	ADC_InitTypeDef ADC_InitStruct;

	
	//配置结构体参数
	ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;	//ADC独立模式
	ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;	//禁止DMA直接传输模式
	ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div2;	//时钟2分频
	ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;	//采样时间间隔
	
	//通用结构初始化函数
	ADC_CommonInit(&ADC_CommonInitStruct);
	
	//配置结构体参数
	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;	//ADC使能
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;	//数据右对齐
	ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止外部边沿触发
	ADC_InitStruct.ADC_NbrOfConversion = 1;	//转换通道数目为1
	ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;	//分辨率12位
	ADC_InitStruct.ADC_ScanConvMode = DISABLE;//禁止扫描
	
	//ADC函数初始化
	ADC_Init(ADC1,&ADC_InitStruct);
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_13,1,ADC_SampleTime_3Cycles);	//配置ADC的通道转换顺序
	ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE);//使能DMA请求
	ADC_DMACmd(ADC1,ENABLE);//使能ADC的DMA传输模式
	
	
	ADC_Cmd(ADC1,ENABLE);	//ADC使能

}


void DMA_Configuration(void)
{
	
	//DMA结构体初始化
	DMA_InitTypeDef DMA_InitStruct;
	
	//DMA是存储器与外设传输的控制器
	//初始化DMA结构体(使用DMA2)
	//由DMA2表格有,应该数据流0通道0才能使用ADC1
	DMA_InitStruct.DMA_Channel = DMA_Channel_0;	//DMA通道0
	DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)ADC_DR_ADDR;	//给予ADC1的基地址
	DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&ADC1ConvertedValue;	//声明的变量的存储器地址
	DMA_InitStruct.DMA_BufferSize = 1;	//传输数据大小为1
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;	//传输的方向
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;	//循环传输
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;	//优先级
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//外设字长
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;	//存储器字长
	DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;	//单次传输
	DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;	//单次传输
	//因为保证了存储器与外设的字长,所以并不需要下述的模式,均给予Disable
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	//不给予外设增量
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;	//不给予存储器增量
	DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;	//不给予FIFO模式
	
	//初始化函数,使用DMA2的流0,在之前结构体定义了使用的是通道0,查表得到对应的ADC1
	DMA_Init(DMA2_Stream0,&DMA_InitStruct);
	//进行使能
	DMA_Cmd(DMA2_Stream0,ENABLE);
}


//外部中断响应函数
void EXTI0_IRQHandler(void)
{
	//中断响应判定标志
	if(EXTI_GetITStatus(EXTI_Line0) != RESET )
	{
		Delay_ARMJISHU(6000000);
		//对应IO口取反
		//GPIOF->ODR ^=  GPIO_Pin_10;
		//GPIOF->ODR ^=  GPIO_Pin_6;

		
		if(flag==0)
		{
			DMA_Cmd(DMA2_Stream0,ENABLE);
			ADC_Cmd(ADC1,ENABLE);	//ADC使能
			ADC_DMACmd(ADC1,ENABLE);//使能ADC的DMA传输模式
			flag=1;
		//清楚中断标志
			printf("\nOpne the ADC!\n");
		}
		else
		{
			DMA_Cmd(DMA2_Stream0,DISABLE);
			ADC_Cmd(ADC1,DISABLE);	//ADC使能
			ADC_DMACmd(ADC1,DISABLE);
			flag=0;			
			printf("\nClose the ADC!\n");
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
		
	}
}

//串口中断响应函数
void USART1_IRQHandler(void)
{
	uint16_t ch;
	//进行接受数据的操作
	if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)//if语句判定串口响应标志
	{
		ch = USART_ReceiveData(USART1);//接受字符ch
		USART_SendData(USART1,ch);
		USART_ClearFlag(USART1,USART_FLAG_RXNE); //清楚标志位
	}
}



发布了27 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42192910/article/details/88218372