搜索
bottom↓
回复: 86

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

  [复制链接]

出0入0汤圆

发表于 2016-3-29 20:27:51 | 显示全部楼层 |阅读模式
本帖最后由 爱则倾心 于 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的时钟源、通道及模式

若使用MSI,要使能LSE,以校准MSI
---->配置时钟

---->配置TIM2,在DMA settings,ADD  TIM2_UP,其余一律默认

---->生成代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

 楼主| 发表于 2016-3-31 12:49:51 | 显示全部楼层
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  需要另外做的工作
有空再写,睡觉了........





出0入0汤圆

 楼主| 发表于 2016-4-1 17:47:38 | 显示全部楼层
爱则倾心 发表于 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的对应寄存器,应为后者讲的更详细些,数据手册如下:


可以这么理解,DMAR中存放的是要传送至的起始寄存器到截止寄存器的地址,其由DCR中的内容决定,DCR中,DBL设置的是TIM一次DMA请求,DMA将传送的数据长度/次数,DBA设置的是TIM一次DMA请求,DMA将传送数据到的起始寄存器。
再来看DMA的数量寄存器:

这个寄存器设置的是DMA传送的次数,传送的字节数跟设置的传输类型有关BYTE/WORD/HALFWORD,没传送一次,该寄存器减一,减到零则产生DMA传输完成中断。

看一下这个函数,有一个问题:
HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStart(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress, uint32_t BurstRequestSrc,
                                              uint32_t* BurstBuffer, uint32_t  BurstLength)//,uint16_t  Bursttimes

看函数内容里这个BurstLength,赋给了TIM寄存器DCR的DBL,又赋给了DMA寄存器DMA_CNDTRx,这样的话每传输完一次TIM的DMA请求,进一次DMA传输完成中断,显然不满足我们的要求,我们用此方式就是为了产生一定数量的脉冲后才进一次中断,否则就直接用TIM溢出中断计数方式了。将此函数改造如下:
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 */                                                                          //((BurstLength) >> 8) + 1
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, 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;

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

   htim->State = HAL_TIM_STATE_READY;

  /* Return function status */
  return HAL_OK;
}

如此,就可以完成Bursttimes次DMA传输进一次DMA传输完成中断,产生的脉冲数为:Bursttimes/BurstLength。

在main里添加:
uint32_t aSRC_Buffer[15] = {1199, 0,600,
1199, 0,600,
1199, 0,600,
1199, 0,600,
1199, 0,600,       
};

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
编译,下载,观测:

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

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



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2016-4-3 00:21:32 | 显示全部楼层
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
编译,下载,观测:

我嘞个去,数量虽对,但前两个脉冲显然不对,其值为定时器初始化时设置的值,究其原因:
在使能定时器之前,前面程序有两次调用了下面这个函数:
/**
  * @brief  Time Base configuration
  * @param  TIMx: TIM peripheral
  * @param  Structure: TIM Base configuration structure
  * @retval None
  */
void TIM_Base_SetConfig(TIM_TypeDef *TIMx, TIM_Base_InitTypeDef *Structure)
{
  uint32_t tmpcr1 = 0;
  tmpcr1 = TIMx->CR1;

  /* Set TIM Time Base Unit parameters ---------------------------------------*/
  if (IS_TIM_COUNTER_MODE_SELECT_INSTANCE(TIMx))
  {
    /* Select the Counter Mode */
    tmpcr1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS);
    tmpcr1 |= Structure->CounterMode;
  }

  if(IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx))
  {
    /* Set the clock division */
    tmpcr1 &= ~TIM_CR1_CKD;
    tmpcr1 |= (uint32_t)Structure->ClockDivision;
  }

  TIMx->CR1 = tmpcr1;

  /* Set the Autoreload value */
  TIMx->ARR = (uint32_t)Structure->Period ;

  /* Set the Prescaler value */
  TIMx->PSC = (uint32_t)Structure->Prescaler;

  if (IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx))
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = Structure->RepetitionCounter;
  }

  /* Generate an update event to reload the Prescaler
     and the repetition counter(only for TIM1 and TIM8) value immediately */
  TIMx->EGR = TIM_EGR_UG;
}
此函数的最后一个语句:TIMx->EGR = TIM_EGR_UG;将产生一次TIM更新事件,此更新事件干了什么呢,手册上是这么说的:

可此时我并没开启定时器,为何开启后会产生两个初始化时的值的脉冲信号?跟STM32定时器的预加载寄存器和影子寄存器有关,我是这么理解的,使能定时器后,第一次溢出事件,比较值是第一次"TIMx->EGR = TIM_EGR_UG"传输的影子寄存器里的值,此时DMA进行了一次传输,原ARR里的值是第二次“TIMx->EGR = TIM_EGR_UG”时的值,又被传送给了影子寄存器,再一次溢出事件后影子寄存器里的值才是DMA传输的第一个值,不知道这样理解对不对,请高手、大侠指正.......
但是,开启定时器前,必须得有一次“TIMx->EGR = TIM_EGR_UG”,不然,第一次更新事件如何产生?DMA传输如何开始?如此理解,不知对不对,请高手、大侠指正.........
但是,我们可以想办法避开此问题。看一下使能定时器函数里做了哪些工作:
/**
  * @brief  Starts the PWM signal generation.
  * @param  htim : TIM handle
  * @param  Channel : TIM Channels to be enabled
  *          This parameter can be one of the following values:
  *            @arg TIM_CHANNEL_1: TIM Channel 1 selected
  *            @arg TIM_CHANNEL_2: TIM Channel 2 selected
  *            @arg TIM_CHANNEL_3: TIM Channel 3 selected
  *            @arg TIM_CHANNEL_4: TIM Channel 4 selected
  *            @arg TIM_CHANNEL_5: TIM Channel 5 selected
  *            @arg TIM_CHANNEL_6: TIM Channel 6 selected
  * @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
  /* Check the parameters */
  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));

  /* Enable the Capture compare channel *///这里,把下一句给屏蔽掉,不开启CH1的输出,在DMA传输里开启,即DMA传输从CCER寄存器开始写入相应值。
  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);   //ENABLE in dma transmit

  //if(IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
  //{
    /* Enable the main output */
  //  __HAL_TIM_MOE_ENABLE(htim);
  //}

  /* Enable the Peripheral */
  __HAL_TIM_ENABLE(htim);

  /* Return function status */
  return HAL_OK;
}

如上函数注释所述,把程序修改如下:
/* Capture Compare buffer1 */
uint32_t aSRC_Buffer[30] = {0x00,0,0,1199, 0,1000,
0x01,0,0,1199, 0,600,
0x01,0,0,1199, 0,100,
0x01,0,0,1199, 0,600,
0x01,0,0,1199, 0,600,
};
HAL_TIM_DMABurst_WriteStart(&htim2, TIM_DMABASE_CCER, TIM_DMA_UPDATE,
                              (uint32_t*)aSRC_Buffer, TIM_DMABURSTLENGTH_6TRANSFERS,30);
        HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);               
在DMA传输完成中断里添加:
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);//停止脉冲输出及TIM2
编译,下载,观测:


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


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2016-4-6 09:20:04 | 显示全部楼层
爱则倾心 发表于 2016-4-3 00:21
6.验证测试:
为了验证产生的5个脉冲信号是不是我们通过DMA传输的数据,我们产生三种规占空比的脉冲,修改 ...

各位看官、朋友、大侠、做过伺服或步进控制的前辈们,谁能提供一个可用的MATLAB的安装程序???若能讲解一下如何做电机加减速的曲线拟合就更好了,可以一块把本帖打造成伺服或步进控制的精华帖,功德无量,好人一生平安 QQ553923543。

出0入0汤圆

发表于 2016-3-29 20:34:54 | 显示全部楼层
带板凳来听课

出5入0汤圆

发表于 2016-3-29 21:05:19 | 显示全部楼层
占个坑               

出0入0汤圆

发表于 2016-3-29 21:29:58 | 显示全部楼层
严重关注!

出0入0汤圆

发表于 2016-3-29 21:56:20 | 显示全部楼层
静待楼主更新!

出0入0汤圆

发表于 2016-3-29 22:11:01 来自手机 | 显示全部楼层
严重关注

出0入0汤圆

发表于 2016-3-29 22:58:57 来自手机 | 显示全部楼层
搬凳子听讲

出0入0汤圆

发表于 2016-3-29 23:14:07 来自手机 | 显示全部楼层
搬小板凳来听课,正在收集资料准备做个小玩意

出0入0汤圆

发表于 2016-3-30 00:01:51 来自手机 | 显示全部楼层
我用过const uint16_t数组,还好变速过程只有几十步,占不了多少flash。如果运动前现算的话,看ram大小了,但一般也没有用到几万不加减速的吧。我用的步进,伺服不知道需要多少不。

出0入0汤圆

发表于 2016-3-30 00:34:08 来自手机 | 显示全部楼层
我想产生梯形波不用中断怎么做

出0入0汤圆

发表于 2016-3-30 10:56:11 | 显示全部楼层
STM32F103的PWM和TIMER,ST的芯片做的不如NXP。
NXP   32位计数器,32位预分频。

出0入0汤圆

发表于 2016-3-30 11:05:08 | 显示全部楼层
我使用过PWM捕获模式,使用DMA也可以行得通

出0入0汤圆

发表于 2016-3-30 11:13:49 来自手机 | 显示全部楼层
dma可以产生可变频率,可变占空比,可变数量脉冲

出100入85汤圆

发表于 2016-3-30 12:26:25 | 显示全部楼层
myxiaonia 发表于 2016-3-30 11:13
dma可以产生可变频率,可变占空比,可变数量脉冲

how to???????

出0入0汤圆

 楼主| 发表于 2016-3-30 22:29:52 | 显示全部楼层
sbusr 发表于 2016-3-30 00:01
我用过const uint16_t数组,还好变速过程只有几十步,占不了多少flash。如果运动前现算的话,看ram大小了, ...

我控的伺服电机要走900000步,按你说的思路做的话,flash大大滴被占用,可以利用内存不增方式的,加速到指定频率后,利用内存不增方式传递,指定传送数量即可。

出0入0汤圆

 楼主| 发表于 2016-3-30 22:38:20 | 显示全部楼层
myxiaonia 发表于 2016-3-30 11:13
dma可以产生可变频率,可变占空比,可变数量脉冲

是滴,你头像好熟悉,搜本贴话题资料时搜到过你,大侠若是能多明示一些,本菜就不用那么辛苦了...........

出0入0汤圆

 楼主| 发表于 2016-3-30 22:39:49 | 显示全部楼层

有时间我就会更新的,但是,谁能告诉我,帖子编辑一次后为嘛就不能编辑了???看来只能在楼下更新了

出0入0汤圆

发表于 2016-3-30 22:46:53 | 显示全部楼层
爱则倾心 发表于 2016-3-30 22:38
是滴,你头像好熟悉,搜本贴话题资料时搜到过你,大侠若是能多明示一些,本菜就不用那么辛苦了.......... ...

其实我是原理上分析可行性,并且是可以做到的

可惜我的工作根本用不到这么复杂的功能,因此我也没有动力去挑战这个事情。。。工作性质限制了我往深入方向发展,谁给推荐个有挑战性的岗位啊

出0入0汤圆

 楼主| 发表于 2016-3-30 22:52:00 | 显示全部楼层
gliet_su 发表于 2016-3-30 00:34
我想产生梯形波不用中断怎么做

不进TIM中断,传输完成后进DMA中断,你是指梯形加速曲线吧?加速是这么个过程,在频率1上走几步、在频率2上走几步.....加速时间到,升到最高频率.......你每个频率上走的步数少的话,可以几个频率点的传送值装进一个数组,传输完进DMA中断.......

出0入0汤圆

发表于 2016-3-31 13:14:40 | 显示全部楼层

出0入0汤圆

发表于 2016-4-1 18:29:12 | 显示全部楼层
不错,支持一下

出0入0汤圆

发表于 2016-4-1 18:58:03 | 显示全部楼层
进来听课

出0入0汤圆

发表于 2016-4-2 18:55:35 | 显示全部楼层
非常好,一直想用这个办法!

出0入71汤圆

发表于 2016-4-2 20:51:28 | 显示全部楼层
本帖最后由 yefeng022 于 2016-4-2 22:53 编辑

我用过之前论坛上讨论过的DMA+TIM的方式。
每一个通道的脉冲需要:用于传输周期寄存器的DMA*1  定时触发DMA传输的TIM×1  用于产生脉冲的TIM×1 用于脉冲个数计数的TIM×1
大致的原理是:用一个定时器固定周期触发DMA将算好的周期寄存器值传输给产生脉冲的TIM寄存器,再用另个TIM配置为级联模式,用于输出脉冲计数。
这种方式有坛友在STM32上实现了4通道的,受到启发我也在PIC的单片机上实现了4通道独立同时发脉冲。
这种方式有几个缺点:
1.DMA定时传输,意味着加速曲线是阶梯状的,无法做到每个相邻脉冲频率都均匀变化。(我是用的1000次DMA传输完成加速,实际上加速效果还是很好的)
2.无法预知加速脉冲个数,这样只能实现对称加减速。(用过这种方法的同学应该知道为什么)

你说的这种方式有些不同
1.加速步数可控,可以实现非对称加减速,并且每个脉冲的的寄存器值都可以连续变化,加速曲线更加平滑。
2.这个方式只能用于低细分或者低速场合,因为加速过程的脉冲数不能太多,否则存储空间够呛。固定加速模式还能放在flash,如果是动态加速模式(加速曲线每次运行可以变化)必须放在ram中就受到很大限制了。
3.非常节省定时器,一个定时器就搞定。

其实你给的这种思路非常好,就是太费内存了,我之前在matlab上计算过16细分下用0.5s加速到1000转(梯形加速)用的总脉冲数,远远超出了单片机ram的大小,直接放弃了。
另外,其实可以不用保证每个脉冲都是50%的占空比,把脉宽设置成驱动器可接受的最小脉宽就行了,这样就不用多计算和保存一个寄存器的值了,可以节约内存。

出0入0汤圆

 楼主| 发表于 2016-4-2 23:21:13 | 显示全部楼层
yefeng022 发表于 2016-4-2 20:51
我用过之前论坛上讨论过的DMA+TIM的方式。
每一个通道的脉冲需要:用于传输周期寄存器的DMA*1  定时触发DMA ...

感谢大侠的指点,关于你说的“因为加速过程的脉冲数不能太多,否则存储空间够呛”,加速完成匀速阶段,可以用内存不增方式,但加速阶段确实比较难办,加速阶段需占用多少空间还没计算,看来大侠对电机控制比较熟,我现在正在做伺服电机加减速这一块,若遇到问题还望不吝赐教,大侠最后给的建议非常好,另外,你怎么删除了一条回复,你说的那个中间一段时间无脉冲输出问题,我是这样干的,电机加速、匀速、减速,定时器始终不停,在DMA传输完成中断里切换内存地址或设置为内存不增方式传输,我验证了一下,脉冲输出是连续的。

出0入71汤圆

发表于 2016-4-2 23:43:32 | 显示全部楼层
爱则倾心 发表于 2016-4-2 23:21
感谢大侠的指点,关于你说的“因为加速过程的脉冲数不能太多,否则存储空间够呛”,加速完成匀速阶段,可 ...

删掉那条是我刚开始理解错了,但是频率高的时候中断处理时可能会多发脉冲。

可能因为我是用在步进电机上的原因,整个加速过程的脉冲数比较大,尤其是某些重载的电机。我想伺服电机应该不会这么敏感吧,毕竟伺服驱动器内部还有闭环控制的,给脉冲只是给指令的一种方式罢了。

出0入0汤圆

 楼主| 发表于 2016-4-3 00:25:27 | 显示全部楼层
yefeng022 发表于 2016-4-2 23:43
删掉那条是我刚开始理解错了,但是频率高的时候中断处理时可能会多发脉冲。

可能因为我是用在步进电机上 ...

嗯,我先按此思路做吧,遇到问题大家再一块探讨,别管怎么说,谢谢指点

出0入0汤圆

发表于 2016-4-3 12:26:53 来自手机 | 显示全部楼层
脉冲结束你是怎么处理的?

出0入0汤圆

发表于 2016-4-3 13:16:39 | 显示全部楼层
看我的帖子  

STM32的一个定时器输出4路频率是可以的

出0入0汤圆

 楼主| 发表于 2016-4-3 14:49:33 | 显示全部楼层
hmd420304805 发表于 2016-4-3 12:26
脉冲结束你是怎么处理的?

在DMA传输完成中断里你可以想怎么处理都行,关定时器、关匹配输出......

出0入0汤圆

 楼主| 发表于 2016-4-3 14:50:28 | 显示全部楼层
RAMILE 发表于 2016-4-3 13:16
看我的帖子  

STM32的一个定时器输出4路频率是可以的

嗯,以前没怎么研究过DMA,用好了发现挺赞

出0入20汤圆

发表于 2016-4-3 15:16:37 来自手机 | 显示全部楼层
我原先发过dma+io十定时器任意输出脉冲,个数控制简单,单个脉冲宽度甴定时控制。个

出0入0汤圆

发表于 2016-4-3 17:59:29 来自手机 | 显示全部楼层
爱则倾心 发表于 2016-4-3 14:49
在DMA传输完成中断里你可以想怎么处理都行,关定时器、关匹配输出......

慢了,你必须直接硬件关闭定时器!必须以最快的速度关闭,也就是说不能用软件去操作。一个中断响应的过程就会多发几个脉冲,最可怕的是,多发多少个你还没法统计

出0入0汤圆

发表于 2016-4-3 18:33:04 | 显示全部楼层
很好的办法

出0入0汤圆

 楼主| 发表于 2016-4-3 20:33:43 | 显示全部楼层
hmd420304805 发表于 2016-4-3 17:59
慢了,你必须直接硬件关闭定时器!必须以最快的速度关闭,也就是说不能用软件去操作。一个中断响应的过程 ...

我在多个频率点用两种方法做验证,一是进DMA传输完成中断立即关闭引脚输出、关闭定时器,二是利用DMA传输关闭引脚输出,即:在最后一次DMA输出关闭引脚输出,两种方法的结果都是:脉冲数比DMA传输次数少两个.........不排除这种验证方法有问题,大侠所论定有道理,还请大侠赐教

出870入263汤圆

发表于 2016-4-3 20:43:09 | 显示全部楼层
hmd420304805 发表于 2016-4-3 17:59
慢了,你必须直接硬件关闭定时器!必须以最快的速度关闭,也就是说不能用软件去操作。一个中断响应的过程 ...

你说的对。我就碰到这个困惑,于是不得不放弃DMA方式。

出870入263汤圆

发表于 2016-4-3 20:44:24 | 显示全部楼层
爱则倾心 发表于 2016-4-3 20:33
我在多个频率点用两种方法做验证,一是进DMA传输完成中断立即关闭引脚输出、关闭定时器,二是利用DMA传输 ...

请问:如何利用DMA传输关闭引脚输出?

出0入0汤圆

 楼主| 发表于 2016-4-3 20:47:08 | 显示全部楼层
armstrong 发表于 2016-4-3 20:44
请问:如何利用DMA传输关闭引脚输出?

/* Capture Compare buffer2 */
uint32_t aSRC_Buffer3[30] = {0x01,0,0,600, 0x00,500,
0x01,0,0,600, 0x00,300,
0x01,0,0,600, 0x00,100,
0x01,0,0,600, 0x00,100,
0x00,0,0,600, 0x00,300,
};

HAL_TIM_DMABurst_WriteStart(&htim2, TIM_DMABASE_CCER, TIM_DMA_UPDATE,
                              (uint32_t*)aSRC_Buffer, TIM_DMABURSTLENGTH_6TRANSFERS,30);


你看一下定时器寄存器CCER

出0入0汤圆

发表于 2016-4-3 23:16:05 | 显示全部楼层
爱则倾心 发表于 2016-4-3 20:33
我在多个频率点用两种方法做验证,一是进DMA传输完成中断立即关闭引脚输出、关闭定时器,二是利用DMA传输 ...

要么你用35L的方案做(我没试过,似乎可行)
要么老实用双定时器,看我的帖子!

出0入0汤圆

发表于 2016-4-4 02:17:52 | 显示全部楼层
hmd420304805 发表于 2016-4-3 23:16
要么你用35L的方案做(我没试过,似乎可行)
要么老实用双定时器,看我的帖子!
...

请问双定时器办法如何就能实现硬件关闭输出的

出0入0汤圆

 楼主| 发表于 2016-4-5 16:28:27 | 显示全部楼层
hmd420304805 发表于 2016-4-3 23:16
要么你用35L的方案做(我没试过,似乎可行)
要么老实用双定时器,看我的帖子!
...

STM32 用户手册中,定时器寄存器CCER:


假如,我要产生100个脉冲,设置DMA传输次数为101次,前100次,给CCER的最低位传输的是1,第101次给CCER最低位传输的是0(关闭输出),之后才进入DMA传输完成中断.....
如此,哪来的会多产生脉冲?请指教

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2016-4-5 16:32:23 | 显示全部楼层
mon51 发表于 2016-4-3 15:16
我原先发过dma+io十定时器任意输出脉冲,个数控制简单,单个脉冲宽度甴定时控制。个 ...

此法能做到同时产生多路频率不同、个数不同的脉冲吗?

出0入0汤圆

发表于 2016-4-5 22:17:32 来自手机 | 显示全部楼层
爱则倾心 发表于 2016-4-5 16:28
STM32 用户手册中,定时器寄存器CCER:



想了一下,这种方法是可以的,但是要在速度和内存大小上可能要做一个平衡。比如说,你要发脉冲频率在1mHz,你一个脉冲块由100个脉冲构成。那么耗费内存数量就是100*30=3000bytes。你为了方便计算。至少要定义10个这样的脉冲块。那么就是30000byte,ram占用恐怖。从速度上面来说,你发这些1m的脉冲,中断频率是多少呢?应该是1000000/100=10000t/s,这只是其中一路脉冲。如果你有4路。。。。

出0入0汤圆

发表于 2016-4-5 22:21:11 来自手机 | 显示全部楼层
内存占用哪里应该需要乘4,uint32_t

出0入0汤圆

 楼主| 发表于 2016-4-6 08:56:31 | 显示全部楼层
本帖最后由 爱则倾心 于 2016-4-6 09:23 编辑
hmd420304805 发表于 2016-4-5 22:17
想了一下,这种方法是可以的,但是要在速度和内存大小上可能要做一个平衡。比如说,你要发脉冲频率在1mHz ...


多谢指教 ,内存占用只在电机加速阶段,我的最高频率在100kHz,做64细分,加速时间拟定为0.5s,用MATLAB拟合加速曲线以确定加速期间每个频率点上的脉冲数,这块工作我正在做,因不懂MATLAB,需要占多少内存还没算。计划是若每个频率点需要的脉冲数较多,就用内存不增方式。加速完成后,用内存不增方式进行DMA传输,匀速后我最多要产生900000个脉冲,用一个6*4个字节的数组就可以了,然后倒查表进行减速到零......看来大侠是做过步进或伺服的控制,请多指教

出0入0汤圆

 楼主| 发表于 2016-4-6 08:59:48 | 显示全部楼层
hmd420304805 发表于 2016-4-5 22:21
内存占用哪里应该需要乘4,uint32_t

另外,打算用固定加速模式,加速数据表格存flash里

出0入20汤圆

发表于 2016-4-6 09:15:37 | 显示全部楼层
爱则倾心 发表于 2016-4-5 16:32
此法能做到同时产生多路频率不同、个数不同的脉冲吗?

频率是由定时器控制,路数由DMA的通道数决定。

出0入20汤圆

发表于 2016-4-6 09:17:32 | 显示全部楼层
hmd420304805 发表于 2016-4-5 22:17
想了一下,这种方法是可以的,但是要在速度和内存大小上可能要做一个平衡。比如说,你要发脉冲频率在1mHz ...

有很好的办法可以节省RAM。

出0入0汤圆

发表于 2016-4-6 09:21:10 | 显示全部楼层
好贴...... 必须顶......

出0入0汤圆

 楼主| 发表于 2016-4-6 09:24:53 | 显示全部楼层
mon51 发表于 2016-4-6 09:15
频率是由定时器控制,路数由DMA的通道数决定。

比如,产生四路不同频率的脉冲,大侠所述方法,恐怕得四个定时器吧?

出0入20汤圆

发表于 2016-4-6 09:40:25 | 显示全部楼层
爱则倾心 发表于 2016-4-6 09:24
比如,产生四路不同频率的脉冲,大侠所述方法,恐怕得四个定时器吧?

是的。用定时器中断,触发DMA。这个方法是为了输出高频率用的。低频就没有必要了。

出0入20汤圆

发表于 2016-4-6 09:43:06 | 显示全部楼层
这是我原来的帖子:http://www.amobbs.com/thread-5616766-1-1.html

出0入0汤圆

发表于 2016-4-6 09:49:36 | 显示全部楼层
楼主的3、4中方法各有利弊,目前项目中已实现方法3的S型曲线控制,方法4有空试试。

出0入0汤圆

 楼主| 发表于 2016-4-6 10:16:20 | 显示全部楼层
wq_601840968 发表于 2016-4-6 09:49
楼主的3、4中方法各有利弊,目前项目中已实现方法3的S型曲线控制,方法4有空试试。 ...

怎么做曲线拟合?可否赐教?

出0入0汤圆

发表于 2016-4-6 11:16:43 | 显示全部楼层
爱则倾心 发表于 2016-4-6 10:16
怎么做曲线拟合?可否赐教?

http://www.amobbs.com/thread-5520529-1-1.html
可以看看这篇帖子里的pdf中的算法思路

出0入0汤圆

 楼主| 发表于 2016-4-6 11:29:33 | 显示全部楼层
wq_601840968 发表于 2016-4-6 11:16
http://www.amobbs.com/thread-5520529-1-1.html
可以看看这篇帖子里的pdf中的算法思路

谢谢

出0入0汤圆

发表于 2016-4-6 12:07:08 来自手机 | 显示全部楼层
好贴,学习了

出0入20汤圆

发表于 2016-4-8 18:59:11 | 显示全部楼层
爱则倾心 发表于 2016-4-3 20:33
我在多个频率点用两种方法做验证,一是进DMA传输完成中断立即关闭引脚输出、关闭定时器,二是利用DMA传输 ...

我是用DMA输出最后一个低电平来关闭IO,第一个输出脉冲也是低电平。在初始化DMA时,脉冲个数要多2个才行!DMA发送完毕后,进入中断关闭自己和定时器。
输出脉冲很好!第一个输出脉冲和最后一个脉冲必须输出0.

出0入0汤圆

发表于 2016-4-8 19:36:46 | 显示全部楼层
标记!备用。

出870入263汤圆

发表于 2016-4-9 22:16:17 | 显示全部楼层
mon51 发表于 2016-4-8 18:59
我是用DMA输出最后一个低电平来关闭IO,第一个输出脉冲也是低电平。在初始化DMA时,脉冲个数要多2个才行 ...

你这个方法好像挺不错!值得一试啊!

出0入0汤圆

发表于 2016-4-9 23:48:32 | 显示全部楼层
还没用过CUBE,也在做电机控制,学习了

出0入0汤圆

发表于 2016-4-10 16:32:22 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2016-5-8 21:07:29 | 显示全部楼层
make pwm 私服电机

出0入0汤圆

发表于 2016-5-11 20:13:14 | 显示全部楼层
谢谢楼主,真的特别棒

出0入0汤圆

发表于 2016-12-28 10:21:23 | 显示全部楼层
谢谢楼主!正在学习DMA实现PWM计数,但是查的资料说这种方法不会太精确。

出0入0汤圆

发表于 2016-12-28 11:38:09 | 显示全部楼层
这个实现方法不错,顶!

出0入0汤圆

发表于 2016-12-28 12:00:07 | 显示全部楼层
牛啊,不错

出0入0汤圆

发表于 2017-8-12 01:03:41 来自手机 | 显示全部楼层
又看了一遍

出0入0汤圆

发表于 2018-1-14 20:55:09 | 显示全部楼层
好帖,收藏下。

出0入0汤圆

发表于 2018-3-12 08:18:22 | 显示全部楼层
思路不错,收藏下~

出0入0汤圆

发表于 2019-1-29 09:03:07 | 显示全部楼层
不错不错,学习了

出0入0汤圆

发表于 2019-1-29 09:24:16 | 显示全部楼层
收藏学习下,,,谢谢楼主咯

出0入0汤圆

发表于 2019-1-31 09:30:55 来自手机 | 显示全部楼层
Make正在搞这个,看看STM32有没有愉快的方式实现频率脉冲都可控

出0入0汤圆

发表于 2020-3-3 23:10:37 | 显示全部楼层
对PWM这块很感兴趣,感谢分享

出0入0汤圆

发表于 2020-12-10 09:51:29 | 显示全部楼层
DMA pwm输出, make一下

出0入4汤圆

发表于 2020-12-15 21:22:28 来自手机 | 显示全部楼层
stm32里好像是可以将一个定时器的输出定义到另一个定时器的输入,然后用比较匹配,计数到匹配值就产生中断,不需要每个脉冲都去软件计数的

出0入10汤圆

发表于 2021-2-22 15:57:33 | 显示全部楼层
不知道哪里出了问题,输出波形和你不一样,再研究研究

出0入0汤圆

发表于 2021-2-23 01:07:34 | 显示全部楼层
记号,朋友需要

出0入0汤圆

发表于 2021-2-23 06:21:14 来自手机 | 显示全部楼层
学习一下

出16390入6836汤圆

发表于 2021-2-24 07:08:59 | 显示全部楼层
迟到的酷贴!

出0入0汤圆

发表于 2021-2-24 08:49:27 | 显示全部楼层
想法独特,学习一下

出0入0汤圆

发表于 2021-2-25 12:31:42 | 显示全部楼层
DMA pwm脉冲输出,谢谢分享

出0入0汤圆

发表于 2021-2-26 13:38:43 | 显示全部楼层
步进电机控制   MARK
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 00:31

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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