STM32的ADC实验(HAL库开发)

        说到ADC大家都知道是模拟量转为数字量,也就是将模拟的量转为电压的变化,但是你有没有想过我们得到的这个电压有什么用,ADC又有何用?其实得到的电压值可以用过算法结合其他模块的传感器转为实际的变量,例如浓度、温度等等,都可以得到自己想要的,也可以用到我们的生活中,这就是ADC的强大之处。下面我来给大家说一下基于STM32F1的ADC实验(HAL库开发)。

        ADC的转化有个过程,那就是采样——保持——量化——编码。

        STM32 ADC 可以进行很多种不同的转换模式,在单次转换模式下,只执行一次转换,该模式可以通过 ADC_CR2 寄存 器的 ADON 位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和 注入通道),这是 CONT 位为 0。 以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在 ADC_DR 寄存器 中,EOC(转换结束)标志将被置位,如果设置了 EOCIE,则会产生中断。然后 ADC 将停止,直到下次启动。

       下面介绍一下我们执行规则通道的单次转换,需要用到的 ADC 寄存器。第一 个要介绍的是 ADC 控制寄存器(ADC_CR1 ADC_CR2)。ADC_CR1 的各位描述如图

        ADC_CR1 的 SCAN 位,该位用于设置扫描模式,由软件设置和清除,如果设置为 1
则使用扫描模式,如果为 0 ,则关闭扫描模式。在扫描模式下,由 ADC_SQRx
ADC_JSQRx 寄存器选中的通道被转换。如果设置了 EOCIE JEOCIE ,只在最后一个通道
转换完毕后才会产生 EOC JEOC 中断。 ADC_CR1[19: 16] 用于设置 ADC 的操作模式,详细的对应关系如图
我们要使用的是独立模式,所以设置这几位为 0 就可以了。接着我们介绍
ADC_CR2 ,该寄存器的各位描述如图
        该寄存器我们也只针对性的介绍一些位:ADON 位用于开关 AD 转换器。而 CONT
用于设置是否进行连续转换,我们使用单次转换,所以 CONT 位必须为 0 CAL
RSTCAL 用于 AD 校准。 ALIGN 用于设置数据对齐,我们使用右对齐,该位设置为 0
EXTSEL[2 0] 用于选择启动规则转换组转换的外部事件,详细的设置关系如图
        我们这里使用的是软件触发(SWSTART ),所以设置这 3 个位为 111 ADC_CR2
SWSTART 位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写
1 AWDEN 为用于使能温度传感器和 Vrefint.
        第二个要介绍的是 ADC 采样事件寄存器( ADC_SMPR1 ADC_SMPR2 ),这两个寄
存器用于设置通道 0~17 的采样时间,每个通道占用 3 个位。 ADC_SMPR1 的各位描述如图
ADC_SMPR2 的各位描述如下图
        对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样
会降低 ADC 的转换速率。 ADC 的转换时间可以由以下公式计算:
                                        Tcovn=采样时间+12.5 个周期
        其中:Tcovn 为总转换时间,采样时间是根据每个通道的 SMP 位的设置来决定的。例
如,当 ADCCLK=14Mhz 的 时 候 , 并 设 置 1.5 个 周 期 的 采 样 时 间 , 则 得 到 :
                                         Tcovn=1.5+12.5=14 个周期=1us。
        第三个要介绍的是 ADC 规则序列寄存器( ADC_SQR1~3 , 该寄存器总共有 3 个,这几
个寄存器的功能都差不多,这里我们仅介绍一下 ADC_SQR1 ,该寄存器的各位描述如图
L[3 0] 用于存储规则序列的长度,我们这里只用了 1 个,所以设置这几个位的值为
0 。其他的 SQ13~16 则存储了规则序列中第 13~16 个通道的编号( 0~17)。我们选择的
是单次转换,所以只有一个通道在规则序列里面,这个序列就是 SQ1 ,通过 ADC_SQR3
最低 5 位(也就是 SQ1 )设置。
        第四个要介绍的是 ADC 规则数据寄存器 (ADC_DR) 。规则序列中的 AD 转化结果都将
被存在这个寄存器里面,而注入通道的转换结果被保存在 ADC_JDRx 里面。 ADC_DR 的各
位描述如图
        这里要提醒一点的是,该寄存器的数据可以通过 ADC_CR2 ALIGN 位设置左对齐还
是右对齐。在读取数据的时候要注意。
        最后一个要介绍的 ADC 寄存器为 ADC 状态寄存器( ADC_SR ),该寄存器保存了 ADC
转换时的各种状态。该寄存器的各位描述如图
这里我们要用到的是 EOC 位,我们通过判断该位来决定是否此次规则通道的 AD 转换
已经完成,如果完成我们就从 ADC_DR 中读取转换结果,否则等待转换完成。
通过以上寄存器的介绍,我们了解了 STM32 的单次转换模式下的相关设置,下面我们
介绍使用库函数的函数来设定使用 ADC1 的通道 1 进行 AD 转换。
        
硬件设计
         ADC 属于 STM32F1 内部资源,实际上我们只需要软件设置 就可以正常工作,不过我们需要在外部连接其端口到被测电压上面。本次,我们通过ADC1 的通道 9 PB1 )来读取外部电压值, 电位器是连接电源 3.3V 的,而调节端是连接到 ADC 采样端,通过调节电位器,实现采集 0~3.3V区域间的电压。 在测试前需要将多功能端口 P3 VR ADC 用跳线帽进行连接(与 PB9 连接)。当然你可以用杜邦线一头插在 P3 ADC 插针上(与 PB9 连接),另外一头就接你要测试的电压点(确保该电压不大于 3.3V 即可)。
软件设计
        建立adc.c文件,如下
ADC_HandleTypeDef ADC1_Handler; //ADC 句柄
//初始化 ADC
//这里我们仅以规则通道为例
//我们默认将开启通道 0~3
void Adc_Init(void)
{ 
    RCC_PeriphCLKInitTypeDef ADC_CLKInit;
    ADC_CLKInit.PeriphClockSelection=RCC_PERIPHCLK_ADC;//ADC 外设时钟
    ADC_CLKInit.AdcClockSelection=RCC_ADCPCLK2_DIV6;
    //分频因子 6 时钟为 72M/6=12MHz
    HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit); //设置 ADC 时钟
    ADC1_Handler.Instance=ADC1;
    ADC1_Handler.Init.DataAlign=ADC_DATAALIGN_RIGHT; //右对齐
    ADC1_Handler.Init.ScanConvMode=DISABLE; //非扫描模式
    ADC1_Handler.Init.ContinuousConvMode=DISABLE; //关闭连续转换
    ADC1_Handler.Init.NbrOfConversion=1; 
    //1 个转换在规则序列中也就是只转换规则序1 
    ADC1_Handler.Init.DiscontinuousConvMode=DISABLE; //禁止不连续采样模式
    ADC1_Handler.Init.NbrOfDiscConversion=0; //不连续采样通道数为 0
    ADC1_Handler.Init.ExternalTrigConv=ADC_SOFTWARE_START; //软件触发
    HAL_ADC_Init(&ADC1_Handler); //初始化
    HAL_ADCEx_Calibration_Start(&ADC1_Handler); //校准 ADC
}
//ADC 底层驱动,引脚配置,时钟使能
//此函数会被 HAL_ADC_Init()调用
//hadc:ADC 句柄
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
     GPIO_InitTypeDef GPIO_Initure;
     __HAL_RCC_ADC1_CLK_ENABLE(); //使能 ADC1 时钟
     __HAL_RCC_GPIOB_CLK_ENABLE(); //开启 GPIOB 时钟
     GPIO_Initure.Pin=GPIO_PIN_1; //PB1
     GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟
     GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉
     HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}
//获得 ADC 值
//ch:通道值 0~3
//返回值:转换结果
u16 Get_Adc(u32 ch) 
{
     ADC_ChannelConfTypeDef ADC1_ChanConf;
 
     ADC1_ChanConf.Channel=ch; //通道
     ADC1_ChanConf.Rank=1; //第 1 个序列,序列 1
     ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_239CYCLES_5;//采样时间 
     HAL_ADC_ConfigChannel(&ADC1_Handler,&ADC1_ChanConf); //通道配置
     HAL_ADC_Start(&ADC1_Handler); //开启 ADC
     HAL_ADC_PollForConversion(&ADC1_Handler,10); //轮询转换
    return (u16)HAL_ADC_GetValue(&ADC1_Handler);
    //返回最近一次 ADC1 规则组的转换结果
}
//获取指定通道的转换值,取 times 次,然后平均
//times:获取次数
//返回值:通道 ch 的 times 次转换结果平均值
u16 Get_Adc_Average(u32 ch,u8 times)
{
    u32 temp_val=0;
    u8 t;
    for(t=0;t<times;t++)
    {
        temp_val+=Get_Adc(ch);
        // delay_ms(5);
    }
    return temp_val/times;
} 
此部分代码就 4 个函数,Adc_Init 函数用于初始化 ADC1 相关参数。第二个函数
HAL_ADC_MspInit 是 MSP 初始化回调函数,用来使能时钟和初始化 IO 口,第三个函数
Get_Adc,用于读取某个通道的 ADC 值,例如我们读取通道 9 上的 ADC 值,就可以通过
Get_Adc(ADC_CHANNEL_9)得到。最后一个函数 Get_Adc_Average,用于多次获取 ADC
值,取平均,用来提高准确度。

下面看一下main.c

// 共阴数字数组
// 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, .,全灭
u8 smg_num[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6,0xee,
0x3e,0x9c,0x7a,0x9e,0x8e,0x01,0x00}; 
int main(void)
{
     HAL_Init(); //初始化 HAL 库 
     Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M
     delay_init(72); //初始化延时函数
    uart_init(9600); //串口初始化为 9600
    LED_Init(); //初始化与 LED 连接的硬件接口
     LED_SMG_Init(); //数码管初始化
    Adc_Init(); //ADC 初始化 
     TIM3_Init(19,7199); //数码管 2ms 定时显示
    while(1)
    {
    } 
}

//ADC 变量
static u16 adcx=0;
static u16 adcx1=0;
static float temp=0;
u8 num=0;//数码管值
u8 smg_wei=0;//数码管位选
u8 adc_t=0;//adc 采样时间值
u8 led_t=0;//led 显示时间值
//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
     if(htim==(&TIM3_Handler))
     {
         adc_t++;
        if(adc_t==100)//ADC 200ms 采样
        {
            adc_t=0;
            adcx=Get_Adc_Average(ADC_CHANNEL_9,3);//ADC 原始值
            temp=(float)adcx*(3.3/4096);//ADC 电压值
            adcx1=temp;
            temp-=adcx1;
            temp*=1000; 
        }
        switch(smg_wei)
        {
             //ADC 采样值
             case 0: 
                if(adcx>1000)num = smg_num[adcx/1000];
                else num = 0x00; break;
             case 1: 
                 if(adcx>100) num = smg_num[adcx%1000/100];
                    else num = 0x00; break; 
             case 2: 
                 if(adcx>10) num = smg_num[adcx%100/10];
                else num = 0x00;
             break;
             case 3: 
                 num = smg_num[adcx%10]; break;
 
                 case 4: num = 0x00; break;

         //ADC 换算后的电压值
         case 5: num = smg_num[adcx1]|0x01;break;
         case 6: num = smg_num[(u16)temp/100]; break; 
         case 7: num = smg_num[(u16)temp%100/10];break; 
 
          }
    LED_Write_Data(num,smg_wei);
    LED_Refresh();
    smg_wei++;
    if(smg_wei==8) smg_wei=0;
    led_t++;
    if(led_t==250)//500ms 闪烁
    {
    led_t=0;
    LED0=!LED0;
    }
  }
}
        在数码管上显示采集的数据,同样数码管在定时器中断中每隔 2ms 刷新显示,每隔
200ms 读取一次 ADC 通道 9 的值,并显示读到的 ADC 值(数字量,在左四位数码管显示),
以及其转换成模拟量后的电压值(在右四位数码管显示),调节电位器采集的数据会实时的
变化。同时控制 LED0 闪烁,以提示程序正在运行。
        
上图中,我们已经将 ADC VR 连接在一起(跳线帽短接),调节 VR1 电位器向右调
采集电压值增大,向左调采集电压值减少。同时伴随 DS0 的不停闪烁,提示程序在运行。
图上可以看到 ADC 采样原始值: 2702 ,换算后的电压值: 2.17V
  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Bugs ToDay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值