OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5283|回复: 7

关于DMA的疑问 求教大侠解释

[复制链接]

83

主题

349

帖子

1

精华

高级会员

Rank: 4

积分
908
金钱
908
注册时间
2012-8-10
在线时间
13 小时
发表于 2013-11-6 14:54:12 | 显示全部楼层 |阅读模式

小弟最近在研究DMA,将ADC采集的电压值通过DMA传给自己定义的一个数组Buff[500],关于存储器地址增量模式不算太明白 ,假如我设置最大数据传输量为100,然后设置存储器为增量模式,那么DMA是不是将ADC采集到的100个数据搬运到Buff里面,即Buff[0]~Buff[99],存放的就是这100个数据   ?

在原子论坛看到一个比较好的的关于ADC+DMA的程序,对其中有些地方不明白

这个函数式ADC的初始化
void  adc_init(void)
{    
 RCC->APB2ENR|=1<<2;    //使能PORTA口时钟 
 GPIOA->CRL&=0XFFFFFFF0;//PA.0 anolog输入通道10/11设置                                                                      
 RCC->APB2ENR|=1<<9;    //ADC1时钟使能
 __nop();
 __nop();     
 RCC->APB2RSTR|=1<<9;   //ADC1复位
 RCC->APB2RSTR&=~(1<<9);//复位结束  
 RCC->CFGR|=3<<14;      //SYSCLK/DIV2=88M/8=11Mhz 得到ADC采样率位43.65Khz   
 
 ADC1->CR1&=0XF0FFFF;   //独立工作模式
 ADC1->CR1|=1<<8;       //扫描模式   
 ADC1->CR2|=1<<1;       //连续转换模式 
 ADC1->CR2|=0x000E0000; //软件控制转换 由bit21控制
 ADC1->CR2|=1<<20;      //使用用外部触发(SWSTART)!!! 必须使用一个事件来触发
 ADC1->CR2&=~(1<<11);   //右对齐

 ADC1->SQR1&=0xFFF0FFFF;//1个转换在规则序列中  
 ADC1->SQR3&=0XFFFFFFE0;//规则序列1=通道0
 ADC1->SQR3|=0;    
 ADC1->SMPR2|=0X07;     //通道0的转换时间为:239.5+12.5个ADC时钟周期    
 ADC1->CR2|=1<<0;       //开启AD转换器,第一次唤醒AD转换器  
 ADC1->CR2|=1<<3;       //使能复位校准  
 while(ADC1->CR2&1<<3); //等待校准结束   
    //该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。    
 ADC1->CR2|=1<<2;       //开启AD校准    
 while(ADC1->CR2&1<<2); //等待校准结束
 //该位由软件设置以开始校准,并在校准结束时由硬件清除    
 ADC1->CR2|=1<<8;    //开启ADC DMA转换

 MYDMA_Config(DMA1_Channel1);//配置DMA通道1
 MYDMA_Enable(DMA1_Channel1);//开启DMA通道1 
 ADC1->CR2|=1<<0;    //开启AD转换   
}
//获得ADC值
u16 get_adc(void)   
{     
 ADC1->CR2|=1<<22;       //启动规则转换通道 
 while(!(ADC1->SR&1<<1));//等待转换结束      
 return ADC1->DR; //返回adc值         
}
记红色的这个函数虽然写在程序里面,但是我找了又找,发没有任何函数都没有调用它,然后我理解的是DMA直接把ADC转换的数据搬运到了BUff里面,不知道这样对不对
             

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

83

主题

349

帖子

1

精华

高级会员

Rank: 4

积分
908
金钱
908
注册时间
2012-8-10
在线时间
13 小时
 楼主| 发表于 2013-11-6 15:22:07 | 显示全部楼层

       下面是我参考论坛资料写的程序:但是有点问题
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3                   
void  Adc_Init(void)
{   
 //先初始化IO口
  RCC->APB2ENR|=1<<2;    //使能PORTA口时钟
 GPIOA->CRL&=0XFFFF0000;//PA0 1 2 3 anolog输入
 //通道10/11设置   
 RCC->APB2ENR|=1<<9;    //ADC1时钟使能  
 RCC->APB2RSTR|=1<<9;   //ADC1复位
 RCC->APB2RSTR&=~(1<<9);//复位结束    
 RCC->CFGR&=~(3<<14);   //分频因子清零 
 //SYSCLK/DIV2=12M ADC时钟设置为12M,ADC最大时钟不能超过14M!
 //否则将导致ADC准确度下降!
 RCC->CFGR|=2<<14;       
 ADC1->CR1&=0XF0FFFF;   //工作模式清零
 ADC1->CR1|=0<<16;      //独立工作模式 
 ADC1->CR1&=~(1<<8);    //非扫描模式
      
 ADC1->CR2&=~(1<<1);    //单次转换模式
 ADC1->CR2&=~(7<<17);   
 ADC1->CR2|=7<<17;    //软件控制转换 
 ADC1->CR2|=1<<20;      //使用用外部触发(SWSTART)!!! 必须使用一个事件来触发
 ADC1->CR2&=~(1<<11);   //右对齐
 ADC1->CR2|=1<<23;      //使能温度传感器

 ADC1->SQR1&=~(0XF<<20);
 ADC1->SQR1&=0<<20;     //1个转换在规则序列中 也就是只转换规则序列1      
 //设置通道0~3的采样时间
 ADC1->SMPR2&=0XFFFFF000;//通道0,1,2,3采样时间清空  
 ADC1->SMPR2|=7<<9;      //通道3  239.5周期,提高采样时间可以提高精确度 
 ADC1->SMPR2|=7<<6;      //通道2  239.5周期,提高采样时间可以提高精确度 
 ADC1->SMPR2|=7<<3;      //通道1  239.5周期,提高采样时间可以提高精确度 
 ADC1->SMPR2|=7<<0;      //通道0  239.5周期,提高采样时间可以提高精确度
  
 ADC1->SMPR1&=~(7<<18);  //清除通道16原来的设置 
 ADC1->SMPR1|=7<<18;     //通道16  239.5周期,提高采样时间可以提高精确度 

 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<<8;
 MYDMA_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)&SendBuff,10);
 MYDMA_Enable(DMA1_Channel1);
    ADC1->CR2|=1<<0;

}

void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar, u32 cmar,u16 cndtr)
{
 RCC->AHBENR|=1<<0;  //开启DMA1时钟
 delay_ms(2);     //等待DMA时钟稳定
 DMA_CHx->CPAR=cpar; //DMA外设地址
 DMA_CHx->CMAR=(u32)cmar; 
    DMA1_MEN_LEN=cndtr;
 DMA_CHx->CNDTR=cndtr;
 DMA_CHx->CCR=0x00000000;
 DMA_CHx->CCR|=0<<4; //数据传输方向 0:从外设读   1:从寄存器读
 DMA_CHx->CCR|=0<<5; //不执行循环模式
 DMA_CHx->CCR|=0<<6; //不执行外设地址增量
 DMA_CHx->CCR|=1<<7; //执行存储器地址增量模式
 DMA_CHx->CCR|=1<<8; //外设数据宽度16Bits
 DMA_CHx->CCR|=0<<9; //外设数据宽度16Bits
 DMA_CHx->CCR|=1<<10;//存储器数据宽度16Bits
 DMA_CHx->CCR|=0<<11;//外设数据宽度16Bits
 DMA_CHx->CCR|=1<<12;//通道优先级:中
 DMA_CHx->CCR|=0<<14;//非存储器到存储器模式

}

void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
 DMA_CHx->CCR&=~(1<<0);   //关闭DMA传输
 DMA_CHx->CNDTR=DMA1_MEN_LEN; //DMA1传输数据量,每次传输的数据量
 DMA_CHx->CCR|=1<<0;    //开启DMA传输
}

 

主函数:

int main(void)

 u8 flag=0,t=0; 
 Stm32_Clock_Init(9);
 delay_init(72);
 uart_init(72,9600);
  
 Adc_Init(); 
 LCD_Init();
 KEY_Init();
 LED_Init();
 
 while(1)
 {
  LCD_ShowString(20,20,"Temperrature:"); 
  if(DMA1->ISR&(1<<1))//传输完成标志
  {
       DMA1->IFCR|=1<<1;//清除传输完成标志 
       MYDMA_Enable(DMA1_Channel1);//开始一次DMA传输    
       flag=1;
  }

  LCD_ShowNum(50,70,SendBuff[0],4,16); //TFT显示电压值

  for(t=0;t<10;t++) //串口打印,循环10次,调试用
  {
   printf("当前电压值:%d \n\n",SendBuff[t]);
  }  

  if(flag==1) //调试用,
  {
   flag=0;   
   LED0=0;
  }

  delay_ms(200);
 }
}

下进去程序发现,LED0不亮,flag也没置1,但是TFT却能显示电压值,即SendBuff【0】,串口打印出来的值第一个是4095,其余的是9个全是0

 

回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
发表于 2013-11-6 15:32:09 | 显示全部楼层
回复【2楼】电子狼:
---------------------------------
jtag调试,查看问题在哪里
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

6

主题

42

帖子

0

精华

初级会员

Rank: 2

积分
92
金钱
92
注册时间
2013-8-27
在线时间
2 小时
发表于 2013-11-6 15:36:54 | 显示全部楼层
你设置ADC是一个一个通道吧,且你采用的是非连续采集,也就是只采集1次,flag也就没赋值1,led就不亮了。

以上是我个人感觉,

另想请教下大家,实际项目中这个功能怎么使用



回复 支持 反对

使用道具 举报

3

主题

2178

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3323
金钱
3323
注册时间
2013-7-19
在线时间
195 小时
发表于 2013-11-6 15:48:46 | 显示全部楼层
LZ你先看看DMA的特性。
另外转换完成后先要显示,然后再开DMA
回复 支持 反对

使用道具 举报

83

主题

349

帖子

1

精华

高级会员

Rank: 4

积分
908
金钱
908
注册时间
2012-8-10
在线时间
13 小时
 楼主| 发表于 2013-11-7 20:23:35 | 显示全部楼层
回复【5楼】ricefat:
---------------------------------DMA未把数据搬运到存储器内,那么应该是0才对啊???
回复 支持 反对

使用道具 举报

3

主题

2178

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3323
金钱
3323
注册时间
2013-7-19
在线时间
195 小时
发表于 2013-11-7 21:34:32 | 显示全部楼层
回复【6楼】电子狼:
---------------------------------
DMA搬运数据过程CPU不参与,就是说你使能DMA后就直接执行后面的代码了。
那么DMA传输未完成时候就不会执行这个
??if(DMA1->ISR&(1<<1))//传输完成标志
??{
????? ?DMA1->IFCR|=1<<1;//清除传输完成标志?
????? ?MYDMA_Enable(DMA1_Channel1);//开始一次DMA传输???? 
?????? flag=1;
??}
仅仅执行while里面的其他代码
结果就是printf的过程中其实DMA还在传数据,而你的AD结果数组还尚未填满
回复 支持 反对

使用道具 举报

83

主题

349

帖子

1

精华

高级会员

Rank: 4

积分
908
金钱
908
注册时间
2012-8-10
在线时间
13 小时
 楼主| 发表于 2013-11-7 22:19:30 | 显示全部楼层
回复【7楼】ricefat:
---------------------------------
真心感谢你的指导,小弟再仔细研究一下DMA去
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-9 07:17

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表