stm32 AD模数转换[操作寄存器+库函数]

发布者:考古专家最新更新时间:2017-02-06 来源: eefocus关键字:stm32  AD模数转换  操作寄存器  库函数 手机看文章 扫描二维码
随时随地手机看文章

stm32f103最少有2个AD模数转换器,每个ADC都有18个通道,可以测量16个外部和2个内部模拟量。最大转换频率为1Mhz,也就是转换时间为1us(在 ADCCLK = 14Mhz,采样周期为1.5个时钟周期时)。最大时钟超过14Mhz,将导致ADC转换准确度降低。stm32的ADC是12位精度的。

 

stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。本例只使用规则通道实现独立模式的中断采样,这里不再赘述两种通道区别。

 

stm32的ADC可以由外部事件触发(例如定时器捕获,EXTI线)和软件触发(即在配置相关寄存器时,直接开启采样)。

 

本例实现AD采样PB0口,使用串口输出PB0口电压值。PB0口接变阻器以改变调整电压。

效果如下:

                                     ADValue = 1.39v

                                     ADValue = 1.38v

                                     ADValue = 1.40v

                                     ADValue = 1.38v

                                     ADValue = 1.39v

 


直接操作寄存器

 

首先需要配置ADC的时钟分频值,在RCC->CFGR的[15:14]位:

  • 00:PCLK2 2分频后作为ADC时钟         01:PCLK2 4分频后作为ADC时钟

  • 10:PCLK2 6分频后作为ADC时钟         11:PCLK2 8分频后作为ADC时钟

设定各通道的采样时间ADCx->SMPR,该寄存器给每个通道3位来选择8种采样周期:

  • 000:1.5周期               100:41.5周期

  • 001:7.5周期               101:55.5周期

  • 010:13.5周期             110:71.5周期

  • 011:28.5周期             111:239.5周期

采样时间算法为: (采样周期+12.5)/分频后的时钟

 

ADC采样得到的只是一个相对值,将 转换值/4096*参考电压 即可得到采样电压 这里的4096是因为stm32的adc为12位精度,表示参考电压时即为 2^12=4096

 

代码如下:  (system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置

User/main.c

#include 	 
#include "system.h" 
#include "usart.h"
#include "adc.h" 
#include "stdio.h"	

#define LED1 PAout(4)
#define LED2 PAout(5)

#define VREF 3.3		 //参考电压
void Gpio_Init(void);

int main(void)
{				  
	u16 ADValue;
	float temp;

	Rcc_Init(9); 			 //系统时钟设置
	Usart1_Init(72,9600);	//设置串口时钟和波特率

	Adc1_Init(8,7);	  //使用8通道采样,采样时间系数为7(111),据手册可得采样时间为 (239.5+12.5)/12= 21 (us)
	Gpio_Init();

	while(1){
	   	
		ADValue = Get_Adc(ADC_1,8);
		temp = (float)VREF*(ADValue/4096);	   //ADC精度为12位精度,即达到 VREF电压时为 2^12 = 4096

		printf("\r\n ADValue = %.2fv\r\n",temp);

		LED2 = !LED2;

		delay(100000);   //延时100ms

	}		
}


void Gpio_Init(void)
{
	RCC->APB2ENR|=1<<2;    //使能PORTA时钟 	
	RCC->APB2ENR|=1<<3;    //使能PORTB时钟 	
	   	 	  

	GPIOA->CRL&=0xFF0FFFF0; 
	GPIOA->CRL|=0xFF3FFFF0; // PA0设置为模拟输入,PA4推挽输出

	GPIOB->CRL&=0xFFFFFFF0; 
	GPIOB->CRL|=0xFFFFFFF0; // PB0设置为模拟输入

	
	//USART1 串口I/O设置

	GPIOA -> CRH&=0xFFFFF00F;   //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
	GPIOA -> CRH|=0x000008B0;	  
}

Library/src/adc.c

#include 		 
#include "adc.h"


//ADC1采样初始化
//独立工作模式
//参数说明:
//			ADC_CH_x    选择使用通道(0~17),目前暂支持0~15通道
//			ADC_CH_SMP 	设定采样周期(0~7)
//采样周期算法:

void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP)
{
	RCC -> APB2ENR |= 1<<9;   		//开启ADC1时钟
	RCC -> APB2RSTR |= 1<<9;  		//复位ADC1
	RCC -> APB2RSTR &= ~(1<<9);  	//ADC1复位结束

	RCC -> CFGR &= ~(3<<14);		//分频因子清零
	RCC -> CFGR |= 2<<14;			//设定分频因数为2,PCLK2 6分频后作为ADC时钟

	ADC1 -> CR1 &= 0xF0FFFF;		//工作模式清零
	ADC1 ->	CR1 |= 0<<16;			//设定为独立模式
	ADC1 -> CR1 &= ~(1<<8);			//非扫描工作模式
	ADC1 -> CR2 &= ~(1<<1);			//关闭连续转换

	ADC1 -> CR2 &= ~(7<<17);		//清除规则通道启动事件
	ADC1 -> CR2 |= 7<<17;			//设定规则通道启动事件为软件启动(SWSTART)

	ADC1 -> CR2 |= 1<<20;			//使用外部事件触发 SWSTART
	ADC1 -> CR2 &= ~(1<<11);		//设置对齐模式为右对齐

	ADC1 -> SQR1 &= ~(0xF<<20);		//清零规则序列的数量
	ADC1 -> SQR1 |= 15<<20;			//设置规则序列的数量为16

	ADC1 -> SMPR2 &= 0x00000000;	//清零通道采样时间
	ADC1 -> SMPR1 &= 0xFF000000;	

	if(ADC_CH_x <= 9 ){
		ADC1 -> SMPR2 |= 7<<(ADC_CH_x*3);			//设置通道x采样时间,提高采样时间可以提高采样精度 
	}

	if(ADC_CH_x > 9 ){
		ADC1 -> SMPR1 |= 7<<((ADC_CH_x-10)*3);		
	}
	

	ADC1 -> CR2 |= 1<<0;			//开启AD转换
	ADC1 -> CR2 |= 1<<3;			//使能复位校准,由硬件清零
	while((ADC1 -> CR2)& (1<<3));	//等待校准结束
	ADC1 -> CR2 |= 1<<2;			//开启AD校准,由硬件清零
	while((ADC1 -> CR2)& (1<<2));	//等待校准结束

}

//取得数模转换的值
//参数说明:(参数定义于adc.h)
//		 ADC_x  (0~3),选择数模转换器
//		 ADC_CH_x    (0~15),选择通道
u16 Get_Adc(u8 ADC_x,u8 ADC_CH_x)
{
	u16 data = 0;

	switch(ADC_x)	
	{
		case 1 : {

			ADC1 -> SQR3 &= 0xFFFFFFE0;	  		//清除通道选择
			ADC1 -> SQR3 |= ADC_CH_x;				//选择通道
			ADC1 -> CR2  |= 1<<22;				//开启AD转换
			while(!(ADC1 -> SR & 1<<1));			//等待转换结束

			data = ADC1->DR;
			break;
		}
		case 2 : {break;}
		case 3 : {break;}
	}

	return data;
}

Library/inc/adc.h

#include 	

#define  ADC_1 0x01
#define  ADC_2 0x02
#define  ADC_3 0x03

void Adc1_Init(u8 ADC_CH_x,u8 ADC_CH_SMP);
u16 Get_Adc(u8 ADC_x,u8 ADC_CH_x);

 

 

库函数操作

 

main.c

#include "stm32f10x.h"
#include "stdio.h"


#define	 PRINTF_ON  1
#define  VREF       3.3        // 参考电压


void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void ADC_Configuration(void);


int main(void)
{
	float ADValue = 0.00;
	u32 delayTime = 0;

  	RCC_Configuration();
  	GPIO_Configuration();
	USART_Configuration();
	ADC_Configuration();

	while(1)
	{
		if(delayTime++ >=2000000)
		{
			delayTime = 0;
			ADValue = VREF*ADC_GetConversionValue(ADC1)/0x0fff;
			printf("\r\n ADValue = %.2fv\r\n",ADValue);
		
		}
	}
}

  
void GPIO_Configuration(void)
{
  	GPIO_InitTypeDef GPIO_InitStructure;                                                                                     
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 


  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 

  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 
}

void ADC_Configuration(void)
{
	ADC_InitTypeDef ADC_InitStructure;	

	RCC_ADCCLKConfig(RCC_PCLK2_Div4);	//配置ADC时钟分频

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_55Cycles5);
	ADC_Cmd(ADC1,ENABLE);
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);


}


void RCC_Configuration(void)
{
	/* 定义枚举类型变量 HSEStartUpStatus */
	ErrorStatus HSEStartUpStatus;

  	/* 复位系统时钟设置*/
  	RCC_DeInit();
  	/* 开启HSE*/
  	RCC_HSEConfig(RCC_HSE_ON);
  	/* 等待HSE起振并稳定*/
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	/* 判断HSE起是否振成功,是则进入if()内部 */
  	if(HSEStartUpStatus == SUCCESS)
  	{
    	/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    	RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    	/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    	RCC_PCLK2Config(RCC_HCLK_Div1); 
    	/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    	RCC_PCLK1Config(RCC_HCLK_Div2);
    	/* 设置FLASH延时周期数为2 */
    	FLASH_SetLatency(FLASH_Latency_2);
    	/* 使能FLASH预取缓存 */
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    	/* 使能PLL */ 
    	RCC_PLLCmd(ENABLE);
    	/* 等待PLL输出稳定 */
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    	/* 选择SYSCLK时钟源为PLL */
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    	/* 等待PLL成为SYSCLK时钟源 */
    	while(RCC_GetSYSCLKSource() != 0x08);
  	} 
  	/* 打开APB2总线上的GPIOA时钟*/
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOB|RCC_APB2Periph_ADC1, ENABLE);

	//RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
		
}

 
void USART_Configuration(void)
{
	USART_InitTypeDef USART_InitStructure;
	USART_ClockInitTypeDef USART_ClockInitStructure;

	USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
	USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
	USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                      
	USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
	USART_ClockInit(USART1 , &USART_ClockInitStructure);

	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART_Init(USART1,&USART_InitStructure);

 	USART_Cmd(USART1,ENABLE);
}


#if	 PRINTF_ON

int fputc(int ch,FILE *f)
{
	USART_SendData(USART1,(u8) ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
	return ch;
}

#endif


关键字:stm32  AD模数转换  操作寄存器  库函数 引用地址:stm32 AD模数转换[操作寄存器+库函数]

上一篇:stm32 窗口看门狗[操作寄存器+库函数]
下一篇:stm32 BKP寄存器操作[操作寄存器+库函数]

推荐阅读最新更新时间:2024-03-16 15:32

STM32存储器和总线架构学习
要学习一个MCU,首先要学习这个芯片的架构,比如这个芯片是32bit 的RSIC V还是哈佛架构,对于STM32F4,它采用ARMv7-ME架构,是32位处理器,哈佛结构,三级流水线,Thumb-2指令集,扩展的DSP指令和SIMD指令,单周期MAC,可选的单精度FPU,可选的MPU,可选的Debug$trace接口 可配置的NVIC,可配置的WIC(wakeup interrupt controller) 3套AHB-Lite总线接口 主系统由 32 位多层 AHB 总线矩阵构成,可实现以下部分的互连: 八条主控总线: Cortex™-M4F 内核,Icode总线(I-bus,读取指令)Dcode总线(D-bus,访问数据)
[单片机]
<font color='red'>STM32</font>存储器和总线架构学习
STM32系统时钟修改
今天,公司丢了一个新的STM32控制板让我调试,一想还是很好调试。结果傻眼了,晶振是12MHZ啊。网上找了一些资料,现在整理一下,供大家参考使用。 具体步骤如下: 第一步,全局搜索HSE_VALUE #define HSE_VALUE ((uint32_t)8000000) /*! Value of the External oscillator in Hz */ 修改为: #define HSE_VALUE ((uint32_t)12000000) /*! Value of the External oscillator in Hz */ 第二步,打开system_stm32f10x.c,修改PLL参
[单片机]
STM32+按键调控PWM输出+串口输出占空比
GPIO.c #include STM32Lib\\stm32f10x.h #include hal.h /******************************************************************************* * Function Name : GPIO_Configuration * 设置PD3,PD4,PD5,PD6为键盘输入 * 设置PB0,5,8,9; PC5,7; PD7 ;PA8 为输出LED灯 *******************************************************************************/
[单片机]
STM32通过硬件SPI模块软件模拟驱动来进行拓展
FSMC一般只有STM32大容量产品才具备。因此在使用中小容量产品外接存储器时,一般会通过硬件SPI模块软件模拟驱动来进行拓展。 本文将以常见的 NOR Flash(多个厂家有对标的同类产品)为例。 我使用的是普亚的P25Q32SH,这个flash除了贵和多一些功能外,在基本控制方面和华邦的W25Q32差不多,基本指令通用。但不同flash之间还是存在一些差异,要注意适配。 一、封装 8引脚的spi Flash除了封装方式有些差异,引脚排列基本是一模一样的。 代码: 总的来说还是很简单的。因为时间比较赶,只求能用,存在代码冗余和效率较低的问题,欢迎改进指正! 复制 //*****************
[单片机]
<font color='red'>STM32</font>通过硬件SPI模块软件模拟驱动来进行拓展
STM8库函数开发手册(2) //中断、定时器4
第一部分 中断控制器库 itc.c 1.ITC_GetCPUCC() //读取CC寄存器 u8 u8_value;u8_value = ITC_GetCPUCC(); 2.ITC_DeInit() //恢复ITC相关寄存器到默认值 3. ITC_GetSoftIntStatus() //返回CC寄存器中的软件中断优先级位(I1,I0)的值 u8 u8_value;u8_value = ITC_GetSoftIntStatus(); 4. ITC_SetSoftwarePriority(IrqNum,PriorityValue) //设置指定中断源的软件优先级 其中IrqNum中断源定义: 其中Priori
[单片机]
STM8<font color='red'>库函数</font>开发手册(2) //中断、定时器4
stm32中i2c之学习浅谈
首先介绍下自己的学习背景,博主本人是在上周刚入门stm32并且学习gpio口基本用法和中断的介绍。在这样的知识储备下我开始学习I2c通信协议,并尝试编写了师兄布置的一个小任务。 1.1. I2C总线物理结构 首先介绍下i2c通信协议,从物理层上来看这是一种非常简洁明了的通信协议。本身一共就两条总线,一条SCL(时钟总线),一条SDA(数据总线)。通信原理是通过对SCL和SDA线高低电平时序的控制,来 产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。硬件图如下: 1.2 I2C总线特征 I2C总线上的每一个设备都可以作为主设备或
[单片机]
<font color='red'>stm32</font>中i2c之学习浅谈
基于STM32+华为云IOT设计的云平台监控系统
1. 前言 智能系统作为新兴产业,是国家重点发展产业之一。国外自美国在1984年建设出真正的智能建筑至今为止已经有30多年的历史了。由于智能家居的安全、高效、便捷以,及智能化等独特魅力,使智能家居行业在2003年逐步进入我国的家居市场,但是由于我国起步晚,使得智能家居行业标准在我国家居市场上并不统一。随着全球资源的不断减少以及环境的破坏,“节能环保”成为了全球科技发展的首要要求,而“全球智能化”在节能环保方面的优势使得智能家居成为了发展主流,因此具有十分广阔的市场前景。同时,随着科技的进步,传统建筑已经不能满足现代人对生活方面舒适度的需求,而智能系统也在不知不觉间渗入我们的生活。 智能家居(smart home)以住宅为平台,
[单片机]
基于STM32+华为云IOT设计的云平台监控系统
STM32为什么需要位带操作呢?
为什么需要位带操作? 因为编程需要操作某个bit位来达到我们想要的功能,比如点灯需要操作GPIOA- ODR 的某个bit假设是第2bit,写1就可以让GPIO输出一个高电平。 GPIOA- ODR |= 1 2; 这样写其实有三个隐含的操作: //1.读取ODR寄存器的值到内存//2.改写第2bit的值//3.再把改写后的值写进ODR寄存器 这样的缺点:效率低 位带操作就是为了解决这个问题,前提是硬件支持这么做。 位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。51 单片机中通过关键字 sbit 来实现位定义,STM32没有这样的关键字,而是通过访问位带别名区来实现,例如 sbit LED P1^2LE
[单片机]
<font color='red'>STM32</font>为什么需要位带<font color='red'>操作</font>呢?
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved