写在前面
做为信号类电赛菜鸡弟弟coder选手,ADC简直就是这部分的核心输出(貌似也确实和游戏ADC类似哈哈哈),丰富的配置ADC的配置过51系列ADC(应该算是外设ADC),msp430系列ADC,FPGA系列ADC(必然外设),STM32ADC今天也配置下吧。目前只是最简版本,后面有机会更新,不同模式下的ADC。
开发环境
- STM32F103RB系列芯片(蓝桥杯开发板可直接用)
- keil5
实现功能
ADC使用单次的连续转换,启用滴答定时器确保ADC的电压值可以在200ms的刷新率下进行刷新,并在TFT屏幕上进行显示
STM32 ADC简介
对于任何数字系统来说,没有ADC(模拟到数字转换器),外部的电压我们就没办法测量,模数转换、数模转换对于任何的测量电子系统来说都是必不可少的东西。举个不太恰当的例子,这就相当于人要和狗狗交流,人(主控)不懂狗的语言(模拟输入)需要狗叫翻译器(模数转换器),把信息翻译进来(板内处理)后,然后再用狗叫模拟机给叫给它听(数模转换)。
这里我参考书中的一些内容来大致解释下在STM32中ADC大致是怎样的情况。
- STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC可以独立使用,也可以使用双重模式(提高采样率)。
- STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。
- ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。
- 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
- STM32F103 系列最少都拥有 2 个 ADC。
- STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。
- STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
ADC通道对应关系
开发指南中的基本把ADC中的所有要配置的东西包含在内了,对于库函数开发来说,配置好具体的结构体,用好库函数,基本这部分也算掌握了。给出ADC初始化结构体大家品一品。
typedef struct { uint32_t ADC_Mode; /*!< Configures the ADC to operate in independent or dual mode. This parameter can be a value of ADC_mode */ FunctionalState ADC_ScanConvMode; /*!< Specifies whether the conversion is performed in Scan (multichannels) or Single (one channel) mode. This parameter can be set to ENABLE or DISABLE */ FunctionalState ADC_ContinuousConvMode; /*!< Specifies whether the conversion is performed in Continuous or Single mode. This parameter can be set to ENABLE or DISABLE. */ uint32_t ADC_ExternalTrigConv; /*!< Defines the external trigger used to start the analog to digital conversion of regular channels. This parameter can be a value of @ref ADC_external_trigger_sources_for_regular_channels_conversion */ uint32_t ADC_DataAlign; /*!< Specifies whether the ADC data alignment is left or right. This parameter can be a value of @ref ADC_data_align */ uint8_t ADC_NbrOfChannel; /*!< Specifies the number of ADC channels that will be converted using the sequencer for regular channel group. This parameter must range from 1 to 16. */ }ADC_InitTypeDef;
ADC配置函数
void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStruct; #时钟的使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //配置gpio管脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN ; GPIO_Init(GPIOB, &GPIO_InitStruct); //配置ADC结构体参数 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//单独工作 ADC_InitStructure.ADC_ScanConvMode = DISABLE;//不使能扫描转换 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//使能连续转换,因为程序简单这里不需要每次测量都单独使能了 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部中断 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据位对齐设置,一般设置右对齐,方便计算 ADC_InitStructure.ADC_NbrOfChannel = 1;//通道长度,这里就使能了一个,所以为1 ADC_Init(ADC1, &ADC_InitStructure);//结构体配置完成,使用函数初始化,保存我们的配置 ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);//设置ADC的通道和采样时钟,具体参数见头文件的定义 }
ADC配置完毕。
使能 ADC并校准
在设置完了ADC结构体的信息后,我们就使能 AD 转换器,执行复位校准和 AD 校准,这两步
是必须的!不校准将导致结果很不准确。这就和买东西上称前要清零,谁也不想吃亏对吧哈哈哈。
ADC_Cmd(ADC1, ENABLE);//使能我们使用的ADC1 //启用ADC1复位校准寄存器,保证ADC能正常使用,先复位 ADC_ResetCalibration(ADC1); //检查ADC1复位校准寄存器的结束 while(ADC_GetResetCalibrationStatus(ADC1)); //开始ADC1校准 ADC_StartCalibration(ADC1); //检查ADC1校准结束 while(ADC_GetCalibrationStatus(ADC1)); //启动ADC1软件转换 ADC_SoftwareStartConvCmd(ADC1, ENABLE);
PS上述部分可以直接键入init函数中,也可以自己定义函数调用。
读取 ADC 值
这里比较简单,就使用了读取电压转换值的函数,然后把结果进行了数学转换为真实电压即可。
这里的0XFFF是右对齐除的精度。
v_value=ADC_GetConversionValue(ADC1)*3.30/0xfff;
main.c
下面在加上显示我们的这个ADC的demo就算完了,这里我思路大致就是在TFT屏幕上进行电压值的显示,然后我用了滴答定时器,让ADC的电压值可以在200ms的刷新率下进行刷新。
#include "stm32f10x.h" #include <stdio.h> #include "lcd.h" uint32_t timingdelay = 0; char ADC_Flag; void delayms(uint16_t n); void ADC_Config(void); float v_value; uint8_t buf[20]; int main(void) { STM3210B_LCD_Init(); ADC_Config(); SysTick_Config(SystemCoreClock/1000); LCD_SetTextColor(Yellow); LCD_SetBackColor(Magenta); LCD_Clear(Magenta); LCD_DisplayStringLine(Line0, " ADC-DEMO "); LCD_DisplayStringLine(Line1, "===================="); LCD_DisplayStringLine(Line3, " "); while(1) { if(ADC_Flag==1) { ADC_Flag = 0; v_value=ADC_GetConversionValue(ADC1)*3.30/0xfff; sprintf(buf,"ADC Value:%.3f",v_value); LCD_DisplayStringLine(Line3,buf); } } } void ADC_Config(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN ; GPIO_Init(GPIOB, &GPIO_InitStruct); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; 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_13Cycles5); ADC_Cmd(ADC1, ENABLE); /* Enable ADC1 reset calibration register */ ADC_ResetCalibration(ADC1); /* Check the end of ADC1 reset calibration register */ while(ADC_GetResetCalibrationStatus(ADC1)); /* Start ADC1 calibration */ ADC_StartCalibration(ADC1); /* Check the end of ADC1 calibration */ while(ADC_GetCalibrationStatus(ADC1)); /* Start ADC1 Software Conversion */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); } void delayms(uint16_t n) { timingdelay = n; while(timingdelay!=0); }
滴答定时器的模块只需要在 stm32f10x_it 中写上:
void SysTick_Handler(void) { timingdelay--; if(++MS == 200){ MS = 0; ADC_Flag = 1; } }
记得外部调用的定义变量
extern uint32_t timingdelay; uint16_t MS=0; extern char ADC_Flag;
Reference
- STM32 开发指南 V1.3 3 正点原子
- STM32F10x固件库中文解释 V2.0