【STM32标准库】【基础知识】ADC转换,非DMA的单通道和多通道


文章基于适用于STM32F4系列,作者使用STM32F401CCU6开发板。
本文章基于此系列和开发板展开讨论。

ADC概述

A是指模拟信号,D是指数字信号,C是指变换

故名思意,ADC即将模拟信号转换为数字信号的一种变换。

STM32F4的ADC为逐次逼近型ADC
原理是利用DAC产生不同大小的电压,与输入的电压比较,得到输入电压的大体范围,这个范围的误差即为分辨率

如为8位分辨率的ADC,则分度值为3.3/2^8,即与真实值的误差不会超过12.89mV

具体原理请看微机原理

ADC初始化

流程

  1. GPIO初始化
  2. 打开时钟
  3. 全局ADC设置
  4. 单独ADC设置

GPIO初始化

之前文章中介绍过,传送门,需要设置为模拟模式
例程

GPIO_InitTypeDef GPIO_InitStruct;						//GPIO初始化结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);	//打开时钟
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;				//模拟模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;				//随意设置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;		//端口
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;			//随意设置
GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;			//高速
GPIO_Init(GPIOA, &GPIO_InitStruct);	

时钟

本系列单片机只有1个ADC,ADC1,端口对应请看这篇文章,传送门

使用这句命令打开ADC的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

全局ADC设置

这部分的设置是对全部的ADC有效的(本单片机即ADC1)

定义结构体

ADC_CommonInitTypeDef ADC_CommonInitStruct;

是否使用DMA

ADC_CommonInitStruct.ADC_DMAAccessMode

可以的选择使用或不使用,本文为不使用的情况。

ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;	   //不使用DMA

工作模式

可以选择外部触发源或者不使用触发
本文为不使用外部触发(独立工作模式)

ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;					   //独立工作模式

分频

ADC需要频率在12MHz以下,以AHB1的频率(与系统频率相等)为基准进行分频
本文选择8分频

ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;				   //分频,建议分频后小于12MHZ	

采样延迟

这个只有在多ADC时才有效,作为两个ADC采样之间的时间间隔

ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样延迟,多ADC使用才有效
	

例程

ADC_CommonInitTypeDef ADC_CommonInitStruct;
ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;	   //不使用DMA
ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;					   //独立工作模式
ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;				   //分频,建议分频后小于12MHZ
ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样延迟,多ADC使用才有效
ADC_CommonInit(&ADC_CommonInitStruct);

单独ADC设置

下面的设置是针对单独的ADC的

定义结构体

ADC_InitTypeDef ADC_InitStruct;

连续转换

将所有开启的通道采集转换完一轮后自动开启下一轮的采集转换,这是连续模式

ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;							 //连续模式

数据对齐

选择右对齐即可,其他基本上用不到

ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;						 //右对齐

外部触发

可以选择边沿触发或者定时器中断触发,或者不使用外部触发
这里选择的不使用外部触发

ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;		 //选择外部触发事件,这里随意即可,因为使用了不允许外部触发
	ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //软件触发

通道数量

即选择打开了几个通道

ADC_InitStruct.ADC_NbrOfConversion = 1;									 //通道数量
	

分辨率

即最大误差范围,这个数据的选择与后期计算电压值相关
可以是

分辨率分割数量
6bit64
8bit256
10bit1024
12bit4096
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;						 //分辨率

扫描模式

用于多通道扫描,完成全部打开的通道的扫描后中断标志置位
但是后接受的数据会被覆盖,需要使用DMA
这里是单通道(非DMA多通道是软件模拟的),因此此处设置与不设置均可

ADC_InitStruct.ADC_ScanConvMode = ENABLE;								 //扫描模式

例程

ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;							 //连续模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;						 //右对齐
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;		 //选择外部触发事件,这里随意即可,因为使用了不允许外部触发
ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //软件触发
ADC_InitStruct.ADC_NbrOfConversion = 1;									 //通道数量
ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;						 //分辨率
ADC_InitStruct.ADC_ScanConvMode = ENABLE;								 //扫描模式
ADC_Init(ADC1, &ADC_InitStruct);
ADC_Cmd(ADC1, ENABLE);

设置规则

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)

使用此函数设置对应规则
分别输入ADC号(本单片机只有ADC1),ADC通道号,标记号,采样时间

标记号只能小于等于通道数,从1开始,采样时间是会将这段时间内的数据平均后输出

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_144Cycles); //设置通道规则

打开软件调用ADC

如果使用软件读取ADC,需要事先调用此函数

ADC_SoftwareStartConv(ADC1);

需要在设置后才可以使用软件调用ADC
因此可以使用这段代码确保已经设置成功

	while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
		;

模拟多通道采集(非DMA)

思路

每次调用此读取ADC时,改变ADC对应的通道,设置成功后读取ADC并返回数据

函数

u16 ADC1_Read(u8 ch)
{
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_144Cycles); //设置通道规则
	ADC_SoftwareStartConv(ADC1);									 //开启软件触发
	while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
		;
	return ADC_GetConversionValue(ADC1); //软件触发
}

总例程

void ADC_init()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	ADC_InitTypeDef ADC_InitStruct;
	ADC_CommonInitTypeDef ADC_CommonInitStruct;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
	GPIO_Init(GPIOA, &GPIO_InitStruct);

	ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;	   //不使用DMA
	ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;					   //独立工作模式
	ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;				   //分频,建议分频后小于12MHZ
	ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样延迟,多ADC使用才有效
	ADC_CommonInit(&ADC_CommonInitStruct);

	ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;							 //连续模式
	ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;						 //右对齐
	ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;		 //选择外部触发事件,这里随意即可,因为使用了不允许外部触发
	ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //软件触发
	ADC_InitStruct.ADC_NbrOfConversion = 1;									 //通道数量
	ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;						 //分辨率
	ADC_InitStruct.ADC_ScanConvMode = ENABLE;								 //扫描模式
	ADC_Init(ADC1, &ADC_InitStruct);
	ADC_Cmd(ADC1, ENABLE);
}

u16 ADC1_Read(u8 ch)
{
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_144Cycles); //设置通道规则
	ADC_SoftwareStartConv(ADC1);									 //开启软件触发
	while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
		;
	return ADC_GetConversionValue(ADC1); //软件触发
}

需要读取时只需要调用此函数即可,更改通道号即可读取多通道数据,例如这样

int main()
{
	int i = 0;
	ADC_init();
	Usart_init();
	while (1)
	{
		i = 0;
		i = ADC1_Read(ADC_Channel_1);
		i = (i * 3300 / 0xfff);
		printf("ch1=%d mV \r\n", i);
		Delay_ms(300);
		i = 0;
		i = ADC1_Read(ADC_Channel_0);
		i = (i * 3300 / 0xfff);
		printf("ch0=%d mV \r\n", i);
		Delay_ms(300);
	}
}
  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值