ポインタに関する文章は誤りがあり、誤解を生むものだった為、削除いたしました。 別記事で再度取り上げたいと思います。 勉強不足で記事を上げてしまい、申し訳ございませんでした。 m(_ _)m (2016/1/6)
今回は、ESPr Developer ( ESP-WROOM-02, ESP8266 )のSRAM メモリが足りなくなってきたので、外付けSPI通信のSRAM を増設してみました。
Web から文字列を抽出しようとすると、多量の文字列のために ESP8266 のメモリだけではかなり厳しくなってきました。
そこで、Microchip の 23K256 を使えば別途 32,768byte のメモリを確保できますので、実際に動作を試してみました。
1.準備するもの
シリアルSRAMモジュール 23K256
Microchip Technology の 23K256 ( SPI通信) チップに5V動作マイコンに直接接続できるようにしたもので、スイッチサイエンスさん販売のものです。
1ページ32byte が1024ページあり、合計32,768byte のメモリです。
マイコンとは SPIで通信します。最大20 MHz です。
これには2.54mmピンヘッダは付いておりませんので、別途入手してください。
これは、arms22さんが作成したものをスイッチサイエンスさんが再構成して販売しているものだそうです。
arms22さんのブログ 「なんでも作っちゃう、かも。」 は、私はよく拝見していて、とても興味深いものを作ってらっしゃいます。
この方からはTwitterで時々アドバイスを下さることがあり、とても勉強になったことがありました。
感謝感謝 m(_ _)m
また、これについては、スイッチサイエンスさんのページも合わせてご参照ください。
https://www.switch-science.com/catalog/1072/
ESPr Developer ( ESP-WROOM-02 開発ボード)
Amazon.co.jp
これは何度も当ブログで解説している、優れものWi-Fiマイコンボードです。
ESPRESSIF社のESP8266を日本の電波法をクリアさせて技適認証取得した ESP-WROOM-02 をスイッチサイエンスさんがとても使いやすくパッケージにしたものです。
2.54mm ピンヘッダは別途購入が必要です。
ブレッドボード
これは、ESPr Developer を挿しても、両サイドとも2列の空きがあるので、とても重宝しています。
2.54 mm ピンヘッダ
SRAMや ESPr Developerにハンダ付けするものです。
固定抵抗 100kΩ
Holdピンを使用不可にするためのプルアップ抵抗です。
100kΩであれば何でもよいです。
ジャンパーワイヤー、USBケーブル、ハンダこて、ハンダ、PC、ネット環境等
2.ピンヘッダハンダ付け
SRAMモジュール 23K256 はこんな感じのものです。
これには2.54mm ピンヘッダはありませんので、別途揃えたものをハンダ付けします。
完成したものはこんな感じです。
ESPr Developer の使い方、およびピンヘッダハンダ付けは以下のページを参照してください。
ESPr Developer ( ESP-WROOM-02 開発ボード )の使い方をザッと紹介
3.接続方法
ブレッドボード上での接続はこんな感じです。
ちなみに、スイッチサイエンスさんのページも合わせてご参照ください。
https://www.switch-science.com/catalog/1072/
今後、SDカードやOLED を接続するために、CS ( Chip Select) はGPIO #02 に接続としておきます。
HOLD端子は通信を一時停止する時に使用するようです。
使わない時は 100kΩの抵抗でプルアップしておきます。
4.Arduino IDE の設定
ESPr Developer を使う場合は、Arduino IDE にESP8266ボードをインストールしておきます。
最近、めでたく Arduino.ccと Arduino.org 団体が統合しましたが、そのArduino IDE 1.8.0 では問題なく動作しました。(2017.1.3時点)
ESP8266 ボードのインストール方法は以下の記事を参照してください。
Arduino IDE に Stable ( Staging )版 ESP8266 ボードをインストールする方法
5.SRAM 23K256 オペレーションの流れ
まず、Microchip 23K256 のデータシートを見てください。
こちら → http://www.microchip.com/wwwproducts/en/23K256
データシートを踏まえた、プログラミングの流れは次のような感じです。
1. SPI通信初期化
SPI 周波数設定( Max 20MHz )
SPI MODE 指定 ( Mode 0 )
SPI MSB First 指定
SPI MODE については以下のページを参照
→ ESP8266 ( ESP-WROOM-02 ) SPI 通信高速化、その2 ( 複数デバイス、Mode、Watchdog Timer 問題 )
2. Status Resister セット
データの読み書きの前に以下の3つのモードの何れかを設定します。
●Byte Mode
●Sequential Mode
●Page Mode
実際のデータ読み書きには、mosiピンにまず以下のようなInstructionバイトを送ります。
【 Instruction Byte 】
Write Status:00000001
Read Status :00000101
そのすぐ後に Mode 設定バイトを送ります。
データシートによると、7bit, 6bit を以下のように設定します。
00 (=0) : Byte mode ( default )
10 (=1) : Page mode
01 (=2) : Sequential mode
11 (=3) : Reserved
0bit目がHOLD ビットですが、これは 1 としてdisable とします。
その他のピットは0とします。
よって、シーケンシャルモードで書き込みをしたい場合は
01000001
として送ります。
ただ、読み込みのStatus Resisterは書き込んだ時点のモードが引き継がれるようなので、Modeを書き込みできません。
その代わり、Status Resister バイトを送信したら、すぐに現在のModeバイトがmiso端子に返ってきます。
その後に実際に書き込みたいデータをバイト単位で送信すればSRAM に記憶されます。
読み込みの場合はmodeバイトの後に実データが送信されてきます。
3. データ書き込み、読み込み
実際のデータ読み書きには、まず以下のようなInstructionバイトを送ります。
【 Instruction Byte 】
Data Write:00000010
Data Read :00000011
そのすぐ後に16bit でアドレスをSRAMに送信します。
その後に実際のデータを8bit ( 1byte ) づつ送ります。
Byte Mode の場合は1byte 毎にアドレスをInstruction Byte と16bitアドレスを付加して送信します。
Page Mode の場合は1ページが32byte なので、アドレス指定が同じならば、その32byte中だけで上書きされていきます。つまり、32バイトを超える文字列を書き込みすると、超えた文字列は最初の文字列に上書きされます。
Sequential Mode の場合は、ページの境界は無視され、自動でアドレスをインクリメントして書き込みします。ただ、アドレスが 32767 ( 0x7FFF )を超えたら、0x0000 にセットされ、順次上書きされていきます。
6.スケッチの入力
スケッチ(プログラム)は3種類に分けて説明します。
スイッチサイエンスさんのページでは、arms22さんのブログ記事のライブラリを使っておりますが、ここでは、動作を理解するためにライブラリを使わず、ESP8266ボード標準ライブラリのSPI.hだけインクルードして組んでみました。
Byte Mode Operation
一文字ずつアドレスを指定して、SRAMに読み書きするものです。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
/* * Byte MODE Operation * Microchip SRAM 23K256 (SPI) * 32byte x 1024 page = 32768byte */ #include <SPI.h> const uint8_t cs_SRAM = 2; //SRAM 23k256 Chip Select PIN const uint8_t sclk = 14; const uint8_t mosi =13; //ESP8266 Master Output Slave Input const uint8_t miso =12; //ESP8266 Master Input Slave Output void setup() { delay(1000); Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println(); SPI.begin(); SPI.setFrequency(20000000); //Max 20MHz SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); pinMode(cs_SRAM, OUTPUT); digitalWrite(cs_SRAM, HIGH); Serial.println("-----byte Mode-----------------"); Sram_Write_Byte(17, 'H'); Sram_Write_Byte(14, 'E'); Sram_Write_Byte(21, 'L'); Sram_Write_Byte(24, 'O'); Serial.write( Sram_Read_Byte(17)); //シリアルモニターに文字表示させたい場合はwrite。数値表示させたい場合はprintを使う Serial.write( Sram_Read_Byte(14)); Serial.write( Sram_Read_Byte(21)); Serial.write( Sram_Read_Byte(21)); Serial.write( Sram_Read_Byte(24)); Serial.println(); } void loop() { } void Sram_Write_Byte(uint16_t adrs, uint8_t b){ digitalWrite(cs_SRAM, LOW); SPI.write(0b00000001); //Write Status Resister //MODE 7-6bit (0: byte), (1: Sequential), (2: Page), (3: Reserved) SPI.write(0b00000001); //Mode & HOLD-diable digitalWrite(cs_SRAM, HIGH); digitalWrite(cs_SRAM, LOW); SPI.write(0b00000010); SPI.write16(adrs); SPI.write(b); digitalWrite(cs_SRAM, HIGH); } uint8_t Sram_Read_Byte(uint16_t adrs){ digitalWrite(cs_SRAM, LOW); SPI.write(0b00000101); //Read Status Resister uint8_t mod = SPI.transfer(0); //MODE 7-6bit (0: byte), (1: Sequential), (2: Page), (3: Reserved) digitalWrite(cs_SRAM, HIGH); //Serial.print("SRAM Read Mode = "); Serial.println(mod, BIN); digitalWrite(cs_SRAM, LOW); SPI.write(0b00000011); SPI.write16(adrs); uint8_t b = SPI.transfer(0); digitalWrite(cs_SRAM, HIGH); return b; }
【解説】
●6行目:
ESP8266 ボードのSPIライブラリをインクルードします。
●8-11行目:
ESPr Developer ( ESP-WROOM-02, ESP8266 ) のGPIO ピン設定です。
SRAM のCS ( Chip Select )ピンとの接続を#2 としたのは、今後SDカードやOLED を接続するために他のピンを空けておくためです。
●21-24行目:
Arduino Core for ESP8266 のSPIライブラリの初期化です。
SRAM 23K256 はデータシートによると、最高周波数は20MHz で、SPI Mode 0 です。
SPIライブラリではデフォルトでMSB First になっていますので、23行目は不要かもしれませんが、念のため設定しておいきます。
●26-27行目:
CS ( Chip Select )ピンは出力設定し、HIGHレベルにしておきます。
●30-33行目:
一文字づつアドレスを指定してSRAMに記憶させる関数です。
45-57行で関数化しています。
アドレスは16bit で指定します。0-32767 ( 0x00-0x7FFF ) まで指定できます。
0x8000 を指定すると、アドレスは自動で先頭に戻り、0x0000 にリセットされて上書きされます。
戻り値は uint8_t 型(byte型)で、char型ではないところがポイントです。
●34-39行目:
シリアルモニターに文字を表示させます。
SRAMからの読み取りは、59-72行で関数化しています。
ここで注意していただきたいのは、Serial.write を使っていることです。
これは、シリアルモニターにバイナリ出力するので、文字表記することができます。
Serial.print を使うと、バイト数値がそのまま10進数で表示されます。
Serial.println(Sram_Read_Byte(17), HEX);
とすると16進表記されるので、ASCII文字コードを解釈しやすくなります。
●45-57行目:
SRAM 23K256 から1バイト読み込む関数です。
先の5項目のところで述べたオペレーション通りにESPr Developer ( ESP8266, ESP-WROOM-02 )からSRAMへSPI通信指令を出します。
まず、レジスタ指令を出す場合は、必ずCS ( Chip Select )ピンをLOWレベルにします。
書込み用のステータスレジスタをセットするには
00000001
を送信します。
次にモード設定バイトを49行のように送信します。
7-6bitが00 なので、Byte書き込みモードになります。
0bit目は1にしてHOLD端子をdisable にしておきます。
それが終わったら、必ずCSピンをHIGHレベルにして、セット終了です。
この後の書き込みは全てByteモードになります。
実は、読み込みも全てByte モードになってしまうんです。
モードを切り替えるためには一度そのモードで書き込みを行わなければなりません。
その後、実際のデータを送りますが、ます、CSピンをLOWレベルにします。
次に書き込み指令のInstructionバイト
00000010
を送信します。
その後 16bit アドレスを送信。
それからようやく実際のバイトデータを送信します。
データ送信が終わったら、CSピンをHIGHレベルにして書き込完了です。
●59-72行目:
SRAM 23K256 から1バイト読み込む関数です。
まず、CSピンをLOWにします。
読み込みStatus Resister は 00000101 を送信。
すると、読み込みの場合はステータスレジスタをセットできません。
現在のSRAM がどのモードで書き込まれたかを示すバイトがSRAMから送信されてきます。
それが62行目です。
つまり、読み取りは書き込んだ時点のモードが引き継がれるわけです。
ですから、同じモードで読み込むのなら、ステータスレジスタセットは不要ということになります。
実行結果はこうなります。
Sequential Mode Operation
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
/* * Sequential MODE Operation * Microchip SRAM 23K256 (SPI) * 32byte x 1024 page = 32768byte */ #include <SPI.h> const uint8_t cs_SRAM = 2; //SRAM 23k256 Chip Select PIN const uint8_t sclk = 14; const uint8_t mosi =13; //ESP8266 Master Output Slave Input const uint8_t miso =12; //ESP8266 Master Input Slave Output void setup() { delay(1000); Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println(); SPI.begin(); SPI.setFrequency(20000000); //Max 20MHz SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); pinMode(cs_SRAM, OUTPUT); digitalWrite(cs_SRAM, HIGH); Serial.println("-----Sequential Mode------------"); char* cstr1 = "0123456789abcdefghijklmnopqrstuvwxyz"; char* cstr2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; uint16_t Len1, Len2; //最後にNull文字を加えた文字列の長さを格納 Len1 = Sram_Write_Seq(0, cstr1); Len2 = Sram_Write_Seq(Len1, cstr2); //cstr1 の後にcstr2を書き込み uint16_t adrs1, adrs2; //分かりやすいようにadrsという名前にする adrs1 = 0; adrs2 = Len1; //cstr1の文字列の後のアドレスにcstr2の文字列を入れるため、cstr1の長さを代入 uint8_t sram_str1[Len1], sram_str2[Len2]; //※ここは必ず固定長の配列にする。読み込んだ文字列を格納 Sram_Read_Seq(adrs1, Len1, sram_str1); Sram_Read_Seq(adrs2, Len2, sram_str2); Serial.write(sram_str1, Len1); Serial.println(); Serial.write(sram_str2, Len2); Serial.println(); Serial.println("-----Sequential Mode Total------------"); Sram_Write_Seq(0x7FFF, "+-/*"); //読み取りモードを変えるには、一度そのモードで書き込みをする uint8_t sram_str3[ 100 ]; //※ここは必ず固定長の配列にする。読み込んだ文字列を格納 Sram_Read_Seq(0, 100, sram_str3); Serial.write(sram_str3, 100); Serial.println(); } void loop() { } uint16_t Sram_Write_Seq(uint16_t adrs, char* buf){ //Write Status Resister Set digitalWrite(cs_SRAM, LOW); SPI.write(0b00000001); //Write Status Resister //MODE 7-6bit (0: byte), (1: Sequential), (2: Page), (3: Reserved) SPI.write(0b01000001); //Mode & HOLD-diable digitalWrite(cs_SRAM, HIGH); uint16_t Len = strlen( buf ); digitalWrite(cs_SRAM, LOW); SPI.write(0b00000010); SPI.write16(adrs); for(int i=0; i<Len; i++){ SPI.write(buf[i]); //Serial.print(buf[i],HEX); Serial.print(','); yield(); } SPI.write('\0'); //最後に必ずNullバイトを挿入 Len = Len + 1; digitalWrite(cs_SRAM, HIGH); return Len; } void Sram_Read_Seq(uint16_t adrs, uint16_t len, uint8_t buf[]){ digitalWrite(cs_SRAM, LOW); SPI.write(0b00000101); //Read Status Resister uint8_t mod = SPI.transfer(0); //MODE 7-6bit (0: byte), (1: Sequential), (2: Page), (3: Reserved) digitalWrite(cs_SRAM, HIGH); Serial.print("SRAM Read Mode = "); Serial.println(mod, BIN); digitalWrite(cs_SRAM, LOW); SPI.write(0b00000011); SPI.write16(adrs); for(int i=0; i<len; i++){ buf[i] = SPI.transfer(0); //Serial.write(buf[i]);Serial.print(','); yield(); } digitalWrite(cs_SRAM, HIGH); }
【解説】
SPIセットアップはByte モードと同じなので省略します。
32byte毎のページ境界を無視して書き込みますので、長い文字列を記憶させる場合は必ずSequentialモードにします。
●33-35行目:
SRAM 23K256 にSequentialモードで書き込みます。60-82行で関数化しています。
文字列の最後にNull文字 ‘\0’ を加えた文字数を返します。
Null文字を入れないと、Serial.print関数を使う時にエラーになる場合があります。
34行でModeを1 として、アドレス0番に文字列を書き込みます。
35行で、その続きのアドレスから文字列を書き込んでいます。
●37-39行目;
ここは単にアドレスと文字数の引数を間違えないように、アドレス専用の引数に置換しているだけです。
●41-43行目;
Sequentialモードで書き込んだ文字列を、同じSequentialモードで読み込みます。
84-100行で関数化しています。
読み取りのモードは書き込んだモードで決定されます。
直前に書き込んだモードと異なるモードは指定できません。
書き込んだ文字列の先頭アドレスを指定して読み込みます。
それは、固定長で宣言されたsram_str1 に格納されます。
●45-46行目:
SRAMから読み取られたデータはbyte型なので、Serial.print関数では数値しか表示できません。
この場合は、Serial.write を使います。ただ、Serial.writeで文字列を表示する場合、バイト数を指定しなければなりませんので、Len1 というバイト数を指定します。
シリアルモニターを見ていただければ分かると思いますが、’\0’ というNull文字はカットされます。
固定長配列のsram_str1 という名前がその配列の先頭アドレスを指すので、これも正にポインタの動作そのものです。
●48-54行目:
シーケンシャルモードでは、アドレスが 0x7FFF ( 32768 ) を超えると0x0000 に戻って上書きされます。
その様子をシリアルモニターで見てみるプログラムです。
以下のような実行結果を見てみると、’+’ が0x7FFFF に書き込まれ、”-/*\0” が0x0000から書き込まれているのが分かると思います。
そして、’\0’ はカットされているのが分かると思います。
最後の方の文字化けはポインタを扱っていると時々目にする文字化けと同じですね。
●60-82行:
62-66行でWrite Status Resister を Sequential Mode にセットします。
68行で、書き込む文字数を算出します。これでは ’\0’ はカウントしません。
71行目のInstructionバイトをWrite に設定。
72行でアドレスを16bit で指定。
73-77行で実際の文字列バイトを送ります。
76行目の yield(); はウォッチドッグタイマエラー ( wdt ) を出さないようにするためのものです。
78-79行目で、必ずヌル文字を最後に追加しておきます。
81行で、文字数を返します。
●84-100行:
85-89行では、Byteモードと同じようにRead設定にしますが、これも書き込んだ時点のモードが引き継がれるので、同じモードで読み込む場合はここは不要です。
92行でInstructionバイトをReadに設定
93行で先頭アドレスを指定。
94-98行でSRAMからバイトデータをbuf1配列に格納していきます。
Arduino および ESP8266 で、格納した配列を関数間で渡すためには、84行のように buf[] として配列宣言しておくことです。
実行結果は以下の通りです。
Page Mode Operation
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
/* * PAGE MODE Operation * Microchip SRAM 23K256 (SPI) * 32byte x 1024 page = 32768byte */ #include <SPI.h> const uint8_t cs_SRAM = 2; //SRAM 23k256 Chip Select PIN const uint8_t sclk = 14; const uint8_t mosi =13; //ESP8266 Master Output Slave Input const uint8_t miso =12; //ESP8266 Master Input Slave Output void setup() { delay(1000); Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println(); SPI.begin(); SPI.setFrequency(20000000); //Max 20MHz SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); pinMode(cs_SRAM, OUTPUT); digitalWrite(cs_SRAM, HIGH); Serial.println("-----Page Mode------------"); char* cstr1 = "0123456789abcdefghijklmnopqrstuvwxyz"; char* cstr2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; uint16_t Len1, Len2; //最後にNull文字を加えた文字列の長さを格納 Len1 = Sram_Write_Page(0, cstr1); Len2 = Sram_Write_Page(Len1, cstr2); //cstr1 の後にcstr2を書き込み Serial.println(Len1); Serial.println(Len2); uint16_t adrs1, adrs2; //分かりやすいようにadrsという名前にする adrs1 = 0; adrs2 = Len1; //cstr1の文字列の後のアドレスにcstr2の文字列を入れるため、cstr1の長さを代入 uint8_t sram_str1[Len1], sram_str2[Len2]; //読み込んだ文字列を格納 Sram_Read_Page(adrs1, Len1, sram_str1); Sram_Read_Page(adrs2, Len2, sram_str2); Serial.write(sram_str1, Len1); Serial.println(); Serial.write(sram_str2, Len2); Serial.println(); Serial.println("-----Page Mode Total------------"); Sram_Write_Page(3000, ""); //読み取りモードを変えるには、一度そのモードで空文字書き込みをする uint8_t sram_str3[ 100 ]; Sram_Read_Page(0, 100, sram_str3); Serial.write(sram_str3, 100); Serial.println(); } void loop() { } uint16_t Sram_Write_Page(uint16_t adrs, char* buf){ //Write Status Resister Set digitalWrite(cs_SRAM, LOW); SPI.write(0b00000001); //Write Status Resister //MODE 7-6bit (0: byte), (1: Sequential), (2: Page), (3: Reserved) SPI.write(0b10000001); //Mode & HOLD-diable digitalWrite(cs_SRAM, HIGH); uint16_t Len = strlen( buf ); digitalWrite(cs_SRAM, LOW); SPI.write(0b00000010); SPI.write16(adrs); for(int i=0; i<Len; i++){ SPI.write(buf[i]); yield(); } SPI.write('\0'); //最後に必ずNullバイトを挿入 Len = Len + 1; digitalWrite(cs_SRAM, HIGH); return Len; } void Sram_Read_Page(uint16_t adrs, uint16_t len, uint8_t* buf1){ digitalWrite(cs_SRAM, LOW); SPI.write(0b00000101); //Read Status Resister uint8_t mod = SPI.transfer(0); //MODE 7-6bit (0: byte), (1: Sequential), (2: Page), (3: Reserved) digitalWrite(cs_SRAM, HIGH); Serial.print("SRAM Read Mode = "); Serial.println(mod, BIN); digitalWrite(cs_SRAM, LOW); SPI.write(0b00000011); SPI.write16(adrs); for(int i=0; i<len; i++){ buf1[i] = SPI.transfer(0); //Serial.write(buf1[i]);Serial.print(','); yield(); } digitalWrite(cs_SRAM, HIGH); }
ページモードでは、32バイト書き込んだらそのページ内で繰り返し文字の入出力ができます。
あまりこのモードを使うことは無いような気がします。
設定等はMode を変えるだけで、上記と同じですので、解説は省略します。
ただ、以下の実行結果を見ていただくとおり、シリアルモニターでは独特な表示になります。
最初に書き込んだアドレスが0ならば、0-32byte が1ページになりますので、32byteを超える文字列はページ内の最初のアドレスに戻って上書きされます。
そして、Null文字 ‘ \0’ はシリアルモニターでは表示されませんが、ドラッグしてテキストコピーすると、間にはちゃんとNull文字が入っているようで、Null文字までしかクリップボードにコピーできませんでした。
サンプルスケッチの53-56行でPage モードで100byte分読み取るようにすると、最初のアドレスのページ内を延々と読み続けます。
そうすると、実行結果のようになります。
これは使い方によっては面白いことができそうですね。
まとめ
以上、外付けSRAM の使い方でした。
これはポインタの使い方にソックリで、ポインタについても同時に記事にしようとしましたが、最初に申し上げたように、私はまだまだ勉強不足でした。
Twitter ではいろいろな方々からご指摘をいただきました。
ポインタの文章は誤りと誤解を生むものだったので削除させていただきました。
ポインタについては、また改めて別記事で取り上げてみたいと思います。
m(_ _)m
ではまた・・・。
Amazon.co.jp 当ブログのおすすめ
コメント
ポインタとかメモリの扱いとか配列とか文字列とかについて、もう少し正しく理解されてから記事を公開した方がいいと思いました。
「関数間のポインタ受け渡しは固定長配列にする。」や「配列やポインタ文字列の最後には Null文字 ‘\0’ を追加する。」はまったく意味不明です。
「可変長ポインタ」とか「ポインタ文字列」とかも意味不明です。
仕事中で、お返事おそくなりスミマセン。
ご指摘、ご尤もです。
今、ポインタのところは今日中に削除しようと思っています。
私は、趣味程度でArduino IDEぐらいしかC言語(C++)を扱ったことがありません。
個人の趣味の電子工作ですから、「こういう風にやれば動いたー」的な感覚で記事にしています。
専門知識よりも、まず思ったように動くこと優先で記事アップしています。
今までもポインタを使っていましたが、今回はさらに深く理解したつもりでした。
しかし、実際はちゃんと分かっていなかったようです。
Twitterでも、他の方々からご指摘を頂きました。
ちょっと、今回はあまりにも無知だったことが分かったので、反省してます。
文章も正しくなかったと思います。
「Arduino IDE でスケッチする場合は、関数間でポインタ受け渡しをするよりも、固定長配列が無難」
「Serial.printで文字列のポインタを使う場合は最後に ‘\0’ を入れる」
という文章に変えればよろしかったでしょうか。
‘\0’については、
char* c=”abc”;
とすれば自動で入りますので、気にしなくて良いのですが・・・。
この件は、また改めて別記事で取り上げたいと思っています。
これでも意味不明でしたら、またコメントいただければと思います。
今後も、趣味の電子工作で、「こうやれば動いたよ」的な記事はバンバン上げていく予定ですので、何か誤り等ありましたらコメントいただけると幸いです。
ご指摘ありがとうございました。
m(_ _)m
たった今、誤りや誤解を生みそうなポインタ関連文章を削除しました。
もし、また何かありましたら、コメント頂けると幸いです。
> 「Arduino IDE でスケッチする場合は、関数間でポインタ受け渡しをするよりも、固定長配列が無難」
Arduino(UNO) の裏で動いているのは avr-g++/avr-gcc というコンパイラです。
http://www.musashinodenpa.com/arduino/ref/ には以下の記載があります。
「Arduino言語はC/C++をベースにしており、C言語のすべての構造と、いくつかのC++の機能をサポートしています。また、AVR Libcにリンクされていて、その関数を利用できます。」
このため、C言語としての制約はないはずです。メモリが少ないとか、ライブラリが少し貧弱というのはありますが。
それに、ポインタを受け渡しする目的は配列の利用だけでもないと思います。
> 「Serial.printで文字列のポインタを使う場合は最後に ‘\0’ を入れる」
文字列なので ‘\0’ で終わるのは普通のことと思います。Serial.print()(だけ)とは関係ない気も。
http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=1259 や https://www.arduino.cc/en/Reference/String を参照してください。Str2 の書き方だけは要注意ですが。
いろいろ偉そうに書いてしまってすみません。
電光掲示板の記事とかはとても楽しく読ませてもらってます。
再びお立ち寄りいただき、感謝いたします。
http://www.musashinodenpa.com/arduino/ref/
このページは私も何度もお世話になりましたが、コンパイラの説明部分は殆ど素通りでした。
コンパイラの構造とか、C言語の仕組みとかはスッ飛ばして Arduino電子工作をやっていましたから、とんちんかんな記事になってしまいました。
なるほど、確かに「C言語のすべての構造」と書いてありますね。
これは失礼いたしました。
それに、
>それに、ポインタを受け渡しする目的は配列の利用だけでもないと思います。
は、ご尤もです。
配列だけではありません。
私も記事の書き方を誤りました。
そもそも、この記事を書くキッカケになったのは、WEBから多量の文字列を取得して抽出する際に、文字列を格納した配列やポインタをメモリを節約しながら他のローカル関数へ渡すことが上手くできなかったことが始まりでした。
char c[6] = “abcde”;
とすれば、’\0′ は自動で入りますが、WEBページから、1文字(1byte)づつ配列に代入していく場合、
として、10文字取得するとすると、WEB側のテキストに'\0'が無い限り、それは挿入されません。
結果、シリアルモニターには入力文字列以降は文字化け表示されてしまいます。
こういうことは、基礎をスッ飛ばして来た私のようなアマチュア電子工作人間にとってはとても疑問なんです。
電子工作を続けていると、
char* c = "abcde";
という初期化で、'\0'も含めて6文字格納されているっていうことは忘れてしまう場合が多々あります。
ですから、
char c[5] = "abcde";
みたいなことを平気でやってエラーとなってしまい、
「なんでやねん???」
となって、永遠と他のエラーを探してしまいます。
Arduino IDE はあまりC言語の基礎を学ばないでも手軽に始められるツールですが、やっぱり、本当に自分のやりたいことをやろうとすると、必ずポインタのところでつまづきます。
こういう人は意外と多いと思います。
長くなってしまいましたが、今回は私のC言語の無知もそうですが、文章の書き方というか、国語力の無さも痛感しました。
確かに、「Serial.printで文字列のポインタを使う場合は最後に ‘\0’ を入れる」という文章だけでは、意味不明となってしまいますね。
ということで、この辺の事はまとめて新たに記事にしたいと思っています。
Twitter でも他の方々からご意見をいただきました。
いろいろとご指摘いただき、とても勉強になりました。
また何かありましたら、遠慮なくご意見いただけると助かります。
別の仕事があり、すぐにお返事できないかも知れませんが、出来るだけ対応したいと思います。
ありがとうございました。
m(_ _)m