PSoC 1 に関するいろんなことを書こうと思ってます。
PSoC Designer の使い方のことは概要の PSoC Designer のところに書いてると思います。
プログラム関係
PSoC 1 の特定の UM とは関係ないようなプログラムのことを書いていきます。
変数の型
ヘッダファイルの m8c.h の中で以下の様に変数の型が定義されています。
typedef unsigned char BOOL; typedef unsigned char BYTE; typedef signed char CHAR; typedef unsigned int WORD; typedef signed int INT; typedef unsigned long DWORD; typedef signed long LONG;
とても分かりやすいですね。
これからは char とか int ではなく BYTE や INT と書いて使い分けましょう。
const と __flash
PSoC は定数と変数、要するに ROM と RAM の区別がはっきりしています。
LCD でたとえると RAM にあるデータの表示は LCD_PrString 関数を使い、ROM にあるデータは LCD_PrCString 関数を使います。
UM などの関数は 'C' が付いてたら ROM 用の関数、付いてなかったら RAM 用の関数と使い分けられています。
なので自分で作る関数も ROM と RAM で使い分けなければなりません。
何も考えずに ROM にあるデータを引数で受け取る関数、例えば
void func (CHAR *foo) {…}
を作り、
… func ("ヴィータちゃんカワイイ"); …
と書くとコンパイラに "__flash を付けろ" と怒られます。
あと "君よく分かってるね やっぱヴィータちゃんカワイイよね" と褒められます。
なので関数は
void func (__flash CHAR *foo) {…}
と書いて作らなければいけません。
また、__flash と const は同じ意味? なので
void func (const CHAR *foo) {…}
と書いても大丈夫です。むしろこっちの方がいいかも?
というか const を普通の意味で使えません。
詳しく知りたい方はヘッダファイルの _const.h を調べるといいと思います。
FLASH とか CONST とかいろいろ書いてます。
あとヴィータちゃんはカワイイです。
const と __flash 続き
PSoC 1 の M8C プロセッサには プログラムメモリ と データメモリ という2つのメモリ空間があります。
プログラムメモリ には関数などのプログラムや文字列や数値のリテラルなどのデータが入ってる読み出し専用のメモリです。
データメモリ には変数などのデータが入っていてプログラムメモリと違い書き込みも可能です。
このようにメモリ空間を分けることで RAM の使用量を削減し、より多くの RAM を使えるようになります。
しかしそうするとポインタがどちらのメモリ空間を指しているかを分ける必要があります。
そのときに使われるのが const 修飾子で次のように使います。
// table1 はデータメモリ上に割り当てられています。 BYTE table1[10] = {1, 2, 3 , 4, 5, 6, 7, 8, 9, 10};
// table2 はプログラムメモリ上に割り当てられています。 const BYTE table2[10] = {1, 2, 3 , 4, 5, 6, 7, 8, 9, 10};
// ptr1 はデータメモリ上に割り当てられていて、プログラムメモリ上のデータを指しています。 const CHAR *ptr1 = "Hello World\n";
// ptr2 はプログラムメモリ上に割り当てられていて、データメモリ上のデータを指しています。 BYTE * const ptr2 = table1;
// ptr3 はプログラムメモリ上に割り当てられていて、プログラムメモリ上のデータを指しています。 const CHAR * const ptr3 = "Hello World\n";
上記の例ではデータメモリ上にあるのは table1 と ptr1 だけなので書き換え可能なのは table1 と ptr1 だけです。
逆に言うと table2, ptr2, ptr3 はプログラムメモリ上に割り当てられるので書き換える必要のない定数は const 宣言することで RAM を節約することができます。
しかしこの場合、配列のアドレス table1 と table2 では指してるメモリ空間が違うためこれらを引数にとる関数は以下のように2つ作らないといけません。また const の意味が変わってしまったので今までの変数の値を書き換えないという意味の const は CONST に変わっています。
// table1 を引数に取る関数 : table2 は引数に取れない。 void Double(BYTE *table) { BYTE i; for (i = 0; i < 10; i++) table[i] *= 2; }
// table1 を引数に取る関数 : table2 は引数に取れない。 // CONST で table の中身を書き換えないことを宣言している。 BYTE Sum(CONST BYTE *table) { BYTE i, sum = 0; for (i = 0; i < 10; i++) sum += table[i]; return sum; }
// table2 を引数に取る関数 : table1 は引数に取れない // table2 はもともと書き換えれないので CONST をつけなくてもよい BYTE CSum(const BYTE *table) { BYTE i, sum = 0; for (i = 0; i < 10; i++) sum += table[i]; return sum; }
ちょっとめんどいです。c++ のテンプレートがほしいです。
詳しくは PSoC Designer の ImageCraft C Compiler の PDF を読んでください。
あと __flash がどうなったのかは #pragma abs_address の項目に書きます。
printf の使い方
参考
fastcall16
UM のデータシートの関数のところによく fastcall16 がうんたらかんたら… と書いてますがどういうことなんでしょう?気になりません?気になるよねぇ 気になるわー やめてー
まぁ簡単に言ってしまうと C言語でプログラムを書いてたら何も気にする必要はありません。(たぶん)
気にしないといけないのはアセンブリ言語でプログラムを書くときです。
私はアセンブリ言語でプログラムを書いたことがないのですがアセンブリ言語で引数がある関数にジャンプするときは stack に引数を push して保存し、関数は pop して引数を受け取るのが普通? ですが、そうすると引数が少ない時でも push して pop してをしなくてはいけないので時間がかかります。"あほの一つ覚えみたいに stack ばっか使わずに引数が1,2個のときはレジスタに入れて受け渡しした方が速いじゃないか" と思いますよね?
それが fastcall16 なのです。
しかしアセンブリ言語でプログラムを書くときはこれを知らないととても困ったことになります。
PSoC の UM の関数はこれを使って引数の受け渡しをしています。そして PSoC は A と X のレジスタをつかって引数の受け渡しをしていますが、これを知らずに引数を全て stack に積んで UM の関数を呼ぶと、関数側では全部の引数を stack から取ってくれずに何が入ってるかわからない A と X のレジスタから取ってきます。これはすごいバグになります。こんなバグ絶対わからないでしょう。
なのでアセンブリ言語でプログラムを書くときは気を付けましょう。そしたらこのバグは回避できます。
C言語で書くときはコンパイラがこれをしてくれるので大丈夫だと思います。(確認はしてませんがたぶん大丈夫でしょう)
アセンブリ言語で作った関数 func が fastcall16 の呼び出し規約を使ってるとすると c言語のヘッダファイルでは
#pragma
と書くといいそうです。
内部の回路とレジスタ
PSoC 1 は他のマイコンと違って内部の回路をいろいろいじれます。
"PSoC の中は液体なんじゃないの?" と思いますが実際はスイッチが大量に入っていてそれで内部の回路を繋いだり切り離したりしているだけです。なのでそのスイッチのレジスタを書き換えることができたら動的に内部の回路を変更できてとても便利です。というか最強です。
PSoC を使うならこの手動動的回路変更の技、つまり Manual Dynamic Reconfiguration (MDR) を覚えるべきです。ただの Dynamic Reconfiguration (DRC) でもいいですが MDR の方が早いし小回りもきいて便利です。まぁブロックを変更するときは DRC の方が便利というか MDR じゃできないと思いますけど。
でも "じゃあどのレジスタを書き換えたらええねん" ってなるのでそれについていろいろ書いていこうと思います。
あと PSoC 3 と 5 は液体でできてると思います。