summaryrefslogtreecommitdiff
path: root/sw/Core/Src/calib.h
diff options
context:
space:
mode:
authorStijn Kuipers <stijnkuipers@gmail.com>2023-06-29 16:26:07 +0200
committerStijn Kuipers <stijnkuipers@gmail.com>2023-06-29 16:26:07 +0200
commitfb5a321dd7c2848128b04b306f3e1e59c87a3f70 (patch)
treea8ef6273f9f331ebb1971a9baf20a8c897955612 /sw/Core/Src/calib.h
parentbae7568fd4dd0676b370be8548c7ec95d6521ba1 (diff)
downloadplinky-fb5a321dd7c2848128b04b306f3e1e59c87a3f70.tar.gz
Initial Filedump
Tadaaa!!
Diffstat (limited to 'sw/Core/Src/calib.h')
-rwxr-xr-xsw/Core/Src/calib.h440
1 files changed, 440 insertions, 0 deletions
diff --git a/sw/Core/Src/calib.h b/sw/Core/Src/calib.h
new file mode 100755
index 0000000..c147a27
--- /dev/null
+++ b/sw/Core/Src/calib.h
@@ -0,0 +1,440 @@
+#ifdef HALF_FLASH
+const static int calib_sector = -1;
+#else
+const static int calib_sector = 255;
+#endif
+const static uint64_t MAGIC = 0xf00dcafe473ff02a; // bump on change
+bool flash_writecalib(int which);
+int flash_readcalib(void) {
+#ifdef EMU
+ openflash();
+#endif
+#if defined EMU // WASM?
+#ifndef CALIB_TEST
+ if (1) {
+ // fake calib results
+ for (int i = 0; i < 18; ++i) {
+ for (int j = 0; j < 8; ++j)
+ calibresults[i].pressure[j] = 8000,
+ calibresults[i].pos[j] = (512 * j - 1792) * (((i % 9) == 8) ? -1 : 1);
+ }
+ return 3;
+ }
+#endif
+#endif
+ volatile uint64_t *flash = (volatile uint64_t*) (FLASH_ADDR_256 + calib_sector * 2048);
+ int ver = 0, ok=0;
+ if (flash[0] == MAGIC && flash[255] == ~MAGIC) ver = 2;
+ if (ver==0) {
+ DebugLog("no calibration found in flash\r\n");
+ return 0;
+ }
+ volatile uint64_t *s = flash + 1;
+ if(*s!=~(uint64_t)(0)) {
+ ok|=1;
+ memcpy(calibresults, (uint64_t*) s, sizeof(calibresults));
+ }
+ s += sizeof(calibresults) / 8;
+ if (*s!=~(uint64_t)(0)) {
+ ok|=2;
+ memcpy(cvcalib, (int64_t*)s, sizeof(cvcalib));
+ }
+ s += sizeof(cvcalib) / 8;
+ return ok;
+}
+
+bool flash_writecalib(int which) {
+ HAL_FLASH_Unlock();
+ int rv = flash_erase_page(calib_sector);
+ if (rv == 0) {
+ uint64_t* flash = (uint64_t*)(FLASH_ADDR_256 + calib_sector * 2048);
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(size_t)flash, MAGIC);
+ uint64_t* d = flash + 1;
+ if (which&1)
+ flash_program_block(d, calibresults, sizeof(calibresults));
+ d+=(sizeof(calibresults)+7)/8;
+ if (which&2)
+ flash_program_block(d, cvcalib, sizeof(cvcalib));
+ d+=(sizeof(cvcalib)+7)/8;
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, (uint32_t)(size_t)(flash + 255), ~MAGIC);
+ }
+ HAL_FLASH_Lock();
+ return 0;
+}
+
+extern s8 enable_audio;
+
+static inline float deadzone(float f, float zone) {
+ if (f<zone && f>-zone) return 0.f;
+ if (f > 0.f) f -= zone; else f += zone;
+ return f;// *f* (1.f / 2048.f);
+}
+
+#ifdef EMU
+static inline int getgatesense(void) {
+ return emugatesense;
+}
+static inline int getpitchsense(void) {
+ return emupitchsense;
+}
+#else
+static inline int getgatesense(void) {
+ return HAL_GPIO_ReadPin(SENSE1_GPIO_Port, SENSE1_Pin) == GPIO_PIN_RESET;
+}
+static inline int getpitchsense(void) {
+ return HAL_GPIO_ReadPin(SENSE2_GPIO_Port, SENSE2_Pin) == GPIO_PIN_RESET;
+}
+#endif
+
+void cv_calib(void) {
+#ifdef WASM
+ return;
+#endif
+ enable_audio = EA_OFF;
+ clear();
+ int topscroll = 128;
+ const char* topline = "unplug all inputs"
+#ifndef NEW_LAYOUT
+ " except plug gate out to gate in"
+#endif
+ ". use left 4 columns to adjust pitch cv outputs. plug pitch lo output to pitch input when done.";
+ int toplinew = strwidth(F_16, topline);
+ const char* const botlines[5] = { "plug gate out->in", "Pitch lo=0v/C0", "Pitch lo=2v/C2", "Pitch hi=0v/C0", "Pitch hi=2v/C2" };
+ u8 ff = finger_frame_ui;
+ float adcavgs[6][2];
+ for (int i = 0; i < 6; ++i)
+ adcavgs[i][0] = -1.f;
+ int curx = -1;
+ float downpos[4], downval[4];
+ float cvout[4] = {
+ cvcalib[8].bias, cvcalib[8].bias + cvcalib[8].scale * 2048.f * 24.f,
+ cvcalib[9].bias, cvcalib[9].bias + cvcalib[9].scale * 2048.f * 24.f,
+ };
+ SetOutputCVPitchLo((int)cvout[0], false);
+ SetOutputCVPitchHi((int)cvout[2], false);
+ u8 curlo = 0;
+ u8 curhi = 2;
+ bool prevprevpitchsense=true;
+ bool prevpitchsense=true;
+ while (1) {
+ clear();
+ drawstr_noright(topscroll, 0, F_16, topline);
+ bool gateok = getgatesense();
+#ifdef NEW_LAYOUT
+ gateok = !gateok; // in new layout, theres a bleed resistor so no need for gate - in fact, we dont want it
+#endif
+ drawstr(0, 18, F_12_BOLD, (curx<0 && gateok) ? "pitch out>in when done" : botlines[curx+1]);
+ if (curx >= 0)
+ fdrawstr(-128, 24, F_8, "(%d)", (int)cvout[curx]);
+ oled_flip(vrambuf);
+ topscroll-=2;
+ if (topscroll < -toplinew)
+ topscroll = 128;
+ while (finger_frame_ui == ff); // wait for new touch data
+ ff = finger_frame_ui;
+ int pitchsense = getpitchsense();
+ if (pitchsense && !prevprevpitchsense && gateok)
+ break;
+ prevprevpitchsense=prevpitchsense;
+ prevpitchsense=pitchsense;
+
+ // calibrate the 0 point for the inputs
+ for (int i = 0; i < 6; ++i) {
+ int tot = 0;
+ for (int j = 0; j < ADC_SAMPLES; ++j)
+ tot += adcbuf[j * ADC_CHANS + i];
+ tot /= ADC_SAMPLES;
+ if (adcavgs[i][0] < 0)
+ adcavgs[i][0] = adcavgs[i][1] = tot;
+ adcavgs[i][0] += (tot - adcavgs[i][0]) * 0.05f;
+ adcavgs[i][1] += (adcavgs[i][0] - adcavgs[i][1]) * 0.05f;
+ }
+ for (int x = 0; x < 4; ++x) {
+ Finger* f = touch_ui_getlatest(x);
+ Finger* pf = touch_ui_getprev(x);
+ if (f->pressure >= 500) {
+ if (pf->pressure < 500) {
+ downpos[x] = f->pos;
+ downval[x] = cvout[x];
+ curx = x;
+ }
+ float delta = deadzone(f->pos - downpos[x], 64.f);
+// delta=(delta*delta)>>12;
+ cvout[x] += (clampf(downval[x] + delta*0.25f, 0.f, 65535.f) - cvout[x])*0.1f;
+ if (x < 2)
+ curlo = x;
+ else
+ curhi = x;
+ }
+ }
+ SetOutputCVPitchLo((int)cvout[curlo], false);
+ SetOutputCVPitchHi((int)cvout[curhi], false);
+ AdvanceCVOut();
+
+ // update the leds, innit
+ for (int fi = 0; fi < 9; ++fi) {
+ for (int y = 0; y < 8; ++y) {
+ int k = (fi<4)?(triangle(y*64-(int)cvout[fi]/4)/4):128;
+ led_ram[fi][y] = led_gamma(((fi==curx)?255:128)-k);
+ }
+ }
+
+ }
+ // zero is now nicely set
+ for (int i = 0; i < 6; ++i) {
+ cvcalib[i].bias = adcavgs[i][1];
+ cvcalib[i].scale = 0.2f / -6548.1f;
+ DebugLog("adc zero point %d - %d\r\n", i, (int)adcavgs[i][1]);
+ }
+ cvcalib[6].bias=32000.f; // hw seems to skew towards 0 slightly...
+ cvcalib[6].scale = 1.05f / -32768.f;
+ cvcalib[7].bias=32000.f;
+ cvcalib[7].scale= 1.05f / -32768.f;
+
+ // output calib is now nicely set
+ cvcalib[8].bias = cvout[0];
+ cvcalib[8].scale = (cvout[1] - cvout[0]) * 1.f / (2048.f*24.f);
+ cvcalib[9].bias = cvout[2];
+ cvcalib[9].scale = (cvout[3] - cvout[2]) * 1.f / (2048.f * 24.f);
+ for (int i = 0; i < 4; ++i)
+ DebugLog("selected dac value %d - %d\r\n", i, (int)cvout[i]);
+ DebugLog("dac pitch lo zero point %d, step*1000 %d\r\n", (int)cvcalib[8].bias, (int)(cvcalib[8].scale*1000.f));
+ DebugLog("dac pitch hi zero point %d, step*1000 %d\r\n", (int)cvcalib[9].bias, (int)(cvcalib[9].scale * 1000.f));
+
+ // use it to calibrate
+
+ clear();
+ drawstr(0, 4, F_12, "waiting for pitch\nloopback cable");
+ oled_flip(vrambuf);
+ HAL_Delay(1000);
+ // wait for them to plug the other end in
+ while (1) {
+ int tots[2] = { 0 };
+ for (int hilo = 0; hilo < 2; ++hilo) {
+ SetOutputCVPitchLo((int)cvout[hilo], false);
+ SetOutputCVPitchHi((int)cvout[hilo + 2], false);
+ HAL_Delay(50);
+ int tot = 0;
+ for (int j = 0; j < ADC_SAMPLES; ++j)
+ tot += adcbuf[j * ADC_CHANS + ADC_PITCH];
+ tot /= ADC_SAMPLES;
+ tots[hilo] = tot;
+ }
+ if (abs(tots[0]- tots[1]) > 5000)
+ break;
+ }
+ clear();
+ drawstr(0,4,F_24_BOLD, "just a mo...");
+ oled_flip(vrambuf);
+ HAL_Delay(1000);
+ for (int hilo = 0; hilo < 2; ++hilo) {
+ SetOutputCVPitchLo((int)cvout[hilo], false);
+ SetOutputCVPitchHi((int)cvout[hilo+2], false);
+ HAL_Delay(50);
+ int tot = 0;
+ for (int iter = 0; iter < 256; ++iter) {
+ HAL_Delay(2);
+ for (int j = 0; j < ADC_SAMPLES; ++j)
+ tot += adcbuf[j * ADC_CHANS + ADC_PITCH];
+ }
+ tot /= ADC_SAMPLES * 256;
+ DebugLog("pitch adc for hilo=%d is %d\r\n", hilo, tot);
+ if (hilo == 0)
+ cvcalib[ADC_PITCH].bias = tot;
+ else
+ cvcalib[ADC_PITCH].scale = 2.f / (minf(-0.00001f,tot - cvcalib[ADC_PITCH].bias));
+ }
+ clear();
+ drawstr(0, 0, F_16_BOLD, "Done!");
+ drawstr(0, 16, F_12_BOLD, "Unplug pitch cable!");
+ oled_flip(vrambuf);
+ while (getpitchsense()) {
+ HAL_Delay(1);
+ }
+}
+
+void reflash(void);
+extern volatile u8 gotclkin;
+
+void led_test(void) {
+ enable_audio = EA_PASSTHRU;
+ for (int y = 0; y < 9; ++y) for (int x = 0; x < 8; ++x)
+ led_ram[y][x] = 255;
+ u16 tri = 128;
+ int encoder_down_count = -1;
+ while (1) {
+ clear();
+ if (encbtn) {
+ if (encoder_down_count >= 0)
+ encoder_down_count++;
+ }
+ else {
+ if (encoder_down_count > 2) {
+ HAL_Delay(20);
+ return;
+ }
+ encoder_down_count = 0;
+ }
+ if (encoder_down_count > 100)
+ reflash();
+ fdrawstr(0, 2, F_12, "TEST %d %d %d %d %02x", adcbuf[0] / 256, adcbuf[1] / 256, adcbuf[2] / 256, adcbuf[3] / 256, gotclkin);
+ fdrawstr(0, 18, F_12, "%d %d %d %d %d %d", adcbuf[4] / 256, adcbuf[5] / 256, adcbuf[6] / 256, adcbuf[7] / 256, encval >> 2, encbtn);
+ oled_flip(vrambuf);
+ HAL_Delay(20);
+ for (int srcidx = 0; srcidx < 9; ++srcidx) {
+ int a = finger_cap(srcidx * 2);
+ int b = finger_cap(srcidx * 2 + 1);
+ int amin = finger_mincap(srcidx * 2);
+ int bmin = finger_mincap(srcidx * 2 + 1);
+ int rawpressure = finger_rawpressure(a - amin, b - bmin);
+ int rawpos = finger_rawpos(a, b);
+ if (rawpressure > 300) {
+ DebugLog("f %d - a=%4d b=%4d amin=%4d bmin=%4d pos=%4d pr=%4d \r\n", srcidx, a, b, amin, bmin, rawpos, rawpressure);
+ }
+ }
+ tri += 256;
+ SetOutputCVTrigger((tri < 16384) ? 65535 : 0);
+ SetOutputCVClk((tri < (16384 + 32768)) ? 65535 : 0);
+ SetOutputCVPressure(tri);
+ SetOutputCVGate((tri * 2) & 65535);
+ SetOutputCVPitchLo(tri, false);
+ SetOutputCVPitchHi((tri * 2) & 65535, false);
+ }
+}
+
+void calib(void) {
+again:
+#ifdef WASM
+ return;
+#endif
+
+
+
+ enable_audio=EA_OFF;
+ touch_reset_calib();
+
+ HAL_Delay(20);
+ CalibProgress* state = GetCalibProgress(0);
+ memset(state, 0, sizeof(CalibProgress) * 18);
+ int prevrawpressure[18] = { 0 };
+ s8 curstep[9] = { 7,7,7,7,7,7,7,7,7 };
+ int done = false;
+ u8 ff = finger_frame_ui;
+ u8 refreshscreen = 0;
+ char helptext[64] = "slowly/evenly press lit pads.\ntake care, be accurate!";
+ bool blink = false;
+ int errors = 0;
+ while (!done) {
+
+ if (!refreshscreen) {
+ refreshscreen = 16;
+ blink = !blink;
+ clear();
+ fdrawstr(0, 0, F_16, "Calibration%c",blink?'!':' ');
+ drawstr(0, 16, F_8, helptext);
+ if (errors)
+ invertrectangle(0, 0, 128, 32);
+ oled_flip(vrambuf);
+ }
+ else {
+ refreshscreen--;
+ }
+
+ if (encbtn) {
+ led_test();
+ goto again;
+ }
+
+
+ while (finger_frame_ui == ff); // wait for new touch data
+ ff = finger_frame_ui;
+ // update the 18 calibration entries for the current step
+ done = 0;
+ int readymask=0;
+ for (int si = 0; si < 18; ++si) {
+ int a = finger_cap(si*2);
+ int b = finger_cap(si*2 + 1);
+ int amin = finger_mincap(si * 2);
+ int bmin = finger_mincap(si * 2 + 1);
+ int amax = finger_maxcap(si * 2);
+ int bmax = finger_maxcap(si * 2 + 1);
+ int rawpressure = finger_rawpressure(a-amin,b-bmin);
+ int prevrawp = prevrawpressure[si];
+ int rawpos = finger_rawpos(a,b);
+ int step = curstep[si % 9];
+
+ int pressureband = rawpressure / 20;
+ if (step >=0 && step<8 && rawpressure > 1200 && rawpressure > prevrawp - pressureband/2 && rawpressure < prevrawp + pressureband) {
+ // pressure is quite stable
+ float w = (rawpressure - 1200.f) / 1000.f;
+ float change = abs(prevrawp - rawpressure)*(1.f/250.f);
+ w *= maxf(0.f,1.f - change);
+ if(w>1.f) w=1.f;
+ w *= w;
+ const static float LEAK = 0.90f;
+ state[si].weight[step] *= LEAK;
+ state[si].pos[step] *= LEAK;
+ state[si].pressure[step] *= LEAK;
+ state[si].weight[step] += w;
+ state[si].pos[step] += rawpos * w;
+ state[si].pressure[step] += rawpressure * w;
+
+ if (0) if (si<9) DebugLog("finger %d step %d pos %4d %4d pressure %5d %5d weight %3d %d \r\n", si, step,
+ (int)(state[si].pos[step] / state[si].weight[step]),
+ (int)(state[si + 9].pos[step] / state[si + 9].weight[step]),
+ (int)(state[si].pressure[step] / state[si].weight[step]),
+ (int)(state[si + 9].pressure[step] / state[si + 9].weight[step]),
+ (int)state[si].weight[step], (int)state[si+9].weight[step]
+ );
+ }
+ int ti = si + 9;
+ bool ready=si < 9 && step<8 && step>=0 && state[si].weight[step]>4.f && state[ti].weight[step]>4.f;
+ if (ready) {
+ if (rawpressure < 900) {
+ // move on!
+ calibresults[si].pressure[step] = state[si].pressure[step] / state[si].weight[step];
+ calibresults[si].pos[step] = state[si].pos[step] / state[si].weight[step];
+ calibresults[ti].pressure[step] = state[ti].pressure[step] / state[ti].weight[step];
+ calibresults[ti].pos[step] = state[ti].pos[step] / state[ti].weight[step];
+ if (step <= 4) {
+ errors &= ~(1 << si);
+ if (amax - amin < 1000) {
+ snprintf(helptext, sizeof(helptext), "!pad %d upper not conn\ncheck soldering", si + 1);
+ errors |= (1 << si);
+ }
+ else if (bmax - bmin < 1000) {
+ snprintf(helptext, sizeof(helptext), "!pad %d lower not conn\ncheck soldering", si + 1);
+ errors |= (1 << si);
+ }
+ else if (abs(calibresults[si].pos[step] - calibresults[si].pos[7]) < 300) {
+ snprintf(helptext, sizeof(helptext), "!pad %d shorted?\ncheck soldering", si + 1);
+ errors |= (1 << si);
+ }
+ }
+ DebugLog("\n");
+ curstep[si]--;
+ }
+ else
+ readymask |= 1 << si; // flash the next finger if we want them to move on
+ }
+ if (step < 0)
+ done++;
+ prevrawpressure[si] = rawpressure;
+ }
+ if (done < 18)
+ done = 0;
+ int flash=triangle(millis());
+ for (int fi = 0; fi < 9; ++fi) {
+ int ready = readymask & (1 << fi);
+ bool err = (errors & (1 << fi));
+ for (int x = 0; x < 8; ++x) {
+ int k = 0;
+ if (x == curstep[fi])
+ k = ready ? flash : 255 - state[fi].weight[x] * 12.f;
+ if (err)
+ k = maxi(k, flash / 2);
+ led_ram[fi][x] = led_gamma(k);
+ }
+ }
+ } // calibration loop
+}