DMA方式控制PWM脉冲数量、频率、占空比,用于步进、伺服.....

2019-12-13 18:32发布

本帖最后由 爱则倾心 于 2016-3-30 18:09 编辑

关键词:STM32、CubeMX、DMA、TIM、PWM、伺服电机控制
1、关于ST的CubeMX,目前来说,让人又爱又恨,恨比爱多。一是从标准库转用Cube库比较费劲,二是ST不在更新标准库,新出的片子只能用Cube库或直接玩寄存器,三是目前来说Cbue里面到处都是地雷.....
2、项目开始前从网上查了一些精确产生PWM脉冲个数的方法,引述如下:
     1)外部再弄个IO口接到PWM脚上,用外部中断的办法,单独来计数。此办法可行,但非常不科学,并且浪费资源。   
     2)使用定时器,使用一个和PWM频率一致的定时器,使用定时器中断来计数。此方法比第一种办法好了很多,但是仍然感觉比较笨,并且单片机会频繁的进中断......
     3)利用定时器内部互联,一个定时器的给另一个定时器提供时钟,主从模式,一个PWM输出脉冲给另一个定时提供时钟,每来一个脉冲,计数器值+1,当+到指定个数后,产生一次中断,然后关闭PWM输出。此方法还是浪费资源,且多路电机控制需                     要产生多路频率不同、个数不同的脉冲时就不能满足要求了。
     4)使用DMA来控制发送的脉冲数,最大可以65535个,如果想使用不同频率和脉宽,可以设置不同的装载值,如果你发送的脉冲数超过65535个,则可以使用DMA传输完成中断中切换DMA传输的数据起始地址及发送数量,继续发送。这个方法即方便,又减轻CPU的负担,可以同时驱动多个电机工作,还可以根据电机的启动-运行-停止使用不同的频率。
3、上数第4)种方法甚好,只是没有大神们没能具体说明如何实现,根据项目需要,选定第4)种方式实现多路、不同频率、不同脉宽、数量精确可控的PWM波。项目仍在开展中,时间有限,本帖今天起开始说说第4)方式的具体实现方法。
4、硬件平台:STM32L476G-DISCO,TIM2、CH1、PA0、DMA1,软件平台:STM32CubeMX+MDK V5.15。
5、软件实现步骤及关键点说明......回家了,待续......
5.1、使用Cube MX---->new project---->选择单片机型号---->选择TIM2的时钟源、通道及模式
CUBE_GENERATOR.jpg (32.61 KB, 下载次数: 0) 下载附件 2016-3-30 18:07 上传
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
76条回答
爱则倾心
1楼-- · 2019-12-13 18:55
5.2  看看Cube为我们做了哪些工作
  /* USER CODE BEGIN 1 */
  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();//初始化了NVIC分组及SysTick

  /* Configure the system clock */
  SystemClock_Config();  //配置时钟、校准了MSI、使能了SysTick 的中断

  /* Initialize all configured peripherals */
  MX_GPIO_Init();//开了GPIOA、GPIOC的时钟
  MX_DMA_Init();//开DMA1时钟,设置DMA1 channel2的NVIC分组与使能
  MX_TIM2_Init();//TIM2的初始化,GPIO设置、PWM配置、DMA设置
/* USER CODE END 1 */

/**
  * @brief  Configure the Flash prefetch, the Instruction and Data caches,
  *         the time base source, NVIC and any required global low level hardware
  *         by calling the HAL_MspInit() callback function to be optionally defined in user file
  *         stm32l4xx_hal_msp.c.
  *
  * @note   HAL_Init() function is called at the beginning of program after reset and before
  *         the clock configuration.
  *            
  * @note   In the default implementation the System Timer (Systick) is used as source of time base.
  *         The Systick configuration is based on MSI clock, as MSI is the clock
  *         used after a system Reset and the NVIC configuration is set to Priority group 4.
  *         Once done, time base tick starts incrementing: the tick variable counter is incremented
  *         each 1ms in the SysTick_Handler() interrupt handler.
  *
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_Init(void)
{
  /* Configure Flash prefetch, Instruction cache, Data cache */
  /* Default configuration at reset is:                      */
  /* - Prefetch disabled                                     */
  /* - Instruction cache enabled                             */
  /* - Data cache enabled                                    */
#if (INSTRUCTION_CACHE_ENABLE == 0)
   __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */

#if (DATA_CACHE_ENABLE == 0)
   __HAL_FLASH_DATA_CACHE_DISABLE();
#endif /* DATA_CACHE_ENABLE */

#if (PREFETCH_ENABLE != 0)
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
  HAL_InitTick(TICK_INT_PRIORITY);

  /* Init the low level hardware */
  HAL_MspInit();

  /* Return function status */
  return HAL_OK;
}


/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE|RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = 0;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_11;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV8;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV8;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

  __PWR_CLK_ENABLE();

  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  HAL_RCCEx_EnableMSIPLLMode();

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}


/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __GPIOC_CLK_ENABLE();
  __GPIOA_CLK_ENABLE();

}


/**
  * Enable DMA controller clock
  */
void MX_DMA_Init(void)
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);

}

/* TIM2 init function */
void MX_TIM2_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;//时钟为总线时钟 12M,不分频
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 1199;   //产生5Khz的PWM
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_Base_Init(&htim2);    //下面有贴出此函数并对其调用函数分析与修改

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;//没用,可注释掉
  HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);//没用,可注释掉

  HAL_TIM_PWM_Init(&htim2);//没用,可注释掉

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;//没用,可注释掉
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;//没用,可注释掉
  HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);//没用,可注释掉

  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 600;  //占空比为50%
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);

}


/**
  * @brief  Initializes the TIM Time base Unit according to the specified
  *         parameters in the TIM_HandleTypeDef and initialize the associated handle.
  * @param  htim: TIM Base handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
{
  /* Check the TIM handle allocation */
  if(htim == NULL)
  {
    return HAL_ERROR;
  }

  /* Check the parameters */
  assert_param(IS_TIM_INSTANCE(htim->Instance));
  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));

  if(htim->State == HAL_TIM_STATE_RESET)
  {
    /* Allocate lock resource and initialize it */
    htim->Lock = HAL_UNLOCKED;

    /* Init the low level hardware : GPIO, CLOCK, NVIC */
    HAL_TIM_Base_MspInit(htim);//下面来看看这个函数
  }

  /* Set the TIM state */
  htim->State= HAL_TIM_STATE_BUSY;

  /* Set the Time Base configuration */
  TIM_Base_SetConfig(htim->Instance, &htim->Init);//此函数里有"Generate an update event to reload the Prescaler"

  /* Initialize the TIM state*/
  htim->State= HAL_TIM_STATE_READY;

  return HAL_OK;
}


void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(htim_base->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */

  /* USER CODE END TIM2_MspInit 0 */
    /* Peripheral clock enable */
    __TIM2_CLK_ENABLE();
  
    /**TIM2 GPIO Configuration   
    PA0     ------> TIM2_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Peripheral DMA init*/
  
    hdma_tim2_up.Instance = DMA1_Channel2;
    hdma_tim2_up.Init.Request = DMA_REQUEST_4;
    hdma_tim2_up.Init.Direction = DMA_MEMORY_TO_PERIPH;//在这里修改一下方向,生成的代码里为“DMA_PERIPH_TO_MEMORY”
    hdma_tim2_up.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim2_up.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim2_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;//这里可根据寄存器情况修改为HALFWORD 或BYTE
    hdma_tim2_up.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;//
    hdma_tim2_up.Init.Mode = DMA_NORMAL;
    hdma_tim2_up.Init.Priority = DMA_PRIORITY_LOW;
    HAL_DMA_Init(&hdma_tim2_up);

    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_UPDATE],hdma_tim2_up);

  /* USER CODE BEGIN TIM2_MspInit 1 */

  /* USER CODE END TIM2_MspInit 1 */
  }

}

5.3  需要另外做的工作
有空再写,睡觉了........





爱则倾心
2楼-- · 2019-12-13 22:07
爱则倾心 发表于 2016-3-31 12:49
5.2  看看Cube为我们做了哪些工作
  /* USER CODE BEGIN 1 */
  /* MCU Configuration-------------------- ...

5.3 需要另外做的工作

初始化后,调用如下两个函数,重点说说如下两个函数。
HAL_TIM_DMABurst_WriteStart(&htim2, TIM_DMABASE_ARR, TIM_DMA_UPDATE,
                              (uint32_t*)aSRC_Buffer, TIM_DMABURSTLENGTH_3TRANSFERS);
//函数1,用来配置要传送的目标外设寄存器起始地址,这里配置为:TIM_DMABASE_ARR,即从自动重加载寄存器开始写,产生DMA请求的事件配置为:TIM_DMA_UPDATE  即定时器更新事件产生DMA请求,接下来是要传送的源数据起始地址,设置成要发送的数据数组的首地址,然后是DMA连续传输的数据长度,配置为TIM_DMABURSTLENGTH_3TRANSFERS,即连续传输三组寄存器的值,可以理解为一次定时器一次DMA请求,DMA进行三次数据传输,分别写给了TIM_DMABASE_ARR起的三组寄存器。
   
        HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //使能比较匹配输出通道,使能定时器               

看看第一个函数的原型及内部调用函数:
/**
  * @brief  Configure the DMA Burst to transfer Data from the memory to the TIM peripheral
  * @param  htim: TIM handle
  * @param  BurstBaseAddress: TIM Base address from when the DMA will starts the Data write
  *         This parameters can be on of the following values:
  *            @arg TIM_DMABASE_CR1
  *            @arg TIM_DMABASE_CR2
  *            @arg TIM_DMABASE_SMCR
  *            @arg TIM_DMABASE_DIER
  *            @arg TIM_DMABASE_SR
  *            @arg TIM_DMABASE_EGR
  *            @arg TIM_DMABASE_CCMR1
  *            @arg TIM_DMABASE_CCMR2
  *            @arg TIM_DMABASE_CCER
  *            @arg TIM_DMABASE_CNT
  *            @arg TIM_DMABASE_PSC
  *            @arg TIM_DMABASE_ARR
  *            @arg TIM_DMABASE_RCR
  *            @arg TIM_DMABASE_CCR1
  *            @arg TIM_DMABASE_CCR2
  *            @arg TIM_DMABASE_CCR3
  *            @arg TIM_DMABASE_CCR4
  *            @arg TIM_DMABASE_BDTR
  *            @arg TIM_DMABASE_DCR
  * @param  BurstRequestSrc: TIM DMA Request sources
  *         This parameters can be on of the following values:
  *            @arg TIM_DMA_UPDATE: TIM update Interrupt source
  *            @arg TIM_DMA_CC1: TIM Capture Compare 1 DMA source
  *            @arg TIM_DMA_CC2: TIM Capture Compare 2 DMA source
  *            @arg TIM_DMA_CC3: TIM Capture Compare 3 DMA source
  *            @arg TIM_DMA_CC4: TIM Capture Compare 4 DMA source
  *            @arg TIM_DMA_COM: TIM Commutation DMA source
  *            @arg TIM_DMA_TRIGGER: TIM Trigger DMA source
  * @param  BurstBuffer: The Buffer address.
  * @param  BurstLength: DMA Burst length. This parameter can be one value
  *         between: TIM_DMABurstLength_1Transfer and TIM_DMABurstLength_18Transfers.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress, uint32_t BurstRequestSrc,
                                              uint32_t* BurstBuffer, uint32_t  BurstLength)//,uint16_t  Bursttimes
{
  /* Check the parameters */
  assert_param(IS_TIM_DMABURST_INSTANCE(htim->Instance));
  assert_param(IS_TIM_DMA_BASE(BurstBaseAddress));
  assert_param(IS_TIM_DMA_SOURCE(BurstRequestSrc));
  assert_param(IS_TIM_DMA_LENGTH(BurstLength));

  if((htim->State == HAL_TIM_STATE_BUSY))
  {
     return HAL_BUSY;
  }
  else if((htim->State == HAL_TIM_STATE_READY))
  {
    if((BurstBuffer == 0 ) && (BurstLength > 0))
    {
      return HAL_ERROR;
    }
    else
    {
      htim->State = HAL_TIM_STATE_BUSY;
    }
  }
  switch(BurstRequestSrc)
  {
    case TIM_DMA_UPDATE:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback = TIM_DMAPeriodElapsedCplt;   

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */                                                                          //用来设置传输的源地址、目标地址、DMA传送次数及中断
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);//Bursttimes
    }
    break;
    case TIM_DMA_CC1:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback =  TIM_DMADelayPulseCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);
    }
    break;
    case TIM_DMA_CC2:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback =  TIM_DMADelayPulseCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);
    }
    break;
    case TIM_DMA_CC3:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback =  TIM_DMADelayPulseCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);
    }
    break;
    case TIM_DMA_CC4:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback =  TIM_DMADelayPulseCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);
    }
    break;
    case TIM_DMA_COM:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferCpltCallback =  TIMEx_DMACommutationCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_COMMUTATION]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_COMMUTATION], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);
    }
    break;
    case TIM_DMA_TRIGGER:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_TRIGGER]->XferCpltCallback = TIM_DMATriggerCplt;

      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_TRIGGER]->XferErrorCallback = TIM_DMAError ;

      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_TRIGGER], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, ((BurstLength) >> 8) + 1);
    }
    break;
    default:
    break;
  }
   /* configure the DMA Burst Mode */
   htim->Instance->DCR = BurstBaseAddress | BurstLength;//这里设置的是DBL和DBA

   /* Enable the TIM DMA Request */
   __HAL_TIM_ENABLE_DMA(htim, BurstRequestSrc);

   htim->State = HAL_TIM_STATE_READY;

  /* Return function status */
  return HAL_OK;
}
此函数中,只有第一个case语句及函数最后几句对我们有用。先看一个case语句中的调用函数

/**
  * @brief  Start the DMA Transfer with interrupt enabled.
  * @param  hdma: pointer to a DMA_HandleTypeDef structure that contains
  *               the configuration information for the specified DMA Channel.
  * @param  SrcAddress: The source memory Buffer address
  * @param  DstAddress: The destination memory Buffer address
  * @param  DataLength: The length of data to be transferred from source to destination
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
  /* Process locked */
  __HAL_LOCK(hdma);

  /* Change DMA peripheral state */
  hdma->State = HAL_DMA_STATE_BUSY;

   /* Check the parameters */
  assert_param(IS_DMA_BUFFER_SIZE(DataLength));

  /* Disable the peripheral */
  __HAL_DMA_DISABLE(hdma);

  /* Configure the source, destination address and the data length */
  DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);//设置源地址、目标地址、DMA传输次数
  /* Enable the transfer complete interrupt */
  /* Enable the Half transfer complete interrupt */
  /* Enable the transfer Error interrupt */
  __HAL_DMA_ENABLE_IT(hdma, DMA_IT_TC);// | DMA_IT_HT | DMA_IT_TE  只开DMA传输完成中断,不开半传输中断及错误中断

   /* Enable the Peripheral */
  __HAL_DMA_ENABLE(hdma);

  return HAL_OK;
}
其中函数:
  /* Configure the source, destination address and the data length */
  DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);//设置源地址、目标地址、DMA传输次数,这里有两个比较难以理解的寄存器,TIMx_DMAR和TIMx_DCR,不要去看TIM2的寄存器,去看TIM1的对应寄存器,应为后者讲的更详细些,数据手册如下:

5.jpg (52.23 KB, 下载次数: 0)

下载附件

2016-4-1 16:53 上传


有成果了吧..........

本着求真、务实、讲科学的态度,还要进行验证测试



爱则倾心
3楼-- · 2019-12-14 03:57
6.验证测试:
为了验证产生的5个脉冲信号是不是我们通过DMA传输的数据,我们产生三种规占空比的脉冲,修改如下:
uint32_t aSRC_Buffer[15] = {1199, 0,1000,
1199, 0,600,
1199, 0,100,
1199, 0,600,
1199, 0,1000,        
};

HAL_TIM_DMABurst_WriteStart(&htim2, TIM_DMABASE_ARR, TIM_DMA_UPDATE,
                              (uint32_t*)aSRC_Buffer, TIM_DMABURSTLENGTH_3TRANSFERS,15);
        HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);               
在DMA传输完成中断里添加:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);//停止脉冲输出及TIM2
编译,下载,观测:

22.jpg (16.09 KB, 下载次数: 0)

下载附件

2016-4-3 00:14 上传



波形对了,但是数量少了两个,为何???可以从上述分析为何前两个脉冲不对的原因中找到答案。
可以不再DMA传输完成中断里关闭定时器,让定制器一直开着,在DMA传输数据里关闭CH1的输出,但还是解决不了少两个的问题,不过,知其原因,抓此规律,可以满足产生数量可控、频率可调、占空比可调的PWM波了........
开始搞伺服电机控制了,此贴结贴,以后只回复问题,不再更新。


爱则倾心
4楼-- · 2019-12-14 04:33
爱则倾心 发表于 2016-4-3 00:21
6.验证测试:
为了验证产生的5个脉冲信号是不是我们通过DMA传输的数据,我们产生三种规占空比的脉冲,修改 ...

各位看官、朋友、大侠、做过伺服或步进控制的前辈们,谁能提供一个可用的MATLAB的安装程序???若能讲解一下如何做电机加减速的曲线拟合就更好了,可以一块把本帖打造成伺服或步进控制的精华帖,功德无量,好人一生平安 QQ553923543。
cqfeiyu
5楼-- · 2019-12-14 06:44
 精彩回答 2  元偷偷看……
NM2012
6楼-- · 2019-12-14 09:35
占个坑               

一周热门 更多>