STM32学习100步之第六十五-六十八步——ADC

ADC

在单片机等硬件电路中,数据是以二进制数存储的(因为各种寄存器的位数决定了存储的方式),外界的模拟量(连续量无法存储在单片机中),对于单片机的各个端口只存在输出数字逻辑电平0、1,一般对应于模拟量就是0V和供电电压,如何将连续的电压值存储呢?这就用到了量化的概念,将参考电压(有时就是电源电压)在相应的ADC转换结果寄存器中所有值都设置成1,而零点电压全部设置成0,参考电压和GND电压按线性比例平均下来便可将二者之间的值存储在相应的寄存器中,便达到了AD转换,但是需要注意的是,转换之后的结果是一个二进制数并不是实际的电压值,比如ADC转换结果寄存器是8位,参考电压值是5V,(在转换结果存储器中存的就是0b11111111,即十进制数就是1024),这时若在输入通道输入了2.5V的电压,这时在ADC转换结果寄存器中存的就0b01111111,即十进制数就是512,若要转换为实际的电压值,可以将寄存器存储的值读取出来,除以1024,再乘5即可。具体采样的参考电压值的范围需要根据单片机来确定,比如STM32F103的参考电压就采用了电源电压3.3V,因此取样的范围便是0-3.3V。AD模块中模拟量转化为数字量的用处在于:取样外界的电压值,电压值是连续的,无法在单片机存储,因此,须将采集进来的电压值转换为数字量存储在单片机中,在转换存储的过程中存在一定的误差,可以增加转换器的位数来提高精度为目的。

STM32F103中的ADC转换模块

ADC基本特性
STM32F103单片机有2个模数转换器- – ADC
每个ADC为12位分辨率(读出数据的位长度)
2个ADC共用16个外部通道(单片机的ADC输入引脚)
2个ADC都可使用DMA进行操作

其中采用DMA转换的模式效率更高,这种方式下,ADC将转换的结果直接通过DMA控制器存储在SRAM中,单片机需要用数据的时候直接从SRAM中读出,从而单片机不必一直等待转换是否结束,再来读取,这个任务交给DMA来完成,DMA不断从ADC转换结果中读取数据,放到SRAM中。
在这里插入图片描述
本次例子

下面是DMA的初始化函数:

vu16 ADC_DMA_IN[4]; //ADC数值存放的变量
#define ADC1_DR_Address    ((uint32_t)0x4001244C) //ADC1这个外设的地址(查参考手册得出)

void ADC_DMA_Init(void){ //DMA初始化设置
	DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体
	DMA_DeInit(DMA1_Channel1);//复位DMA通道1
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_Address
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址
	DMA_InitStructure.DMA_BufferSize = 4;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 当前存储器地址:Disable不变,Enable递增(用于多通道采集)
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1
	DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}

需要注意的是,要告诉DMA从哪里读出数据,存放到哪里,以及DMA一次性读取多少转换结果,即如果有多个电压输入的时候,分别存储起来,前面的声明一已经给出从哪里读,要存在哪里。

下面是对应的参数修改:

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA从这个寄存器中读取转换结果
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
DMA_InitStructure.DMA_BufferSize = 4;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)

ADC_DMA_IN是DMA要将转换的结果存在该数组中,根据前面的DMA一次要读取多少模拟量设置该数组有多少个单元即可。这里取样了4个电压值,因此前面设置为4。

ADC初始化函数:

void ADC_Configuration(void){ //初始化设置
	ADC_InitTypeDef ADC_InitStructure;//定义ADC初始化结构体变量
	ADC_GPIO_Init();//GPIO初始化设置
	ADC_DMA_Init();//DMA初始化设置
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有软件控制转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 4;//!!!顺序进行规则转换的ADC通道的数目(根据ADC采集通道数量修改)
	ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC1,ADC通道x,规则采样顺序值为y,采样时间为28周期		 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 4, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	

	ADC_DMACmd(ADC1, ENABLE);// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
	ADC_Cmd(ADC1, ENABLE);//使能ADC1
	ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成
	ADC_StartCalibration(ADC1);//开始ADC1校准
	while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件开始转换
}

当一次需要利用ADC读取多个采样电压时,通过

ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期

决定从哪个输入通道采取以及采取的优先级。
下面的函数与采样4个电压值相对应

ADC_InitStructure.ADC_NbrOfChannel = 4;//!!!顺序进行规则转换的ADC通道的数目(根据ADC采集通道数量修改)

利用语句

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式

使得ADC转换模式一直转换。
另外一旦DMA初始化,它就不断的从ADC转换结果中读取采样值,存放在要存放的存储器,从而在主函数中CPU不用一直等待转换结果是否结束,到需要数据的时候读取即可。

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	ADC_Configuration(); //ADC初始化设置(模拟摇杆的ADC初始化)
	JoyStick_Init(); //模拟摇杆的按键初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	

	
	OLED_DISPLAY_8x16_BUFFER(0,"          :"); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"          :"); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(4,"          x:"); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6,"          y:"); //显示字符串
	
	OLED_DISPLAY_16x16(0,1*16,0);//汉字显示	 光敏调节
  OLED_DISPLAY_16x16(0,2*16,1);
  OLED_DISPLAY_16x16(0,3*16,2);
  OLED_DISPLAY_16x16(0,4*16,3);
	
	OLED_DISPLAY_16x16(2,1*16,4);//汉字显示	 滑变调节
  OLED_DISPLAY_16x16(2,2*16,5);
  OLED_DISPLAY_16x16(2,3*16,2);
  OLED_DISPLAY_16x16(2,4*16,3);
	
	OLED_DISPLAY_16x16(4,1*16,6);//汉字显示	 模拟摇杆x
  OLED_DISPLAY_16x16(4,2*16,7);
  OLED_DISPLAY_16x16(4,3*16,8);
  OLED_DISPLAY_16x16(4,4*16,9);
	
	OLED_DISPLAY_16x16(6,1*16,6);//汉字显示	 模拟摇杆y
  OLED_DISPLAY_16x16(6,2*16,7);
  OLED_DISPLAY_16x16(6,3*16,8);
  OLED_DISPLAY_16x16(6,4*16,9);
	


	while(1){
		
		
		//将光敏电阻的ADC数据显示在OLED上
		OLED_DISPLAY_8x16(4,11*8,ADC_DMA_IN[0]/1000+0x30);//
		OLED_DISPLAY_8x16(4,12*8,ADC_DMA_IN[0]%1000/100+0x30);//
		OLED_DISPLAY_8x16(4,13*8,ADC_DMA_IN[0]%100/10+0x30);//
		OLED_DISPLAY_8x16(4,14*8,ADC_DMA_IN[0]%10+0x30);//

		OLED_DISPLAY_8x16(6,11*8,ADC_DMA_IN[1]/1000+0x30);//
		OLED_DISPLAY_8x16(6,12*8,ADC_DMA_IN[1]%1000/100+0x30);//
		OLED_DISPLAY_8x16(6,13*8,ADC_DMA_IN[1]%100/10+0x30);//
		OLED_DISPLAY_8x16(6,14*8,ADC_DMA_IN[1]%10+0x30);//
		
		OLED_DISPLAY_8x16(2,11*8,ADC_DMA_IN[2]/1000+0x30);//
		OLED_DISPLAY_8x16(2,12*8,ADC_DMA_IN[2]%1000/100+0x30);//
		OLED_DISPLAY_8x16(2,13*8,ADC_DMA_IN[2]%100/10+0x30);//
		OLED_DISPLAY_8x16(2,14*8,ADC_DMA_IN[2]%10+0x30);//
		
		OLED_DISPLAY_8x16(0,11*8,ADC_DMA_IN[3]/1000+0x30);//
		OLED_DISPLAY_8x16(0,12*8,ADC_DMA_IN[3]%1000/100+0x30);//
		OLED_DISPLAY_8x16(0,13*8,ADC_DMA_IN[3]%100/10+0x30);//
		OLED_DISPLAY_8x16(0,14*8,ADC_DMA_IN[3]%10+0x30);//
		
		

		if(GPIO_ReadInputDataBit(JoyStickPORT,JoyStick_KEY)==0){
			OLED_DISPLAY_8x16(0,0,'Y');//
		}else{
			OLED_DISPLAY_8x16(0,0,' ');//
		}
		delay_ms(200); //延时
	}
}

主函数的while用于显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值