Picoには12ビット4チャネルのA-Dコンバータが内蔵されています。ENOB(effective number of bits:有効ビット数)が9.5なので、もう少し高いビット数がほしいという想定で外部にICを追加します。
###ADS1115のおもなスペック
- ビット数 16
- チャネル数 4(シングルエンド)、2(差動)
- 基準電圧 内蔵
- 電源電圧 2.7~5.5V
- 入力 差動(A0-A1、A0-A3、A1-A3、A2-A3)、シングル(A0、A1、A2、A3とGND)
- 変換方式 ΔΣ型
- データ・レート 128SPS以下で16ビット
- インターフェース I2C
- I2C転送速度 最大3.4MHz
- スレーブ・アドレス デフォルトは0x48
- プログラマブル・ゲイン・アンプ フルスケール;±6.144V、±4.096V、±2.048V、±1.024V、±0.512V、±0.256V
###接続
ボード化されたADS1115は、Adafruit製の旧製品で、スイッチサイエンスで購入しました。
|ADS1115ボードの端子| Picoの端子(GPIO) |名称|
|------------|------------|:-------------:|:------|
|Vcc| 3.3V| 3.3V|
|GND| GND|GND|
|SCL| GP9| SCL|
|SDA| GP8| SDA|
|A0| -| -|
|A1| -| -|
|A2| -| -|
|A3| -| -|
プルアップ抵抗10kΩがSDA/SCLに入っていましたが取りました。
ADS1115の入力には、直流標準電圧電流発生器TR6142で電圧を入力しています。1.0000だと小数点第4位まで正確です。
###プログラム
フォルダ名はads1115としました。cmake makeの手順は省略します。17回以前を参考にしてください。プログラムは、ads1115.hとads1115.cに分けました。
add_executable(ads1115
ads1115.c
)
# Pull in our (to be renamed) simple get you started dependencies
target_link_libraries(ads1115 pico_stdlib hardware_i2c)
# create map/bin/hex file etc.
pico_add_extra_outputs(ads1115)
###設定レジスタ デフォルト b10001100
設定用ポインタ・レジスタのアドレスは0x01です。ads1115.hに記述しました。
- bit1 OS。'1'で変換開始。デフォルト1
- bit14、bi13、bit12 チャネル・セレクト。
-
- 000 : AINP = AIN0 and AINN = AIN1 (デフォルト)
-
- 001 : AINP = AIN0 and AINN = AIN3
-
- 010 : AINP = AIN1 and AINN = AIN3
-
- 011 : AINP = AIN2 and AINN = AIN3
-
- 100 : AINP = AIN0 and AINN = GND
-
- 101 : AINP = AIN1 and AINN = GND
-
- 110 : AINP = AIN2 and AINN = GND
-
- 111 : AINP = AIN3 and AINN = GND
- bit11、bit10、bit9 PGA。
-
- 000 : FSR = ±6.144 V
-
- 001 : FSR = ±4.096 V
-
- 010 : FSR = ±2.048 V (デフォルト)
-
- 011 : FSR = ±1.024 V
-
- 100 : FSR = ±0.512 V
-
- 101 : FSR = ±0.256 V
-
- 110 : FSR = ±0.256 V
-
- 111 : FSR = ±0.256 V
- bit8 MODE。1;連続、0;ワンショット(デフォルト)
- bit7、bit6、bit5 サンプル・レート。
-
- 000 : 8 SPS
-
- 001 : 16 SPS
-
- 010 : 32 SPS
-
- 011 : 64 SPS
-
- 100 : 128 SPS (デフォルト)
-
- 101 : 250 SPS
-
- 110 : 475 SPS
-
- 111 : 860 SPS
- bit4 COMP_MODE;コンパレータ。省略
- bit3 COMP_POL ;コンパレータ。省略
- bit2 COMP_LAT ;コンパレータ。省略
- bit1、bit0 COMP_QUE[1:0] ;コンパレータ。省略(デフォルト3h ディセーブル)
例;電源を入れた直後のデフォルト設定値。 変換スタート、差動A0-A1チャネル、フルスケール±2.048V、ワンショット、128SPS、コンパレータは無効;1000010110000011h
//ads1115 Config Register reset = 8583h
/// input
#define D1 0x0000 // Differential : AINP = AIN0 and AINN = AIN1 (default)
// #define MUX001 0x1000 // not use : AINP = AIN0 and AINN = AIN3
// #define MUX010 0x2000 // not use : AINP = AIN1 and AINN = AIN3
#define D2 0x3000 // Differential : AINP = AIN2 and AINN = AIN3
#define S1 0x4000 // Single-ended: AINP = AIN0 and AINN = GND
#define S2 0x5000 // Single-ended: AINP = AIN1 and AINN = GND
#define S3 0x6000 // Single-ended: AINP = AIN2 and AINN = GND
#define S4 0x7000 // Single-ended: AINP = AIN3 and AINN = GND
/// Gain
#define Gain0 0x0000 // 000 : FSR = ±6.144 V
#define Gain1 0x0200 // 001 : FSR = ±4.096 V
#define Gain2 0x0400 // 010 : FSR = ±2.048 V (default)
#define Gain4 0x0600 // 011 : FSR = ±1.024 V
#define Gain8 0x0800 // 100 : FSR = ±0.512 V
#define Gain16 0x0a00 // 101 110 111: FSR = ±0.256 V
/// Data Rate
#define DR8 0x0000 // 000 : 8 SPS
#define DR16 0x0020 // 001 : 16 SPS
#define DR32 0x0040 // 010 : 32 SPS
#define DR64 0x0060 // 011 : 64 SPS
#define DR128 0x0080 // 100 : 128 SPS (default)
#define DR250 0x00a0 // 101 : 250 SPS
#define DR475 0x00c0 // 110 : 475 SPS
#define DR860 0x00e0 // 111 : 860 SPS
###プログラムads1115.c
読み出すポインタ・レジスタのアドレスは0x00です。
読み出したデータは2バイトで、0から32767までのデータです。2の補数形式で、D15は符号を表します。
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
I2Cの転送速度は100kHzにしました。
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "ads1115.h"
#define I2C_PORT i2c0
static int addr = 0x48;
static uint16_t config = 0x8583;
#define configPointer 0x01
#define conversionPointer 0x00
#define READ_BIT 0x80
void setup_I2C(){
// This example will use I2C0 on GPIO8 (SDA) and GPIO9 (SCL) running at 100kHz.
i2c_init(I2C_PORT, 100 * 1000);
gpio_set_function(8, GPIO_FUNC_I2C);
gpio_set_function(9, GPIO_FUNC_I2C);
gpio_pull_up(8);
gpio_pull_up(9);
}
static void I2C_read_registers(uint8_t reg, uint8_t *buf, uint16_t len) {
// For this particular device, we send the device the register we want to read
// first, then subsequently read from the device. The register is auto incrementing
// so we don't need to keep sending the register we want, just the first.
reg = reg | READ_BIT;
i2c_write_blocking(I2C_PORT, addr, ®, 1, true);
i2c_read_blocking(I2C_PORT, addr, buf, len, false);
}
static void I2C_write_register16(uint8_t reg, uint16_t data) {
uint8_t buf[3];
buf[0] = reg;
buf[1] = (uint8_t)(data >> 8);
buf[2] = (uint8_t)(data & 0xff);
i2c_write_blocking(I2C_PORT, addr, buf, 3, true);
}
float readADC(uint16_t channel, uint16_t PGAgain, uint16_t DataRate){
config = config | channel | PGAgain | DataRate;
I2C_write_register16(configPointer, config);
uint8_t buffer[2];
I2C_read_registers(conversionPointer, buffer, 2);
uint16_t temp = (buffer[0]<<8) | buffer[1];
int16_t tempSign = -(temp & 0b1000000000000000) | (temp & 0b0111111111111111);
float V = tempSign / 32767.f;
return V;
}
int main() {
stdio_init_all();
printf("\nstart ADS1115\n");
setup_I2C();
printf("\n==S1=0x0200=DR8 input: %.5fV\n", readADC(S4, Gain1, DR8));
printf("\n==S1=0x0200=DR8 input: %.5fV\n", readADC(S4, Gain2, DR8));
printf("\n==S1=0x0200=DR8 input: %.5fV\n", readADC(S4, Gain4, DR8));
printf("\n==S1=0x0200=DR8 input: %.5fV\n", readADC(S4, Gain2, DR128));
printf("\n==S1=0x0200=DR8 input: %.5fV\n", readADC(S4, Gain4, DR128));
/*
printf("\n==G1=0x0200=DR8 input: %.5fV\n", readADC(D1, Gain1, DR8));
printf("-G1-DR16-- input: %.5fV\n", readADC(D1, Gain1, DR16));
printf("-G1-DR32-- input: %.5fV\n", readADC(D1, Gain1, DR32));
printf("i-G1-DR64--input: %.5fV\n", readADC(D1, Gain1, DR64));
printf("-G1-DR128--input: %.5fV\n", readADC(D1, Gain1, DR128));
printf("-G1-DR250--input: %.5fV\n", readADC(D1, Gain1, DR250));
printf("-G1-DR475--input: %.5fV\n", readADC(D1, Gain1, DR475));
printf("-G1-DR860--input: %.5fV\n", readADC(D1, Gain1, DR860));
printf("==G2=0x0400=DR8 input: %.5fV\n", readADC(D1, Gain2, DR8));
printf("-G2-DR16-- input: %.5fV\n", readADC(D1, Gain2, DR16));
printf("-G2-DR32-- input: %.5fV\n", readADC(D1, Gain2, DR32));
printf("-G2-DR64-- input: %.5fV\n", readADC(D1, Gain2, DR64));
printf("-G2-DR128--input: %.5fV\n", readADC(D1, Gain2, DR128));
printf("-G2-DR250--input: %.5fV\n", readADC(D1, Gain2, DR250));
printf("-G2-DR475--input: %.5fV\n", readADC(D1, Gain2, DR475));
printf("-G2-DR860--input: %.5fV\n", readADC(D1, Gain2, DR860));
printf("==G4=0x0600=DR8 input: %.5fV\n", readADC(D1, Gain4, DR8));
printf("-G4-DR16-- input: %.5fV\n", readADC(D1, Gain4, DR16));
printf("-G4-DR32-- input: %.5fV\n", readADC(D1, Gain4, DR32));
printf("-G4-DR64-- input: %.5fV\n", readADC(D1, Gain4, DR64));
printf("-G4-DR128--input: %.5fV\n", readADC(D1, Gain4, DR128));
printf("-G4-DR250--input: %.5fV\n", readADC(D1, Gain4, DR250));
printf("-G4-DR475--input: %.5fV\n", readADC(D1, Gain4, DR475));
printf("-G4-DR860--input: %.5fV\n", readADC(D1, Gain4, DR860));
*/
return 0;
}
mainのprinf文はテスト時の残骸です。
フルスケール±6.144Vと±4.096Vのレンジは電源電圧3.3Vを超えて入力できません。16ビットの確度は、多くがDR128以下のデータレートで得られます。