文章目录[隐藏]
最近学习调试了利用 ID386 型号的驱动器来驱动三相电机,分享给大家,mcu是stm32f103
实现功能:
利用 ID386 驱动器驱动三相电机
在串口工具中输入命令来使电机按照命令转动,命令格式如下:
$motor 0999 180 1
$motor为帧头,0999为转速(这里单位未做转化),180为旋转角度(单位 度),1为方向
实现方式:
1.设置主从定时器输出脉冲给驱动器
这里感谢这个帖子提供的参考
STM32 精确控制PWM脉冲个数_博客-CSDN博客_stm32控制pwm脉冲数量
主要就是设置TIM4为主定时器,TIM3作为从定时器。TIM4设置为输出PWM,并通过设置参数 TIM_Prescaler和TIM_Period来设置PWM的频率,即脉冲的频率;TIM3用于计 脉冲 个数,将参数TIM_Prescaler设为零,参数TIM_Period所设置的值即为设定的脉冲数,当TIM3计数计到该值时,即产生中断。以下为定时器的配置。
上代码:
void TIM4_Master_TIM3_Slave_Config(u32 PulseFreq,u32 PulseNum)
{
GPIO_InitTypeDef GPIO_InitTypeStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruct4;
TIM_OCInitTypeDef TIM_OCInitTypeStruct4;
//NVIC_InitTypeDef NVIC_InitTypeStruct4;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruct3;
NVIC_InitTypeDef NVIC_InitTypeStruct3;
//TIM_OCInitTypeDef TIM_OCInitTypeStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitTypeStruct3.TIM_Prescaler=0;
TIM_TimeBaseInitTypeStruct3.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitTypeStruct3.TIM_Period = PulseNum;
TIM_TimeBaseInitTypeStruct3.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitTypeStruct3);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
NVIC_InitTypeStruct3.NVIC_IRQChannel=TIM3_IRQn;
NVIC_InitTypeStruct3.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitTypeStruct3.NVIC_IRQChannelSubPriority=2;
NVIC_InitTypeStruct3.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitTypeStruct3);
TIM_SelectInputTrigger(TIM3,TIM_TS_ITR3);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
TIM_SelectMasterSlaveMode(TIM3,TIM_MasterSlaveMode_Enable);
TIM3->CNT=0X00;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitTypeStruct);
TIM_TimeBaseInitTypeStruct4.TIM_Prescaler=71;
TIM_TimeBaseInitTypeStruct4.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitTypeStruct4.TIM_Period=PulseFreq;
TIM_TimeBaseInitTypeStruct4.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitTypeStruct4);
TIM_OCInitTypeStruct4.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitTypeStruct4.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitTypeStruct4.TIM_Pulse=0;
TIM_OCInitTypeStruct4.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitTypeStruct4.TIM_OCIdleState=TIM_OCIdleState_Set;
TIM_OC1Init(TIM4,&TIM_OCInitTypeStruct4);
TIM_CtrlPWMOutputs(TIM4,ENABLE);
TIM_SelectOutputTrigger(TIM4,TIM_TRGOSource_OC1Ref);
TIM_SelectMasterSlaveMode(TIM4,TIM_MasterSlaveMode_Enable);
TIM_SetCompare1(TIM4,PulseFreq/2);
TIM_Cmd(TIM3,ENABLE);
TIM_Cmd(TIM4,ENABLE);
}
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
TIM_SetCompare1(TIM4,0); //定时器4停止产生PWM,这样定时器3的时钟就停止了。如果重新开始计数只需重新设置即可
TIM3->CNT=0X00;
rotate_ok_flag = 1;
}
}
2.命令接收处理部分
从串口中断中接收上位机发送的命令,并从命令中提取所配置的速度、角度和方向,主要利于函数 sscanf(...) (这个函数是真好用><),并加上一些简单的逻辑。这里就提供一下中断服务函数,和数据处理函数。
上代码:
int speed;
int angle;
int direct;
u8 config_success_fail;
char ReceiveData[30];
u8 ReceiveDataCounter=0;
u8 DataReceiveHandle(u8 Res)
{
if(ReceiveDataCounter>=sizeof(ReceiveData))
{
ReceiveDataCounter = sizeof(ReceiveData)-1;
}
ReceiveData[ReceiveDataCounter] = Res;
ReceiveDataCounter++;
if( ReceiveData[0] == '$')
{
;
}
else
{
ReceiveDataCounter = 0;
}
if( ReceiveDataCounter == 17)
{
sscanf(ReceiveData, "%*s%d%d%d",&speed,&angle,&direct);
printf("\n\r======== 已接收字符: %s =========\n\r", ReceiveData);
if(speed<999||speed>9999) //当speed为999是,速度为120度每秒
{
config_success_fail = 0;
ReceiveDataCounter = 0;
printf("\n\r======== SPEED_ERROR =========\n\r");
printf("\n\r======== SPEED must be [0999,9999] =========\n\r");
return 0;
}
if(angle<0||angle>360)
{
config_success_fail = 0;
ReceiveDataCounter = 0;
printf("\n\r======== ANGLE_ERROR =========\n\r");
printf("\n\r======== ANGLE must be [000,360] =========\n\r");
return 0;
}
if(direct!=0&&direct!=1)
{
config_success_fail = 0;
ReceiveDataCounter = 0;
printf("\n\r======== DIRECT_ERROR =========\n\r");
printf("\n\r======== DIRECT must be 0 or 1 =========\n\r");
return 0;
}
printf("\n\r======== SPEED is %d =========\n\r",speed);
printf("\n\r======== ANGLE is %d =========\n\r",angle);
printf("\n\r======== DIRECT is %d =========\n\r",direct);
config_success_fail = 1;
ReceiveDataCounter = 0;
}
return 1;
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res = USART_ReceiveData(USART1); //读取接收到的数据
DataReceiveHandle(Res);
}
}
3.主函数
主函数直接上代码吧,没啥好说的
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
my_GPIO_Init();
speed = 0;
angle = 0;
while(1)
{
if( rotate_ok_flag ==1 ) //等待上一次转动结束
{
rotate_ok_flag = 0; //记得及时将flag清零,以便下次使用
while(1)
{
if(config_success_fail==1) //等待参数配置成功
{
config_success_fail = 0;
if(direct==1)
{
delay_us(20);
MotorDirect1;
delay_us(20);
}
else if(direct==0)
{
delay_us(20);
MotorDirect0;
delay_us(20); //这里的延时是根据驱动器手册的要求
}
TIM4_Master_TIM3_Slave_Config(speed,(u32)((angle/360.0)*3000)); //这里的3000是因为驱动器的设置,即3000个脉冲转1圈
break;
}
}
}
}
}
版权声明:本文为CSDN博主「白开水_0」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38918815/article/details/121913986
暂无评论