stm32入门学习9-ADC模数转换器2

(一)ADC模数转换

ADC模数转换器除了单次转换、非扫描模式,还有扫描模式,这个模式在对多个模拟信号进行转换的中间没有产生标志位,且其转换后的数据都放在一个寄存器上(ADC的DR寄存器),如果我们没有及时取出数据,后一个数据就会将前一个数据覆盖,因此我们需要一个东西可以在ADC完成一个模拟信号的转换后及时帮助我们取出数据

(二)DMA

DMA(Direct Memory Access)是一种计算机总线技术功能,它允许某些计算机硬件子系统在不通过中央处理单元(CPU)的情况下直接访问系统内存进行数据读写操作,在ADC进行多次转换时,我们可以让其每转换完成一个信号,就发给DMA一个标志位,让DMA把DR里面的数据读走,这样我们就可以获得多次转换中每一个信号的转换值了

DMA可以通过配置相关参数来把数据从一个地方搬到另一个地方,其配置如图

这里我们可以通过配置数据开始搬运的地址和目的地的地址来确定两个搬运点,可以使用硬件触发搬运(ADC产生的标志位),可以自动重装,配合ADC的多次转换即可实现全自动的转换,整个过程不需要CPU的参与

DMA模块的参数很少,可以很简单地进行配置

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
//初始化DMA,这里会用到

void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
//给初始化结构体默认值

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
//使能DMA,在初始化完DMA后要使用这个函数使能DMA才开始工作,这里会用到

void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState 
NewState);
//配置DMA中断

void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); 
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
//设置计数器值,也就是DMA要进行多少次转换,这里的值应当和模拟信号的输入数量相同,如果不适用连续模式要每次都重新配置计数器,且要在DMA不使能的时候配置,这里会用到

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
//获取转运标志位,转运完成置1,这里会用到

void DMA_ClearFlag(uint32_t DMAy_FLAG);
//清除标志位,这里会用到

ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
//获取中断标志位

void DMA_ClearITPendingBit(uint32_t DMAy_IT);
//清除中断标志位

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//这个是ADC中的函数,要其打开ADC才会向DMA发出转换完成标志位

(三)ADC+DMA实现单次转换扫描模式

单次转换扫描模式,我们可以把要转换的模拟信号排成一个队列,从第一个开始转换,一直到最后一个结束,中间不会产生转换完成标志位,只有最后一个转换完成后才会置转换完成标志位;

(1)DMA模块

配置DMA很简单,只要(1)打开DMA时钟;(2)初始化DMA

(1)打开DMA时钟

DMA在AHB总线上,我们要调动AHB的函数来开启其时钟,如下

void dma_rcc_init()
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
}

(2)初始化DMA

我们可以看下DMA初始化结构体的变量

  1. 第一个参数选择外设的地址,之前我们说过,ADC转换完成后数据放在DR寄存器中,我们需要把DR的地址传给这个参数;
  2. 第二个参数是存储器的地址,我们可以用一个数组来存储读到的数据;
  3. 第三个参数是选择方向,这里有两个选择,一个是选择外设为目的地,一个是选择外设为源头,我们要把外设寄存器的值读出来,自然选择外设到存储器的方向,即外设为源头;
  4. 第四个参数是选择转运数据的个数,我们这里连续四个AD转换,我们需要连续转运四个数据;
  5. 第五个参数选择外设寄存器是否自增,外设寄存器只有一个,地址不需要自增;
  6. 第六个参数选择存储器地址是否要自增,存储器为四个int类型的数组,每转运一次需要地址增加,避免覆盖;
  7. 第七个参数和第八个参数选择的是外设和存储器数据的大小,这里可以选择1个字(32位)、半字(16位)和比特(1位),这里我们的外设的数据大小是16位的,因此为了方便我们存储器和外设都选择16位,即半字;
  8. 第九个参数选择的是DMA的模式,这里有两种模式,正常模式适合我们使用单次扫描模式的AD转换,循环模式适合我们使用多次扫描模式的转换;
  9. 第十个参数选择转运优先级,我们没有同时转运数据,这个可以任意选择;
  10. 第十一个参数选择的是软件触发还是硬件触发,这里我们需要等待ADC转换完成后发来的DMA信号,我们选择硬件触发(不选择软件触发即硬件触发);

这样我们就配置好了DMA

void dma_dma_init()
{
	DMA_InitTypeDef dma_init;
	dma_init.DMA_PeripheralBaseAddr = (int)&ADC1->DR;
    dma_init.DMA_MemoryBaseAddr = (int)dma_value;
    dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
	dma_init.DMA_BufferSize = 4;
    dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    dma_init.DMA_Mode = DMA_Mode_Normal;
    dma_init.DMA_Priority = DMA_Priority_Medium;
    dma_init.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &dma_init);
}

其实这里只是初始化了DMA,其还没有开始工作,因为DMA还没有使能,因为我们配置计数器时要DMA失能,因此我们把使能DMA的函数放在ADC模块中,等我们要读取转换数据再去使能DMA

(3)封装和头文件声明

我们把初始化函数封装到一个函数中,并且在头文件声明此函数,这里我们把存放数据的数组也声明为外部可调用,最后的.c文件和.h文件如下

#include "stm32f10x.h"                  // Device header

uint16_t dma_value[4];

void dma_rcc_init()
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
}

void dma_dma_init()
{
	DMA_InitTypeDef dma_init;
	dma_init.DMA_PeripheralBaseAddr = (int)&ADC1->DR;
    dma_init.DMA_MemoryBaseAddr = (int)dma_value;
    dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
	dma_init.DMA_BufferSize = 4;
    dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    dma_init.DMA_Mode = DMA_Mode_Normal;
    dma_init.DMA_Priority = DMA_Priority_Medium;
    dma_init.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &dma_init);
}

void dma_init()
{
	dma_rcc_init();
	dma_dma_init();
}
#ifndef __DMA_H__
#define __DMA_H__

extern uint16_t dma_value[4];

void dma_init(void);


#endif

(2)ADC模块

我们计划在ADC中添加四个模数转换通道,分别为通道1、2、3、4,其对应的IO口为PA0、PA1、PA2、PA3;我们的初始化思路依然是(1)打开时钟;(2)配置通道;(3)初始化ADC;(4)使能ADC;(5)校准

(1)打开对应时钟

void adc_rcc_init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
}

(2)配置通道

这里我们有四个通道,我们要把对应的IO口初始化

void adc_gpio_init()
{
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &gpio_init);
}
void adc_channel_init()
{
	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);
}

(3)初始化ADC

这里我们把ADC配置成单次转换、扫描模式,且通道个数为4个

void adc_adc_init()
{
	ADC_InitTypeDef adc_init;
	adc_init.ADC_ContinuousConvMode = DISABLE;
	adc_init.ADC_DataAlign = ADC_DataAlign_Right;
	adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	adc_init.ADC_Mode = ADC_Mode_Independent;
	adc_init.ADC_NbrOfChannel = 4;
	adc_init.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC1, &adc_init);
}

(4)打开ADC和其DMA通道

这里分别用一个函数打开ADC和其DMA触发通道

void adc_dma_open()
{
	ADC_DMACmd(ADC1, ENABLE);
}
void adc_open()
{
	ADC_Cmd(ADC1, ENABLE);
}

(5)ADC校准

void adc_check()
{
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

(6)封装为一个函数

在这里我们顺便把DMA的初始化也封装到此函数,这样主函数就不用再引用一个头文件了

void adc_init()
{
	adc_rcc_init();
	adc_gpio_init();
	adc_channel_init();
	adc_adc_init();
	adc_dma_open();
	adc_open();
	adc_check();
	
	dma_init();
}

(7)获取转运值

由于我们这里没有使用ADC的多次模式和DMA的循环转运,我们需要在程序中调用转运函数,这样才会把我们的五个端口依次转运过来

在开始转运前,我们要重新给DMA计数器值,这个过程要先失能DMA,赋值后再使能DMA

uint16_t* adc_start_transfer()
{
	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);
	return dma_value;
}

我们等待转运完成后返回存放数据的数组的指针,注意最后要手动清除DMA的转换完成标志位

最终我们把初始化函数和开始转换函数声明为外部可调用即可,最终.c和.h文件如下

#include "stm32f10x.h"                  // Device header
#include "dma.h"


void adc_rcc_init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
}

void adc_gpio_init()
{
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &gpio_init);
}

void adc_channel_init()
{
	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);
}

void adc_adc_init()
{
	ADC_InitTypeDef adc_init;
	adc_init.ADC_ContinuousConvMode = DISABLE;
	adc_init.ADC_DataAlign = ADC_DataAlign_Right;
	adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	adc_init.ADC_Mode = ADC_Mode_Independent;
	adc_init.ADC_NbrOfChannel = 4;
	adc_init.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC1, &adc_init);
}

void adc_dma_open()
{
	ADC_DMACmd(ADC1, ENABLE);
}

void adc_open()
{
	ADC_Cmd(ADC1, ENABLE);
}

void adc_check()
{
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
}

void adc_init()
{
	adc_rcc_init();
	adc_gpio_init();
	adc_channel_init();
	adc_adc_init();
	adc_dma_open();
	adc_open();
	adc_check();
	
	dma_init();
}

uint16_t* adc_start_transfer()
{
	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);
	return dma_value;
}
#ifndef __ADC_H__
#define __AD_H__

void adc_init(void);
uint16_t* adc_start_transfer(void);

#endif

(3)主函数

最后我们调用初始化函数,在主函数的循环中不断调用获取函数即可,这里在1通道接的是电位器,2通道接的是光敏电阻,3通道接的是温度传感器,4通道接的是红外传感器

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

int main()
{
	uint16_t* num;
	OLED_Init();
	adc_init();
	OLED_ShowString(1, 1, "Volts:");
	OLED_ShowString(2, 1, "Light:");
	OLED_ShowString(3, 1, "temp:");
	OLED_ShowString(4, 1, "infrared:");
	while(1)
	{
		num = adc_start_transfer();
		OLED_ShowNum(1, 7, num[0], 5);
		OLED_ShowNum(2, 7, num[1], 5);
		OLED_ShowNum(3, 6, num[2], 5);
		OLED_ShowNum(4, 10, num[3], 5);
		
		Delay_s(1);	
	}
	return 0;
}

(四)ADC+DMA实现多次转换扫描模式

在单次转换扫描模式中,我们已经实现了一次转换多个传感器,但是每一次转换还需要我们在程序中调用一下启动函数,我们可以用ADC的多次转换扫描模式配合DMA的循环模式来实现自动转换,这里我们把DMA模块和ADC模块写再同一文件中,我们要做的事情有(1)打开对应时钟并设置ADC比较分频;(2)打开对应IO口;(3)打开ADC对应转换通道;(4)初始化ADC;(5)使能ADC并且校准;(6)打开DMA;(7)初始化DMA;(8)封装并程序启动转换

(1)配置相关时钟

这里我们要打开GPIO的时钟、ADC时钟和DMA时钟,还要设置ADC分频

void adc_rcc_init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}

(2)打开IO口

void adc_gpio_init()
{
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpio_init);
}

(3)打开转换通道

这里依然选择通道1、2、3、4

void adc_channel_init()
{
	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);
}

(4)初始化ADC

这里ADC的配置要配置成多次转换、扫描模式

void adc_adc_init()
{
	ADC_InitTypeDef adc_init;
	adc_init.ADC_ContinuousConvMode = ENABLE;
	adc_init.ADC_DataAlign = ADC_DataAlign_Right;
	adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	adc_init.ADC_Mode = ADC_Mode_Independent;
	adc_init.ADC_NbrOfChannel = 4;
	adc_init.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC1, &adc_init);
}

(5)ADC使能和校准

void adc_open()
{
	ADC_Cmd(ADC1, ENABLE);
}

void adc_check()
{
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1) == SET);
}

(6)打开对应DMA

void adc_dma_open()
{
	ADC_DMACmd(ADC1, ENABLE);
}

(7)初始化DMA

这里的模式我们要选择循环模式,以匹配我们ADC转换中的多次转换

void dma_dma_init()
{
	DMA_InitTypeDef dma_init;
	dma_init.DMA_PeripheralBaseAddr = (int)&ADC1->DR;   
    dma_init.DMA_MemoryBaseAddr = (int)value;
    dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
    dma_init.DMA_BufferSize = 4;
    dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    dma_init.DMA_Mode = DMA_Mode_Circular;
    dma_init.DMA_Priority = DMA_Priority_Medium;
    dma_init.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &dma_init);
}

初始化之后不要忘了使能DMA

void dma_open()
{
	DMA_Cmd(DMA1_Channel1, ENABLE);
}

(8)封装

我们把前面所有初始化封装在一个函数里,并且在最后出发转换,这样ADC就会不停地从1通道到4通道依次转换,转换完成后自动从1通道重新开始,同时DMA也在ADC的触发下依次转运每个通道产生的数据,转运完成后自动给计数器重装,不需要任何CPU的资源

void adc_multi_channel_init()
{
	adc_rcc_init();
	adc_gpio_init();
	adc_channel_init();
	adc_adc_init();
	adc_dma_open();
	adc_open();
	adc_check();
	
	dma_dma_init();
	dma_open();
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

(9)最终调用

最后.c和.h文件如下

#include "stm32f10x.h"                  // Device header

int value[4];

void adc_rcc_init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}

void adc_gpio_init()
{
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpio_init);
}

void adc_channel_init()
{
	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);
}

void adc_adc_init()
{
	ADC_InitTypeDef adc_init;
	adc_init.ADC_ContinuousConvMode = ENABLE;
	adc_init.ADC_DataAlign = ADC_DataAlign_Right;
	adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	adc_init.ADC_Mode = ADC_Mode_Independent;
	adc_init.ADC_NbrOfChannel = 4;
	adc_init.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC1, &adc_init);
}

void adc_open()
{
	ADC_Cmd(ADC1, ENABLE);
}

void adc_check()
{
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1) == SET);
}

void adc_dma_open()
{
	ADC_DMACmd(ADC1, ENABLE);
}

void dma_dma_init()
{
	DMA_InitTypeDef dma_init;
	dma_init.DMA_PeripheralBaseAddr = (int)&ADC1->DR;   
    dma_init.DMA_MemoryBaseAddr = (int)value;
    dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
    dma_init.DMA_BufferSize = 4;
    dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    dma_init.DMA_Mode = DMA_Mode_Circular;
    dma_init.DMA_Priority = DMA_Priority_Medium;
    dma_init.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel1, &dma_init);
}

void dma_open()
{
	DMA_Cmd(DMA1_Channel1, ENABLE);
}

void adc_multi_channel_init()
{
	adc_rcc_init();
	adc_gpio_init();
	adc_channel_init();
	adc_adc_init();
	adc_dma_open();
	adc_open();
	adc_check();
	
	dma_dma_init();
	dma_open();
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
#ifndef __ADC_H__
#define __ADC_H__

extern int value[4];

void adc_multi_channel_init(void);

#endif

主函数只要在前面初始化,硬件就会自动进行模数转换,DMA会自动帮我们把数据搬运到数组中

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

int main()
{
	OLED_Init();
	adc_multi_channel_init();
	while(1)
	{
		OLED_ShowNum(1, 1, value[0], 5);
		OLED_ShowNum(2, 1, value[1], 5);
		OLED_ShowNum(3, 1, value[2], 5);
		OLED_ShowNum(4, 1, value[3], 5);
		Delay_s(1);
	}
	return 0;
}

(五)总结

通过实现ADC中的单次转换扫描模式和多次转换扫描模式,我们学会了DMA搬运数据,实现了多通道的模数转换和自动的模数转换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值