OpenEdv-开源电子网

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

ADC扫描模式单次转换+DMA库函数代码(成功)

[复制链接]

2

主题

21

帖子

0

精华

初级会员

Rank: 2

积分
65
金钱
65
注册时间
2016-1-4
在线时间
13 小时
发表于 2016-1-29 21:30:39 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 cpu12g000 于 2016-1-30 17:59 编辑

本代码的目的是,进行三路通道的扫描,(当然可修改成更多路),
扫一次处理一次,然后软件控制再一次扫描。

看到许多文章,没见到有ADC扫描模式单次转换的代码。
见这里许多弟兄议的热火朝天,俺也来凑个热闹。
经过两天的琢磨试验,搞了这几行代码。经试成功。
如果此篇文章对你有帮助,不妨高抬贵手点个赞,让俺心里安慰一下。呵呵。
闲话少说,上代码:
(这里面头文件及主函数不晒出了,相信都一看下文都明白,
过两天来此一游,如有需要者,我再晒出)

#include "adc.h"
#include "delay.h"
#include "dma.h"
#include "stm32f10x.h"
#include "oled.h"

DMA_InitTypeDef DMA_InitStructure;
#define SEND_BUF_SIZE 3 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
u16 RevBuff[SEND_BUF_SIZE]; //发送数据缓冲区
u8 MyDmaLen = 3;
u16 DMA1_MEM_LEN = 3;//保存DMA每次数据传送的长度   
//以上这几行代码写的有点啰嗦,凑合着看吧,不行自已可以精炼一下,我懒得改了。
/DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从外设模式-〉存储器/16位数据宽度/存储器增量模式
//DMA_CHxMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输

  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
DMA1_MEM_LEN=cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取数据发送到内存
DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道ADC_DMA_Channel所标识的寄存器
   
}
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE );  //关闭TX DMA1 所指示的通道
//必须先关闭这个通道后,才能修改下面的缓存     
  DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
//实际上,这里的 DMA1_MEM_LEN  跟DMA初始化结构体中的 ”DMA_InitStructure.DMA_BufferSize = cndtr;"   
//是一个变量,下面看看它原始材料中的描述。因为它会在一次传输后减为0,所以要重新装载。
//通道开启后该寄存器变为只读,指示剩余的待传输字节数目。
  //寄存器内容在每次DMA传输后递减。
  //数据传输结束后,寄存器的内容或者变为0;
  //原文“或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时的数值。
  //当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。”
DMA_Cmd(DMA_CHx, ENABLE);  //使能 TX DMA1 所指示的通道

}
void  Adc_Init(void)
{  
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1,ENABLE );   //使能ADC1通道时钟

RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1 作为模拟通道输入引脚                        
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 |GPIO_Pin_2|GPIO_Pin_3;//三路AD采集的输入脚?
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  //,模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);  //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //模数转换工作在扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 3; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
  //ADC_DMACmd(ADC1,ENABLE);
  //设置指定ADC的规则组通道,一个序列,采样时间
  //以下是函数原型
   // void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel,
//uint8_t Rank, uint8_t ADC_SampleTime // Rank 为槽的顺序
ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
  ADC_RegularChannelConfig(ADC1, 2, 2, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
  ADC_RegularChannelConfig(ADC1, 3, 3, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期

  //
//应先将这里分配DMA 的设置,最后效准:
//-------------------------------------------------------------
MYDMA_Config(DMA1_Channel1, (u32)&ADC1->DR,(u32)RevBuff,MyDmaLen);
//这里一定要先开启DMA通道
//DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //使能传输完成中断。
//-------------------------------------------------------------
ADC_DMACmd(ADC1,ENABLE);

ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1

ADC_ResetCalibration(ADC1); //使能复位校准  
  
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

ADC_StartCalibration(ADC1);  //开启AD校准

while(ADC_GetCalibrationStatus(ADC1));  //等待校准结束

  // ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //使能指定的ADC1的软件转换启动功能
}     
  
void Enter_Adc(void)   
{
   
MYDMA_Enable(DMA1_Channel1); //此处必须重新设定DMA使能,因为DMA计数器会减到0

//ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //使能指定的ADC1的软件转换启动功能
                                            //CR2_EXTTRIG_SWSTART_Set  注意这个值是:500000
                                            //即它同时启动了:SWSTART 和 EXTTRIG
                                            //这也就是为什么被强调:"必须使用一个事件来触发" 的理由:
                                            // 即: ADC1->CR2 = 1 << 2;  (SWSTART = 1)
//ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1

//while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
//return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
//SWSTART:开始转换规则通道 (Start conversion of regular channels) 位22 由软件设置该位以启动转换,
//转换开始后硬件马上清除此位。(笔者注:这是区别转换与再次转换不同之处)
//以下为原文摘录,主要是为了让弟兄们更加明白:
//如果在EXTSEL[2:0]位中选择了SWSTART为触发事件,该位用于启动一组规则通道的转换,
//0:复位状态; 1:开始转换规则通道。
//ADON:开/关A/D转换器 (A/D converter ON / OFF) 该位由软件设置和清除。当该位为’0’时,写入’1’将把ADC从断电模式下唤醒。
  // 当该位为’1’时,写入’1’将启动转换。
//应用程序需注意,在转换器上电至转换开始有一个延迟tSTAB,参见图25。
//0:关闭ADC转换/校准,并进入断电模式; 1:开启ADC并启动转换。
//注:如果在这个寄存器中与ADON一起还有其他位被改变,则转换不被触发。这是为了防止触发错误的转换。
//---------------------------------------------------------------------------------
//这个函数在读取一次三组数据后,可反复调用,进行下次的读取。
//-------------------------------------------------------------------------------
}
void ADC_Display(void)
{
u32 VTemp[3];
if(DMA_GetFlagStatus(DMA1_FLAG_TC1) == SET)//如果传输完毕标志位设置
{
          DMA_ClearFlag(DMA1_FLAG_TC1|DMA1_FLAG_HT1|DMA1_FLAG_GL1);  //清除传输完成标志位

      VTemp[0] = 3300 * RevBuff[0]/4095;
      VTemp[1] = 3300 * RevBuff[1]/4095;
      VTemp[2] = 3300 * RevBuff[2]/4095;
      
      OLED_ShowNumbFour(8,8,VTemp[0],12,0,1);// 这个地方是我自定义的一个显示四位数的函数。不必深究
      OLED_ShowNumbFour(8,24,VTemp[1],12,0,1);
      OLED_ShowNumbFour(8,40,VTemp[2],12,0,1);
          OLED_Refresh_Gram();
      //Get_Adc();
}
  //这个地方说明一下:当ADC连续扫描模式+DMA的时候,扫描结束时EOC 不置位,调试中已发现。
//因为什么?因为原文有句话:大意是:当ADC->DR(ADC的结果寄存器被读取后,它会清此位,)
//而英文资料中有句话(中文没有)每次ADC单个通道转换结束后,就会发生一次DMA传输,
//意思是DMA控制器读取了ADC->DR,所以,EOC自然不置位了。
}
void DMA1_Channel1_IRQHandler(void)
{
//  @arg DMA1_FLAG_GL1: DMA1 Channel1 global flag.
  //  @arg DMA1_FLAG_TC1: DMA1 Channel1 transfer complete flag.
//if(DMA_GetFlagStatus(DMA1_FLAG_TC1) == SET)//如果传输完毕标志位设置
          //DMA_ClearFlag(DMA1_FLAG_TC1);  //清除传输完成标志位
if(DMA_GetITStatus(DMA1_IT_TC1) == SET)      //
          DMA_ClearITPendingBit(DMA1_IT_TC1);   //经查,这两函数跟上面的一样,不知为什么弄两个这种函数
//看库函数中的详尽代码中,以上这两组函数说的是一个东西,不知为啥弄得重复。
//此中断函数我这里没用,有想调用的,可改一下。
}



最佳答案

查看完整内容[请看2#楼]

void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx) { DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道 DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小 //实际上,这里的 DMA1_MEM_LEN 跟DMA初始化结构体中的 ”DMA_InitStructure.DMA_BufferSize = cndtr;" //是一个变量,下面看看它原始材料中的描述。因为它会在一次传输后减为0,所以要重新装载。 //通道开启后该寄 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

21

帖子

0

精华

初级会员

Rank: 2

积分
65
金钱
65
注册时间
2016-1-4
在线时间
13 小时
 楼主| 发表于 2016-1-29 21:30:40 | 显示全部楼层
本帖最后由 cpu12g000 于 2016-1-29 21:56 编辑

void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道      
  DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
//实际上,这里的 DMA1_MEM_LEN  跟DMA初始化结构体中的 ”DMA_InitStructure.DMA_BufferSize = cndtr;"   
//是一个变量,下面看看它原始材料中的描述。因为它会在一次传输后减为0,所以要重新装载。
//通道开启后该寄存器变为只读,指示剩余的待传输字节数目。
  //寄存器内容在每次DMA传输后递减。
  //数据传输结束后,寄存器的内容或者变为0;
  //原文“或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时的数值。
  //当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。”
DMA_Cmd(DMA_CHx, ENABLE);  //使能TX DMA1 所指示的通道

}   
//这个函数上面在复制时给弄错位了,
//加到了ADC_Init()里去了,
//(本人初次发帖,一会琢磨下怎么在原帖中修改下)
回复

使用道具 举报

2

主题

21

帖子

0

精华

初级会员

Rank: 2

积分
65
金钱
65
注册时间
2016-1-4
在线时间
13 小时
 楼主| 发表于 2016-1-29 21:47:43 | 显示全部楼层
本帖最后由 cpu12g000 于 2016-1-30 18:01 编辑

标题咋修改?本人初次发帖,怎么修改我靠。
有弟兄知道的,告知一声,我就不费这个脑子了。
这文章的标题死都没法改,谁有办法?



回复

使用道具 举报

2

主题

21

帖子

0

精华

初级会员

Rank: 2

积分
65
金钱
65
注册时间
2016-1-4
在线时间
13 小时
 楼主| 发表于 2016-1-29 22:17:10 | 显示全部楼层
没人顶,自已先顶一个!
回复

使用道具 举报

1

主题

78

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2015-12-10
在线时间
49 小时
发表于 2016-2-14 02:31:14 | 显示全部楼层
有参考价值!

逆水行舟不进则退
回复

使用道具 举报

1

主题

6

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2017-8-1
在线时间
9 小时
发表于 2017-8-16 14:00:39 | 显示全部楼层
帮顶  来参考下
回复

使用道具 举报

0

主题

5

帖子

0

精华

新手上路

积分
48
金钱
48
注册时间
2017-8-20
在线时间
4 小时
发表于 2017-8-22 00:38:35 | 显示全部楼层
参考一下,实验后再回来评价,谢楼主了
回复

使用道具 举报

0

主题

1

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2019-7-21
在线时间
8 小时
发表于 2019-9-12 11:04:05 | 显示全部楼层
先点赞,再观看!
回复

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
19
金钱
19
注册时间
2020-2-7
在线时间
4 小时
发表于 2020-9-21 17:09:46 | 显示全部楼层
源码呢?
回复

使用道具 举报

11

主题

140

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1701
金钱
1701
注册时间
2019-5-2
在线时间
169 小时
发表于 2020-10-15 15:55:42 | 显示全部楼层
楼主能给下源码吗
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
13
金钱
13
注册时间
2021-7-5
在线时间
3 小时
发表于 2021-7-8 14:52:09 | 显示全部楼层
赞哈哈哈
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-5-23 16:23

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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