かなり前にJPEGカメラ(C328-7640)で撮影したファイルをSDカードに保存するプロジェクトを作成しました。
今回はコンパイラをImageCraft用に変更したついでに、いつものSG12864Aに表示するようにしてみました。
JPEGデコードはPSoCではかなり厳しいので、80x60ドットの8bitGrayScaleで撮影しています
どうせLCDもモノクロだし...
80x60の8bitのため、データサイズは4800バイトになります。この領域を確保するのは、やはりPSoCでは厳しい
のでEEPROMにデータを保存しています。
とりあえず動画です。
プロジェクトと回路図は後日UPします。
そのかわりに、カメラ(C328-7640)について書きます。
かつてない程の長文となりましたwwwww
C328-7640は、非力なマイコンでもJPEG画像が取得できる便利なカメラです。
値段は5000円ぐらいと、ちょっと高いなぁと思いますが、SDカードやXBee等の無線装置と組み合わせると簡単に定点観測やスパイカメラwが作れます。
解像度は低いですが、非圧縮形式でも画像(グレースケール、16bitカラー)を取得できるため、ロボットの目としても使えそうです。
カメラとのインターフェイスもUARTで制御できるため比較的簡単ではないかと思います。
しかし、簡単とは言え、やはりそれなりの手順は必要となります。
今回は備忘録として、JPEG画像の取得と8bitグレースケール画像の取得についてまとめてみました。
短時間でまとめたため読みにくい部分があるかと思いますが、そこは脳内補間とグーグル先生で自己解決してくださいw
それから僕の書く記事の大前提ですが、内容は保証するものではありません。
必ずデータシートを読んで、自分で確認してください。
【C328-7640仕様】
動作電圧: 3.0V~3.6V
消費電流: 60mA~
通信: UART
通信速度: 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200bps
色数: Gray Scale(2bit, 4bit, 8bit)[YCbCrのY(輝度)のみ]、Color(12bit[444:RGB], 16bit[565:RGB])、JPEG
解像度: 80x60, 160x120
JPEG解像度: 80x64, 160x128, 320x240, 640x480
SLEEP: 有り(100μA)
【C328-7640のコマンド送受信】
[1] C328とは基本的に専用のコマンドを使用してやりとりします
やりとりのコマンドはコマンドIDとオプションパラメータが4つの計6バイトが1セットになります。
例えばSYNCコマンドの場合、
0xAA 0x0D 0x00 0x00 0x00 0x00
(ID Number Param1 Param2 Param3 Param4)
と、先頭から順に送信します。
void sendSyncCmd()
{
C328TX_putByte( 0xAA );
C328TX_putByte( 0x0D );
C328TX_putByte( 0x00 );
C328TX_putByte( 0x00 );
C328TX_putByte( 0x00 );
C328TX_putByte( 0x00 );
}
って感じです。
UART送受信はバイナリモードで行う必要があります。テキストモードでは制御できません。
※C328TX_putByte()は、C328カメラに1バイト送信する関数です。
[2] コマンドを送信し、カメラ側が受領するとACKコマンドを返してきます
つまり、コマンド送信→ACK受け取り→次のコマンド送信→ACK受け取り‥‥‥
という感じです。
ACK送出はカメラ側だけではなく、マイコン側から送信する場合もあります。
カメラから送られてくるACKはパラメータで必要なもの(SYNCとか画像サイズ等)以外は、とりあえず6バイト読み込むだけでパラメータの内容は考慮しなくても良さそうです。
void getACK( void )
{
for( i = 0; i < 6; i++ ) C328RX_getByte();
}
って感じ。
[3] コマンドの送信は速すぎると失敗する場合があります
その場合はコマンド送信前に適度(20msぐらい)のwaitを入れてあげるとうまくいく場合があります。
【JPEG画像を取得する方法】
[1] SYNCコマンドを送信してカメラとの接続を確立する
通信速度をカメラ側がスキャンして自動的に認識するため、何回か送信してやる必要があります。
データシートには、だいたい25回ぐらいで確立できると書いてあります。
60回送信しても応答がなければ何らかの問題が発生していると考えられます。
一回接続が確立するとボーレート変更コマンドで指定しないかぎり通信速度は維持されます。
カメラ側からACKコマンドを受信したらマイコン側からもACKコマンドを送信し、手続きを完了します。
この時、Param1にはSYNCコマンドのACKである事を示す0x0Dを入れ、Param2には受信したACKと同じParam2の値を入れます。
また、接続確立直後はカメラの輝度が安定しないため、1~2秒待つか、高解像度で撮影を行う前に低解像度で撮影すると安定するそうです。
1.SYNC(0xAA 0x0D 0x00 0x00 0x00 0x00)を送信
※適当に待ち時間を挟みながら最大60回送信する。それでも反応がなければエラー。
2.ACK(0xAA 0x0E 0x0D nnn 0x00 0x00)を受信
3.ACK(0xAA 0x0E 0x0D nnn 0x00 0x00)を送信
※2.のACKで受け取った値(nnn)を入れる
[2] Initialコマンドを送信する
ここでカラータイプや解像度を指定します。
例えば、カラータイプをJPEG(0x07)、プレビュー解像度を80x60(0x01)、JPEG解像度を640x480(0x07)にする場合は、以下のコマンドを送信します。
1.Initial(0xAA 0x01 0x00 0x07 0x01 0x07)を送信
2.ACK(0xAA 0x0E 0x01 0x00 0x00 0x00)を受信
[3] SetPackageSizeコマンドを送信する
JPEGデータはパッケージという単位でデータを受け取るため、そのサイズを指定します。
パッケージサイズが大きいほど効率よく転送できますが、非力なマイコンでは用意できるバッファサイズが限定されると思いますので適当に調整します。
尚、サイズはデフォルトは64バイトで最大512バイトまでです。
仮にパッケージサイズを256バイトに設定してもヘッダーとフッターが付く(6バイト)ため、実際に取得できる画像データは250バイトになります。
1.SetPackageSize(0xAA 0x06 0x08 LB HB 0x00)を送信
2.ACK(0xAA 0x0E 0x06 0x00 0x00 0x00)を受信
LBとHBはバッケージサイズのLowByteとHighByteを入れます。(WORD型を上位バイト、下位バイトに分解)
サイズを変数BUFFSIZEに指定したとすると、
LBは BUFFSIZE&0xFF
HBは BUFFSIZE/256
となります。
[4] Snapshotコマンドを送信し、画像を撮影する
JPEGは圧縮してデータをもらうため、Compressed Picture(0x00)を指定します。
1.Snapshot(0xAA 0x05 0x00 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x05 0x00 0x00 0x00)を受信
[5] GetPictureコマンドを送信し、データを受け取る準備をする
PictureTypeはSnapshotPicture(0x01)を指定します。
GetPictureコマンドを送信すると、カメラ側からACKコマンドに続いて最初のDataコマンドが送信されてきます。
この中には撮影画像のデータサイズが入っていますので取得しておきます。
1.GetPicture(0xAA 0x04 0x01 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x04 0x00 0x00 0x00)を受信
3.Data(0xAA 0x0A 0x01 LB HB 0x00)を受信
LBとHBは画像データサイズのLowByteとHighByteが入っていますので、以下のように変換してサイズを取得します。
JPEGFILESIZE = HB*256+LB
[6] 全てのデータを受信するまでACKコマンドを送信する
マイコン側がACKを送信すると、カメラ側は1パッケージを送信してきます。
送信するACKにはパッケージIDを指定します。
受信するパッケージの構造は、
ID(2bytes) ............ パッケージID
DataSize(2bytes) ...... パッケージ内に含まれるデータサイズ
ImageData(DataSize) ... イメージデータ
VerifyCode(2bytes) .... チェックサム
です。
基本的に指定したパッケージサイズ分が返ってきますが、最後のパッケージは指定サイズよりも少ない可能性があります。
1.パッケージID、受信サイズを初期化 pid=0 total=0
2.ACK(0xAA 0x0E 0x00 0x00 LB HB)をカメラへ送信
LBとHBはパッケージIDのLowByteとHighByteを入れます。
具体的には 0xAA 0x0E 0x00 0x00 pid&0xFF pid/256 となります。
3.パッケージを受信する
id = C328RX_getByte()+C328RX_getByte()*256;
ds = C328RX_getByte()+C328RX_getByte()*256;
for( i = 0; i < ds; i++ ){
data[i] = C328RX_getByte();
}
verify = C328RX_getByte()+C328RX_getByte()*256;
total += ds;
pid++;
ここで受け取ったデータ(data)をSDカードに保存する等の処理を行います。
もし、チェックサムと合わなければ受信エラーとして同じパッケージIDを指定して再送してもらう事もできると思います。
※C328RX_getByte()はC328カメラから1バイト受信する関数
4.全て受信していなければ(total < JPEGFILESIZE)、2.へ戻る
[7] 全て受信したら受信完了ACKを送信する
1.ACK(0xAA 0x0E 0x00 0x00 0xF0 0xF0)を送信
[8] PowerOffコマンドを送信してカメラをスリープさせる(必要に応じて)
スリープした後に再び撮影する場合は、SYNCコマンドでカメラを起こす必要があります。
1.PowerOff(0xAA 0x09 0x00 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x09 0x00 0x00 0x00)を受信
これで完了です
【8-bitGrayScale画像を取得する方法】
[1] SYNCコマンドを送信してカメラとの接続を確立する
JPEGと手順は同じです。
[2] Initialコマンドを送信する
例えば、カラータイプを8-bitGrayScale(0x03)、プレビュー解像度を80x60(0x01)にする場合は、以下のコマンドを送信します。
1.Initial(0xAA 0x01 0x00 0x03 0x01 0x00)を送信
2.ACK(0xAA 0x0E 0x01 0x00 0x00 0x00)を受信
[3] Snapshotコマンドを送信し、画像を撮影する
非圧縮で画像データをもらうため、Uncompressed Picture(0x01)を指定します。
1.Snapshot(0xAA 0x05 0x01 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x05 0x00 0x00 0x00)を受信
※非圧縮ではパッケージ単位でやりとりしないため、SetPackageSizeコマンドは必要ありません。
[4] GetPictureコマンドを送信し、画像データを受け取る準備をする
JPEGと手順は同じです。
データサイズは非圧縮のため取得しても計算(80x60=4800)しても同じです。
Dataコマンドを受信した直後に画像データが一気に送信されてきます!
[5] 全ての画像データを受信する
JPEGと違い、非圧縮の生データがデータサイズ分だけ連続で送信されてきます。
パッケージ単位ではありません!
for( i = 0; i < 80x60; i++ ){
data[i] = C328RX_getByte();
}
※2/8追記 コードを追加しました。
注意しなければならないのは、低速(9600bps以下)で通信する場合や受信バッファが十分に取れるマイコン環境なら問題ありませんが、バッファが少ない環境では、連続してデータが送信されくるため、データを外部メモリ(SDやEEPROM)に記録している間に受信バッファが満杯になり、取りこぼしが発生する可能性があります。
このあたりは上手に設計する必要がありそうです。JPEGと同じようにパッケージ単位の手法も用意してほしかった。
JPEGのパッケージ単位でやりとりの場合は、次の取得データのタイミングをマイコン側で制御できるため、最高速で通信が可能です。
[6] 全て受信したら受信完了ACKを送信する
1.ACK(0xAA 0x0E 0x0A 0x00 0x00 0x00)を送信
これで完了です
今回はコンパイラをImageCraft用に変更したついでに、いつものSG12864Aに表示するようにしてみました。
JPEGデコードはPSoCではかなり厳しいので、80x60ドットの8bitGrayScaleで撮影しています

80x60の8bitのため、データサイズは4800バイトになります。この領域を確保するのは、やはりPSoCでは厳しい

とりあえず動画です。
プロジェクトと回路図は後日UPします。
そのかわりに、カメラ(C328-7640)について書きます。
かつてない程の長文となりましたwwwww
C328-7640は、非力なマイコンでもJPEG画像が取得できる便利なカメラです。
値段は5000円ぐらいと、ちょっと高いなぁと思いますが、SDカードやXBee等の無線装置と組み合わせると簡単に定点観測やスパイカメラwが作れます。
解像度は低いですが、非圧縮形式でも画像(グレースケール、16bitカラー)を取得できるため、ロボットの目としても使えそうです。
カメラとのインターフェイスもUARTで制御できるため比較的簡単ではないかと思います。
しかし、簡単とは言え、やはりそれなりの手順は必要となります。
今回は備忘録として、JPEG画像の取得と8bitグレースケール画像の取得についてまとめてみました。
短時間でまとめたため読みにくい部分があるかと思いますが、そこは脳内補間とグーグル先生で自己解決してくださいw
それから僕の書く記事の大前提ですが、内容は保証するものではありません。
必ずデータシートを読んで、自分で確認してください。
【C328-7640仕様】
動作電圧: 3.0V~3.6V
消費電流: 60mA~
通信: UART
通信速度: 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200bps
色数: Gray Scale(2bit, 4bit, 8bit)[YCbCrのY(輝度)のみ]、Color(12bit[444:RGB], 16bit[565:RGB])、JPEG
解像度: 80x60, 160x120
JPEG解像度: 80x64, 160x128, 320x240, 640x480
SLEEP: 有り(100μA)
【C328-7640のコマンド送受信】
[1] C328とは基本的に専用のコマンドを使用してやりとりします
やりとりのコマンドはコマンドIDとオプションパラメータが4つの計6バイトが1セットになります。
例えばSYNCコマンドの場合、
0xAA 0x0D 0x00 0x00 0x00 0x00
(ID Number Param1 Param2 Param3 Param4)
と、先頭から順に送信します。
void sendSyncCmd()
{
C328TX_putByte( 0xAA );
C328TX_putByte( 0x0D );
C328TX_putByte( 0x00 );
C328TX_putByte( 0x00 );
C328TX_putByte( 0x00 );
C328TX_putByte( 0x00 );
}
って感じです。
UART送受信はバイナリモードで行う必要があります。テキストモードでは制御できません。
※C328TX_putByte()は、C328カメラに1バイト送信する関数です。
[2] コマンドを送信し、カメラ側が受領するとACKコマンドを返してきます
つまり、コマンド送信→ACK受け取り→次のコマンド送信→ACK受け取り‥‥‥
という感じです。
ACK送出はカメラ側だけではなく、マイコン側から送信する場合もあります。
カメラから送られてくるACKはパラメータで必要なもの(SYNCとか画像サイズ等)以外は、とりあえず6バイト読み込むだけでパラメータの内容は考慮しなくても良さそうです。
void getACK( void )
{
for( i = 0; i < 6; i++ ) C328RX_getByte();
}
って感じ。
[3] コマンドの送信は速すぎると失敗する場合があります
その場合はコマンド送信前に適度(20msぐらい)のwaitを入れてあげるとうまくいく場合があります。
【JPEG画像を取得する方法】
[1] SYNCコマンドを送信してカメラとの接続を確立する
通信速度をカメラ側がスキャンして自動的に認識するため、何回か送信してやる必要があります。
データシートには、だいたい25回ぐらいで確立できると書いてあります。
60回送信しても応答がなければ何らかの問題が発生していると考えられます。
一回接続が確立するとボーレート変更コマンドで指定しないかぎり通信速度は維持されます。
カメラ側からACKコマンドを受信したらマイコン側からもACKコマンドを送信し、手続きを完了します。
この時、Param1にはSYNCコマンドのACKである事を示す0x0Dを入れ、Param2には受信したACKと同じParam2の値を入れます。
また、接続確立直後はカメラの輝度が安定しないため、1~2秒待つか、高解像度で撮影を行う前に低解像度で撮影すると安定するそうです。
1.SYNC(0xAA 0x0D 0x00 0x00 0x00 0x00)を送信
※適当に待ち時間を挟みながら最大60回送信する。それでも反応がなければエラー。
2.ACK(0xAA 0x0E 0x0D nnn 0x00 0x00)を受信
3.ACK(0xAA 0x0E 0x0D nnn 0x00 0x00)を送信
※2.のACKで受け取った値(nnn)を入れる
[2] Initialコマンドを送信する
ここでカラータイプや解像度を指定します。
例えば、カラータイプをJPEG(0x07)、プレビュー解像度を80x60(0x01)、JPEG解像度を640x480(0x07)にする場合は、以下のコマンドを送信します。
1.Initial(0xAA 0x01 0x00 0x07 0x01 0x07)を送信
2.ACK(0xAA 0x0E 0x01 0x00 0x00 0x00)を受信
[3] SetPackageSizeコマンドを送信する
JPEGデータはパッケージという単位でデータを受け取るため、そのサイズを指定します。
パッケージサイズが大きいほど効率よく転送できますが、非力なマイコンでは用意できるバッファサイズが限定されると思いますので適当に調整します。
尚、サイズはデフォルトは64バイトで最大512バイトまでです。
仮にパッケージサイズを256バイトに設定してもヘッダーとフッターが付く(6バイト)ため、実際に取得できる画像データは250バイトになります。
1.SetPackageSize(0xAA 0x06 0x08 LB HB 0x00)を送信
2.ACK(0xAA 0x0E 0x06 0x00 0x00 0x00)を受信
LBとHBはバッケージサイズのLowByteとHighByteを入れます。(WORD型を上位バイト、下位バイトに分解)
サイズを変数BUFFSIZEに指定したとすると、
LBは BUFFSIZE&0xFF
HBは BUFFSIZE/256
となります。
[4] Snapshotコマンドを送信し、画像を撮影する
JPEGは圧縮してデータをもらうため、Compressed Picture(0x00)を指定します。
1.Snapshot(0xAA 0x05 0x00 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x05 0x00 0x00 0x00)を受信
[5] GetPictureコマンドを送信し、データを受け取る準備をする
PictureTypeはSnapshotPicture(0x01)を指定します。
GetPictureコマンドを送信すると、カメラ側からACKコマンドに続いて最初のDataコマンドが送信されてきます。
この中には撮影画像のデータサイズが入っていますので取得しておきます。
1.GetPicture(0xAA 0x04 0x01 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x04 0x00 0x00 0x00)を受信
3.Data(0xAA 0x0A 0x01 LB HB 0x00)を受信
LBとHBは画像データサイズのLowByteとHighByteが入っていますので、以下のように変換してサイズを取得します。
JPEGFILESIZE = HB*256+LB
[6] 全てのデータを受信するまでACKコマンドを送信する
マイコン側がACKを送信すると、カメラ側は1パッケージを送信してきます。
送信するACKにはパッケージIDを指定します。
受信するパッケージの構造は、
ID(2bytes) ............ パッケージID
DataSize(2bytes) ...... パッケージ内に含まれるデータサイズ
ImageData(DataSize) ... イメージデータ
VerifyCode(2bytes) .... チェックサム
です。
基本的に指定したパッケージサイズ分が返ってきますが、最後のパッケージは指定サイズよりも少ない可能性があります。
1.パッケージID、受信サイズを初期化 pid=0 total=0
2.ACK(0xAA 0x0E 0x00 0x00 LB HB)をカメラへ送信
LBとHBはパッケージIDのLowByteとHighByteを入れます。
具体的には 0xAA 0x0E 0x00 0x00 pid&0xFF pid/256 となります。
3.パッケージを受信する
id = C328RX_getByte()+C328RX_getByte()*256;
ds = C328RX_getByte()+C328RX_getByte()*256;
for( i = 0; i < ds; i++ ){
data[i] = C328RX_getByte();
}
verify = C328RX_getByte()+C328RX_getByte()*256;
total += ds;
pid++;
ここで受け取ったデータ(data)をSDカードに保存する等の処理を行います。
もし、チェックサムと合わなければ受信エラーとして同じパッケージIDを指定して再送してもらう事もできると思います。
※C328RX_getByte()はC328カメラから1バイト受信する関数
4.全て受信していなければ(total < JPEGFILESIZE)、2.へ戻る
[7] 全て受信したら受信完了ACKを送信する
1.ACK(0xAA 0x0E 0x00 0x00 0xF0 0xF0)を送信
[8] PowerOffコマンドを送信してカメラをスリープさせる(必要に応じて)
スリープした後に再び撮影する場合は、SYNCコマンドでカメラを起こす必要があります。
1.PowerOff(0xAA 0x09 0x00 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x09 0x00 0x00 0x00)を受信
これで完了です

【8-bitGrayScale画像を取得する方法】
[1] SYNCコマンドを送信してカメラとの接続を確立する
JPEGと手順は同じです。
[2] Initialコマンドを送信する
例えば、カラータイプを8-bitGrayScale(0x03)、プレビュー解像度を80x60(0x01)にする場合は、以下のコマンドを送信します。
1.Initial(0xAA 0x01 0x00 0x03 0x01 0x00)を送信
2.ACK(0xAA 0x0E 0x01 0x00 0x00 0x00)を受信
[3] Snapshotコマンドを送信し、画像を撮影する
非圧縮で画像データをもらうため、Uncompressed Picture(0x01)を指定します。
1.Snapshot(0xAA 0x05 0x01 0x00 0x00 0x00)を送信
2.ACK(0xAA 0x0E 0x05 0x00 0x00 0x00)を受信
※非圧縮ではパッケージ単位でやりとりしないため、SetPackageSizeコマンドは必要ありません。
[4] GetPictureコマンドを送信し、画像データを受け取る準備をする
JPEGと手順は同じです。
データサイズは非圧縮のため取得しても計算(80x60=4800)しても同じです。
Dataコマンドを受信した直後に画像データが一気に送信されてきます!
[5] 全ての画像データを受信する
JPEGと違い、非圧縮の生データがデータサイズ分だけ連続で送信されてきます。
パッケージ単位ではありません!
for( i = 0; i < 80x60; i++ ){
data[i] = C328RX_getByte();
}
※2/8追記 コードを追加しました。
注意しなければならないのは、低速(9600bps以下)で通信する場合や受信バッファが十分に取れるマイコン環境なら問題ありませんが、バッファが少ない環境では、連続してデータが送信されくるため、データを外部メモリ(SDやEEPROM)に記録している間に受信バッファが満杯になり、取りこぼしが発生する可能性があります。
このあたりは上手に設計する必要がありそうです。JPEGと同じようにパッケージ単位の手法も用意してほしかった。
JPEGのパッケージ単位でやりとりの場合は、次の取得データのタイミングをマイコン側で制御できるため、最高速で通信が可能です。
[6] 全て受信したら受信完了ACKを送信する
1.ACK(0xAA 0x0E 0x0A 0x00 0x00 0x00)を送信
これで完了です
