diff --git a/circuit/mkbldcdriver/eagle.epf b/circuit/mkbldcdriver/eagle.epf
index b7dc703852c482ce7f222a541fd910b85f304f70..9a391b0c0cc194ecfc00627dcbf807296753bba3 100644
--- a/circuit/mkbldcdriver/eagle.epf
+++ b/circuit/mkbldcdriver/eagle.epf
@@ -36,11 +36,11 @@ UsedLibrary="D:/Dropbox (Personal)/CBA/doc/libraries_jake/eagle/sparkfun/SparkFu
 
 [Win_1]
 Type="Schematic Editor"
-Loc="0 0 1919 1016"
+Loc="-1928 -8 -9 1008"
 State=1
 Number=2
 File="mkbldcdriver.sch"
-View="-133.101 -16.4054 69.9278 60.3757"
+View="-191.238 -0.355913 101.124 110.209"
 WireWidths=" 0.0762 0.1016 0.127 0.15 0.2 0.2032 0.254 0.3048 0.4064 0.508 0.6096 0.8128 1.016 1.27 2.54 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.3 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"
@@ -79,7 +79,7 @@ ArcDirection=0
 AddLevel=2
 PadsSameType=0
 Layer=91
-Views=" 1: -133.101 -16.4054 69.9278 60.3757"
+Views=" 1: -191.238 -0.355913 101.124 110.209"
 Sheet="1"
 
 [Win_2]
@@ -88,7 +88,7 @@ Loc="0 0 1919 1016"
 State=1
 Number=1
 File="mkbldcdriver.brd"
-View="13.5716 56.9498 32.0603 57.5241"
+View="21.1469 45.1135 67.1527 46.5425"
 WireWidths=" 0.0762 0.1016 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.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/circuit/mkbldcdriver/mkbldcdriver.brd b/circuit/mkbldcdriver/mkbldcdriver.brd
index 7f9ddeede93d93d5a8554d2b75518ac863da2ebb..ccf6ab2fff04d8543d6d681aab1e23ae48123968 100644
--- a/circuit/mkbldcdriver/mkbldcdriver.brd
+++ b/circuit/mkbldcdriver/mkbldcdriver.brd
@@ -173,8 +173,8 @@
 <text x="35.052" y="20.193" size="0.6096" layer="21" font="vector">V-W</text>
 <text x="35.052" y="22.86" size="0.6096" layer="21" font="vector">V-V</text>
 <text x="35.052" y="25.273" size="0.6096" layer="21" font="vector">V-U</text>
-<text x="35.052" y="30.48" size="0.6096" layer="21" font="vector">C-V</text>
-<text x="35.052" y="27.813" size="0.6096" layer="21" font="vector">C-W</text>
+<text x="35.052" y="30.48" size="0.6096" layer="21" font="vector">I-V</text>
+<text x="35.052" y="27.813" size="0.6096" layer="21" font="vector">I-W</text>
 <text x="35.052" y="33.02" size="0.6096" layer="21" font="vector">3V3</text>
 <text x="35.052" y="35.56" size="0.6096" layer="21" font="vector">GND</text>
 <text x="35.052" y="37.973" size="0.6096" layer="21" font="vector">LO3</text>
diff --git a/embedded/README.md b/embedded/README.md
index 506601ec60c362b12b01cc992c6b30b6e08a27ac..03d2adcb5b9c1f14fb1ebc48c457206fcdf46a04 100644
--- a/embedded/README.md
+++ b/embedded/README.md
@@ -517,3 +517,42 @@ OK!
 
 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.
+
+![clvideo](https://gitlab.cba.mit.edu/jakeread/mkbldcdriver/raw/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
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/Makefile b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/Makefile
index 08c97de7fef9c1933b750b651dc16bf28e0ab7f0..dd50fc2eebf3e8794f8818aebb106861a35e2c47 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/Makefile
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/Makefile
@@ -38,8 +38,10 @@ SUBDIRS :=  \
 
 # Add inputs and outputs from these tool invocations to the build variables 
 C_SRCS +=  \
+../bldc_pfoc.c \
 ../Device_Startup/startup_samd51.c \
 ../Device_Startup/system_samd51.c \
+../encoder.c \
 ../main.c \
 ../pin.c \
 ../pwm_foc.c \
@@ -55,8 +57,10 @@ ASM_SRCS +=
 
 
 OBJS +=  \
+bldc_pfoc.o \
 Device_Startup/startup_samd51.o \
 Device_Startup/system_samd51.o \
+encoder.o \
 main.o \
 pin.o \
 pwm_foc.o \
@@ -65,8 +69,10 @@ spiport.o \
 uartport.o
 
 OBJS_AS_ARGS +=  \
+bldc_pfoc.o \
 Device_Startup/startup_samd51.o \
 Device_Startup/system_samd51.o \
+encoder.o \
 main.o \
 pin.o \
 pwm_foc.o \
@@ -75,8 +81,10 @@ spiport.o \
 uartport.o
 
 C_DEPS +=  \
+bldc_pfoc.d \
 Device_Startup/startup_samd51.d \
 Device_Startup/system_samd51.d \
+encoder.d \
 main.d \
 pin.d \
 pwm_foc.d \
@@ -85,8 +93,10 @@ spiport.d \
 uartport.d
 
 C_DEPS_AS_ARGS +=  \
+bldc_pfoc.d \
 Device_Startup/startup_samd51.d \
 Device_Startup/system_samd51.d \
+encoder.d \
 main.d \
 pin.d \
 pwm_foc.d \
@@ -127,14 +137,18 @@ LINKER_SCRIPT_DEP+=  \
 
 
 
-Device_Startup/%.o: ../Device_Startup/%.c
+
+
+
+
+./%.o: .././%.c
 	@echo Building file: $<
 	@echo Invoking: ARM/GNU C Compiler : 6.3.1
 	$(QUOTE)C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-gcc.exe$(QUOTE)  -x c -mthumb -D__SAMD51J18A__ -DDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\arm\CMSIS\5.0.1\CMSIS\Include" -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\SAMD51_DFP\1.0.70\include"  -O1 -ffunction-sections -mlong-calls -g3 -Wall -mcpu=cortex-m4 -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"   -o "$@" "$<" 
 	@echo Finished building: $<
 	
 
-./%.o: .././%.c
+Device_Startup/%.o: ../Device_Startup/%.c
 	@echo Building file: $<
 	@echo Invoking: ARM/GNU C Compiler : 6.3.1
 	$(QUOTE)C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-gcc.exe$(QUOTE)  -x c -mthumb -D__SAMD51J18A__ -DDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\arm\CMSIS\5.0.1\CMSIS\Include" -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\SAMD51_DFP\1.0.70\include"  -O1 -ffunction-sections -mlong-calls -g3 -Wall -mcpu=cortex-m4 -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"   -o "$@" "$<" 
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/makedep.mk b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/makedep.mk
index d58c0ac36fd33ca578c91a5c9483ee4acf23fb42..48ac82f043585d6ba80915c4985fb8604fa8b45a 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/makedep.mk
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/Debug/makedep.mk
@@ -2,10 +2,14 @@
 # Automatically-generated file. Do not edit or delete the file
 ################################################################################
 
+bldc_pfoc.c
+
 Device_Startup\startup_samd51.c
 
 Device_Startup\system_samd51.c
 
+encoder.c
+
 main.c
 
 pin.c
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/bldc_pfoc.c b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/bldc_pfoc.c
new file mode 100644
index 0000000000000000000000000000000000000000..9e00465bf03795e20d069de6cf13d0ed951f7c02
--- /dev/null
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/bldc_pfoc.c
@@ -0,0 +1,78 @@
+/*
+ * bldc_pfoc.c
+ *
+ * Created: 2/19/2018 10:40:54 AM
+ *  Author: Jake
+ */ 
+
+#include "bldc_pfoc.h"
+#include "encoder.h"
+#include "sinelut.h"
+#include "pwm_foc.h"
+#include "hardware.h"
+
+void bldc_init(bldc_t *bldc, uint32_t resolution, uint32_t modulo, uint32_t offset, uint32_t reverse){
+	bldc->resolution = resolution;
+	bldc->modulo = modulo;
+	bldc->offset = offset;
+	bldc->reverse = reverse;
+	bldc->modmap = modulo / 1024;
+}
+
+// super simple throttle
+void bldc_command(bldc_t *bldc, uint32_t scalar, uint32_t dir){
+	if(scalar > BLDC_MAX_SCALAR){
+		bldc->scalar = BLDC_MAX_SCALAR;
+	} else {
+		bldc->scalar = scalar;
+	}
+	bldc->dir = dir;
+}
+
+void bldc_update(bldc_t *bldc){
+	// read encoder, where 0 - 2^14 is 0 - 2*PI rads mechanical phase
+	encoder_read(&bldc->reading);		
+	// reverse the reading if that flag is set
+	if(bldc->reverse){
+		bldc->reading = bldc->resolution - bldc->reading;
+	}
+	
+	// electric position and offsetting 
+	// one modulo is one full electric phase, is some division of full rotations
+	// so, elecpos is 0 - modulo (uint) describing 0 - 2*PI rads of an electric phase
+	bldc->elecpos = (bldc->reading + bldc->offset) % bldc->modulo;
+	
+	// now we map the phases from 0 - modulo (uint) -> 0 - 1024 (uint) 
+	// this is the depth of our lookup table for sin()
+	// we computed this value at init, as modulo / lut_depth;
+	// we compute the modpos once
+	bldc->modpos = bldc->elecpos / bldc->modmap;
+	if(bldc->dir){
+		bldc->phaseu = bldc->modpos;
+		bldc->phasev = bldc->modpos + 341; // + 120* in lut phase
+		bldc->phasew = bldc->modpos + 682; // + 240*
+	} else {
+		bldc->phaseu = bldc->modpos + 682;
+		bldc->phasev = bldc->modpos + 341; // + 120* in lut phase
+		bldc->phasew = bldc->modpos; // + 240*
+	}
+
+	
+	// now we check for wrap arounds in the lut phase
+	(bldc->phaseu > 1023) ? (bldc->phaseu -= 1023) : (0);
+	(bldc->phasev > 1023) ? (bldc->phasev -= 1023) : (0);
+	(bldc->phasew > 1023) ? (bldc->phasew -= 1023) : (0);
+	
+	// and set the pwm value to be the sin of it's current electric phase,
+	// where the sinelut returns a value 0 - 255
+	// midpoint (0 volts, equal time on / off) being 128
+	// and multiply that value by a scalar
+	bldc->pwmu = (sinelut[bldc->phaseu] * bldc->scalar) / 255;
+	bldc->pwmv = sinelut[bldc->phasev] * bldc->scalar / 255;
+	bldc->pwmw = sinelut[bldc->phasew] * bldc->scalar / 255;
+	
+	// finally, sending new pwm values
+	// we assert the minimum value to be 5 and the max to be 250
+	// this leaves some time for deadtime insertion (set with TCC_WEXCTRL_DTHS(1) in PWM setup)
+	pwmupdate_foc(bldc->pwmu,bldc->pwmv,bldc->pwmw);
+}
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/bldc_pfoc.h b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/bldc_pfoc.h
new file mode 100644
index 0000000000000000000000000000000000000000..a46ee934e4b43a847e3260a56114e1d39dce7932
--- /dev/null
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/bldc_pfoc.h
@@ -0,0 +1,46 @@
+/*
+ * bldc_pfoc.h
+ *
+ * Created: 2/19/2018 10:40:36 AM
+ *  Author: Jake
+ */ 
+
+
+#ifndef BLDC_PFOC_H_
+#define BLDC_PFOC_H_
+
+#include "sam.h"
+
+#define BLDC_MAX_SCALAR 255
+#define BLDC_INIT_OFFSET 700
+#define BLDC_RESOLUTION 16834
+#define BLDC_MODULO 2340
+#define BLDC_REVERSE 1
+
+// struct to wrap commutation information
+typedef struct{
+	uint32_t resolution;
+	uint32_t modulo;
+	uint32_t offset;
+	uint32_t reverse;
+	uint32_t modmap;
+	
+	uint32_t scalar;
+	uint32_t dir;
+	
+	uint32_t reading;
+	uint32_t elecpos;
+	uint32_t modpos;
+	
+	uint32_t phaseu, pwmu;
+	uint32_t phasev, pwmv;
+	uint32_t phasew, pwmw;
+}bldc_t;
+
+void bldc_init(bldc_t *bldc, uint32_t resolution, uint32_t modulo, uint32_t offset, uint32_t reverse);
+
+void bldc_command(bldc_t *bldc, uint32_t scalar, uint32_t dir);
+
+void bldc_update(bldc_t *bldc);
+
+#endif /* BLDC_PFOC_H_ */
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/encoder.c b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/encoder.c
new file mode 100644
index 0000000000000000000000000000000000000000..9e2114269ea6c5582e1a330c3972b9cd870ff39c
--- /dev/null
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/encoder.c
@@ -0,0 +1,19 @@
+/*
+ * encoder.c
+ *
+ * Created: 2/19/2018 11:11:57 AM
+ *  Author: Jake
+ */ 
+
+#include "encoder.h"
+#include "hardware.h"
+
+void encoder_read(uint32_t *data_return){
+	uint8_t data_tx[2] = {0xFF, 0xFF}; // read command: 1s, noop: 0s
+	uint8_t data_rx[2];
+	spi_txrxchars_polled(&spi_encoder, data_tx, 2, data_rx);
+	data_tx[0] = 0x00;
+	data_tx[1] = 0x00;
+	spi_txrxchars_polled(&spi_encoder, data_tx, 2, data_rx);
+	* data_return = (data_rx[0] & 0b00111111) << 8 | data_rx[1];
+}
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/encoder.h b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/encoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..4bc3d48c7e9b00171e87c70d6da55517ed9db116
--- /dev/null
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/encoder.h
@@ -0,0 +1,16 @@
+/*
+ * encoder.h
+ *
+ * Created: 2/19/2018 11:11:23 AM
+ *  Author: Jake
+ */ 
+
+
+#ifndef ENCODER_H_
+#define ENCODER_H_
+
+#include "sam.h"
+
+void encoder_read(uint32_t *data_return);
+
+#endif /* ENCODER_H_ */
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/hardware.h b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/hardware.h
index 1566dda3d369e8e5cb02c7eca5fa801c27ebef5a..54e7ff6a0dcace4359c17bccc90076a9c3def94c 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/hardware.h
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/hardware.h
@@ -13,6 +13,8 @@
 #include "ringbuffer.h"
 #include "uartport.h"
 #include "spiport.h"
+#include "bldc_pfoc.h"
+#include "pwm_foc.h"
 
 pin_t stlb;
 
@@ -33,4 +35,8 @@ pin_t m_gain;
 pin_t fault;
 pin_t octw;
 
+bldc_t bldc;
+
+pin_t testp;
+
 #endif /* HARDWARE_H_ */
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/main.c b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/main.c
index 4cbb5f5cac5daf6de3696bf3b2acdc5986690d38..cd5bedc5a712c7d6c272d76eed79f33febb86e72 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/main.c
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/main.c
@@ -7,27 +7,42 @@
 
 
 #include "sam.h"
-#include "pwm_foc.h"
-#include "pin.h"
-#include "ringbuffer.h"
-#include "uartport.h"
-#include "spiport.h"
 #include "hardware.h"
-#include "sinelut.h"
+#include "encoder.h"
 #include <stdio.h>
 
-// stlb is PA23
-
-uint32_t encoder_read(void){
-	uint8_t data_tx[2] = {0xFF, 0xFF}; // read command: 1s, noop: 0s
-	uint8_t data_rx[2];
-	spi_txrxchars_polled(&spi_encoder, data_tx, 2, data_rx);
-	data_tx[0] = 0x00;
-	data_tx[1] = 0x00;
-	spi_txrxchars_polled(&spi_encoder, data_tx, 2, data_rx);
-	
-	uint32_t data = (data_rx[0] & 0b00111111) << 8 | data_rx[1];
-	return data;
+void comticker_init(uint32_t cc){
+	// Timers: in 32 bit mode we pair two - obscure datasheet reading later, they pair in a predefined way: 0 with 1...
+	// a word of warning: with the same code, a 16-bit timer was not working. I am mystified.
+	TC2->COUNT32.CTRLA.bit.ENABLE = 0;
+	TC3->COUNT32.CTRLA.bit.ENABLE = 0;
+	// unmask clocks
+	MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC2 | MCLK_APBBMASK_TC3; // at 15.8.9
+	// generate a gclk
+	GCLK->GENCTRL[11].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL) | GCLK_GENCTRL_GENEN;
+	while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(11));
+	// ship gclk to their channels
+	GCLK->PCHCTRL[TC2_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(11);
+	GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(11);
+	// turn on in mode, presync
+	TC2->COUNT32.CTRLA.reg = TC_CTRLA_MODE_COUNT32 | TC_CTRLA_PRESCSYNC_PRESC | TC_CTRLA_PRESCALER_DIV16 | TC_CTRLA_CAPTEN0;// | TC_CTRLA_CAPTEN1;
+	TC3->COUNT32.CTRLA.reg = TC_CTRLA_MODE_COUNT32 | TC_CTRLA_PRESCSYNC_PRESC | TC_CTRLA_PRESCALER_DIV16 | TC_CTRLA_CAPTEN0;// | TC_CTRLA_CAPTEN1;
+	// do frequency match
+	TC2->COUNT32.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
+	TC3->COUNT32.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
+	// allow interrupt to trigger on this event (compare channel 0)
+	TC2->COUNT32.INTENSET.bit.MC0 = 1;
+	TC2->COUNT32.INTENSET.bit.MC1 = 1; // don't know why, but had to turn this on to get the interrupts
+	// set period
+	while(TC2->COUNT32.SYNCBUSY.bit.CC0);
+	TC2->COUNT32.CC[0].reg = 165;
+	while(TC2->COUNT32.SYNCBUSY.bit.CC1);
+	TC2->COUNT32.CC[1].reg = 165;
+	// enable, sync for enable write
+	TC2->COUNT32.CTRLA.bit.ENABLE = 1;
+	while(TC2->COUNT32.SYNCBUSY.bit.ENABLE);
+	TC3->COUNT32.CTRLA.bit.ENABLE = 1;
+	while(TC3->COUNT32.SYNCBUSY.bit.ENABLE);
 }
 
 int main(void)
@@ -46,6 +61,7 @@ int main(void)
 	NVIC_EnableIRQ(SERCOM4_2_IRQn); //up1rx
 	NVIC_EnableIRQ(SERCOM5_0_IRQn);
 	NVIC_EnableIRQ(SERCOM5_2_IRQn);
+	NVIC_EnableIRQ(TC2_IRQn);
 	
 	// Rinbuffers for UARTs
 	rb_init(&up1_rbrx);
@@ -103,11 +119,10 @@ int main(void)
 	// OCTW PA22: input, open drain when active, use pullup
 	fault = pin_new(&PORT->Group[0], 21);
 	pin_input(&fault);
+	pin_pullup(&fault);
 	octw = pin_new(&PORT->Group[0], 22);
 	pin_input(&octw);
-	
-	// do closed loop sinucommutate, following old ATSAMS70 code
-	// do BLDC closed, open loop
+	pin_pullup(&octw);
 	
 	// enable or don't
 	//pin_clear(&en_gate);
@@ -118,61 +133,31 @@ int main(void)
 	pin_clear(&m_gain);
 	pin_clear(&dc_cal);
 	
-	uint32_t resolution = 16384;
-	uint32_t modulo = 2340;
-	uint32_t offset = 800;
-	uint32_t reverse = 1;
-	uint32_t scalar = 42; // of 255
+	bldc_init(&bldc, BLDC_RESOLUTION, BLDC_MODULO, BLDC_INIT_OFFSET, BLDC_REVERSE);
+	bldc_command(&bldc, 90, 1);
 	
-	uint32_t reading;
-	uint32_t elecpos;
-		
-	uint32_t phaseu, pwmu;
-	uint32_t phasev, pwmv;
-	uint32_t phasew, pwmw;
+	// startup timer for 5khz commutation loop
+	comticker_init(64);
 	
     while (1) 
     {
-		// read encoder & get electric position (relative phase period, not rotation period)
-		reading = encoder_read();		
-		if(reverse){
-			reading = resolution - reading;
+		if(!pin_read(&octw)){
+			pin_clear(&stlb);
 		}
-		elecpos = (reading + offset) % modulo;
-		
-		// not LUT'd yet
-		phaseu = elecpos / (modulo / 1024);
-		phasev = elecpos / (modulo / 1024) + 341;
-		phasew = elecpos / (modulo / 1024) + 682;
-		
-		(phaseu > 1023) ? (phaseu -= 1023) : (0);
-		(phasev > 1023) ? (phasev -= 1023) : (0);
-		(phasew > 1023) ? (phasew -= 1023) : (0);
-		
-		pwmu = sinelut[phaseu] * scalar / 255 + 1;
-		pwmv = sinelut[phasev] * scalar / 255 + 1;
-		pwmw = sinelut[phasew] * scalar / 255 + 1;
-		
-		pwmupdate_foc(pwmu,pwmv,pwmw);
+		// timer should be running, doing bldc things
+		// use these while() loops to handle network
     }
 }
 
-// ok, we're commutating
-/*
- - ticking: it's a PWM update error, occasionally setting some values to 100%
- - implement a timer to commutate at a set frequency
- - work through the mess of values above, go faster, cleaner
- - basic search for reverse, offset, modulo etc would be cool, hard (probably)
- - verify all gates are opening
-*/
-
 uint8_t loopcnt;
 
 void SysTick_Handler(void){
-	pin_toggle(&stlb);
+	pin_set(&stlb);
 	loopcnt ++;
 	uart_sendchar_buffered(&up1, loopcnt);
-	uint32_t reading = encoder_read();
+	uint32_t reading;
+	//encoder_read(&reading);
+	reading = bldc.reading;
 	uint8_t d1 = reading >> 8;
 	uint8_t d2 = reading;
 	uart_sendchar_buffered(&up1, d1);
@@ -194,3 +179,9 @@ void SERCOM5_0_Handler(void){
 void SERCOM5_2_Handler(void){
 	uart_rxhandler(&up2);
 }
+
+void TC2_Handler(void){
+	TC2->COUNT32.INTFLAG.bit.MC0 = 1;
+	TC2->COUNT32.INTFLAG.bit.MC1 = 1;
+	bldc_update(&bldc);
+}
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/mkbldcdriver-v03.cproj b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/mkbldcdriver-v03.cproj
index afe85b64ef67f91c5fd6183067a3f0b530010b38..fc8e8ed7f97fb9073b2607668c73b307ff32f261 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/mkbldcdriver-v03.cproj
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/mkbldcdriver-v03.cproj
@@ -159,12 +159,24 @@
     </ToolchainSettings>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="bldc_pfoc.c">
+      <SubType>compile</SubType>
+    </Compile>
+    <Compile Include="bldc_pfoc.h">
+      <SubType>compile</SubType>
+    </Compile>
     <Compile Include="Device_Startup\startup_samd51.c">
       <SubType>compile</SubType>
     </Compile>
     <Compile Include="Device_Startup\system_samd51.c">
       <SubType>compile</SubType>
     </Compile>
+    <Compile Include="encoder.c">
+      <SubType>compile</SubType>
+    </Compile>
+    <Compile Include="encoder.h">
+      <SubType>compile</SubType>
+    </Compile>
     <Compile Include="hardware.h">
       <SubType>compile</SubType>
     </Compile>
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.c b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.c
index 6bf2a5e55b0893e0b4b4052a31436964ebcbce3a..9912983950eebb318ebba0db9b954f338553c376 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.c
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.c
@@ -11,6 +11,7 @@
 pin_t pin_new(PortGroup *port, uint32_t pin_number){
 	pin_t pin;
 	pin.port = port;
+	pin.pin = pin_number;
 	pin.bm = (uint32_t)(1 << pin_number);
 	return pin;
 }
@@ -23,6 +24,16 @@ void pin_output(pin_t *pin){
 
 void pin_input(pin_t *pin){
 	pin->port->DIRCLR.reg = pin->bm;
+	pin->port->PINCFG[pin->pin].bit.INEN = 1;
+}
+
+void pin_pullup(pin_t *pin){
+	pin->port->PINCFG[pin->pin].bit.PULLEN = 1;
+	pin->port->OUTSET.reg |= pin->bm;
+}
+
+void pin_pulldown(pin_t *pin){
+	pin->port->PINCFG[pin->pin].bit.PULLEN = 1;
 }
 
 void pin_set(pin_t *pin){
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.h b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.h
index 8ea143d44285a299ca78e49da22ffc154f0b4a7f..bc6c45f7b5ba388717003bd3ffe887134a6cd624 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.h
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pin.h
@@ -13,14 +13,20 @@
 
 typedef struct {
 	PortGroup *port;
+	uint32_t pin;
 	uint32_t bm; // bitmask
 } pin_t;
 
 pin_t pin_new(PortGroup *port, uint32_t pin);
 
 void pin_output(pin_t *pin);
+
 void pin_input(pin_t *pin);
 
+void pin_pullup(pin_t *pin);
+
+void pin_pulldown(pin_t *pin);
+
 void pin_set(pin_t *pin);
 void pin_clear(pin_t *pin);
 void pin_toggle(pin_t *pin);
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.c b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.c
index 03b334718a58e644f828a677c6d49d19b74b2075..c748d902d38c4ad1fea1d5bae6ca56354841c70c 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.c
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.c
@@ -5,6 +5,7 @@
  *  Author: Jake
  */ 
 #include "pwm_foc.h"
+#include "hardware.h"
 
 void pwmsetup_foc(void){
 	/* TCC SETUP */
@@ -49,7 +50,7 @@ void pwmsetup_foc(void){
 	
 	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);
+	TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV4 | TCC_CTRLA_PRESCSYNC_PRESC |TCC_CTRLA_RESOLUTION(0);
 	
 	// (2) Select Waveform Generation operation in the WAVE register WAVE.WAVEGEN
 	// we want dual slope pwm
@@ -62,14 +63,14 @@ void pwmsetup_foc(void){
 	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->PER.reg = TCC_PER_PER(256); 
 	
 	TCC0->COUNT.reg = 0;
 	
-	TCC0->CC[0].reg = 0; // '3'
-	TCC0->CC[1].reg = 0; // '2'
-	TCC0->CC[2].reg = 0; // '1'
-	TCC0->CC[3].reg = 0;
+	TCC0->CCBUF[0].reg = 0; // '3'
+	TCC0->CCBUF[1].reg = 0; // '2'
+	TCC0->CCBUF[2].reg = 0; // '1'
+	TCC0->CCBUF[3].reg = 0;
 	
 	// (4) Enable with CTRLA.ENABLE
 	
@@ -77,8 +78,27 @@ void pwmsetup_foc(void){
 	while(TCC0->SYNCBUSY.bit.ENABLE);
 }
 
+#define PWMFOC_MIN_PWM 3
+#define PWMFOC_MAX_PWM 253
+
 void pwmupdate_foc(uint32_t one, uint32_t two, uint32_t three){
-	TCC0->CC[0].reg = three; // '3'
-	TCC0->CC[1].reg = two; // '2'
-	TCC0->CC[2].reg = one; // '1'
+	pwm_bounds(&three);
+	pwm_bounds(&two);
+	pwm_bounds(&one);
+	/*
+	uart_sendchar_buffered(&up1, one);
+	uart_sendchar_buffered(&up1, two);
+	uart_sendchar_buffered(&up1, three);
+	*/
+	TCC0->CCBUF[0].reg = three; // '3'
+	TCC0->CCBUF[1].reg = two; // '2'
+	TCC0->CCBUF[2].reg = one; // '1'
+}
+
+void pwm_bounds(uint32_t *val){
+	if(*val > PWMFOC_MAX_PWM){
+		*val = PWMFOC_MAX_PWM;
+	} else if(*val < PWMFOC_MIN_PWM){
+		*val = PWMFOC_MIN_PWM;
+	}
 }
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.h b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.h
index 94b7909950635e1c92add467fbdf2dc953e10f84..c9a35bee01d1a656bc7d34fefbed67d4fef8379e 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.h
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/pwm_foc.h
@@ -12,6 +12,11 @@
 #include "sam.h"
 
 void pwmsetup_foc(void);
-void pwmupdate_foc(uint32_t one, uint32_t two, uint32_t three); // 0 -> 255 (but count deadtime?) midpoint is 126
+
+// 0 -> 255 (but count deadtime?) midpoint is 126
+void pwmupdate_foc(uint32_t one, uint32_t two, uint32_t three); 
+
+// asserts min and max for pwm
+void pwm_bounds(uint32_t *val);
 
 #endif /* PWM_FOC_H_ */
\ No newline at end of file
diff --git a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/sinelut.h b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/sinelut.h
index 198f4b46dc015cfeb6c52245aea8c4516279835d..943c4f388dcc425b28a42a4412263f725fd8100a 100644
--- a/embedded/mkbldcdriver-v03/mkbldcdriver-v03/sinelut.h
+++ b/embedded/mkbldcdriver-v03/mkbldcdriver-v03/sinelut.h
@@ -11,8 +11,8 @@
 
 /*
 takes 0 - 1023 phase position and returns sin from 0 - 255
+http://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml
 */
-
 uint32_t sinelut[1024] = {
 	128,128,129,130,131,131,132,133,
 	134,135,135,136,137,138,138,139,
@@ -144,5 +144,4 @@ uint32_t sinelut[1024] = {
 	122,123,124,124,125,126,127,128
 	};
 
-
 #endif /* SINELUT_H_ */
\ No newline at end of file
diff --git a/images/programming-currents-olcommutate-1.png b/images/programming-currents-olcommutate-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..70e5f1c980841ccf3880faa77974bd53acdf667c
Binary files /dev/null and b/images/programming-currents-olcommutate-1.png differ
diff --git a/images/programming-currents-olcommutate-2.png b/images/programming-currents-olcommutate-2.png
new file mode 100644
index 0000000000000000000000000000000000000000..19cfcda57666956cbef1ca0f78a0a93169704779
Binary files /dev/null and b/images/programming-currents-olcommutate-2.png differ
diff --git a/images/programming-pwm-ticking.png b/images/programming-pwm-ticking.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8bd6576714b180d5c05bc6f8b94917d7f1d0cad
Binary files /dev/null and b/images/programming-pwm-ticking.png differ
diff --git a/video/20khz-commutate.mp4 b/video/20khz-commutate.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..fb0945c781d785c08aec9296a054a155092f23aa
Binary files /dev/null and b/video/20khz-commutate.mp4 differ