功能描述:使用STM32F405RGT6的ADC1的通道4和通道5分别对输入到PA4和PA5上的模拟电压进行模数转换,同时通过DMA将转换的值搬运到内存中,最后在OLED上显示ADC的原始采样值和实际电压值。ADC配置为独立模式、多通道、连续转换、扫描模式、软件触发。
- main.c文件内容如下
#include "stm32f4xx.h" // Device header
#include "OLED.h"
#include "MyADC.h"
uint16_t* ADC_Value;
double Voltage[2];
int main(void)
{
MyADC_Init();
OLED_Init();
OLED_Clear(); //清屏OLED
OLED_Refresh(); //刷新OLED
while(1)
{
ADC_Value = MyADC_GetValue(); //获取ADC1的采样值
Voltage[0] = ((double)ADC_Value[0]/4096)*3.3; //计算对应的电压
OLED_Printf(0,0,8,"ADC_Value = %d",ADC_Value[0]); //显示ADC1通道4采样值
OLED_Printf(0,16,8,"Voltage = %.2lfV",Voltage[0]); //显示对应的电压值
Voltage[1] = ((double)ADC_Value[1]/4096)*3.3; //计算对应的电压
OLED_Printf(0,32,8,"ADC_Value = %d",ADC_Value[1]); //显示ADC1通道5采样值
OLED_Printf(0,48,8,"Voltage = %.2lfV",Voltage[1]); //显示对应的电压值
OLED_Refresh(); //刷新OLED
}
}
- MyADC.c文件内容如下
#include "MyADC.h"
uint16_t MyADC_Buff[2];
uint16_t MyADC_Value[2];
/**
* @摘要 初始化ADC和相应的GPIO
* @参数 无
* @返回值 无
* @说明 无
*/
void MyADC_Init(void)
{
//开启GPIOA的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//定义GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
//初始化上述结构体
GPIO_StructInit(&GPIO_InitStructure);
//配置GPIO模式为模拟输入模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
//配置GPIO输出类型为推挽输出(此处无用)
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
//指定GPIO引脚为Pin4hePin5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
//配置GPIO输入类型为浮空输入(此处无用)
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
//配置GPIO的速度为快速50MHZ
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;
//初始化对应的GPIO
GPIO_Init(GPIOA, &GPIO_InitStructure);
//开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
//定义所有ADC共用配置的初始化结构体
ADC_CommonInitTypeDef ADC_CommonInitStructure;
//初始化上述结构体
ADC_CommonStructInit(&ADC_CommonInitStructure);
//配置为独立模式
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
//配置ADC时钟的预分频器的值为4,即21MHZ(<30MHZ)
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
//失能DMA(这是多ADC模式下的DMA,不是这里要用的DMA)
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
//配置多ADC模式下的两个ADC采样间隔(此处无用)
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_8Cycles;
//初始化所有ADC共用配置
ADC_CommonInit(&ADC_CommonInitStructure);
//定义ADC初始化结构体
ADC_InitTypeDef ADC_InitStructure;
//初始化上述结构体
ADC_StructInit(&ADC_InitStructure);
//配置ADC分辨率为12位
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
//配置ADC为连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
//配置ADC为扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
//配置ADC数据对齐方式为右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//配置ADC外部触发为无,即关闭外部触发
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
//配置ADC外部触发源为TIM2的TRGO输出(此处无用)
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
//配置ADC转换通道数为2
ADC_InitStructure.ADC_NbrOfConversion = 2;
//初始化对应的ADC
ADC_Init(ADC1, &ADC_InitStructure);
//配置每一个规则通道的排序和采样周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_56Cycles);
//开启ADC1的DMA通道
ADC_DMACmd(ADC1, ENABLE);
//允许ADC一次传输后发出新的DMA请求
ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE);
//初始化DMA
MyDMA_Init((uint32_t)&(ADC1->DR), (uint32_t)MyADC_Buff, 2);
//开启ADC1,此时ADC1进入等待触发的状态
ADC_Cmd(ADC1, ENABLE);
//软件触发ADC1,此时ADC1进入采样转换状态
ADC_SoftwareStartConv(ADC1);
//ADC采样完成后会硬件自动触发DMA进行数据转运,并开始下一轮采样
}
/**
* @摘要 软件触发ADC并读取采样值
* @参数 无
* @返回值 指向ADC采样值的指针
* @说明 无
*/
uint16_t* MyADC_GetValue(void)
{
//将MyADC_Buff中的值赋给MyADC_Value,并将MyADC_Buff清零
/*因为MyADC_Buff是由硬件自动写入,这样做的目的是避免同时对MyADC_Buff进行读写,
即主程序中读到的值不会被硬件自动修改,其就等于调用该函数那个时刻ADC的采样值。
当然也可以直接返回MyADC_Buff,根据实际需求灵活变通
*/
for(uint8_t i=0; i<2; i++)
{
MyADC_Value[i] = MyADC_Buff[i];
MyADC_Buff[i] = 0;
}
//返回ADC1的采样值
return MyADC_Value;
}
- MyADC.h文件内容如下
#ifndef __MYADC_H
#define __MYADC_H
#include "stm32f4xx.h" // Device header
#include "MyDMA.h"
void MyADC_Init(void);
uint16_t* MyADC_GetValue(void);
#endif
- MyDMA.c文件内容如下
#include "MyDMA.h"
/**
* @摘要 初始化DMA
* @参数 SAddress: 源地址
* @参数 DAddress: 目的地址
* @参数 Size: 要传输的数据个数
* @返回值 无
* @说明 无
*/
void MyDMA_Init(uint32_t SAddress, uint32_t DAddress,uint16_t Size)
{
//开启DMA2的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
//定义DMA初始化结构体
DMA_InitTypeDef DMA_InitStructure;
//初始化上述结构体
DMA_StructInit(&DMA_InitStructure);
//配置DMA通道为通道0
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
//配置DMA传输的存储器地址(即目的地址)
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DAddress;
//配置存储器接收数据的单位为半字
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
//配置存储器地址指针为自增模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//配置DMA传输的外设(即源地址)
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SAddress;
//配置外设发送数据的单位为半字
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
//配置外设地址指针为非自增模式
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//配置存储器突发传输配置为单次传输
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
//配置外设突发传输配置为单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
//配置DMA传输方向为外设到存储器
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
//配置DMA要传输的数据个数
DMA_InitStructure.DMA_BufferSize = Size;
//配置DMA为循环模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
//配置DMA为非直接模式
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
//配置FIFO阈值为全满
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
//配置该通道的DMA优先级为中
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
//等待DMA失能,以确保初始化可以成功
while(DMA_GetCmdStatus(DMA2_Stream0) == ENABLE);
//初始化DMA
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
//开启DMA,此时DMA等待硬件触发
DMA_Cmd(DMA2_Stream0, ENABLE);
}
- MyDMA.h文件内容如下
#ifndef __MYDMA_H
#define __MYDMA_H
#include "stm32f4xx.h" // Device header
void MyDMA_Init(uint32_t SAddress, uint32_t DAddress, uint16_t Size);
#endif