打印
[应用相关]

stm32 AD模数转换[操作寄存器+库函数]-Changing's Blog

[复制链接]
1152|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
突然下起雨|  楼主 | 2021-7-30 23:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
stm32f103最少有2个AD模数转换器,每个ADC都有18个通道,可以测量16个外部和2个内部模拟量。最大转换频率为1Mhz,也就是转换时间为1us(在 ADCCLK = 14Mhz,采样周期为1.5个时钟周期时)。最大时钟超过14Mhz,将导致ADC转换准确度降低。stm32的ADC是12位精度的。
stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。本例只使用规则通道实现独立模式的中断采样,这里不再赘述两种通道区别。
stm32的ADC可以由外部事件触发(例如定时器捕获,EXTI线)和软件触发(即在配置相关寄存器时,直接开启采样)。

使用特权

评论回复
沙发
突然下起雨|  楼主 | 2021-7-30 23:29 | 只看该作者
本例实现AD采样PB0口,使用串口输出PB0口电压值。PB0口接变阻器以改变调整电压。
效果如下:
                                     ADValue = 1.39v
                                     ADValue = 1.38v
                                     ADValue = 1.40v
                                     ADValue = 1.38v
                                     ADValue = 1.39v

使用特权

评论回复
板凳
突然下起雨|  楼主 | 2021-7-30 23:32 | 只看该作者
直接操作寄存器
首先需要配置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

使用特权

评论回复
地板
突然下起雨|  楼主 | 2021-7-30 23:33 | 只看该作者
代码如下:  (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

        }               
}

使用特权

评论回复
5
突然下起雨|  楼主 | 2021-7-30 23:34 | 只看该作者
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);                        //

使用特权

评论回复
6
突然下起雨|  楼主 | 2021-7-30 23:35 | 只看该作者
设置通道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 0x03

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

使用特权

评论回复
7
突然下起雨|  楼主 | 2021-7-30 23:35 | 只看该作者
库函数操作
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);
               
                }
        }
}

使用特权

评论回复
8
突然下起雨|  楼主 | 2021-7-30 23:36 | 只看该作者
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);


}

使用特权

评论回复
9
突然下起雨|  楼主 | 2021-7-30 23:36 | 只看该作者
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);
               
}

使用特权

评论回复
10
突然下起雨|  楼主 | 2021-7-30 23:37 | 只看该作者
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);
}

使用特权

评论回复
11
突然下起雨|  楼主 | 2021-7-30 23:37 | 只看该作者
#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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

38

主题

284

帖子

1

粉丝