/ caliper-teensy31.ino
caliper-teensy31.ino
1 /* 2 USB HID driver for Harbor Freight 100mm Electronic Digital Caliper 3 Copyright © 2017 Jeff Epler 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #if defined(__MK20DX256__) && defined(TEENSYDUINO) && TEENSYDUINO == 140 20 // internet lore says that ARDUINO_BOARD_TEENSY31 should have been defined, but 21 // not for me (in arduino 1.8.5)! 22 #define TEENSY32 23 #endif 24 25 #if defined(TEENSY32) 26 // note: actually only works with Teensy 3.2, DAC wasn't exposed on 3.1 27 // but this case will be selected anyway :-/ 28 constexpr auto PIN_V1_5 = A14; 29 constexpr auto PIN_nACT = 12; 30 constexpr auto PIN_LED = 13; 31 constexpr auto DAC_SETTING_V1_5 = int(1.5 * 4096 / 3.3 + .5); 32 bool read_clk() { return !!(CMP0_SCR & CMP_SCR_COUT); } 33 bool read_data() { return !!(CMP1_SCR & CMP_SCR_COUT); } 34 void board_setup() { 35 // Set up the analog comparators to read the low-voltage signals. 36 // 37 // Unfortunately, analog comparators aren't standardized by the Arduino library, 38 // or by any third-party library I ran across, so we're going to go directly 39 // down to the register level for this. 40 SIM_SCGC4 |= SIM_SCGC4_CMP; // enable clock to comparator module 41 42 // CMP1 (data pin) 43 CMP1_CR0 = CMP_CR0_FILTER_CNT(1) | CMP_CR0_HYSTCTR(1); 44 CMP1_CR1 = CMP_CR1_INV | CMP_CR1_EN; 45 CMP1_MUXCR = CMP_MUXCR_PSEL(0) | CMP_MUXCR_MSEL(7); 46 // set DAC1 ref to code 14, since 3.3v * 14/64 = .67v 47 CMP1_DACCR = CMP_DACCR_DACEN | CMP_DACCR_VRSEL | CMP_DACCR_VOSEL(14); 48 49 // CMP0 (clock pin) 50 CMP0_CR0 = CMP_CR0_FILTER_CNT(1) | CMP_CR0_HYSTCTR(1); 51 CMP0_CR1 = CMP_CR1_INV | CMP_CR1_EN; 52 CMP0_MUXCR = CMP_MUXCR_PSEL(0) | CMP_MUXCR_MSEL(7); 53 // set DAC0 ref to code 14, since 3.3v * 14/64 = .67v 54 CMP0_DACCR = CMP_DACCR_DACEN | CMP_DACCR_VRSEL | CMP_DACCR_VOSEL(14); 55 // For other pin assignment possibilities, see the reference manual 56 // https://cache.freescale.com/files/32bit/doc/ref_manual/K20P64M72SF1RM.pdf 57 // 3.7.2.1 (page 105) for internal connections to the analog comparator, 58 // and 10.3.1 (page 207ff) for external pin assignments. Remember that 59 // each signal needs to go to a different CMPx comparator (e.g., CMP0 and CMP1, 60 // CMP1 and CMP2, or CMP0 and CMP2) 61 } 62 void keyboard_write(const char *buf) { 63 Keyboard.write(buf); 64 } 65 #elif defined(ARDUINO_TRINKET_M0) 66 #warning "this is only aspirational support for now" 67 constexpr auto PIN_V1_5 = A0; 68 constexpr auto DAC_SETTING_V1_5 = int(1.5 * 4096 / 3.3 + .5); 69 constexpr auto PIN_nACT = 12; 70 // PIN_LED is predefined 71 bool read_clk() { return !(REG_AC_STATUSA & 1); } 72 bool read_data() { return !(REG_AC_STATUSA & 2); } 73 void board_setup() { 74 // enable APB clock to the analog comparator 75 REG_PM_APBCMASK |= PM_APBCMASK_AC; 76 77 // Enable GCLK0 to comparator digital section 78 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | 79 GCLK_CLKCTRL_GEN_GCLK0 | 80 GCLK_CLKCTRL_ID_AC_DIG; 81 82 // Enable GCLK0 to comparator analog section 83 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | 84 GCLK_CLKCTRL_GEN_GCLK0 | 85 GCLK_CLKCTRL_ID_AC_ANA; 86 87 // Enable AC 88 REG_AC_CTRLA |= AC_CTRLA_ENABLE; 89 // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Atmel-42181-SAM-D21_Datasheet.pdf page 21 90 91 // AC IN[2] is SAMD21E package pin 7 (clk pin), labeled "4" 92 REG_AC_COMPCTRL0 = 93 AC_COMPCTRL_FLEN_MAJ5 | 94 AC_COMPCTRL_HYST | 95 AC_COMPCTRL_MUXPOS(2) | 96 AC_COMPCTRL_MUXNEG(5) | 97 AC_COMPCTRL_ENABLE; 98 99 REG_AC_SCALER0 = 14; 100 // AC IN[3] is SAMD21E package pin 8 (data pin), labeled "3" 101 REG_AC_COMPCTRL1 = 102 AC_COMPCTRL_FLEN_MAJ5 | 103 AC_COMPCTRL_HYST | 104 AC_COMPCTRL_MUXPOS(3) | 105 AC_COMPCTRL_MUXNEG(5) | 106 AC_COMPCTRL_ENABLE; 107 108 REG_AC_SCALER1 = 14; 109 } 110 void keyboard_write(const char *buf) {} 111 #else 112 #error "need to define a board type macro (or your board is not supported)" 113 #endif 114 115 char buf[81]; 116 117 static_assert(sizeof(1ull) == 8, "unsigned long long is 64 bits"); 118 119 void setup() { 120 Serial.begin(9600); 121 analogWriteResolution(12); 122 pinMode(PIN_nACT, INPUT_PULLUP); 123 pinMode(PIN_LED, OUTPUT); 124 analogWrite(PIN_V1_5, DAC_SETTING_V1_5); 125 snprintf(buf, sizeof(buf), "no data"); 126 board_setup(); 127 128 } 129 130 // (I don't use an enum because of a deficiency in arduino's handling of enums: https://playground.arduino.cc/Code/Enum) 131 #define STATE_WAIT_IDLE (0) 132 #define STATE_WAIT_LOW (1) 133 #define STATE_WAIT_HIGH (2) 134 135 uint8_t state, bitno; 136 bool clk, data, stable; 137 uint16_t time_idle; 138 uint64_t reading, old_reading; 139 140 uint8_t wait_idle() { 141 if (!clk) 142 time_idle ++; 143 else 144 time_idle = 0; 145 if (time_idle > 300) { 146 reading = 0; 147 bitno = 0; 148 time_idle = 0; 149 return STATE_WAIT_HIGH; 150 } 151 return STATE_WAIT_IDLE; 152 } 153 154 uint8_t wait_high() { 155 if (!clk) return STATE_WAIT_HIGH; 156 return STATE_WAIT_LOW; 157 } 158 159 160 uint8_t wait_low() { 161 if (clk) return STATE_WAIT_LOW; 162 if (!data) reading = reading | (1ull << bitno); // (data << bitno); 163 164 bitno ++; 165 if (bitno == 48) { 166 stable = (reading == old_reading); 167 old_reading = reading; 168 auto position = (reading & 0xfffffull); 169 auto signbit = bool(reading & 0x100000ull); 170 auto inch = bool(reading & (1ull << 47)); 171 172 digitalWrite(PIN_LED, stable); 173 174 if (inch) { 175 snprintf(buf, sizeof(buf), " %s%d.%03d%c", 176 signbit ? "-" : "", 177 (int)(position / 2000), 178 (int)(position / 2) % 1000, 179 position % 2 ? '5' : '0'); 180 } else { 181 snprintf(buf, sizeof(buf), " %s%d.%02d", 182 signbit ? "-" : "", 183 (int)(position / 100), 184 (int)(position % 100)); 185 } 186 Serial.write(buf); 187 Serial.write("\n"); 188 189 return STATE_WAIT_IDLE; 190 } 191 return STATE_WAIT_HIGH; 192 } 193 194 bool old_act; 195 196 void loop() { 197 bool act = !digitalRead(PIN_nACT); 198 if (act && !old_act && stable) { 199 keyboard_write(buf); 200 *buf = 0; 201 old_reading = ~0; 202 stable = false; // this ends up acting like a debounce 203 digitalWrite(PIN_LED, false); 204 } 205 old_act = act; 206 clk = read_clk(); 207 data = read_data(); 208 209 switch (state) { 210 case STATE_WAIT_IDLE: state = wait_idle(); break; 211 case STATE_WAIT_LOW: state = wait_low(); break; 212 case STATE_WAIT_HIGH: state = wait_high(); break; 213 } 214 }