OpenEdv-开源电子网

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

关于定时器定时采样,然后DMA存储数据的相关问题

[复制链接]

2

主题

4

帖子

0

精华

新手入门

积分
15
金钱
15
注册时间
2020-3-3
在线时间
4 小时
发表于 2020-3-9 15:47:46 | 显示全部楼层 |阅读模式
1金钱
开发板是原子的迷你板stm32f103RCT6
我用ADC1的通道6也就是PA6进行采样,然后用TIM2_CH2触发ADC1转换,转换的数据再用DMA1搬运到一个数组里


批注 2020-03-09 151446.jpg


我设置的是ADC独立模式,单次转换,禁止扫描,使能定时器的触发转换
定时器TIM2配置的是PWM1输出比较,向上计数,周期为500us,每500us触发转换一个采样点
DMA1配置的是正常缓存模式,内存地址递增,使能传输完成中断
我在代码中设置的是采样10个点,在DMA传输中断函数中让LED1点亮来指示采样了10个点
现在的问题是,DMA没有进入中断,理由是LED1没有被点亮,不知道是不是配置错误
目前我还对一下问题存在疑惑:
第一:DMA配置结构体里有一个DMA_InitStructure.DMA_BufferSize设置DMA缓存的是不是采样多少个点就设置多少??
第二:DMA的传输完成中断是指传完一个采样点的数据就进入中断,还是传完DMA_InitStructure.DMA_BufferSize个数据才进入中断
第三:DMA是在每次ADC转换完成了才传输一次,还是在第一次触发后就不管有没有更新,一直传DMA_InitStructure.DMA_BufferSize个数据
第四:ADC应该配置为单次转换还是循环转换
第五:定时器要不要下面两行代码,我看有些博客写了,有些又没写

TIM_InternalClockConfig(TIM2);

TIM_UpdateDisableConfig(TIM2, DISABLE);



下面是各模块文件的代码

  1. #include "gpio.h"

  2. void rcc_Clock_Init(void)
  3. {
  4.         //使能PA,PD端口时钟,ADC1时钟
  5.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD|RCC_APB2Periph_ADC1, ENABLE);         
  6.        
  7.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能TIM2时钟
  8.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);        //使能DMA传输
  9.        
  10. }
  11. void gpio_Init(void)
  12. {
  13.         GPIO_InitTypeDef  GPIO_InitStructure;
  14.        
  15.         //PA6 作为模拟通道输入引脚                        
  16.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  17.         //GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_50MHz;//作为模拟输入口要不要设置速度??
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                //模拟输入引脚
  19.         GPIO_Init(GPIOA, &GPIO_InitStructure);       
  20.        
  21.         //初始化PA8和PD2,PA8和PD2是连接LED0和LED1的
  22.         //LED IO初始化
  23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;                                 //LED0-->PA.8 端口配置
  24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
  25. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 //IO口速度为50MHz
  26. GPIO_Init(GPIOA, &GPIO_InitStructure);                                         //根据设定参数初始化GPIOA.8
  27. GPIO_SetBits(GPIOA,GPIO_Pin_8);                                                 //PA.8 输出高

  28. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                             //LED1-->PD.2 端口配置, 推挽输出
  29. GPIO_Init(GPIOD, &GPIO_InitStructure);                                           //推挽输出 ,IO口速度为50MHz
  30. GPIO_SetBits(GPIOD,GPIO_Pin_2);                                                  //PD.2 输出高

  31. }

复制代码
  1. #include "adc.h"
  2.                                                                                                                           
  3. void  Adc_Init(void)
  4. {        
  5.         ADC_InitTypeDef ADC_InitStructure;

  6.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

  7.         ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

  8.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //ADC工作模式:独立模式
  9.         ADC_InitStructure.ADC_ScanConvMode = DISABLE;        //模数转换工作在单通道模式
  10.         ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //模数转换工作在单次转换模式
  11.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;        //转换由外部触发启动
  12.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;        //ADC数据右对齐
  13.         ADC_InitStructure.ADC_NbrOfChannel = 1;        //顺序进行规则转换的ADC通道的数目
  14.         ADC_Init(ADC1, &ADC_InitStructure);        //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
  15.        
  16.   
  17.         ADC_RegularChannelConfig(ADC1,ADC_Channel_6, 1, ADC_SampleTime_239Cycles5 );
  18.        
  19.         //使能ADC、DMA
  20.         ADC_DMACmd(ADC1,ENABLE);
  21.         ADC_Cmd(ADC1,ENABLE);
  22.        
  23.         ADC_ResetCalibration(ADC1);        //使能复位校准  
  24.          
  25.         while(ADC_GetResetCalibrationStatus(ADC1));        //等待复位校准结束
  26.        
  27.         ADC_StartCalibration(ADC1);         //开启AD校准

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

  29.         ADC_ExternalTrigConvCmd(ADC1, ENABLE);                //设置外部触发模式使能
  30. }
复制代码
  1. #include "dma.h"

  2. DMA_InitTypeDef DMA_InitStructure;
  3. NVIC_InitTypeDef NVIC_InitStructure;
  4. //DMA1的各通道配置
  5. //这里的传输形式是固定的,这点要根据不同的情况来修改
  6. //从存储器->外设模式/8位数据宽度/存储器增量模式
  7. //DMA_CHx:DMA通道CHx
  8. //cpar:外设地址
  9. //cmar:存储器地址
  10. //cndtr:数据传输量
  11. void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
  12. {
  13.        
  14.        
  15.   DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
  16.         DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设ADC基地址
  17.         DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
  18.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从内存读取发送到外设
  19.         DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
  20.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
  21.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
  22.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //数据宽度为16位
  23.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
  24.         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
  25.         DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //DMA通道 x拥有超高优先级
  26.         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
  27.         DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道所标识的寄存器
  28.        
  29.         DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);                //使能传输完成中断

  30.        
  31.         NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
  32.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  33.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  34.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  35.         NVIC_Init(&NVIC_InitStructure);
  36.        
  37.         DMA_Cmd(DMA1_Channel1,ENABLE);
  38. }
复制代码
  1. #include "timer.h"

  2. //PWM输出初始化
  3. //arr:自动重装值
  4. //psc:时钟预分频数
  5. void TIM2_Init(u16 arr,u16 psc)
  6. {  
  7.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  8.         TIM_OCInitTypeDef  TIM_OCInitStructure;

  9.        
  10.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         80K
  11.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
  12.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
  13.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  14.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


  15.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
  16.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  17.         TIM_OCInitStructure.TIM_Pulse = (arr+1)/2; //设置待装入捕获比较寄存器的脉冲值
  18.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
  19.         TIM_OC1Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIM2

  20.   TIM_CtrlPWMOutputs(TIM2,ENABLE);        //MOE 主输出使能       
  21.         TIM_InternalClockConfig(TIM2);
  22.         TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  //CH1预装载使能         
  23.         TIM_UpdateDisableConfig(TIM2, DISABLE);
  24.         TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIM2在ARR上的预装载寄存器
  25.        
  26.         TIM_Cmd(TIM2, ENABLE);  //使能TIM2

  27.    
  28. }
复制代码
main文件如下:

  1. #include "delay.h"
  2. #include "sys.h"
  3. #include "gpio.h"
  4. #include "timer.h"
  5. #include "adc.h"
  6. #include "dma.h"
  7. #include "usart.h"

  8. #define ADC1_DR_Address ((u32)0x4001244C) //ADC1的数据寄存器的地址
  9. #define buf_len 10 //采样点数10个,同时也是采样存储数据的数组ADC1_ConvertedValue[]的长度

  10. u8 ADC1_Full_Flag = 0;//采样buf_len个点数完成标志,若DMA传输完所有点数,则由DMA传输中断置1
  11. u16 ADC_ConvertedValue[buf_len];//该数组用于存储DMA1从采样数据寄存器传过来的数据

  12. int main(void)
  13. {       
  14.         u8 i=0;
  15.         u16 times = 0;
  16.         rcc_Clock_Init();  //时钟初始化
  17.         delay_init();                     //延时函数初始化          
  18.   uart_init(9600);    //串口初始化
  19.   gpio_Init();        //初始化GPIO
  20.         Adc_Init();  //初始化ADC1
  21.         MYDMA_Config(DMA1_Channel1,(u32)ADC1_DR_Address,(u32)ADC_ConvertedValue,buf_len);//DMA1初始化
  22.         TIM2_Init(499,71);//72分频。采样周期=(499+1)*(71+1)/72Mhz=500us ,即每500us采样一次,采样点数由len_buf控制
  23.   while(1)
  24.         {
  25.                 if(ADC1_Full_Flag)//如果采样len_buf个点完成
  26.                 {
  27.                                 DMA_Cmd(DMA1_Channel1, DISABLE );//关闭DMA传输通道
  28.                                 TIM_Cmd(TIM2, DISABLE);//关闭定时器,定时采样也结束了
  29.                           LED1 = 0;        //点亮LED1,指示采样完成
  30.                                 ADC1_Full_Flag = 0;
  31.                 }
  32.                 else if(USART_RX_STA&0x8000)//串口有数据发送过来,表示电脑端准备好接收数据了
  33.                 {
  34.                         //开始发送数据给电脑
  35.                         printf("\r\n开始发送数据:\r\n");
  36.                         for(i=0;i<buf_len;i++)
  37.                         {
  38.                                 //由于串口一次只能传一个字节,但采样数据是两个字节的,所以分两次传送
  39.                                 USART1->DR = (ADC_ConvertedValue[i]>>8);//发送高8位
  40.                                 while((USART1->SR&0X40)==0);//等待发送结束(判断状态寄存器的TC标志位,若为1则发送完成)
  41.                                 USART1->DR=(ADC_ConvertedValue[i])&0x00ff;//发送低8位
  42.                                 while((USART1->SR&0X40)==0);//等待发送结束(判断状态寄存器的TC标志位,若为1则发送完成)
  43.                         }
  44.                         printf("\r\n发送完成\r\n");
  45.                         USART_RX_STA=0;
  46.                 }
  47.                 else
  48.                 {
  49.                         times++;
  50.                         if(times%50==0)LED0=!LED0;//闪烁LED0,提示系统正在运行(若在进行串口通信,LED0不再闪烁)
  51.                         delay_ms(5);   
  52.                 }
  53.         }               
  54. }

  55. //DMA1传输完成中断
  56. void  DMA1_Channel1_IRQHandler(void)
  57. {
  58.         if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)
  59.         {
  60.                 ADC1_Full_Flag = 1;
  61.                 LED1=0;//传送完成则点亮LED1
  62.                 DMA_ClearITPendingBit(DMA1_IT_TC1);
  63.         }
  64. }
复制代码



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

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165524
金钱
165524
注册时间
2010-12-1
在线时间
2116 小时
发表于 2020-3-10 00:28:15 | 显示全部楼层
回复

使用道具 举报

1

主题

3

帖子

0

精华

新手入门

积分
9
金钱
9
注册时间
2020-4-2
在线时间
5 小时
发表于 2020-4-4 22:11:01 来自手机 | 显示全部楼层
牛逼了,这谁看得下去
回复

使用道具 举报

5

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
120
金钱
120
注册时间
2019-11-24
在线时间
24 小时
发表于 2020-4-8 16:48:59 | 显示全部楼层
#define ADC1_DR_Address ((u32)0x4001244C) //ADC1的数据寄存器的地址,你这个地址是怎么确定的?怎么保证跟其它变量没有地址冲突?
回复

使用道具 举报

80

主题

931

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3348
金钱
3348
注册时间
2013-5-28
在线时间
468 小时
发表于 2020-4-8 16:59:30 | 显示全部楼层
我都是直接ADC循环采集,然后DMA负责运输数据,然后定时去看,^_^
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2020-4-8 19:39:52 | 显示全部楼层
你的DMA传输方向应该设置成从外设到内存,工作方式应该设置为循环缓存方式,定时器不应该设置为PWM模式应该设置为触发事件(ADC转换)
回复

使用道具 举报

0

主题

89

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1286
金钱
1286
注册时间
2020-4-7
在线时间
309 小时
发表于 2020-5-11 09:00:59 | 显示全部楼层
可以不用这个,我没有用这个也配置成功了。
回复

使用道具 举报

0

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2020-4-21
在线时间
34 小时
发表于 2020-5-11 15:01:35 | 显示全部楼层
dw772 发表于 2020-4-8 16:48
#define ADC1_DR_Address ((u32)0x4001244C) //ADC1的数据寄存器的地址,你这个地址是怎么确定的?怎么保证 ...

这个查手册的,基地址加数据寄存器的偏移量
回复

使用道具 举报

0

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2020-4-21
在线时间
34 小时
发表于 2020-5-11 15:03:30 | 显示全部楼层
y284858 发表于 2020-4-8 19:39
你的DMA传输方向应该设置成从外设到内存,工作方式应该设置为循环缓存方式,定时器不应该设置为PWM模式应该 ...

配置PWM应该也没问题吧,ADC触发配置成TIM2_CC2事件了,就是由PWM触发了,直接片内定时器触发
回复

使用道具 举报

0

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2020-4-21
在线时间
34 小时
发表于 2020-5-11 15:07:54 | 显示全部楼层
聚东风 发表于 2020-4-8 16:59
我都是直接ADC循环采集,然后DMA负责运输数据,然后定时去看,^_^

你这个采样的分辨率只能由采样周期控制了,如果只是想采集一段时间内的AD平均值,那我觉得你的方法也挺好,采集多个数据,然后定时读出来求平均
回复

使用道具 举报

6

主题

211

帖子

0

精华

高级会员

Rank: 4

积分
833
金钱
833
注册时间
2019-12-17
在线时间
157 小时
发表于 2020-5-12 09:41:09 | 显示全部楼层
tfm121 发表于 2020-5-11 15:03
配置PWM应该也没问题吧,ADC触发配置成TIM2_CC2事件了,就是由PWM触发了,直接片内定时器触发

你可以查查手册,如果手册上说可以那大概时没有问题。我没有试过这样做
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-5-29 17:18

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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