STM32 深入模拟数字转换器ADC

ADC 是现实世界和数字化世界的的桥梁

1.ADC整体思路
2.STM32 ADC模块结构
3.单次单通道ADC1 软件触发
4.规则组多通道转换
a.不使用DMA
b.使用DMA
c.EXTI Line 外部触发ADC
d.内部TIM触发ADC (未写)
5.注入组
6.双ADC采样
7.三ADC采样
8.最简易的STM32F103C8T6 小型示波器的实现
9.ADC的误差分析和精准采集

ADC整体思路

ADC模型
在这里插入图片描述
Analog in put Pin,为输入引脚,需要采集的电平通过输入引脚接入ADC。
上图为一个简单的ADC模拟数字转换器模型。模拟量指的是电压量,打开S1,开关那么电流会流过电阻RSH给电容CSH
充电。充电完成后电容两端的电压等于输入引脚的电压。这个过程叫做采样周期。目的是对输入引脚电压采样,也叫做采样保持。
采样保持是采取目标的电压。
下一个时刻,为转换周期,也就是利用采集来的电压进行逐次逼近。最后输出二进制结果。
逐次比较会使用到一组电压作为基准来和输入电压进行比较。这组输入电压为VREF+,VREF-。

((VREF+)-(VREF-))/ 2^采样深度 就是ADC的分辨率
在这里插入图片描述
在这里插入图片描述

ADC的供电方式如上图所示
VCC:C=circuit 表示电路的意思, 即接入电路的电压;
VDD:D=device 表示器件的意思, 即器件内部的工作电压;
VSS:S=series 表示公共连接的意思,通常指电路公共接地端电压。
GND:在电路里常被定为电压参考基点。

VDDA 也就是VDD Analog
在这里插入图片描述
((VREF+)-(VREF-))/ 2^采样深度
意义为参考电压之间的差值进行了等分。一旦MCU型号确定下来,参考电压的稳定性基本上决定了数字量输出的稳定性。
若参考电压波动,数字量的输出也会波动。

STM32 ADC Module结构
在这里插入图片描述
两组电压 参考电压,模拟电源
输入引脚,通过多路选择器和内部转换器连接。基本上所有的外围模组都有这样的设定。
ADC输入选用的是模拟输入模式,数字输入一般都会有施密特或者一些门电路。
规则通道,就是按照事先的排序,一个一个的依次转换,有16路。转换后的结果储存在数据寄存器内,但是只有16位,所有通道共用。
也就是说,转换器对规则通道转换完成后,结果都往 结果寄存器丢。

注入通道,就是在有排队转换的情况下,进行插队转换,有4路。转换后的结果存在通道转用的数据寄存器内。
储存结果的数据寄存器连接数据/地址总线,那么它可以被CPU或者DMA访问。
数据寄存器可以产生事件,对应的事件有使能位,可以发往中断管理器NVCI。
ADCCLK为ADC转换器的时钟信号,有专门的分频器。
在这里插入图片描述

框图下半部分是转换的的触发事件源。注入和规则通道分开,都可以由内部TIM模块触发还可以由外部EXTI事件触发。

STM32 的ADC模块整体结构比较简单清晰。
有的时候需要同时启动多个转换器,同时对电压进行转换,例如YUV三项的电压
在这里插入图片描述
多重转换的的思路就是,触发同步。
ADC1作为主转换器,可以把它的触发事件同时发送到ADC2,ADC3.从而开启同步转换。

单次单通道ADC1 软件触发

在这里插入图片描述
SET ADON 代表写入1 到ADON,不代表ADON 内容的变化。
在这里插入图片描述

先看一下STM32F103系列的ADC初始化,使用库函数
1.ADC模拟输入

void adc_pin_init(void)
{


	GPIO_InitTypeDef adc_gpio_init;
	adc_gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	adc_gpio_init.GPIO_Pin    = GPIO_Pin_1;
	adc_gpio_init.GPIO_Speed =GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &adc_gpio_init);

	/*ENABLE adc1 & clock PA Clock*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );

}

2.配置ADC 比较常规

使用库函数提供的

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)

初始化结构体
在这里插入图片描述
转换模式 Continuous or Single mode.

adc_init_struct.ADC_ContinuousConvMode =  DISABLE ;

数据对齐

	adc_init_struct.ADC_DataAlign   =ADC_DataAlign_Right;

STM32 的寄存器一般为32位或者16位
ADC的转换结果为12位,所以出现对齐的问题。

ADC转换触发条件外部触发,这里选择非外部触发

	adc_init_struct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;

ADC模式选择

adc_init_struct.ADC_Mode=ADC_Mode_Independent;

在这里插入图片描述
转换通道数量

	adc_init_struct.ADC_NbrOfChannel=1;

扫描转换模式

	adc_init_struct.ADC_ScanConvMode=DISABLE ;

配置ADC转换时间,通道

RCC_ADCCLKConfig(RCC_PCLK2_Div8);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);

ADC转换过程中会产生事件,事件可以产生中断。基本上所有的模块都是这个思路

    NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);

NVIC无法管理所有外围设备的所有事件,所以对ADC1,2开发一个通道ADC1_2_IRQn。

在这里插入图片描述

使用软件触发ADC开始转换

	ADC_SoftwareStartConvCmd(ADC1, ENABLE);

完整代码如下

void adc_pin_init(void)
{


	GPIO_InitTypeDef adc_gpio_init;
	adc_gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	adc_gpio_init.GPIO_Pin    = GPIO_Pin_1;
	adc_gpio_init.GPIO_Speed =GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &adc_gpio_init);

	/*ENABLE adc1 & clock PA Clock*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );

}
void test_adc_init(void)
{
	ADC_InitTypeDef adc_init_struct;
	NVIC_InitTypeDef NVIC_InitStructure;
	adc_pin_init();
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	ADC_DeInit(ADC1);
	
	adc_init_struct.ADC_ContinuousConvMode =  DISABLE ;
	adc_init_struct.ADC_DataAlign   =ADC_DataAlign_Right;
	adc_init_struct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	adc_init_struct.ADC_Mode=ADC_Mode_Independent;
	adc_init_struct.ADC_NbrOfChannel=1;
	adc_init_struct.ADC_ScanConvMode=DISABLE ;
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);

	
	ADC_Init(ADC1,&adc_init_struct);
	ADC_Cmd(ADC1, ENABLE);
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1));
	

	NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);

	
}
void task1_task(void *pvParameters)
{
	
    extern void test_adc_init(void);
	test_adc_init();
    while(1)
    {
    
		ADC_SoftwareStartConvCmd(ADC1, ENABLE);
		vTaskDelay(2000);
        vTaskDelay(2000);
    }
    
}
void ADC1_2_IRQHandler(void)
{	
	traceISR_ENTER();

	bit1 =~bit1;

	GPIO_WriteBit(GPIOC, GPIO_Pin_13,bit1 & 0x01);

	ADC_ConvertedValue = ADC_GetConversionValue(ADC1);
	ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
	traceISR_EXIT();

}


这里使用了SEGGA Systemview Trace in FreeRTOS。ISR34 为ADC1_2_IRQHandler
在这里插入图片描述

多通道规则组转换

在这里插入图片描述
设置ADC1 通道1,2,3 转换

	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3,ADC_SampleTime_55Cycles5);

按照手册上多通道规则组转换的定义,每一个通道转换完成都会有EOC事件。

在这里插入图片描述
那么每完成一次转换,就会有一次中断取结果的机会,实际上手册有些矛盾
在这里插入图片描述
EOC事件居然是针对Group,也就是说规则组转换完成后才会中断,那么只有最后一个通道(CHANNEL)的数据能拿到
其他通道的数据统统被覆盖掉了。
所以使用规则组在不使用DMA的情况下,多通道又能够获取ADC值的办法,就需要每次转换都停下来一次。
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

意思就是说,可以使用间断模式,每触发一次,转换N个通道,组内所有的通道转换完成EOC置位。
当N=1,每转换一个通道就会停止直到新的触发来临开启下一次转换。
由于EOC转换完成位在组所有通道转换完成是才会置位,所以这里没有标志位去判断单个通道转换完成。
只能去估计ADC转换时间,也就是说 触发转换->等待一段时间->读取DR寄存器值(转换结果)。

//关闭EOC中断
//ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);
void dothings(void)
{
	int i=2000;
	while (i>0)
		{
			i--;
		}
}
void Get_adc_spin(void)
{	
	unsigned int adc;
	for(int i=0;i<3;i++)
	{
		ADC_SoftwareStartConvCmd(ADC1, ENABLE);
		dothings();
		adc = ADC_GetConversionValue(ADC1);
		SEGGER_RTT_printf(0,"i %d  Read cover: %x \r\n",i,adc);
		
	}
}

dothings相当于延时一段时间。这样在不使用DMA的情况下来。也能去读每个通道的ADC值。
注意 这种方法并不是完美的,程序会在这里自旋,不断的触发,等待读取。

使用DMA 进行多通道规则传输

代码如下

void ADC_train_use_dma_init()
{

	DMA_InitTypeDef DMA_InitStruct;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_DeInit(DMA1_Channel1);


	DMA_InitStruct.DMA_BufferSize = 30;
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStruct.DMA_M2M =DMA_M2M_Disable;

	DMA_InitStruct.DMA_MemoryBaseAddr =ADC_ConvertedValue2;
	
	DMA_InitStruct.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord;
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_Mode  =DMA_Mode_Circular;
	
	DMA_InitStruct.DMA_PeripheralBaseAddr = (unsigned int)(&ADC1->DR);
	DMA_InitStruct.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_Priority  = DMA_Priority_High;

	DMA_Init(DMA1_Channel1,& DMA_InitStruct);

	DMA_Cmd(DMA1_Channel1,ENABLE);


	

	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	//NVIC_Init(&NVIC_InitStructure);
	//DMA_ITConfig(DMA1,DMA_IT_TC,ENABLE);

void test_adc_init_2(void)
{
	ADC_InitTypeDef adc_init_struct;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	
	ADC_DeInit(ADC1);
	
	adc_init_struct.ADC_ContinuousConvMode =  DISABLE ;
	adc_init_struct.ADC_DataAlign   =ADC_DataAlign_Right;
	adc_init_struct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	adc_init_struct.ADC_Mode=ADC_Mode_Independent;
	adc_init_struct.ADC_NbrOfChannel=3;
	adc_init_struct.ADC_ScanConvMode=ENABLE ;
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3,ADC_SampleTime_55Cycles5);

	ADC_DMACmd(ADC1,ENABLE);
	ADC_Init(ADC1,&adc_init_struct);
	
	/*Discontinuous mode*/
	//ADC1->CR1 =ADC1->CR1 |(1<<11);

	ADC_Cmd(ADC1, ENABLE);
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1));
	

	NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	//ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);

	
}

}

F103 ADC章节用户手册写的不是很详细
关于DMA部分更是少之又少。

1.DMA请求信号
在这里插入图片描述
在这里插入图片描述
规则通道转换结束的时候产生DMA请求

DMA请求的使能
数据手册中没有对DMA使能有额外的解释,而是直接放到寄存器里面。
在这里插入图片描述

使用外部触发ADC转换

在这里插入图片描述
ADC通道的触发框图
对于规则通道而言,触发线 由EXTTRIG控制位确定
多路选择器选择内部或者外部的事件连接 触发线

1.第一步,设置EXTTRIG外部触发使能,EXTSEL外部触发选通器。ETRGREG_REMAP 导入EXTI_11Line
2.配置EXTI模块,指定EXTI_11Line 引脚。
在这里插入图片描述
上表为可选择的触发ADC转换事件。EXTSEL外部触发选通器
在这里插入图片描述
ADC使用外部触发需要打开外部使能位,选择触发源。
在这里插入图片描述

在这里插入图片描述
EXTI模块产生EXTI_line事件,由输入线经过边沿检测,判断电平变化代表事件产生,事件使能后通过脉冲发生器,产生EXTI_LIne
在这里插入图片描述
EXTI LineN连接 GPIO PxN
所以选择的ExitLine11 对应所有GPIO组的PIN11
具体什么组需要配置

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource11);

void test_adc_init_ExitTrig(void)
{
	ADC_InitTypeDef adc_init_struct;
	NVIC_InitTypeDef NVIC_InitStructure;
	EXTI_InitTypeDef EXTI_init_struct;
	GPIO_InitTypeDef adc_gpio_init;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	
	ADC_DeInit(ADC1);
	
	adc_init_struct.ADC_ContinuousConvMode =  DISABLE ;
	adc_init_struct.ADC_DataAlign   =ADC_DataAlign_Right;
	adc_init_struct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO;
	adc_init_struct.ADC_Mode=ADC_Mode_Independent;
	adc_init_struct.ADC_NbrOfChannel=3;
	adc_init_struct.ADC_ScanConvMode=ENABLE ;
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3,ADC_SampleTime_55Cycles5);

	ADC_ExternalTrigConvCmd(ADC1,ENABLE);

	ADC_DMACmd(ADC1,ENABLE);
	ADC_Init(ADC1,&adc_init_struct);
	
	/*Discontinuous mode*/
	//ADC1->CR1 =ADC1->CR1 |(1<<11);

	ADC_Cmd(ADC1, ENABLE);
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1));
	

	NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	//ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);


		
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	adc_gpio_init.GPIO_Mode = GPIO_Mode_IPD;
	adc_gpio_init.GPIO_Pin    = GPIO_Pin_11;
	adc_gpio_init.GPIO_Speed =GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &adc_gpio_init);

	/*EXTI Line Config*/
	EXTI_init_struct.EXTI_Line = EXTI_Line11;
	EXTI_init_struct.EXTI_LineCmd = ENABLE ;
	EXTI_init_struct.EXTI_Mode = EXTI_Mode_Event;
	EXTI_init_struct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_Init( &EXTI_init_struct);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource11);

    GPIO_PinRemapConfig(GPIO_Remap_ADC1_ETRGREG,ENABLE);
	
}


以上代码完成PA11 口上升沿触发ADC1 进行PA1,PA2,PA3口的输入ADC转换,并通过DAM搬运。
在这里插入图片描述
过程会比较复杂一点
外部GPIO PIN 电平发生变化,经过EXTI 产生事件,在芯片内部输出到ADC选通器,然后触发ADC转换
1.ADC 模块
需要配置外部触发,使能外部事件。
配置选通器,选择什么触发源

adc_init_struct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO;
ADC_ExternalTrigConvCmd(ADC1,ENABLE);

2.EXTI模块
配置EXTI line,配置事件输出,配置边沿触发,使能

	/*EXTI Line Config*/
	EXTI_init_struct.EXTI_Line = EXTI_Line11;
	EXTI_init_struct.EXTI_LineCmd = ENABLE ;
	EXTI_init_struct.EXTI_Mode = EXTI_Mode_Event;
	EXTI_init_struct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_Init( &EXTI_init_struct);

配置GPIO口作为外部输入引脚,打开复用时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
adc_gpio_init.GPIO_Mode = GPIO_Mode_IPD;
	adc_gpio_init.GPIO_Pin    = GPIO_Pin_11;
	adc_gpio_init.GPIO_Speed =GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &adc_gpio_init);

配置GPI输入到EXTI

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource11);

复用功能ReMap 作为ADC1的触发源

 GPIO_PinRemapConfig(GPIO_Remap_ADC1_ETRGREG,ENABLE);

外部触发ADC1 常规组多通道转换到此。

双ADC 同步触发

在上个例子的基础上使用ADC1,ADC2同步触发规则通道
也就是外部EXTI_Line11触发ADC1,ADC2同步开始规则通道的转换
在这里插入图片描述
这里的意思是
MASTER ADC1 需要设置为外部触发,SLAVE ADC2设置为软件触发。ADC1,ADC2外部触发使能,ADC Mode 为双ADC模式。

In dual mode, when configuring conversion to be triggered by an external event, the user
must set the trigger for the master only and set a software trigger for the slave to prevent
spurious triggers to start unwanted slave conversion. However, external triggers must be
enabled on both master and slave ADCs.
在这里插入图片描述
在这里插入图片描述
1.MASTER ADC1 外部触发源配置
2。MASTER ADC1 Dual mode ENABLE
3.trigger
4.SLAVE ADC2 外部触发使能,信号可以通过
代码如下

void dual_adc_init()
{
	ADC_InitTypeDef adc_init_struct1;
	ADC_InitTypeDef adc_init_struct2;
	GPIO_InitTypeDef adc_gpio_init;
	EXTI_InitTypeDef EXTI_init_struct;	

	


	/********ADC 2 init**********/
	
	adc2_pin_init();/*ADC2 channel GPIO init, 3 Channel*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2,ENABLE);
	ADC_DeInit(ADC2);
	
	adc_init_struct1.ADC_ContinuousConvMode =  DISABLE ;
	adc_init_struct1.ADC_DataAlign   =ADC_DataAlign_Right;
	adc_init_struct1.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	adc_init_struct1.ADC_Mode=ADC_Mode_RegSimult;
	adc_init_struct1.ADC_NbrOfChannel=3;
	adc_init_struct1.ADC_ScanConvMode=ENABLE ;
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC2, ADC_Channel_8, 1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC2, ADC_Channel_7, 3,ADC_SampleTime_55Cycles5);

	ADC_Init(ADC2,&adc_init_struct1);
	

	ADC_Cmd(ADC2, ENABLE);
	ADC_ResetCalibration(ADC2);
	while (ADC_GetResetCalibrationStatus(ADC2));
	ADC_StartCalibration(ADC2);
	while (ADC_GetCalibrationStatus(ADC2));

	ADC_ExternalTrigConvCmd(ADC2, ENABLE);





	/******ADC 1 Master init****/
	
	adc_pin_init_2();/*ADC1 channel GPIO init, 3 Channel*/
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	
	ADC_DeInit(ADC1);
	
	adc_init_struct2.ADC_ContinuousConvMode =  DISABLE ;
	adc_init_struct2.ADC_DataAlign   =ADC_DataAlign_Right;
	adc_init_struct2.ADC_ExternalTrigConv=ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO;
	adc_init_struct2.ADC_Mode=ADC_Mode_RegSimult;
	adc_init_struct2.ADC_NbrOfChannel=3;
	adc_init_struct2.ADC_ScanConvMode=ENABLE ;
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3,ADC_SampleTime_55Cycles5);

	ADC_ExternalTrigConvCmd(ADC1,ENABLE);

	ADC_DMACmd(ADC1,ENABLE);
	ADC_Init(ADC1,&adc_init_struct2);
	
	/*Discontinuous mode*/
	//ADC1->CR1 =ADC1->CR1 |(1<<11);

	ADC_Cmd(ADC1, ENABLE);
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1));
	

		
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	adc_gpio_init.GPIO_Mode = GPIO_Mode_IPD;
	adc_gpio_init.GPIO_Pin    = GPIO_Pin_11;
	adc_gpio_init.GPIO_Speed =GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &adc_gpio_init);

	/*EXTI Line Config*/
	EXTI_init_struct.EXTI_Line = EXTI_Line11;
	EXTI_init_struct.EXTI_LineCmd = ENABLE ;
	EXTI_init_struct.EXTI_Mode = EXTI_Mode_Event;
	EXTI_init_struct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_Init( &EXTI_init_struct);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource11);

    GPIO_PinRemapConfig(GPIO_Remap_ADC1_ETRGREG,ENABLE);

	




}
void ADC_dual_use_dma_init()
{

	DMA_InitTypeDef DMA_InitStruct;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	DMA_DeInit(DMA1_Channel1);


	DMA_InitStruct.DMA_BufferSize = 30;
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStruct.DMA_M2M =DMA_M2M_Disable;

	DMA_InitStruct.DMA_MemoryBaseAddr =ADC_ConvertedValueDual;
	
	DMA_InitStruct.DMA_MemoryDataSize =DMA_MemoryDataSize_Word;
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_Mode  =DMA_Mode_Circular;
	
	DMA_InitStruct.DMA_PeripheralBaseAddr = (unsigned int)(&ADC1->DR);
	DMA_InitStruct.DMA_PeripheralDataSize  = DMA_PeripheralDataSize_Word;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_Priority  = DMA_Priority_High;

	DMA_Init(DMA1_Channel1,& DMA_InitStruct);

	DMA_Cmd(DMA1_Channel1,ENABLE);


	

	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	//NVIC_Init(&NVIC_InitStructure);
	//DMA_ITConfig(DMA1,DMA_IT_TC,ENABLE);


}

小型示波器

示波器的采样使用单通道,CH1 PA1
STM32F103C8T6 最小系统板 Blue Pill
以下两部分代码完成ADC单通道初始化

电脑上位显示使用 SEGGER J-Scope ,用JLINK连接板子。
代码端需要SEGGER RTT代码,官网可下载

/**PA1 sample Pin*/
void adc_pin_init(void)
{


	GPIO_InitTypeDef adc_gpio_init;
	adc_gpio_init.GPIO_Mode = GPIO_Mode_AIN;
	adc_gpio_init.GPIO_Pin    = GPIO_Pin_1;
	adc_gpio_init.GPIO_Speed =GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &adc_gpio_init);

	/*ENABLE adc1 & clock PA Clock*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE );

}
void test_adc_init(void)
{
	ADC_InitTypeDef adc_init_struct;
	NVIC_InitTypeDef NVIC_InitStructure;

	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	ADC_DeInit(ADC1);
	
	adc_init_struct.ADC_ContinuousConvMode =  DISABLE ;
	adc_init_struct.ADC_DataAlign   =ADC_DataAlign_Right;
	adc_init_struct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	adc_init_struct.ADC_Mode=ADC_Mode_Independent;
	adc_init_struct.ADC_NbrOfChannel=1;
	adc_init_struct.ADC_ScanConvMode=DISABLE ;
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1,ADC_SampleTime_55Cycles5);

	
	ADC_Init(ADC1,&adc_init_struct);
	ADC_Cmd(ADC1, ENABLE);
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1));
	

	NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);
	ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);

	
}

中断函数

void ADC1_2_IRQHandler(void)
{	
   
	bit1 =~bit1;

	GPIO_WriteBit(GPIOC, GPIO_Pin_13,bit1 & 0x01);

	ADC_ConvertedValue[cnt] = ADC_GetConversionValue(ADC1);
	
	//SEGGER_RTT_printf(0,"cnt: %d, adc: %x \r\n",cnt,ADC_ConvertedValue[cnt]);

	SEGGER_RTT_Write(1, &ADC_ConvertedValue[cnt], 1);
	cnt++;
	if(cnt >=30) cnt=0;
	ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);

}

main中加入J SCOPE初始化代码

	uint8_t buf[2048];
	SEGGER_RTT_ConfigUpBuffer(1, "JScope_u2", buf, 2048, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

Task 中200ms触发一次ADC采样

	vTaskDelay(100);
	  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

示波器显示如下
在这里插入图片描述

9.ADC的误差分析

篇幅较大分篇描述
https://blog.csdn.net/denghuajing/article/details/122287931

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值