[应用相关] 通过内部VREF得到实际的VDDA值

[复制链接]
1765|2
 楼主| deviceplugs 发表于 2016-5-16 22:54 | 显示全部楼层 |阅读模式
使用STM32 ADC功能测试外部电压,在一些精度不高的场合,我们一般就用3.3V作为参考电压来计算测到的电压值。不过,这种情况很少见,可能只有单片机学习板才会这样使用。因为我们使用的3.3V稳压芯片,很少有标准的3.300V输出,有可能是3.270V,也可能是3.345V,而且,还存在个体差异,这个板子上的电压是3.294V,另外一个板子上面,就可能是3.312V。如果我们都用3.300来计算的话,同样的电压,测出来的结果就会存在mV级别的差异。
在实际使用中,我们一般使用外部基准电压芯片,例如,100脚的STM32一般都有VREF引脚,就是用来接外部基准电压芯片,例如REF3133,输出的电压是标准的3.300V。
为什么不用基准电压芯片作为电源供电芯片?
因为基准电压芯片的输出电流都是小于25mA的,对于一般的电路板应用,这么点输出电流不足以让电路板工作。
不同的STM32 如何使用基准电压芯片?
对于100脚及其以上的芯片,把基准电压芯片连接到VREF芯片即可。
对于100脚以下的芯片,STM32没有把VREF引脚引出来,所以,我们只能把基准电压芯片连接到VDDA引脚。注意,STM32单片机上面有好多电源引脚,其中有若干VDD引脚,只有一个VDDA引脚,VDDA引脚就是模拟供电引脚。不过,需要注意,VDDA的电压不是随便定义的。例如,STM32F051系列单片机就规定,VDDA必须要大于或者等于VDD才可以正常工作,所以这时候,最好是给单片机3.0V供电,再给VDDA采用一个3.3V的基准电压芯片供电。

不用电压基准电压芯片可以吗?
如果在你的应用中,VDDA引脚和VDD引脚连在一起,都是由电源芯片供电,这时候,如果你能知道VDDA的实际电压,也可以得到精确的ADC结果。例如,你可以用万用表测到VDDA电压,例如,是3.286V,你就可以使用3286来计算。不过,这也只能是在实验的时候,在实际使用中,如果你做了1万个板子,然后需要用万用表量1万个板子的电源电压,然后再给每个板子修改程序,显示是不可能的。
所以,STM32给我们又一个解决方案,STM32在内部都有一个参考电压引脚,可以通过配置,把这个脚连接到ADC输入引脚,是内部连接。然后再计算实际的VDDA值。不过,STM32也存在个体差异,所以,它并没有在手册上给出我们这个参考电压是多大。而是用出厂时调教好的校准值和得到的参考电压值一起使用。如下图:

VREFINT_CAL是校准值,这个值,每个单片机都不一样,被存储到了0X1FFFF7BA和0X1FFFF7BB,使用的时候,需要先读出来。
说多了都是眼泪,不说了,给你个程序看吧,程序当中,有读取VREFINT_CAL和VREFINT_DATA值的语句。你可以看个究竟。这个程序已经实践验证过了,不会有问题,下面是实验结果:

我把测到的VDDA值,发送到了串口查看。然后我用万用表测了一下实际的VDDA值,由于我的万用表精度有限,只能测到小数点后2位数字,万用表得到的是3.32V和3.31V之间跳动。串口发送的值,小数点后第三位四舍五入之后,结果也是3.32V和3.31V。结果是一致的。说明这种方法也是可取的。

 楼主| deviceplugs 发表于 2016-5-16 22:54 | 显示全部楼层
  1. /****************************************************/
  2. // 程序用途:用来测试通过内部基准电压计算外部VDDA的值
  3. // 程序作者:孟瑞生
  4. // 微信公众号:科技老顽童
  5. /****************************************************/
  6. #include "stm32f0xx.h"
  7. #include "stdio.h"

  8. __IO uint16_t VREFINT_CAL;
  9. __IO uint16_t VREFINT_DATA;
  10. __IO float VDDA_VAL;

  11. // 下面三个延时函数,是用示波器试出来的,非精确延时(48MHz)
  12. void delay_1us(void)
  13. {
  14.         volatile uint16_t i=1;
  15.         
  16.         while(i--);        
  17. }

  18. void delay_us(uint16_t us)
  19. {
  20.         while(us--)
  21.         {
  22.                 delay_1us();
  23.         }
  24. }

  25. void delay_ms(uint16_t ms)
  26. {
  27.         while(ms--)
  28.         {
  29.                 delay_us(995);
  30.         }
  31. }

  32. /**
  33.   * [url=home.php?mod=space&uid=247401]@brief[/url]  ADC Configuration
  34.   * @param  None
  35.   * @retval None
  36.   */
  37. static void ADC_Config(void)
  38. {
  39.   ADC_InitTypeDef     ADC_InitStructure;
  40.   GPIO_InitTypeDef    GPIO_InitStructure;
  41.   
  42.   /* GPIOC Periph clock enable */
  43.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  44.   
  45.   /* ADC1 Periph clock enable */
  46.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  47.   
  48.   /* Configure ADC Channel 0 as analog input */
  49.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  50.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  51.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  52.   GPIO_Init(GPIOA, &GPIO_InitStructure);
  53.   
  54.   /* ADCs DeInit */  
  55.   ADC_DeInit(ADC1);
  56.   
  57.   /* Initialize ADC structure */
  58.   ADC_StructInit(&ADC_InitStructure);
  59.         ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  60.         ADC_Init(ADC1, &ADC_InitStructure);
  61.         
  62.         /* Convert the ADC1 Channel 0 with 239.5 Cycles as sampling time */
  63.   ADC_ChannelConfig(ADC1, ADC_Channel_Vrefint , ADC_SampleTime_239_5Cycles);
  64.         ADC_VrefintCmd(ENABLE);
  65.         
  66.   /* 得到基准电压校准值 */
  67.         VREFINT_CAL = *(__IO uint16_t *)(0X1FFFF7BA);
  68.         
  69.         /* ADC Calibration */
  70.   ADC_GetCalibrationFactor(ADC1);
  71.         
  72.         /* Enable the ADC peripheral */
  73.   ADC_Cmd(ADC1, ENABLE);     
  74.   
  75.   /* Wait the ADRDY flag */
  76.   while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY));
  77.   
  78.   /* ADC1 regular Software Start Conv */
  79.   ADC_StartOfConversion(ADC1);
  80. }

  81. /**
  82.   * @brief  UART1 Configuration
  83.   * @param  None
  84.   * @retval None
  85.   */
  86. void UART1_Init()
  87. {
  88.         GPIO_InitTypeDef GPIO_InitStructure;
  89.         NVIC_InitTypeDef NVIC_InitStructure;
  90.         USART_InitTypeDef USART_InitStructure;
  91.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  92.         /* Enable USART1 Clock */
  93.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  94.         
  95.         /* USART1 Pins configuration ************************************************/
  96.   /* Connect pin to Periph */
  97.   GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
  98.   GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);   
  99.   
  100.   /* Configure pins as AF pushpull */
  101.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
  102.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  103.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  104.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  105.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  106.   GPIO_Init(GPIOA, &GPIO_InitStructure);

  107.         /* USART1 IRQ Channel configuration */
  108.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  109.   NVIC_InitStructure.NVIC_IRQChannelPriority = 0x01;
  110.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  111.   NVIC_Init(&NVIC_InitStructure);        
  112.         
  113.         /* USART1  configured as follow:
  114.   - BaudRate = 9600 baud  
  115.   - Word Length = 8 Bits
  116.   - One Stop Bit
  117.   - No parity
  118.   - Hardware flow control disabled (RTS and CTS signals)
  119.   - Receive and transmit enabled
  120.   */
  121.   USART_InitStructure.USART_BaudRate = 115200;
  122.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  123.   USART_InitStructure.USART_StopBits = USART_StopBits_1;
  124.   USART_InitStructure.USART_Parity = USART_Parity_No;
  125.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  126.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  127.         USART_Init(USART1, &USART_InitStructure);
  128.    
  129.         /* Enable the USART1 */
  130.         USART_Cmd(USART1, ENABLE);
  131. }

  132. int fputc(int ch, FILE *f)
  133. {
  134.         /* Place your implementation of fputc here */
  135.   /* e.g. write a character to the USART */
  136.   USART_SendData(USART1, (uint8_t) ch);

  137.   /* Loop until transmit data register is empty */
  138.   while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

  139.   return ch;
  140. }

  141. // 主函数
  142. int main(void)
  143. {
  144.         delay_ms(200);
  145.         ADC_Config();
  146.         UART1_Init();
  147.         
  148.         while(1)
  149.         {
  150.                 VREFINT_DATA =ADC_GetConversionValue(ADC1);
  151.                 VDDA_VAL = (3.3*VREFINT_CAL)/VREFINT_DATA;
  152.                 printf("\n\rVDDA:%.3fV\n\r",VDDA_VAL);
  153.                 while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
  154.                 delay_ms(1000);
  155.         }
  156. }



734774645 发表于 2016-5-16 23:42 | 显示全部楼层
基准电压的目的不是用来驱动设备的,而是提供参考的,也就是说,只可参考,不可驱动。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

135

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部