STM32标准库ADC读取摇杆电压值

目录

前言:

一、ADC介绍

二、DMA介绍

 三、代码编写 ——不使用DMA进行数据转运

1.开启对应的时钟以及ADCCLK的配置

2.初始化输入引脚

3.ADC的基本配置

4.ADC校准

5.读取ADC转换数据

6.ADC部分全部代码

7、主函数代码及运行现象

现象:

四、代码编写——使用DMA配合ADC进行数据采集

1.开启对应的时钟:

2.初始化ADC采集引脚,并配置采样通道

 3.配置ADC结构体

4.ADC上电,并开启DMA至ADC的转运通道

 5.设置全局变量接收DMA转运的数据

 6.配置DMA,DMA上电

 7.ADC校准,并通过软件触发ADC

8.ADC+DMA所有代码

9.主函数编写

最终的效果

前言:

        填坑:在完成纯软件实现贪吃蛇后,CLION 基于Easyx的贪吃蛇小游戏(链表)CLION 基于EasyX的贪吃蛇小游戏,开始将代码移植到STM32上使用硬件实现贪吃蛇,项目分成两部分,一部分先介绍ADC读取摇杆值,第二部分再将贪吃蛇具体的落实到硬件上,现在先进行第一部分记录。

一、ADC介绍

        STM32上的ADC为12位ADC,且输入电压只能是0~3.3V,所以后期在给摇杆上电时要格外注意,摇杆上电压的5V一定要接3.3V,否则可能会损坏单片机。

        通过查看手册可知,STM32F103ZET6上有三个ADC,每个ADC有21个输入通道。

        这里我选用ADC3上的通道4和通道5即PF6,PF7进行摇杆X、Y两轴的模拟量读取。

         ADC框图如图所示:

二、DMA介绍

        DMA是数据转运的助手,通过DMA进行数据转运可以极大的节省软件资源,让系统通过硬件自动执行相应的操作。

        通过查看数据手册可知,STM32F103ZET6共有两个ADC可以使用,两者均挂载在AHB总线上。

 三、代码编写 ——不使用DMA进行数据转运

1.开启对应的时钟以及ADCCLK的配置

    //使用ADC3_IN4和ADC3_IN5,ADC时钟使用6分频,即12MHz
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

2.初始化输入引脚

注意:ADC的GPIO配置一定是模拟输入

    //初始化ADC输入引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AIN ;
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;
    GPIO_Init (GPIOF ,&GPIO_InitStructure);

3.ADC的基本配置

        对结构体进行配置,并对ADC进行上电(ADC_CMD)

    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//单ADC模式
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//无外部触发源(使用软件触发)
    ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//单次转换模式
    ADC_InitStructure.ADC_ScanConvMode=DISABLE;//非扫描模式
    ADC_InitStructure.ADC_NbrOfChannel=1;//一个通道
    ADC_Init(ADC3,&ADC_InitStructure);

    ADC_Cmd(ADC3,ENABLE);

4.ADC校准

        先是复位校准,等待ADC复位校准完成后,开始进行ADC校准,通过函数描述以及手册即可得出ADC复位校准和ADC校准完成的标准为均为RESET。

    ADC_ResetCalibration(ADC3);
    while (ADC_GetResetCalibrationStatus(ADC3)==SET);
    ADC_StartCalibration(ADC3);
    while (ADC_GetCalibrationStatus(ADC3)==SET);

5.读取ADC转换数据

        读取时先设定规则组转换的通道,接着通过软件触发ADC转换,等待ADC转换完成之后,将转换值返回。

uint16_t MyADC_GetValue(uint8_t ADC_Channel )
{
    ADC_RegularChannelConfig(ADC3,ADC_Channel,1,ADC_SampleTime_28Cycles5);
    ADC_SoftwareStartConvCmd(ADC3,ENABLE);
    while (ADC_GetFlagStatus(ADC3,ADC_FLAG_EOC)==RESET);
    return ADC_GetConversionValue(ADC3);
}

6.ADC部分全部代码

#include "MyADC.h"
void MyADC_Init(void )
{
    //使用ADC3_IN4和ADC3_IN5,ADC时钟使用6分频,即12MHz
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    //初始化ADC输入引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AIN ;
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;
    GPIO_Init (GPIOF ,&GPIO_InitStructure);


    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//单ADC模式
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//无外部触发源(使用软件触发)
    ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//单次转换模式
    ADC_InitStructure.ADC_ScanConvMode=DISABLE;//非扫描模式
    ADC_InitStructure.ADC_NbrOfChannel=1;//一个通道
    ADC_Init(ADC3,&ADC_InitStructure);

    ADC_Cmd(ADC3,ENABLE);

    ADC_ResetCalibration(ADC3);
    while (ADC_GetResetCalibrationStatus(ADC3)==SET);
    ADC_StartCalibration(ADC3);
    while (ADC_GetCalibrationStatus(ADC3)==SET);
}

uint16_t MyADC_GetValue(uint8_t ADC_Channel )
{
    ADC_RegularChannelConfig(ADC3,ADC_Channel,1,ADC_SampleTime_28Cycles5);
    ADC_SoftwareStartConvCmd(ADC3,ENABLE);
    while (ADC_GetFlagStatus(ADC3,ADC_FLAG_EOC)==RESET);
    return ADC_GetConversionValue(ADC3);
}

7、主函数代码及运行现象

#include "main.h"

uint16_t value1=0;
uint16_t value2=0;
int main(void)
{

    uart_init(115200);
    delay_init();
    MyADC_Init();
    OLED_Init();
    OLED_Clear();

    while(1)
    {
        value1=MyADC_GetValue(ADC_Channel_4);
        value2=MyADC_GetValue(ADC_Channel_5);
        OLED_ShowString(0,0,"ADCValue1:",16);
        OLED_ShowString(0,2,"ADCValue2:",16);
        OLED_ShowNum(80,0,value1,4,16);
        OLED_ShowNum(80,2,value2,4,16);
    }
    return 0;
}

现象:

        可见,已经成功读出采样值,后面经过线性变换即可获得实际电压值。

四、代码编写——使用DMA配合ADC进行数据采集

        将ADC配置成连续转换、扫描模式,DMA配置成ADC触发,循环转运模式。

1.开启对应的时钟:

    //使用ADC3_IN4和ADC3_IN5,ADC时钟使用6分频,即12MHz,配合DMA进行数据转运
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

2.初始化ADC采集引脚,并配置采样通道

    //初始化ADC输入引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AIN ;
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;
    GPIO_Init (GPIOF ,&GPIO_InitStructure);

    ADC_RegularChannelConfig(ADC3,ADC_Channel_4,1,ADC_SampleTime_28Cycles5);
    ADC_RegularChannelConfig(ADC3,ADC_Channel_5,2,ADC_SampleTime_28Cycles5);

 3.配置ADC结构体

        ADC依然通过软件触发,并使用连续转换(EOC置位后并不停下来,继续进行转换),扫描模式。

    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//单ADC模式
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//无外部触发源(使用软件触发)
    ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式
    ADC_InitStructure.ADC_ScanConvMode=ENABLE;//扫描模式
    ADC_InitStructure.ADC_NbrOfChannel=2;//两个通道
    ADC_Init(ADC3,&ADC_InitStructure);

4.ADC上电,并开启DMA至ADC的转运通道

    ADC_DMACmd(ADC3,ENABLE);
    ADC_Cmd(ADC3,ENABLE);

 5.设置全局变量接收DMA转运的数据

uint16_t ADC_Value[2]={0};

 6.配置DMA,DMA上电

        外设的地址(DMA_PeripheralBaseAddr)为ADC3的DR寄存器的值,需要强制转换为32位的地址,存储器的地址(DMA_MemoryBaseAddr)为第5点定义的全局变量ADC_Value,将外设寄存器作为源头,且外设地址不自增(一直为ADC3的DR寄存器地址),存储器地址自增(两个通道),由于ADC采集出的数据为16位,故外设和存储器均为半字(halfword),DMA使用循环转运,最后将DMA上电。

        下图位DMA的硬件触发源,由手册可知,可以通过DMA2的通道5进行ADC3的触发,故DMA_Init(DMA2_Channel5,&DMA_InitStructure);选择通道5进行触发。

    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC3->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)ADC_Value;
    DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//外设作为数据源头
    DMA_InitStructure.DMA_BufferSize=2;//ADC采集两个通道的信息
    DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设不自增,地址一直是ADC的DR寄存器地址
    DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//ADC采集数据为16位
    DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
    DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//不使用软件触发,即通过ADC触发
    DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
    DMA_Init(DMA2_Channel5,&DMA_InitStructure);

    DMA_Cmd(DMA2_Channel5,ENABLE);

 7.ADC校准,并通过软件触发ADC

        注意,在配置完成之后一定要使用软件触发一次ADC进行转运才能使得ADC进行连续转换,扫描模式,DMA也进行循环转运。

    ADC_ResetCalibration(ADC3);
    while (ADC_GetResetCalibrationStatus(ADC3)==SET);
    ADC_StartCalibration(ADC3);
    while (ADC_GetCalibrationStatus(ADC3)==SET);
    ADC_SoftwareStartConvCmd(ADC3,ENABLE);

8.ADC+DMA所有代码

        记得在ADC的头文件申明全局变量出去。

#include "MyADC.h"
uint16_t ADC_Value[2]={0};
void MyADC_Init(void )
{
    //使用ADC3_IN4和ADC3_IN5,ADC时钟使用6分频,即12MHz,配合DMA进行数据转运
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    //初始化ADC输入引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AIN ;
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;
    GPIO_Init (GPIOF ,&GPIO_InitStructure);

    ADC_RegularChannelConfig(ADC3,ADC_Channel_4,1,ADC_SampleTime_28Cycles5);
    ADC_RegularChannelConfig(ADC3,ADC_Channel_5,2,ADC_SampleTime_28Cycles5);

    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//单ADC模式
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//无外部触发源(使用软件触发)
    ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式
    ADC_InitStructure.ADC_ScanConvMode=ENABLE;//扫描模式
    ADC_InitStructure.ADC_NbrOfChannel=2;//两个通道
    ADC_Init(ADC3,&ADC_InitStructure);

    ADC_DMACmd(ADC3,ENABLE);
    ADC_Cmd(ADC3,ENABLE);

    /*DMA进行转运的三个条件
     * 1.DMA使能上电
     * 2.DMA的传输计数器BUFFER不为零
     * 3.有触发信号
     * */

    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC3->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)ADC_Value;
    DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//外设作为数据源头
    DMA_InitStructure.DMA_BufferSize=2;//ADC采集两个通道的信息
    DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设不自增,地址一直是ADC的DR寄存器地址
    DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//ADC采集数据为16位
    DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
    DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//不使用软件触发,即通过ADC触发
    DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
    DMA_Init(DMA2_Channel5,&DMA_InitStructure);

    DMA_Cmd(DMA2_Channel5,ENABLE);

    ADC_ResetCalibration(ADC3);
    while (ADC_GetResetCalibrationStatus(ADC3)==SET);
    ADC_StartCalibration(ADC3);
    while (ADC_GetCalibrationStatus(ADC3)==SET);
    ADC_SoftwareStartConvCmd(ADC3,ENABLE);
}

9.主函数编写

#include "main.h"
float Voltage1=0;
float Voltage2=0;

int main(void)
{

    uart_init(115200);
    delay_init();
    MyADC_Init();
    OLED_Init();
    OLED_Clear();
    OLED_ShowString(0,0,"LocationX:",16);
    OLED_ShowString(0,2,"LocationY:",16);
    OLED_ShowString(0,4,"VoltageX:0.00V",16);
    OLED_ShowString(0,6,"VoltageY:0.00V",16);
    while(1) {
        Voltage1=(float )ADC_Value[0]/4095*3.3;
        Voltage2=(float )ADC_Value[1]/4095*3.3;


        OLED_ShowNum(80,0,ADC_Value[0],4,16);
        OLED_ShowNum(80,2,ADC_Value[1],4,16);

        OLED_ShowNum(72,4,Voltage1,1,16);
        OLED_ShowNum(88,4,(uint16_t)(Voltage1*100)%100,2,16);
        OLED_ShowNum(72,6,Voltage1,1,16);
        OLED_ShowNum(88,6,(uint16_t)(Voltage2*100)%100,2,16);
    }
    return 0;
}

最终的效果:

       项目基于CLION进行开发 

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值