写在前面
做为信号类电赛菜鸡弟弟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