想想第一次接触ADC已经是两年前了,写的第一个记录的文章就是51ADC的,时间好像过的很快,又好像过的很慢,两年了还在搞ADC😂😂,那么,今天就来写一下ADC的代码吧!
控制流程:
1、初始化TIM2,定时1ms,触发ADC
2、初始化ADC,转换相应通道的值
3、初始化DMA,转运ADC的DR寄存器值到数组
1、TIM2相应代码
#include "tim2.h"
//频率: Freq=CK_PSC/ (PSC +1) /(ARR +1)
//占空比: Duty=CCR /(ARR +1)
//分辨率=1/(ARR +1)
/****************************************************************************************************************
定时器2初始化函数,定时1ms,用于触发ADC1数据转换
****************************************************************************************************************/
void Tim2_1ms_Init()//定时器2定时1ms
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIOA的时钟
TIM_InternalClockConfig(TIM2);//选择tim2时钟为内部时钟
/*72 000 000/72=1000000(1us)--0.000001*50000=50ms*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructurn;
TIM_TimeBaseInitStructurn.TIM_ClockDivision=TIM_CKD_DIV1;//输入捕获滤波频率分频(暂时不需要)
TIM_TimeBaseInitStructurn.TIM_CounterMode=TIM_CounterMode_Up;//计数模式(向上计数 更新事件(U)不会触发中断,但可以触发内部其他电路工作,可以映射到TRGO
TIM_TimeBaseInitStructurn.TIM_Period=1000-1;//ARR(自动重装寄存器)CNT=ARR产生的中断,称为更新中断(UI),会通往NVIC
TIM_TimeBaseInitStructurn.TIM_Prescaler=72-1;//PSC(实际预分频值=PSC+1,所以不分频要写PSC=1-1,写0为不分频,1为2分频)
TIM_TimeBaseInitStructurn.TIM_RepetitionCounter=0;//重复计数器
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructurn);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//配置结构体成员默认值
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//定时器PWM模式1
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//输出极性为高
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//定时器输出比较状态,使能
TIM_OCInitStructure.TIM_Pulse=990;//CCR捕获寄存器值
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);//定时器使能
}
定时器2,不需要输出PWM,不需要中断,完整代码里可以注释掉
2、ADC相应代码
#include "adc1.h"
void ADC1_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启时钟APB2_adc1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIOA的时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC的分频为6分频:72M/6=12M(ADC最大输入时钟不能超过14M)
ADC_InitTypeDef ADC_InitStructure;//单次扫描模式——TIM2_CCR触发
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//单ADC模式(运行模式)
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//单次模式(ENABLE:连续;DISABLE:单次;)
ADC_InitStructure.ADC_ScanConvMode=ENABLE;//非扫描模式(ENABLE:扫描模式;DISABLE:非扫描模式;)
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//右对齐(数据对齐)
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T2_CC2;//外部触发为TIM2_CCR2通道(触发方式)
ADC_InitStructure.ADC_NbrOfChannel=4;//需要转换的通道数码
ADC_Init(ADC1,&ADC_InitStructure);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟输入模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1,ADC_Channel_16,1,ADC_SampleTime_55Cycles5); //需要ADC的通道,通道序号1,采样周期55.5
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,2,ADC_SampleTime_55Cycles5); //需要ADC的通道,通道序号2,采样周期55.5
ADC_RegularChannelConfig(ADC1,ADC_Channel_17,3,ADC_SampleTime_55Cycles5); //需要ADC的通道,通道序号3,采样周期55.5
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,4,ADC_SampleTime_55Cycles5); //需要ADC的通道,通道序号4,采样周期55.5
ADC_DMACmd(ADC1,ENABLE); //使能ADC1的DMA请求
ADC_TempSensorVrefintCmd(ENABLE); //启用温度传感器通道(IN16)
ADC_ExternalTrigConvCmd(ADC1,ENABLE);//使能外部触发
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);//复位校准
while(ADC_GetResetCalibrationStatus(ADC1)==SET);//获取复位校准状态,RESET表示复位完成
ADC_StartCalibration(ADC1);//开始校准 //((CR2_RSTCAL)由软件置1,复位完成硬件清0)
while(ADC_GetCalibrationStatus(ADC1)==SET); //获取开始校准状态,RESET表示校准完成
} //((CR2_CAL)由软件置1,校准完成硬件清0)
3、DMA相应代码
void ADC1_DMAInit(uint32_t MemoryBaseAddr)//传入内存地址
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_InitTypeDef DMA_InitSturcrute;
DMA_InitSturcrute.DMA_PeripheralBaseAddr=(uint32_t)(&ADC1->DR);//外设基地址为TIM1的CCR1
DMA_InitSturcrute.DMA_MemoryBaseAddr=MemoryBaseAddr;//内存基地址
DMA_InitSturcrute.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外设数据宽度16位
DMA_InitSturcrute.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//内存数据宽度16位
DMA_InitSturcrute.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址不递增
DMA_InitSturcrute.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址自增
DMA_InitSturcrute.DMA_DIR=DMA_DIR_PeripheralSRC;//方向((ADC_DR)外设到内存(数组))
DMA_InitSturcrute.DMA_BufferSize=4;//传输次数(可以用DMA_SetCurrDataCounter()单独修改)
DMA_InitSturcrute.DMA_Mode=DMA_Mode_Normal;//单次模式
DMA_InitSturcrute.DMA_M2M=DMA_M2M_Disable;//硬件触发;(为1软件触发,为0硬件触发)
DMA_InitSturcrute.DMA_Priority=DMA_Priority_VeryHigh;//优先级
DMA_Init(DMA1_Channel1,&DMA_InitSturcrute);//ADC1的数据转换
DMA_Cmd(DMA1_Channel1,ENABLE);
}
DMA,不需要中断,完整代码里可以注释掉
4、main函数
/************************************************************************************************
控制流程
1、使用定时器2定时1ms的更新事件,映射到TRGO触发ADC转换,每通道转换完成,触发DMA转运。
2、ADC1对应DMA1通道一,DMA不需要进入中断
3、需要时直接读数组ADC_Value[4],就可得到ADC的值
4、
注意事项:1、TIM2不需要输出PWM波形
2、PA0为ADC1的一通道,同时为TIM2的CH2通道,如果采用TIM2_CCR触发ADC,
应该PA0就不能ADC的模拟通道使用;————实际未验证
************************************************************************************************/
#include "stm32f10x.h" // Device header
#include "tim2.h"
#include "adc1.h"
#include "dma.h"
uint16_t ADC_Value[4];
double ADC_Value1;
short Temp;
int main(void)
{
ADC1_Init();//ADC1初始化
Tim2_1ms_Init();
ADC1_DMAInit((uint32_t)(ADC_Value));
/***********************************************************************************************************
假设MCU温度为25度:25度时为1.43V; 公式:T(℃) ={( V25 - Vsense) /Avg_Slope}+25
分变率为:3.3/4096=0.0008V;
1.43V对应数值:1.43/0.0008=1787;
所以:假设是25度,则 1787*(3.3/4096.0)=1.43V;
((1.43-1.43)/0.0043)+25=25度
***********************************************************************************************************/
while (1)
{
}
}
初始化完成,每隔1ms,就会触发ADC进行转换。
调试问题:在使用Keil软件调试的时候,ADC用的是软件触发,程序跑着跑着就会卡在
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);//判断是否转换完成,转换完成EOC位置1
单步调试发现EOC标志位为0.
网上找到的解释:是查看寄存器导致EOC标志位清0了(右下角)
解决办法是:不查看寄存器(103目前只有这样)
okok就这样了,完整的代码链接我放下面,里面有些没有到的代码,你只需要看ADC、TIM2、DMA的就可以了;