08: STM32 ---- Передача данных DMA

Оглавление

1: Возобновить

2: Образ памяти

3: Базовая структура DMA

4: Условия передачи DMA 

5: запрос прямого доступа к памяти

A: передача данных DMA

1: Схема подключения

2: Передача данных + DMA

3: Введение в функцию

4: Шаги

5: Код

 B: многоканальный DMA+AD

1: Схема подключения

2: Структурная диаграмма

 3: Введение в функцию

4: Код  


1: Возобновить

DMA (Direct Memory Access) прямой доступ к памяти

DMA может обеспечить высокоскоростную передачу данных между периферийными устройствами и памятью или памятью и памятью без вмешательства ЦП, экономя ресурсы ЦП.

12 независимо настраиваемых каналов: DMA1 (7 каналов), DMA2 (5 каналов)

Каждый канал поддерживает программный запуск и специальный аппаратный запуск (источник запуска для каждого канала DMA различен)

STM32F103C8T6 Ресурс DMA: DMA1 (7 каналов)

Периферийное устройство (периферийная память)  : обычно регистр данных DR периферийного устройства, например регистр данных АЦП, регистр данных последовательного порта и т. д.

Память   . Под памятью здесь понимаются оперативная память SRAM и флэш-память программ, в которой мы храним массивы переменных и программные коды.

Передача данных между периферийными устройствами и памятью --------- Используйте определенные аппаратные триггеры (источники триггеров для каждого канала DMA разные)

Передача данных между памятью и памятью ------Программный триггер

2: Образ памяти

ПЗУ:   постоянное запоминающее устройство, энергонезависимая память, которая не теряется при отключении питания.

ОЗУ:  Оперативная память — это своего рода энергозависимая память, которая теряется при отключении питания.

        Потому что процессор 32-битный. Таким образом, ведущий диапазон адресов представляет собой 32-битный диапазон.

3: Базовая структура DMA

Следующие параметры используются при настройке DMA

Направление : определяет направление данных. Периферийное устройство ---> Хранение или хранилище ----> Периферийное устройство или хранилище ----> Хранилище.

        Флэш-память является постоянной памятью, поэтому при выборе направления передачи «Хранилище--->хранилище» можно выбрать только направление передачи «Флэш-------------->SRAM».

        X.DMA_DIR=Укажите периферийный сайт (периферийный регистр) в качестве источника или назначения данных.

Счетчик передач:  используется для указания общего количества операций передачи. Этот счетчик передач является уменьшающимся счетчиком.

        Например, если вы запишете в него цифру 5, то DMA сможет выполнить только 5 передач данных. В процессе передачи счетчик будет уменьшаться на 1 для каждой передачи. Когда счетчик передач уменьшится до 0, DMA не будет работать. дольше выполнять передачу данных., после того как он уменьшится до 0, ранее увеличенный адрес также будет восстановлен до начального адреса.

        X.DMA_BufferSize=Счетчик передач

Autoreloader : После того, как счетчик передач уменьшится до 0, следует ли восстанавливать его до исходного значения

        Например, если счетчик начальной передачи равен 5, то если автоматический перезагрузщик не используется, DMA завершится после 5 передач. Если используется автоматический перезагрузщик, DMA будет передан 5 раз. После того, как счетчик уменьшится до 0, он будет немедленно перезагружен.Первоначальное значение — 5, которое определяет режим перевалки.        

        Никакой переустановки, просто обычный одиночный режим

        Переустановка - это циклический режим

        X.DMA_Mode=Автозагрузчик

Начальный адрес  : определяет, откуда поступают данные.

Поскольку stm32 — это 32-битный микроконтроллер, один из блоков его памяти имеет размер 32 бита, поэтому начальный адрес заполняется 32 битами.

Ширина данных : укажите ширину данных, которая будет использоваться для передачи.

        Байт (uint8_t), HalfWord (uint16_t) и Word (uint32_t)

Программный триггер и режим петли нельзя использовать одновременно.

        Программный триггер: логика выполнения программного триггера заключается в непрерывном запуске DMA на самой высокой скорости, очистке счетчика передачи как можно скорее и завершении этого раунда преобразования.

        Программный триггер предназначен для скорейшей очистки счетчика передач. Режим цикла предназначен для автоматической перезагрузки после очистки. При одновременном использовании DMA не остановится.

4: Условия передачи DMA 

1: Управление переключателем, функция DMA _Cmd должна быть включена.

2: счетчик передач должен быть больше 0.

3: Источник триггера, должен быть сигнал триггера.

        Он срабатывает один раз, передается один раз, и счетчик передач уменьшается один раз. Когда счетчик передач равен 0 и нет автоматической перезагрузки, независимо от того, сработала она или нет, DMA больше не будет передавать. В это время , вам нужно функцию DMA_Cmd отключить, чтобы закрыть DMA.. Затем записать число больше 0 в счетчик передач, затем DMA_Cmd, дать ENABLE и включить DMA для нормальной работы.

        Обратите внимание, что при записи счетчика передач необходимо сначала отключить DMA, прежде чем продолжить. Вы не можете записать счетчик передач, когда DMA включен.

5: запрос прямого доступа к памяти

Конкретный аппаратный триггер:  аппаратный источник триггера каждого канала различен. Если вам нужно использовать АЦП1 для запуска, то должен быть выбран канал 1; если для запуска используется событие обновления таймера 2, тогда должен быть выбран канал 2.

        Поскольку аппаратный источник запуска каждого канала различен, если вы хотите использовать определенный аппаратный источник запуска, вы должны использовать тот канал, на котором он расположен.

Программный триггер : если вы используете программный триггер, канал можно выбрать произвольно. Программный триггер для каждого канала одинаков.

A: передача данных DMA

1: Схема подключения

2: Передача данных + DMA

3: Введение в функцию

 Настройте в файле dmah stm32f10x ---- инициализация MDA.

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

 Настройте в файле dmah stm32f10x ---- включите MDA.

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

 Настройте в файле dmah stm32f10x ---- настройки регистра данных.

void DMA_SetCurrDataCounter (DMA_Channel_TypeDef * DMAy_Channelx, uint16_t DataNumber); 


uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

DMA_SetCurrDataCounter : устанавливает текущий регистр данных, который используется для записи данных в этот счетчик передачи.Это аналогично настройке параметра X.DMA_Buffersize в DMA.

DMA_GetCurrDataCounter : Получить текущий регистр данных.Эта функция возвращает значение счетчика передачи и устанавливает значение флага в 1 после завершения передачи.

 Настройте в файле dmah stm32f10x ---- очистите бит флага прерывания.

void DMA_ClearFlag (uint32_t DMAy_FLAG)

DMA_ClearFlag : очистить бит флага прерывания.

4: Шаги

1: Включите тактовый сигнал RCC ----- тактовый сигнал DMA находится на шине AHB.

2. Настройка прямого доступа к памяти

5: Код

Используется передача данных между памятью и памятью ------ Программный триггер



#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint16_t MyDMA_Size;
void MyMDA_init(uint32_t AddrA, uint32_t AddrB, uint16_t Size){
	
	MyDMA_Size=Size;
	//开启RCC--DMA是AHB总线的设备 
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*
	我们使用的STM32型号为: STMF103
	参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
	参数1的第一个选择  : 其他设备---103选择这个
	*/
	
	
	//配置DMA
	DMA_InitTypeDef DMA_initstruct;
	//外设备站点的3个参数
    //因为stm32是32位的单片机,他的一个内存单元是32位的
	DMA_initstruct.DMA_PeripheralBaseAddr=AddrA; //外设的起始地址--要求:32位
	DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;外设的数据宽度---我们选择以字节的方式传输(uint8_t)
	DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//外设的地址是否自增--自增
	//储存器的3个参数
	DMA_initstruct.DMA_MemoryBaseAddr=AddrB;//储存器的起始地址--要求:32位
	DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//储存器的数据宽度--我们选择以字节的方式传输(uint8_t)
	DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
	//传输方向
	DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
	//缓冲区大小---传输计数器
	DMA_initstruct.DMA_BufferSize=Size;//传输几次
	//传输模式----是否使用自动重装
	DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//正常模式 //传输计数器到0直接停止
	//选择触发模式---硬件触发或者软件触发
	DMA_initstruct.DMA_M2M=DMA_M2M_Enable;  //使用软件触发
	//优先级
	DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_initstruct);
	
	
	//开启MDA
	DMA_Cmd(DMA1_Channel1,DISABLE);
	
}


void MyDMA_Transfer(void)
{
	//需要给我传输寄存器重新赋值,首先要使CMD失能
	
	DMA_Cmd(DMA1_Channel1, DISABLE);
	/*
	设置当前数据寄存器 ,  就是给这个传输计数器写数据的
	和配置DMA中X.DMA_BufferSize参数相似
	*/
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//DMA1_FLAG_TC1---转运完成标志位   转运完成后置1
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); 
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
}


uint8_t dataA[]={0x01,0x02,0x03,0x04};
uint8_t dataB[]={0,0,0,0};
int main(void)
{
	
	OLED_Init();
	MyMDA_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);
	
	
	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);
	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);	
		
	}
}

Включите часы RRC. 

        Включаем RCC--DMA это устройство шины AHB

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    /*
    Модель STM32, которую мы используем: STMF103
    Первый выбор параметра 1: Взаимосвязанное устройство -- Взаимосвязанный тип является первым
    выбором параметра модели STM23F 105/107 1: Другие устройства ---103Выберите это
    */

Проблема с байтом

        Поскольку параметризуемые данные — это dataA и dataB, размер uint8_t, поэтому при настройке DMA выберите DMA_PeripheralDataSize_Byte(uint8_t) для параметра ширины (X.DMA_PeripheralDataSize).

        Выберите uint32_t при настройке начального адреса DMA: поскольку stm32 — это 32-битный микроконтроллер, один из его блоков памяти — 32-битный.

 B: многоканальный DMA+AD

1: Схема подключения

То же, что 07:STM32 ---- Аналого-цифровой преобразователь АЦП --- B: Многоканальный AD

2: Структурная диаграмма

 3: Введение в функцию

В файле stm32f10x_adc.c -----включите вывод ADC на DMA.

void ADC_DMACmd (ADC_TypeDef* ADCx, FunctionalState NewState)

4: Код  

        В 07:STM32----АЦП мы не использовали режим сканирования.Это связано с проблемой покрытия данных АЦП.В следующем коде мы используем DMA для передачи данных, поэтому В АЦП используется режим сканирования, для передачи данных необходимо использовать DMA

        Режим одиночного сканирования АЦП + режим одной передачи DMA

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
//ADC单次扫描+DMA单次转运的模式
uint16_t AD_Value[4];

void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*
	我们使用的STM32型号为: STMF103
	参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
	参数1的第一个选择  : 其他设备---103选择这个
	*/
	

	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_initstruct);
	
	 
	 
	/*ADC_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	//选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
	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
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=4;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
	ADC_Init(ADC1,&ADC_initstruct);
	//ADC在完成后面数据写在DR寄存器中
	



	//配置DMA
	//外设备站点的3个参数
	//因为stm32是32位的单片机,他的一个内存单元是32位的
	DMA_InitTypeDef DMA_initstruct;
	DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
	DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
//	//储存器的3个参数
	DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位
	DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
	//传输方向
	
	DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
	//缓冲区大小---传输计数器
	DMA_initstruct.DMA_BufferSize=4;//传输几次
	//传输模式----是否使用自动重装
	DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//普通模式    //传输计数器到0直接停止  //DMA_Mode_Circular--循环模式   DMA_Mode_Normal
	//选择触发模式---硬件触发或者软件触发
	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发
	//优先级
	DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_initstruct);
	

	//开启MDA
	DMA_Cmd(DMA1_Channel1,ENABLE);
	//开启ADC到DMA的输出
	
	ADC_DMACmd(ADC1,ENABLE);
	
	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);
	
	
}




void ad_getvalue(void){
	
		
	//重新写一下传输寄存器
	DMA_Cmd(DMA1_Channel1, DISABLE);
	/*
	设置当前数据寄存器 ,  就是给这个传输计数器写数据的
	和配置DMA中X.DMA_BufferSize参数相似
	*/
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	
	
	//单次模式-----软件触发转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	
	//这里因为转运(DMA)总是在转换之后的
	//等待MDA完成的代码
	//DMA1_FLAG_TC1---转运完成标志位   转运完成后置1
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); 
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位


}




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_getvalue

        Поскольку здесь АЦП представляет собой одно преобразование, программное обеспечение необходимо запускать один раз для каждого преобразования, и эту функцию необходимо вызывать для каждого преобразования — ADC_SoftwareStartConvCmd(ADC1,ENABLE);

        Потому что DMA тоже находится в обычном однократном режиме: поэтому перед запуском АЦП необходимо перезаписать счетчик передач.

//重新写一下传输寄存器
	DMA_Cmd(DMA1_Channel1, DISABLE);
	/*
	设置当前数据寄存器 ,  就是给这个传输计数器写数据的
	和配置DMA中X.DMA_BufferSize参数相似
	*/
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
	DMA_Cmd(DMA1_Channel1, ENABLE)

        Наконец, дождитесь завершения преобразования АЦП и передачи DMA. Поскольку передача всегда происходит после преобразования, нам нужно только написать код для ожидания завершения DMA, и не нужно писать код для ожидания завершения DMA. АЦП для завершения.

//这里因为转运(DMA)总是在转换之后的
	//等待MDA完成的代码
	//DMA1_FLAG_TC1---转运完成标志位   转运完成后置1
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); 
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位

Запустить режим сканирования

        Просто выберите еще несколько при выборе группы, а затем выберите режим сканирования при настройке АЦП.Канал должен соответствовать выводу.Заполните соответствующий номер канала для количества каналов, настроенных АЦП.

	/*ADC_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	//选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
	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_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
    ADC_initstruct.ADC_NbrOfChannel=4;  //通道数目--指定在扫描模式下,总共会用到几个通道

 регистр ДР

АЦП запишет данные в регистр DR АЦП. Начальный адрес периферийного устройства, используемого при настройке DMA, должен быть

DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位

 Периферийный адрес источника данных не увеличивается автоматически, но автоматически увеличивается адрес регистра.

        Режим АЦП, используемый здесь снаружи, - это режим сканирования, что означает, что группа запускается для записи каналов в группе. Если адрес увеличивается, он перейдет в другие группы. Приращение адреса регистра: каждый канал в группе в периферийном устройстве . Данные соответствует разным местам в реестре, чтобы можно было осуществить передачу данных

Другие примечания

        Перед включением АЦП: включите выход АЦП на DMA.

DMA_Cmd(DMA1_Channel1,ENABLE);

        При настройке счетчика передач DMA его также необходимо изменить на соответствующее количество передач.

	DMA_initstruct.DMA_BufferSize=4;//传输几次

        Настройте режим запуска DMA на аппаратный триггер.

	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发

  Непрерывное сканирование АЦП + режим циклической передачи DMA

         Поскольку он является непрерывным, программный триггер необходимо срабатывать только один раз во время инициализации. 

        Когда АЦП срабатывает, АЦП непрерывно преобразует, а DMA циклически передает. Оба они всегда работают, всегда обновляя последние результаты преобразования в массив SRAM. Когда нам нужны данные, мы можем просто обратиться к массиву, чтобы получить их в любой момент. время.


#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD_Value[4];

void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*
	我们使用的STM32型号为: STMF103
	参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
	参数1的第一个选择  : 其他设备---103选择这个
	*/
	

	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_initstruct);
	
	 
	 
	/*ADC_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	//选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
	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
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=ENABLE;//选择是连续转换还是单次转换--连续
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=4;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
	ADC_Init(ADC1,&ADC_initstruct);
	//ADC在完成后面数据写在DR寄存器中
	



	//配置DMA
	//外设备站点的3个参数
	//因为stm32是32位的单片机,他的一个内存单元是32位的
	DMA_InitTypeDef DMA_initstruct;
	DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
	DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
//	//储存器的3个参数
	DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位
	DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
	//传输方向
	
	DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
	//缓冲区大小---传输计数器
	DMA_initstruct.DMA_BufferSize=4;//传输几次
	//传输模式----是否使用自动重装
	DMA_initstruct.DMA_Mode=DMA_Mode_Circular;//循环模式    //传输计数器到0直接停止  //DMA_Mode_Circular--循环模式   DMA_Mode_Normal
	//选择触发模式---硬件触发或者软件触发
	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发
	//优先级
	DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_initstruct);
	

	//开启MDA
	DMA_Cmd(DMA1_Channel1,ENABLE);
	//开启ADC到DMA的输出
	
	ADC_DMACmd(ADC1,ENABLE);
	
	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);
	
		ADC_SoftwareStartConvCmd(ADC1,ENABLE);

}




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);

	}
}

おすすめ

転載: blog.csdn.net/m0_74739916/article/details/132571722