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

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

新規登録して質問してみよう
ただいま回答率
85.36%
Arduino

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

Q&A

解決済

2回答

5315閲覧

arduino unoで1kHz以上のサンプリング周波数でAD変換したデータをSDカードに保存しようと思っていますがうまくいきません.

shio

総合スコア3

Arduino

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

0グッド

0クリップ

投稿2021/11/11 12:04

前提・実現したいこと

マイコン初心者の者です.arduino unoを用いてAD変換器を作成しています.ただAD変換をする上で,サンプリング周波数を自由に設定できるようにしたいです.現在,timer1の割り込みによって,サンプリング周波数を調整しようとしています.シリアル通信でPCへprintするときは,指定したサンプリン周波数で正しいデータを得ることができました.ただ,シリアル通信によるPCへのプリントとSDカードへの保存を同時にしようとすると正しいデータがでてきません.SDカードはV10(10Mbps)のものを使用しています.SPI通信の速度が遅いことが原因だと私は考えています.ただ,1[kHz]以上でのサンプリング及び,SDカードへの保存を実現したいです.なにかいい方法はあるのでしょうか.
詳しい方,ご教授ください.よろしくお願いします.

発生している問題・エラーメッセージ

上記のとおりです.

該当のソースコード

作成したプログラムは以下の通りです. #include <SPI.h> #include <SD.h> #include <TimerOne.h> #include <MsTimer2.h> const int chipSelect = 10; // Arduino UNOでは10、Arduino MEGAでは53 int ainput; //読み取ったbit数:intは整数 float vinput; //bit数を電圧に変換したもの:floatは浮動小数点数 //電圧測定及びSDカード保存 void mesuring(void){ vinput=5.0*ainput/1023; //上記を電圧mVに変換 Serial.println(vinput);//電圧をシリアル通信でパソコンに送信 /* SDカードに書き込み */ File dataFile = SD.open("datalog.csv", FILE_WRITE); if (dataFile) { dataFile.println(vinput); } dataFile.close(); } void setup(void) { /* ----- Setting up serial communication with PC ------ */ /* ここでUSBを介してPCとシリアル通信を始める。9600はシリアル通信のボーレート */ Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only //何らかの問題があってシリアルポートに接続できないときは、このループにトラップされる } /* ----- Initialisation of SD card ------ */ Serial.print("Initializing SD card..."); //see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); // don't do anything more: return; } Serial.println("card initialized."); int i= 100;//サンプリング間隔[micro sec] Timer1.initialize(1000); //マイクロ秒単位で設定 Timer1.attachInterrupt(mesuring); } void loop(void){ ainput=analogRead(A0); }

試したこと

試せることはありません.他力本願で申し訳ありません.

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

ここにより詳細な情報を記載してください。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

thkanaさんも挙げられた質問「microSDの読み書き時間について」での私の回答の内容のとおりですが、

SDカードは512バイトごとに書き込みます。普通にSDライブラリを使って書き込みする場合、たぶん512バイトごとにファイル情報も書き換えていたと思いますので、ここがボトルネックとなります。
その時間はあちらの質問者によれば2.5msほど掛かるようです。
「割り込み内ではデータをバッファに貯めておき、地のコードでSDへ書き出し」という方法でSDへの書き出しを中断して他の処理を行えます。
あちらにも書いたコードですが、以下が400μsごとにタイマーの値を取得してSDに書き出すコードです。全体の動作は異常ですがSDへの書き込みは想定通りの値になりました。
更に速度を上げるならファイルシステムを自力で処理する手もありますが1kHzならそこまでは不要そうです。

Arduino

1#include <SD.h> 2#include <SPI.h> 3 4const byte SD_SS = 4; 5String fname = "SDwrite.txt"; 6File file; 7 8unsigned long time_last = 0; 9unsigned long time_now = 0; 10 11byte buf[2][256] = {0}; 12int count[2] = {0}; 13byte phase = 0; 14int i = 0; 15 16ISR(TIMER2_COMPA_vect) 17{ 18 time_now = micros(); 19 i++; 20 unsigned long time_diff = time_now-time_last; 21 buf[phase][count[phase] ] = time_diff&0xFF; 22 buf[phase][count[phase]+1] = (time_diff>> 8)&0xFF; 23 buf[phase][count[phase]+2] = (time_diff>>16)&0xFF; 24 buf[phase][count[phase]+3] = i&0xFF; 25 count[phase]+=4; 26 time_last = time_now; 27} 28 29void setup() 30{ 31 TCCR2A = 0b10000010; 32 TCCR2B = 0b00000011; // clk/32 33 OCR2A = 199; // 400usで割り込み 34 TIMSK2 = 0b00000010; 35 Serial.begin(57600); 36 37 unsigned long temp = 0L; 38 39 if (!SD.begin(SD_SS)) 40 { 41 Serial.println("SD_FAIL"); 42 return; 43 } 44 45 Serial.println("start"); 46 file = SD.open(fname, FILE_WRITE); //ファイル新規作成、書き込みモード 47 sei(); 48 49 byte buf_x[100] = {0}; 50 int num = 100; 51 52 while (true) 53 { 54 if(micros() > 5000000L) 55 { 56 break; 57 } 58 phase ^= 1; 59 temp += count[phase^1]; 60 if(count[phase^1] != 0) 61 { 62 file.write(buf[phase^1], count[phase^1]); 63 count[phase^1] = 0; 64 } 65 } 66 file.close(); 67 68 Serial.println("close"); 69 Serial.println(i); 70 Serial.println(temp); 71} 72 73void loop() 74{ 75 //何もしない 76}

投稿2021/11/11 16:41

ikadzuchi

総合スコア3047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

まず。ArduinoのSDカード処理は「遅い」です。SDカードの「おまけ」的にあるシリアルモードを使っているので。何MB/Sの転送速度!とか言っているのはSDカードの本来のアクセス方法を使っているのですが、それをやるならライセンス料金を支払う必要があります。
ArduinoのSDカードの実力がどのくらいかは私は測ったことがないので知りませんが...

つぎに。SDカードに書くのに、毎回ファイルオープン/書き込み/クローズしたら遅くなって当然です。最初にオープン、次々とデータを書き込んで、使い終わる時にクローズする、というのが普通の使い方です。

もひとつ、割り込み中はいろいろと機能制限があります。大きくは、割り込み中は(それなりの手を打っていないと)他の割り込みが禁止になっています。SPI通信とかは結構割り込みをつかっているんじゃないかしら? 多分Serial.printも危ないです。
そういうこともありますので、「割り込みハンドラ内ではあまり大きな仕事をせず、速やかにメインコンテキストに復帰する」のが原則です。

'Arduino SDカード 書き込み速度'でぐぐったらこんなのがトップでヒットしました。ご参考まで。


ちょっとやってみたので追記。
プログラムは

Arduino

1#include <MsTimer2.h> 2#include <SD.h> 3#include <SPI.h> 4#include <TimerOne.h> 5 6const int chipSelect = 10; // Arduino UNOでは10、Arduino MEGAでは53 7volatile int ainput; //読み取ったbit数:intは整数 8volatile uint8_t rdy; 9File dataFile; 10 11//電圧測定及びSDカード保存 12void mesuring(void) { 13 if (!rdy) { 14 ainput = analogRead(A0); 15 } 16 rdy = 1; 17} 18 19void setup(void) { 20 pinMode(2, INPUT_PULLUP); 21 pinMode(3, OUTPUT); 22 /* ----- Setting up serial communication with PC ------ */ 23 /* ここでUSBを介してPCとシリアル通信を始める。9600はシリアル通信のボーレート 24 */ 25 Serial.begin(115200); 26 while (!Serial) { 27 ; // wait for serial port to connect. Needed for native USB port only 28 //何らかの問題があってシリアルポートに接続できないときは、このループにトラップされる 29 } 30 31 /* ----- Initialisation of SD card ------ */ 32 Serial.print("Initializing SD card..."); 33 // see if the card is present and can be initialized: 34 if (!SD.begin(chipSelect)) { 35 Serial.println("Card failed, or not present"); 36 // don't do anything more: 37 return; 38 } 39 Serial.println("card initialized."); 40 41 int i = 100; //サンプリング間隔[micro sec] 42 Timer1.initialize(1000); //マイクロ秒単位で設定 43 Timer1.attachInterrupt(mesuring); 44 dataFile = SD.open("datalog.csv", FILE_WRITE); 45 if (!dataFile) { 46 Serial.println("File Open Error."); 47 while (1) 48 ; 49 } 50 rdy = 0; 51} 52 53void loop(void) { 54 /* SDカードに書き込み */ 55 if (rdy) { 56 digitalWrite(3, HIGH); 57 dataFile.println(ainput, HEX); 58 // dataFile.write(&ainput,2 ); 59 digitalWrite(3, LOW); 60 rdy = 0; 61 Serial.println(ainput, HEX); 62 } 63 if (digitalRead(2) == LOW) { 64 dataFile.close(); 65 Serial.println("Stop."); 66 while (1) 67 ; 68 } 69}

で、D3の波形。基本的には1ms毎にパルスが立つんだけど、長い目?で見ると、
波形
100msの後10msからときとしてもっと長く、書き込みに専念しているようです。この期間割り込みも止まっていました。割り込みが動いていればバッファリングで誤魔化す手もあるんだけど...(誤り。オシロのサンプリングが足りなくて細くなったパルスが見えてなかった...)
512byte溜めて一気に書き込む、というのが対応でしょうね。


しつこくいじってみました。以下のプログラム参照。多分動いているんじゃないかと。
512byte毎にSDカードに書き込んでいて、大抵は10ms~18msぐらいでwriteが終わるのですが時々100ms~170msかかるときがあります(発生原因は把握していませんがSDカードの都合?)。
これをカバーしようとするとAD結果のバッファサイズ(BUFSIZE)をそれなりに大きくしなきゃいけない、というあたりが落とし穴か。(100ms以上かかるのが連続しないという保証も実はないのだけれど、UNOではもうメモリに余裕がない)

Arduino

1#include <MsTimer2.h> 2#include <SD.h> 3#include <SPI.h> 4#include <TimerOne.h> 5 6//#define HARDDEBUG 7 8const int chipSelect = 10; // Arduino UNOでは10、Arduino MEGAでは53 9File dataFile; 10 11const int BUFSIZE = 200; 12int rbuf[BUFSIZE]; 13int wp = 0, rp = 0; 14int overrun = 0; 15 16//電圧測定及びSDカード保存 17void mesuring(void) { 18#ifdef HARDDEBUG 19 digitalWrite(4, HIGH); 20#endif 21 wp = (wp + 1) % BUFSIZE; 22 rbuf[wp] = analogRead(A0); 23 if (wp == rp) { 24 overrun = 1; 25 } 26#ifdef HARDDEBUG 27 digitalWrite(4, LOW); 28#endif 29} 30 31void setup(void) { 32 pinMode(2, INPUT_PULLUP); 33#ifdef HARDDEBUG 34 pinMode(3, OUTPUT); 35 pinMode(4, OUTPUT); 36#endif 37 /* ----- Setting up serial communication with PC ------ */ 38 /* ここでUSBを介してPCとシリアル通信を始める。9600はシリアル通信のボーレート 39 */ 40 Serial.begin(115200); 41 while (!Serial) { 42 ; // wait for serial port to connect. Needed for native USB port only 43 //何らかの問題があってシリアルポートに接続できないときは、このループにトラップされる 44 } 45 46 /* ----- Initialisation of SD card ------ */ 47 Serial.print("Initializing SD card..."); 48 // see if the card is present and can be initialized: 49 if (!SD.begin(chipSelect)) { 50 Serial.println("Card failed, or not present"); 51 // don't do anything more: 52 while (1) 53 ; 54 } 55 Serial.println("card initialized."); 56 dataFile = SD.open("datalog.csv", FILE_WRITE); 57 if (!dataFile) { 58 Serial.println("File Open Error."); 59 while (1) 60 ; 61 } 62 Serial.println("File Open."); 63 Timer1.initialize(1000); //マイクロ秒単位で設定 64 Timer1.attachInterrupt(mesuring); 65} 66 67void loop(void) { 68 /* SDカードに書き込み */ 69 if (wp != rp) { 70#ifdef HARDDEBUG 71 digitalWrite(3, HIGH); 72#endif 73 rp = (rp + 1) % BUFSIZE; 74 // Serial.println(rbuf[rp]); 75 pushBuf(rbuf[rp]); 76#ifdef HARDDEBUG 77 digitalWrite(3, LOW); 78#endif 79 } 80 if (digitalRead(2) == LOW) { 81 dataFile.close(); 82 Serial.println("Stop."); 83 while (1) 84 ; 85 } 86 if (overrun) { 87 Serial.println("OR"); 88 overrun = 0; 89 dataFile.close(); 90 Serial.println("."); 91 while (1) 92 ; 93 } 94} 95uint32_t t0, t1, t2; 96void pushBuf(int val) { 97 int wdat = val * 500l / 1023; //固定小数点数 98 t1 = millis(); 99 dataFile.print(wdat/100); 100 dataFile.print('.'); 101 dataFile.println(wdat %100); 102 t2 = millis(); 103 if (t2 - t1 > 8) { 104 Serial.print(t1 - t0); 105 Serial.print("/"); 106 Serial.println(t2 - t1); 107 t0 = t2; 108 } 109}

投稿2021/11/11 13:08

編集2021/11/14 12:39
thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2021/11/11 23:32

> 何MB/Sの転送速度!とか言っているのはSDカードの本来のアクセス方法を使っているのですが、それをやるならライセンス料金を支払う必要があります。 正確にはSPIモードでもSD規格独自のコマンド(マルチメディアカードの仕様にない部分) を使用するとアウトです
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問