PSoCプログラミング・メモ
by K.I
Index
CypressのPSoCのプログラムのメモ。気が付いたこと、分かったことを書き留めておこう。
[top]
普通はsprintfとか使って、あまり考えずに出来るが、ここらへんのライブラリは未だ完全じゃないみたいだ。
- 代わりにitoa,ltoa,ftoaとかが用意1されている。
- 整数値を10進数として、文字列に変換してLCDに表示する例
ちょっと面倒だなぁ。フォーマットも出来ないし。あとstdlib.hのincludeが必要。
- Junk-Boxさんの方法を参考に右詰め出力を考えた。→あんまりスマートじゃないかなぁ(さらにstring.hのincludeが必要になるし、コンパイルしたらどうなることか。。)
cstrcpy(line," "); //16文字空白(出力幅8文字まで)
itoa(line+8,123,10); //123を10進数として文字変換
LCD_1_PrString(line+8-5+strlen(line+8));//幅5文字として右詰め出力
- Junk-Boxさんが、PSoC版printfを 公開されたので、これを使った方が良いかもしれない。
- LCDを使ってると数値の右詰め表示が多いので、マクロにした。
- あまりキレイじゃない2けど、printfを使うまでもない時に
#include <string.h>
#include <stdlib.h>
//---- 右詰めn桁10進出力マクロ(出力幅8文字まで)
//---- テンポラリで16文字分のバッファを使用します
#define lcd_print_d(buf,n,data) do { \
cstrcpy((buf)," "); \
itoa((buf)+8,(data),10); \
LCD_1_PrString((buf)+8-(n)+strlen((buf)+8)); \
} while(0)
char line[16];
lcd_print_d(line,2,count); // MACRO printf("%2d",count);
- まず、前述のJunk-Boxさんのページから、 lcdtest.lzhをダウンロード。
- 解凍して、cstdef.h,ctype.c,ctype.h,pr.c等を、自分のプロジェクトへコピー。
- PICのCCSCのprintfは、例えばLCDやUART用の一文字出力を切替えて、使えるようになってる。
- で、printfの定義を以下のように、出力ルーチンを指定出来るように変更。
- まぁ、出来るだけ変更せずに済ましたのでいい加減ですが、、一応パラメータが増えるので、関数名をxprintfに変更。
//void printf(const unsigned char *string, unsigned int arg) // 出力ルーチンを指定出来るように変更
void xprintf( void(*outfunc)(unsigned char c), const unsigned char *string, unsigned int arg)
{
print_chr = outfunc; // 関数ポインタをコピー
prtout(string, &arg);
}
- これで、printfの1文字出力ルーチンを変更出来るようになったので、例えば以下のように一文字出力ルーチンを用意しておく。
void txout(unsigned char c)
{
int i;
switch (c) {
case '\f': UART_1_PutCRLF(); break; // carriage return
case '\n': UART_1_PutCRLF(); break; // carriage return
case '\b': break; // dummy
case '\r': break; // dummy
default: UART_1_PutChar(c); break; // normal character
}
}
void lcdout(unsigned char c)
{
int i;
switch (c) {
case '\f': LCD_1_Control(0x01); // clear screen (1st line home)
LCD_1_Delay50uTimes(40); break; // (Execution delay 2ms)
case '\n': LCD_1_Position(1,0); break; // carriage return (2nd line home)
case '\b': LCD_1_Control(0x10); break; // backward
case '\r': break; // dummy
default: LCD_1_WriteData(c); break; // normal character
}
}
xprintf(lcdout,">Data:%d",xxx); // LCDへ出力
xprintf(txout,">Data:%d",xxx); // UARTへ出力
やっぱり、何にでもprintf出来るのは便利だな〜。JUNK-BOXさんに感謝!
- あと蛇足だが、うまくprintf出来ない時は、フォーマット文字列の文字数が20文字を超えていないことをチェック。
- 超えていたら、出来るだけ分割して出力するようにする。どうしてもという時は、pr.cのtbufの大きさをちょっと大きく3する。
M8Cはハーバードアーキテクチャなので、プログラムメモリとデータメモリが分かれている。
- プログラムメモリは、基本的にはReadOnlyだが容量が大きい。
- データメモリは、高速に書換えが可能だが、容量は小さい。
プログラムでは、テーブルを良く使うが、書換える必要がない固定のテーブルは結構良く使われる。この場合はデータメモリは勿体無いので、プログラムメモリ上に置いた方が良い。
const int table[] = { 1, 2, 3, 4, 5 };
const char ctable[] = "0123456789ABCDEF";
- 逆に書換えをする場合で、とりあえず値を設定しておきたい場合には、例えば以下のように4する。
char ctable[17] = "0123456789ABCDEF";
或は
cstrcpy(ctable,"12345");
定数の場合、strcpyは使えないのでcstrcpyを使う必要がある。
1あまり聞かない関数だなぁ。atoiとかは良く使うけど。
2それに冗長だ。。。
3所詮、1チップマイコンなので欲張らないように。。
4const指定が無い場合はデータメモリ上にStringが配置されるはずだが、これはもしかすると間違ってるかもしれない。(061105追記)
[top]
- 単純なI/Oポートの操作方法が分かんなかったので、メモっておこう。
間違って理解してるかもしれないけど。。。
- I/Oポートに直接関係ない設定レジスタのメモ書きも含む
- ちなみに、レジスタの詳しい設定方法については、PSoC DesignerのHelpメニューから、
- Documentation...を選択し、Technical Reference Manualsの、Technical Reference Manual.pdfを参照すれば良い。
汎用I/Oポートのモード設定
DM2 | DM1 | DM0 | Mode | Drive |
0 | 0 | 0 | Hドライブ、Lは抵抗ドライブ | Pull Down |
0 | 0 | 1 | H、Lともドライブ(通常は出力モード) | Strong |
0 | 1 | 0 | High-Z(通常は入力モード) | High Z |
0 | 1 | 1 | Hは抵抗ドライブ、Lドライブ | Pull Up |
1 | 0 | 0 | Pch オープンDrain(Hドライブのみ) | Open Drain High |
1 | 0 | 1 | H、LともSLOWドライブ5 | Strong Slow |
1 | 1 | 0 | High-Z(アナログモード) | High Z Analog |
1 | 1 | 1 | Nch オープンDrain (Lドライブのみ) | Open Drain Low |
- 110(アナログ)以外の時は、どのモードでもPRTxDRでピン状態を読む事が出来る
ここをアクセスすることで、プログラムから直接I/Oポートを読み書き出来る。
- 直接I/Oポートを操作する例(実はPRTxDRの処理はこれじゃマズイ6)
PRT0DM0 |= 0x03; // Port0のbit0〜1を出力ポートに設定
PRT0DM1 &= ~0x03; // (mode = 001)
PRT0DM2 &= ~0x03;
PRT0DR &= ~0x03; // Port0のbit0〜1をクリアして、(ダメ)
PRT0DR |= 0x01; // Port0のbit0〜1を'01'に設定(これもダメ)
while (TRUE) {
PRT0DR ^= 0x03; // Port0のbit0〜1を反転(これもダメです)
}
- なんか、いまいち面倒だなぁ。もっとすっきり書けないものか。。。
- PRTxDMxについては、普通はデバイスエディタで設定する方が良さそう
- PRTxDRを読んだ場合、それは端子の状態で、出力ポートのデータを読んでいるワケじゃない。
- だから、現在の出力状態を元に、出力状態を変化させたい場合は前述のようにやらない方が良い。(090825追記)
- 出力用の変数を用意しておいて、それを出力するか、Shadowレジスタを使う7と良い。
PRT0DR = (port0 = 0x00); // Port0の初期値を出力
PRT0DR = (port0 &= ~0x03); // Port0のbit0〜1をクリアして、
PRT0DR = (port0 |= 0x01); // Port0のbit0〜1を'01'に設定
while (TRUE) {
PRT0DR = (port0 ^= 0x03); // Port0のbit0〜1を反転
}
- でも、あまりキレイじゃないコードだなぁ。
- 或いは、出力値をテーブルか定数にしておいて、順番に出力するとか。→このやり方をすることが多いかも。
- といっても、Shadowレジスタのモジュールを使うんじゃなくて、勝手に使う感じかな。
- 未だに、Designer4.4を使ってるので、少し違うところもあるかもしれないけど、Designer5でコンパイルしても動くので多分大丈夫のはず。
- PSoCのモジュール、例えばLCDを使った時、ポートが1つ余る。
- でも、これを使おうとしても勝手にポートが書き換えられてしまう。
- LCDモジュールが内部的に使っているポート出力用の変数を使ってしまえば問題ない。
- 実際、これはグローバル変数になってるので自由に使える。
- メインのプログラムのグローバル変数として、以下のように定義しておく。
BYTE Port_0_Data_SHADE;
extern BYTE Port_2_Data_SHADE;
ポート入出力を行うPSoCのモジュールは、Port_x_Data_SHADEという名前で、グローバル変数を定義している。
- だから、externすれば、同じ変数にアクセス出来るという単純な話。
- これに気が付いてからは、ポート用のグローバル変数を必ず定義するようにして、
- コンパイル時に、多重定義のエラーが出たらexternにして共用するようにしている。
- 当然、必要なビット以外は弄らないように注意する必要がある。
Ex. Port_2_Data_SHADE |= 0x10;
PRT2DR = Port_2_Data_SHADE;
ちょっと面倒だけど、I/Oポートのアクセスは、このグローバル変数を介して行ったほうが良さそうだ。
- Shadowレジスタのモジュールって、実は使ったこと無いんだけど、結局は同じことをしてるんだと思う。
- このレジスタをセットすると、グローバルバスがGPIOポートに接続される。
- PRTxGSレジスタをセット(BYP=1)すると、
- グローバルアウトプットバスの値が、GPIOポートに出力され、
- PRTxDM=001(アナログ入力)以外は、GPIOポートの入力がグローバルインプットバスに接続される
- PRTxGSレジスタをクリア(BYP=0)すると、PRTxDRの値が出力される
- PRTxGSレジスタの設定に関係なく、PRTxDM=001(アナログ入力)以外は、GPIOポートの状態は、PRTxDRから読むことが出来る
PRT0GS |= 0x02; // Port0のbit1に、グローバルバス(GOO1 or GOE1)を出力
PRT0GS &= ~0x02; // Port0のbit1に、PRT0DRのbit1を出力
- 普通はグローバルバスに接続するとBYP=1に設定されるが、
- 強制的に値を設定したい時などに、一時的にPRTxGSをコントロールして、PRTxDRを出力させることが出来る。
- 逆に、必要な時だけグローバルバスに、接続するような使い方も出来る。
- 切換えた場合、元に戻すのを忘れないようにすること!
- このレジスタで、GIO,GIEバスからの入力を、同期にするか非同期にするか変更できる。
- 同期にすると1クロック分の遅延を作れるので、微妙な遅延を調整したい時に使える。
- ユーザモジュールDIGBUFのバッファ2も同様に、同期・非同期を切換えられる
- これはDxBxxOUの上位2bitが、00→nosync、01→sync
- 普通はDesignerの設定すれば良いので、
- プログラムで弄らなければならないことはあまり無いと思う。
- このレジスタは、アナログ出力バッファのコントロール用だが、
- 上位2ビット、Bit7,6が、Col1MUX,ACol2MUXで、アナログブロックの入力ColumnMUXの切替えが可能
- AMUX4はモジュールで切替えられるけど、ColumnMUXはモジュールが無いので、このレジスタで切り替える。
- あとは本来のアナログ出力バッファのコントロールで、
- Bit5〜2が、アナログ出力バッファのEnable/Disable、
- Bit1が、アナログ出力バッファのBypassモード(アナログ出力バッファを使わないということかな?)
- Bit0が、アナログ出力バッファのHighPowerモード
- ASCブロックのモジュレータ機能をコントロールする信号を設定するレジスタ
- 設定出来るのは、GOE0,GOE1,Row0BroadCast,AnalogColumnComparator0〜3
- RefをGNDにした場合、ASCブロックのA入力の極性を反転する信号の設定が出来る。
- A入力の信号と、コントロールする信号の乗算となるので、この応用に一つとしてヘテロダイン動作をさせることが出来る。
- Ref側をGNDにに落とさずに別入力にした場合、A入力とRef入力の加算・減算を切り替える信号となる。
- 例えば、コンパレータ出力をコントロールにして、Refにヒステリシス電圧/2を入れると、ヒステリシスコンパレータ動作になる。
- このレジスタは、PSoCのSCFならではの特徴的な設定が出来るので、是非使いこなしたい。
- DesignerのGUIでは切替えられないので、プログラムで設定する必要がある。
- デジタルブロックの出力についているLUTの切換え
- AND,OR,XORなど、いろいろ選ぶことができる
- 例えば、29466の場合、4個のデジタルブロックが4列あって、
- 上から、RO0,RO1,RO2,RO3の4つの列(ROW)に、RDI0,RDI1,RDI2,RDI3が対応している
- さらに列毎の4つの出力の、上の2つをLT0、下の2つをLT1で設定する
- 1つめがLT0の下位ニブル、2つめがLT0の上位ニブル、3つめがLT1の下位ニブル、4つめがLT1の上位ニブルに対応する
- 例えば、デジタルブロックの一番上の列はRDI0なので、
- その上から3番目の出力は、RDI0LT1の下位ニブルを設定すれば良い
- 要するに、デジタルブロックの右側の出力の部分にANDとかORとか設定できる。
- これは普通、PSoC DesignerのChipエディタで設定する。
- これは、そのままI/Oピンに出力しても良いが、
- InterConnectで、デジタルブロックの入力に戻して使うことも出来る。
- これを使って、簡単なロジックを構成することも可能8。
- CMP_CR0の、bit4〜 7がCBUS0〜3に対応している
- 但し、コンパレータバスの出力のLUT9の状態によって変化するので注意
- コンパレータバスの出力のLUTの切り替えは、ALT_CR0,ALT_CR1を設定して行う。
- ARF_CRの、bit3〜5がアナログの基準電圧のコントロールになっている
REF[2:0] | AGND | RefHi | RefLo |
000b | Vdd/2 | Vdd/2+Bandgap | Vdd/2−Bandgap |
001b | P2[4] | P2[4]+P2[6] | P2[4]−P2[6] |
010b | Vdd/2 | Vdd/2+Vdd/2 | Vdd/2−Vdd/2 |
011b | 2xBandgap | 2xBandgap+Bandgap | 2xBandgap−Bandgap |
100b | 2xBandgap | 2xBandgap+P2[6] | 2xBandgap−P2[6] |
101b | P2[4] | P2[4]+Bandgap | P2[4]−Bandgap |
110b | Bandgap | Bandgap+Bandgap | Bandgap−Bandgap |
111b | 1.6xBandgap | 1.6xBandgap+1.6xBandgap | 1.6xBandgap−1.6xBandgap |
- グローバルリソースで設定可能。Bandgap電圧は約1.3V。
- あとから気が付いたけど、I/Oポート設定はデバイスエディタでやるのが正統みたいだ。
- ここで設定すると自動的にポート設定が、psocconfigtbl.asmに記述される。
- 割り込みを設定すると boot.asmに、ljmp PSoC_GPIO_ISR が記述され、psocgpioint.asmが追加される。
- 便利だけど、どうせなら C用で作ってくれれば9良いのに。。。 →これについては、 割込みベクタが消えるを参照のこと
- 初め、モード011のPull-Upにすると、何故か入力モードになっておらず、Lにドライブされているのが不思議だった。
- 実際のところPSoCの場合は、入力、出力モードの区別は曖昧であり、どのモードでも入力は可能になっている。
- 例えば、Pull-Upの入力ポートとして使いたければ、PORTxDMxに011のH抵抗ドライブを設定し、
- Hを出力すれば、抵抗でHにドライブされるのでPull-Upとなり、この状態でポートを読み出せば良い。
- ところがLを出力すれば、単にLにドライブされる。この状態ではポートを読んでも常にLで入力ポートとしては使えない。
- こんな風に常に入力ポートであり、出力ポートでもある、非常に自由度のある面白いやり方をとっている。
- PSoCでは、さっきまで動いていたI/Oポートが、入出力出来なくなることがある10。
- こういう時は、大概の場合、ポート設定がおかしくなっている場合が多い。
そんなの、ちゃんと設定してるし前に確認してるのに、と思って見てみると変になってたりする。
- 原因としては、PSoCデザイナで配線を弄ってる所為が大きいと思う。
- デジタルモジュールは、一度グローバルバスに繋ぐけど、入力をOUTバスを使って繋いでしまったり。
- 別の配線を弄っていて、間違って既存の配線を消してしまったり。
- LCDモジュールみたいな、端子の決まってるモジュールの設定で、ポート設定を間違った時とか→これ結構簡単に壊れるので恐い
- Designerでみると、1つの端子が複数の箇所から出ている。→未使用と思って、別の設定をしてしまう
始めから回路構成が決まってれば、そんな間違いはしないと思うけど、PSoCだとどうしても試行錯誤が多くなるから、よくやっちゃうんだと思う。
- Designerで、I/Oポートの設定のロックが掛けられれば良いのかなぁ。
5って何?
6フローティング状態なら動作するので、場合によっては大丈夫なんだけど。
7変数使った場合と同じような気がして、自分は使ったことないけど。
8あまり自由度は無いけど。。
9結局、あとでljmp _PSoC_GPIO_ISR って書換えないといけない。
10いや、少なくとも自分は良くある。。普通の人は無いかもしれないけど。。。
[top]
- PSoCの割込みは、割込み要因毎に割込みベクタで各処理へジャンプするようになっている。
- 基本的には、以下のようにすれば良い
- 割込みベクタのジャンプ先に割込み処理を記述し、
- 割込み要因毎の割込みマスクビットを解除し、
- 全体の割込み許可を出す
- Cで割り込みを記述するには、まずコンパイラに割り込みハンドラを教える。
- コンパイラは、まずレジスタの退避、そしてGIEをdisableにして、割り込みハンドラを呼び出す。
- 割り込みハンドラを実行して、レジスタを回復、そしてGIEをenableにしてreturnの代わりにretiを実行までを自動的に行なうので、そこらへんは何も考えなくて良い。
- 後は、割り込みベクタに割り込みハンドラへのljmpを記述して、割り込みを許可するだけで良い。PICのように割り込みのたびにフラグをクリアする必要は無い。
- 割り込みは種別に、ジャンプテーブルになってるので、割り込みハンドラをljmpで指定すれば良い。
- Cコンパイラは、割り込みハンドラの最後のreturnをretiに置き換えるため、callで呼んではならない(重要)
- 従って、割込みハンドラ後に別の処理を実行する事は出来ない。→ljmpの下のretiは実行されることはない。
org 1Ch //GPIO Interrupt Vector
// ljmp PSoC_GPIO_ISR // asmのハンドラを
ljmp _gpio_handler // Cで書いたものに置き換える
reti
- でも、この方法だと 割込みベクタが消えてしまうので、Library Source Filesに追加されるファイルに記述しましょう。
優先度 | address | 割り込み種別 |
0(高) | 0000h | Reset |
1 | 0004h | Supply voltage monitor |
2 | 0008h | Analog column 0 |
3 | 000Ch | Analog column 1 |
4 | 0010h | Analog column 2 |
5 | 0014h | Analog column 3 |
6 | 0018h | VC3 |
7 | 001Ch | GPIO |
8 | 0020h | PSoC block DBB00 |
9 | 0024h | PSoC block DBB01 |
10 | 0028h | PSoC block DCB02 |
11 | 002Ch | PSoC block DCB03 |
12 | 0030h | PSoC block DBB10 |
13 | 0034h | PSoC block DBB11 |
14 | 0038h | PSoC block DCB12 |
15 | 003Ch | PSoC block DCB13 |
24 | 0060h | I2C |
25(低) | 0064h | Sleep timer |
- 未処理の最大の優先度の割り込みベクトルは、INT_VCレジスタで知ることが出来るが、多重割り込みとか考えない限り、使用する必要は無いだろう。
PSoCの割込みプログラムのデバッグをやってると、いつのまにか割込みベクタが消えていることがある。
- 割込みベクタは、boot.asmに記述する必要があるが、boot.asmは自動生成されるので、すぐ上書きされてしまう。
- だから確定したら、boot.tplを直接書き直してしまえば良い11。
- boot.tplを書き直す方法より、Library Sourceに追加されるルーチンに割込みベクタを書く方が正統なようだ。(080125追記)
- Counter16_INT.asm等には、ご丁寧に「;lcall _My_C_Function」というコメントがあったりするが、
- lcallを使ってはいけない。前述したが、Cコンパイラは割込みルーチンの最後にretiを自動的に付加する。
- lcallを使うと暴走するので、必ずljmpを使ってCファンクションを呼び出すようにすること。
- I/Oポートの変化による割込み、GPIO割り込みの場合の例を示す
- GPIOの割込みモードをPRTxICxで設定 →デザイナで設定可12
- GPIOのbit毎の割込みをPRTxIEで許可 →これもデザイナで設定可
- GPIO割込みのマスクbitを設定
INT_MSK0 |= INT_MSK0_GPIO;
- これでも同じ。どうせマクロにするなら、もっと短くしてくれれば良いのに。。
M8C_EnableIntMask(INT_MSK0,INT_MSK0_GPIO);
タイマ割込みに限らず、カウンタやPWMなどデジタルブロックを使う割込み(DBB00〜DCB13)は、デジタルブロックの割込みとして掛ける必要がある。
- タイマモジュールは、カウンタが0、或はコンペア出力による割込みのいずれかをデザイナで設定出来るようになっている。
- boot.asmに割込みベクタが設定されるので、あとは割込みルーチンを書いて、タイマのデジタルブロックの割込みを許可すれば良い。
INT_MSK1 |= INT_MSK1_DxBxx;
- Timer16の様に2個以上のデジタルブロックを使う場合は、実際に割込みを掛けているブロック、つまり最後のブロック13の割込みを許可する。
- コンパレータモジュールには割込み指定用のAPIは無い。
- これはコンパレータから直接割込みを掛けるわけではなく、Analog Columnからの割込みになるからだ。
- つまり、コンパレータの出力をコンパレータバスに出力して、Analog LUTを通してAnalogCulumnに繋ぎ、そこから割込みを掛ける。
- AnalogColumnからの割込みは、PSoC Designerでは設定出来ないので、boot.asmには自分で割込みベクタを記述する必要がある。
- あとは、割込みルーチンを書いて、AnalogColumnの割込みを許可すれば良い。
INT_MSK0 |= INT_MSK0_ACOLUMN_x;
個別の割込み許可をした後は、全体の割込みを許可する必要がある。
M8C_EnableGInt;
PSoCの割込みは、最初ちょっと分かり難かった。
- たとえば、タイマ割込みやろうと思っても、割込みベクタにタイマ割込みが無い!
- コンパレータモジュールに割込み機能が付いてない!!
初めは、何がなんだか、全然分かりませんでした。。
- マニュアルのいろんな処に、情報が散りばめられてるし。。
- まぁ、結局PSoCのハードウェアの構成(あくまで概要)を知らないと割込みを掛けられないってことなんだけど、ちょっと面倒かも。
- でも、この訳分からん変なところがパズルみたいで、PSoCの魅力かもしれないって、ちょっと思ったりする。
- こちらに、割込み記述方法について、こんな風に書いてあった。
- 単に lcall で呼ばれるだけなのでふつうに関数を書けばいい。巷の資料に書いてあるような #pragma interrupt_handler hogehogeISR のような #pragma文は書いてはいけない
- え〜って思ったけど、pragmaを使うと、lcallじゃなくてljmpで書かなきゃいけないのは変だよな〜とは感じていたので、GPIO割込みで確認してみる。
- まず、Pinoutの設定で、FallingEdgeのピン割込みを設定
- Build→Genarate/Build All Projectsで、一度コンパイル
- Workspaceのlib→Library Source Filesに、PSOCGPIOINT.asmが生成される
- これだけで、ちゃんと動くジャン。 。
- #pragma interrupt_handlerって何だったんだよ〜って感じ。
- 勝手にretiを付けてくれるので、lcallじゃ無くてljmpで呼ばなきゃいけないので、 最初ずいぶん悩まされたし。。
- 確かに、#pragma interrupt_handlerを使わない方が、 良いみたいだ。
- と思ったんだけど、pragmaを使わない場合、いままで通り動いてくれないようだ(130424追記)
- ボタンの入力割込みに使っているんだけど、 全く動かないわけではないが、どうもまともに動いてくれない。
- M8C_EnableGIntとか、 INT_MSK0_GPIOをセットしてみたけどダメ
- 何が違うのか良く分からないが、 とりあえず今まで通りpragmaを指定してljmpする方法で記述することにした。
- Cの割込みに関して、ydohiさんからコメントで情報を頂いた。
- 基本的な考え方は間違っていなかったようだが、少し曖昧だったので、こういう情報14は有り難い。
- pragmaを使えば、コンパイラが割込み処理にレジスタ退避・復帰を追加して、retiするので、
- とりあえず、Cの割込みルーチンはpragmaを使って記述して、ljmpで呼び出すということで良さそうだ。
- pragmaを使わずにlcallで呼び出す場合は、レジスタの退避/復帰をちゃんとしなければならない。
- そのためのPRESERVE_CPU_CONTEXT/RESTORE_CPU_CONTEXTマクロというのがあるらしい。
11boot.asmの冒頭のメモに書かれていることだけど。
12各ピンのInterrupt設定で、FallingEdge,RisingEdgh,ChangeFromReadを選ぶ。→DisableIntにすれば、PRTxIEはDisableになる。
13あれ、でもCapture割込みの場合はどれにすれば良いんだろう?
14最終更新日:February 29, 2012 だが、PSOC1の開発環境はそんなに変わっていないので、この記述を信じて良いと思われる。
[top]
PSoCの各モジュールの動作についてのメモ
- PSoCのモジュールって、それぞれ結構クセがあって、なかなか手強い。
タイマの端子の意味が分からなかったので、TimerTimingDiagramを見て、以下のような動作と理解した。
- 最初にカウンタは、Periodの値にプリセットする。
- スタートすると、Clockによりカウントダウンされる。
- カウンタがCompareValueより小さくなると、CompareOutが H になる。
- カウンタが0になると、Periodの値にプリセットされる。
- カウンタがプリセットされると、TerminalCountOutが H になる。
- CompareOutが L になるのはプリセットされた次のクロックであることに注意。
- 従って、CompareOutの周期はPeriod+1、H出力の長さはCompareValue+1になる。
- TerminalCountOutの周期もPeriod+1だが、H出力はクロックの半分になるようだ。
- CaptureがHになると、その時のカウンタ値がCompareValueにコピー15される。
- PSoCでは、TimerもPWMもCounterも動作的には同様(というか、使い分けがイマイチ分かってないが)で、分周数をPeriod、DutyをCompareValueに設定して、CompareOutに出力されるという感じだ。
- TimerはCaptureがあるけど、Enableが無い。
- CounterはEnableがあるけど、Captureが無い。
- EEPROMの初期化って出来ないのかと思っていたけど、 ちゃんとやってる方16が居る。
- #pragma abs_address でアドレス指定すれば良いだけなのか。。結局PICとかと同じなんだなぁ。
- FlashSecryty.txtで、設定やアドレス確認が出来るのか。なるほどね〜。
- まだ、試してないけど。忘れないようにメモメモ。
実際に使ってみた(080228追記)
- 27433は0〜3FFF、29466は0〜7FFFがFlashROM領域。1blockが64byteなので、それぞれ64×256、64×512byte
- ブロック番号で言うと、それぞれ0〜255、0〜511になる。
- E2PROMモジュールで指定する、FirstBlockを何処にするべきかなんだけど、
- 変数とかは最初から取られていくみたいなので、とりあえず最後のブロックを指定する。 →511(29466なので)
- Lengthについては、64の倍数にしないと効率的じゃない17というか、64の倍数にした方が良い。。 →64(実際は16byteしか使わないとしても)
- E2PROMモジュールを使うとflashsecurity.txtが出来るので、使用するブロック番号のプロテクトを外す(w→uにする)
- 初期値を#pragma abs_addressで指定しておく。FirstBlock=511に指定した場合は、511x64=32704、つまり7FC0hからになるので
#pragma abs_address 0x7FC0 // FlashROM領域の7FC0〜の初期化
const signed char r_rom[16] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
#pragma end_abs_address
- この例のr_romっていうのは、初期化のための仮の名前で、ここでしか使用しない。
- 後は、FlashROMの内容をワーク用RAMに読みこんでから使用すれば良い。
- 書き込みはワーク用RAMの内容をFlashROMに書き込む
BYTE r[16]; // ワーク用RAM
E2PROM_1_E2Read(0,(BYTE *)r,16); // FlashROM→RAM
E2PROM_1_bE2Write(0,(BYTE *)r,64,25); // RAM→FlashROM
- E2PROM_E2ReadやE2PROM_bE2Writeの最初に指定しているのが、FirstBlockで指定したブロックからのオフセット。
- FirstBlock=511と指定したので、FlashROMの7FC0hから読書きすることになる。
- この時のByte数は64の倍数じゃなくても良かったような気がする
15CompareValueを動的に変化することが出来るので、面白い使い方が出来そうだが思い付かない。。。
16Dr.Matrixさん、なのかな?こちらのPSoC関連のTipsは分かり易い。素晴らしい。
17実際、コンパイルは通っても実行時にエラーになったりした。と思う。うろ覚えだが。。
[top]
- PSoCの回路は、それぞれ一工夫してあって自由度がある反面、分かりにくいところがある。
- また、PSoC Designerでの設定方法等、ちょっと便利な使い方についてのメモ
これはテクニックでも何でもないんだけど、PSoC Designerを使っていると接続可能であることに意外と気がつかないかもしれない。
コンパレータバスは、直接見ることが出来ないみたいだ。
- SPISをゲート代わりに使って、アナログブロック出力をデジタルブロックで利用する方法が、桑野さんの「はじめてのPSoCマイコン」に載って18いた。
- この方法を使えば、SlaveSelectInputに外部から L を入れて、コンパレータ出力をMOSIに接続して、MISOからコンパレータバスを観測出来る。
- クロックで同期して入力するので、DFFを1回通したようになる。
→単にコンパレータバスを出力するだけなんだけど。
- 内部バスが余っていれば、図のように InterConnectでSSに L を設定することも出来る。
- ただ、単にコンパレータ出力を見るだけなら、コンパレータの割込みプログラムでGPIOに出力すれば良いだけなんだけどね。
DigBuf
- PSoC Designer4.2から、DigBufモジュールが追加されたので、簡単にコンパレータバスをデジタルブロックで利用出来るようになった。
- ただコンパレータ出力を見るだけなら、上記のようにする必要は無くなったけど、結構苦労したので、無意味になったけど残しておこう。
- でも「はじめてのPSoCマイコン」で解説されている方法は、ORゲートとして使うということだからまだ有効だ。
- どうせならBufだけじゃなくて、ORゲートモジュールも作ってくれたら良かったのに。。と思ったけど。。。
- ORみたいな単純なゲートは、デジタルブロックからグローバルバスへのLUTでも出来るな〜って後から気がついた。
- つまりDigiBufは、これの入力を繋ぎ換えるのに使うと便利ということか。なるほどね。
これも「はじめてのPSoCマイコン」より
- アナログ信号を受けるとき、リファレンス電圧が必要になることが多い。
- PGA等で入力を受けて、フィルタに通したりする場合はAnalogBusへ出力しないので、TestMUXを出力すれば良い。
- 詳細は「はじめてのPSoCマイコン」のColumn1を参照のこと
ACB02CR2 = (ACB02CR2 & 0xE3) | 0x14;
- 上記は、AnalogBus2よりAGNDを出力する例。
- PSoC Designer上ではAnalogBusに何も接続されていないが、ちゃんと出力される。
- これは、AnalogBus出力しない場合に常套手段として使えそうだ。
- これもユーザモジュールの設定として入れてもらいたいかも。
- トラ技の2005年9月号の桑野さんの記事が参考になる。
- まず、ダイナミックコンフィグレーション用のヘッダファイルをインクルードする。
#include "psocdynamic.h"
- 最初のコンフィグレーションがデフォルトになる。(初期状態のモジュール配置)
- 他のコンフィグレーションに切り替えるためには、UnloadConfigで配置情報をクリアしてから、LoadConfigで配置する。
- 各コンフィグレーション毎に、以下のようにプログラムすれば良い。
LoadConfig_xxx
モジュールをスタート
プログラム
モジュールを停止
UnloadConfig_xxx
- 注意点として、ダイナミック・リコンフィグレーションは、入出力設定とかグローバルリソースは切り替えられない。
- グローバルバスとGPIOとの接続は、PRTxGSでコントロールすれば良い。
18この方法は普通ちょっと思いつかない。この本は良く見るといろいろ参考になります。
[top]
PSoC Designerって、慣れないと結構分からないことがある。気が付いたことをメモしておこう。
- PSoC Designerで、既存のプロジェクトをCloneProjectでコピーして、新しいプロジェクトを作ることって良くある。
- これを見ても、エラーの出ているファイル名が良く分からないので、結構悩んだ。
- 結局、Windowsで8文字以上のファイル名だと、変な名前になるのが原因みたい。
- つまり、Cloneを作る場合は8文字以内のファイル名にする必要がある。ってことか。。
- このエラーはSleepTimerモジュールを使った時にも、起きたことがある。
- この時は、モジュール名をSleepTimer_1→Sleep_1にしたら、直った。
- 割込みベクタ等が記述されている boot.asmは、場合によって19boot.tplから生成される。
- でもBuild→Rebuild Allしても、boot.tplの変更はboot.asmに反映されない。
- このような時は、DeviceEditorで I/OポートのInterruptの設定、一旦変更してまた元に戻して(保存する必要はない)から、改めてBuildするとboot.tplからboot.asmが再生成される。
- さらに、PSoC Designerをバージョンアップすると、boot.tplが上書きされてしまう。
- だからバージョンアップする場合は、boot.tplを保存しておく必要がある。→と思ったけど、backupというフォルダに保存されるようだ。
- でもバージョンアップで機能追加が行われている可能性があるので、古いtplに置き換えるのは危険だ。必要な箇所だけ書換えるだけにした方が良い。 →これについては、 割込みベクタが消えるを参照のこと
- PSoC Designer4で作成されているプロジェクトは、最新のDesigner5.xではOpenできない。(140728追記)
- でもPSoC1については、むしろ過去のProjectにこそ面白いものが多い
- もちろん、Cのソースファイルだけなら、普通のTEXTエディタで見れば良いんだけど。。
- PSoC_Designer_Archiveから、旧バージョンのDesignerをダウンロードできる
- Akamai Download Managerを使うらしいので、これも先にインストールする必要がある様だ
- あと、Cypressのサイトで予めユーザ登録をしておく必要がある
- Designer4.4をインストールすれば、Designer4のプロジェクトを見ることが出来る。
- でもWindows7のでは、表示がうまく出来なかった。(これは、自分の環境だけかもしれないが)
- Designer5.0だけは20、Designer4のファイルのUpdate機能がある様だ
- PSoC Designer 5.0 Service Pack 6 Installerをインストールして、
- Designer4のSOCファイルを開くとUpdate出来る
- Updateで作成されたCMXファイルは、最新のDesiger5.xでもOpen可能となる
- 過去のDesignerのプロジェクトファイルを使うことを考えると、
- Designer4のプロジェクトの変換用に、Designer5.0をインストールして21おいた方が良いと思う。
19どんな場合か良く分からないんだけど、多分Designerで割込み関連の設定を弄った時だと思われる。
20Designer5.1以降は、このUpdate機能が無い。全部のバージョンにこの機能を付けておいてくれたら良かったのに。。
21但し、古い環境をインストールすると弊害もあるかもしれないので注意。
[top]
- まぁ、自分ほど失敗する人はなかなか居ないと思うけど。。
- 同じ失敗をしないように備忘録としてメモしておこう。
- PSoCのLCDモジュールは、簡単に表示出来るので便利。
- でもデバッグ用に入れたら上半分が黒で下半分が白、文字が表示されない。
- 配線や、LCDの初期化など、プログラムは特に問題無いようにみえる。。
- UARTのAPIを使っていて、デバッグ中に設定ミスでUARTのクロックが極端に高いことが原因だった。
- 理由は、そのときはUART割込みでLCDの表示が喰われると思ったんだけど。。。
- 改めて考えてみると、LCDってCPUが常にアクセスする必要ないから表示は喰われないよなぁ。
- LCD初期化ルーチン自体が喰われてるのかな?
- 割込みの問題だろうか。。特にUARTのAPIの割込みは、自分で書いていないだけに、忘れがちなので注意が必要だ。
- ちょっと、この結論は自信が無い。そのうち検証してみよう。
- これは後から考えてみると、上記の記述は多分間違い。(追記)
- この状態は、LCDの初期化が出来ていないということなので、
- まずはLCD初期化ルーチンが実行されていることを確認することだと思う。
- UARTのデバッグ用にRS232Cインターフェース部分だけ作ってICクリップで繋げられるようにしている。
- シリアルインターフェースを後から追加してみたり、デバッグ用に便利だ。
- ある時、デバッグの際にPSoCの書き込みが出来なくなった。
- RS232Cインターフェースを外すと、書き込めるようになることが分かった。
- でも、このインターフェースは今まで結構使ってたが、書込み出来てたのに何故?
- これはRS232インターフェースの電源を、それまでPSoC側から貰っていたのだが、パソコン側から貰えるようにした22のが原因だった。
- この電源はPSoCには接続していないので、関係ないと思っていたのだが、、
- RS232Cインターフェースが、'H'の状態でPSoCのI/Oに接続すると、I/Oピンを通じてPSoCに電源が供給されてしまう。
- つまり、I/Oの保護回路を通じて電源供給されてしまうということ。
- この状態だと、ちゃんと書き込み出来ない(ってことだと思う)
- RS232Cインターフェースの電源はPSoC側から貰うようにして、解決した。
- 入力ポート設定が、いつの間にかStrongになっていた。
- 原因は、入力ポートをGlobalOutputバス経由で繋いでしまったために、ポート設定が狂ってしまった。
- DeviceEditorで、見た目が繋がってると安心してしまうので要注意。
- それ以外に、グローバル割込み許可を忘れてしまっていたこともあった。
M8C_EnableGInt; →これの入れ忘れ
- 割込み許可されていないと、RXは全く動作しなくなってしまう。
- UART_cGetCharの受信データがどうもオカシイ。
- 受信データが抜けたり、同じデータを2回続けて受信してしまったり。。。
- 結局、UARTのHighLevel APIは割込み許可しなければならないと思っていたが、必ずしもそうでは無いらしい。
- UART_bCmdCheckとかUART_szGetParamを使う時は割込みが必要だが、UART_cGetCharを使う場合は、割込みは不要だった。
- ポート設定や接続を何度見直しても、アナログ入力がフローティング状態のようになっている。
- 自分の場合は、大概は入力に付けたPGAのスタートし忘れ。この失敗は2〜3回やってる。
- 他のモジュールは、スタートするの忘れ難いけど、PGAに限らず単純なモジュールは繋がってるだけで安心してしまって、スタートし忘れてしまう。
- コンパイルすると、このWarningが結構出ることがある。
!W ./main.c(47):[warning] calling function without prototype may cause errors
自分の場合は、いつも文字列関連のルーチンとかを使ってるのに、ヘッダファイルをインクルードし忘れてることが多い。
- 例えば、cstrcpyを使ってるのに、#include してないとか。
- 同じ関数では複数使っていても最初の1回しか出ないし、どうも1行前にずれてWarningが出たりするので、何のエラーだか分かり難いことがある。
つまらない失敗だけど、毎回繰返してやっちゃうので、自戒を込めて。
- MiniProgのファームが古いと書き込めないことがある。
- この場合、 ここに示すように、新しいPSoC programmerをダウンロードして、
- Utilities→UpgradeFirmwareで、MiniProgのファームを書き換えると書き込めるようになる。
- 以前、一度やったんだけど、MiniProgは幾つか持ってるので、まだやってないのが残ってたりした。
まぁスタート忘れも多いんだけど、それ以外に、
- CounterやPWMモジュールをSync to SysClkで、24MHzの分周しないクロックを入れると、Counterの出力が出てこない。
- 同期カウンタの場合、SysClkより速いデータをカウント出来ないということらしい。
- だから、Sync to SysClk*2にすれば、24MHzの分周しないクロックを入れても、Counter出力できる。
- 48MHz(SysClk*2)のクロックを使いたい場合は、Unsynchronizedにする必要がある。
- これは、CounterやPWMモジュールのデータシートのClockSyncの説明で明記されてるんだけど、いつも忘れてしまうので書いとこう。
- GPIOポート出力を1にしたい時に、以下のように記述したとする。
PRT1DR |= 0x01; // Port1[0]を1にする
正しく動作することもあるけど、なんか変な風に動作することもあったりする。
- 調べてみると、Port1[0]は確かに1にセットされるんだけど、他のビットが変になったりならなかったり。
- PRTxDRを、Readした時はGPIOポートを読み込んでいるのであって、PRTxDRレジスタを読んでいるワケじゃない23のが原因。
- こういう単純なミスで、意外と原因が分からなくて悩んだりする。
- PRTxDRレジスタ以外のレジスタの場合は、普通問題ないので、つい考えずにやっちゃうんだよね。。。
- 割込み処理、例えばキー入力を複数回、受け付けてしまったりする。
- これは、単純に割込み後に割込み禁止にしていないからだった。
- Z80だと、割込みが掛かると自動的に割込み禁止になる。
- プログラムで禁止する場合、禁止するまでに割込みが掛かるとマズイ24わけなので、これが当然の機能だと思っていた。
- PSoCを使い始めた時もそう思っていたので、暫く自動的に割込み禁止になるわけじゃないことに気が付かなかった。
- 未だに、これを時々やっちゃうのでメモしておく。
- しかし、やっぱり禁止する前に割込み掛ったら困る事もあるんじゃないかな〜。
- これは、後述の「割込みが有効にならない」と矛盾するなぁ。
- 今となっては分らないけど、別の原因だったのかもしれない。(追記)
- MiniProgを使ったISSPで、"Can’t Acquire Device"のエラーで書き込みが出来なくなった。
- MiniProgのファームが古いと書き込めなかったりするのはよくあることだけど、最新だし。
- 最初は書き込めていたのに、しばらく使っていたら全く書き込めなくなった。
- 実はこの回路ではISSPで使うSCLKとSDATA線を別の目的に使うために、両方とも10kΩでプルアップしていた。
- PullUp取るしかないか。。でもこの端子使えないと困るし。。。
- でも1度書けたんだから別の理由かも??なんて迷ったのでググって見ると、
- PSoC Hacker25に、 そのものずばりの問題について書かれていた。
- やはりPull-Upが原因のようだ。要約すると、
- 最善の方法は、ISSP端子を使わないこと →仰る通り!
- 次善の策は、ISSP端子のPull-Upを書き込み時はジャンパで切り離す →今回は基板の問題で難しいなぁ
- もしこれが出来ない場合は、書き込み時に500〜1kΩぐらいのPull-Down抵抗を繋ぐ。
- PSoCDeveloperで数年前に紹介されていた方法らしい。
- まず、MiniProgのSCLKとSDATAを10kΩでPull-Downしてみる→ダメでした
- 今度は、1kΩでPull-Down。→OKでした。やった〜
- この方法はMiniProgを壊すリスクがあるけど、やってみる価値はあるかも。
- この改造MiniProgは、10kΩPull-Up専用にしよう。
- I2Cmは、I2Cマスタしか使えないが、ソフトウェアなので、どのポートにも自由に設定できる。
- それで、適当なポートに割り当てて使おうとしたが、全く動作しない。
- ポート設定は、ちゃんとOpenDrainのLdriveになっているし、問題なさそうだけど。。
- SDAもSCLも、ずっとHレベルのままで、全く信号が出ていない。
- ICEで追ってみると、I2Cm_fSendStart呼出しで、Hレベル待ちで無限ループになっている。→Hレベルであるにも拘らず!
- 理由は、何故かポート設定がAnalogHi-Zになっており、ポートが読めていなかった。→ちゃんとOpenDrainLdriveにしたはずなのに?
- モジュールのプログラムを読んで、やっと原因が分かった。
- まずI2Cmモジュールは、初期設定をポート設定をHi-Z(010)にして、ポートにLを出力する。
- 外部で、Pull-Upされているので、初期状態では、Hレベルとなる。
- Lを出力する時は、ポート設定を、Pull-Up(011)にして、Lドライブする。
- ところが、DM0〜DM2を全て設定しているわけではなく、少なくともDM2は全く操作していない。
- そのため、I2Cmはポート設定を、OpenDrainLdrive(111)にすると、おかしな事になる。
- まず、I2Cmモジュールはポート設定を、Hi-Zanalog(110)に設定してしまう。
- するとポート状態を読むことが出来なくなり、ポート状態に関係なくLになってしまう。
- つまり、I2Cmでは、ポート設定をHi-Zにしなければならないということ。
- 実は、PSoC Designerは、I2Cmモジュールを配置すると、自動的にポートをHi-zに設定してくれる。
- しかし、自分はI2CはOpenDrainという頭があるので、チェック時に設定し直してしまったようだ。
- DataSheetを良くみるとDriveModeは設定の必要ありません。とは書いてあるけど、Hi-Zにしなければならないとは書いていない。
- まぁ、全部自分が悪いんだけど、I2CHWの場合は普通はOpenDrainにするので、
- 結構間違いやすいと思うんだけどなぁ。。みんな間違えないのかなぁ。。。
- あと、Designerでポート設定変えたら、必ずRebuildすること。
- 外部からのアナログ入力を、LPFを通してDelSigでA/D変換して出力していた。
- LPFのカットオフ周波数やDelSigのサンプル周波数を外部から変更できるようにしていたんだけど、
- A/D変換値が、パラメータ設定する度に不安定に値が変化してしまう。
- 試しに全くパラメータ設定しない(Designerの設定のみ)様にしたら、急に値が安定した。
- DelSigやLPFを停止させてから設定するようにしてもダメ。
- LPFのサンプル周波数を分周しているCounter16をStop、パラメータ設定、Startしてもダメ。
- でも完全に停止した状態で、パラメータ設定したら、全く問題なくなった。
- 基本的にはCouter16が動作中に、パラメータを書き換えたのが悪い26らしい。
- 特に16bitのカウンタは1byteづつ2回書き換えるので、ちゃんと値が設定されないようだ。
- Counter16をStop、書換え、Startを連続してやった時、何故ダメだったのか分からないが、
- 基本的にパラメータ設定は、関連するモジュールを、完全に停止させてやった方が無難なようだ。
- ある入力ポートを、出力ポートに切替えて使おうとしたら、全く出力が出ない。
- PRTxDMxを出力に設定したのに、フローティングになっているようだった。
- 結局これは、このポートを別の目的でグローバルインプットパスに繋いでいたためだった。
- 出力ポートにするためには、グローバルセレクタのPRTxGSをLにして、グローバルバスから切り離してから、PRTxDMxを出力に設定すれば良い。
- これ、以前もやったことがあるんだけど、性懲りも無くまた悩んでしまったので、記録しておく。
- キー入力時に、カウンターの割込みを有効にして別処理を行うようにプログラムしたら、どうしても割込みが有効にならなかった。
- プログラムの最初で同じ事をやれば、ちゃんと有効になる。
Counter16_1_EnableInt();
- これは、キー割込み処理内でこれを行おうとしたのがダメだった。
- Cコンパイラは割込みルーチン開始前に自動的にGIEを無効にし、終了後にGIEを有効にしてからretiで戻る。
- つまり、割込みルーチン内では全割込みが無効になっているだけのことだった。
- 分かっていたつもりだったのに、全然気が付かなくて随分悩んでしまったので。。
- 結局、キー入力割り込み中で、処理に飛ぶようにした場合、割り込みルーチンが動かないので、
- フラグだけセットしておいて、割込み終了後にフラグで処理を実行するようにした。
- で、もう一つ。例えばCounter16の場合は、Counter16_1INT.asmに、
- I2Cm_1_bWriteBytesのオプションで、I2Cm_1_NoStopしても、約1秒後にストップコンディションが発行されてしまう。
- これは、実際のデバイスを接続せずに抵抗でPull-Upだけしてテストしていたが、
- ACKが返らないと、1秒後にストップコンディションを発行する仕様になっているらしい。
- 久々にGPIO割込みを新規に書いてみたら、割込みが掛からない。
- Designerで、ポートをPull-Up、FallingEdge割込みに設定
- ポートに1を出力して、Pull-Upが掛かるように
- コンパイルして、boot.asmに、ljmp PSoC_GPIO_ISR が追加されたのを確認
- PSoCGPIOINT.asmに、Cの割込みルーチンへのジャンプ追加→ljmp _gpio_handler
- Cプログラムに、割込みルーチン明記→#pragma interrupt_handler gpio_handler
- GPIO割込み許可→INT_MSK0 |= INT_MSK0_GPIO;
- グローバル割込み許可→M8C_EnableGInt;
- もちろん、割込み本体の関数gpio_handlerも記述したけど、割込みが掛からない
- これだけ設定すればOKのはずなんだけど?とずいぶんと悩んでしまった。
- LCD繋いであったので、ポートの値を表示してみたら、Pull-Upのはずなのに0?
- 念のためポートの電圧見てみたら、Lowになってる。何故??
- 結局、MiniProgを接続したままデバッグしてたので、そのポートの電圧がLowのままだった。
- MiniProgを外したら、全く問題なくなった。。。
- それにこのMiniProgは、少し上の方に書いてあるISSPで書込み出来ないという対策のため
- 1kΩでPullDownしてあるものだったので、普通のMiniProgに変えたら付けたままでも一応動いた。
- 久々に割込みを設定したので、その所為だとばかり思って、こっちを疑わなかった。
- でも、割込み設定って意外と面倒だな〜と改めて思った。。
- 0Vを入力した時のADC出力が、0x80になる。
- オフセットが付いてるのかと思って、入力に付けたPGAのRefを、AGND→Vssに変えてみたけど変わらない。
- ただ単に、DataFormatがSingedになっていただけだった。
- あと、入力レンジが、約2.4〜3.6V程度と、かなり狭い。
- これもPGAのオフセットを疑っていたが、RefMuxの設定だった。
- RefMuxが(Vdd/2)+/-BandGapになっていたが、(Vdd/2)+/-(Vdd/2)にしなければならなかった
- 両方とも簡単な間違いだが、PGAばかり疑って、すぐに気が付かなかったので。。
long data = 0x800000000;
data >>= 1;
- で、dataが0x40000000になることを期待したのですが、0xC0000000になります。
考えてみれば、当たり前か。unsigned longにしないとダメなんだ。
- 忘れちゃいそうなので、基本的にはBYTE,WORD,DWORDだけを使うようにしました。
#define XPORT XXX|YYY|ZZZ //設定するポートを列挙
//全ポート入力の時、特定のポートだけ出力ポートに変更
PRT0DM1 &= ~XPORT; // XPORT input->PullDown(010->000)
PRT0DM0 |= XPORT; // XPORT PullDown->Strong(000->001)
ちゃんと出力に変更されたが、出力レベルが変。
- いろいろ調べてみると、端子XXXは正しくStrong(001)だが、端子YYY,ZZZはPull-Up(011)になっている?
- これは、#defineが展開された時、PRT0DM1 &= ^XXX|YYY|ZZZになってしまったためだった。
- これは、出力に接続する回路によっては分からないことがあるので、要注意。
- 実は、以前作った外部回路はプルアップされていたので全く気が付かず、今回プルダウンされた回路を接続したので気付いた。。。
- defineは、括弧を付けて定義しよう。ということでした。
- 9600bpsで動作しているプログラムの速度を速く設定しなおそうとしたら嵌った
- 19200bpsなら動作するが不安定、38400bpsはダメ。
- 送信は、230400bpsでもOKだが、受信は全く速度が上げられない。
- CPU速度は、CysClk/1=24Mなので最高速。こんなに遅いわけ無いんだけど。。
- 原因は、Sleepさせていたから。
- Sleep中はUARTも停止しているので、Rxの端子の立下りでGPIO割込み起動させていた。
- 割込み起動後、UARTが動作する前に、信号が来るためだった。
- 当たり前だが、これ以前にも少し悩んだ気がする。もう一度やってしまった。。。
- で、忘れないようにメモしておく。
- PSoC Designerでのプログラムで、変数の値が勝手に書き換わるBUGに悩まされた。
- 最初は、ポインタ操作や、配列の範囲外アクセスを疑って、 それらを排除していったが、
- 最後は、ほとんどループと printfだけになってもダメ
- いろいろ調べると、 変数領域が最初の方から順に壊れているようにみえる
- また、 使用しているユーザモジュールの数に較べて、コンパイル後のメモリ使用量が大きい気がする。
- 結局、プロジェクトを手動で全く同じ設定で作り直して、ソースをコピーしたら動いた。
- おそらく、 今までプロジェクトのクローンで何回も(多分10回以上)コピーを繰返して作っていたプロジェクトなので、
- プロジェクトが一部破損して、リンクしていないはずのモジュールがリンクされメモリを占有し、
- 実行時に、スタックオーバーフローして、RAM領域を上書きしていたと思われる。
- 以下のように、SPIモジュールを記述していたが、時々、2回目のループから抜け出せなくなる。
while( !( SPIM_1_bReadStatus() & SPIM_1_SPIM_TX_BUFFER_EMPTY ) );
SPIM_1_SendTxData(wdata);
while ( (SPIM_1_bReadStatus() & SPIM_1_SPIM_RX_BUFFER_FULL) ) ;
rdata = SPIM_1_bReadRxData();
- 動いていたので気が付かなかったが、そもそもSPIM_RX_BUFFER_FULLってどういう意味なんだろう
- SPIのバッファが何byteだとしても、受信してるからこそ、立つフラグの気がする
- だとしたら、受信した状態で無限ループになる可能性がある。
- 少なくとも、 上のループと同様に反転する必要がある →実際、そうしたら安定した
- SPIMのデータシートには、SPIM_RX_BUFFER_FULLの意味は書いてないけど、
while( !( SPIM_1_bReadStatus() & SPIM_1_SPIM_TX_BUFFER_EMPTY ) );
SPIM_1_SendTxData(wdata);
while ( !(SPIM_1_bReadStatus() & (SPIM_1_SPIM_SPI_COMPLETE | SPIM_1_SPIM_RX_BUFFER_FULL)) ) ;
rdata = SPIM_1_bReadRxData();
- 何で、最初そういう風に書いたかわからないけど(ぉぃぉぃ)、
- たまたま動いたから、あまり考えずに大丈夫なんて思っちゃダメだな〜ってことで。 。
- SPIは入出力が同時に可能なので、 以下のようにプログラムを書いてみた
BYTE sendspi(BYTE *data)
{
while( !( SPIM_1_bReadStatus() & SPIM_1_SPIM_TX_BUFFER_EMPTY ) );
SPIM_1_SendTxData(*data);
while ( !(SPIM_1_bReadStatus() & (SPIM_1_SPIM_SPI_COMPLETE | SPIM_1_SPIM_RX_BUFFER_FULL)) );
*data = SPIM_1_bReadRxData();
return *data;
}
- 連続して呼出すと書き込みは成功するんだけど、 読出しは時々というか、かなりの確率でデータが化ける(シフトした様なデータになる)
- あと、クロックを2MHzだとちゃんと動くけど、200kHzにすると完了を待たずに次のデータを送信してしまう。
- 速度が関係ない用途だったので、SPIモジュールを使わずに直接読書きすることにした。
- テキトウに書いたので、かなり遅いけど。
BYTE sendspi(BYTE *data)
{
BYTE i;
for (i=0; i<8 ;i++) {
if (*data&0x80)
chg_port(SPI_MOSI,1);
else
chg_port(SPI_MOSI,0);
chg_port(SPI_SCLK,1);
*data <<= 1;
if (PRT0DR&SPI_MISO)
*data |= 0x01;
chg_port(SPI_SCLK,0);
}
return *data;
}
- 速度は遅いんだけど、ちゃんと動く( chg_portはポート0をビット毎にセットする自前の関数)
- SPIモジュールの使い方が悪いと思うんだけど、 いったい何が悪いんだろう。。。
- UARTで16進データを受けて、そのまま SPIに出力するプログラムを書いたんだけど、
- 連続して出力すると出力が途中で、 特にAlphabetがあると止まってしまう(9600bps)
- どうも16進→10進変換に使っている strtolが遅いためらしい
- エラー処理とか無いけど、 とりあえず、ちゃんと動くようになった。
- 空白は、continueでなく breakの方が良いかも(用途による)
- プリント基板で作った回路で、 実績があるのに、ある基板だけ書込みできない。
Can not Acquire Device! Please verify the device connection to the Programmer
- 配線やはんだ付けを何度もチェックしたが問題なし。
- PSoCを外して、別のものに付け替えてもダメ。
- 各端子の電圧を測定してみたが、正常なものと変わらず。
- 基板自体に問題があるかもと思い始めたが、電圧が正しいのはおかしい。
- 最後の手段で、書込みのSDA,SCLである P1[0],P1[1]の書込み時の波形をオシロで見てみた。
- 基板をチェックすると、コンデンサが付いていた。
- というか、この端子はXOでもあるので、水晶発振器をつける場合に 100pF付けるんだけど、
- 間違えて、パスコン用の0.1uFが27付いていた。
- 分かってみれば、簡単なことだったが、結構悩んだので、記録しておく。
- PSoC Designerで、I2CmをPlaceして、ピンを設定しようとしたが、Noneになっていて選択できない。
- Portを変えても、一度Deleteして置き直してもダメ
- 思い余って、PSoC designerを再インストールしてみたがダメだった。
- 結局、Projectを新規作成したら、使えるようになった。
- ダメだったProjectは、Cloneで作ったものだった。
- Cloneで作ったProjectの動作がおかしい場合は、新規で作り直した方が良いかもしれない。
- 27443用のプロジェクトファイルから、クローンで29466用のプロジェクトを作成した時に、
- どうやら、クローンでプロジェクトを作成した時は、リンカの設定が変更されない様だ
- 記事によると、Project→Setting...→Build→Linkerで、Relocatable code start addressを、0x149以上にすれば良いらしい。
- 試しに29466で新規作成してみると、0x1A0に設定されるみたいなので、その方が良いのかもしれない。
22実は、別の回路を繋ぐ時に、電源供給したかったので改造したところだった。
23設定によっては、PRTxDRの値と同じになる場合もあるけど。
24マズくない場合もあるけど、その時は明示的に割込み許可すれば良い訳だし。
25これって、Cypressのサイトじゃないか。へー凄いなぁ。。
26もしかすると他のモジュールもダメな奴があるかも。
27SMDだったので、間違いになかなか気がつかなかった。。
[top]
[電子工作関連に戻る]