10089-基于STM32的平板车行走的双无刷直流电机控制电路设计(实物图+PCB+原理图+源代码工程+详细介绍说明书)
功能描述:
1、设计要求。
(1) 认真搜集、查阅相关文献期刊资料。
(2) 设计的方案合理,实现以STM32单片机为核心的控制方案
(3) 硬件设计正确、完整
(4) 控制算法合理,软件编程较完整。
2、设计参数。
(1) 供电电压48V,双无刷电机功率35W.
(2) 两个电机同步速度调节差0-20%。
平板车在设计上需要保证在运行过程中速度可控。并且需要实现无刷电机,在控制上能够达到最佳的性能控制器较为稳定,能够实现平板车在转速过程中能够有较小的速度差并且能够精确的对速度进行控制。本次系统需要使用48V电源供电,将两个轮子转动的速度差控制在020%以内。根据需求对系统的功能性进行测试,本次测试需要外接一个48V的直流电源,然后将电脑连接如设备,通过发生指令的方式实现两个电机转动。通过设定运转的速度与参数使两个电机转动起来。测试系统的速度差是否在020%以内。
资料摘要:
随着人们在生活上的出行要求变高,城市中出现了各种各样的交通工具,巴士、轿车等设备已经成为了人们出行的必备工具。但是堵车和汽车尾气造成了环境污染,所以现在需要设计出更加环保的交通方式。
本次设计一款平板车行走的双无刷直流电机控制电路设计,制作一款基于STM32F405RGT6单片机为主控制器,DRV8301作为电机控制驱动,供电电压48V的双无刷电机控制系统,使两个电机之间的速度同步调节误差在0-20%之间。本次设计能够驱动FOC控制板,推导FOC控制算法中的主要公式。并使用FOC控制算法对双无刷电机进行有效控制,可以防止像传统方式控制无刷直流电机的能力不足导致性能误差。使用FOC算法可以提高电机的控制效果和效率,可以使平板车在运行上性能佳,在转弯出能够更加平稳,提高双无刷电机的准确性和鲁棒性。
本次设计通过对原理图设计、PCB设计并最终制作实物测试系统的性能,最终对系统的功能进行验证符合设计性能要求。
资料包含:
1、详细设计说明书-17862字
2、实物图片
3、原理图工程文件、PCB工程文件
4、源代码工程文件
5、实物图片
6、英文文献及翻译-3327字
7、测试表格
8、流程图
9、无刷电机FOC算法研究资料
10、参考文献
#include "drv8301.hpp"
#include "utils.hpp"
#include "cmsis_os.h"
#include "board.h"
const SPI_InitTypeDef Drv8301::spi_config_ = {
.Mode = SPI_MODE_MASTER,
.Direction = SPI_DIRECTION_2LINES,
.DataSize = SPI_DATASIZE_16BIT,
.CLKPolarity = SPI_POLARITY_LOW,
.CLKPhase = SPI_PHASE_2EDGE,
.NSS = SPI_NSS_SOFT,
.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
.FirstBit = SPI_FIRSTBIT_MSB,
.TIMode = SPI_TIMODE_DISABLE,
.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
.CRCPolynomial = 10,
};
bool Drv8301::config(float requested_gain, float* actual_gain) {
// Calculate gain setting: Snap down to have equal or larger range as
// requested or largest possible range otherwise
// for reference:
// 20V/V on 500uOhm gives a range of +/- 150A
// 40V/V on 500uOhm gives a range of +/- 75A
// 20V/V on 666uOhm gives a range of +/- 110A
// 40V/V on 666uOhm gives a range of +/- 55A
uint16_t gain_setting = 3;
float gain_choices[] = {10.0f, 20.0f, 40.0f, 80.0f};
while (gain_setting && (gain_choices[gain_setting] > requested_gain)) {
gain_setting--;
}
if (actual_gain) {
*actual_gain = gain_choices[gain_setting];
}
RegisterFile new_config;
new_config.control_register_1 =
(21 << 6) // Overcurrent set to approximately 150A at 100degC. This may need tweaking.
| (0b01 << 4) // OCP_MODE: latch shut down
| (0b0 << 3) // 6x PWM mode
| (0b0 << 2) // don't reset latched faults
| (0b00 << 0); // gate-drive peak current: 1.7A
new_config.control_register_2 =
(0b0 << 6) // OC_TOFF: cycle by cycle
| (0b00 << 4) // calibration off (normal operation)
| (gain_setting << 2) // select gain
| (0b00 << 0); // report both over temperature and over current on nOCTW pin
bool regs_equal = (regs_.control_register_1 == new_config.control_register_1)
&& (regs_.control_register_2 == new_config.control_register_2);
if (!regs_equal) {
regs_ = new_config;
state_ = kStateUninitialized;
enable_gpio_.write(false);
}
return true;
}
bool Drv8301::init() {
uint16_t val;
if (state_ == kStateReady) {
return true;
}
// Reset DRV chip. The enable pin also controls the SPI interface, not only
// the driver stages.
enable_gpio_.write(false);
delay_us(40); // mimumum pull-down time for full reset: 20us
state_ = kStateUninitialized; // make is_ready() ignore transient errors before registers are set up
enable_gpio_.write(true);
osDelay(20); // t_spi_ready, max = 10ms
// Write current configuration
bool wrote_regs = write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1)
&& write_reg(kRegNameControl1, regs_.control_register_1) // the write operation tends to be ignored if only done once (not sure why)
&& write_reg(kRegNameControl2, regs_.control_register_2);
if (!wrote_regs) {
return false;
}
// Wait for configuration to be applied
delay_us(100);
state_ = kStateStartupChecks;
bool is_read_regs = read_reg(kRegNameControl1, &val) && (val == regs_.control_register_1)
&& read_reg(kRegNameControl2, &val) && (val == regs_.control_register_2);
if (!is_read_regs) {
return false;
}
if (get_error() != FaultType_NoFault) {
return false;
}
// There could have been an nFAULT edge meanwhile. In this case we shouldn't
// consider the driver ready.
CRITICAL_SECTION() {
if (state_ == kStateStartupChecks) {
state_ = kStateReady;
}
}
return state_ == kStateReady;
}
void Drv8301::do_checks() {
if (state_ != kStateUninitialized && !nfault_gpio_.read()) {
state_ = kStateUninitialized;
}
}
bool Drv8301::is_ready() {
return state_ == kStateReady;
}
Drv8301::FaultType_e Drv8301::get_error() {
uint16_t fault1, fault2;
if (!read_reg(kRegNameStatus1, &fault1) ||
!read_reg(kRegNameStatus2, &fault2)) {
return (FaultType_e)0xffffffff;
}
return (FaultType_e)((uint32_t)fault1 | ((uint32_t)(fault2 & 0x0080) << 16));
}
bool Drv8301::read_reg(const RegName_e regName, uint16_t* data) {
tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0);
if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) {
return false;
}
delay_us(1);
tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Read, regName, 0);
rx_buf_ = 0xffff;
if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), (uint8_t *)(&rx_buf_), 1, 1000)) {
return false;
}
delay_us(1);
if (rx_buf_ == 0xbeef) {
return false;
}
if (data) {
*data = rx_buf_ & 0x07FF;
}
return true;
}
bool Drv8301::write_reg(const RegName_e regName, const uint16_t data) {
// Do blocking write
tx_buf_ = build_ctrl_word(DRV8301_CtrlMode_Write, regName, data);
if (!spi_arbiter_->transfer(spi_config_, ncs_gpio_, (uint8_t *)(&tx_buf_), nullptr, 1, 1000)) {
return false;
}
delay_us(1);
return true;
}