Raspberry PiでSC1602とSC2004を制御する
C++を使って可読性にこだわったプログラムを作っているうちに、SC1602の不具合まで見つけてしまいました。
Raspberry Pi(以下ラズベリー)を購入するきっかけとなったのは日経Linux 2013年3月号です。この時の話は「ド素人がRaspberry Piで電子工作を始める」に書いた通りです。この記事はブログの定番となりました。Googleにて「ラズベリーパイ」で検索するとこのページがトップ10に表示されるのです。(2014/09/22現在)
ブログはLEDを点滅させるまでで終わっていますが、日経Linuxの記事には続きがあります。SC1602という液晶ディスプレイの制御です。実は私はLEDと一緒にSC1602も購入しました。そして日経Linux 2013年3月号の掲載を試し、SC1602の制御に成功してはいました。しかしSC1602に添付されていた説明書に書かれている事はさっぱり理解できませんでした。それでも少しずつ「解読」し続け、最終的に全てを理解できるようになりました。
SC1602とは
SC1602とは文字専用のディスプレイです。搭載している機械を探してみました。
本題からそれますが、ローソンのリーダーは使い勝手が悪いと思いませんか。カードをリーダーにタッチするだけでなく、カードの種類を画面で選択し、さらにもう一度タッチしなければならないからです。私は面倒なので他のコンビニに行く事がよくあります。
SC1602の定義
私はSC1602の歴史についてよく知りません。そして調べてみましたがよくわかりませんでした。SC1602とはこれらのディスプレイの総称でもあり、これらのディスプレイの制御方法を示すようです。
SC1602はHD44780「互換」コントローラを搭載したディスプレイでもあるようです。どうもSC1602は機種名のようなのですが、その機種は日立のHD44780を搭載しているようです。これは1980年台の製品であり、色々な会社によって「互換」製品がリリースされているようです。
店頭で販売されているキャラクタ液晶に内蔵されているのはHD44780「互換」コントローラがほとんどです。互換と謳っていますので、基本的にはどれでも同じ方法で操作できますが、様々な製品があり、データ送信タイミングや電源電圧等が微妙に異なったりしますので、挙動がおかしいなと思ったときには、実際に入手したLCDのデータシートでデータ送信タイミングを確認するようにしましょう。 – マイコン徹底入門
WikipediaにはHitachi HD44780 LCD controllerについて書かれていましたが、日本語による説明がありませんでした。日本の日立が開発したデバイスなのに日本語では寄稿されていないのです。
マルツパーツ館の機種はTINSHARPという会社が製造した「サンライク社SC1602互換仕様」と書かれています。SC1602は日立のHD44780互換だとすると、マルツパーツ館の機種は互換の互換ということになります。
SC1602にはいくつかのサイズがあります。10×4、16×1、16×2、16×4、20×2、20×4、40×2、40×4などです。SC1602という名称の由来はサイズが16×2だからということでしょう。
実際に購入したSC1602
私はいくつかのSC1602と1つのSC2004を購入しました。
ピンをこのようにはんだづけしました。
こうすると取り外しが楽です。ラズベリー側はこうなっています。
可変抵抗は10KΩ(103)です。
最初はメスのピンを使っていました。
しかしこれだと何かを実際に製作する時にはんだづけできないことに気付きました。
また、Vdd端子とGND端子は1番と2番ですが、逆になっている事もあるので注意してください。VddとGNDを逆にすると機器が起動しないばかりでなく機器が故障すると思います。何か「大人の事情」があるのでしょうか。謎は深まるばかりです。
日経Linuxのコード
日経Linuxに掲載されていたコードの最初はこのようになっています。
GPIO_CLR = 0x1F << 7; // GPIO10 7-10(DB4-7)とGPIO 11(RS)をクリア GPIO_CLR = 1 << 22; // GP10 22(E:イネーブル信号) = 0 usleep(1); GPIO_SET = 3 << 7; // GP10 7-10(DB4-7)に8ビット接続設定出力 // GP10 11(RS)=0 usleep(1); GPIO_SET = 1 << 22; //イネーブル信号 = 1 usleep(20); //十分なウェイト(20μs) GPIO_SET = 1 << 22; //イネーブル信号 = 0
<<が<になってしまっています。WordPressに手を入れると解決できそうですが難しそうなので放置しています。
私にはC言語の知識がありましたが、それでもかなり面くらいました。1行目ではGPIOをクリアするのだとコメントを見てわかりました。ビット演算をしている事もすぐにわかりました。私がわかったのは、「16進数で1Fという数を用意し、それを7ビットシフトさせてGPIOのI/Oアドレスへ書き込む」ということです。
16進数で1F?7ビットシフト?GPIOのI/Oアドレス?こんな状態からはじめ、1つ1つ意味がわかるまで調査していきました。
GPIO_CLRはgpio_setup.hに定義されていました。
#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0
*(gpio+7)はgpioポインタの7つ先という意味です。gpioポインタは
gpio = (volatile unsigned *)gpio_map;
と書かれているので、gpio_mapと基本的には同じです。ではgpio_mapとは何か。
・・・というように、調査し続けたのです。
そうしてわかったのは、このプログラムはBCM2835を直接制御しているという事です。それもビット演算を駆使して。
1Fは2進数で11111です。7ビットシフトすると
1111 1000 0000
となります。1ビット目からGPIO0、GPIO1・・・を意味するので、1になっているのはちょうどGPIO7からGPIO11です。これらのビットをGPIO_CLRが示すアドレスに上書きするとGPIOがクリアされるのです。
これはかなり萎える作業です。この方法で制御できるのはわかりましたが、こんな方法しかないのでしょうか。
電子工作とプログラミング
色々と検索したり本を読んだりしてわかったのは、全ての人は例外なく何らかのライブラリを呼び出していることでした。それらのライブラリは最終的に0と1を巧みに操っていました。ライブラリの呼び出し方(以下関数)もこんな感じです。
lcdwrite(0x23);
writeByte(b("00111000"));
こういったソースコードは後で見たら意味がわかりません。writeByteやlcdwriteといった関数を知らなければ忘れたら呼び出せません。またbはwriteByteやlcdwriteと同じであること、すなわち関数であること、それにすら気付けません。
関数がわかったとしても、次に00111000や0x23の意味を調べる必要があります。「後で見る」という事は、新たな機能を作るか不具合を直すという事です。そういった時に毎回苦労しそうです。
私はプログラミングを先に経験してから電子工作を始めました。だから思うのですが、これらのコードを書いた人は電子工作を専門にやっている人のように思えるのです。電子工作する上で避けて通れないからプログラミングをしているように思えるのです。
ソースコードと可読性
後で見てもわかるソースコードとなるよう改良を重ね、このようになりました。
#include "SC1602.cpp" int main (int argc, char *argv[]) { SC1602 *lcd; lcd = new SC1602; lcd << "Hello World!"; }
これだけで液晶画面の初期化から文字列の表示までできてしまいます。関数がわからなければ、「lcd ->」とタイプすればeclipseが教えてくれます。「CentOSにてEclipseを使ってC++のコードをクロスコンパイルし、Raspberry Pi用プログラムを作る」も参照してください。
もしSC2004を使うのであれば、このように書けます。
#include "SC1602.cpp" #include "SC2004.cpp" int main (int argc, char *argv[]) { SC1602 *lcd; lcd = new SC2004; lcd << "Hello World!"; }
そして、SC2004のプログラミングはたったこれだけです。
class SC2004 : public SC1602 { // SC2004固有の設定 SC2004 () { // 1行の表示文字数 DISPLAY_WIDTH = 20; // 1行の文字数 INTERNAL_DISPLAY_WIDTH = 20; // ディスプレイの行数 DISPLAY_HEIGHT = 4; }; unsigned int setDDRAM (unsigned char x, unsigned char y) { unsigned char address = x; if (y == 0); // 何もしない else if (y == 1) address += 0x40; else if (y == 2) address += 0x14; else if (y == 3) address += 0x54; address |= FUNCTION_CODE_SET_DD_RAM_ADDRESS; SC1602::setDDRAM(address); return SUCCESS; }; };
SC2004はSC1602を「継承」しています。制御方法がSC1602と全く同じなので、SC1602をそのまま使い、違う部分だけ「オーバーライド」するのです。これがオブジェクト指向プログラミングの便利なところです。
一方、SC1602のsetDDRAMはオーバーライドしてもらえるように作られています。
protected: virtual unsigned int setDDRAM (unsigned char x, unsigned char y) { ... }
違う部分だけをオーバーライドする/されるために欠かせないのはリファクタリングです。逆に我々にリファクタリングするつもりがなければ、オーバーライドしてもらえるように元のクラス(ここではSC1602)を作るのは難しいでしょう。私もSC1602とSC2004を行ったり来たりしながらリファクタリングしてここまでたどり着きました。
私は初めからこのようなプログラミングができた訳ではありませんでした。オブジェクト指向は「車」と「バイク」というクラスが「走る」「スピードを表示する」「給油する」といった例で説明されます。それはわかるのですが、例は例に過ぎません。実際に扱われるオブジェクトは実際の「車」ではなくパソコン上の「データ」です。それがまさかSC1602とSC2004といった実在する機器で使われることになるとは思ってもいませんでした。
なお、C++は必ず電子工作に使えるとは限らないので注意してください。それでも私がC++を選択したのは、Microchip社のPIC32シリーズであれはC++をサポートしていることと、PIC32シリーズのコストと物理的なサイズよりも、オブジェクト指向によるプログラミングを重視したからです。しかし私はMicrochip社のPICKIT3を購入したものの全く使っておらずよくわかっていません。そもそも購入した理由は、将来的にラズベリーからソースコードを無難に移植できるプログラミング言語を検討するためです。Microchip社のPICは電子工作に幅広く利用されており、今後利用する可能性が非常に高いと思ったからです。
SC1602には不具合がある
「後で見てもわかるソースコード」を書いたからか、私はSC1602に不具合がある事を発見しました。私は秋月電子とマルツパーツ館にてSC1602互換機を購入しましたが、秋月電子で購入したものに限って不具合が発生するのです。
不具合は左カーソルシフトで発生します。左カーソルシフト時にカーソルがアドレス0x00を超えると、SC1602はDDRAMアドレスを正しく管理できなくなるのです。
左カーソルシフトについてご存知でしょうか。SC1602の文字の表示方向は右から左ですが、これを逆にするのが左カーソルシフトです。制御コード「5:Cursor/display Shift」にて設定できます。どういう機会に必要なのかはよくわかりません。アラビア語のように右から左へ文字を書く言語をドライバーが想定しているのだと思います。
ドライバーとは、ディスプレイ制御するチップの事です。SC1602の基板上のどこかにはんだづけされているようです。台湾のサンライクに確認したところ、秋月電子で販売している機種のドライバーはSitronix社のST7066Uだそうです。また、SPLC780Dというドライバーを搭載している機種を使ってみては、と言われました。それは確認の「しよう」がないのですが。
なお、サンライクの人はマルツパーツ館で販売されている機種は異なるドライバーを搭載しているとも言っていました。不具合が裏付けできました。
他にもあった違い
SC1602は制御コードを受け付けると、その処理を実行するために時間が必要になります。処理時間は仕様書に書かれている通りです。例えば、制御コード(Instruction)の「Clear Display」の処理時間は1.5ミリ秒です。この値が1.52msと書かれていたり、1.64msと書かれていたりするようですが、違いはよくわかりません。ラズベリーが処理時間を待たずに次の制御コードを送ってもSC1602は無視します。厳密に言うと無視しているように見えます。
私が発見したのは、この時間が機種によって異なる事です。秋月電子で販売されている機種は0.5ミリ秒以上の待ち時間が必要でしたが、マルツパーツ館で販売されている機種は0.4ミリ秒以上の待ち時間で動作しました。いずれにしても、1.5ミリ秒を守っていれば問題ありません。
「1.5ミリ秒を守らない」というコードをわざわざ書く人はいません。問題なのは1.5ミリ秒を守る事を知らなければ、プログラムは0.1ミリ秒すら待ってくれない事です。という事は何らかのきっかけがなければ原因がまったくわからなくなるという事です。さらに言えば、コンソールにデバッグ文字を出力してしまうと0.5ミリ秒以上かかりますのでバグが隠れてしまいます。
ダウンロード
検証に使用したプログラムを公開します。以前に書いた「Raspberry PiでRHT03を制御する」で公開したプログラム「Tango」を機能拡張したものです。
オプションについて
オプションを指定すると動作が変わります。オプションには必須のものと任意のものがあります。指定順序は動作に関係ありません。大文字小文字を区別します。オプション一覧に書かれた順にそれぞれ1度だけ実行されます。demo10は無限ループするため、プログラム検証後は[Ctrl]と[c]キーで停止させてください。
実行例
./tango db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11
SC1602を4ビットモードで初期化します。SC1602には点滅するカーソルが表示されます。
./tango db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 clear
SC1602へ制御コード1 clearを送信します。SC1602には点滅するカーソルが表示されます。
./tango db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 write=ABCDEFG
SC1602へ文字列ABCDEFGを出力します。
./ tango db0=18 db1=23 db2=24 db3=25 db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 info
8ビットモードで初期化して設定情報をコンソールへ表示します。SC1602には点滅するカーソルが表示されます。
./ tango db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 write=ABCDEFG && ./ tango db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 noinit x=0 y=0 read=7
SC1602へ文字列ABCDEFGを出力します。エラーコードが0ならカーソルをアドレス0,0へ戻します。(homeでは文字が消えてしまいます) ラズベリーはSC1602から1文字を読み込みコンソールへ出力します。これを7回繰り返すため、文字列すべて(ABCDEFGのはずです)が表示されます。
もしうまく動作しない場合は、最低限の接続だけで動作するかどうか試してみてください。
SC1602端子 | Raspberry Pi端子 |
DB0~DB3 | 接続しない |
DB4~DB7 | 任意のGPIO |
RW | GND |
RS | 任意のGPIO |
E | 任意のGPIO |
VO | GND |
オプション一覧
オプション | 動作 |
help | 使い方を表示しプログラムを終了します。他のオプションはすべて無視されます。usageも使用可能です。 |
debug | デバッグ情報を表示します。 |
sc2004 | SC2004を制御します。(未指定時はSC1602を制御します) |
noadjust | LCDによるカーソル移動後にカーソル位置を調整しません。詳細は後述します。 |
noinit | 初期化用コマンドを実行しません。readコマンドと併用してください。 |
nosave | サスペンドしません。サスペンドの有無は消費電力に影響しますが、機能には影響しません。詳細は後述します。 |
db0=[2..31] | DB0を接続するGPIO番号を指定します。 |
db1=[2..31] | DB1を接続するGPIO番号を指定します。(db0指定時に必須) |
db2=[2..31] | DB2を接続するGPIO番号を指定します。(db0指定時に必須) |
db3=[2..31] | DB3を接続するGPIO番号を指定します。(db0指定時に必須) |
db4=[2..31] | DB4を接続するGPIO番号を指定します。(必須) |
db5=[2..31] | DB5を接続するGPIO番号を指定します。(必須) |
db6=[2..31] | DB6を接続するGPIO番号を指定します。(必須) |
db7=[2..31] | DB7を接続するGPIO番号を指定します。(必須) |
rw=[2..31] | R/Wを接続するGPIO番号を指定します。(接続しない場合も必須) |
rs=[2..31] | RSを接続するGPIO番号を指定します。(必須) |
e=[2..31] | Eを接続するGPIO番号を指定します。(必須) |
info | 接続設定を表示します。 |
制御コード1,2 | |
---|---|
clear=[0..1000000] | 画面をクリアします。カーソル位置やディスプレイ位置も初期化します。待ち時間を指定できます。(単位はマイクロ秒で、デフォルトは1520です。) |
return | カーソル位置および文字列表示位置を初期位置に戻します。 |
制御コード3 Entry Mode | |
direction=[0,1] | どちらの方向にカーソルとディスプレイを移動させるか設定します。0:左, 1:右(デフォルト) |
shift=[0,1] | カーソルとディスプレイのどちらを移動させるか設定します。0:カーソル(デフォルト), 1:ディスプレイディスプレイを指定すると見かけ上の出力位置は変わらなくなりますが、カーソル位置(内部的な位置(DDRAM))は変わる事に注意してください。 |
demo1 | Entry Modeのデモです。 |
demo2 | 右から左へ文字を出力するデモです。 |
制御コード4 Display On/Off | |
display=[0,1] | ディスプレイ表示を設定します。0:消灯, 1:点灯(デフォルト) |
cursor=[0,1] | カーソルの形を設定します。0:なし, 1:_(デフォルト) |
blink=[0,1] | カーソルの表示方法を設定します。0:点灯, 1:■点滅(デフォルト) |
demo3 | Display On/Offのデモです。 |
制御コード5 Cursor/display Shift | |
demo4 | Cursor/display Shiftのデモです。 |
demo5 | 文字をスクロールして一周させるデモです。 |
制御コード6 Function Set | |
line=[0,1] | ライン数を設定します。0:1ライン, 1:2ライン(デフォルト) |
font=[0,1] | フォントのドット数を設定します。0:5×8(デフォルト), 1:5×11 |
demo6 | demonstrates both combinations. |
制御コード7 Set CGRAM | |
char1 | 外字文字(スマイリー)を定義して表示します。 |
char2 | 外字文字(ボウリング)を定義して表示します。 |
char3 | 外字文字(電池)を定義して表示します。 |
制御コード8 Set DDRAM | |
x=[0..] | カーソルの位置(横方向)を設定します。 |
y=[0..] | カーソルの位置(縦方向)を設定します。 |
demo7 | カーソルを動かすデモです。 |
制御コード9 Read Busy Flag/Address Counter | |
demo8 | ビジーフラグを読み込むデモです。 |
demo9 | アドレスカウンタを読み込むデモです。 |
制御コード10 Write Data | |
write=ABC | 文字列ABCを出力します。 |
demo10 | キーボードから入力された文字をそのまま出力するデモです。 |
制御コード11 Read Data | |
read=[0..] | 表示している文字列を読み込み画面へ出力します。文字数を指定した場合は、カーソルの位置から文字数だけ読み込みます。 |
chardef | 外字設定を読み込んで表示するデモです。 |
検証機器
検証に用いた機器は以下の通りです。
- Raspberry Pi Model B
- OS 2014-01-07-wheezy-raspbian
- LCDは以下の通りです
No | 販売店 | メーカー | 型番 | 製造番号 | Vddピン | バックライト |
1 | RS Components | Powertip | PC1602-LRS-H | 921932019 | 2 | あり |
2 | 秋月電子 | 不明 | SC1602BS*B-XA-GB-K | 1006-005-109 | 1 | なし |
3 | 秋月電子 | 不明 | SC2004CBWB-XA-GB-G | 1102-003-034 | 2 | あり 白抜き文字20×04 |
4 | マルツパーツ館 | Linkman | TC1602E-06T | 134600012052 | 1 | あり (黄緑) |
左カーソルシフトの不具合を検証する
左カーソルシフト時にカーソルがアドレス0x00を超えると、DDRAMアドレスがどのように変化するか検証します。
./tango lcdbas db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 noadjust direction=0 demo9
実行すると以下が表示されます。
[0,0 (0000)]
意味は[カーソル位置x, カーソル位置y (DDRAMアドレス)]です。
もし[0,0 (007F)]と表示される場合は、DDRAMアドレスが正しく読めていません。RW端子をGPIO10へ接続していないと思われます。
キーボードから1文字入力しEnterを押します。カーソル位置がどのように変化するか確認してください。
正常動作 | 不具合 |
[0,0 (0000)] | [0,0 (0000)] |
[39,1 (0067)] | [39,1 (007F)] |
[38,1 (0066)] | [38,1 (007E)] |
[37,1 (0065)] | [37,1 (007D)] |
[36,1 (0064)] | [36,1 (007C)] |
[35,1 (0063)] | [35,1 (007B)] |
[34,1 (0062)] | [34,1 (007A)] |
[33,1 (0061)] | [33,1 (0079)] |
[32,1 (0060)] | [32,1 (0078)] |
… | … |
[17,1 (0051)] | [17,1 (0069)] |
[16,1 (0050)] | [16,1 (0000)] |
[15,1 (004F)] | [15,1 (007F)] |
… | … |
以下はa~hをタイプした後です。
検証結果
販売店 | 型番 | 結果 |
RS Components | PC1602-LRS-H | 不具合あり |
秋月電子 | SC1602BS*B-XA-GB-K | 不具合あり |
秋月電子 | SC2004CBWB-XA-GB-G | 不具合あり |
マルツパーツ館 | TC1602E-06T | 正常 |
マルツパーツ館のSC1602では1文字しか表示されていませんが、これが正常動作です。文字は赤い部分に格納されます。グレーの部分は非表示の領域です。スクロールすると表示できます。
マルツパーツ館以外のものでは、存在しないDDRAMアドレスにカーソルが移動します。そしてなぜか0x00~0x07に表示されてしまいます。
カーソル位置xとyが正常であるのは、プログラム内でカーソル位置を数えているからです。カーソル位置がわかればDDRAMを上の表を使って変換できます。そして不具合を回避するにはDDRAMを計算結果で上書きすればいいのです。
Clear Displayの待ち時間を検証するコマンド
Clear Displayコマンド実行後ABCDEFGを出力します。ただし待ち時間を意図的に少なくします。(1520マイクロ秒から500マイクロ秒へ減少させます)
./tango lcdbas db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 clear=500 write=ABCDEFG
コマンドを何回か実行してみてください。大体10回に1回は正常表示されます。
10回に8回位はBから表示されます。
では、後の1回はというと・・・
全く意味がわからない文字列になります。しかし、表示された文字列は必ず再現性があります。今回は「 $4DTd」となっていますが、何回か後にはまた「 $4DTd」が表示されます。
サスペンド状態を検証する
おまけですが、サスペンドの効果も検証しました。
テスターをmAモードにして、ラズベリーとSC1602の間に直列につなぎます。
コマンドを実行します。文字を出力するだけです。
./tango lcdbas db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 write=ABCDEFG
./tango lcdbas db4=4 db5=17 db6=27 db7=22 rw=10 rs=9 e=11 write=ABCDEFG nosave
nosaveオプション省略時は、E以外(DB, R/W, RS)につないだGPIOの設定が、入力モード、電圧HIGH、プルUPに設定されます。nosaveオプション指定時はこのGPIO設定が省略されます。すなわち文字を出力し終わっても、GPIOの設定が出力モード、電圧HIGHかLOW、プルUPかDOWNのままとなります。
検証結果
No | 販売店 | 型番 | サスペンドなし | サスペンドあり | 削減率 |
1 | RS Components | PC1602-LRS-H | 0.92mA | 0.39mA | 58% |
2 | 秋月電子 | SC1602BS*B-XA-GB-K | 0.71mA | 0.26mA | 64% |
3 | 秋月電子 | SC2004CBWB-XA-GB-G | 31.46mA | 31.07mA | 1.1% |
4 | マルツパーツ館 | TC1602E-06T | 0.88mA | 0.43mA | 52% |
どのLCDでも電流が0.5mAほど減ります。よって消費電力も半分になります。SC2004CBWB-XA-GB-Gでも電流が0.5mAほど減りますが、もともと電流が大きいため、相対的な効果はほとんどありません。
もし本記事に興味、ご関心があれば、ぜひ「ラズベリーパイと電子工作の記事一覧」を見てください。例えばこんな記事があります。
- Raspberry PiでRHT03を制御する
RHT03はMaxDetect社が製造している湿度計です。これをRaspberry Piで制御する方法を紹介します。ド素人だけに大変苦労しました。 - Raspberry Piのケースで学ぶ穴あけ加工
Raspberry Piのケースに穴をあけ、GPIOピンを外に出してみました。ケース加工にはどちらかというと、木工・金属DIYの知識が必要です。 - CentOSにてEclipseを使ってC++のコードをクロスコンパイルし、Raspberry Pi用プログラムを作る
6月末に書いた記事「ド素人がRaspberry Piで電子工作を始める」では、シェルスクリプトを使ってLEDを点灯できました。シェルスクリプトは簡単に始められますが、もしC言語でプログラムを書けるようになれば、可能性が大きく広がると思いました。
This post is also available in: 英語
コメントを残す