打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
stm32 AD模数转换[操作寄存器+库函数]-Changing's Blog
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 <stm32f10x_lib.h>	 #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 <stm32f10x_lib.h>		 #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 <stm32f10x_lib.h>	#define  ADC_1 0x01#define  ADC_2 0x02#define  ADC_3 0x03void 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_ONint fputc(int ch,FILE *f){	USART_SendData(USART1,(u8) ch);	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);	return ch;}#endif
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
STM32学习笔记之ADC
stm最简单例子
stm32串口程序(全)
STM32的MCO时钟功能输出
野火STM32 ADC(DMA模式)实验 详细分析
Stm32串口发送字符
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服