最近在调试电机驱动,刚好写到电压采集这一部分,板子的主控是STM32F040K6T6.用到了ADC,使用非DMA方式。刚开始读取到的值全部是0,然后程序卡死,我在使用万用表测IO口电压后,排除了硬件问题。网上找了一堆资料,得到的解决方案有这么几种:
1.说是官方的库函数ADC_ChannelConfig()有问题,需要把“|”符号去掉的,代码如下,修改之后,依旧卡死
void ADC_ChannelConfig(ADC_TypeDef* ADCx, uint32_t ADC_Channel, uint32_t ADC_SampleTime)
{
uint32_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_ADC_ALL_PERIPH(ADCx));
assert_param(IS_ADC_CHANNEL(ADC_Channel));
assert_param(IS_ADC_SAMPLE_TIME(ADC_SampleTime));
/* Configure the ADC Channel */
ADCx->CHSELR |= (uint32_t)ADC_Channel; //把这里的或运算符去掉
/* Clear the Sampling time Selection bits */
tmpreg &= ~ADC_SMPR1_SMPR;
/* Set the ADC Sampling Time register */
tmpreg |= (uint32_t)ADC_SampleTime;
/* Configure the ADC Sample time register */
ADCx->SMPR = tmpreg ;
}
2. 在配置中加入:ADC_InitStructure.ADC_ExternalTrigConv =ADC_ExternalTrigConv_T1_TRGO;这一句,代码如下,测试没用
ADC_StructInit(&ADC_InitStructure);
ADC_DeInit(ADC1); //复位并初始化
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO; //据说加入这句
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;
3.在while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY));这一句后面加入标志位清除函数,如下,测试之后,对我没用
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY)); //启动就绪标志
ADC_ClearFlag(ADC1, ADC_FLAG_EOC); //!!!加入这一句
4.有博主在重新命名了GPIO 结构体的名字后解决问题,如下面的代码,我尝试了一下,也没有解决。
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
//GPIO_InitTypeDef GPIO_InitStructure; //原来在其他地方多次使用GPIO_InitStructure
GPIO_InitTypeDef GPIO_ADC; //将GPIO_InitStructure改为GPIO_ADC
}
我这里两种情况都出现了。卡死是首要问题,先解决卡死问题,上会卡死的代码:
//配置部分
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_ADC;
/*ADC以及GPIO时钟使能*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOB,ENABLE);//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);
/*PA5 作为模拟通道输入引脚*/
GPIO_ADC.GPIO_Pin = GPIO_Pin_5; //PA5
GPIO_ADC.GPIO_Mode = GPIO_Mode_AN; //模拟输入引脚
//PIO_ADC.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_ADC.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
GPIO_Init(GPIOA, &GPIO_ADC);
/*ADC配置*/
ADC_StructInit(&ADC_InitStructure);
ADC_DeInit(ADC1); //复位并初始化
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位分辨率
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发无
//ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO; //???
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右端对齐
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; //浏览模式
ADC_Init(ADC1,&ADC_InitStructure); //初始化ADC
ADC_GetCalibrationFactor(ADC1); //校验
ADC_Cmd(ADC1,ENABLE); //使能
//ADC通道配置
ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles); //IN5
/*程序卡在了这里*/
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY)); //启动就绪标志
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
}
分析流程:初始化时钟(OK)->GPIO配置(OK)->ADC配置(OK)->ADC启动(?)->ADC转换(卡死) ,问题出在ADC启动之后,转换并未开始,通过打印调试(记住keil不支持F0系列的Debug),发现程序卡死在while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY));找到数据手册ADC配置一节,有几个细节引起了我的注意。
请看这句: 对 于 以 下 的 这 些 位 ADC_IER、ADC_CFGRi、ADC_SMPR、ADC_TR、ADC_CHSELR 和 ADC_CCR 寄存器,软件必须在 ADC 开启 (ADEN = 1) 且无转换期间 (ADSTART = 0) 的情况 下才能进行改写。我们来看下ADC_CHSELR这个寄存器,它是选择ADC通道的,他的意思是,我们只能在开启ADC后再选择通道,列个表说明一下这几个寄存器的名字。所以问题就出在这里:我们必须先配置,启动ADC,再操作这些寄存器,不然就会使ADC进入未知状态。
综上,第一个问题解决,正确代码思路如下:
Adc_Init();//配置并启动ADC
Get_Value();//转换
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_ADC;
/*ADC以及GPIO时钟使能*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOB,ENABLE);//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
/*PA4,5 作为模拟通道输入引脚*/
GPIO_ADC.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_4; //PA4与PA5
GPIO_ADC.GPIO_Mode = GPIO_Mode_AN; //模拟输入引脚
GPIO_ADC.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
GPIO_Init(GPIOA, &GPIO_ADC);
/*PB0 作为模拟通道输入引脚*/
GPIO_ADC.GPIO_Pin = GPIO_Pin_0; //PA4与PA5
GPIO_ADC.GPIO_Mode = GPIO_Mode_AN; //模拟输入引脚
GPIO_ADC.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
GPIO_Init(GPIOB, &GPIO_ADC);
/*ADC配置*/
ADC_StructInit(&ADC_InitStructure);
ADC_DeInit(ADC1); //复位并初始化
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位分辨率
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发无
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右端对齐
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; //浏览模式
ADC_Init(ADC1,&ADC_InitStructure); //初始化ADC
ADC_GetCalibrationFactor(ADC1); //校验
ADC_VrefintCmd(ENABLE);
ADC_Cmd(ADC1,ENABLE); //使能
}
float Get_Value(void)
{
u32 Value= 0;
u8 index;
ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles); //选择通道
ADC_StartOfConversion(ADC1); //启动转换,注意不是启动ADC,而是启动ADC转换
for(index = 0;index<8;index++) //均值滤波
{
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
Get_Value+= ADC_GetConversionValue(ADC1);
}
Result_Voltage = Result_Voltage>>3; //求平均值
return (float)((Get_Value/4096.00)*3.3); //计算电压值
}
关于第二个问题,黑脸,忘记在4096后面加小数点了,4096是整型,我的的ADC为分子,就会得到小数,刚好运气不好,小数为0.34---,于是计算机取整,默认它就是0,后面的我就不用讲了。粗心害人呀。
return (float)(((Result_Voltage/4096)*3.3*(39+2.2))/2.2); //错误
return (float)(((Result_Voltage/4096.00)*3.3*(39+2.2))/2.2); //正确