ADCライブラリを作ってみたのはいいが内部温度センサーを読み取りしようとしたら正しく読めない。データシートのADCの章を何度見直してもわからない。で、なんとなくデータシート全体を”ADC”で検索したところ、SYSCTRL->VREF.bit.TSENという温度センサー用に追加されたと思われる制御ビットがあることをたまたま発見。それを設定してみたら正しく読み取れるようになったのだが...簡単だと思っていたのにしょうもないところで時間を費やしてしまった。
【概要】
1 |
void begin(ADCONVERTER_PRESCALER ps, bool leftadj = false, bool runstdby = false) |
プリスケーラ(ps)を指定してライブラリを開始する。
1 |
void end(void) |
ライブラリを終了する。
1 |
void correction(bool enable, uint16_t gain = 0, uint16_t offset = 0) |
ADCの補正値を設定する。
1 |
void samplingTime(uint8_t cycles) |
サンプリング時間(増分クロック数)を設定する。←何のためにあるのかわからん。
1 |
void reference(ADCONVERTER_REFSEL refsel, bool refcomp = false) |
リファレンス電圧を設定する。
1 |
void resolution(ADCONVERTER_RESSEL res, ADCONVERTER_SAMPLENUM samples = ADCONVERTER_SAMPLENUM_1) |
分解能を設定する。8/10/12-bitと、average mode (12-bit, N samples) 及び oversampling mode (13/14/15/16-bit)が設定できる。
1 |
void resolution(void) |
分解能(ビット数)を取得する。
1 |
void input(ADCONVERTER_MUXPOS pos, ADCONVERTER_MUXNEG neg = ADCONVERTER_MUXNEG_IOGND, ADCONVERTER_GAIN gain = ADCONVERTER_GAIN_1X, uint8_t scanLen = 0, uint8_t scanOff = 0) |
アナログ入力を設定する。マイナス入力(neg)がADCONVERTER_MUXNEG_GND or ADCONVERTER_MUXNEG_IOGNDであれば単極入力、それ以外は差動入力に設定する。この呼出し後、diffMode(bool enable)で差動入力モードを変更可能。
1 |
uint8_t gain(void) |
ゲインを取得する。
1 |
void diffMode(bool enable) |
差動入力モードを設定する。
1 |
bool diffMode(void) |
差動入力モードを取得する。
1 |
void window(ADCONVERTER_WINMODE mode, uint16_t lower, uint16_t upper) |
ウインドウを設定する。
1 |
EVENTSYS_USER eventInput(ADCONVERTER_EVACT evact, bool enable) |
イベント入力を設定する。
1 |
EVENTSYS_GEN eventOutput(ADCONVERTER_EVOSEL evosel, bool enable) |
イベント出力を設定する。
1 |
void setCallback(ADCONVERTER_CALLBACK cb) |
割り込みコールバック関数を設定する。
1 |
void debugControl(bool dgbrun) |
デバッグ設定を行う。
1 |
void start(bool freerun = false) |
変換を開始する。
1 |
void stop() |
変換を停止する。
1 |
void flush(void) |
変換中の結果を捨てる。
1 |
bool running(void) |
動作状態を取得する。
1 |
ADCONVERTER_INT intflag(bool clear = false) |
割り込みフラグを取得する。ビットの組み合わせを返すことに注意。
1 |
uint16_t result(bool readSync = true, uint8_t *scanoff = 0) |
ADC変換結果を取得する。
1 |
int integer(uint16_t adc) |
ADC変換結果を符号付整数に変換する。
1 |
int voltage(uint16_t adc, int vref = 0) |
ADC変換結果を電圧(uV)に変換する。外部リファレンス指定ではvrefにその電圧(uV)を指定する。
1 |
int divideGain(int value) |
データ(value)をゲインで割る。
1 |
float temperature(uint16_t adc) |
ADC変換結果を温度(内部温度センサー用)に変換する。
【サンプル・スケッチ】
ATSAMD21G18の内部温度センサーを読み取るプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "adconverter.h" void setup() { ADConverter::begin(ADCONVERTER_PRESCALER_TEMP); ADConverter::input(ADCONVERTER_MUXPOS_TEMP); ADConverter::start(true); } void loop() { float temp = ADConverter::temperature(ADConverter::result(true)); Serial.print("temperature = "); Serial.print(temp); Serial.println(" C"); delay(1000); } |
【修正履歴】
2020-06-15
voltage()の戻り値をゲイン補正した値に変更。
1 2 3 4 5 6 7 |
1V-Ref/12-bitでの精度 x1 ... 244.2uV x2 ... 122.1uV x4 ... 61.05uV x8 ... 30.53uV x16 ... 15.27uV 1/2 ... 488.4uV |
2020-06-13
integer()/voltage()/divideGain()を追加。
2020-06-12
リファレンス変更後の初回変換結果は使用できないとデータシートに記載されていたのでその対応とbegin()/correction()/result()/intflag()の仕様変更、及び、stop()/running()/resolution()/diffMode()を追加。
あと、result(true)を間違った使い方をしても永久ループらないよう改良。それと、AREFピンの設定を忘れてたので追加。
【ライブラリ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
/* adconverter.h - ADC Library for Microchip ATSAMD21 (Cortex®-M0+) Copyright (c) 2020 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __ADCONVERTER_H #define __ADCONVERTER_H #include <stdint.h> #include <stdbool.h> #include <sam.h> #include "gclock.h" #include "eventsys.h" #include "IOBUS.h" // // Port Define for Seeeduino XIAO // #ifdef SEEED_XIAO_M0 #define ADCONVERTER_MUXPOS_PIN_A0 ADCONVERTER_MUXPOS_PIN0 #define ADCONVERTER_MUXPOS_PIN_A1 ADCONVERTER_MUXPOS_PIN4 #define ADCONVERTER_MUXPOS_PIN_A2 ADCONVERTER_MUXPOS_PIN18 #define ADCONVERTER_MUXPOS_PIN_A3 ADCONVERTER_MUXPOS_PIN19 #define ADCONVERTER_MUXPOS_PIN_A4 ADCONVERTER_MUXPOS_PIN16 #define ADCONVERTER_MUXPOS_PIN_A5 ADCONVERTER_MUXPOS_PIN17 #define ADCONVERTER_MUXPOS_PIN_A6 ADCONVERTER_MUXPOS_PIN2 #define ADCONVERTER_MUXPOS_PIN_A7 ADCONVERTER_MUXPOS_PIN3 #define ADCONVERTER_MUXPOS_PIN_A8 ADCONVERTER_MUXPOS_PIN7 #define ADCONVERTER_MUXPOS_PIN_A9 ADCONVERTER_MUXPOS_PIN5 #define ADCONVERTER_MUXPOS_PIN_A10 ADCONVERTER_MUXPOS_PIN6 #define ADCONVERTER_MUXNEG_PIN_A0 ADCONVERTER_MUXNEG_PIN0 #define ADCONVERTER_MUXNEG_PIN_A1 ADCONVERTER_MUXNEG_PIN4 #define ADCONVERTER_MUXNEG_PIN_A6 ADCONVERTER_MUXNEG_PIN2 #define ADCONVERTER_MUXNEG_PIN_A7 ADCONVERTER_MUXNEG_PIN3 #define ADCONVERTER_MUXNEG_PIN_A8 ADCONVERTER_MUXNEG_PIN7 #define ADCONVERTER_MUXNEG_PIN_A9 ADCONVERTER_MUXNEG_PIN5 #define ADCONVERTER_MUXNEG_PIN_A10 ADCONVERTER_MUXNEG_PIN6 #define ADCONVERTER_REFSEL_PIN_A1 ADCONVERTER_REFSEL_AREFB #endif #define ADCONVERTER_PIN_AIN0 IOBUS_PIN('A', 2) #define ADCONVERTER_PIN_AIN1 IOBUS_PIN('A', 3) #define ADCONVERTER_PIN_AIN2 IOBUS_PIN('B', 8) #define ADCONVERTER_PIN_AIN3 IOBUS_PIN('B', 9) #define ADCONVERTER_PIN_AIN4 IOBUS_PIN('A', 4) #define ADCONVERTER_PIN_AIN5 IOBUS_PIN('A', 5) #define ADCONVERTER_PIN_AIN6 IOBUS_PIN('A', 6) #define ADCONVERTER_PIN_AIN7 IOBUS_PIN('A', 7) #define ADCONVERTER_PIN_AIN8 IOBUS_PIN('B', 0) #define ADCONVERTER_PIN_AIN9 IOBUS_PIN('B', 1) #define ADCONVERTER_PIN_AIN10 IOBUS_PIN('B', 2) #define ADCONVERTER_PIN_AIN11 IOBUS_PIN('B', 3) #define ADCONVERTER_PIN_AIN12 IOBUS_PIN('B', 4) #define ADCONVERTER_PIN_AIN13 IOBUS_PIN('B', 5) #define ADCONVERTER_PIN_AIN14 IOBUS_PIN('B', 6) // L excluded #define ADCONVERTER_PIN_AIN15 IOBUS_PIN('B', 7) // L excluded #define ADCONVERTER_PIN_AIN16 IOBUS_PIN('A', 8) #define ADCONVERTER_PIN_AIN17 IOBUS_PIN('A', 9) #define ADCONVERTER_PIN_AIN18 IOBUS_PIN('A', 10) #define ADCONVERTER_PIN_AIN19 IOBUS_PIN('A', 11) #define ADCONVERTER_PIN_AREFA ADCONVERTER_PIN_AIN1 #define ADCONVERTER_PIN_AREFB ADCONVERTER_PIN_AIN4 typedef enum { ADCONVERTER_PRESCALER_DIV4 = ADC_CTRLB_PRESCALER_DIV4_Val, // Peripheral clock divided by 4 ADCONVERTER_PRESCALER_DIV8 = ADC_CTRLB_PRESCALER_DIV8_Val, // Peripheral clock divided by 8 ADCONVERTER_PRESCALER_DIV16 = ADC_CTRLB_PRESCALER_DIV16_Val, // Peripheral clock divided by 16 ADCONVERTER_PRESCALER_DIV32 = ADC_CTRLB_PRESCALER_DIV32_Val, // Peripheral clock divided by 32 ADCONVERTER_PRESCALER_DIV64 = ADC_CTRLB_PRESCALER_DIV64_Val, // Peripheral clock divided by 64 ADCONVERTER_PRESCALER_DIV128 = ADC_CTRLB_PRESCALER_DIV128_Val, // Peripheral clock divided by 128 ADCONVERTER_PRESCALER_DIV256 = ADC_CTRLB_PRESCALER_DIV256_Val, // Peripheral clock divided by 256 ADCONVERTER_PRESCALER_DIV512 = ADC_CTRLB_PRESCALER_DIV512_Val, // Peripheral clock divided by 512 } ADCONVERTER_PRESCALER; #define ADCONVERTER_PRESCALER_TEMP ADCONVERTER_PRESCALER_DIV64 // prescaler for tempereture typedef enum { ADCONVERTER_REFSEL_INT1V = ADC_REFCTRL_REFSEL_INT1V_Val, // 1.0V voltage reference ADCONVERTER_REFSEL_INTVCC0 = ADC_REFCTRL_REFSEL_INTVCC0_Val, // 1/1.48 VDDANA [3.3/1.48=2.23V] ADCONVERTER_REFSEL_INTVCC1 = ADC_REFCTRL_REFSEL_INTVCC1_Val, // 1/2 VDDANA (only for VDDANA > 2.0V) [3.3/2 =1.65V] ADCONVERTER_REFSEL_AREFA = ADC_REFCTRL_REFSEL_AREFA_Val, // External reference VREFA (PA03) ADCONVERTER_REFSEL_AREFB = ADC_REFCTRL_REFSEL_AREFB_Val, // External reference VREFB (PA04) } ADCONVERTER_REFSEL; typedef enum { ADCONVERTER_RESSEL_12BIT = ADC_CTRLB_RESSEL_12BIT_Val, // 12-bit result ADCONVERTER_RESSEL_12BIT_N = ADC_CTRLB_RESSEL_16BIT_Val, // 12-bit result (Averaging mode. N Samples) ADCONVERTER_RESSEL_10BIT = ADC_CTRLB_RESSEL_10BIT_Val, // 10-bit result ADCONVERTER_RESSEL_8BIT = ADC_CTRLB_RESSEL_8BIT_Val, // 8-bit result ADCONVERTER_RESSEL_13BIT = ADC_CTRLB_RESSEL_8BIT_Val + 1, // 13-bit result (Oversampling mode. 4 Samples) ADCONVERTER_RESSEL_14BIT = ADC_CTRLB_RESSEL_8BIT_Val + 2, // 14-bit result (Oversampling mode. 16 Samples) ADCONVERTER_RESSEL_15BIT = ADC_CTRLB_RESSEL_8BIT_Val + 3, // 15-bit result (Oversampling mode. 64 Samples) ADCONVERTER_RESSEL_16BIT = ADC_CTRLB_RESSEL_8BIT_Val + 4, // 16-bit result (Oversampling mode. 256 Samples) } ADCONVERTER_RESSEL; typedef enum { ADCONVERTER_MUXPOS_PIN0 = ADC_INPUTCTRL_MUXPOS_PIN0_Val, // ADC AIN0 Pin ADCONVERTER_MUXPOS_PIN1 = ADC_INPUTCTRL_MUXPOS_PIN1_Val, // ADC AIN1 Pin ADCONVERTER_MUXPOS_PIN2 = ADC_INPUTCTRL_MUXPOS_PIN2_Val, // ADC AIN2 Pin ADCONVERTER_MUXPOS_PIN3 = ADC_INPUTCTRL_MUXPOS_PIN3_Val, // ADC AIN3 Pin ADCONVERTER_MUXPOS_PIN4 = ADC_INPUTCTRL_MUXPOS_PIN4_Val, // ADC AIN4 Pin ADCONVERTER_MUXPOS_PIN5 = ADC_INPUTCTRL_MUXPOS_PIN5_Val, // ADC AIN5 Pin ADCONVERTER_MUXPOS_PIN6 = ADC_INPUTCTRL_MUXPOS_PIN6_Val, // ADC AIN6 Pin ADCONVERTER_MUXPOS_PIN7 = ADC_INPUTCTRL_MUXPOS_PIN7_Val, // ADC AIN7 Pin ADCONVERTER_MUXPOS_PIN8 = ADC_INPUTCTRL_MUXPOS_PIN8_Val, // ADC AIN8 Pin ADCONVERTER_MUXPOS_PIN9 = ADC_INPUTCTRL_MUXPOS_PIN9_Val, // ADC AIN9 Pin ADCONVERTER_MUXPOS_PIN10 = ADC_INPUTCTRL_MUXPOS_PIN10_Val, // ADC AIN10 Pin ADCONVERTER_MUXPOS_PIN11 = ADC_INPUTCTRL_MUXPOS_PIN11_Val, // ADC AIN11 Pin ADCONVERTER_MUXPOS_PIN12 = ADC_INPUTCTRL_MUXPOS_PIN12_Val, // ADC AIN12 Pin ADCONVERTER_MUXPOS_PIN13 = ADC_INPUTCTRL_MUXPOS_PIN13_Val, // ADC AIN13 Pin ADCONVERTER_MUXPOS_PIN14 = ADC_INPUTCTRL_MUXPOS_PIN14_Val, // ADC AIN14 Pin ADCONVERTER_MUXPOS_PIN15 = ADC_INPUTCTRL_MUXPOS_PIN15_Val, // ADC AIN15 Pin ADCONVERTER_MUXPOS_PIN16 = ADC_INPUTCTRL_MUXPOS_PIN16_Val, // ADC AIN16 Pin ADCONVERTER_MUXPOS_PIN17 = ADC_INPUTCTRL_MUXPOS_PIN17_Val, // ADC AIN17 Pin ADCONVERTER_MUXPOS_PIN18 = ADC_INPUTCTRL_MUXPOS_PIN18_Val, // ADC AIN18 Pin ADCONVERTER_MUXPOS_PIN19 = ADC_INPUTCTRL_MUXPOS_PIN19_Val, // ADC AIN19 Pin/ ADCONVERTER_MUXPOS_TEMP = ADC_INPUTCTRL_MUXPOS_TEMP_Val, // Temperature Reference ADCONVERTER_MUXPOS_BANDGAP = ADC_INPUTCTRL_MUXPOS_BANDGAP_Val, // Bandgap Voltage [1.1V] ADCONVERTER_MUXPOS_SCALEDCOREVCC = ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC_Val, // 1/4 Scaled Core Supply [1.2/4=0.3V] ADCONVERTER_MUXPOS_SCALEDIOVCC = ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC_Val, // 1/4 Scaled I/O Supply [3.3/4=0.825V] ADCONVERTER_MUXPOS_DAC = ADC_INPUTCTRL_MUXPOS_DAC_Val, // DAC Output } ADCONVERTER_MUXPOS; typedef enum { ADCONVERTER_MUXNEG_PIN0 = ADC_INPUTCTRL_MUXNEG_PIN0_Val, // ADC AIN0 Pin ADCONVERTER_MUXNEG_PIN1 = ADC_INPUTCTRL_MUXNEG_PIN1_Val, // ADC AIN1 Pin ADCONVERTER_MUXNEG_PIN2 = ADC_INPUTCTRL_MUXNEG_PIN2_Val, // ADC AIN2 Pin ADCONVERTER_MUXNEG_PIN3 = ADC_INPUTCTRL_MUXNEG_PIN3_Val, // ADC AIN3 Pin ADCONVERTER_MUXNEG_PIN4 = ADC_INPUTCTRL_MUXNEG_PIN4_Val, // ADC AIN4 Pin ADCONVERTER_MUXNEG_PIN5 = ADC_INPUTCTRL_MUXNEG_PIN5_Val, // ADC AIN5 Pin ADCONVERTER_MUXNEG_PIN6 = ADC_INPUTCTRL_MUXNEG_PIN6_Val, // ADC AIN6 Pin ADCONVERTER_MUXNEG_PIN7 = ADC_INPUTCTRL_MUXNEG_PIN7_Val, // ADC AIN7 Pin ADCONVERTER_MUXNEG_GND = ADC_INPUTCTRL_MUXNEG_GND_Val, // Internal Ground ADCONVERTER_MUXNEG_IOGND = ADC_INPUTCTRL_MUXNEG_IOGND_Val, // I/O Ground } ADCONVERTER_MUXNEG; typedef enum { ADCONVERTER_GAIN_1X = ADC_INPUTCTRL_GAIN_1X_Val, // 1x ADCONVERTER_GAIN_2X = ADC_INPUTCTRL_GAIN_2X_Val, // 2x ADCONVERTER_GAIN_4X = ADC_INPUTCTRL_GAIN_4X_Val, // 4x ADCONVERTER_GAIN_8X = ADC_INPUTCTRL_GAIN_8X_Val, // 8x ADCONVERTER_GAIN_16X = ADC_INPUTCTRL_GAIN_16X_Val, // 16x ADCONVERTER_GAIN_DIV2 = ADC_INPUTCTRL_GAIN_DIV2_Val, // 1/2x } ADCONVERTER_GAIN; typedef enum { ADCONVERTER_SAMPLENUM_1 = ADC_AVGCTRL_SAMPLENUM_1_Val, // 1 sample ADCONVERTER_SAMPLENUM_2 = ADC_AVGCTRL_SAMPLENUM_2_Val, // 2 samples ADCONVERTER_SAMPLENUM_4 = ADC_AVGCTRL_SAMPLENUM_4_Val, // 4 samples ADCONVERTER_SAMPLENUM_8 = ADC_AVGCTRL_SAMPLENUM_8_Val, // 8 samples ADCONVERTER_SAMPLENUM_16 = ADC_AVGCTRL_SAMPLENUM_16_Val, // 16 samples ADCONVERTER_SAMPLENUM_32 = ADC_AVGCTRL_SAMPLENUM_32_Val, // 32 samples ADCONVERTER_SAMPLENUM_64 = ADC_AVGCTRL_SAMPLENUM_64_Val, // 64 samples ADCONVERTER_SAMPLENUM_128 = ADC_AVGCTRL_SAMPLENUM_128_Val, // 128 samples ADCONVERTER_SAMPLENUM_256 = ADC_AVGCTRL_SAMPLENUM_256_Val, // 256 samples ADCONVERTER_SAMPLENUM_512 = ADC_AVGCTRL_SAMPLENUM_512_Val, // 512 samples ADCONVERTER_SAMPLENUM_1024 = ADC_AVGCTRL_SAMPLENUM_1024_Val, // 1024 samples } ADCONVERTER_SAMPLENUM; typedef enum { ADCONVERTER_WINMODE_DISABLE = ADC_WINCTRL_WINMODE_DISABLE_Val, // No window mode (default) ADCONVERTER_WINMODE_MODE1 = ADC_WINCTRL_WINMODE_MODE1_Val, // Mode 1: RESULT > WINLT ADCONVERTER_WINMODE_MODE2 = ADC_WINCTRL_WINMODE_MODE2_Val, // Mode 2: RESULT < WINUT ADCONVERTER_WINMODE_MODE3 = ADC_WINCTRL_WINMODE_MODE3_Val, // Mode 3: WINLT < RESULT < WINUT ADCONVERTER_WINMODE_MODE4 = ADC_WINCTRL_WINMODE_MODE4_Val, // Mode 4: !(WINLT < RESULT < WINUT) } ADCONVERTER_WINMODE; typedef enum { ADCONVERTER_EVACT_START = 0, // Start Conversion Event In ADCONVERTER_EVACT_SYNC = 1, // Synchronization Event In } ADCONVERTER_EVACT; typedef enum { ADCONVERTER_EVOSEL_RESRDY = 0, // Result Ready Event Out ADCONVERTER_EVOSEL_WINMON = 1, // Window Monitor Event Out } ADCONVERTER_EVOSEL; typedef enum { ADCONVERTER_INT_RESRDY = ADC_INTFLAG_RESRDY, ADCONVERTER_INT_OVERRUN = ADC_INTFLAG_OVERRUN, ADCONVERTER_INT_WINMON = ADC_INTFLAG_WINMON, } ADCONVERTER_INT; typedef void (*ADCONVERTER_CALLBACK)(ADCONVERTER_INT); class ADConverter { private: static void discard(void); static void sync(void) { while (ADC->CTRLA.bit.SWRST || ADC->STATUS.bit.SYNCBUSY) continue; } static void controlGCLK(bool enable) { GClock::control(GCLOCK_ID_ADC, enable); } static void multiplexing_ain(bool enable) { static const uint32_t ADC_PINS[] = { ADCONVERTER_PIN_AIN0, ADCONVERTER_PIN_AIN1, ADCONVERTER_PIN_AIN2, ADCONVERTER_PIN_AIN3, ADCONVERTER_PIN_AIN4, ADCONVERTER_PIN_AIN5, ADCONVERTER_PIN_AIN6, ADCONVERTER_PIN_AIN7, ADCONVERTER_PIN_AIN8, ADCONVERTER_PIN_AIN9, ADCONVERTER_PIN_AIN10, ADCONVERTER_PIN_AIN11, ADCONVERTER_PIN_AIN12, ADCONVERTER_PIN_AIN13, ADCONVERTER_PIN_AIN14, ADCONVERTER_PIN_AIN15, ADCONVERTER_PIN_AIN16, ADCONVERTER_PIN_AIN17, ADCONVERTER_PIN_AIN18, ADCONVERTER_PIN_AIN19, }; static bool _adcpin = false; if (_adcpin == enable) return; _adcpin = enable; IOBUS_PMUX pmux = enable ? IOBUS_PMUX_B : IOBUS_PMUX_DISABLE; uint32_t pin = ADC->INPUTCTRL.bit.INPUTOFFSET; uint32_t len = ADC->INPUTCTRL.bit.INPUTSCAN; if (len == 0) { len = 1; pin = 0; } pin += ADC->INPUTCTRL.bit.MUXPOS; while (len--) { if (pin > ADCONVERTER_MUXPOS_PIN19) break; IOBUS::multiplexing(ADC_PINS[pin++], pmux); } pin = ADC->INPUTCTRL.bit.MUXNEG; if (pin <= ADCONVERTER_MUXNEG_PIN7) IOBUS::multiplexing(ADC_PINS[pin], pmux); } static void multiplexing_ref(bool enable) { IOBUS_PMUX pmux = enable ? IOBUS_PMUX_B : IOBUS_PMUX_DISABLE; switch (ADC->REFCTRL.bit.REFSEL) { case ADCONVERTER_REFSEL_AREFA: IOBUS::multiplexing(ADCONVERTER_PIN_AREFA, pmux); break; case ADCONVERTER_REFSEL_AREFB: IOBUS::multiplexing(ADCONVERTER_PIN_AREFB, pmux); break; } } static void reset(void) { // disable ADC->CTRLA.reg = 0; sync(); // software reset ADC->CTRLA.reg = ADC_CTRLA_SWRST; sync(); // [datasheet] 10.3.2 NVM Software Calibration Area Mapping // ADC Bias Calibration uint32_t bias = (*((uint32_t *)ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos; // ADC Linearity bits 4:0 uint32_t linearity = (*((uint32_t *)ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos; // ADC Linearity bits 7:5 linearity |= ((*((uint32_t *)ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5; ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity); // discard of first result discard(); } public: static void begin(ADCONVERTER_PRESCALER ps, bool leftadj = false, bool runstdby = false) { controlGCLK(true); reset(); ADC->CTRLA.reg = ADC_CTRLA_ENABLE | (runstdby ? ADC_CTRLA_RUNSTDBY : 0); sync(); ADC->CTRLB.reg = ADC_CTRLB_PRESCALER(ps) | (leftadj ? ADC_CTRLB_LEFTADJ : 0); sync(); ADC->INTENSET.reg = ADC_INTENSET_RESRDY | ADC_INTENSET_WINMON | ADC_INTENSET_OVERRUN; NVIC_EnableIRQ(ADC_IRQn); NVIC_SetPriority(ADC_IRQn, 0); } static void end(void) { if (ADC->CTRLA.bit.ENABLE) { multiplexing_ref(false); multiplexing_ain(false); NVIC_DisableIRQ(ADC_IRQn); reset(); controlGCLK(false); SYSCTRL->VREF.bit.TSEN = 0; // disable temperature sensor SYSCTRL->VREF.bit.BGOUTEN = 0; // disable bandgap output } } static void correction(bool enable, uint16_t gain = 0, uint16_t offset = 0) { ADC->GAINCORR.reg = ADC_GAINCORR_GAINCORR(gain); ADC->OFFSETCORR.reg = ADC_OFFSETCORR_OFFSETCORR(offset); ADC->CTRLB.bit.CORREN = enable; } static void samplingTime(uint8_t cycles) { ADC->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(cycles); } static void reference(ADCONVERTER_REFSEL refsel, bool refcomp = false) { // [datasheet] 33.6.2.1 Initialization if (ADC->REFCTRL.bit.REFSEL != refsel) discard(); multiplexing_ref(false); ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL(refsel) | (refcomp ? ADC_REFCTRL_REFCOMP : 0); multiplexing_ref(true); } static void resolution(ADCONVERTER_RESSEL res, ADCONVERTER_SAMPLENUM samples = ADCONVERTER_SAMPLENUM_1) { switch (res) { case ADCONVERTER_RESSEL_12BIT_N: ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM(samples) | ADC_AVGCTRL_ADJRES(samples >= 4 ? 4 : samples); break; case ADCONVERTER_RESSEL_13BIT: ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM(ADCONVERTER_SAMPLENUM_4) | ADC_AVGCTRL_ADJRES(1); res = ADCONVERTER_RESSEL_12BIT_N; break; case ADCONVERTER_RESSEL_14BIT: ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM(ADCONVERTER_SAMPLENUM_16) | ADC_AVGCTRL_ADJRES(2); res = ADCONVERTER_RESSEL_12BIT_N; break; case ADCONVERTER_RESSEL_15BIT: ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM(ADCONVERTER_SAMPLENUM_64) | ADC_AVGCTRL_ADJRES(1); res = ADCONVERTER_RESSEL_12BIT_N; break; case ADCONVERTER_RESSEL_16BIT: ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM(ADCONVERTER_SAMPLENUM_256) | ADC_AVGCTRL_ADJRES(0); res = ADCONVERTER_RESSEL_12BIT_N; break; default: ADC->AVGCTRL.reg = 0; break; } ADC->CTRLB.bit.RESSEL = res; sync(); } static uint8_t resolution(void) { switch (ADC->CTRLB.bit.RESSEL) { case ADC_CTRLB_RESSEL_8BIT_Val: return 8; case ADC_CTRLB_RESSEL_10BIT_Val: return 10; case ADC_CTRLB_RESSEL_12BIT_Val: return 12; case ADC_CTRLB_RESSEL_16BIT_Val: uint8_t samples = ADC->AVGCTRL.bit.SAMPLENUM; if ((samples >= ADC_AVGCTRL_SAMPLENUM_4_Val) && (ADC->AVGCTRL.bit.ADJRES < (samples >= 4 ? 4 : samples))) return ((samples - ADC_AVGCTRL_SAMPLENUM_4_Val) >> 1) + 13; return 12; } return 0; } static void input(ADCONVERTER_MUXPOS pos, ADCONVERTER_MUXNEG neg = ADCONVERTER_MUXNEG_IOGND, ADCONVERTER_GAIN gain = ADCONVERTER_GAIN_1X, uint8_t scanLen = 0, uint8_t scanOff = 0) { if (pos == ADCONVERTER_MUXPOS_TEMP) { // [datasheet] 37.11.8.2.1 Temperature Log Row resolution(ADCONVERTER_RESSEL_12BIT_N, ADCONVERTER_SAMPLENUM_4); reference(ADCONVERTER_REFSEL_INT1V); neg = ADCONVERTER_MUXNEG_GND; // SYSCTRL->VREF.bit.TSEN = 1; // enable temperature sensor SYSCTRL->VREF.bit.BGOUTEN = 0; // disable bandgap output } else if (pos == ADCONVERTER_MUXPOS_BANDGAP) { SYSCTRL->VREF.bit.TSEN = 0; // disable temperature sensor SYSCTRL->VREF.bit.BGOUTEN = 1; // enable bandgap output } else { SYSCTRL->VREF.bit.TSEN = 0; // disable temperature sensor SYSCTRL->VREF.bit.BGOUTEN = 0; // disable bandgap output } multiplexing_ain(false); ADC->INPUTCTRL.reg = ADC_INPUTCTRL_MUXPOS(pos) | ADC_INPUTCTRL_MUXNEG(neg) | ADC_INPUTCTRL_INPUTSCAN(scanLen) | ADC_INPUTCTRL_INPUTOFFSET(scanOff) | ADC_INPUTCTRL_GAIN(gain); sync(); multiplexing_ain(true); diffMode(neg < ADCONVERTER_MUXNEG_GND); } static uint8_t gain(void) { return ADC->INPUTCTRL.bit.GAIN; } static void diffMode(bool enable) { ADC->CTRLB.bit.DIFFMODE = enable; sync(); } static bool diffMode(void) { return ADC->CTRLB.bit.DIFFMODE; } static void window(ADCONVERTER_WINMODE mode, uint16_t lower, uint16_t upper) { ADC->WINCTRL.reg = ADC_WINCTRL_WINMODE(mode); sync(); ADC->WINLT.reg = lower; sync(); ADC->WINUT.reg = upper; sync(); } static EVENTSYS_USER eventInput(ADCONVERTER_EVACT evact, bool enable) { switch (evact) { case ADCONVERTER_EVACT_START: ADC->EVCTRL.bit.STARTEI = enable; return EVENTSYS_USER_ADC_START; case ADCONVERTER_EVACT_SYNC : ADC->EVCTRL.bit.SYNCEI = enable; return EVENTSYS_USER_ADC_SYNC; } return EVENTSYS_USER_NONE; } static EVENTSYS_GEN eventOutput(ADCONVERTER_EVOSEL evosel, bool enable) { switch (evosel) { case ADCONVERTER_EVOSEL_RESRDY: ADC->EVCTRL.bit.RESRDYEO = enable; return EVENTSYS_GEN_ADC_RESRDY; case ADCONVERTER_EVOSEL_WINMON: ADC->EVCTRL.bit.WINMONEO = enable; return EVENTSYS_GEN_ADC_WINMON; } return EVENTSYS_GEN_NONE; } static void setCallback(ADCONVERTER_CALLBACK cb); static void debugControl(bool dgbrun) { ADC->DBGCTRL.bit.DBGRUN = dgbrun; } static void start(bool freerun = false); static void stop(void); static void flush(void); static bool running(void); static ADCONVERTER_INT intflag(bool clear = false); static uint16_t result(bool readSync = true, uint8_t *scanoff = 0); static int integer(uint16_t adc) { if (ADC->AVGCTRL.bit.SAMPLENUM == 0) { uint8_t bit = resolution(); if (ADC->CTRLB.bit.LEFTADJ) adc >>= 16 - bit; if (diffMode()) { uint16_t msb = 1 << (bit - 1); if (adc & msb) return (int16_t)(~(msb - 1) | adc); } } return adc; } static int voltage(uint16_t adc, int vref = 0) { switch (ADC->REFCTRL.bit.REFSEL) { case ADCONVERTER_REFSEL_INT1V: vref = 1000000; break; // 1.00V case ADCONVERTER_REFSEL_INTVCC0: vref = 2230000; break; // 2.23V case ADCONVERTER_REFSEL_INTVCC1: vref = 1650000; break; // 1.65V } return divideGain((int64_t)vref * integer(adc) / ((1 << resolution()) - 1)); } static int divideGain(int value) { return gain() == ADCONVERTER_GAIN_DIV2 ? value << 1 : value >> gain(); } #define ADCONVERTER_TEMPERATURE(adc, int1v) (TEMPr + (((((adc) * (int1v) / 4095) - (ADCr * INT1Vr / 4095)) * TEMPd) / ((ADCh * INT1Vh / 4095) - (ADCr * INT1Vr / 4095)))) static float temperature(uint16_t adc) { static uint16_t ADCh , ADCr; static float INT1Vh, INT1Vr, INT1Vd; static float TEMPh , TEMPr , TEMPd; static bool init = false; // [datasheet] 37.11.8.2.1 Temperature Log Row // [datasheet] 37.11.8.2.2 Using Linear Interpolation if (!init) { init = true; // 12-bit ADC conversion at hot temperature ADCh = (*(uint32_t *)FUSES_HOT_ADC_VAL_ADDR & FUSES_HOT_ADC_VAL_Msk ) >> FUSES_HOT_ADC_VAL_Pos; ADCr = (*(uint32_t *)FUSES_ROOM_ADC_VAL_ADDR & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos; // 2's complement of the internal 1V reference drift at hot temperature (versus a 1.0 centered value) int8_t INT1Vh_VAL = (*(uint32_t *)FUSES_HOT_INT1V_VAL_ADDR & FUSES_HOT_INT1V_VAL_Msk ) >> FUSES_HOT_INT1V_VAL_Pos; int8_t INT1Vr_VAL = (*(uint32_t *)FUSES_ROOM_INT1V_VAL_ADDR & FUSES_ROOM_INT1V_VAL_Msk) >> FUSES_ROOM_INT1V_VAL_Pos; INT1Vh = 1 - (INT1Vh_VAL / 1000.0); INT1Vr = 1 - (INT1Vr_VAL / 1000.0); INT1Vd = INT1Vh - INT1Vr; // Decimal part of hot temperature int8_t TEMPh_DEC = (*(uint32_t *)FUSES_HOT_TEMP_VAL_DEC_ADDR & FUSES_HOT_TEMP_VAL_DEC_Msk ) >> FUSES_HOT_TEMP_VAL_DEC_Pos; int8_t TEMPr_DEC = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_DEC_ADDR & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos; // Integer part of hot temperature in oC int8_t TEMPh_INT = (*(uint32_t *)FUSES_HOT_TEMP_VAL_INT_ADDR & FUSES_HOT_TEMP_VAL_INT_Msk ) >> FUSES_HOT_TEMP_VAL_INT_Pos; int8_t TEMPr_INT = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_INT_ADDR & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos; TEMPh = TEMPh_INT + (TEMPh_DEC / 10.0); TEMPr = TEMPr_INT + (TEMPr_DEC / 10.0); TEMPd = TEMPh - TEMPr; } float ADCm = integer(adc); float TEMPc = ADCONVERTER_TEMPERATURE(ADCm, 1); float INT1Vm = (INT1Vr + ((INT1Vd * (TEMPc - TEMPr)) / TEMPd)); return ADCONVERTER_TEMPERATURE(ADCm, INT1Vm); } #undef ADCONVERTER_TEMPERATURE }; #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
/* adconverter.cpp - ADC Library for Microchip ATSAMD21 (Cortex®-M0+) Copyright (c) 2020 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "adconverter.h" #define IRQ_STATUS uint32_t _primask_ #define DISABLE_IRQ() do { _primask_ = __get_PRIMASK(); __disable_irq(); } while (0) #define ENABLE_IRQ() do { if ((_primask_ & 1) == 0) __enable_irq(); } while (0) static volatile bool _discard; static volatile bool _running; static volatile uint8_t _scaning; static volatile uint8_t _scanoff; static volatile uint8_t _intflag; static volatile uint16_t _result; static ADCONVERTER_CALLBACK _callback; void ADC_Handler(void) { uint8_t oldflag = _intflag; _intflag |= (ADC->INTFLAG.reg & (ADC_INTFLAG_RESRDY | ADC_INTFLAG_OVERRUN | ADC_INTFLAG_WINMON)); if (ADC->INTFLAG.bit.RESRDY) { ADC->INTFLAG.bit.RESRDY = 1; _result = ADC->RESULT.reg; _scanoff = _scaning; _scaning = _scaning + 1 < ADC->INPUTCTRL.bit.INPUTSCAN ? _scaning + 1 : 0; if (oldflag & ADC_INTFLAG_RESRDY) _intflag |= ADC_INTFLAG_OVERRUN; if (_discard) { if (!ADC->CTRLB.bit.FREERUN) ADC->SWTRIG.bit.START = 1; // once more _discard = false; _intflag &= ~ADC_INTFLAG_RESRDY; } else { if (!ADC->CTRLB.bit.FREERUN) _running = false; if (ADC->INTENSET.bit.RESRDY && _callback) _callback(ADCONVERTER_INT_RESRDY); } } if (ADC->INTFLAG.bit.OVERRUN) { ADC->INTFLAG.bit.OVERRUN = 1; _result = ADC->RESULT.reg; _scanoff = _scaning; _scaning = _scaning + 1 < ADC->INPUTCTRL.bit.INPUTSCAN ? _scaning + 1 : 0; if (ADC->INTENSET.bit.OVERRUN && _callback) _callback(ADCONVERTER_INT_OVERRUN); } if (ADC->INTFLAG.bit.WINMON) { ADC->INTFLAG.bit.WINMON = 1; if (ADC->INTENSET.bit.WINMON && _callback) _callback(ADCONVERTER_INT_WINMON); } } void ADConverter::setCallback(ADCONVERTER_CALLBACK cb) { _callback = cb; } void ADConverter::discard(void) { _discard = true; } void ADConverter::start(bool freerun) { IRQ_STATUS; if (!ADC->CTRLA.bit.ENABLE || running()) return; DISABLE_IRQ(); _running = true; _intflag = 0; _scaning = 0; if (freerun) ADC->CTRLB.bit.FREERUN = 1; else ADC->SWTRIG.bit.START = 1; sync(); ENABLE_IRQ(); } void ADConverter::stop(void) { IRQ_STATUS; DISABLE_IRQ(); _running = false; _intflag = 0; _scaning = 0; discard(); ADC->CTRLB.bit.FREERUN = 0; sync(); ADC->CTRLA.bit.ENABLE = 0; sync(); ADC->CTRLA.bit.ENABLE = 1; sync(); ENABLE_IRQ(); } void ADConverter::flush(void) { ADC->SWTRIG.bit.FLUSH = 1; sync(); } bool ADConverter::running(void) { return _running; } ADCONVERTER_INT ADConverter::intflag(bool clear) { IRQ_STATUS; DISABLE_IRQ(); uint8_t flag = _intflag; if (clear) _intflag &= (ADC_INTFLAG_OVERRUN | ADC_INTFLAG_RESRDY); ENABLE_IRQ(); return (ADCONVERTER_INT)flag; } uint16_t ADConverter::result(bool readSync, uint8_t *scanoff) { if (readSync) { while (running() && (((intflag(false) & ADCONVERTER_INT_RESRDY)) == 0)) continue; } IRQ_STATUS; DISABLE_IRQ(); uint16_t data = _result; _intflag &= ~(ADC_INTFLAG_OVERRUN | ADC_INTFLAG_RESRDY); if (scanoff) *scanoff = _scanoff; ENABLE_IRQ(); return data; } |
【参照ライブラリ】
Seeeduino XIAO (ATSAMD21G18) のGPIO速度 (IOBUS.h)
Seeeduino XIAO (ATSAMD21G18) のGCLKライブラリ (gclock.h/gclock.cpp)
Seeeduino XIAO (ATSAMD21G18) のEVSYSライブラリ (eventsys.h/eventsys.cpp)