最近在调试电机驱动,刚好写到电压采集这一部分,板子的主控是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); //正确
