本人最近课程设计学习STM32F4系列,在此进行学习记录!本次记录完成的结课项目!
课程实训涉及STM32基本知识,GPIO,外部中断EXTI,串口USART,ADC以及DMA,最后贴有注解代码!
A:按键控制ADC开始工作
B:通过旋转滑动变阻器改变电压,随着电压的不同,通过点亮LED灯发出警告,当电压达到最大值,蜂鸣器叫。
C:串口输出当前电压值。
考虑该项目相当于于之前的实验来说,体量更大,所以在编写代码的时候,分成了main函数,头文件,函数定义c文件三个部分。使代码更便于阅读与修改。在.h文件中对函数进行声明,在.c文件对所有函数进行定义。
volatile是一个类型修饰符(type specifier),在库函数的代码中定义位“__IO”。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
而在使用ADC读取实时读取电压值的时候就应该使用该类型。
只需要发和收,所以在发的时候不需要中断否则会程序无功能。
应该在主函数中定义变量,然后在其他.c文件中使用extern来使用外部变量进行传参。否则会导致传参失败。
在使用RCC时钟配置函数,应该在main函数中首先调用,否则会出错。
圆满完成了项目要求。
在对STM32复位后,初始化ADC是关闭的,串口打印出“ADC DID NOT OPEN”的信息。
当按下了WAKEUP KEY之后,通过外部中断EXTI响应打开了ADC,串口打印出“OPEN THE ADC”信息,并实时发送电压信息。
通过调节电位器旋钮进行LED灯的点亮,其中电压比小于25,点亮一盏灯;电压比小于50,点亮两盏灯;电压比小于75,点亮3盏灯,电压比大于75,四盏灯全亮,当电压比达到98及更高的时候,蜂鸣器鸣叫。
#include
#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);
}
}
}
#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
#include
#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); //清楚标志位
}
}