Commit e6fa92d2 authored by Jake Read's avatar Jake Read

new doc

parent 5e8b4663
# MachineKit BLDC Driver
![board](/images/fab-front.jpg)
![board](/images/fab-back.jpg)
See: [Circuit Development](/circuit), [Code Development](/embedded), and [Education](/education)
# Next Board Notes
- crystal
- qfn64 footprint update!
- power pad size
## Next Steps
- stuff one board
- re-hash code for different pinouts, with updated libraries
- try speed control with IQ-less FOC (simple sine commutation)
- if this is not satisfactory (field lag due to inductance), use 6-step comm
- implement speed control network command
- re-hash documentation
- use to control a spindle in a machine
# AutomataKit BLDC Driver
## What
The MMKBLDC Project is my attempt at building a decent controller for Brushless Motors. BLDCs are excellent sources of mechatronic actuation: they're [power dense](https://jakeread.pages.cba.mit.edu/actuators/) and serve the motion control justice present in most state of the art robotics. However, controlling them is non-trivial, and requires the combination of some EE expertise, some control expertise, and some hardware know-how.
![board](/images/board.png)
![schematic](/images/schematic.png)
## Why
......@@ -38,29 +17,48 @@ TESC, April 2016 - August 2016
so did those motors.
Once around is never enough*
Eulagies aside, I am still motivated to do this. Brushless motors are the go-to motive force for electromechanical systems. By that I mean that just about any time you see a robot-like thing, or machine, moving about, there's a big likelihood that the thing doing-the-moving has a brushless motor in it's guts - or some variant thereof (stepper motors count as BLDCs in my books). See [this characterization of actuators](https://jakeread.pages.cba.mit.edu/actuators/) to get a sense for why.
## How
We use an ATSAMD51 microcontroller to drive phase voltages through a gate driver, through half-bridge drivers, through motor phases. An encoder reads motor position back to the uc, and we use shunt resistors to measure current flowing through the motor coils. With this information, we can use maths to determine where the motor's magnetic field is, and control that to drive position and torque outputs.
The driver uses an ATxmega microcontroller to drive phase voltages through a gate driver, through half-bridges, through motor windings. An encoder reads motor position back to the microcontrolelr, and shunt resistors measure current flowing through the motor coils. With this information, we can use some maths to determine where the motor's magnetic field is, and control that to drive position and torque outputs.
## Development Notes
See [circuit chatter](/circuit), [firmware chatter](/embedded)
## Learning About Brushless Motors and their Control
[See the (ongoing) notes I put together to explain the wizardry of BLDC driving under / eductation.](/education)
# Viable Commands
*none yet - firmware in development*
### Test
Keycode: **127**
- to test networking, this will reply with a payload packet containing ```127, 12, 24, 48``` and will toggle an LED on the board
### Reset
Keycode: **128**
- issues a software microcontroller reset
- if the microcontroller is already hung up, this will not work
This controller runs with the assumtion you're using the [mkxmods](https://gitlab.cba.mit.edu/jakeread/mkxmods) infrastructure for controls. Over the network (see link) you will be able<sup>1</sup> to issue commands like:
### eRPM Target
*Speed Control*
- use key 129, int32_t speed in rpm
- does closed loop PID control to set RPM for spindle control, mostly
*'Dumb' Torque Control*
- use key 130, int32_t torque val/(2^32) pwm duty
- applies this pwm value to the motor, under simple sinusoid control, to apply closed loop commutation to motor
*'Smart' Torque Control*
- will require some search for FOC implementation, and should reply with available torques, bandwidths, etc... here be dragons
Keycode: **141**
Argments: Electrical RPM, int32_t, in electric phase periods / minute
Returns: Ack, uint8_t, 141
- sets motor spinning at the set electrical rpm.
## Building MKBLDC
### Read Current RPM
Mostly, contact me and I'll get you set and ready to programme and use the circuit to control bldc motors.
Keycode: **142**
Arguments: none
Returns: Electrical RPM at time of read, in electric phase periods / minute
In the [circuit dev page](/circuit) you'll find doc on the BOM for the MKBLDC, and in the [embedded dev page](/embedded) you'll find documentation and source code for the firmware to run it as (along with networking code to send messages over the mkxmods network). This is all under development.
# Reproducing This Work
# Footnotes
All automatakit works are open source, and while we cannot sell you boards, if you have an interesting application, get in touch to ask about collaborating.
1. working on it
To reproduce boards and load code, see the document ['Reproducing Automatakit Work'](https://gitlab.cba.mit.edu/jakeread/automatakit/reproducing)
\ No newline at end of file
# Machine Kit BLDC Driver: Circuit Development
Uses DRV8302 to drive fancy TI half-bridges, etc. Current sensing, voltage sensing, thermistor, search.
![schematic](https://gitlab.cba.mit.edu/jakeread/mkstepper/raw/master/images/schematic.png)
![routing](https://gitlab.cba.mit.edu/jakeread/mkstepper/raw/master/images/routed.png)
The circuit uses a DRV8302 to drive CSD88584 (or CSD88599) half-bridges, which are rated to 40V / 50A and 60V / 30A respectively. Normally, I'll run 24V power. It connects to an AS5147 or AS5047D or AEAT6600 absolute encoder over SPI, and reads phase currents from the amplifier built into the DRV8302, as well as phase voltages from simple voltage dividers (which are clamped to 3V3 with a series of diodes).
Power is bussed to the board with two M3 Screw Terminals. The board includes room for a 16mm (?) diameter capacitor, and I'll normally use a 1000uF 35V cap here, or as big as I can fit happily.
## Notes
- might have to go to DRV8320 - newer, available
- CSD88548 is CSD88599 but more amps less volts, use these
- use RDT RTD for BLDC - 223-1563-1-ND
- phy RS422 and bring back buck
- do 24v fan 40mm on M3 Terminals! silk lands for heatsink 1.1" square
Fan
- 1053-1210-ND
- 1053-1375-ND
\ No newline at end of file
......@@ -88,7 +88,7 @@ Sheet="1"
Type="Board Editor"
Number=1
File="atkbldcdriver.brd"
View="49.5499 31.7808 81.9318 58.7494"
View="40.4207 5.232 120.997 72.3385"
WireWidths=" 0.0762 0.127 0.15 0.508 0.6096 0.8128 2.54 1.016 1.27 0.3048 0.254 0.2 0.4064 0.1016 0.2032 0.1524"
PadDiameters=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 6.4516 0"
PadDrills=" 0.2 0.25 0.35 0.4 0.45 0.5 0.55 0.65 0.7 0.75 0.8 0.85 0.9 1 0.6 0.3"
......
# Programming the BLDC Driver
# AutomataKit BLDC Driver Firmware Notes
While I went through this previously with the [ATSAMS70](atsams70.md), we're going to do it again on the ATSAMD51. I have a v0.3 board here (which will shortly be rev'd to 0.31 due to a few already apparent mistakes, welp) and I can program it. I'm in the process of checking all of the hardware so that I can go forward with a new board order, knowing a bit better that I'll be o-k with that set.
Firmware for the board follows the [automatakit](https://gitlab.cba.mit.edu/jakeread/automatakit) architecture, and is available here for perusal.
I've got the PWM up, and it's running as expected. Nice.
Communication is handled asynchronously: bytes received on the UART are loaded into a ringbuffer, where they are later parsed in to packets. Parsing happens as oven as possible.
```C
int main(void)
{
/* Initialize the SAM system */
SystemInit();
PORT->Group[1].DIRSET.reg |= (uint32_t)(1 << 9);
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 23);
SysTick_Config(5000000);
/* TCC SETUP */
// from 49.6.2.1
// a few registers are protected - and can only be updated when
// TCCn.CTRLA.ENABLE = 0
// FCTRLA and FCTRLB, WEXCTRL, DRVCTRL, and EVCTRL
// (4) Configure Output Pin with PORT->Group[n].DIRSET.reg
// PA8 PA9 PA10 PA12, PB10 PB11
// 32.9.13
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 8) | (uint32_t)(1 << 9) | (uint32_t)(1 << 10) | (uint32_t)(1 << 12);
PORT->Group[1].DIRSET.reg |= (uint32_t)(1 << 10) | (uint32_t)(1 << 11);
// 1 lo / hi
PORT->Group[0].PINCFG[10].bit.PMUXEN = 1;
PORT->Group[0].PMUX[10>>1].reg |= PORT_PMUX_PMUXE(0x5); // on peripheral F
PORT->Group[0].PINCFG[12].bit.PMUXEN = 1;
PORT->Group[0].PMUX[12>>1].reg |= PORT_PMUX_PMUXE(0x5);
// 2 lo / hi
PORT->Group[0].PINCFG[9].bit.PMUXEN = 1;
PORT->Group[0].PMUX[9>>1].reg |= PORT_PMUX_PMUXO(0x5); // on peripheral F
PORT->Group[1].PINCFG[11].bit.PMUXEN = 1;
PORT->Group[1].PMUX[11>>1].reg |= PORT_PMUX_PMUXO(0x5);
// 3 lo / hi
PORT->Group[0].PINCFG[8].bit.PMUXEN = 1;
PORT->Group[0].PMUX[8>>1].reg |= PORT_PMUX_PMUXE(0x5); // on peripheral F
PORT->Group[1].PINCFG[10].bit.PMUXEN = 1;
PORT->Group[1].PMUX[10>>1].reg |= PORT_PMUX_PMUXE(0x5);
// (1) enable the TCC Bus Clock - CLK_TCCn_APB
// https://www.eevblog.com/forum/microcontrollers/atmel-sam-d-tc-and-tcc-(no-asf)/
TCC0->CTRLA.bit.ENABLE = 0;
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC0; // at 15.8.9
GCLK->GENCTRL[5].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL5);
GCLK->PCHCTRL[TCC0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK5;
TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV8 | TCC_CTRLA_PRESCSYNC_PRESC |TCC_CTRLA_RESOLUTION(0);
// (2) Select Waveform Generation operation in the WAVE register WAVE.WAVEGEN
// we want dual slope pwm
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_DSBOTH; // 'dual slope both' - updates on both hi and lo of slope ?
// (3) We want OTMX - Output Matrix Channel Pin Routing Configuration - at 0x0
TCC0->WEXCTRL.reg = TCC_WEXCTRL_DTHS(1) | TCC_WEXCTRL_DTLS(1) |
TCC_WEXCTRL_DTIEN1 | TCC_WEXCTRL_DTIEN2 | TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_DTIEN0 |
TCC_WEXCTRL_OTMX(0);
TCC0->PER.reg = TCC_PER_PER(256); // 18 bit
TCC0->COUNT.reg = 0;
TCC0->CC[0].reg = 12; // '3'
TCC0->CC[1].reg = 24; // '2'
TCC0->CC[2].reg = 48; // '1'
TCC0->CC[3].reg = 0;
// (4) Enable with CTRLA.ENABLE
TCC0->CTRLA.bit.ENABLE = 1;
while(TCC0->SYNCBUSY.bit.ENABLE);
while (1)
{
PORT->Group[1].OUTTGL.reg = (uint32_t)(1 << 9);
}
}
```
![atsamd51 pwm](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-pwm-alive-atsamd51.png)
![atsamd51 pwm](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-pwm-alive-atsamd51-picture.jpg)
Next up is the SPI wakeup.
Great, this is running as well.
```C
int main(void)
{
/* Initialize the SAM system */
SystemInit();
PORT->Group[1].DIRSET.reg |= (uint32_t)(1 << 9);
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 23);
SysTick_Config(5000000);
/* BEGIN SPI SETUP */
// PA04, SER0-0, SPI_MISO
// PA05, SER0-1, SPI_SCK
// PA06, SER0-2, SPI_CSN
// PA07, SER0-3, SPI_MOSI
PORT->Group[0].DIRCLR.reg |= (uint32_t)(1 << 4);
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 5) | (uint32_t)(1 << 6) | (uint32_t)(1 << 7);
PORT->Group[0].PINCFG[4].bit.PMUXEN = 1;
PORT->Group[0].PMUX[4>>1].reg |= PORT_PMUX_PMUXE(0x3); // on peripheral D
PORT->Group[0].PINCFG[5].bit.PMUXEN = 1;
PORT->Group[0].PMUX[5>>1].reg |= PORT_PMUX_PMUXO(0x3); // on peripheral D
PORT->Group[0].PINCFG[6].bit.PMUXEN = 1;
PORT->Group[0].PMUX[6>>1].reg |= PORT_PMUX_PMUXE(0x3); // on peripheral D
PORT->Group[0].PINCFG[7].bit.PMUXEN = 1;
PORT->Group[0].PMUX[7>>1].reg |= PORT_PMUX_PMUXO(0x3); // on peripheral D
// setup clocks to sercom
MCLK->APBAMASK.reg |= MCLK_APBAMASK_SERCOM0; // at 15.8.9
GCLK->GENCTRL[6].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL6);
GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK6;
// TCC0_GCLK_ID
// Some registers can't be written unless CTRL.ENABLE = 0:
// CTRLA, CTRLB, BAD and ADDR
// (1) set to master
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); // 0x2 or 0x3, slave or master
// SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_CPHA | SERCOM_SPI_CTRLA_CPOL; // clock phase and polarity
// (2) set pin configurations
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_DIPO(0x0) | SERCOM_SPI_CTRLA_DOPO(0x2); // pin selections, see 35.8.1 bits 21:20 and 17:16, pg. 910
// (3) set character size, data direction
//SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_DORD; // 0 MSB, 1 LSB
//SERCOM0->SPI.CTRLB.reg |= SERCOM_SPI_CTRLB_CHSIZE(0x0); // 8 bits character - 0x0, so no need to set
// (4) setup baud rate
// f_baud = f_ref / (2 * (BAUD +1)) so BAUD = f_ref / (2 * f_baud) - 1
SERCOM0->SPI.BAUD.reg |= SERCOM_SPI_BAUD_BAUD(126);
SERCOM0->SPI.CTRLB.reg |= SERCOM_SPI_CTRLB_MSSEN | SERCOM_SPI_CTRLB_RXEN; // slave select hardware yes
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
while (1)
{
while(!(SERCOM0->SPI.INTFLAG.bit.DRE));
SERCOM0->SPI.DATA.reg = SERCOM_SPI_DATA_DATA(80);
PORT->Group[1].OUTTGL.reg = (uint32_t)(1 << 9); // i-v, to check we made it thru setup
}
}
```
![atsamd51 spi](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-spi-alive-atsamd51.png)
Now we do v0.31 board, new step board, etc. Go team, big day.
# Waking up v0.31
![pwm-one-channel](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-spi-atsamd51-as5147.png)
Have this SPI running with the encoder in-line and PWM still setup.
I integrated UART / Ringbuffers etc from the mkstepper project. So I have everything I need to start commutating.
OK!
## Commutating
Properly, I should do this on a timer. I'm going to do it in the while() loop for now, just to check that I'm having the output on the PWMs that I want.
Great - I have this running in the open while() loop. It commutates! I suppose I shouldn't be surprised at this stuff anymore.
I set up my logic analyzer so that I can start on this commutation debug cycle. I have three pins on the lo-sides of the PWMs, three on the voltage sense pinse, and two on the current sense pins.
I'm hearing this 'tick' every so often as the motor commutates. Here's what it looks like on the analyzer:
![pwm-tick](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-pwm-ticking.png)
So I think that one of the PWM registers is occasionally being written to 100% in error. My suspicion is that this has something to do with my fast-and-loose commutation scheme, which I'm about to improve.
While I did notice that this was only occuring while the motor driver's gates where enabled (so, 'stuff was happening'), I tried using the ATSAMD's PWM Capture-Compare Buffer (capture-compare is the value the pwm timer checks to switch-or-not-switch the output). The buffer let's me write into the PWM registers when I'm sure they're not being read by the peripheral. This eliminated the problem. I also pushed the PWM frequency to 22kHZ and it's all silky smooth sounding now.
OK, some current / voltage waveforms:
Channels: Fault, PWM Hi U, Pwm Hi V, Pwm Hi W, Current V, Current W, Voltage V, Voltage W.
![currents](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-currents-olcommutate-1.png)
![currents](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-currents-olcommutate-2.png)
I set this up to accept a commanded 'torque' (just PWM duty cycle) and direction, so next step here is doing some network integration as well.
[Video](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/blob/master/video/20khz-commutate.mp4)
Then, a longer list of development:
# Next:
## Firmware
- Closed-Loop Speed Control (maybe using simple 6-step commutation?)
- Search for Encoder Offset
- Closed-Loop Position Control
- Probably just Sinusoid PWM Commutation
- The Big Bite: FOC
## Hardware
- I have a list of incremental improvements... mostly:
- Go to 2oz copper so that I dont' blow up any traces when the power hits
- Discrete LEDs and more indication (there's an overcurrent / overtemp warning I want to break out)
\ No newline at end of file
Firmware for the xmega is under development, but you can see [firmware notes for the atsamd51 board](/embedded/atsamd51.md), and incidentally I've also done this with the [atsams70](/embedded/atsams70.md). Clearly, a few trips around the same yak. To shave. If you know what I mean.
# Programming the BLDC Driver
While I went through this previously with the [ATSAMS70](atsams70.md), we're going to do it again on the ATSAMD51. I have a v0.3 board here (which will shortly be rev'd to 0.31 due to a few already apparent mistakes, welp) and I can program it. I'm in the process of checking all of the hardware so that I can go forward with a new board order, knowing a bit better that I'll be o-k with that set.
I've got the PWM up, and it's running as expected. Nice.
```C
int main(void)
{
/* Initialize the SAM system */
SystemInit();
PORT->Group[1].DIRSET.reg |= (uint32_t)(1 << 9);
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 23);
SysTick_Config(5000000);
/* TCC SETUP */
// from 49.6.2.1
// a few registers are protected - and can only be updated when
// TCCn.CTRLA.ENABLE = 0
// FCTRLA and FCTRLB, WEXCTRL, DRVCTRL, and EVCTRL
// (4) Configure Output Pin with PORT->Group[n].DIRSET.reg
// PA8 PA9 PA10 PA12, PB10 PB11
// 32.9.13
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 8) | (uint32_t)(1 << 9) | (uint32_t)(1 << 10) | (uint32_t)(1 << 12);
PORT->Group[1].DIRSET.reg |= (uint32_t)(1 << 10) | (uint32_t)(1 << 11);
// 1 lo / hi
PORT->Group[0].PINCFG[10].bit.PMUXEN = 1;
PORT->Group[0].PMUX[10>>1].reg |= PORT_PMUX_PMUXE(0x5); // on peripheral F
PORT->Group[0].PINCFG[12].bit.PMUXEN = 1;
PORT->Group[0].PMUX[12>>1].reg |= PORT_PMUX_PMUXE(0x5);
// 2 lo / hi
PORT->Group[0].PINCFG[9].bit.PMUXEN = 1;
PORT->Group[0].PMUX[9>>1].reg |= PORT_PMUX_PMUXO(0x5); // on peripheral F
PORT->Group[1].PINCFG[11].bit.PMUXEN = 1;
PORT->Group[1].PMUX[11>>1].reg |= PORT_PMUX_PMUXO(0x5);
// 3 lo / hi
PORT->Group[0].PINCFG[8].bit.PMUXEN = 1;
PORT->Group[0].PMUX[8>>1].reg |= PORT_PMUX_PMUXE(0x5); // on peripheral F
PORT->Group[1].PINCFG[10].bit.PMUXEN = 1;
PORT->Group[1].PMUX[10>>1].reg |= PORT_PMUX_PMUXE(0x5);
// (1) enable the TCC Bus Clock - CLK_TCCn_APB
// https://www.eevblog.com/forum/microcontrollers/atmel-sam-d-tc-and-tcc-(no-asf)/
TCC0->CTRLA.bit.ENABLE = 0;
MCLK->APBBMASK.reg |= MCLK_APBBMASK_TCC0; // at 15.8.9
GCLK->GENCTRL[5].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL5);
GCLK->PCHCTRL[TCC0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK5;
TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV8 | TCC_CTRLA_PRESCSYNC_PRESC |TCC_CTRLA_RESOLUTION(0);
// (2) Select Waveform Generation operation in the WAVE register WAVE.WAVEGEN
// we want dual slope pwm
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_DSBOTH; // 'dual slope both' - updates on both hi and lo of slope ?
// (3) We want OTMX - Output Matrix Channel Pin Routing Configuration - at 0x0
TCC0->WEXCTRL.reg = TCC_WEXCTRL_DTHS(1) | TCC_WEXCTRL_DTLS(1) |
TCC_WEXCTRL_DTIEN1 | TCC_WEXCTRL_DTIEN2 | TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_DTIEN0 |
TCC_WEXCTRL_OTMX(0);
TCC0->PER.reg = TCC_PER_PER(256); // 18 bit
TCC0->COUNT.reg = 0;
TCC0->CC[0].reg = 12; // '3'
TCC0->CC[1].reg = 24; // '2'
TCC0->CC[2].reg = 48; // '1'
TCC0->CC[3].reg = 0;
// (4) Enable with CTRLA.ENABLE
TCC0->CTRLA.bit.ENABLE = 1;
while(TCC0->SYNCBUSY.bit.ENABLE);
while (1)
{
PORT->Group[1].OUTTGL.reg = (uint32_t)(1 << 9);
}
}
```
![atsamd51 pwm](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-pwm-alive-atsamd51.png)
![atsamd51 pwm](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-pwm-alive-atsamd51-picture.jpg)
Next up is the SPI wakeup.
Great, this is running as well.
```C
int main(void)
{
/* Initialize the SAM system */
SystemInit();
PORT->Group[1].DIRSET.reg |= (uint32_t)(1 << 9);
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 23);
SysTick_Config(5000000);
/* BEGIN SPI SETUP */
// PA04, SER0-0, SPI_MISO
// PA05, SER0-1, SPI_SCK
// PA06, SER0-2, SPI_CSN
// PA07, SER0-3, SPI_MOSI
PORT->Group[0].DIRCLR.reg |= (uint32_t)(1 << 4);
PORT->Group[0].DIRSET.reg |= (uint32_t)(1 << 5) | (uint32_t)(1 << 6) | (uint32_t)(1 << 7);
PORT->Group[0].PINCFG[4].bit.PMUXEN = 1;
PORT->Group[0].PMUX[4>>1].reg |= PORT_PMUX_PMUXE(0x3); // on peripheral D
PORT->Group[0].PINCFG[5].bit.PMUXEN = 1;
PORT->Group[0].PMUX[5>>1].reg |= PORT_PMUX_PMUXO(0x3); // on peripheral D
PORT->Group[0].PINCFG[6].bit.PMUXEN = 1;
PORT->Group[0].PMUX[6>>1].reg |= PORT_PMUX_PMUXE(0x3); // on peripheral D
PORT->Group[0].PINCFG[7].bit.PMUXEN = 1;
PORT->Group[0].PMUX[7>>1].reg |= PORT_PMUX_PMUXO(0x3); // on peripheral D
// setup clocks to sercom
MCLK->APBAMASK.reg |= MCLK_APBAMASK_SERCOM0; // at 15.8.9
GCLK->GENCTRL[6].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL6);
GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK6;
// TCC0_GCLK_ID
// Some registers can't be written unless CTRL.ENABLE = 0:
// CTRLA, CTRLB, BAD and ADDR
// (1) set to master
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); // 0x2 or 0x3, slave or master
// SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_CPHA | SERCOM_SPI_CTRLA_CPOL; // clock phase and polarity
// (2) set pin configurations
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_DIPO(0x0) | SERCOM_SPI_CTRLA_DOPO(0x2); // pin selections, see 35.8.1 bits 21:20 and 17:16, pg. 910
// (3) set character size, data direction
//SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_DORD; // 0 MSB, 1 LSB
//SERCOM0->SPI.CTRLB.reg |= SERCOM_SPI_CTRLB_CHSIZE(0x0); // 8 bits character - 0x0, so no need to set
// (4) setup baud rate
// f_baud = f_ref / (2 * (BAUD +1)) so BAUD = f_ref / (2 * f_baud) - 1
SERCOM0->SPI.BAUD.reg |= SERCOM_SPI_BAUD_BAUD(126);
SERCOM0->SPI.CTRLB.reg |= SERCOM_SPI_CTRLB_MSSEN | SERCOM_SPI_CTRLB_RXEN; // slave select hardware yes
SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
while (1)
{
while(!(SERCOM0->SPI.INTFLAG.bit.DRE));
SERCOM0->SPI.DATA.reg = SERCOM_SPI_DATA_DATA(80);
PORT->Group[1].OUTTGL.reg = (uint32_t)(1 << 9); // i-v, to check we made it thru setup
}
}
```
![atsamd51 spi](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-spi-alive-atsamd51.png)
Now we do v0.31 board, new step board, etc. Go team, big day.
# Waking up v0.31
![pwm-one-channel](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-spi-atsamd51-as5147.png)
Have this SPI running with the encoder in-line and PWM still setup.
I integrated UART / Ringbuffers etc from the mkstepper project. So I have everything I need to start commutating.
OK!
## Commutating
Properly, I should do this on a timer. I'm going to do it in the while() loop for now, just to check that I'm having the output on the PWMs that I want.
Great - I have this running in the open while() loop. It commutates! I suppose I shouldn't be surprised at this stuff anymore.
I set up my logic analyzer so that I can start on this commutation debug cycle. I have three pins on the lo-sides of the PWMs, three on the voltage sense pinse, and two on the current sense pins.
I'm hearing this 'tick' every so often as the motor commutates. Here's what it looks like on the analyzer:
![pwm-tick](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-pwm-ticking.png)
So I think that one of the PWM registers is occasionally being written to 100% in error. My suspicion is that this has something to do with my fast-and-loose commutation scheme, which I'm about to improve.
While I did notice that this was only occuring while the motor driver's gates where enabled (so, 'stuff was happening'), I tried using the ATSAMD's PWM Capture-Compare Buffer (capture-compare is the value the pwm timer checks to switch-or-not-switch the output). The buffer let's me write into the PWM registers when I'm sure they're not being read by the peripheral. This eliminated the problem. I also pushed the PWM frequency to 22kHZ and it's all silky smooth sounding now.
OK, some current / voltage waveforms:
Channels: Fault, PWM Hi U, Pwm Hi V, Pwm Hi W, Current V, Current W, Voltage V, Voltage W.
![currents](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-currents-olcommutate-1.png)
![currents](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/master/images/programming-currents-olcommutate-2.png)
I set this up to accept a commanded 'torque' (just PWM duty cycle) and direction, so next step here is doing some network integration as well.
[Video](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/blob/master/video/20khz-commutate.mp4)
Then, a longer list of development:
# Next:
## Firmware
- Closed-Loop Speed Control (maybe using simple 6-step commutation?)
- Search for Encoder Offset
- Closed-Loop Position Control
- Probably just Sinusoid PWM Commutation
- The Big Bite: FOC
## Hardware
- I have a list of incremental improvements... mostly:
- Go to 2oz copper so that I dont' blow up any traces when the power hits
- Discrete LEDs and more indication (there's an overcurrent / overtemp warning I want to break out)
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment