LoginSignup
0
2

More than 3 years have passed since last update.

初めてのRaspberry Pi Pico ㉕ C言語 16ビットADC ADS1115

Last updated at Posted at 2021-02-23

 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に分けました。

  ADS1115データシート

CMakeLists.txt
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.h
//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にしました。

ads1115.c
/**
 * 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, &reg, 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以下のデータレートで得られます。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2