本帖最后由 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 上能正常实现; 区别只有细节
|
|