野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 17080|回复: 8

ADC与DMA+定时器 问题

[复制链接]
发表于 2015-10-26 10:15:31 | 显示全部楼层 |阅读模式
    各位好。我是一名STM32的初级学员。我这段时间一直在使用野火大哥的STM32F103教程。现在进行到AD转换和SPWM这一板块。我想用滑动变阻器产生模拟信号,经过AD转换来产生数字量然后使用DMA传给以TIM1定时器产生SPWM。程序参考了野火的ADC(DMA+USART)板块,同时没有设置中断,但现在出现了一点问题,就是按照教程配置好外设之后,在主函数里边必须将ADC与PWM置于while(1)大循环之中不断初始化才能产生SPWM,通过示波器也可以观察到稳定的方波信号,但是总觉得这种程序运行方式有点鬼搐,占用了大量的CUP资源。我不知道是哪里出现了问题。想请教一下各位。希望得到各位的指导。论坛里也有小伙伴出现过这种问题。请大家和版主解疑释惑。
  
以下是程序:
主函数部分:
#include "stm32f10x.h"
#include "pwm_output.h"
#include "adc.h"

void Delay(__IO uint32_t nCount)
{
        for(; nCount != 0; nCount--);
}
/*
* 函数名:main
* 描述  : 主函数
* 输入  :无
* 输出  : 无
*/
int main(void)
{               
         
        while(1)
        {
        ADC1_Init();
        Delay(11100);
        /* TIM3 PWM 波输出初始化,并使能 TIM3 PWM 输出 */
        TIM1_PWM_Init();
        }
}

ADC部分:
#include"adc.h"

#define ADC1_DR_Address    ((u32)0x40012400+0x4c)

// ADC1 转换的电压值通过 MDA 方式传到 flash
__IO u16 ADC_ConvertedValue;

/*
* 函数名: ADC1_GPIO_Config
* 描述 :使能 ADC9 和 DMA1 的时钟,初始化 PC.01
* 输入 : 无
* 输出 :无
* 调用 :内部调用
*/
static void ADC1_GPIO_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;//还是设置GPIO常用的方式
       
        /* Enable DMA clock */
               
        /* Enable ADC1 ADCand GPIOC clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB,ENABLE);// | RCC_APB2Periph_GPIOB
       
        /* Configure PC.01 as analog input */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//将此口设置为模拟输入模式
        GPIO_Init(GPIOB, &GPIO_InitStructure); // PB1,输入时不用设置速率
}

/* 函数名:ADC1_Mode_Config
* 描述  :配置ADC9的工作模式为MDA模式
* 输入  : 无
* 输出  :无
* 调用  :内部调用
*/
static void ADC1_Mode_Config(void)
{
        DMA_InitTypeDef DMA_InitStructure;
        ADC_InitTypeDef ADC_InitStructure;

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
       
        DMA_DeInit(DMA1_Channel1);//使用DMA1的1通道       
        DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;         //ADC地址
        DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;//内存地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
        DMA_InitStructure.DMA_BufferSize = 1;
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址固定
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;  //内存地址固定
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;        //半字
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循环传输
        DMA_InitStructure.DMA_Priority = DMA_Priority_High;
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
        DMA_Init(DMA1_Channel1, &DMA_InitStructure);
       
        /* Enable DMA channel1 */
        DMA_Cmd(DMA1_Channel1, ENABLE);
       
        /* ADC1 configuration */       
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //独立ADC模式
        ADC_InitStructure.ADC_ScanConvMode = DISABLE ;          //禁止扫描模式,扫描模式用于多通道采集
//        ADC_InitStructure.ADC_ScanConvMode = ENABLE ;          //采用扫描模式,扫描模式用于多通道采集
        ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;        //开启连续转换模式,即不停地进行ADC转换
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;        //不使用外部触发转换
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//采集数据右对齐
        ADC_InitStructure.ADC_NbrOfChannel = 1;                 //要进行数据转换的通道数目1
        ADC_Init(ADC1, &ADC_InitStructure);
       
        /*配置ADC时钟,为PCLK2的8分频,即9Hz*/
        RCC_ADCCLKConfig(RCC_PCLK2_DiARM);   //而PCLK2最大的频率为72MHz所以最大的为8分频

        /*配置ADC1的通道9为55.        5个采样周期,序列为1 */
        ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_55Cycles5);
       

         /*Enable TempSensorVrefintCmd*/
        //         ADC_TempSensorVrefintCmd(DISABLE);

        /* Enable ADC1 DMA */
        ADC_DMACmd(ADC1, ENABLE);
       
        /* Enable ADC1 */
        ADC_Cmd(ADC1, ENABLE);
       
        /*复位校准寄存器 */   
        ADC_ResetCalibration(ADC1);

        /*等待校准寄存器复位完成 */
        while(ADC_GetResetCalibrationStatus(ADC1));
       
        /* ADC校准 */
        ADC_StartCalibration(ADC1);
        /* 等待校准完成*/
        while(ADC_GetCalibrationStatus(ADC1));
         
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);        /* 由于没有采用外部触发,所以使用软件触发ADC转换 */
}

/*
* 函数名: ADC1_Init
* 描述 :无
* 输入 :无
* 输出 :无
* 调用 :外部调用
*/
void ADC1_Init(void)
{
        ADC1_GPIO_Config();
        ADC1_Mode_Config();
}


定时器与PWM部分:
/******************** (C) COPYRIGHT 2012 WildFire Team **************************
* 文件名  :pwm_output.c
* 描述    :         
* 实验平台:野火STM32开发板
* 硬件连接:---------------------
*          |  PA.06: (TIM3_CH1)  |
*          |  PA.07: (TIM3_CH2)  |
*              |  PB.00: (TIM3_CH3)  |
*                    |  PB.01: (TIM3_CH4)  |
*           ---------------------                           
* 库版本  :ST3.5.0
* 作者    :wildfire team
* 论坛    :http://www.amobbs.com/forum-1008-1.html
* 淘宝    :http://firestm32.taobao.com
**********************************************************************************/
#include "pwm_output.h"


  extern __IO u16 ADC_ConvertedValue;
  u16 CCR1_Val;

static void TIM1_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

        /* TIM3 clock enable */
        //PCLK1经过2倍频后作为TIM3的时钟源等于72MHz
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

  /* GPIOA and GPIOB clock enable */
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);

  /*GPIOA Configuration: TIM3 channel 1 and 2 as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                    // 复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

}

/*
* 函数名:TIM3_Mode_Config
* 描述  :配置TIM3输出的PWM信号的模式,如周期、极性、占空比
* 输入  :无
* 输出  :无
* 调用  :内部调用
*/
static void TIM1_Mode_Config(void)
{
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        TIM_OCInitTypeDef  TIM_OCInitStructure;
        TIM_BDTRInitTypeDef        TIM_BDTRInitStructure;
        /* PWM信号电平跳变值 */

        CCR1_Val=ADC_ConvertedValue;

/* -----------------------------------------------------------------------
    TIM3 Configuration: generate 4 PWM signals with 4 different duty cycles:
    TIM3CLK = 72 MHz, Prescaler = 0x0, TIM3 counter clock = 72 MHz
    TIM3 ARR Register = 999 => TIM3 Frequency = TIM3 counter clock/(ARR + 1)
    TIM3 Frequency = 72 KHz.
    TIM3 Channel1 duty cycle = (TIM3_CCR1/ TIM3_ARR)* 100 = 50%
    TIM3 Channel2 duty cycle = (TIM3_CCR2/ TIM3_ARR)* 100 = 37.5%
    TIM3 Channel3 duty cycle = (TIM3_CCR3/ TIM3_ARR)* 100 = 25%
    TIM3 Channel4 duty cycle = (TIM3_CCR4/ TIM3_ARR)* 100 = 12.5%
  ----------------------------------------------------------------------- */

  /* Time base configuration */                 
  TIM_TimeBaseStructure.TIM_Period = 4200;       //当定时器从0计数到999,即为1000次,为一个定时周期
  TIM_TimeBaseStructure.TIM_Prescaler = 0;            //设置预分频:不预分频,即为72MHz
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //设置时钟分频系数:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式

  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
                   
  /* PWM1 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            //配置为PWM模式1
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;       
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;           //设置跳变值,当计数器计数到这个值时,电平发生跳变
  //  TIM_SetCompare1  (TIM3,2000);
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //当定时器计数值小于CCR1_Val时为高电平
  //  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_low;

  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;     
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;     
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;     
  TIM_BDTRInitStructure.TIM_DeadTime = 0x00;  //这里调整死区大小0-0xff  
  TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

  TIM_OC1Init(TIM1, &TIM_OCInitStructure);         //使能通道1

  TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

  TIM_ARRPreloadConfig(TIM1, ENABLE);                         // 使能TIM1重载寄存器ARR

  /* TIM3 enable counter */
  TIM_Cmd(TIM1, ENABLE);                   //使能定时器1

  TIM_CtrlPWMOutputs(TIM1, ENABLE);        
}
/*
* 函数名:TIM3_PWM_Init
* 描述  :TIM3 输出PWM信号初始化,只要调用这个函数
*         TIM3的四个通道就会有PWM信号输出
* 输入  :无
* 输出  :无
* 调用  :外部调用
*/
void TIM1_PWM_Init(void)
{
        TIM1_GPIO_Config();
        TIM1_Mode_Config();       
}
/******************* (C) COPYRIGHT 2012 WildFire Team *****END OF FILE************/





回复

使用道具 举报

发表于 2015-10-26 10:23:33 | 显示全部楼层
只能帮顶了         你想实现的程序功能是不是这样,你看我理解的对不对

1、滑动电位器的模拟信号经过ADC转换成数字信号

2、数字信号传给TIM1的计数器,计数器根据这个数字信号改变而改变,从而输出不同频率的PWM

是不是这样?  如果是这样,你这样做的目的是什么?或者说你有项目需要这样用到?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-10-26 10:27:33 | 显示全部楼层

版主是不是程序贴的过于多了
回复 支持 反对

使用道具 举报

发表于 2015-10-26 10:28:48 | 显示全部楼层
QQ_DC4DB7 发表于 2015-10-26 10:27
版主是不是程序贴的过于多了

发贴的时候有个贴代码的功能,<> 里面放代码,这样看起来就没有那么乱。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-10-26 11:39:49 | 显示全部楼层
本帖最后由 QQ_DC4DB7 于 2015-10-26 11:48 编辑

那我改一下
实在抱歉,改不了了。下次发帖的时候我注意一下这方面,感谢版主。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-10-26 11:47:13 | 显示全部楼层
本帖最后由 QQ_DC4DB7 于 2015-10-26 11:49 编辑
fire 发表于 2015-10-26 10:23
只能帮顶了         你想实现的程序功能是不是这样,你看我理解的对不对

1、滑动电位器的模拟信号经过AD ...

版主理解的相当准确,这个只是为了学习STM32的ADC与定时器 ,自己给自己出的题目。但是就是没有找到原因。因为定时器与ADC如果就像我程序中那样一直靠初始化来完成SPWM 的输出,程序是不是有点病态运行,而且占用资源。
回复 支持 反对

使用道具 举报

发表于 2015-10-26 12:32:59 | 显示全部楼层
QQ_DC4DB7 发表于 2015-10-26 11:47
版主理解的相当准确,这个只是为了学习STM32的ADC与定时器 ,自己给自己出的题目。但是就是没有找到原因 ...

你把定时器输出PWM的程序移植到ADC转换的这个例程里面,在定时器中断里面修改装载寄存器的值就可以了,
装载寄存器的值来自你的ADC转换的数据,这个思路应该可以。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-10-26 17:01:31 | 显示全部楼层
本帖最后由 QQ_DC4DB7 于 2015-10-26 17:03 编辑
fire 发表于 2015-10-26 12:32
你把定时器输出PWM的程序移植到ADC转换的这个例程里面,在定时器中断里面修改装载寄存器的值就可以了,
...

     是的,但是就是ADC转换之后的值经过DMA传送到TIM1产生SPWM,完成ADC初始化与TIM1的初始化之后SPWM的占空比就能变化一次,必须按复位之后才能产生新的占空比。我只好让ADC与TIM1的初始化不停地在while(1)里重新初始化。控制效果是出来了SPWM方波的占空比也可以流畅地变化。也但是就是觉得有点不对劲。哪能让ADC和TIM1不停初始化的。
     因为您的ADC转换然后通过DMA传送到串口1的这个例程中就没有使用而且ADC触发也是和您的一样在ADC初始化程序中采用软件触发,我也就移植了您的那个教程里的例程。    因为之后想了很多的办法,但是都不奏效,所以只好来请教您和论坛里的朋友了。
回复 支持 反对

使用道具 举报

发表于 2015-10-26 18:37:05 | 显示全部楼层
QQ_DC4DB7 发表于 2015-10-26 17:01
是的,但是就是ADC转换之后的值经过DMA传送到TIM1产生SPWM,完成ADC初始化与TIM1的初始化之后SPWM的 ...

看来只能帮顶了            
回复 支持 反对

使用道具 举报

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

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-11-23 17:25 , Processed in 0.032373 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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