5.1 DMA直接存储器读取

1、DMA简介

DMA Direct Memory Access )直接存储器存取
DMA 可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须 CPU 干预,节省了 CPU 的资源
12 个独立可配置的通道: DMA1 7 个通道), DMA2 5 个通道)
每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA 资源: DMA1 7 个通道)

2、存储器映像(STM32F103C8T6)

类型

起始地址

存储器

用途

ROM

0x0800 0000

程序存储器Flash

存储C语言编译后的程序代码

0x1FFF F000

系统存储器

存储BootLoader,用于串口下载

0x1FFF F800

选项字节

存储一些独立于程序代码的配置参数

RAM

0x2000 0000

运行内存SRAM

存储运行过程中的临时变量

0x4000 0000

外设寄存器

存储各个外设的配置参数

0xE000 0000

内核外设寄存器

存储内核各个外设的配置参数

 计算机系统的五大组成系统:运算器、控制器、存储器、输入与输出设备。

核心CPU(运和控)。存储器关键一个内容,一个地址。

ROM(只读存储器):非易失性、掉电不丢失。

RAM(随机):易失性、掉电丢失。

选项字节:主要存储Flash的读保护、写保护、看门狗等配置。 

外设寄存器:初始化各个外设,最终读写的东西。

内核外设:NVIC和SysTick

3、DMA框图

Flash:主闪存

SRAM:运行内存

各个外设都可以看成寄存器,和SRAM存储器。寄存器:一种特殊的存储器,一方面可以由CPU进行读写,另一方面,寄存器的每一位背后,连接一根导线,用于控制外设电路状态。

仲裁器是在DMA通道产生冲突时,由它根据通道优先级决定谁先用。总线矩阵也有,如果DMA和CPU同时访问一个目标,DMA暂停CPU访问,防止冲突 。但任然保证CPU得到一半总线带宽使CPU正常工作。

DMA作为一个外设,它自己也会有相应配置寄存器连接于总线右边的AHB总线上,所以,DMA即是总线矩阵的主动单元,可以读写各种存储器,也是AHB上的被动单元,CPU通过线路,对DMA进行配置。

DMA请求就是DMA的硬件触发源。

4、DMA基本结构

 传输计数器:自减计数器。转运完成后,自增的地址,也会恢复起始地址位置方便新的一轮转换。计数器右边,有个自动重装器,计数器到0,是否自动恢复到最初的值,如果不使用,DMA就直接结束,使用,陷入循环。 

M2M:Memory to Memory存储器到存储器的意思。置1为软件,以最快的速度,连续不断的触发DMA,将其请0。软件触发一般是存储器到存储器触发。硬件触发,一般是与外设有关的转运。

5、DMA转运条件:

1)开关控制,DMA_Cmd必须使能

 2)传输寄存器必须大于0

3)触发源必须有触发信号

注意事项,不能在DMA开启时,写传输计数器。

6、DMA请求

7、 数据宽度与对齐

当源端与目标宽度小时,在源端读B0,在目标写00B0,在前端补0

源端与目标宽度大时,在源端读B1B0,在目标写B1,多出来高位舍掉

8、 数据转运+DMA

 当位置一一对应,所以地址左右都应该自加,移动到下一个数据位置,继续转运。

9、 ADC扫描模式+DMA

10、 DMA数据转运

1)main

#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};
int main(void)
{
	OLED_Init();
	OLED_ShowHexNum(1,1,DataA[0],2);
	OLED_ShowHexNum(1,4,DataA[1],2);
	OLED_ShowHexNum(1,7,DataA[2],2);
	OLED_ShowHexNum(1,10,DataA[3],2);
	OLED_ShowHexNum(2,1,DataB[0],2);
	OLED_ShowHexNum(2,4,DataB[1],2);
	OLED_ShowHexNum(2,7,DataB[2],2);
	OLED_ShowHexNum(2,10,DataB[3],2);	
	MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
	
	//OLED_ShowHexNum(2,1,(uint32_t)&ADC1->DR,8);//不加强制类型转换,即指针跨级赋值
	OLED_ShowHexNum(3,1,DataA[0],2);
	OLED_ShowHexNum(3,4,DataA[1],2);
	OLED_ShowHexNum(3,7,DataA[2],2);
	OLED_ShowHexNum(3,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);	

	while (1)
	{
		
	}
}

2)MyDMA

#include "stm32f10x.h"                  // Device header

void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t size)
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitTypeDef DMA_Initstructure;
	DMA_Initstructure.DMA_PeripheralBaseAddr = AddrA;//起始地址
	DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度
	DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;//是否自增(外设)
	DMA_Initstructure.DMA_MemoryBaseAddr = AddrB;//起始地址
	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 = size;//缓存区大小(传输计数器)
	DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;//传输模式(是否自动重装),正常模式
	DMA_Initstructure.DMA_M2M = DMA_M2M_Enable;//是否存储器到存储器(硬软触发),软件触发
	DMA_Initstructure.DMA_Priority = DMA_Priority_Medium;//优先级,中等优先级
	
	DMA_Init(DMA1_Channel1,&DMA_Initstructure);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);

}

3)OLED

#include "stm32f10x.h"
#include "OLED_Font.h"

/*引脚配置*/
#define OLED_W_D0(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)(x))
#define OLED_W_D1(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_13, (BitAction)(x))
#define OLED_W_RES(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_14, (BitAction)(x))
#define OLED_W_DC(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_15, (BitAction)(x))
#define OLED_W_CS(x)		GPIO_WriteBit(GPIOA, GPIO_Pin_8, (BitAction)(x))

/*引脚初始化*/
void OLED_SPI_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	OLED_W_D0(1);
	OLED_W_D1(1);
	OLED_W_RES(1);
	OLED_W_DC(1);
	OLED_W_CS(1);
}

/**
  * @brief  SPI发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_SPI_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		OLED_W_D0(0);
		OLED_W_D1(Byte & (0x80 >> i));
		OLED_W_D0(1);
	}
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_W_CS(0);
	OLED_W_DC(0);
	OLED_SPI_SendByte(Command);
	OLED_W_CS(1);
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	OLED_W_CS(0);
	OLED_W_DC(1);
	OLED_SPI_SendByte(Data);
	OLED_W_CS(1);
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置高4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置低4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_SPI_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}

4)实物

11、源码改变,持续存储,需要给传输器重新赋值

1) main

#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");	
	OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
	OLED_ShowHexNum(3,8,(uint32_t)DataB,8);
	
	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);	


	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);
		Delay_ms(1000);
		MyDMA_Transfer();
		
		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);		
	}
}

2)MyMDA

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;

void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t size)
{
	MyDMA_Size=size;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitTypeDef DMA_Initstructure;
	DMA_Initstructure.DMA_PeripheralBaseAddr = AddrA;//起始地址
	DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度
	DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;//是否自增(外设)
	DMA_Initstructure.DMA_MemoryBaseAddr = AddrB;//起始地址
	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 = size;//缓存区大小(传输计数器)
	DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;//传输模式(是否自动重装),正常模式
	DMA_Initstructure.DMA_M2M = DMA_M2M_Enable;//是否存储器到存储器(硬软触发),软件触发
	DMA_Initstructure.DMA_Priority = DMA_Priority_Medium;//优先级,中等优先级
	
	DMA_Init(DMA1_Channel1,&DMA_Initstructure);
	
	DMA_Cmd(DMA1_Channel1,DISABLE);

}

//外设数据改变,将其重新存入存储器
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);//先失能
	DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);//重新给传输计数器赋值
	DMA_Cmd(DMA1_Channel1,ENABLE);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//获取DMA转运完成标志
	DMA_ClearFlag(DMA1_FLAG_TC1);//清楚
}

3)实物

12、ADC单次扫描+DMA单次转运

1) main

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




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

2) AD

#include "stm32f10x.h"                  // Device header

//ADC单次扫描+DMA单次循环
uint16_t AD_Value[2];
void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC是APB2上的设备
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK六分频在RCC.H里面找680
	
	GPIO_InitTypeDef GPIO_Initstructure;
	GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AIN;//ADC专属模式,在AIN模式下GPIO口无效,防止干扰
	GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
	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************/
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续转换模式:
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ;//数据对齐:右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发控制的触发源:内部触发,软件触发
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ;//工作模式:独立模式
	ADC_InitStructure.ADC_NbrOfChannel = 2;//通道数目
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描转换模式:
	ADC_Init(ADC1,&ADC_InitStructure);


	/*****************DMA结构体**************/
	DMA_InitTypeDef DMA_Initstructure;
	DMA_Initstructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//起始地址
	DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度
	DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable ;//是否自增(外设)
	DMA_Initstructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//起始地址
	DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;//数据宽度
	DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//是否自增(存储器)
	DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC  ;//传输方向, 外设到存储器
	DMA_Initstructure.DMA_BufferSize = 2;//缓存区大小(传输计数器)
	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);//开启DMA触发信号
	/************开启ADC电源************/
	
	ADC_Cmd(ADC1,ENABLE);
	
	/************ADC进行校准************/
	ADC_ResetCalibration(ADC1);//四步骤:复位校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
	ADC_StartCalibration(ADC1);//开始校准
	while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}

void AD_GetValue(void)
{
	
	DMA_Cmd(DMA1_Channel1,DISABLE);//先失能
	DMA_SetCurrDataCounter(DMA1_Channel1,2);//重新给传输计数器赋值
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换函数
	
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//获取DMA转运完成标志
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除
}

13、ADC连续扫描+DMA循环转运

 1)main

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




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

2) AD

#include "stm32f10x.h"                  // Device header

//ADC单次扫描+DMA单次循环
uint16_t AD_Value[2];
void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC是APB2上的设备
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK六分频在RCC.H里面找680
	
	GPIO_InitTypeDef GPIO_Initstructure;
	GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AIN;//ADC专属模式,在AIN模式下GPIO口无效,防止干扰
	GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
	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************/
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式:
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ;//数据对齐:右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发控制的触发源:内部触发,软件触发
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ;//工作模式:独立模式
	ADC_InitStructure.ADC_NbrOfChannel = 2;//通道数目
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描转换模式:
	ADC_Init(ADC1,&ADC_InitStructure);


	/*****************DMA结构体**************/
	DMA_InitTypeDef DMA_Initstructure;
	DMA_Initstructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//起始地址
	DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度
	DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable ;//是否自增(外设)
	DMA_Initstructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//起始地址
	DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;//数据宽度
	DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//是否自增(存储器)
	DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC  ;//传输方向, 外设到存储器
	DMA_Initstructure.DMA_BufferSize = 2;//缓存区大小(传输计数器)
	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);//开启DMA触发信号
	/************开启ADC电源************/
	
	ADC_Cmd(ADC1,ENABLE);
	
	/************ADC进行校准************/
	ADC_ResetCalibration(ADC1);//四步骤:复位校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
	ADC_StartCalibration(ADC1);//开始校准
	while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换函数
}

void AD_GetValue(void)
{
	
	DMA_Cmd(DMA1_Channel1,DISABLE);//先失能
	DMA_SetCurrDataCounter(DMA1_Channel1,2);//重新给传输计数器赋值
	DMA_Cmd(DMA1_Channel1,ENABLE);

	while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//获取DMA转运完成标志
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除
}

3)实物

 节省软件资源,全由内部软件触发。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值