PSoC5LPボードを使ってみる
by K.I
Index
- PSoC1ばかり使っていたんだけど、新しいPSoCファミリも使ってみたいということで、
- CPUコアは、ARMのCoretex-M3で、最大動作周波数は67MHz
- PSoC1のM8Cは、CPUとしては今ひとつだったので、かなり強化されている
- ピン配置を見ると、ある程度機能毎にピンが決まっているみたいで、
- 回路図を見ると、24MHzと32kHzの水晶発振子や外付けのEEPROMも搭載されており
- とりあえず、何でも出来そうなフルスペックの贅沢な構成になっている。
- 2つあるUSBコネクタの、左側(CN3)がEzUSBを使った書込器、右側(CN4)がPSoC5LPに直結しており、
- 逆流防止のダイオードも付いているので、どちらのUSBコネクタからも電源供給可能。
- 書込器内臓で、PSoC Creatorから簡単にProgramできるのが良い
- このボードは、1つ手に入れておくと、いろいろ実験するのに便利だと思う。
[top]
- 開発環境は、PSoC Creatorを使う
- PSoC Designerとは、かなり違っているみたいだ。
- PSoC Creator3をダウンロードして、インストール
- 予め、Cypressのユーザ登録とLoginが必要
- 自分の場合、Creator2.1と2.2がインストール済だったが、そのままではうまくインストール出来なかった。
- 全部、アンインストールしてから、再度インストールし直したらうまく行った。
- PSoC5LPは、コントローラとしてARMを搭載している。
- と思ったんだけど、Build Settingsを見ると、ARM GCC 4.7.3となっている
- Keilのコンパイラを使うのは、PSoC3だけなのかもしれない
- Keil License Registrationのダイアログが表示される
- Step1の、Get LIC via Internet...のボタンを押すと、ライセンス申請のページが表示され
- メールで、License ID Code (LIC)が送られてくるので、コピーして、
- Step2の、Enter LICに入力して、Add LICボタンを押す
- Step3の、Verify LICで、ライセンス登録が成功していれば、
- File→New→Project...→Empty PSoC 5LP Designで、新規プロジェクトを作成
- プロジェクト名を適当に指定して、Advancedで、DeviceをCY8C5868AXI-LP035に設定しておく
- TopDesign.cyschが開いた状態になる
- この画面で、回路図のようにモジュールの配置やI/Oの接続を指定するようだ。
[top]
- まず、LEDが接続されているポートを置いてみる。
- とりあえず、LEDの点灯をソフトでコントロールする
- 最初は、左側のWorkspace Explorerで、cyschが選択され、空の回路図が表示されている
- 右側のComponent Catalogの、Ports and Pins→Digital Output Pinを、cyschの画面にドラッグする
- 最初、表示が小さいので100%ぐらいにして、ダブルクリックするとConfigureウィンドウが開く。
- Typeは、Digital OutputでHW Connectionになっていて、これは内部のハードウェアにI/Oが接続された状態
- ピン名を、分かりやすくLEDにしておく
- I/Oをソフト制御にするので、HW Connectionのチェックを外す
- 次に、左側のWorkspace Explorerで、cydwrを選択すると、デバイスのピン配置が表示される
- 右側に、ピン情報が表示されているので、PortをLEDが接続されているP6[7]に設定する
- 入出力に関しては、ちょっと奇妙な仕様になっている。
- ここでコンパイルしてみると、入出力ルーチンが生成される。
- ピン名をLEDに設定したので、LEDというフォルダに、LED.c,LED.h,LED_alias.hというファイルが作られる。
- 以下のようなルーチンが生成されている
- LED_Write →ポートへの出力
- LED_SetDriveMode →ポートの設定
- LED_ReadDataReg
- LED_Read →ボートから入力
- LED_ClearInterrupt
- 実際のポートの定義は、cyfittergnu.incでやってるみたいだけど、何でこんな面倒な仕様になってるんだろう?
- 直接ポートを制御しても良いと思うんだけど、何か利点があるのかな?
void LED_Write(uint8 value)
{
uint8 staticBits = (LED_DR & (uint8)(~LED_MASK));
LED_DR = staticBits | ((uint8)(value << LED_SHIFT) & LED_MASK);
}
P6[7]なので、LED_MASK=0x80、LED_SHIFT=7で、LED_DRは、CYREG_PRT6_DRで 値は0x40005160u。
- って、レジスタアドレスが、何でこんなに大きな値なんだろ。
- まぁ、とにかくセットする対象ビット以外は、そのまま戻すようにしているけど、
uint8 LED_Read(void)
{
return (LED_PS & LED_MASK) >> LED_SHIFT;
}
- やってること自体は普通だけど、出力ルーチンとレジスタが違う。
- LED_PSは、CYREG_PRT6_PSで、値は0x40005096u
- GPIOの構造的は、PSoC1とそれほど大きな違いは無い様にみえる。
- PSoC1の時は、DRレジスタのWriteとReadでは、内部的に別のアドレスをアクセスしていたが、
- PSoC5LPでは、明示的にレジスタが分かれているというだけのことみたいだ。
- PSoC1では、出力レジスタの状態が読めないので、ソフト的にシャドウレジスタに書き込んでから出力したりしていたが、
- でも、いちいちI/Oルーチンを生成するような仕様にしなくても良かったんじゃないかなぁ。
[top]
- とりあえずDelayルーチンが無いか検索してみると、
- CyDelayCyclesと、CyDelayUs、CyDelayというのが見つかった。
- CyDelayCyclesは、MPUのクロックサイクルで決まる様だ
- CyDelayUsがus単位、CyDelayがms単位のディレイを設定できるらしいが、
- いろいろ制限もありそうなので使用には注意が必要かも
- Delayルーチンを使って、LED点滅プログラムを書いてみる
#include <project.h>
int main()
{
for(;;)
{
LED_Write(0);
CyDelay(100);
LED_Write(1);
CyDelay(100);
}
}
- ちゃんとDelayルーチンが効いているみたいだ。
- とりあえずディレイルーチンが用意されているのは有難い。
- 教科書の9章の例題を見てみる
- Cortex-M3コアの割込みによるDelayルーチンを作成して、LEDを点滅させるようだ
#include <project.h>
volatile uint32 gTick = 0; ;gTickは割込みで変動するので、volatile宣言しておく
CY_ISR(SysTick_Handler) ;割込みをマクロCY_ISRで定義する
{
gTick++; ;割込みルーチンは、gTickをインクリメントするだけ
}
void Delay(uint32 tick_delay) ;Delayルーチン
{
uint32 tick_entry = gTick;
while((gTick-tick_entry) < tick_delay); ;gTickの値が、tick_delayだけ増加するまで待つ
}
int main()
{
CyIntSetSysVector(15,(cyisraddress)SysTick_Handler); ;SysTick割込みベクタに、SysTick_Handlerのアドレスをセット
SysTick_Config(BCLK__BUS_CLK__HZ/1000); ;SysTick割込みの周期を1msに設定する
CyGlobalIntEnable; ;マクロCyGlobalIntEnableで、割込みを許可する
for(;;)
{
LED_Write(~LED_Read()); ;LEDを点滅させる
Delay(500); ;500msのディレイ
}
}
- 割込みベクタの#15が、どうやらSysTick割込みらしい
- #15の割込みベクタに、自分で作成した割込みルーチンSysTick_Handklerのアドレスをセット、
- SysTick割込みの周期を1msに設定してから、
- 割込みを許可すれば、SysTick割込みが実行されるということらしい。
- 初めから、タイマ割込みが用意されているのは、物凄く有難いなぁ。
- PSoCならではのやり方、ハードウェアだけで、LED点滅させてみる。
- システムクロック出力を、出力ポートに直結するだけ
- 出力ポートの設定は、HW Connectionにしておく必要がある
- でもクロック出力を1Hzに設定するだけで、1Hzが出力されるけど
- Workspace Explorerでcydwrをダブルクリック、そしてClockタブを選択すると、クロック設定を確認できる
- Clock_1は、Auto: ILOで、1Hzを出力していることが分かる
- さらにSystemクロックのどれかを選択した状態で、Edit Clock...をクリックすると、
- システムクロックのブロックダイアグラムが表示される
- ILO出力は1kHzに設定されているが、Clock_1はこれを分周して1Hzを生成しているようだ。
- 回路図上は見えないが、分周器が自動生成されて、接続されていると思われる
- デジタルブロックのカウンタを使ってるのか、専用の分周器があるのか、どっちなんだろう。。
- でも、これは分周器を自分で回路図上に置いた方が思ったような回路構成に出来る。
- 自動生成するとILOが使われてしまうので、周波数の精度が -50%,+100%と、とんでもなく悪くなってしまう
- 試しにAutoじゃなくて、32kHzのX'talを32768分周するように指定してみたが、エラーになってしまった
M0025:Clock Error: (Source clock is not enabled).
- 割込み処理で、マルチスレッドのプログラムを書くのは、いろいろ面倒だが、
- FreeRTOSは、複数のタスクを扱うためのフリーライセンスのRTOS(リアルタイムOS)らしい
- その中から必要なファイルというものを、FreeRTOSというディレクトリに纏めておく
- Project→Build Setting...を選択、ARM GCC→Compiler→Generalで、
- Additional Include Directoriesで、..\FreeRTOSのパスを追加しておくことを忘れないように
- コンパイルしたらdevice.hが無いと言われたので探してみると、
- project.hをincludeしているだけのファイルだったが、追加しておく
#inclued <project.h>
- コンパイルで、以下のWarningが出る。なんか仕様が変わったのかな?
pointer targets in passing argument 2 of 'xTaskGenericCreate' differ in signedness [-Wpointer-sign]
- FreeRTOSで、LEDを点滅するタスクを起動する
- 教科書の例題の通りに入力
- 111ページのリストで、CyRamVectorsのextern宣言がエラーになる。voidは要らないので取る
#include <project.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#define TASK_STACK_LED 128 //タスクに割り当てる領域サイズ
#define TASK_PRIORITY_LED (tskIDLE_PRIORITY+1) //タスクのプライオリティ
void Task_LED (void *pvParameters); //関数のプロトタイプ
void prvHardwareSetup(void);
void vApplicationStackOverflowHook( //RTOSのエラー処理用
xTaskHandle pxTask,
signed char *pcTaskName)
{
taskDISABLE_INTERRUPTS();
for(;;);
}
void vApplicationMallocFailedHook(void) //同上
{
taskDISABLE_INTERRUPTS();
for(;;);
}
void prvHardwareSetup(void) //RTOS初期化
{
extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
extern cyisraddress CyRamVectors[];
CyRamVectors[11]
= (cyisraddress) vPortSVCHandler;
CyRamVectors[14]
= (cyisraddress) xPortPendSVHandler;
CyRamVectors[15]
= (cyisraddress) xPortSysTickHandler;
}
int main()
{
prvHardwareSetup();
CYGlobalIntEnable;
xTaskCreate(Task_LED, \ //タスクの登録
(signed portCHAR *)"LED", \
TASK_STACK_LED, \
NULL, \
TASK_PRIORITY_LED, \
NULL);
vTaskStartScheduler(); //RTOSカーネル起動
while(1); //無限ループ
}
void Task_LED(void *pvParameters) //タスクルーチン
{
const portTickType xDelay
= 200 / portTICK_RATE_MS;
while(1)
{
LED_Write(~LED_Read()); //LED点滅
vTaskDelay(xDelay); //RTOSのディレイルーチン
}
}
- このプログラムだと、タスクが1つだけなので、
- RTOSのありがたみは、全く分からないが、ちゃんと動くことは確認できた
- vTaskDelayは、単なるディレイじゃなくて、
- カーネルに制御を戻して、他のタスクに処理をさせる意味合いもあるらしい
- 最初、コンパイルすると、port.hでSyntaxエラーが出てかなり悩んだ。
- 結局、プロジェクトの作成時にデバイス名を間違えてPSoC3を選択していただけだった。
- Project→device selecterでデバイスを選択しなおして、Clean-Buildした
- Additional Include Directories指定は無効になるので、再設定が必要
[top]
- UART接続を、USB経由で行うUSBUARTを使ってみる
- PSoC Creatorで、回路図(cysch)上にUSBUART(CDC)を置く
- 次に、cydwrでPortを設定する。
- ポートは固定なので、P15[7],P15[6]に設定される
- 後は、cydwrのClockタブで、USBのクロックを設定する
- IMOを24MHzで発振させ、2逓倍の48MHzをUSBのクロックとする
- UARTで、送信されたデータをそのまま返すだけのプログラム
- そのままだとつまらないので、大文字に変換して返すようにしてみた
#include <project.h>
uint8 Count;
uint8 Buffer[128];
void ToUpper(uint8 *s) {
while ( *s ) {
*s = toupper((unsigned char)*s); //小文字→大文字変換
s++;
}
}
int main()
{
/* Initialization Code: */
CYGlobalIntEnable;
USBUART_1_Start(0, USBUART_1_3V_OPERATION); //USBUARTをスタート
while(!USBUART_1_bGetConfiguration()); //ホストとの接続待ち
USBUART_1_CDC_Init(); //CDCの初期化
/* Main Loop: */
for(;;) {
Count = USBUART_1_GetCount(); //受信文字数
if(Count != 0) {
USBUART_1_GetAll(Buffer); //受信データをバッファに入れる
ToUpper(Buffer); //大文字に変換
USBUART_1_PutData(Buffer, Count); //データを送信する
while(!USBUART_1_DataIsReady()){} //送信完了待ち
}
}
}
- USBUARTのDATASHEETを見たんだけど、サンプルが全くないので困る
- PSoC1のモジュールのDATASHEETには必ず簡単なサンプルがあって、とても分かりやすかったんだけど。。
- USBケーブルを接続しても、USB機器が認識されない旨のダイアログが一瞬出るだけ。
- デバイスマネージャをみても、それらしいデバイスが無い。
- 結構悩んだが、結局、PSoC Creatorを終了させてから接続したら、認識した
- というか、USB書込み用のUSB接続デバイスが残っていて、邪魔していたようだ。
- 認識すると、デバイスマネージャで、HID(ヒューマンインターフェースデバイス)に、!(びっくりマーク)が付いているので、
- ドライバは、プロジェクトのcydsn→Generated_Source→PSoC5ディレクトリ以下に、USBUART_1_cdc.infという名前で生成されているので、
- インストールが成功すると、COMxxというように、COMポートとして認識されるので、
- TeraTermのようなターミナルソフトで、接続してテストしてみる
- 正常に書き込めていれば、ターミナルでタイプした文字が、大文字に変換されて表示される。
- ドライバのインストールは、最初の1回だけ行えば良い。
- ドライバを一度インストールしてしまえば、PSoC Creatorを終了させなくても
- USBコネクタの差換えで、HIDデバイスとしてちゃんと認識するようになる。
- 教科書で独自のAPI関数を紹介している。便利そうなので使ってみる。
- Project→Build Setting...で、以下の設定
- Compiler→General→Additional Include Derectoriesに、../API
- Linker→General→Additional Librariesに、m(math.hを使うため)
- COM_printfは、snprintfの独自版で文字列に変換してから、USBUARTで出力している様だ
- USB接続されていない場合の処理とか、いろいろ参考になる
- 教科書のサンプルを、FreeRTOSを使わないように簡単にしたもの
#include <project.h>
#include <math.h>
#include "com.h"
#include "utility.h"
uint8 i;
uint8 ch;
uint8 str[16];
uint8 *pstr;
int32 tcyc;
int main()
{
CYGlobalIntEnable;
Init_COM(); //USBUARTの初期化、ホスト側と接続したらCDC初期化、出来なければUSBUART停止
COM_Wait_PC_Term(); //ホスト側端末の起動待ち、ホスト側からのキー入力を待つ
for(;;) {
COM_printf(">Tcyc? "); //1周期のサイクル数を入力
for (i=0; i<16-1 ;i++) {
while(!USBUART_1_DataIsReady()){}
USBUART_1_GetData(&ch,1);
USBUART_1_PutData(&ch,1);
if (ch=='\r') break;
str[i] = ch;
}
str[i] = '\0';
pstr = str;
if (xatoi(&pstr, &tcyc) && (tcyc>0)) { //入力を数値化して
double fTH,fSIN;
int32 iTH,iSIN;
COM_printf("\n\rTcyc = %d\n\r",tcyc);
for (iTH=0; iTH<=tcyc; iTH++) { //グラフを表示
fTH = ((double)iTH)/((double)tcyc)*2*M_PI;
fSIN = sin(fTH);
iSIN = (int32)((fSIN+1.0)*20);
COM_printf("%3d ",iSIN);
for (i=0; i<iSIN; i++) COM_printf(" ");
COM_printf("*\n\r");
}
COM_printf("\n\r");
}
}
}
- プログラムの動作
- キー入力するまで、"Push any Key to Start..."を表示し続ける
- プロンプト">Tcyc?"を表示して入力待ち
- 数値を入力してReturnキーを押すと、サイクル数を表示して、
- さらに、1周期分のグラフをテキストで表示する
- プロンプト表示以降を、繰り返し実行する
[top]
- 雑誌の付録のボードは、Creatorでそのまま書けるし、電源はUSBから取れるので、とても使いやすい
- ただUSBUARTとか使う場合、書込み後にケーブル差換えがちょっと面倒だった。
- PSoC5LPが、PSoC1と比較して、良いと思ったのは
- MPUがかなり高速になっている。通信I/Fもかなり高速化されている
- タイマ割込みが、初めからあるのが楽で良い
- Quadrature Decoderやシフトレジスタモジュールの追加
- ちょっとがっかりだったのは、
- アナログの構成がPSoC1とかなり違う、特にSCFモジュールが無いのが悲しい
- 入出力端子が専用化されて、少し自由度が減った気がする
- DFB(デジタルフィルタブロック)に関しては、いまいち理解できなかった。
- プログラム可能なDSPになっていて、いろいろ出来そうなんだけど、ちょっと難しそう
- PSoC1のMPUだけ高速版に交換して、アナログ構成はそのままにして欲しかったなぁ。
- デジタルブロックは、単純なシフトレジスタやUP/DOWNカウンタが欲しいとか
- レジスタ読み書きが、カウンタ動作に影響しないようにして欲しいとかあるけど、基本そのままで
- Creatorは、回路図で配線できるのは分かりやすいんだけど、
- Designerのパズルみたいなところや、自由度の高さはやはり良かったと思う。
[top]
[電子工作関連に戻る]
⇒ Disqusの広告がうるさすぎるので基本は非表示にしました