STM32F103的ADC基于DMA方式传输数据发生错位现象解决

    xiaoxiao2022-07-15  136

    STM32F103的ADC基于DMA方式传输数据发生错位现象解决

    实验要求实现过程结果错误分析更改

    实验要求

    不停的读取10个ADC的值,用DMA发送

    实现过程

    想要获取ADC的值,我们要对ADC进行初始化的配置,通过查阅手册要按照以下的方式进行ADC初始化。

    开启IO口,设置模拟输入。使能ADC1时钟,并设置分频因子。设置ADC1的工作模式。设置ADC1规则序列的相关信息。开启AD转换,并校准。开启规则序列转换ADC值进行读取。

    因为要求是获取10个ADC的值,通过比对选择了比较简单的规则序列转换。通过对比将10个序列信息依次配置好。 按照要求通过DMA传输方式将ADC的数值保存下来。根据中文手册传输ADC1的DMA通道是channel1。将DMA和ADC配置好后,开始编译。 DMA配置如下图:

    void Adc_Init(void) { //先初始化IO口 RCC->APB2ENR|=1<<2; //使能PORTA口时钟 RCC->APB2ENR|=1<<3; //使能PORTB口时钟 RCC->APB2ENR|=1<<4; //使能PORTC口时钟 RCC->APB2ENR|=1<<6; //使能PORTE口时钟 HC4951M 使用 GPIOE->CRL&=0XFF000FFF; GPIOE->CRL|=0X00222000; //PE. 3/4/5推挽输出 GPIOE->ODR|=7<<3; //PE 3.4.5输出高 GPIOA->CRL&=0Xfffff0f0;//PA0,PA2 anolog输入 ADC0,2 GPIOA->CRL|=0X00000000;//PA0,PA2 anolog输入 GPIOB->CRL&=0Xffffff00;//PB0-PB1 anolog输入 ADC9~10 GPIOB->CRL|=0X00000000;//PB0-PB1 anolog输入 GPIOC->CRL&=0Xff000000;//PC0-PC5 anolog输入 ADC11~16 GPIOC->CRL|=0X00000000;//PC0-PC5 anolog输入 RCC->APB2ENR|=1<<9; //ADC1时钟使能 RCC->APB2RSTR|=1<<9; //ADC1复位 RCC->APB2RSTR&=~(1<<9);//复位结束 RCC->CFGR&=~(3<<14); //分频因子清零 RCC->CFGR|=2<<14; //SYSCLK/DIV2=12M ADC时钟设置为12M,ADC最大时钟不能超过14M! //否则将导致ADC准确度下降! ADC1->CR1&=0XF0FFFF; //工作模式清零 ADC1->CR1|=0<<16; //独立工作模式 ADC1->CR1|=1<<8; //扫描模式,1使能,0禁止 ADC1->CR2|=1<<1; //连续转换模式 ADC1->CR2|=(1<<20); //外部触发转换关闭 ADC1->CR2|=7<<17; //软件触发 ADC1->CR2&=~(1<<11); //右对齐 ADC1->CR2|=1<<23; //使能温度传感器 ADC1->SQR1&=~(0XF<<20); ADC1->SQR1|=10<<20; //11个转换在规则序列中 //设置通道的采样时间 ADC1->SMPR2&=0X0; ADC1->SMPR2&=~(7<<(3*0)); //通道0采样时间清空 PA0 ADC1->SMPR2|=7<<(3*0); //通道0 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR2&=~(7<<(3*2)); //通道1采样时间清空 PA2 ADC1->SMPR2|=7<<(3*2); //通道2 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR2&=~(7<<(3*8)); //通道8采样时间清空 PB0 ADC1->SMPR2|=7<<(3*8); //通道8 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR2&=~(7<<(3*9)); //通道9采样时间清空 PB1 ADC1->SMPR2|=7<<(3*9); //通道9 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=0X0; ADC1->SMPR1&=~(7<<(3*0)); //通道10采样时间清空 PC0 ADC1->SMPR1|=7<<(3*0); //通道10 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=~(7<<(3*1)); //通道11采样时间清空 PC1 ADC1->SMPR1|=7<<(3*1); //通道11 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=~(7<<(3*2)); //通道12采样时间清空 PC2 ADC1->SMPR1|=7<<(3*2); //通道12 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=~(7<<(3*3)); //通道13采样时间清空 PC3 ADC1->SMPR1|=7<<(3*3); //通道13 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=~(7<<(3*4)); //通道14采样时间清空 PC4 ADC1->SMPR1|=7<<(3*4); //通道14 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=~(7<<(3*5)); //通道15采样时间清空 PC5 ADC1->SMPR1|=7<<(3*5); //通道15 239.5周期,提高采样时间可以提高精确度 ADC1->SMPR1&=~(7<<(3*6));//清除通道16原来的设置 温度传感器 ADC1->SMPR1|=7<<(3*6); //通道16 239.5周期,提高采样时间可以提高精确度 ADC1->SQR1 &=0X00A00000; //共计11个转换长度 ADC1->SQR1 |=0X00A00000; //共计11个转换长度 ADC1->SQR2 &=0X0; ADC1->SQR3 &=0X0; ADC1->SQR3 |=11<<(5*0); //规则序列中第1个转换通道11 ADC1->SQR3 |=10<<(5*1); //规则序列中第2个转换通道10 ADC1->SQR3 |=12<<(5*2); //规则序列中第3个转换通道12 ADC1->SQR3 |=13<<(5*3); //规则序列中第4个转换通道13 ADC1->SQR3 |=0<<(5*4); //规则序列中第5个转换通道0 ADC1->SQR3 |=9<<(5*5); //规则序列中第6个转换通道9 ADC1->SQR2 |=8<<(5*0); //规则序列中第7个转换通道8 ADC1->SQR2 |=15<<(5*1); //规则序列中第8个转换通道15 ADC1->SQR2 |=14<<(5*2); //规则序列中第9个转换通道14 ADC1->SQR2 |=2<<(5*3); //规则序列中第10个转换通道2 ADC1->SQR2 |=16<<(5*4); //规则序列中第11个转换通道16 ADC1->CR2|=1<<8; //开启DMA模式 ADC1->CR2|=1<<0; //开启AD转换器 ADC1->CR2|=1<<3; //使能复位校准 while(ADC1->CR2&1<<3); //等待校准结束 //该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。 ADC1->CR2|=1<<2; //开启AD校准 while(ADC1->CR2&1<<2); //等待校准结束 //该位由软件设置以开始校准,并在校准结束时由硬件清除 ADC1->CR2|=1<<22; //开始转换规则通道 }

    配置好上面东西之后,在主函数内开启DMA传输与ADC初始化

    Adc_Init(); //ADC初始化 MYDMA_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)&adc_buff0,(u16)N*M);//DMA1通道1,外设为ADC1,存储器为内存,长度.

    按照我所想要的方式应该会在adc_buff0内按顺序得到按照规则顺序排列的adc值。

    结果错误

    但是在我运行之后本应在adc_buff[0]~buff[11]的值却出现在 adc_buff[7] buff[11]~buff[0]至buff[6]是错序的。

    于是我上网百度到有三种解决方法解决错序问题 1.先进行DMA的初始化,再进行ADC的初始化 这么做的原因是因为对先将DMA初始化成功,ADC初始化之后,就开始AD的转换,就不会丢失数据。 2.DMA初始化代码里有Delay_ms的延时 3.先AD的复位校准,再开启DMA。

    用以上的三种方法更改完还是存在错序的问题。 于是在老板的帮助下,我们一步一步的调试,来解决这个问题。

    分析更改

    之前的代码将ADC先初始化,再开启DMA这样是不对的,于是我们将顺序进行更改,先初试DMA再初始ADC。

    MYDMA_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)&adc_buff0,(u16)N*M);//DMA1通道1,外设为ADC1,存储器为内存,长度. Adc_Init(); //ADC初始化

    我们先检查了ADC,确保了ADC的规则序列初始化是正确的。 于是在老板的思路带领下,我们去查看dma的初始化。 在这里我们注意到dma的最后一句话

    DMA1_CHX->CCR|=1<<0; //开启DMA传输

    初始化之后DMA已经开始传输,我们在对ADC初始化之前,是按照文章一开始的6个步骤进行初始化,当我们进行到第六个步骤也就是开启规则序列转换读取ADC值的时候,是有时间消耗的。虽然时间很短,但是对于DMA来说,就会产生错位现象。 于是我们在DMA初始化内关闭了DMA,并将开启DMA传输放在了ADC初始化里,开启规则转换的前面。 如下

    DMA DMA1_CHX->CCR|=0<<0; //关闭DMA传输 void Adc_Init(void) { ...... DMA1_CHX->CCR|=1<<0; //开启DMA传输 ADC1->CR2|=1<<22; //开始转换规则通道 }

    注意这里的DMA关闭传输的代码存在一个问题 这是老板告诉我,身为一个新入门的单片机菜鸟存在的一个问题 就是开启是使用|,关闭是使用& 或0是或不上的,只能与上0,于是在老板的指导下将那句错误代码更改正确

    DMA DMA1_CHX->CCR&=~1<<0; //关闭DMA传输

    用以上方法,就能将数据错位更改过来。

    但是我最终实现的是使用4051开关进行8个通道的切换,每个通道要采集10+1个ADC的值。要利用DMA的中断实现88个ADC数据采集,所以在老板帮助下, 我们将上面的DMA为循环模式更改为非循环模式,将开启DMA传输和开启ADC规则转换这两句话单独拿出。 每一次DMA传输都在特定条件下,由我们自己开启。在每一次DMA传输完成进入中断之前 都将AD转换器和DMA传输关闭,切换通道完成之后都重新开启AD转换和DMA传输关闭,确保不会发生错位现象。

    DMA1_Channel1->CCR&=~(1<<0); //关闭DMA传输 在ADC开始规则通道转换之前打开DMA传输 ADC1->CR2&=~(1<<0); //关闭AD转换器 ... 通道转换 ... DMA1_Channel1->CCR|=1<<0; //开启DMA传输 ADC1->CR2|=1<<22; //开始转换规则通道

    对于新手来说这次调试错误过程让我受益匪浅以及感叹STM32的强大。 我所进行的每一步,都应考虑会产生什么现象。 所以对于我这种新手来说,一定要认真阅读STM32中文手册,反复推敲。

    最新回复(0)