在SimpleFOC代码中,ABZ信号和霍尔电机信号处理方式很像,都是信号边沿触发中断,在中断中获取状态计算角度。
本次移植驱动板103和官方代码保持一致。
驱动板405采用的是定时器计数脉冲,处理方式不同。
一、ABZ编码器介绍
ABZ或者ABI,叫法不同但指的都是同一种编码器,也叫增量式编码器。
ABZ编码器最大的优点就是接口统一,兼容性好。
不像SPI接口的磁编码器,不同厂家的芯片甚至同一厂家的不同型号,通信协议各不相同,换个编码器就要重新写代码。
1.1、ABZ信号
A、B两相相差90度,可通过比较A相在前还是B相在前,以判断编码器的正转与反转,通过零位脉冲,可获得编码器的零位参考位。
一般采用四倍频技术,比如500线的编码器,转一圈可以获得2000个脉冲信号,
1.2、光电式编码器
最初的ABZ编码器是光电式的,最有名的品牌是安华高( Avago),分辨率叫“线”,比如500线就是500ppr(2000cpr)。
拆开后是这样的:下图
500线属于正常分辨率,1000线算是比较高的,2000线的电机肯定很贵了。不像现在的磁编码器,起步就是1024ppr。
1.3、磁编码器
磁编码器体积小,性能强,价格便宜,发展的很快,会越来越多的代替光电编码器。
磁编码器购买链接: 某宝购买
需要说明的是,有些磁编码器上电后可以输出当前绝对位置对应的脉冲数(需要配置相应的寄存器)。下图:
ABZ编码器每次上电都需要校准,所以使用时会有不方便的时候,但这个功能完美的解决了校准问题。这也是光电编码器望尘莫及的功能,充分体现了磁编码器的灵活。
1.4、差分信号
在传输距离比较长的情况下(比如大于1m),信号容易发生变形和干扰,需要将编码器输出的TTL信号转为差分信号,待到控制器端再转为TTL信号。
工业应用中,ABZ差分信号是应用非常普遍的编码器接口。
在驱动板103或者驱动板405上,可以只用A+,B+,Z+做为ABZ信号,A-,B-,Z-悬空即可。虽然不够严谨,但是做为开发学习还是可以的。
二、电机驱动板103
ABZ编码器电机的代码是独立的,没有与其它编码器混在一起,不需要在 myproject.h 中设置编码器类型,代码看起来更简洁。
2.1、读ABZ信号的cpr
如果你已经知道编码器的cpr,可以跳过当前小节。
2.1.1、接线
A—SCL1(PB6/TIM4_1)、B—SDA1(PB7/TIM4_2)、Z—PA12
电机驱动板103 购买链接:某宝购买
2804电机购买链接:某宝购买
2.1.2、编译,下载代码
2.1.3、转动电机,观察串口打印
第一次输出的cpr肯定不准确,因为电机上电为任意位置,
可以发送指令“2”读取任意时刻的cpr。
2.2、驱动ABZ电机M1
2.2.1、接线
————————————————
版权声明:本文为CSDN博主「loop222」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/loop222/article/details/129819585
#include "bsp_motor.h"
#include "bsp_uart.h"
unsigned int n = 0;
unsigned int enval = 0;
void DC_Brush_Motor_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//PA1úPWM
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 99; //10KHz peroid -->200ms
TIM_TimeBaseStructure.TIM_Prescaler = 71; //71+1= 72ؖƵ
//TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC2Init(TIM2, & TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
//PA2:AIN1 PA3:AIN2 P4;STBY
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
AIN1_HIGH;
AIN2_LOW;
}
//PB6ìPB7طΪ֨ʱǷҠëǷޓࠚìԃԚӉܯABРҠëǷ״!
void Encoder_Init_TIM4(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 0x0;
//TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
// TIM4->CCMR1 |= 1<<0;
// TIM4->CCMR1 |= 1<<8;
// TIM4->CCER &= ~(1<<1);
// TIM4->CCER &= ~(1<<5);
// TIM4->SMCR |= 3<<0;
// TIM4->CR1 |= 1<<0;
TIM_SetCounter(TIM4, 0);
TIM_Cmd(TIM4, ENABLE);
}
void TIM3_Base_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 49999; //50000us
TIM_TimeBaseStructure.TIM_Prescaler = 71; //71+1= 72ؖƵ
//TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{
static unsigned int encode_value = 0;
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
n++;
if(n == 20)
{
encode_value = TIM_GetCounter(TIM4);
// printf("Speed = %drpm\r\n", (3 * encode_value) / 78);
// printf("data = %d\r\n", rxData);
enval = (3 * encode_value) / 78;
n = 0;
TIM_SetCounter(TIM4, 0);
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
极对数按照实际写入,
voltage_sensor_align是电机零点校准时的电压值,需要根据电机功率设置,100W以内的电机基本属于小功率,可以设置的大一点,200W以上的属于大功率,设置的小一点。
如果不清楚电机功率,可以多试几个值,电源有电流显示的话观察下零点校准时的电流,只要在0.2A — 0.8A这个范围内就行,电流太小电机无力,校准可能不准确,电流太大电机会发热,容易损坏电机。
初次调试请选择 Type_voltage 模式,因为可以跳过电流环,电流环的PI参数非常难设置。
云台电机请选择 Type_voltage 模式,电机转速低且电流小,检测电流反而增加误差。
速度环的PI请自行设置,不清楚可以先把速度I参数设置为0,只用速度P参数,先让电机转起来再说。不同电机的PI参数不相同,具体请百度。
以下参数针对2804电机,编译后下载,
2.2.3、串口发送指令,观察电机运行
2.3、驱动ABZ电机M2
A—SCL2(PB10)、B—SDA2(PB11)、Z—SWD(PA13),
其实最初的设计是打算接PA11引脚的,但是调试代码的时候发现PB11和PA11是同一个中断源EXTI_Line11,并且在中断代码中无法区分,所以用SWD(PA13)来代替。
SWD(PA13)并没有设计外部上拉电阻,反复测试后确定只要代码中打开了内部上拉也可以的。
只是占用SWD后不能再用ST-link下载代码了,请大家使用串口下载。
串口下载的方法在 SimpleFOC移植STM32(二)—— 开环控制 的第3.2小节有说明,不清楚的可以去查看。
————————————————
版权声明:本文为CSDN博主「loop222」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/loop222/article/details/129819585