STM32学习笔记六——ADC模数转化器&DMA直接存储器存取

ADC模数转化器

ADC简介

在这里插入图片描述

逐次逼近型ADC

在这里插入图片描述

STM32的ADC框图

12位分辨率
● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
● 单次和连续转换模式
● 从通道0到通道n的自动扫描模式
● 自校准
● 带内嵌数据一致性的数据对齐
● 采样间隔可以按通道分别编程
● 规则转换和注入转换均有外部触发选项
● 间断模式
● 双重模式(带2个或以上ADC的器件)
● ADC转换时间:
─ STM32F103xx增强型产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
─ STM32F101xx基本型产品:时钟为28MHz时为1μs(时钟为36MHz为1.55μs)
─ STM32F102xxUSB型产品:时钟为48MHz时为1.2μs
─ STM32F105xx和STM32F107xx产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
● ADC供电要求:2.4V到3.6V
● ADC输入范围:VREF- ≤ VIN ≤ VREF+
● 规则通道转换期间有DMA请求产生。
下图是ADC模块的方框图。
在这里插入图片描述
在这里插入图片描述
具体可以参考STM32参考手册11 模拟/数字转换(ADC)部分。

ADC基本结构

在这里插入图片描述

输入通道

在这里插入图片描述

转换模式

单次转换,非扫描模式

在这里插入图片描述### 连续转换,非扫描模式
在这里插入图片描述

单次转换,扫描模式

在这里插入图片描述

连续转换,扫描模式

在这里插入图片描述

触发控制

在这里插入图片描述

数据对齐

在这里插入图片描述

转换时间

在这里插入图片描述

校准

在这里插入图片描述

硬件电路

在这里插入图片描述

AD单通道

第一步:新建工程

第二步:连接线路

第三步:编写程序

实验现象:在OLED显示屏上显示ADCValue:和Volatage:,向右拧动电位器,ADCValue增加,向左,减少。Volatage:为相关ADCValue对应的电压值。
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;
float Voltage;

int main (void)
{
	OLED_Init ();
	AD_Init ();
	
	OLED_ShowString (1,1,"ADValue:");
	OLED_ShowString(2, 1, "Volatge:0.00V");
	
	while(1)
	{
		ADValue = AD_GetValue ();
		Voltage = (float)ADValue / 4095 * 3.3;
		
		OLED_ShowNum (1,9,ADValue ,4);
		OLED_ShowNum(2, 9, Voltage, 1);
		OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);
		
		Delay_ms(100);
	}
}

AD.c文件

#include "stm32f10x.h"                  // Device header

void AD_Init(void )
{
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1 ,ENABLE );
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE );
	
	//配置ADC的时钟分频器的,这里设置的是PCLK2分频为6
	RCC_ADCCLKConfig (RCC_PCLK2_Div6 );
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN ;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
	GPIO_Init(GPIOA ,&GPIO_InitStructure );
	
	//配置ADC1的常规通道,这里设置的是通道0,采样时间为55.5个周期
	ADC_RegularChannelConfig(ADC1 ,ADC_Channel_0 ,1,ADC_SampleTime_55Cycles5 );
	
	//对ADC1进行初始化,设置其工作模式为独立模式,数据对齐方式为右对齐,触发方式为外部触发,扫描方式为禁用,通道数量为1
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE ;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE ;
	ADC_Init (ADC1 ,&ADC_InitStructure);
	
	//使能ADC1
	ADC_Cmd (ADC1 ,ENABLE );
	
	//复位并开始ADC的校准
	ADC_ResetCalibration(ADC1 );
	while (ADC_GetResetCalibrationStatus(ADC1 ) == SET );
	//开始ADC的校准并等待校准完成
	ADC_StartCalibration(ADC1 );
	while (ADC_GetCalibrationStatus(ADC1 ) == SET );
}

//获取ADC转换后的值
uint16_t AD_GetValue(void )
{
	//使能ADC1的转换
	ADC_SoftwareStartConvCmd (ADC1 ,ENABLE );
	//等待ADC1转换完成
	while (ADC_GetFlagStatus (ADC1 ,ADC_FLAG_EOC ) == RESET );
	//获取ADC1的转换值并返回
	return ADC_GetConversionValue (ADC1 );
}

AD.h文件

#ifndef __AD_H
#define __AD_H

void AD_Init(void );
uint16_t AD_GetValue(void );

#endif

AD多通道

第一步:新建工程

第二步:连接线路

第三步:编写程序

实验现象:在OLED显示屏上分别显示AD0(电位器)、AD1(光敏传感器)、AD2(热敏传感器)、AD3(反射式红外传感器)的通道值。
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;

int main(void)
{
	OLED_Init();
	AD_Init();
	
	OLED_ShowString (1,1,"AD0:");
	OLED_ShowString (2,1,"AD1:");
	OLED_ShowString (3,1,"AD2:");
	OLED_ShowString (4,1,"AD3:");
	
	while (1)
	{
		AD0 = AD_GetValue (ADC_Channel_0 );
		AD1 = AD_GetValue (ADC_Channel_1 );
		AD2 = AD_GetValue (ADC_Channel_2 );
		AD3 = AD_GetValue (ADC_Channel_3 );
		
		OLED_ShowNum (1,5,AD0,4);
		OLED_ShowNum (2,5,AD1,4);
		OLED_ShowNum (3,5,AD2,4);
		OLED_ShowNum (4,5,AD3,4);
		
		Delay_ms(100);
	}
}

AD.c文件

#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
		
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
	return ADC_GetConversionValue(ADC1);
}

AD.h文件

#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);

#endif

DMA直接存储器存取

DMA简介

在这里插入图片描述

存储器映像

在这里插入图片描述

DMA框图

12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过
软件来配置。
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、
中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目
标地址必须按数据传输宽度对齐。
● 支持循环的缓冲器管理
● 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志
逻辑或成为一个单独的中断请求。
● 存储器和存储器间的传输
● 外设和存储器、存储器和外设之间的传输
● 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
● 可编程的数据传输数目:最大为65535
在这里插入图片描述
DMA控制器和Cortex™-M3核心共享系统数据总线,执行直接存储器数据传输。当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求会暂停CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。

DMA处理

DMA处理
在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理
请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当
从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制
器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。
总之,每次DMA传送由3个操作组成:
● 从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传
输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。
● 存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输
时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。
● 执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。

仲裁器

仲裁器根据通道请求的优先级来启动外设/存储器的访问。
优先权管理分2个阶段:
● 软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
─ 最高优先级
─ 高优先级
─ 中等优先级
─ 低优先级
● 硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。举个例子,通道2优先于通道4。

DMA通道

每个通道都可以在有固定地址的外设寄存器和存储器地址之间执行DMA传输。DMA传输的数据量是可编程的,最大达到65535。包含要传输的数据项数量的寄存器,在每次传输后递减。

可编程的数据量

外设和存储器的传输数据量可以通过DMA_CCRx寄存器中的PSIZE和MSIZE位编程。

指针增量

通过设置DMA_CCRx寄存器中的PINC和MINC标志位,外设和存储器的指针在每次传输后可以
有选择地完成自动增量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量
值,增量值取决与所选的数据宽度为1、2或4。第一个传输的地址是存放在DMA_CPARx
/DMA_CMARx寄存器中地址。在传输过程中,这些寄存器保持它们初始的数值,软件不能改变
和读出当前正在传输的地址(它在内部的当前外设/存储器地址寄存器中)。
当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。要开始新的
DMA传输,需要在关闭DMA通道的情况下,在DMA_CNDTRx寄存器中重新写入传输数目。
在循环模式下,最后一次传输结束时,DMA_CNDTRx寄存器的内容会自动地被重新加载为其初
始数值,内部的当前外设/存储器地址寄存器也被重新加载为DMA_CPARx/DMA_CMARx寄存器
设定的初始基地址。

通道配置过程

下面是配置DMA通道x的过程(x代表通道号):

  1. 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将是数据传输的源或目标。
  2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数据将从这个地址读出或写入这个地址。
  3. 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
  4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。
  5. 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。
  6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道。一旦启动了DMA通道,它既可响应连到该通道上的外设的DMA请求。当传输一半的数据后,半传输标志(HTIF)被置1,当设置了允许半传输中断位(HTIE)时,将产生一个中断请求。在数据传输结束后,传输完成标志(TCIF)被置1,当设置了允许传输完成中断位
    (TCIE)时,将产生一个中断请求。
循环模式

循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。在DMA_CCRx寄存器中的CIRC位用于开启这一功能。当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。

存储器到存储器模式

DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。当设置了DMA_CCRx寄存器中的MEM2MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。

DMA基本结构

在这里插入图片描述

DMA请求

在这里插入图片描述

数据宽度与对齐

在这里插入图片描述

数据转运+DMA

在这里插入图片描述

ADC扫描模式+DMA

在这里插入图片描述

DMA数据转运

第一步:新建工程

第二步:连接线路

第三步:编写程序

实验现象:将A数组中的数据复制到数组B中,在OLED屏上,第一行显示DataA:和DataA的地址,第二行显示DataB的四个数据;第三行显示DataB:和DataB的地址,第二行显示DataB的四个数据;DataA的四个数据每两秒增加一,DataB的数据每两秒(在DataA的数据增加的一秒后)增加一。
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"

uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};
uint8_t DataB[] = {0, 0, 0, 0};

int main(void)
{
	OLED_Init();
	
	//初始化
	MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);
	
	OLED_ShowString(1, 1, "DataA");
	OLED_ShowString(3, 1, "DataB");
	//以十六进制形式显示 DataA 和 DataB 数组的地址
	OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);
	OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
		
	while (1)
	{
		DataA[0] ++;
		DataA[1] ++;
		DataA[2] ++;
		DataA[3] ++;
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);
		OLED_ShowHexNum(2, 4, DataA[1], 2);
		OLED_ShowHexNum(2, 7, DataA[2], 2);
		OLED_ShowHexNum(2, 10, DataA[3], 2);
		OLED_ShowHexNum(4, 1, DataB[0], 2);
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);
		
		Delay_ms(1000);
		
		//进行DMA传输,将 DataA 和 DataB 数组的内容传输到DMA通道
		MyDMA_Transfer();
		
		OLED_ShowHexNum(2, 1, DataA[0], 2);
		OLED_ShowHexNum(2, 4, DataA[1], 2);
		OLED_ShowHexNum(2, 7, DataA[2], 2);
		OLED_ShowHexNum(2, 10, DataA[3], 2);
		OLED_ShowHexNum(4, 1, DataB[0], 2);
		OLED_ShowHexNum(4, 4, DataB[1], 2);
		OLED_ShowHexNum(4, 7, DataB[2], 2);
		OLED_ShowHexNum(4, 10, DataB[3], 2);

		Delay_ms(1000);
	}
}

MyDMA.c文件

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;//存储DMA传输的大小

//初始化DMA传输的函数,接收三个参数,分别是源端地址(AddrA)、目标地址(AddrB)和传输大小(Size)
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	//将传输大小赋值给全局变量MyDMA_Size
	MyDMA_Size = Size;
	
	//使能DMA1通道1的时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	//初始化DMA
	//义了一个DMA_InitTypeDef类型的结构体变量DMA_InitStructure,这个结构体会被用来配置DMA的配置参数
	DMA_InitTypeDef DMA_InitStructure;
	//设置DMA从外设(peripheral)的基地址,这里的AddrA就是传入的外设基地址。
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
	//设置DMA从外设传输的数据大小,这里的DMA_PeripheralDataSize_Byte表示每次传输一个字节的数据。
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	//设置DMA从外设是否连续传输,这里的DMA_PeripheralInc_Enable表示启用连续传输。
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	//设置DMA到内存的基地址,这里的AddrB就是传入的内存基地址。
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;
	//设置DMA到内存传输的数据大小,这里的DMA_MemoryDataSize_Byte表示每次传输一个字节的数据。
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	//设置DMA到内存是否连续传输,这里的DMA_MemoryInc_Enable表示启用连续传输。
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	//设置DMA的方向,这里的DMA_DIR_PeripheralSRC表示数据从外设传输到内存。
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	//设置DMA传输的数据量,这里的Size就是传入的Size。
	DMA_InitStructure.DMA_BufferSize = Size;
	//设置DMA的模式,这里的DMA_Mode_Normal表示正常模式。
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	//设置DMA是否支持多设备模式,这里的DMA_M2M_Enable表示启用多设备模式。
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
	//设置DMA的优先级,这里的DMA_Priority_Medium表示中等优先级。
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	//使用前面定义和配置好的DMA_InitStructure结构体来初始化DMA1通道1,完成DMA的初始化。
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	//禁用DMA1通道1
	DMA_Cmd(DMA1_Channel1, DISABLE);
}

//执行DMA传输的函数
void MyDMA_Transfer(void)
{
	//禁用DMA1通道1
	DMA_Cmd(DMA1_Channel1, DISABLE);
	//设置当前数据计数器为传输大小
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
	//启用DMA1通道1
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//等待DMA传输完成
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	//清除DMA传输完成的标志
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

MyDMA.h文件

#ifndef __MYDMA_H
#define __MYDMA_H

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);

#endif

MDA+AD多通道

第一步:新建工程

第二步:连接线路

第三步:编写程序

实验现象:OLED屏上的四行分别显示“AD0:”、“AD1:”、“AD2:”、“AD3:”,分别显示PA0-PA3这四个通道的模拟量(和AD多通道的实验现象相同)。
ADC单次扫描+DMA单次转运
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;

int main(void)
{
	OLED_Init();
	AD_Init();
	
	OLED_ShowString (1,1,"AD0:");
	OLED_ShowString (2,1,"AD1:");
	OLED_ShowString (3,1,"AD2:");
	OLED_ShowString (4,1,"AD3:");
	
	while (1)
	{
		AD_GetValue ();
		
		OLED_ShowNum (1,5,AD_Value[0],4);
		OLED_ShowNum (2,5,AD_Value[1],4);
		OLED_ShowNum (3,5,AD_Value[2],4);
		OLED_ShowNum (4,5,AD_Value[3],4);
		
		Delay_ms(100);
	}
}

AD.c文件

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_0 ,1 ,ADC_SampleTime_55Cycles5 );
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_1 ,2 ,ADC_SampleTime_55Cycles5 );
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_2 ,3 ,ADC_SampleTime_55Cycles5 );
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_3 ,4 ,ADC_SampleTime_55Cycles5 );
		
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
	ADC_InitStructure.ADC_NbrOfChannel = 4;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )&ADC1->DR ;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t )AD_Value;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 4;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	DMA_Cmd (DMA1_Channel1 ,ENABLE );
	ADC_DMACmd (ADC1 ,ENABLE );
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

uint16_t AD_GetValue(void )
{
	DMA_Cmd(DMA1_Channel1, DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

AD.h文件

#ifndef __AD_H
#define __AD_H

extern uint16_t AD_Value[4];

void AD_Init(void);
uint16_t AD_GetValue(void );

#endif


ADC连续扫描+DMA循环转运
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0,AD1,AD2,AD3;

int main(void)
{
	OLED_Init();
	AD_Init();
	
	OLED_ShowString (1,1,"AD0:");
	OLED_ShowString (2,1,"AD1:");
	OLED_ShowString (3,1,"AD2:");
	OLED_ShowString (4,1,"AD3:");
	
	while (1)
	{		
		OLED_ShowNum (1,5,AD_Value[0],4);
		OLED_ShowNum (2,5,AD_Value[1],4);
		OLED_ShowNum (3,5,AD_Value[2],4);
		OLED_ShowNum (4,5,AD_Value[3],4);
		
		Delay_ms(100);
	}
}

AD.c文件

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_0 ,1 ,ADC_SampleTime_55Cycles5 );
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_1 ,2 ,ADC_SampleTime_55Cycles5 );
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_2 ,3 ,ADC_SampleTime_55Cycles5 );
	ADC_RegularChannelConfig (ADC1 ,ADC_Channel_3 ,4 ,ADC_SampleTime_55Cycles5 );
		
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
	ADC_InitStructure.ADC_NbrOfChannel = 4;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t )&ADC1->DR ;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t )AD_Value;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 4;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	DMA_Cmd (DMA1_Channel1 ,ENABLE );
	ADC_DMACmd (ADC1 ,ENABLE );
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

AD.h文件

#ifndef __AD_H
#define __AD_H

extern uint16_t AD_Value[4];

void AD_Init(void);

#endif


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值