电机,即电动机(Motor),也称之为马达,是把电能转换成机械能的一种设备。
① 按照电源进行分类:直流电动机、交流电动机;
② 按用途区分进行分类:驱动用电动机、控制用电动机
③ 按运转速度分类 :高速电动机、低速电动机、恒速电动机、调速电动机
直流有刷电机(Brushed DC Motor)是内含电刷装置的将直流电能转换成机械能的电动机。
特点
直流无刷电机(BLDC)是指无电刷和换向器(或集电环)的电机,又称无换向器电机。
特点
1、没有了碳刷结构,干扰小,噪音低,运转流畅,高速,而且寿命更长
2、控制较为复杂,可以使用方波或者正弦波换相
3、主要应用场景:四轴飞行器,汽车,工业工控,发动机等等
外转式-四轴飞行器
内转式-工业控制
步进电机是一种把电脉冲信号转换为角位移或线位移的电动机。
特点
1、控制简单,低速扭矩大、速度取决脉冲频率、角位移取决脉冲个数等特点
2、存在空载启动频率,不可高于该频率否则就会丢步甚至是堵转。
3、主要应用场景:3D打印机,绘图仪,数控机床等等
伺服电机(servo motor)可以理解为绝对服从控制信号指挥的电机
特点
1、可使控制速度,位置精度非常准确,效率高,寿命长
2、驱动器可设置电机工作在转速、转矩、位置等模式
3、价格昂贵,一套至少都是大几k以上;控制较复杂。
4、主要应用场景:自动化生产线,机器人,自动化工业设备等等
舵机(Servo)实际上可以看作一个伺服电机,主要由直流电机、减速齿轮组、角度传感器、控制电路构成
特点
1、一般而言旋转角度范围在0-180°
2、闭环系统,可以反馈转动的角度信息
3、通过控制PWM脉冲占空比大小,指定输出轴的旋转角度
4、主要应用场景:飞机的舵面,机器人关节等等
直流有刷电机(BDC)是一种内含电刷装置的将直流电能转换成机械能的电动机。在允许的范围之内,供电即可工作,只需要调整电压,即可调整电机的转速
拥有良好的调速性能
直流有刷电机结构包含:定子、转子、电刷和换向器
定子:产生固定的磁场
转子:由一个或多个绕组构成,通电后在磁场中受力运动
电刷:将外部电流输入到转子绕组上
换向器:改变绕组中电流的流向
优点 | 缺点 |
---|---|
驱动简单、操控方便、成本低 | 廉寿命短、可靠性差、换向火花易产生电磁干扰 |
直流有刷电机常被应用于电动玩具、砂轮机、电风扇、低端电动自行车等方面
对于扭矩有要求的场所我们会加上减速齿轮,以增大扭矩
电机的输出功率是一定的,电机的转速跟扭矩成反比,通过加装减速齿轮,起到降低速度,提高扭矩的作用。
参数 | 解释 |
---|---|
额定电压 | 电机正常工作的电压 |
额定电流 | 也叫负载电流,电机带负载正常工作时的电流。电机不带负载的电流叫空载电流。如果电机的负载太大导致电机堵转,这时的电流叫做堵转电流。通过分析电机的电流就可以知道电机的工作是否正常。 |
额定转速 | 也叫负载转速,单位是 r/min,也常用 RPM 表示 |
额定扭矩 | 电机额定电流下输出力的大小,单位常用 kg · cm |
减速比 | 电机原始转速和经过减速器之后转速的比值,表示为N:1 |
注意:不要使用过大负载,防止堵转造成电机过热
左手四指和大拇指伸直,使其相互垂直。磁场方向垂直穿过掌心,四指指向电流方向,大拇指所指的方向即所受安培力方向。
嗯嗯学费了
S、N:定子磁极
A、B:电刷
C、D:换向器
线圈:转子绕组
磁场方向:N极到S极
换向器的作用是,当转子转过一定角度时,对于转子而言,电流反向,此时转子的受力依旧保持不变。
反转原理:改变电流方向
测量转速可以采用编码器计数的原理。磁电编码器由磁盘和霍尔传感器组成。磁盘是由一个个交替的S极和N极组成,利用霍尔传感器的霍尔效应(即霍尔传感器对S极和N极感知的变化),可以生成脉冲信号。
IIC:Inter Integrated Circuit,集成电路总线,是一种同步 串行 半双工通信总线。
① 由时钟线SCL和数据线SDA组成,并且都接上拉电阻,确保总线空闲状态为高电平
② 总线支持多设备连接,允许多主机存在,每个设备都有一个唯一的地址
③ 连接到总线上的数目受总线的最大电容400pf限制
④ 数据传输速率:标准模式100k bit/s 快速模式400k bit/s 高速模式3.4Mbit/s
起始信号(S):当SCL为高电平时,SDA从高电平变为低电平
停止信号§:当SCL为高电平时,SDA从低电平变为高电平
应答信号:上拉电阻影响下SDA默认为高,而从机拉低SDA就是确认收到数据即ACK,否则NACK
数据先发送高位
数据以字节(8bit)传输
数据在SCL高电平稳定
八位数据发送完成释放SDA,主机把SDA数据线的控制权交给从机,
Start and Stop
void iic_start(void)
{ /* SCL为高电平期间, SDA从高电平往低电平跳变*/
IIC_SDA ( 1 );
IIC_SCL ( 1 );
iic_delay( );
IIC_SDA ( 0 );
iic_delay( );
IIC_SCL ( 0 );
iic_delay( ); /* 钳住总线, 准备发送/接收数据 */
}
void iic_stop(void)
{ /* SCL为高电平期间, SDA从低电平往高电平跳变*/
IIC_SDA ( 0 );
iic_delay( );
IIC_SCL ( 1 );
iic_delay( );
IIC_SDA ( 1 ); /* 发送总线停止信号*/
iic_delay( );
}
主机还需要干的事情
uint8_t iic_wait_ack (void) /* return 1:fail 0:succeed*/
{
IIC_SDA (1); /* 主机释放SDA线 ,把主动权交给从机*/
iic_delay( );
IIC_SCL (1); /* 从机返回ACK*/
iic_delay( );
if ( IIC_READ_SDA ) /* SCL高电平读取SDA状态*/
{
iic_stop(); /* SDA高电平表示从机nack */
return 1;
}
IIC_SCL(0); /* SCL低电平表示结束ACK检查 */
iic_delay( );
return 0;
void iic_ack(void)//继续向从机要数据
{
IIC_SCL (0);
iic_delay( );
IIC_SDA (0); /* 数据线为低电平,表示应答 */
iic_delay( );
IIC_SCL (1);
iic_delay( );
}
void iic_nack(void)
{
IIC_SCL (0);
iic_delay( );
IIC_SDA (1); /* 数据线为高电平,表示非应答 */
iic_delay( );
IIC_SCL (1);
iic_delay( );
}
发送1字节数据
void iic_send_byte(uint8_t data)
{
for (uint8_t t = 0; t < 8; t++)
{ /* 高位先发 */
IIC_SDA((data & 0x80) >> 7);
iic_delay( );
IIC_SCL ( 1 );
iic_delay( );
IIC_SCL ( 0 );
data <<= 1; /* 左移1位, 用于下一次发送 */
}
IIC_SDA ( 1 ); /* 发送完成,主机释放SDA线 */
}
读取1字节数据
uint8_t iic_read_byte (uint8_t ack) /* 1:ack 0:nack*/
{
uint8_t receive = 0 ;
for (uint8_t t = 0; t < 8; t++)
{ /* 高位先输出,先收到的数据位要左移 */
receive <<= 1;
IIC_SCL ( 1 );
iic_delay( );
if ( IIC_READ_SDA ) receive++;
IIC_SCL ( 0 );
iic_delay( );
}
if ( !ack ) iic_nack();
else iic_ack();
return receive;
}
EEPROM是一种掉电后数据不丢失的储存器,常用来存储一些配置信息,在系统重新上电时就可以加载。AT24C02是一个2K bit(256byte)的EEPROM存储器,使用IIC通信方式。
A0/1/2 : 设备地址决定引脚
WP : 写保护引脚(接0就是关闭写保护)
SCL : 时钟线
SDA : 数据线
AT24C02通讯地址
设备地址不包括读写位
通讯地址有包括读写位
AT24C02支持的读写操作
写操作
AT24C02支持字节写模式和页写模式。
字节写模式就是一个地址一个数据进行写入。
页写模式就是连续写入数据。只需要写一个地址,连续写入数据时地址会自增,但存在页的限制,超出一页时,超出数据覆盖原先写入的数据。但读会自动翻页。
读操作
AT24C02支持当前地址读模式,随机地址读模式和顺序读模式。
当前读模式是基于上一次读/写操作的最后位置继续读出数据。
随机地址读模式是指定地址读出数据。
随机地址读模式是指定地址读出数据。
数据有效性
IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
起始信号
地址和方向
应答信号从机发送应答,主机等待应答
内存地址
应答信号从机发送应答,主机等待应答
数据内容
应答信号从机发送应答,主机等待应答
停止信号
注意:EEPROM比较慢,必须等到10ms后再写下一个字节
起始信号
地址和方向
应答信号从机发送应答,主机等待应答
内存地址
应答信号从机发送应答,主机等待应答
(以上步骤的目的是告诉从机主机要操作的内存地址)
起始信号
地址和方向就是最后一位读写位要置1
应答信号从机发送应答,主机等待应答
数据内容
应答信号
停止信号
硬件和软件IIC对比
IIC | 用法 | 速度 | 稳定性 | 管脚 |
---|---|---|---|---|
硬件IIC | 比较复杂 | 快 | 较稳定 | 需使用特定管脚 |
软件IIC | 操作过程比较清晰 | 较慢 | 稳定 | 任意管脚,比较灵活 |
IIC配置步骤
1、使能SCL和SDA对应时钟__HAL_RCC_GPIOB_CLK_ENABLE()
2、设置GPIO工作模式SDA开漏/SCL推挽输出模式,使用HAL_GPIO_Init初始化
3、编写基本信号 起始信号 停止信号 应答信号
主机:send ack send nack
从机:wait ack
4、编写读和写函数
iic_read_byte
iic_send_byte
注意:发送完成,主机释放SDA
为什么IIC总线SDA建议用开漏模式?
IIC的SDA脚即要作为输出,又要作为输入,用开漏输出模式,很好实现输出输入共用,避免IO模式频繁切换带来的麻烦。
输出时:主机(MCU)输出0,可以拉低信号,来实现低电平发送,主机输出1(实际不起作用),由外部上拉电阻上拉,实现高电平发送。
输入时:主机(MCU)设置输出1状态,此时因为MCU无法输出1,相当于释放了SDA脚,此时外部器件可以主动拉低SDA脚/释放SDA脚(同样由上拉电阻提供“输出1的功能”),实现SDA脚的高低电平变化。
由于开漏输出模式下,MCU还是可以读取IDR状态寄存器,来获取引脚高低电平,因此MCU读取IDR,即可获得SDA脚的高低电平状态,从而实现输入检测。
AT24C02
1、初始化IIC接口
2、编写写入/读取一个字节数据函数 遵循时序流程编写
3、编写连续读和连续写函数(在2的基础上进行实现
)
IIC
Master features 主模式特性
I2C Speed Mode: IIC模式设置 快速模式和标准模式。实际上也就是速率的选择。
I2C Clock Speed:I2C传输速率,默认为100KHz
Slave features 从模式特性
Clock No Stretch Mode: 时钟没有扩展模式
IIC时钟拉伸(Clock stretching)
clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
Primary Address Length selection: 从设备地址长度 设置从设备的地址是7bit还是10bit 大部分为7bit
-Dual Address Acknowledged: 双地址确认
Primary slave address: 从设备初始地址
USART
我们需要将AT24C02中存储的数据发送到上位机上
需要用到的函数
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
*hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2
DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0
MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1
MemAddSize: 从机寄存器地址字节长度 8位或16位
写入数据的字节类型 8位还是16位
I2C_MEMADD_SIZE_8BIT
I2C_MEMADD_SIZE_16BIT
抑或是用之前串口使用的printf函数也可以
MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
加速度计只需要测量出力的大小就能通过F=ma计算出加速度
(测力计)
角速度只需要积分就能算出角度
引脚 | 功能 |
---|---|
VCC、GND | 电源 |
SCL、SDA I2C | 通信引脚 |
XCL、XDA | 主机I2C通信引脚 |
AD0 | 从机地址最低位 |
INT | 中断信号输出 |
左下角的六个排针中大多数都是最小系统板固定死了,就是那个XDA和XCL可以为以后外接磁力计和气压计和气压计。MPU6050可以直接读取扩展芯片的数据,在MPU6050芯片中会有DMP单元可有进行数据融合和姿态解算。 | |
左上角是LDO供电模块能输出稳定的3.3v电压给芯片端供电 |
左上角是外部时钟的输出引脚和输入引脚(但其实大多数情况是用内部时钟的)
左边是Selftest就是mpu6050自带的一个自测模块,使能时会向传感器施加一个外力,使能时又会得到一个数据,两个数据相减如果在对应芯片手册的允许范围之内,就是正常的e
灰色部分是三轴加速计和三轴陀螺仪外加一个温度传感器(本质上都是可变电阻,通过分压后输出模拟电压)
温度传感器下边的那个东西是电荷泵升压
右侧引脚输出部分依次是:中断寄存器、FIFO先入先出寄存器(对数据流进行缓存)、配置继勋奇、传感器寄存器、数值运动春初器
最右侧是GPIO接口跟用于与扩展设备进行通讯的接口
serial Interface Bypass Mux是接口旁路选择器,就是一个开关,拨到上面MPU6050和他的拓展设备就都是STM32的从机,如果拨到下边STM32是MPU6050的主机,MPU6050是其他拓展设备的主机,下边是供电
实际上我们只需要一个IIC使能、读取姿态传感器的数据并反馈到OLED/串口上即可
实际的配置可参考小小白的博客
添加链接描述
亲测有效
笔者的直流电机是跟着正点原子学的,由于所使用的单片机和驱动板与正点原子的板子并不相同(我的板子是STM32F103C8T6和TB661FNG),在跟着他们学电机的过程中会加入自己的理解和与他们开源的代码进行本土化适配的想法。再说一次,正点原子——我的超人。
PWM互补输出和死区控制的代码与之前在高级定时器所学内容一致。
输入引脚 | 输入引脚 | 输出引脚 | 输出引脚 |
---|---|---|---|
IN | SD | HO | LO |
- | 0 | 0 | 0 |
0 | 1 | 0 | 1 |
1 | 1 | 1 | 0 |
void dcmotor_init(void)
{
SHUTDOWN_GPIO_CLK_ENABLE(); /* PF口时钟使能 */
GPIO_InitTypeDef gpio_init_struct;
/* SD引脚和IN引脚共同是控制输出引脚输出的正负 */
/* 当SD引脚被设置为0时电机停止输出 */
/* SD引脚设置,设置为推挽输出 */
gpio_init_struct.Pin = SHUTDOWN1_Pin|SHUTDOWN2_Pin;
/*初始化的两个引脚都是SD引脚*/
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SHUTDOWN1_GPIO_Port, &gpio_init_struct);
HAL_GPIO_WritePin(GPIOF, SHUTDOWN1_Pin|SHUTDOWN2_Pin, GPIO_PIN_RESET); /* SD拉低,关闭输出 */
dcmotor_stop(); /* 停止电机 */
dcmotor_dir(0); /* 设置正转 */
dcmotor_speed(0); /* 速度设置为0 */
dcmotor_start(); /* 开启电机 */
}
对于我的TB661FNG就应该是
AIN1 | AIN2 | 状态 |
---|---|---|
0 | 0 | 停止 |
1 | 0 | 正转 |
0 | 1 | 反转 |
BIN1 | BIN2 | 状态 |
– | – | – |
0 | 0 | 停止 |
1 | 0 | 正转 |
0 | 1 | 反转 |
原理图 | ||
我的TB661FNG可以同时驱动两个到电机,两个电机驱动的H桥就是分别对应的AIN1、AIN2和BIN1、BIN2。所以我这边初始化要将所有的输入引脚置0. |
HAL_GPIO_CLK_ENABLE();
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = AIN1|AIN2;
/*初始化的两个引脚都是SD引脚*/
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SHUTDOWN1_GPIO_Port, &gpio_init_struct);
HAL_GPIO_WritePin(GPIOF, AIN1|AIN2, GPIO_PIN_RESET); /* SD拉低,关闭输出 */
void dcmotor_start(void)
{
ENABLE_MOTOR;
/* 拉高SD引脚,开启电机 */
}
#define ENABLE_MOTOR HAL_GPIO_WritePin(SHUTDOWN1_GPIO_Port,SHUTDOWN1_Pin,GPIO_PIN_SET)
/*视频里正点原子的motor启动函数是还有一行定时器使能的代码*/
/*程序源码中被弄到定时器初始化那里了*/
void dcmotor_stop(void)
{
DISABLE_MOTOR
/* 拉低SD引脚,关闭电机 */
}
#define DISABLE_MOTOR HAL_GPIO_WritePin(SHUTDOWN1_GPIO_Port,SHUTDOWN1_Pin,GPIO_PIN_RESET)
/**
* @brief 电机旋转方向设置
* @param para:方向 0正转,1反转
* @note 以电机正面,顺时针方向旋转为正转
* @retval 无
*/
void dcmotor_dir(uint8_t para)
{
HAL_TIM_PWM_Stop(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1); /* 关闭主通道输出 */
HAL_TIMEx_PWMN_Stop(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);/* 关闭互补通道输出 */
if (para == 0) /* 正转 */
{
HAL_TIM_PWM_Start(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);/* 开启主通道输出 */
}
else if (para == 1) /* 反转 */
{
HAL_TIMEx_PWMN_Start(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);/* 开启互补通道输出 */
}
}
我感觉我可以不用互补通道就能实现正反转的切换
AIN1 | AIN2 | 状态 |
---|---|---|
0 | 0 | 停止 |
1 | 0 | 正转 |
0 | 1 | 反转 |
这个玩意他本身就是一个H桥 | ||
只需要用HAL_GPIO_WritePin(GPIOx,GPIO_PIN_x,1/0);就可以控制正转反转 |
通过改变比较值就能达到限速的目的
void dcmotor_speed(uint16_t para)
{
__HAL_TIM_SetCompare(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1, para);
}
编码器:一种将直线位移、角位移数据转换为脉冲信号、二进制编码的设备。
常用于测量物体运动的位置、角度或者速度
编码器可以按照检测原理、编码类型进行分类
1.光电+绝对式
2.光电+增量式
3.磁电+绝对式
4.磁电+增量式
磁电+增量式:利用霍尔效应,将位移转换成计数脉冲,用脉冲个数计算位移和速度
分辨率:编码器可以测量的最小距离。 对于增量式编码器,分辨率即转轴每旋转一圈所输出的脉冲数(PPR)
精度:编码器输出的信号数据与实际位置之间的误差,常用 角分 ′ 、角秒 ″ 表示
最大响应频率:编码器每秒能输出的最大脉冲数,单位Hz,也称为PPS
最大转速:指编码器机械系统所能承受的最高转速
STM32定时器的编码器接口模式就相当于带有方向选择的外部时钟
当编码器电机正转的时候,输出的PWM波是以A的上升沿为初相,编码器电机反转的时候是以B的上升沿为初相。当然我的电机貌似也是两个霍尔传感器的相位差是90度。
编码器正转(此时为向上计数模式)
两个PWM波只能是从定时器的通道一和通道二进行输入。进过滤波器和边沿检测器后输入TI1FP1,然后接到编码器接口,编码器接口再接到下方的时基模块。由此将编码器输出的脉冲信号转化为计数值吗。
首先需要控制寄存器:TIMx 从模式控制寄存器 (TIMx_SMCR),控制位 2:0 SMS:从模式选择
SMC位 | 从模式 | 倍频 |
---|---|---|
001 | 计数器根据 TI1FP1 电平,仅在 TI2FP2 边沿递增/递减计数 | 二倍 |
010 | 计数器根据 TI2FP2 电平,仅在 TI1FP1 边沿递增/递减计数 | 二倍 |
011 | 计数器在 TI1FP1 和 TI2FP2 的边沿都计数,计数的方向取决于另外一个信号的电平 | 四倍 |
例如001:计数器根据 TI1FP1 电平,仅在 TI2FP2 边沿递增/递减计数 |
正转时的PWM波
由于正转时的在A相高电平期间B相都是上升沿,在A相低电平期间B相都是低电平,所以就都是递增计数模式。反转的时候,在A相高电平期间B相都是下降沿,在A相低电平的时候B相都是上升沿,所以都是递减计数模式。
010模式也是一致的
010:计数器根据 TI2FP2 电平,仅在 TI1FP1 边沿递增/递减计数
当前总计数值 = 计数器当前值 + 溢出次数 * 65536
当计数模式是递增计数模式的时候,如果计数值中断就在重复计数寄存器里+1,这样就能算出真实值。单计数模式是递减计数模式的时候,计数器TIMx_CNT的复位值是0,由于向下递减计数器马上会发生溢出中断,此时溢出次数减1.再用第二次的值减去第一次的值就能算出变化量。由于再递减计数模式的时候,第二次的值减去第一次的值算出来是负数,此时说明计数器反转了。通过变化量的正负就能算出正转/反转了。
在TIMx控制寄存器(TIMx_CR1)
位4 DIR写0是计数器递增计数写1是计数器递减计数,这样我们才能在计数溢出时,读取到溢出方向,后续才能计算总的计数变化量
函数 | 主要功能 |
---|---|
HAL_TIM_Encoder_Init() | 初始化定时器基础参数及编码器接口 |
HAL_TIM_Encoder_Start() | 开启编码器接口通道 |
HAL_TIM_PeriodElapsedCallback() | 定时器更新中断回调函数 |
__HAL_TIM_IS_TIM_COUNTING_DOWN() | 读取DIR位,判断计数方向 |
关键结构体介绍(编码器初始化结构体) |
typedef struct
{
uint32_t EncoderMode; /* 编码器模式(设置在A/B/AB检测,这也决定是2/2/4倍频) */
uint32_t IC1Polarity; /* 输入极(反相/不反相)性(边沿检测器)*/
uint32_t IC1Selection; /* 输入通道选择 */
uint32_t IC1Prescaler; /* 时钟分频因子 */
uint32_t IC1Filter; /* 滤波器 */
uint32_t IC2Polarity;
uint32_t IC2Selection;
uint32_t IC2Prescaler;
uint32_t IC2Filter;
} TIM_Encoder_InitTypeDef;
笔者使用的主控是STM32F407ZGT6,电机驱动模块为AT8236
RCC
选择外部高速晶振
SYS
TIM
定时器1
首先需要跑通下面这些简单的代码才能继续走
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);//打开定时器的encoder模式
Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1);//可以获得当前电机的转向
enc1 = (__HAL_TIM_GET_COUNTER(&htim1));//获取encoder编码器的计数值
在主函数中写初始化定时器与中断
HAL_TIM_Base_Start_IT(&htim7);//打开定时器中断
HAL_TIM_Base_Start_IT(&htim1);//打开定时器中断
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//使能PWM输出
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 0);//设置PWM比较值
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //开启编码器定时器
__HAL_TIM_ENABLE_IT(&htim1,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理
中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
unsigned char Direction;
unsigned int enc1;
/*判断当进入定时器7中断时*/
if(htim->Instance == TIM7)
{
// CNT++;
// Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1);//可以获得当前电机的转向
// enc1 = (__HAL_TIM_GET_COUNTER(&htim1));//获取encoder编码器的计数值
// if(CNT%100==0)
// {
// printf("Direction=%d,enc1=%d\r\n",Direction,enc1);
// CNT=0;
// }
Encode_now = get_encode();
printf("%lf\r\n",speed);
var=Encode_now-Encode_old;
Encode_old=Encode_now;
speed=(double)var*10/1320;
}
if(htim->Instance == TIM1)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1)) /* 判断CR1的DIR位 */
{
encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
encode_count++; /* DIR位为0,也就是递增计数 */
}
}
}
测速的代码其实很简单
int get_encode(void)
{
return (int32_t)__HAL_TIM_GET_COUNTER(&htim1) + encode_count * 500; /* 当前计数值+之前累计编码器的值=总的编码器值 */
}
在收取到编码器脉冲次数得基础上/一圈脉冲数/倍频数/采样周期即可
解释一下中断函数回调函数,中断回调函数的溢出之后,读取Direction的值判断电机正转/反转(其实在电机开始转动输出PWM波就可以判断正转和反转,这是由于正转和反转的相位是不一样的)。
挺突然的 冒泡排序
int i , j , temp;
int data[ 10 ] = { 1 , 5 , 4 , 3 , 9 , 10 , 15 , 14 , 6 , 0 } ;
for ( i = 10 ; i >= 1 ; i-- ) /* 冒泡排序 */
{
for ( j = 0 ; j < ( i – 1 ) ; j++ )
{
if ( data[ j ] > data[ j + 1] ) /* 数值大小比较 */
{
temp = data[ j ] ; /* 数值换位 */
data[ j ] = data[ j + 1 ] ;
data[ j + 1 ] = temp ;
}
}
}
通过两个嵌套的循环,每次循环两两交换位置,把最大的数交换到后面去
在电机中的运用:减去最大值和最小值 剩下的值取平均数就能达到滤波的效果
滤掉高频的部分(为了稳定性减少响应速度)
Y(n) = q * X(n) + (1-q) * Y(n-1)
Y(n)是本次滤波输出值
Y(n-1)是上次滤波输出值
X(n)本次采样值
q:滤波系数(0~1)
q越大,响应越快,但曲线不平滑
q越小,曲线越平滑,但响应越慢
自动控制系统:用自动控制装置,对关键参数进行自动控制,使它在受到外界干扰而偏离正常状态时,能够被自动地调节回到目标范围内。
开环控制:输出只受系统输入控制,没有反馈回路,控制精度和抗干扰能力差
例如:电风扇风力控制系统:
目标风力->控制电路->电机->扇叶转速->风力
闭环控制:引入反馈回路,利用输出和输入值的偏差对系统进行控制,避免偏离预定目标
大棚温控系统:
PID 是 Proportional(比例)、Integral(积分)、Differential(微分)的首字母缩写,它是一种结合比例、积分和微分三个环节于一体的闭环控制算法。
本质:根据输入的偏差值,按照比例、积分、微分的函数关系进行运算,运算结果用以控制输出。
成比例地反应控制系统的偏差信号,即输出与输入偏差成正比,可以用来减小系统的偏差。
= _p∙
:输出
:偏差值
_p :比例系数
1 、_p越大,系统响应越快,越快达到目标值。
2 、_p过大会使系统产生较大的超调和振荡,导致系统的稳定性变差。
3 、仅有比例环节无法消除静态误差。
静态误差
系统控制过程趋于稳定时,目标值与实测值之间的偏差。
产生静差原因:
输出被外部影响抵消
消除静差方法:引入积分环节
对输入偏差进行积分,只要存在偏差,积分环节就会不断起作用,主要用于消除静态误差。
= _p ∙ + _ ∙ ∑
:输出
:偏差值
_p :比例系数
_ :积分系数
1、 _越大,消除静态误差的时间越短,越快达到目标值。
2、 _过大会使系统产生较大的超调和振荡,导致系统的稳定性变差。
3、对于惯性较大的系统,积分环节动态响应较差,容易产生超调、振荡。
反应偏差量的变化趋势,根据偏差的变化量提前作出相应控制,减小超调,克服振荡。
_= _p ∙ _ + _ ∙ ∑_(=0)^_ + _ ∙ ( _–_(−1))
_ :第k次输出
_ :第k次偏差值
_(−1) :第k-1次偏差值
_p :比例系数
_ :积分系数
_ :微分系数
1、_d或者变化趋势越大,微分环节作用越强,对超调和振荡的抑制越强。
2、 _d过大会引起系统的不稳定,容易引入高频噪声。
位置式PID公式:
_= _p ∙ _ + _ ∙ ∑_(=0)^_j + _ ∙( _–_(−1))
1、 _直接对应对象的输出(位置),如果计算出现异常,对系统影响很大。
2、全量计算,要对偏差 进行累加,计算量大。
3、在不带积分部件的对象中可以得到很好效果,例如电液伺服阀、温控设备等。
增量式PID公式推导: