A sensorless BLDC driver breakout board using TI's DRV10987. It is a simple implementation of the typical application in the datasheet. The speed and direction can be controlled using two pins.
Features:
- Motor operation, 6.2 V to 28 V
- Total RDS(on) of 0.25Ω at T A = 25°C
- I2C Interface
- Speed and DIR pins
- Tach output
- Spin-Up Profile Can Be Customized With EEPROM
The following code was written to use test EVM with micropython on an ESP8266.
"""
Copyright © 2021 codelv.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
from machine import Pin, I2C, PWM
# Register names
class DRV10987:
REGISTERS = {
0x00: 'Fault',
0x01: 'MotorSpeed',
0x02: 'MotorPeriod',
0x03: 'MotorKt',
0x04: 'MotorCurrent',
0x05: 'IPD/SupplyVolt',
0x06: 'SpeedCmd/CmdBuf',
0x07: 'AnalogInLvl',
0x08: 'DevId/RevId',
0x30: 'SpeedCtl',
0x31: 'EEPROMProg1',
0x32: 'EEPROMProg2',
0x33: 'EEPROMProg3',
0x34: 'EEPROMProg4',
0x35: 'EEPROMProg5',
0x36: 'EEPROMProg6',
0x60: 'EECTRL',
0x90: 'Config1',
0x91: 'Config2',
0x92: 'Config3',
0x93: 'Config4',
0x94: 'Config5',
0x95: 'Config6',
0x96: 'Config7',
}
EEPROM_DEFAULTS = {
0x90: 0xC000,
0x91: 0x0049,
0x92: 0x00C1,
0x93: 0x3788,
0x94: 0x3BAF,
0x95: 0x7840,
0x96: 0x007A,
}
addr = 0b1010010
def __init__(self, sda=4, scl=5, spd=14, dir=12):
self.pwm = PWM(Pin(spd), freq=1000, duty=0)
self.dir = Pin(dir, Pin.OUT)
self.i2c = I2C(freq=100000, scl=Pin(scl), sda=Pin(sda))
# EEPROM Config
self.config = {}
def dump_reg(self, reg, n=2):
name = DRV10987.REGISTERS.get(reg, '')
r = self.read_reg(reg, n)
print("{: <30} 0x{:02x}: 0x{:04x}".format(name, reg, r))
def dump(self):
for i, name in DRV10987.REGISTERS.items():
self.dump_reg(i)
def write_reg(self, reg, data):
""" Data is in format high byte, low byte """
upper = (data >> 8) & 0xFF
lower = data & 0xFF
value = bytearray((upper, lower))
self.i2c.writeto_mem(self.addr, reg, value)
def read_reg(self, reg, n=2):
mem = self.i2c.readfrom_mem(self.addr, reg, n)
return int.from_bytes(mem, 'big')
def disable_motor(self):
self.write_reg(0x60, 0x8000)
def enable_motor(self):
self.write_reg(0x60, 0x0000)
def set_motor_resistance(self, r):
# R = 3.03
assert 0 <= r <= 19.8656, "R is out of range"
s = 0.0097
base = 0
for rm_shift in range(8):
step = 2**rm_shift*s
rlim = step*16
if r < rlim:
break
base = rlim
rm_v = int((r-base)/step)
if rm_shift == 0:
rm = rm_v
else:
rm = (rm_shift << 4) | 8 | rm_v
reg = self.config[0x90]
upper = reg >> 8
lower = (reg & 0xc0) | rm
self.config[0x90] = upper | lower
def set_motor_kt(self, kt):
# Kt = 54.8 mV/R/S
assert 0 <= kt <= 1884.16, "Kt is out of range"
s = 0.92
base = 0
for kt_shift in range(8):
step = 2**kt_shift*s
kt_lim = step*16
if kt < kt_lim:
break
base = kt_lim
kt_v = int((kt-base)/step)
if kt_shift == 0:
k = kt_v
else:
k = (kt_shift << 4) | 8 | kt_v
reg = self.config[0x91]
upper = k
lower = reg & 0xFF
self.config[0x91] = upper | lower
def reset_config(self):
self.config = DRV10987.EEPROM_DEFAULTS.copy()
def set_open_loop_current(self, v):
r = self.config[0x92]
r &= 0xFF3F
r |= ((v & 0x3) << 4)
self.config[0x92] = r
def set_speed_ctrl_pwm(self):
r = self.config[0x95]
r |= 1 << 15
self.config[0x95] = r
def set_speed_ctrl_analog(self):
r = self.config[0x95]
r &= 0x7FFF
self.config[0x95] = r
def enable_eeprom_access(self):
self.disable_motor()
# Clear access code
self.write_reg(0x31, 0)
# Set access code
self.write_reg(0x31, 0xC0DE)
if not self.wait_eeprom_ready():
raise RuntimeError("EEPROM Timeout")
def read_eeprom(self):
# Load into registers
self.write_reg(0x35, 0b010)
self.wait_eeprom_ready()
# Read them
for i in range(0x90, 0x97):
self.config[i] = self.read_reg(i)
self.enable_motor()
def write_eeprom(self):
if not self.config:
raise RuntimeError("Use read_eeprom first!")
# Write them
for i in range(0x90, 0x97):
self.write_reg(i, self.config[i])
# Load into registers
self.write_reg(0x35, 0b110)
self.wait_eeprom_ready()
self.enable_motor()
def wait_eeprom_ready(self, attempts=100):
for i in range(attempts):
if self.is_eeprom_ready:
return True
@property
def is_eeprom_ready(self):
return (self.read_reg(0x32) & 1) == 1
@property
def faults(self):
return self.read_reg(0x00)
@property
def current(self):
r = self.read_reg(0x04) & 0x7FF
return 3 * (r-1023)/2048
@property
def speed_ctrl(self):
r = self.read_reg(0x30)
if r >> 15:
return r & 0x1FF
@property
def motor_r(self):
r = self.read_reg(0x90)
rm_v = r & 0b111
rm_shift = r & 0b11000
r_md = rm_v >> rm_shift
return r_md * 0.009615
@property
def motor_kt(self):
r = self.read_reg(0x03)
return r/2/1090
@property
def supply_voltage(self):
r = self.read_reg(0x05) & 0xFF
return r * 30/255
@property
def ipd_position(self):
return self.read_reg(0x05) >> 8
@property
def analog_in_level(self):
r = self.read_reg(0x07) * 0x3FF
return r * 3.3/1024
@property
def die_id(self):
return self.read_reg(0x10) >> 8
@property
def rev_id(self):
return self.read_reg(0x10) & 0xFF
def configure(self, r_m, k_t, open_loop_current):
self.enable_eeprom_access()
self.read_eeprom()
self.enable_eeprom_access()
self.set_motor_resistance(r_m)
self.set_motor_kt(k_t)
self.set_open_loop_current(open_loop_current)
self.set_speed_ctrl_pwm()
self.write_eeprom()
drv = DRV10987()
I made to run a brushless engraver spindle motor that had a damaged hall sensor.
It's is my first board. It was designed using Horizon-EDA. The v1.0 boards were made by OSH Park the latest v1.1 were made by JLCPCB and assembled by me.