質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.12%
組み込み開発

組み込み開発とは、スマートフォンや家電、自動車などに組み込まれているコンピューターシステムの開発のことです。特定の用途に特化しており、限られた機能のための開発を指します。組み込み開発で作られた機器を組み込み機器と呼び、近年ではPCのオペレーションシステム(OS)にも採用されています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

解決済

SPIクラスを継承したクラスでSPI ADCの値が取得できない

tain
tain

総合スコア241

組み込み開発

組み込み開発とは、スマートフォンや家電、自動車などに組み込まれているコンピューターシステムの開発のことです。特定の用途に特化しており、限られた機能のための開発を指します。組み込み開発で作られた機器を組み込み機器と呼び、近年ではPCのオペレーションシステム(OS)にも採用されています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

1回答

0リアクション

0クリップ

192閲覧

投稿2022/09/28 15:08

編集2022/10/06 05:45

前提

ArduinoとSPI接続ADC(MCP3008)を接続し、ADCの値を取得するクラスを作成したいです。
SPIライブラリを用いた動作はうまくいったので、SPISettingクラスを継承してCS端子の管理やチャンネルの設定などを行うクラスを作っています。

質問

プログラムを動かしたところ、SPIで値が取得できず無限ループ状態になっているようでした。オシロスコープで計測したところ、CSピンがLowに落ちたままでクロックがでていないことを確認しています。

当該プログラム

cpp:mcp3008.h

/* */ #ifndef __MCP3008_H__ #define __MCP3008_H__ #include <arduino.h> #include <stdint.h> #include <SPI.h> class ADC_MCP3008 : public SPISettings { using SPISettings::SPISettings; public: ADC_MCP3008(uint8_t CSPinNo, uint32_t clock) : m_ADC_MCP3008Setting(clock, MSBFIRST, SPI_MODE0) , m_CSPinNumber(CSPinNo) { pinMode(m_CSPinNumber, OUTPUT); digitalWrite(m_CSPinNumber, m_CSPIN_NEGATE); } virtual ~ADC_MCP3008() { } uint16_t readSingleMode(uint8_t ch); uint16_t readDifferentialMode(uint8_t ch); uint32_t readTest(void); uint32_t privateRead(void); void readBurstSingleMode(uint16_t * buffer); void readBurstDifferentialMode(uint16_t * buffer); private: const SPISettings m_ADC_MCP3008Setting; const uint8_t m_CSPinNumber; const uint8_t m_CSPIN_ASSERT = LOW; const uint8_t m_CSPIN_NEGATE = HIGH; const uint8_t m_ADC_CHNUM = 8; uint32_t _readRaw(uint8_t dummy, uint8_t highByte, uint8_t lowByte); }; #endif /* __MCP300x_H__ */

cpp:mcp3008.cpp

#include <arduino.h> #include <stdint.h> #include <SPI.h> #include "mcp3008.h" uint32_t ADC_MCP3008::_readRaw(uint8_t startByte, uint8_t highByte, uint8_t lowByte) { volatile uint8_t rcvs[3]; digitalWrite(m_CSPinNumber, m_CSPIN_ASSERT); rcvs[0] = SPI.transfer(startByte); rcvs[1] = SPI.transfer(highByte); rcvs[2] = SPI.transfer(lowByte); digitalWrite(m_CSPinNumber, m_CSPIN_NEGATE); return (uint32_t)((rcvs[0] << 16) & (rcvs[1] << 8) & (rcvs[2])); } uint16_t ADC_MCP3008::readSingleMode(uint8_t ch) { uint16_t rcv; if (ch < m_ADC_CHNUM) { SPI.beginTransaction(m_ADC_MCP3008Setting); rcv = _readRaw(0x01, (uint8_t)( 0x80 | (ch << 4)), 0x00); SPI.endTransaction(); } else { rcv = 0xFFFF; } return rcv; } uint16_t ADC_MCP3008::readDifferentialMode(uint8_t ch) { } void ADC_MCP3008::readBurstSingleMode(uint16_t * buffer) { } void ADC_MCP3008::readBurstDifferentialMode(uint16_t * buffer) { } uint32_t ADC_MCP3008::readTest(void) { SPI.beginTransaction(m_ADC_MCP3008Setting); return _readRaw(0x01, 0x80, 0x00); SPI.endTransaction(); } uint32_t ADC_MCP3008::privateRead(void) { digitalWrite(m_CSPinNumber, m_CSPIN_ASSERT); delay(10); digitalWrite(m_CSPinNumber, m_CSPIN_NEGATE); return m_CSPinNumber; }

cpp:mcp3004_3008.ino

#include <arduino.h> #include "mcp3008.h" #define CSPIN A4 ADC_MCP3008 spiADC = ADC_MCP3008(CSPIN, 1000000); void setup() { Serial.begin(115200); Serial.println("MCP3004_3008_TEST"); delay(100); } void loop() { uint32_t readval; // readval = spiADC.readSingleMode(0); readval = spiADC.readTest(); // readval = 10; ///readval = spiADC.privateRead(); Serial.print(millis()); Serial.print(", "); Serial.print(readval); Serial.println(); delay(10); }

試したこと

readSingleModeではうまく通信ができていないので、readTestで送信値を固定して送信してみましたがだめでした。また、
コンストラクタでの初期化がうまくいっている事を確認するため、privateRead関数内でCSを動かしたところ、正しく動いているようです。

補足情報(FW/ツールのバージョンなど)

cpp:readok.ino

#include <SPI.h> #define CSPIN A4 float Vref = 5.0 ; SPISettings settings(1000000,MSBFIRST,SPI_MODE0); void setup() { pinMode(SS, OUTPUT); Serial.begin(9600); SPI.begin(); } void loop(){ SPI.beginTransaction(settings); digitalWrite(SS, LOW); SPI.transfer(0b00000001); // Start bit 1 byte highByte = SPI.transfer(0b10000000); // CH0 singleEnd byte lowByte = SPI.transfer(0x00); // dummy digitalWrite(SS, HIGH); SPI.endTransaction(); unsigned int dataCh0 = ((highByte & 0x03) << 8) + lowByte; float volts = dataCh0*Vref /1024; Serial.println("highByte= " + String(highByte,DEC) + "lowByte= " + String(lowByte,DEC) ); Serial.println("CH0 " + String(volts,3) + "V"); }

参考にしたHP。こちらのプログラムは動作することを確認しました。

解決

皆様のご指摘を参考にしつつ、それ以外のバグも見つけ解決しました。

cpp:mcp3008.h

#ifndef __MCP3009_H__ #define __MCP3008_H__ #include <arduino.h> #include <stdint.h> #include <SPI.h> class ADC_MCP3008 { public: ADC_MCP3008(uint8_t CSPinNo, uint32_t clock, float vref) : m_ADC_MCP3008Setting(clock, MSBFIRST, SPI_MODE0) , m_CSPinNumber(CSPinNo) , ADC_VREF(vref) { SPI.begin(); pinMode(m_CSPinNumber, OUTPUT); digitalWrite(m_CSPinNumber, m_CSPIN_NEGATE); } virtual ~ADC_MCP3008() { } uint16_t readSingleMode(uint8_t ch); uint16_t readDifferentialMode(uint8_t ch); void readBurstSingleMode(uint16_t * buffer); void readBurstDifferentialMode(uint16_t * buffer); float convertBinToVoltage(uint16_t bin); const uint8_t ADC_SINGLE_CHNUM = 8; const uint8_t ADC_DUAL_CHNUM = 4; const float ADC_VREF; private: const SPISettings m_ADC_MCP3008Setting; const uint8_t m_CSPinNumber; const uint8_t m_CSPIN_ASSERT = LOW; const uint8_t m_CSPIN_NEGATE = HIGH; const uint16_t m_ADC_MASK = 0x03FF; // 10bit const uint8_t m_ADC_STARTBIT = 0x01; // 0b0000 0001 const uint8_t m_ADC_SINGLMODE = 0x80; // 0b1000 0000 const uint8_t m_ADC_DUALMODE = 0x00; // 0b0000 0000 uint32_t _readRaw(uint8_t startByte, uint8_t highByte, uint8_t lowByte); }; #endif /* __MCP300x_H__ */

cpp:mcp3008.cpp

#include <arduino.h> #include <stdint.h> #include <SPI.h> #include "mcp3008.h" uint32_t ADC_MCP3008::_readRaw(uint8_t startByte, uint8_t highByte, uint8_t lowByte) { volatile uint8_t rcvs[3]; digitalWrite(m_CSPinNumber, m_CSPIN_ASSERT); rcvs[0] = SPI.transfer(startByte); rcvs[1] = SPI.transfer(highByte); rcvs[2] = SPI.transfer(lowByte); digitalWrite(m_CSPinNumber, m_CSPIN_NEGATE); return (uint32_t)((rcvs[0] << 16) | (rcvs[1] << 8) | (rcvs[2])); } uint16_t ADC_MCP3008::readSingleMode(uint8_t ch) { uint16_t rcv; if (ch < ADC_SINGLE_CHNUM) { SPI.beginTransaction(m_ADC_MCP3008Setting); rcv = (uint16_t)( _readRaw(m_ADC_STARTBIT, (uint8_t)( m_ADC_SINGLMODE | (ch << 4)), 0x00) & m_ADC_MASK); SPI.endTransaction(); } else { rcv = 0xFFFF; } return rcv; } uint16_t ADC_MCP3008::readDifferentialMode(uint8_t ch) { uint16_t rcv; if (ch < ADC_DUAL_CHNUM) { SPI.beginTransaction(m_ADC_MCP3008Setting); rcv = (uint16_t)(_readRaw(m_ADC_STARTBIT, (uint8_t)( m_ADC_DUALMODE| (ch << 4)), 0x00)) & m_ADC_MASK; SPI.endTransaction(); } else { rcv = 0xFFFF; } return rcv; } void ADC_MCP3008::readBurstSingleMode(uint16_t * buffer) { int cnt; SPI.beginTransaction(m_ADC_MCP3008Setting); for (cnt = 0; cnt < ADC_SINGLE_CHNUM; cnt++, buffer++) { *buffer = (uint16_t)(_readRaw(m_ADC_STARTBIT, (uint8_t)( m_ADC_SINGLMODE | (cnt << 4)), 0x00)) & m_ADC_MASK; } SPI.endTransaction(); } void ADC_MCP3008::readBurstDifferentialMode(uint16_t * buffer) { int cnt; SPI.beginTransaction(m_ADC_MCP3008Setting); for (cnt = 0; cnt < ADC_DUAL_CHNUM; cnt++, buffer++) { *buffer = (uint16_t)(_readRaw(m_ADC_STARTBIT, (uint8_t)( m_ADC_DUALMODE| (cnt << 4)), 0x00)) & m_ADC_MASK; } SPI.endTransaction(); } float ADC_MCP3008::convertBinToVoltage(uint16_t bin) { return (float)bin * ADC_VREF / m_ADC_MASK; }

以下のような質問にはリアクションをつけましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

リアクションが多い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

thkana

2022/09/28 22:49

SPI.begin()は呼ばれていなくていいんだっけ?
tain

2022/09/28 23:50

確かにSPISettingを使っているので、SPI.beginは不要です。ただ、readTestのbeginTransactionは抜けてますね。お恥ずかしい。残念ながら追加してもだめでした。
matukeso

2022/09/29 02:15

メンバにSPISettings m_ADC_MCP3008Setting;を持ってるなら、SPISettingsを継承する意味がないような。 >SPISettingを使っているので、SPI.beginは不要 というのも、根拠がよくわかりませんが、、
thkana

2022/09/29 12:28

> 確かにSPISettingを使っているので、SPI.beginは不要です いやいや、そんなことはないでしょう。とりあえずArduino UNOとして、SPIClass::begin()の中で SPCR |= _BV(MSTR); SPCR |= _BV(SPE); とかやっているのはやっぱりなきゃダメでしょう。 そもそもが SPISettingじゃなくてSPIClassを継承する話なんじゃないかしら、とか思ったりして。

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.12%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

組み込み開発

組み込み開発とは、スマートフォンや家電、自動車などに組み込まれているコンピューターシステムの開発のことです。特定の用途に特化しており、限られた機能のための開発を指します。組み込み開発で作られた機器を組み込み機器と呼び、近年ではPCのオペレーションシステム(OS)にも採用されています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。