diff --git a/README.md b/README.md index ffe7331583db675eeb626ca6bb16715cf905665e..6da5b4c299d0105555acb5d49f20eb71db7d00e8 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,11 @@ -# MachineKit BLDC Driver - - - - - -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.  +  ## 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 diff --git a/circuit/README.md b/circuit/README.md index e3588a3629fbc75ea5c3d81f3c89aefbcd788db7..50b99f53f78e521bb639c52b1ec05efe8607e72b 100644 --- a/circuit/README.md +++ b/circuit/README.md @@ -1,6 +1,21 @@ # Machine Kit BLDC Driver: Circuit Development -Uses DRV8302 to drive fancy TI half-bridges, etc. Current sensing, voltage sensing, thermistor, search. + + + + +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 diff --git a/circuit/atkbldcdriver/eagle.epf b/circuit/atkbldcdriver/eagle.epf index 4b5f4d76f6f6246dbdc3f95220b47128e514e43e..be957d0498a4ed6287e9379de47610b5abd6f2f6 100644 --- a/circuit/atkbldcdriver/eagle.epf +++ b/circuit/atkbldcdriver/eagle.epf @@ -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" diff --git a/embedded/README.md b/embedded/README.md index c92e3e73956789534a51556aa936d7226891b2c6..233a2fb354a3ab9d65491ecb4aae9b9838b5a5fd 100644 --- a/embedded/README.md +++ b/embedded/README.md @@ -1,234 +1,7 @@ -# 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); - } -} -``` - - - - - -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 - } -} -``` - - - -Now we do v0.31 board, new step board, etc. Go team, big day. - -# Waking up v0.31 - - - -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: - - - -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. - - - - - -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. diff --git a/embedded/atsamd51.md b/embedded/atsamd51.md new file mode 100644 index 0000000000000000000000000000000000000000..c92e3e73956789534a51556aa936d7226891b2c6 --- /dev/null +++ b/embedded/atsamd51.md @@ -0,0 +1,234 @@ +# 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); + } +} +``` + + + + + +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 + } +} +``` + + + +Now we do v0.31 board, new step board, etc. Go team, big day. + +# Waking up v0.31 + + + +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: + + + +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. + + + + + +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