打印

GD32F103 定时器比较捕获触发DMA遇到问题

[复制链接]
1185|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hceng|  楼主 | 2022-8-27 09:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 hceng 于 2022-8-27 09:23 编辑

用定时器比较捕获触发DMA传输修改定时器ARR,实现步进电机的加减速功能
发现电机插上前后代码运行结果会不一样, 插上前能发出正常的脉冲, 插上后脉冲数就不正常而且定时器一直跑
个人认为GD32的定时器和STM32的差别很大; 影子寄存器没有很好的工作
初始化
void TIM_PWMOut_Init(const MOTOR_PORT_S* port)
{
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;   
    dma_parameter_struct dma_init_struct;
   
    PeriphClockEnable(port->DirPort.GPIO, ENABLE);
    PeriphClockEnable(port->StepPort.GPIO,ENABLE);
    PeriphClockEnable(port->TorqPort.GPIO,ENABLE);
    PeriphClockEnable(port->ENPort.GPIO,  ENABLE);
    PeriphClockEnable(port->StepTim,      ENABLE);
    PeriphClockEnable(port->TorqTim,      ENABLE);
    PeriphClockEnable(DMA0,               ENABLE);

    //方向
    gpio_init(port->DirPort.GPIO, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, BIT16(port->DirPort.Pin));
   
    //脉冲
    gpio_init(port->StepPort.GPIO, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, BIT16(port->StepPort.Pin));
   
    //使能   
    gpio_init(port->ENPort.GPIO, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, BIT16(port->ENPort.Pin));
   
    //力矩
    gpio_init(port->TorqPort.GPIO, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, BIT16(port->TorqPort.Pin));
   
    //定时器初始化
    timer_initpara.prescaler         = 1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_DOWN;
    timer_initpara.period            = 0;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(port->StepTim, &timer_initpara);
   
    //定时器通道初始化
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
    timer_channel_output_config(port->StepTim, port->StepTimChl, &timer_ocintpara);
   
    timer_channel_output_pulse_value_config(port->StepTim,port->StepTimChl, FeedTblDwn[0]>>1);
    timer_channel_output_mode_config(port->StepTim, port->StepTimChl, TIMER_OC_MODE_PWM1); //CNT>CHX高电平, <CHX低电平

    //关闭比较捕获预装载,使能arr预装载
    timer_channel_output_shadow_config(port->StepTim, port->StepTimChl, TIMER_OC_SHADOW_DISABLE);
    timer_auto_reload_shadow_enable(port->StepTim);
   
    //定时器dma配置
    timer_primary_output_config(port->StepTim, ENABLE); //主输出使能
    timer_dma_enable(port->StepTim, (TIMER_DMA_CH0D<<port->StepTimChl) ); //使能定时器捕获比较触发
   
    //力矩初始化
    timer_initpara.prescaler         = TORQ_PSC-1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_DOWN;
    timer_initpara.period            = TORQ_ARR;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(port->TorqTim, &timer_initpara);
    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
    timer_channel_output_config(port->TorqTim, port->TorqTimChl, &timer_ocintpara);
    timer_channel_output_pulse_value_config(port->TorqTim,port->TorqTimChl, 0);
    timer_channel_output_mode_config(port->TorqTim, port->TorqTimChl, TIMER_OC_MODE_PWM0); //CNT<CHX高电平, >CHX低电平
    timer_primary_output_config(port->TorqTim, ENABLE); //主输出使能
   
    //dma通道初始化
    dma_deinit(DMA0, port->DMAChannel);
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr  = (u32)0;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
    dma_init_struct.number       = 0;
    dma_init_struct.periph_addr  = (u32)TIMER_CAR_REG(port->StepTim);
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA0, port->DMAChannel, &dma_init_struct);
   
    dma_interrupt_enable(DMA0, port->DMAChannel, DMA_INT_FTF);   
    dma_memory_to_memory_disable(DMA0, port->DMAChannel);
   
    //nvic设置   
    nvic_irq_enable(port->DMA_Int_IRQ, port->DmaPrePrio, port->DmaSubPrio);
    nvic_irq_enable(port->TIM_CC_IRQ, 0, 0);    //使能比较捕获中断
   
    timer_enable(port->StepTim); //定时器使能
    timer_enable(port->TorqTim);
    dma_channel_enable(DMA0, port->DMAChannel);
   
    //产生更新实际设置arr=0
    timer_event_software_generate(port->StepTim, TIMER_EVENT_SRC_UPG);

}
dma传输完成中断
void MotorDMAInt(MOTOR_CTL_S*ctrl)
{
    const SPEED_TBL_S* tbl   = ctrl->tbl;
    const MOTOR_PORT_S* port = ctrl->port;
    u32 tim                  = port->StepTim;

    if( ctrl->hSteps )
    {
      TIMER_CAR(port->StepTim)=tbl->pTblSpdUp[0]; //第二次启动的时候,使能dma不会传输第一个数据;这里使用手动填充第一个到定时器
      if(ctrl->hSteps==1)
      {
         NVIC_SetPendingIRQ( ctrl->port->DMA_Int_IRQ );
      }
      else
      {
        DMA_Transfer(port->DMAChannel, (u32)&tbl->pTblSpdUp[1], ctrl->hSteps-1, 1);  
        TIMER_INTF(port->StepTim) = 0;
        TIMER_DMAINTEN(port->StepTim) |= (TIMER_DMAINTEN_CH0IE<<port->StepTimChl); //定时器比较中断时启动dma传输
      }
      ctrl->hSteps = 0;
    }
    else if( ctrl->kSteps )
    {
      u32 s = (ctrl->kSteps<=0xFFFF)? ctrl->kSteps : 0xFFFF;
      DMA_Transfer(port->DMAChannel, (u32)tbl->pTblSpdDw, s, 0);
//      DMA_CHCTL(DMA0, port->DMAChannel) |= DMA_CHXCTL_CHEN; //启动dma
      TIMER_INTF(port->StepTim) = 0;
      TIMER_DMAINTEN(port->StepTim) |= (TIMER_DMAINTEN_CH0IE<<port->StepTimChl);
      ctrl->kSteps -= s;
    }
    else if( ctrl->lSteps )
    {
      const u16* add = tbl->pTblSpdDw + tbl->usTblDwLen - ctrl->lSteps;
      if( TIMER_CAR(port->StepTim) == 0 ) //定时器没有启动,手动填充第一个到定时器
      {
        TIMER_CAR(port->StepTim)=tbl->pTblSpdUp[0]; //第二次启动的时候,使能dma不会传输第一个数据
        DMA_Transfer(port->DMAChannel, (u32)add[1], ctrl->lSteps-1, 1);
      }
      else
      {
        DMA_Transfer(port->DMAChannel, (u32)add, ctrl->lSteps, 1);
      }
      //在比较中断时启动dma传输
      TIMER_INTF(port->StepTim) = 0;
      TIMER_DMAINTEN(port->StepTim) |= (TIMER_DMAINTEN_CH0IE<<port->StepTimChl);
      ctrl->lSteps = 0;
    }
    else
    {
       DMA_CHCTL(DMA0, port->DMAChannel) &= ~DMA_CHXCTL_CHEN; //使能后会传输第一个到定时器
       OSQPost(gAssemQ, (void*)((ctrl==&MotorCtrl[0])? FEED_IDLE:SKIN_IDLE));      
    }
    GPIO_Inv(GPIOB, 11);
}
定时器比较捕获
void TIMER0_Channel_IRQHandler(void)
{
   GPIO_Inv(GPIOB, 10);
   TIMER_DMAINTEN(TIMER0)   &= ~TIMER_DMAINTEN_CH0IE;
   TIMER_INTF(TIMER0)       = 0; //清除中断
   DMA_CHCTL(DMA0, DMA_CH1) |= DMA_CHXCTL_CHEN; //启动dma
}

电机启动:
u8 MotorMakeReady(MOTOR_CTL_S* ctrl, s32 steps, u8 spd)
{
  const MOTOR_PORT_S* port  = ctrl->port;
  u32 tim                   = port->StepTim;
  const SPEED_TBL_S* tbl    = (ctrl==&MotorCtrl[0])? &m_feedTbl : &m_skinTbl;

  if( steps == 0 )
  {
     return FALSE;
  }
  
  while( MotorBuzy(ctrl) );

  //停止空闲定时器
  if( ctrl->idleTmr )
  {
     u8 err;
     OSTmrStop(ctrl->idleTmr, OS_TMR_OPT_NONE, ctrl, &err);
  }
  
  if( steps < 0 )
  {
      steps = -steps;
      MotorDirSet(&port->DirPort, 1);
  }
  else
  {
      steps = steps;
      MotorDirSet(&port->DirPort, 0);//正的顺时针旋转
  }
  
  if( steps > tbl->totalLen )
  {
    ctrl->hSteps  = tbl->usTblUpLen;
    ctrl->lSteps  = tbl->usTblDwLen;
    ctrl->kSteps  = steps - tbl->totalLen;
  }
  else
  {
    ctrl->hSteps  = steps>>1;
    ctrl->lSteps  = steps - ctrl->hSteps + 1;
    ctrl->kSteps  = 0;
  }
  
  TIMER_PSC(tim)  = spd;
  ctrl->tbl = tbl;

  return TRUE;
}
//电机运行
void MotorGoToRun(MOTOR_CTL_S* ctrl)
{
    u32 tim = ctrl->port->StepTim;
    NVIC_SetPendingIRQ( ctrl->port->DMA_Int_IRQ );
    //while( !TIMER_CAR(tim) );
//    TIMER_SWEVG(tim) |= TIMER_EVENT_SRC_UPG; //产生更新事件arr写入影子寄存器
}
//电机运行
u8 MotorRun(MOTOR_CTL_S* ctrl,u16 torq, s32 steps, u8 spd, u8 iswait)
{
    OSSchedLock();
    if( MotorBuzy( ctrl ) )
    {
        LOG("%s buzy when run\r\n",ctrl->port->name);
        OSSchedUnlock();
        return FALSE;
    }
    if( Torq_set(ctrl, torq) )
      delay_ms(TORQ_DLY_TM);
    MotorMakeReady(ctrl, steps, spd);   
    MotorGoToRun(ctrl);        
    OSSchedUnlock();
   
    if(iswait) while( MotorBuzy(ctrl) )OSTimeDly(1);   
   
          return TRUE;
}
实在不行只能换单片机,以上代码框架在STM32, CH32 上能正常实现; 区别只有细节

使用特权

评论回复
沙发
CharryW| | 2022-9-1 16:15 | 只看该作者
可以试下用updata做dma请求来搬运比较值

使用特权

评论回复
板凳
redone| | 2022-9-2 21:07 | 只看该作者
大段代码

使用特权

评论回复
地板
mutable| | 2022-9-4 21:14 | 只看该作者
这个没有实测风向么

使用特权

评论回复
5
tpgf| | 2022-9-8 11:36 | 只看该作者
步进电机的加减速取决于什么呢

使用特权

评论回复
6
晓伍| | 2022-9-8 11:46 | 只看该作者
具体如何搬运比较值呢

使用特权

评论回复
7
八层楼| | 2022-9-8 11:54 | 只看该作者
移植过来后修改了哪部分的代码呀

使用特权

评论回复
8
观海| | 2022-9-8 12:06 | 只看该作者
初始化代码结构有些乱

使用特权

评论回复
9
guanjiaer| | 2022-9-8 12:15 | 只看该作者
使用的是哪个定时器啊

使用特权

评论回复
10
heimaojingzhang| | 2022-9-8 13:11 | 只看该作者
电机的力矩如何计算呢

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

48

帖子

0

粉丝