SSブログ

サイクルタイムを測定しよう (3) [PSoC]このエントリーを含むはてなブックマーク#

実験回路

タイマを使用して、サイクルタイムを測定するしかけができたので、具体的にサイクルタイムを測定していきます。 今回は、命令フェッチのサイクル数にこだわってみます。

測定ルーチンをちょっと変更

前回の記事では、 measure() 関数の中でカウンタレジスタを直接読み出していました。 そのため、以下のように .L5 に格納されたカウンタレジスタのアドレスを CPU のレジスタ r3 に格納する命令が入ってしまいます。

  38:.\main.c      ****     uint32  s;
  39:.\main.c      ****     uint32  e;
  40:.\main.c      ****     
  41:.\main.c      ****     s = Timer_COUNTER_REG;
  88 0000 024B     		ldr	r3, .L5
  89 0002 1868     		ldr	r0, [r3]
:
:
  43:.\main.c      ****     e = Timer_COUNTER_REG;
  92 0004 1B68     		ldr	r3, [r3]
  44:.\main.c      ****     
  45:.\main.c      ****     return e - s;
  95 0006 181A     		sub	r0, r3, r0
  46:.\main.c      **** }
  98              		@ sp needed
  99 0008 7047     		bx	lr
:
:
 102              	.L5:
 103 000c 08012040 		.word	1075839240

インストラクションもレジスタアドレスも Flash ROM に格納されますので、レジスタアドレスの取り出しが命令フェッチに影響を与える可能性があります。 そこで、レジスタアドレスを関数の引数として渡す方式に改めました。

// Measure the execution cycle time
uint32 measure(reg32 *reg) __attribute__((aligned(256)));
uint32 measure(reg32 *reg) {
    uint32  s;
    uint32  e;
    
    s = *reg;
:
:
    e = *reg;
    
    return e - s;
}

:
:

    // Measure the cycle time
    for (i = 0; i < N_DIFF; i++) {
        diffs[i] = measure(Timer_COUNTER_PTR) & Timer_16BIT_MASK;
    }

こうすると、レジスタアドレスは CPU のレジスタに格納されたまま使用されます。

  39:.\main.c      ****     uint32  s;
  40:.\main.c      ****     uint32  e;
  41:.\main.c      ****     
  42:.\main.c      ****     s = *reg;
  89 0000 0268     		ldr	r2, [r0]
:
:
  43:.\main.c      ****     e = *reg;
  92 0002 0368     		ldr	r3, [r0]

また、 "aligned(256)" という属性を追加して、関数の配置アドレスがブレないようにしています。

レジスタ読み出しが連続した場合

まず、タイマレジスタのアクセスが連続した場合について調べます。

  42:.\main.c      ****     s = *reg;
  89 0000 0268     		ldr	r2, [r0]
  43:.\main.c      ****     e = *reg;
  92 0002 0368     		ldr	r3, [r0]

この場合、タイマカウンタの値の差分をとると、 "ldr" 命令一回分のサイクル数を求めることが出来ます。 UART への出力は、以下のようになりました。

Freq:48
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004

Freq:24
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004

Freq:12
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004
0004 0004 0004 0004 0004 0004 0004 0004

CPU の周波数が変わっても4サイクルでした。 もしかしたら、プログラムの配置アドレスに依存するかもしれないと考えて、これらの前に "nop" 命令を入れて、配置を変更してみました。

    asm(
        "nop\n"   // 14
        "nop\n"   // 13
        "nop\n"   // 12
        "nop\n"   // 11
        "nop\n"   // 10
        "nop\n"   // 9
        "nop\n"   // 8
        "nop\n"   // 7
        "nop\n"   // 6
        "nop\n"   // 5
        "nop\n"   // 4
        "nop\n"   // 3
        "nop\n"   // 2
        "nop\n"   // 1
        "label_1:\n"
    );
    s = *reg;
    e = *reg;

結果は、いずれの場合も4サイクルのままでした。 PSoC 4 の Flash ROM は、バス周波数が高くなる場合には最大2サイクルのウェイトサイクルが入ります。 しかし、いずれのバス周波数の場合でも同じサイクル数を示しています。 これは、 Flash ROM から読み出した命令を先読みするプリフェッチ機構が存在するためです。 プリフェッチのおかげで、命令を連続して実行する場合には、 Flash ROM のアクセス時間が見えなくなります。


サイクルタイムを測定しよう (1) [PSoC]このエントリーを含むはてなブックマーク#

サイクルタイム測定キット

PSoC 4 にも CPU として Cortex-M0 が搭載されています。 普段は気にすることもないのですが、プログラムの実行時間をタイマを使って測定してみました。

実験回路

回路は、タイマと UART で構成されています。 タイマを16ビットのフリーランニングカウンタとして使用して、プログラムの実行前と実行後に読み出したカウンタの値の差を計算します。 この測定を何回か繰り返したら、まとめて UART から測定値を表示します。

実験では、 Internal Main Oscillator (IMO) の周波数を変更しながら、実行時間を測定します。 そのため、 UART の駆動クロックは、外部から与える構成にしました。

メイン関数

プログラムは、いくつかの部分で構成されています。 まずは、メイン関数から見ていきます。

int main(void) {
    // CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    Timer_Start();
    UART_Start();
        
    loop(48);
    loop(24);
    loop(12);

    for(;;) {
        /* Place your application code here. */
    }
}

コンポーネント "Timer" と "UART" の初期化を行ったら、 loop() 関数で測定を三回行います。 loop() 関数の引数には、 IMO の周波数を MHz 単位で与えます。 この実験では、 48MHz, 24MHz, 12MHz の三種類の周波数について測定を行います。 測定が終わったら、無限ループに突入します。

測定ループ

loop() 関数は、三つの部分で構成されています。

// Measure the cycle time repeatedly
// The CPU frequency is set as specified in argument
void loop(uint32 freq) {
    uint32  i;
    char    sbuf[32];
    
    // Set CPU frequency
    setFreq(freq);
    
    // Measure the cycle time
    for (i = 0; i < N_DIFF; i++) {
        diffs[i] = measure() & Timer_16BIT_MASK;
    }

    // Show the measurement results
    sprintf(sbuf, "\r\nFreq:%ld\r\n", freq);
    UART_UartPutString(sbuf);
    for (i = 0; i < N_DIFF; i++) {
        sprintf(sbuf, "%04X", diffs[i]);
        UART_UartPutString(sbuf);
        if ((i & 7) < 7) {
            UART_UartPutChar(' ');
        } else {
            UART_UartPutCRLF(' ');
        }
    }

    // Wait for UART communication completed
    while (!(UART_GetTxInterruptSource() & UART_INTR_TX_UART_DONE));
}
IMO の設定
最初は、 IMO の周波数を設定するために setFreq() 関数を呼び出します。 中身は、のちほど。
サイクルタイムの測定
サイクルタイムの測定を measure() 関数で行い、 測定結果は diff[] 配列に格納していきます。 測定は、 N_DIFF で示される回数だけ繰り返されます。
測定結果の表示
測定結果は、 UART から出力されます。 出力形式は、16進数です。

IMO の設定関数

IMO の設定を変える時には、発振周波数の変更以外に、いくつかパラメータを調整する必要があります。

// Set the CPU clock frequency
void setFreq(uint32 freq) {
    // Select a longest wait cycle
    CySysFlashSetWaitCycles(48);
    
    // Change the IMO and related parameters
    CySysClkWriteImoFreq(freq);
    CySysFlashSetWaitCycles(freq);
    CyDelayFreq(freq * 1000000u);

    // Reconfigure the UART clock frequency
    Clock_UART_SetDivider(cydelayFreqHz/BAUDRATE/UART_UART_OVS_FACTOR-1);
}

まず、 CySysFlashSetWaitCycles(48) で、 Flash ROM のウエイトサイクル数を最大設定にします。 PSoC 4 に内蔵されている Flash ROM は、システムクロックが早い場合には、ウエイトサイクルを入れる必要があります。 この関数で、最大周波数 48MHz の時に使用されるウエイトサイクル、具体的には2サイクルのウエイトが挿入されます。

次に CySysClkWriteImo() 関数で IMO の周波数を変更します。 さらに、この周波数に適切なウエイトサイクルを Flash ROM に設定します。 引き続き、 CyDelayFreq() 関数を使って、サイクルタイムの測定で使用されている CyDelay() 関数などで使用される周波数パラメータを変更します。

最後に UART に使用されるクロックの周波数を変更します。 この時に、別途宣言されている BAUDRATE を使用して、 UART のボーレートを調整しています。 この実験では、 IMO を 12MHz まで落としているので、 UART のボーレートは 9600bps と低めの値に設定されています。

サイクルタイムの測定

プログラムのサイクルタイムを測定するには、プログラムの実行前と実行後にカウンタの値を読み出します。

// Measure the execution cycle time
uint32 measure(void) {
    uint32  s;
    uint32  e;
    
    s = Timer_COUNTER_REG;
    CyDelay(1);
    e = Timer_COUNTER_REG;
    
    return e - s;
}

実行前のカウンタ値を s に格納し、実行後のカウンタ値を e に格納します。 そして、これらの差分を関数の値として返します。

この実験では、 CyDelay(1) の実行時間(約1ミリ秒)を測定しています。

実行結果

この実験し使用した機材は、 CY8C4247AZI-M485 を搭載した CY8CKIT-044 です。 実行結果は、以下のようになりました。

Freq:48
BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8
BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8
BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8
BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8 BBB8

Freq:24
5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0
5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0
5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0
5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0 5DF0

Freq:12
2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A
2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A
2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A
2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A 2F0A

48MHz のとき 0xBBB8 (48056) サイクル、 24MHz のとき 0x5DF0 (24048) サイクル、 12MHz のとき 0x2F0A (12042) サイクルを要しています。 CyDelay() 関数は、 CPU の実行時間を利用して遅延時間を作り出していますので、このくらいの精度が有れば十分でしょう。

プロジェクトアーカイブ

この記事で作成したプロジェクトは、このファイルの拡張子を "zip" に変更すると再現できます。


新・ウォッチドッグタイマを使ってみる ~PSoC 40xx 編~ [PSoC]このエントリーを含むはてなブックマーク#

低速クロック設定ダイアログ

前回の記事「新・ウォッチドッグタイマを使ってみる ~PSoC 42xx 編~」では、 PSoC 42xx を題材に Interrupt Service Routine (ISR) を自動生成させて楽をする方法をさぐりました。 今回は、 PSoC 40xx で試してみます。

WDT 設定は簡易版

WDT 設定部

PSoC 40xx の低速クロック設定ダイアログにも Watch-Dog Timer (WDT) 設定が追加されました。 ただし、 PSoC 40xx は、 PSoC 42xx とは異なり、 WDT カウンタを1系統しか持っていません。 そのため、配置されている設定ブロックも一つだけです。

また、16ビットのフリーランニングカウンタが使用されている所も異なっています。 このカウンタの値が MATCH レジスタの値と等しくなったら割り込みが発行されます。 デフォルトでのカウンタの周期は 65536/40kHz=1.6sec となります。

MATCH レジスタとの比較では、上位ビットの比較結果を無視する事によって、割込みの周期を短く設定することが出来ます。 例えば、上位2ビットを無視すると周期は 16384/40kHz=0.4sec になります。 ドロップダウンリスト "Period" で周期を設定して、何ビットを無視するのか決定する事ができます。 この実験では、約 0.5 秒周期で割り込みを発生させたかったので、 409.6ms を使用します。

WDT 設定部では、動作モードを "Free Running Timer" と "Watchdog (w/ Interrupt)" から選択する事ができます。 これら二つのモードには、ウォッチドッグリセットが有効になるか無効になるかの違いが有ります。 "Free Running Timer" ではウォッチドッグリセットが無効に、 "Watchdog (w/ Interrupt)" ではウォッチドッグリセットが有効になります。 つまり、 "Free Running Timer" を使うと餌をやる手間がかかりません。 どちらにしても、フリーランニングカウンタの周期で割り込みフラグがセットされるところは同じです。 また、チェックをはずして WDT 設定そのものを無効にするとウォッチドッグリセットが無効になります。

ウォッチドッグリセット機能は、フリーランニングカウンタが MATCH レジスタの値と一致するイベントが3回発生するまでにウォッチドッグに餌をやらないとリセットが発生する仕組みです。 餌をやるためには、割り込みフラグをクリアします。 割込み機能を使う場合、通常は割込みルーチン内でフラグをクリアします。 そのため、メインループが暴走しても割り込みさえ正しく処理できればウォッチドッグリセットは発生しません。 どうやら、割込み機能も使用する場合、ウォッチドッグとしての機能は期待できないようです。

ウォッチドッグリセットの機能は、 Technical Reference Manual (TRM) によるとリセット直後は有効になっています。 ところが、 PSoC Creator が生成しているコードでは、デフォルト状態で無効になっているように見えます。 これは、 "Cm0Start.c" ファイルの中でリセット直後にウォッチドッグリセットを無効にしているのが原因でした。 ウォッチドッグリセットの機能から考えると、デフォルトでは有効にすべきですが、そうはなっておらず、デフォルトの状態を設定することも出来ないように見えます。 ウォッチドッグリセットを使う時には、注意が必要です。

ISR はあるけれど

割り込み関連コンポーネント

PSoC 40xx でも、 "CYLFClk.c" に Interrupt Service Routine (ISR) が作成されています。 ところが、 ISR 設定部が存在しないのに加えて、割込みベクタとして設定もされていないので、デフォルトのままでは使用できません。 そのため、従来通りに Global Signal コンポーネントの出力を Interrupt コンポーネントに接続して割込み機能を実現します。

ソースコードが簡単になったかな?

LED 出力部

ウォッチドッグタイマを使った「Lチカ」の完成です。 ソースコードは、以下のようになりました。

#include <project.h>

void Wdt_Callback(void) {
    Pin_LED_Write(~Pin_LED_Read());
}

int main()
{
    // Initialize WDT interrupt
    Wdt_int_StartEx(CySysWdtIsr);
    CySysWdtSetInterruptCallback(Wdt_Callback);
    CySysWdtUnmaskInterrupt();
        
    CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */

    for(;;)
    {
        /* Place your application code here. */
    }
}

初期設定部分で使用している関数 CySysWdtUnmaskInterrupt() で、ウォッチドッグタイマによる割り込みを受け付けます。 コード量は、多少減ったんじゃないかな?

今回の記事で使用した手法は、ウォッチドッグタイマのオーバーフローを利用しています。 これは、割込みが発生するたびに MATCH レジスタを書き換えていた以前の記事とは異なり、周期の解像度で劣ります。 もっと解像度を上げたい場合には、自前で ISR を記述する必要があるでしょう。

プロジェクトアーカイブ

この記事で作成したプロジェクトは、このファイルの拡張子を "zip" に変更すると再現できます。

参考文書

PSoC 4 Low-Frequency Clock (cy_lfclk), Version 1.0
現在の PSoC Creator では、 "Watchdog Timer" などの LFCLK を利用した機能は、 cy_lfclk という独立したコンポーネントにまとめられています。 "Watchdog Timer" の使い方などの詳しい情報は、この文書に書いてあります。
PSoC 4000 Family: PSoC[レジスタードトレードマーク] 4 Architecture Technical Reference Manual (TRM)
PSoC 40xx の内部構成について書かれた文書です。 残念ながら、ウォッチドッグについての記述は充実しているとは言い難い状態です。

新・ウォッチドッグタイマを使ってみる ~PSoC 42xx 編~ [PSoC]このエントリーを含むはてなブックマーク#

低速クロック設定ダイアログ

以前の記事「CY8CKIT-049 Prototyping Kit で簡単に Bootloader を呼び出す」で実験したように PSoC 42xx でウォッチドッグ・タイマを周期タイマとして使用するために、次々と API を呼び出すコードを記述していました。 賢明なる皆さんはお気づきと思いますが、長々とコードを書いて何かをしようとするのは、 PSoC らしくありません。

もっと楽が出来ないかと思っていたところ、 PSoC Creator のクロック設定ダイアログが、最近変わってきていることに気が付きました。 おっ、これを使えば、もっと楽にウォッチドッグ・タイマが使えるんじゃないかな?

以前の低速クロック設定ダイアログは、そっけない

PSoC Creator 3.0 SP2 の低速クロックダイアログ

ちなみに、以前の低速クロック設定ダイアログは、このように簡素なものでした。 出来るのは、ILO を使うか否かを指定することだけです。 他の細かい設定が必要な場合は、コードを書いて解決するという思想が表れています。

WDT 設定が増えた

WDT 設定部

ダイアログの変更点を見ていきます。 第一に LFCLK を使用した Watch-Dog Timer (WDT) を設定する箇所が増えました。

PSoC 41xx/42xx では、 WDT と称されるタイマが3系統存在しています。 これら3系統のタイマのそれぞれに対応する箱が用意され、これらが個別に設定できるようになっています。

WDT0 の箱でチェックボックスをチェックすると、詳細な設定を変更できるようになります。 まず、 "Mode" では、 WDT をどのような用途に使うかを指定します。

Free Running Timer
このモードでは、 WDT の16ビットカウンタをまるごと 65536 分周のタイマとして使用します。 そのため、割り込み周期は 65536/32kHz=2.0sec に固定されます。
Periodic Timer
このモードでは、16ビットカウンタの比較機能を利用して、任意の周期のタイマとして使用します。 割込みが発生するまでのカウント数は、 "Divider (16-bit)" で設定できます。
Watchdog
このモードでは、ウォッチドッグ・タイマ本来の機能、すなわち暴走状態を検出する機能を使用します。 決められた時間内にウォッチドッグに餌をやらないと、リセットがかかります。
Watchdog (w/ Interrupt)
このモードでは、指定された周期で割り込みが発生します。 それと同時に上記割り込みが3回発生するまでにウォッチドッグに餌をやらないとリセットが発生します。 ひとつの16ビットカウンタを周期タイマとウォッチドッグの二つの用途に同時に利用します。

WDT0 の設定ブロック

今回のプロジェクトでは、 "Periodic Timer" の周期を 16000 カウントに設定して、 16000/32kHz=0.5sec ごとに割り込みを発生させます。 このダイアログでは、分周比を設定すると周波数と周期が表示されます。 逆に周波数や周期を入力する機能はありませんので、周波数などから分周比を逆算して入力する必要があります。

ISR 設定が増えた

ISR の設定ブロック

クロック設定ダイアログには、もうひとつ変更箇所が有ります。 "Timer (WDT) ISR" という箱が追加され、そこにラジオボタンが二つ並んでいます。 ラジオボタンには、 "Auto generated" と "User provided" とラベルが付いており、 Interrupt Service Routine (ISR) を自動的に生成するか、ユーザが記述するかを選べるようになっています。 ISR と呼ばれているのは、たとえば以下のようなコードです。

// Watchdog 割り込みサービスルーチン
CY_ISR(int_Wdt_isr) {
    CySysWdtClearInterrupt(CY_SYS_WDT_COUNTER0_INT);
    LedMachine();
    BootMachine();
}

これは、以前の記事で使用したものです。 CySysWdtClearInterrupt() が割り込みフラグをクリアする部分で、 LedMachine() と BootMachine() がユーザが真に周期的な実行を必要としている処理です。 "User provided" を選択すると、フラグの処理を含めてすべての記述をコーディングする必要があります。

この例では、三つのタイマのうち、一つだけを扱っていますが、場合によっては一つの ISR で三種類のタイマを扱う必要が有ります。 "Auto generated" を選択すると、三つのタイマすべてについての処理が自動的に生成されます。 周期的な実行を必要とする処理は Callback 関数として、別途 CySysWdtSetInterruptCallback() 関数で設定します。

ソースコードが簡単になった

Lチカ回路図

実際に周期割り込みを使った「Lチカ」をコーディングしてみました。 これが、回路図です。

#include <project.h>

void Wdt_Callback(void) {
    Pin_LED_Write(~Pin_LED_Read());
}

int main(void) {
    CySysWdtSetInterruptCallback(CY_SYS_WDT_COUNTER0, Wdt_Callback);
    
    CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */

    for (;;) {
        /* Place your application code here. */
    }
}

一方、コードはこうなりました。 Wdt_Callback() が、周期的に呼び出される Callback 関数です。 "Pin_LED" 端子の状態を反転させるだけです。

この Callback 関数を main() 関数で設定しています。 Wdt_Callback() は、 "WDT0" で駆動されますので、 CySysWdtSetInterruptCallback() 関数の第一引数に CY_SYS_WDT_COUNTER0 を指定しています。 WDT1 と WDT2 についても、独立した Callback 関数を設定する事ができます。

プロジェクトアーカイブ

この記事で作成したプロジェクトは、このファイルの拡張子を "zip" に変更すると再現できるようになります。

参考文書

PSoC 4 Low-Frequency Clock (cy_lfclk), Version 1.0
現在の PSoC Creator では、 "Watchdog Timer" などの LFCLK を利用した機能は、 cy_lfclk という独立したコンポーネントにまとめられています。 "Watchdog Timer" の使い方などの詳しい情報は、この文書に書いてあります。

関連商品

PSoC 4200 Prototyping Kit

PSoC 4200 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス
PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

MBR3 をホストから使ってみた (3) [PSoC]このエントリーを含むはてなブックマーク#

実験セット

今回は、ちょっと横道へ。 MBR3110 では、 LED は5本しか扱えません。 もっと、多くの LED たとえば7セグメント LED などを扱いたい時には、 PSoC 40xx を使えば解決しできます。

7セグメント LED をつなぎたい

7セグメント LED 制御モジュール

SOIC16 パッケージの MBR3110 は、プログラムを作りたくない人には便利なのだけど、代わりに自由度が大幅に制限されてしまい、7セグメント LED を接続することもできません。 でも、同じパッケージの CY8C401x であれば、対応可能です。

そこで、設計してみたのが、このような基板です。 MBR3 と同じように I2C で制御できます。


PSoC 内部回路

中身は簡単です。 ホストと通信を行う EZI2C コンポーネントと7セグメント LED を制御するための出力端子が並んでいるだけです。 これに、 Watchdog タイマと Systick タイマを組み合わせて、ソフトウェアで LED を制御します。

ファームウェアは、こうなった

ファームウェアですべての制御を行っています。

#define     MAX_DIGIT       (16)
#define     DUTY_BASE       (16)
#define     DUTY_FREQ       (100) // frame frequency
#define     DUTY_CLOCK      (DUTY_FREQ*DUTY_BASE)
#define     SYSTICK_PERIOD  (12000000/DUTY_CLOCK)
#define     NDIGIT_DEFAULT  (0)
#define     DUTY_DEFAULT    (4)
#define     SPEED_DEFAULT   (8)
#define     INTERVAL_DEFAULT (2)

struct i2c_frame {
    uint8       ndigit;
    uint8       digit[MAX_DIGIT];
    uint8       duty;
    uint8       speed;
    uint8       interval;
};

EZI2C コンポーネントで送受信するデータは、仮想レジスタとして実装されています。 レジスタの中身を表しているのが、 struct i2c_frame 構造体です。 それぞれのレジスタは、以下のような意味を持っています。

ndigit
LED で表示する桁数を表します。
  • 0 の時 LED は消灯します。
  • 1 の時 LED は設定されたパターンにしたがって連続して点灯します。
  • 2 以上の時 LED は設定されたパターンを順に表示していきます。このとき、一桁目を表示する前に消灯期間を設けて一桁目を表現します。
初期値は NDIGIT_DEFAULT=0 です。
digit[MAX_DIGIT]
表示するパターンを格納します。 最大桁数は、 MAX_DIGIT であらわされる 16 ケタです。
duty
LED の明るさを 0 から 16 までの値で示します。 最大値は DUTY_BASE=16 で、初期値は DUTY_DEFAULT=4 です。 LED は、ソフトウェア PWM で制御されています。 キャリア周波数は DUTY_FREQ であらわされており、 100Hz となっています。 電流制限抵抗を適当に 220Ω にしたら意外に明るかったので、明度を落とす目的で入れてあります。
speed
2ケタ以上の表示を行う時の一桁あたりの表示時間を示します。 初期値は SPEED_DEFAULT=8 です。 単位は、 Watchdog タイマの周期で、このプロジェクトでは 102.4ms に設定されています。
interval
2ケタ以上の表示を行う時の桁と桁の間に設ける消灯時間を示します。 初期値は INTERVAL_DEFAULT=2 です。 LED に同じパターンを連続して表示させると、桁と桁の区切りが分からなくなります。 これを防ぐために、桁と桁の間に消灯時間を設けて、区切りを表現しています。
void Led_Write(uint8 seg) {
    (seg & 0x01)?CyPins_ClearPin(Seg_A ):CyPins_SetPin(Seg_A );
    (seg & 0x02)?CyPins_ClearPin(Seg_B ):CyPins_SetPin(Seg_B );
    (seg & 0x04)?CyPins_ClearPin(Seg_C ):CyPins_SetPin(Seg_C );
    (seg & 0x08)?CyPins_ClearPin(Seg_D ):CyPins_SetPin(Seg_D );
    (seg & 0x10)?CyPins_ClearPin(Seg_E ):CyPins_SetPin(Seg_E );
    (seg & 0x20)?CyPins_ClearPin(Seg_F ):CyPins_SetPin(Seg_F );
    (seg & 0x40)?CyPins_ClearPin(Seg_G ):CyPins_SetPin(Seg_G );
    (seg & 0x80)?CyPins_ClearPin(Seg_DP):CyPins_SetPin(Seg_DP);
}

LED に表示するパターンは、8ビットの値で表現されます。 Led_Write 関数では、この8ビットのパターンを使って LED のそれぞれのセグメントを制御しています。

static struct i2c_frame i2c_buffer;
static struct i2c_frame digit_buffer;

レジスタの値を保持するバッファを二組宣言します。 i2c_buffer は、 EZI2C コンポーネントから受信した値を格納します。 受信した値は、通信直後に digit_buffer にコピーして表示に使用する事で安全に表示を行うことができます。

static uint8 segment = 0;
static uint8 tick = 0;

CY_ISR(duty_isr) {
    if (digit_buffer.duty >= DUTY_BASE) {  // 100%
        tick = DUTY_BASE;
        Led_Write(segment);
    } else if (tick == 0) {  // turn OFF
        tick = DUTY_BASE;
        Led_Write(0);
    } else if (tick == digit_buffer.duty) {  // turn ON
        Led_Write(segment);
    }
    tick--;
}

void duty_init(void) {
    CyIntSetSysVector((SysTick_IRQn + 16), duty_isr);
    SysTick_Config(SYSTICK_PERIOD);
}

これらのコードで、ソフトウェア PWM の制御を行います。 ソフトウェア PWM には、 SysTick タイマを使用しています。

static uint8 position = 0;
static uint8 phase = 0;

void wdt_init(void) {
    CySysWdtDisable();
    memcpy(&digit_buffer, &i2c_buffer, sizeof i2c_buffer);
    position = 0;
    CySysWdtEnable();
}

void wdt_isr(void) {
    switch (digit_buffer.ndigit) {
        case 0: // BLANK
            segment = 0;
            phase = 0;
            break;
        case 1: // STATIC
            segment = digit_buffer.digit[position];
            phase = 0;
            break;
        default: // Multiple digits
            if (phase == 0) {
                if (position == 0) {
                    // Blank digit
                    segment = 0;
                } else {
                    segment = digit_buffer.digit[position - 1];
                }
                position++;
                if (position > digit_buffer.ndigit) {
                    position = 0;
                }
                phase = digit_buffer.speed - 1;
            } else {
                if (phase == digit_buffer.interval) {
                    // Interval phase
                    segment = 0;
                }
                phase--;
            }
            break;
    }
}

これらのコードで、複数桁の表現の制御を行っています。

int main()
{
    uint8 status;
    
    CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    EZI2C_Start();
    i2c_buffer.ndigit = 0;
    i2c_buffer.duty = 4;
    i2c_buffer.speed = 8;
    i2c_buffer.interval = 2;
    memcpy(&digit_buffer, &i2c_buffer, sizeof i2c_buffer);
    EZI2C_EzI2CSetBuffer1(sizeof i2c_buffer, sizeof i2c_buffer, (uint8*)&i2c_buffer);
    
    CySysWdtDisable();
    CySysWdtSetInterruptCallback(wdt_isr);
    duty_init();

    for(;;) {
        /* Place your application code here. */
        status = EZI2C_EzI2CGetActivity();
        if (status & EZI2C_EZI2C_STATUS_WRITE1) {
            wdt_init();
        }
        CySysPmSleep();
    }
}

EZI2C コンポーネントは、すべてのレジスタをホストから書き込み可能にしています。 メインループでは、 I2C インターフェイスから書き込み通信を待ち、 wdt_init 関数で書き込まれた値を使った表示を開始させる仕組みになっています。

ホスト側のソフトウェア

ホスト側には、以下のようなファームウェアを書き込みました。

#include "mbed.h"

// One I2C port is used to connect to MBR3.
// DIP9 and DIP10 are used for I2C but ports.
I2C i2c(p9, p10);

// Signal device register map
const uint8_t SIGNAL_NDIGIT = 0x00;

static const uint8_t seg_digit[] = {
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
    0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71,
};

// Node "Signal"
// Device: CY8C4014
// Function: 7-segment LED
const uint8_t signalAddr = 0x68 << 1; // 8bit I2C address

int main(void) {
    uint8_t cmd[8];
    uint32_t count = 0;
    
    for (;;) {
        // Put COUNT status to Signal
        cmd[0] = SIGNAL_NDIGIT;  // NDIGIT
        cmd[1] = 1; // 1 column
        cmd[2] = seg_digit[count & 0x0000000Fu];
        i2c.write(signalAddr, (char *)cmd, 3);
        count++;
        
        // Wait for while
        wait(1);
    }
}

一秒ごとに表示パターンを送り込んで、カウンタの値を表示させています。 特に有意義な事はしていません。

プロジェクトアーカイブ

この記事を書くために作成した PSoC 4 のプロジェクトは、このファイルの拡張子を ZIP に変更して展開すると再現できます。

関連商品

mbed NXP LPC1768

mbed NXP LPC1768

  • 出版社/メーカー: スイッチサイエンス
  • メディア: おもちゃ&ホビー

MBR3 をホストから使ってみた (2) [PSoC]このエントリーを含むはてなブックマーク#

MBR3110 の実験セット

前回は、 SOIC8 パッケージの MBR3102 を使用した静電容量ボタンを使ってホストから制御してみました。 今回は、 SOIC16 パッケージの MBR3110 を使用して、 LED とブザーを制御します。

回路図は、これだ

MBR3110 の実験回路

作成したハードウェアは、このような構成になっています。

静電容量ボタンが CS2 と CS3 に配置してあります。 そして、それぞれに対応した LED が GPO2 と GPO3 に配置してあります。 MBR3 では、静電容量センサと GPO 出力を1対1で対応させて、プログラムをしなくても、静電容量ボタンをタッチすると LED が点灯する、あるいは論理信号を出力するという使い方が出来るようになっています。 通常の使い方では、 CS2 にタッチすると GPO2 に出力が現れます。

さらに、圧電ブザーが接続されています。 静電容量ボタンにタッチすると圧電ブザーに矩形波を与えて、音を鳴らす仕組みです。

MBR3 の設定

CapSense Sensor Configuration

次に MBR3 の設定を見ていきます。

静電容量センサの設定は、 CS2, CS3 をボタンとして設定しただけで、他の部分はデフォルトのままです。


Global Configuration

その他の設定で、かなりクセのある使い方をしています。

まず、このデバイスは、アドレス 0x60 に設定されています。 前回のデバイスとは異なるアドレスですので、ふたつのデバイスを同一のバスに接続する事が出来ます。

"Enable GPIO host control" にチェックが入っています。 ここにチェックを入れない使い方では、静電容量センサ CS2/CS3 の状態が出力 GPO2/GPO3 に自動的に伝達されます。 ところが、チェックを入れるとボタンの状態が出力に伝達されなくなります。 そのかわりに、ホストが I2C から通信を行う事で GPO2/GPO3 の制御を行う事が出来るようになります。 GPO の出力論理は、デフォルトのままで「負論理のオープンドレイン出力」となっています。

さらに、 "Buzzer" の項目群が設定されています。 圧電ブザーは、ボタンと連動して鳴動するようになっており、ホストが制御する訳ではありません。 ボタンにタッチすると、 4000Hz の周波数で 100ms の期間、ブザーが鳴ります。

ホスト側のソフトウェア

ホスト側には、以下のようなファームウェアを書き込みました。

#include "mbed.h"

// Two LEDs are assigned as button status indicators
DigitalOut led3(LED3);
DigitalOut led4(LED4);

// One I2C port is used to connect to MBR3.
// DIP9 and DIP10 are used for I2C but ports.
I2C i2c(p9, p10);

// MBR3 device register map
const uint8_t MBR_GPO_OUTPUT_STATE = 0x80; // GPO STATE
const uint8_t MBR_BUTTON_STAT = 0xaa; // BUTTON_STAT

// Node "Katze"
// Device: MBR3110
// Function: Two button CapSense with Buz
const uint16_t katzeAddr = 0x60 << 1; // 8bit I2C address
const uint8_t katzeGreenButton = 0x04; // CS2
const uint8_t katzeRedButton = 0x08; // CS3
const uint8_t katzeGreenLed = 0x04; // GPO2
const uint8_t katzeRedLed = 0x08; // GPO3

int main(void) {
    uint8_t cmd[8];
    uint16_t katzeStat[1];
    uint8_t katzeLed;
    
    for (;;) {
        // Get status from Katze
        cmd[0] = MBR_BUTTON_STAT;  // 
        i2c.write(katzeAddr, (char *)cmd, 1);
        i2c.write(katzeAddr, (char *)cmd, 1);
        i2c.write(katzeAddr, (char *)cmd, 1, true);
        i2c.read( katzeAddr, (char *)katzeStat, 2);

        // Copy button status
        katzeLed =
            ((katzeStat[0] & katzeRedButton)?(katzeGreenLed):(0x00)) |
            ((katzeStat[0] & katzeGreenButton)?(katzeRedLed):(0x00));
        cmd[1] = ~katzeLed;
        cmd[0] = MBR_GPO_OUTPUT_STATE;
        i2c.write(katzeAddr, (char *)cmd, 1);
        i2c.write(katzeAddr, (char *)cmd, 1);
        i2c.write(katzeAddr, (char *)cmd, 2);
        
        // Show button indicator LEDs
        led3 = (katzeStat[0] & katzeRedButton)?(1):(0);
        led4 = (katzeStat[0] & katzeGreenButton)?(1):(0);
        
        wait(0.05);
    }
}

"BUTTON_STAT" レジスタから静電容量ボタンの状態を読み込んで、 mbed LPC1768 の LED3/LED4 に表示するという動作は、前回と同じです。 今回は、それに加えて、 MBR3110 の GPO2/GPO3 に接続された LED もホストから制御しています。 "GPO_OUTPUT_STATE" レジスタに値を書き込むと、 LED の点灯を制御することができます。 レジスタへ書き込みを行うときにも際には MBR3 をスタンバイ状態から起こすために "write" メソッドを三回発行しています。

CS2/CS3 の状態を GPO2/GPO3 にそれぞれ伝達すると、自動的に伝達されたものと見分けがつきませんので、このソフトウェアでは、あえて「赤色のボタンにタッチすると緑色の LED が点灯する」「緑色のボタンにタッチすると赤色の LED が点灯する」ようにしてみました。

参考文献

CY8CMBR3102, CY8CMBR3106S, CY8CMBR3108, CY8CMBR3110, CY8CMBR3116 CapSense® Express™ Controllers Registers TRM

I2C でアクセス可能なレジスタが解説されています。 MBR3 では、ここで記述されているレジスタに対するアクセスによって、すべての動作を規定しています。

CY8CMBR3xxx CapSense® Design Guide

この記事では、被覆導線を利用して実験を行うための簡易ボタンを作成しました。 本格的に静電容量ボタンを使いたい場合には、この文書を参照して作成します。

関連商品

mbed NXP LPC1768

mbed NXP LPC1768

  • 出版社/メーカー: スイッチサイエンス
  • メディア: おもちゃ&ホビー

MBR3 をホストから使ってみた (1) [PSoC]このエントリーを含むはてなブックマーク#

EZ-Click 設定アプリケーション

静電容量ボタンを使いたい時、部品点数を少なくしたいと思ったら、 PSoC に制御プログラムまで組み込んでワンチップで実現するのが便利です。 しかしながら、従来のアプリケーションに静電容量ボタンを追加したいだけという用途では、アプリケーション側のコントローラをホストとして外付けの静電容量ボタンコントローラを追加した方が簡単になります。 本シリーズでは、 MBR3 を外付け静電容量ボタンコントローラとして使う方法について考えます。

静電容量センサ

静電容量センサ

静電容量センサには、 MBR3 で、静電容量センサの実験 (1) で使用した2ボタン基板を使用します。 使用した設定は、以下の通りです。


CapSense Sensor Configuration

ふたつのセンサ端子をボタンとして割り当てます。 他のパラメータは、デフォルトのままです。


Global Configuration

このデバイスは、アドレス 0x63 の I2C のデバイスとして動作させます。 端子に余裕がないので、 LED 出力もシールドも追加できません。 ボタンの情報は、 I2C を介して受信するしかありません。

ホスト側のコントローラ

実験環境

ホスト側は、 I2C のマスター側インターフェイスが付いていれば、何でも使えますが、ここでは mbed LPC1768 を使用しました。 オンラインでアプリケーション開発が出来るので、便利です。

mbed LPC1768 の P9, P10 端子を I2C インターフェイスの SDA, SCL として使っています。 これらの端子には、 4.7kΩ のプルアップ抵抗をそれぞれ接続しています。 MBR3 とは VOUT, GND を加えた全部で4本の配線で接続されました。

ホスト側のソフトウェア

ホスト側には、以下のようなファームウェアを書き込みました。

#include "mbed.h"

// Two LEDs are assigned as button status indicators
DigitalOut led1(LED1);
DigitalOut led2(LED2);

// One I2C port is used to connect to MBR3.
// DIP9 and DIP10 are used for I2C but ports.
I2C i2c(p9, p10);

// MBR3 device register map
const char MBR_BUTTON_STAT = 0xaa; // BUTTON_STAT

// Node "Kitten"
// Device: MBR3102
// Function: Two button CapSense
const uint8_t kittenAddr = 0x63 << 1; // 8bit I2C address
const char kittenYellowButton = 0x01;
const char kittenBlueButton = 0x02;

int main(void) {
    uint8_t cmd[8];
    uint16_t kittenStat[2];
    
    for (;;) {
        // Get status from Kitten
        cmd[0] = MBR_BUTTON_STAT;  // 
        i2c.write(kittenAddr, (char *)cmd, 1);
        i2c.write(kittenAddr, (char *)cmd, 1);
        i2c.write(kittenAddr, (char *)cmd, 1, true);
        i2c.read( kittenAddr, (char *)kittenStat, 2);
                
        // Show button indicator LEDs
        led1 = (kittenStat[0] & kittenYellowButton)?(1):(0);
        led2 = (kittenStat[0] & kittenBlueButton)?(1):(0);
        
        wait(0.05);
    }
}

アドレス MBR_BUTTON_STAT(0xAA) の2バイトレジスタから値を読み出して、ふたつのボタンの状態を検出しています。 ボタンの状態によって、 LED1, LED2 を点灯させます。

レジスタを読み出すとき、 "read" メソッドの前にレジスタのアドレスを示す "write" メソッドが三つも並んでいます。 本来、これらの "write" メソッドは、アドレスを指定するだけなら一回だけで十分なはずです。 それが三回も必要な理由は、スタンバイ状態に入ってしまった MBR3 を起こすためです。

MBR3 は、低消費電力アプリケーションでも使用する事を考慮して、ホストからの通信が無くなると、極力スタンバイ状態に入ろうとします。 スタンバイ状態から起こすためには、 I2C の通信を使用しますが、通信内容そのものは破棄されます。 このような事情から、スタンバイ状態から確実に MBR3 が起きるように、二回の通信を行います。 そして、三回目の通信で、レジスタのアドレスが確実に伝達されます。

関連商品

mbed NXP LPC1768

mbed NXP LPC1768

  • 出版社/メーカー: スイッチサイエンス
  • メディア: おもちゃ&ホビー

PSoC 4 を MDK-ARM で使いたい (9) [PSoC]このエントリーを含むはてなブックマーク#

μVision のスプラッシュ

今回は、 CY8CKIT-043 PSoC 4 M-Series Prototype KitCMSIS-DAP を使う方法について考察しました。 なかなか、一筋縄ではいかなかったのです。

KitProg あるところに CMSIS-DAP あり

前回の記事で実験した KitProgCMSIS-DAP に使う方法ですが、 PIONEER Kit だけではなく Prototype Kit でも使うことが出来ます。 これは、 PIONEER KitPrototype KitKitProg ハードウェアに違いが無いということからも判断できます。

反面、 CMSIS-DAP で使用する為に必要なデバイス側の設定ファイルは、別途準備する必要があります。 CY8C42xx の設定ファイルは、 PSoC 4 を MDK-ARM で使いたい (2) で導入した "Legacy Support" が持っていました。 "Legacy Support" で提供される設定ファイルで対応できれば、デバイスを使用する事が出来ますが、対応していないデバイスを使用する事はできません。

たとえば、 PSoC 4 M-Series は、 v5.15 の "Legacy Support" では対応していません。 そのため、 PSoC 4 M-Series のプロジェクトを Export するだけでは、うまく動作しないのです。 動かないのであれば、動くようにしてやろうじゃないですか。

設定ファイルの拡張子は FLM

FLM 設定ファイル

デバイスの素性が記述された設定ファイルは、 "FLM" という拡張子が付けられて、 のインストールパスに配置されています。 デフォルトでは、 "C:\Keil_v5\ARM\Flash" に配置されています。 "Legacy Support" を導入しただけでは、 PSoC 4 M-Series の FLM 設定ファイルは配置されません。

PSoC 4 M-Series の FLM 設定ファイル

PSoC 4 M-Series の設定ファイル

でも、大丈夫です。 FLM ファイルは、 PSoC Programmer 3.24 と共にインストールされています。 デフォルトでは、サードパーティーの IDE に対応するための設定ファイルが、 "C:\Program Files (x86)\Cypress\Programmer\3rd_Party_Configuration_Files" に配置されています。

このディレクトリの "CY8C42xx-M\Prog_Algorithm" に FLM ファイルがあります。 この FLM ファイルを先に示した "C:\Keil_v5\ARM\Flash" にコピーすると、 μVision から読み出せるようになります。 あらかじめ、この FLM ファイルをコピーしておきます。

μVision でプロジェクトを設定する

Option ダイアログ

それでは、 μVision でプロジェクトを開いて、設定を行います。 "Options for Target" ダイアログで、デバッガに "CMSIS-DAP Debugger" を選択するところまでは CY8CKIT-042 PSoC 4 PIONEER Kit と同じです。 次に "Settings" ボタンをクリックして、ドライバの設定をおこないます。


Debugger settings

"Debug" タブで、 CY8CKIT-043 PSoC 4 M-Series Prototype KitKitProg とその先に接続された PSoC 4 M-Series が検出されたのが確認できます。


Flash Download タブ

次に "Flash Download" タブを開いてみますが、 "Programming Algorithm" にアルゴリズムが登録されていません。 ここに適切なアルゴリズムを登録するのが、第一歩です。


アルゴリズム一覧表

"Add" ボタンをクリックすると、ダイアログが開き、先ほどコピーした FLM ファイルがリストの中にならんでいるのが見えます。 このアルゴリズムを選択して、 "Add" ボタンをクリックすると "Flash Download" タブに選択されたアルゴリズムが並びます。


アルゴリズム選択完了

次に変更しなくてはならないのが、 "RAM for Algorithm" の項目です。 ここには、 Flash 書き込みの際に使用する RAM 領域を指定します。 この RAM 領域に CMSIS-DAP ドライバが小さなプログラムを送り込み、そのプログラムが実行されて Flash の書き換えを行うという仕組みになっています。

通常、 RAM 領域の先頭にこのプログラムを送り込めば良いので、デフォルトの状態では RAM の先頭アドレスと全体の大きさが入っています。 ところが、 PSoC 4 の場合には事情が異なっています。 PSoC 4 の Flash 書き換えルーチンは、 SROM (Supervisory ROM) と呼ばれる領域に配置されており、 NMI を使用したシステムコールによって呼び出す方式をとっています。 この際に利用されるバッファが、標準で提供されるアルゴリズムでは、 0x20000100 から128バイト確保されるので、この部分を避けて、プログラムを送り込む必要があります。 そのため、使用する RAM 領域の先頭 (Start) は 0x20000180 以降にする必要があります。

また、送り込まれるプログラムのサイズも適切に確保しなくてはなりません。 実験的に求められたプログラムのサイズは、 0x0580 でした。 したがって、 RAM 領域のサイズ (Size) もこの大きさ以上にする必要があります。


RAM 領域の設定完了

余裕をとって、 Start:0x20000200 Size:0x0600 と設定しました。 適切に設定が出来たら、 Flash への書き込みもデバッグ動作も、問題なく実行できます。

CY8C42xx では、どうなってたっけ?

CY8C42xx の場合

なぜ、前回の記事で PSoC 42xx を使った場合には、このような設定が必要なかったのでしょうか。 振り返って設定ダイアログを確認すると、アルゴリズムも RAM 領域も自動的に適切な値が入っていることがわかります。 現在のところ、これらの値がどこから来た値なのかは不明です。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (8) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM のスプラッシュ

前回の記事で、 CMSIS-DAP インターフェイスの準備が出来ました。 今回は、いよいよ CMSIS-DAP を使用して書き込みとデバッグを行います。

μVision IDE でプロジェクトを開く

プロジェクトを開く

今回は、前回までの実験で使用していた μVision のプロジェクトをそのまま使用します。 そのため、 PSoC Creator で、あらためてプロジェクトを Export する必要はありません。

μVision を起動して、メニューの "Project" → "Open Project..." からプロジェクトファイルを開きます。

デバッガインターフェイスを選ぶ

デバッガの選択

プロジェクトが開いたら、 "Project" → "Options for Target..." でオプションダイアログを呼び出し、 "Debug" タブを開きます。 これまで、 "Cypress MiniProg3/KitProg v3.3" を選んでいましたが、今回は "CMSIS-DAP Debugger" を選択します。

隣の "Settings" ボタンをクリックすると、デバッガドライバの詳細設定を行うことが出来ます。


Debug タブ

"Debug" タブが開くと、すでに "CMSIS-DAP - JTAG/SW Adapter" として "KitProg CMSIS-DAP" が表示されています。 もし、表示されていない時には、 CY8CKIT-042 PSoC 4 PIONEER Kit が PC に接続されていて、 "STATUS LED" が CMSIS-DAP モードを示しているか確認してください。

ドライバの設定が正しければ、 PSoC 4200 デバイスが検出された事が右側の "SW Device" で表示されます。


Flash Download タブ

"Flash Download" タブでは、内蔵 Flash に書き込みを行う際のアルゴリズムを指定しています。 CY8C42xx のプロジェクトを作成した場合には、 "Programming Algorithm" と "RAM for Algorithm" には、適切なデータが選択されます。

これら二つのタブを確認したら、 "OK" ボタンをクリックしてオプションダイアログに戻り、さらに "OK" ボタンをクリックして μVision に戻ります。

あとは、 KitProg と同じ

Download ボタン

準備が出来たら、 "Download" ボタンをクリックして、ファームウェアを書き込みます。 ファームウェアが書き込まれたら、これまでの KitProg を介した場合とまったく同じ動作をします。 同一のバイナリーが書き込まれたのですから、当然と言えば当然です。


Start/Stop Debug Session ボタン

デバッガを呼び出すには、 "Start/Stop Debug Session" ボタンをクリックします。 これも、 KitProg を使用したデバッグと同じです。

次回は、 CY8CKIT-043 PSoC 4 M-Series Prototype Kit でも同様の操作が行えるか、調べますます。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (7) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM のスプラッシュ

これまでの記事では、 KitProg を介して MDK-ARM を使う方法について探求してきました。 今回からは、 KitProg の代わりに というインターフェイスを使う方法について調べます。 まずは、 CMSIS-DAP を手に入れましょう。

PSoC Programmer 3.24 の新機能

リリースノート

2015年11月に公開された PSoC Programmer 3.24 のリリースノートに、気になる記述がありました。 このバージョンから CMSIS-DAPMass-Storage が使用できるようになったという事です。 これが利用できれば、 CY8CKIT-042 PSoC 4 PIONEER Kit をそのまま使えます。

PSoC Programmer で KitProg を検出させる

PSoC Programmer のダイアログ

さっそく、 PSoC Programmer 3.24 をインストールして起動します。 そして、 CY8CKIT-042 PSoC 4 PIONEER Kit を接続して検出させると、このようなダイアログが表示されます。

このダイアログは、検出した KitProg が最新のバージョンでは無いという事を示しています。 ダイアログには、「"Utilities" タブを開いて、 "Update Firmware" ボタンをクリックせよ」と解決方法も記されています。 ひとまず、 "OK" ボタンをクリックして、ダイアログを閉じます。

ファームウェアを更新する

Upgrade Firmware ボタン

Utilities タブにある "Upgrade Firmware" ボタンをクリックします。 ダイアログの指示には、 "Update Firmware" ボタンと書いてあって多少違いが有りますが、気にしない事にします。 すると、ブートローダが動き出して、ファームウェアの書き込みとベリファイが行われます。


ファームウェア更新完了

ウィンドウ下部の状態表示に "Firmware Update Finished" が表示されたら、ファームウェアの更新は完了です。 表示から、 "KitProg Version 2.14" に更新された事もわかります。

モードを切り替える

KitProg モードのデバイス

書き換えたファームウェアは、 KitProg のモードと CMSIS-DAP のモードと切り替えられるようになっています。 まず、ファームウェアを更新しただけの状態をデバイスマネージャで見ると、「KitProg Programmer 独自インターフェイス」と「USB-UART 仮想 COM ポート」と「KitBridge HID デバイス」 の三つのデバイスのコンポジットデバイスになっている事がわかります。

この状態からモードを切り替えるには、リセットボタンを5秒以上押し続けます。 リセットボタンは、ターゲットである PSoC 4200 のリセットボタンに接続されていますが、同時に KitProg ファームウェアを書き込んだ PSoC 5LP の GPIO 端子にも接続されています。 KitProg は、この端子の状態を監視していて、5秒以上押された場合には、 CMSIS-DAP モードに移行します。


CMSIS-DAP モードのデバイス

すると、デバイスの状態が変化して、 ”STATUS LED" が、緩やかに明滅するようになります。 また、デバイスマネージャからはこのように見えます。 コンポジットデバイスとして見えるのは同じですが、その内容が変化しており「Mass Storage」と「HID デバイス」の二つが含まれています。 これらのうち、 HID デバイスが、 CMSIS-DAP として振る舞います。

これをふたたび KitProg に戻すには、もう一度、リセットボタンを5秒以上押します。 このモードは、 PSoC 5LP に内蔵された EEPROM に記録されているため、 CY8CKIT-042 PSoC 4 PIONEER Kit を PC に接続し直した時でも直前のモードで立ち上ります。 また、 "Upgrade Firmware" を実行しても EEPROM の内容は保持されているので、厳密にはどちらのモードで立ち上るかわかりません。 どちらのモードになっているか、 STATUS LED を確認しながら使用する必要があります。

次回は、 MDK-ARM から CMSIS-DAP を経由して書き込みとデバッグを行います。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (6) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM のスプラッシュ

これまでの実験では、 CY8CKIT-042 PSoC 4 PIONEER Kit を使用してきました。 CY8CKIT-043 PSoC 4 M-Series Prototyping Kit でも同じ事が出来るか、試してみます。

プロジェクトの作成

ターゲットの選択

PSoC Creator を使用したプロジェクトの作成手順には違いがありません。 ターゲットとして選択するキットが異なっているだけで、同じ回路図を使用することが出来ます。 もちろん、 Build でも問題は起こりません。

プロジェクトの Export

プロジェクトの Export

すでに MDK-ARM はインストールされていますので、インストールし直す必要はありません。 次のステップは、 PSoC Creator による Export です。

すでに KitProg ドライバのインストールは完了しています。 CY8CKIT-043 PSoC 4 M-Series Prototyping KitKitProg を搭載していますので、同じドライバを使用する事が出来ます。

以上のように、 Export の手順も、 PSoC 4200 と同様です。

プロジェクトの Download まで

プロジェクトのオプション

プロジェクトのオプション設定も、 PSoC 4200 と同じでした。 また、 Build と Download の双方ともに PSoC 4200 と同様の手順となりました。 KitProg を使っているときには、お任せ設定で動作するようです。

デバッガの動作

デバッガの動作

デバッガの動作も、まったく同じでした。 KitProg を使っていれば、心配なく使えます。

まとめ

CY8CKIT-043 PSoC 4 M-Series Prototyping Kit を使用した場合でも、 KitProg を利用すれば MDK-ARM が問題なく使える事を確認できました。 他のデバイスでも使えるかは、別途確認する必要があります。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (5) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM のスプラッシュ

内蔵 Flash にプログラムが書けるようになったので、次はデバッガの動作を試してみます。

デバッグしようにもソフトウェアがありません

main.c を開く

そうなのです。 PSoC 4 のプロジェクトを作成はしましたが、このプロジェクトでは、一行もソフトウェアを書いていないのです。 さすがに、デバッグ対象のソフトウェアが無いのではしょうがないので、まずは、コードを追加します。

左側にある "Project" ペインから "main.c" ファイルを探して、ダブルクリックするとテキスト・エディタが開きます。


コードを追加する

エディタが開いたら、 for ループの中にコードを追加します。

    for(;;)
    {
        /* Place your application code here. */
        Clock_1000Hz_Stop();
        CyDelay(5000);
        Clock_1000Hz_Start();
        CyDelay(5000);
    }

このコードによって、ふたつのクロックの片方が5秒動いて、5秒止まって、という動作を繰り返すようになります。 LEDは、クロックが動いている間は明るさが変化し、クロックが止まると一定の明るさを保つようになります。

プロジェクトを Build して Download すると、5秒ごとに異なる動作をするのが観測できます。

デバッグモードに投入

デバッグモードに入る

デバッグを開始するには、 "Start/Stop Debug Session" ボタンをクリックします。


32K 制限の警告

すると、このようなダイアログが表示されます。 これは、無償版の MDK-ARM では、 32k バイトのコードサイズ制限があるという事を示す警告です。 PSoC 4200 の場合には、最大 Flash 容量が 32k バイトですので、問題ないでしょう。


デバッグモード

"OK" ボタンをクリックすると、デバッグモードに入ります。

ブレークポイントを設定する

Step Over 実行

デバッグモードに入ったとき、 PSoC の初期化を行う initialize_psoc() の直前で CPU が止まっています。 つまり、 LED を駆動するための信号が出力されておらず、 LED は消灯したままの状態です。

初期化だけを行うためには、 "Step Over" ボタンをクリックして、初期化関数を実行します。 初期化を行うと、 LED の明るさが変化し始めます。


ブレークポイントの設定

次に "main.c" ファイルを開いて、二か所にブレークポイントを設定します。 それぞれの行の左側で左クリックすると、赤い印が現れ、ブレークポイントが設定されたことがわかります。

ブレークポイントまで実行

Run ボタン

"Run" ボタンをクリックすると、次のブレークポイントまで実行されます。 LED は、 "Run" ボタンをクリックするたびに、明るさが変化する状態と変化しない状態を繰り返します。 PSoC の場合、 CPU の動作とディジタルロジックで使用するクロックが連動していないため、このような動作になります。

デバッグモードから抜けるには、ふたたび "Start/Stop Debug Session" ボタンをクリックします。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (4) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM のスプラッシュ

お待たせしました。 今回から MDK-ARM での操作が始まります。

オプション設定

プロジェクトのオプション

まず、最初に行うのは、プロジェクトのオプションを整える事です。 メニューの "Project" → "Options for Target..." を選択して、ダイアログを開きます。


Device タブ

"Device" タブを開くと、 CY8C4245AXI-483 が指定されているのがわかります。

デバッグハードウェアを設定する

デバッグハードウェアの選択

次に "Debug" タブを選ぶとデバッガの設定画面が開きます。 左半分がシミュレータを使用する時の設定で、右半分が実物を使う時の設定です。 右側のドロップダウンリストから "Cypress MiniProg3/KitProg v3.3" を選択します。


設定ダイアログの呼び出し

そして、ドロップダウンリストの右側にある "Settings" ボタンをクリックして設定ダイアログを開きます。


KitProg 設定ダイアログ

このダイアログでは、 KitProg の指定と設定を行います。 CY8CKIT-042 PSoC 4 PIONEER Kit が PC に接続されていれば、自動的に認識されて "S/N" に表示されます。 ここでデバッガに使用される KitProg は、設定項目がほとんどありません。 右端に内蔵 Flash メモリにファームウェアをダウンロードする際の振る舞いを定義できるようになっていますが、すべてチェックしたままにしておきます。

設定が終わったら、 "OK" をクリックします。

Flash の書き込み設定

Utilities タブの設定

次は、 "Utilities" タブをクリックします。 このダイアログの上半分では、内蔵 Flash への書き込みをどのように行うのかを指定します。 Flash への書き込みには、デバッガをそのまま使います。 必要なのは、 "Use Debug Driver" をチェックする事だけです。

この Flash への書き込み設定ですが、 "Use Target Driver for Flash Programming" を選んでいたはずなのになぜか "Use External Tool for Flash Programming" に切り替わってしまう事があります。 その時には、 Flash への書き込みに失敗しますので、このオプション項目を設定し直して、再度書き込んでみてください。

以上で最低限のオプション設定は完了しました。 "OK" ボタンをクリックして、ダイアログを閉じます。

プロジェクトを Build する

Build ボタン

デバッガの設定ができたら、 "Build" ボタンをクリックして、プロジェクトを Build します。

Flash に書き込む

Download ボタン

Build できたら、次は Flash への書き込みです。 MDK-ARM の場合、 Flash への書き込みは "Download" と呼ばれています。 "Download" ボタンをクリックすると、 Flash への書き込みが始まります。


書き込み完了

書き込みが完了したら、 "Build Output" に "Programming Succeeded" と表示され、正常に書き込みが行われた事がわかります。 CY8CKIT-042 PSoC 4 PIONEER Kit の赤い LED の明るさが緩やかに変化しているのが観測されます。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (3) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM のスプラッシュ

前回は、 MDK-ARM とその周辺を整備しました。 今回は、PSoC Creatorで作成したプロジェクトを MDK-ARM に Export します。

IDE Export Wizard の呼び出し

Export メニュー

PSoC Creator で作成したプロジェクトは、 PSoC Creator アプリケーションから MDK-ARM で使用できるプロジェクトに変換 (Export) できます。 具体的には、メニューの "Project" → "Export to IDE" で作成を開始します。


IDE の選択

すると、 "IDE Export Wizard" が開きます。 MDK-ARM を使用するため、その IDE である μVision を選びます。 二つの選択肢 "μVision" と "Generated CMSIS-PACK (Beta)" のうち、 Beta ではない μVision を選びます。

インストールした IDE は、 μVision 5 なのですが、ここでは μVision 4 を選択して、 "Legacy Support" を利用します。

MiniProg3/KitProg ドライバのインストール

ドライバのインストール

"IDE Export Wizard" を使うと、 "MiniProg3/KitProg" のドライバが準備されているかが検査されます。 ドライバがなければ、このダイアログが表示されます。 "Install Drivers for μVision" ボタンをクリックして、ドライバをインストールします。


インストール完了

表示にしたがってインストールを進めます。 最後に "Finish" をクリックすると、インストールが完了します。


ドライバ準備完了

ドライバの準備が出来たら、 "Next" をクリックして次に進みます。

ツールチェインを選択

ツールチェインの選択

この画面では、ツールチェイン、すなわちコンパイラなどを選びます。 もちろん、 "ARM MDK Generic" を選択します。

ソースファイルの選択

ソースファイルの選択

この画面では、どのファイルを μVision で使用するかを指定します。 すべてのファイルを選択するために "Select All" をクリックして、 "Next" をクリックして次の画面に進みます。

Wizard の完了

IDE Export Wizard の最終確認

いよいよ、最後の確認です。 "Wizard" は、プロジェクトの Build 前後で実行されるスクリプトを作成し、プロジェクトを Export します。 "Next" をクリックすると、 Export が始まります。


IDE Export Wizard 完了

Wizard が正常に完了したら、ふたつの項目にチェックが表示されます。 "Open in μVision" にチェックを入れて、 "Finish" をクリックすると "Wizard" の終了後 IDE が自動的に開きます。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4 を MDK-ARM で使いたい (2) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM スプラッシュ

前回は、タネとなるプロジェクトを作成しました。 今回は、 MDK-ARM をインストールします。

MDK-ARM のインストーラをダウンロードする

MDK-ARM のインストーラをダウンロードするためには、 MDK Microcontroller Development Kit ページから "Download" ページに入り、 いくつかのリンクをたどって "MDK-ARM" をダウンロードします。 ダウンロードするためには、途中で個人情報を登録する必要があります。
Export エラー

この記事を執筆している時点では、 MDK-ARM v5.17 が最新版になっています。 ところが、 PSoC CreatorMDK-ARM v5.17 との組み合わせでは、後で述べる "Export" のステップでこのようなエラーが発生してしまい、うまく動かないようです。 色々と実験を行った所、 MDK-ARM v5.15 以前の古い版では動作しました。


ダウンロード先のリンク

そこで、ダウンロード先を示す URL の最後尾の "517.exe" を "515.exe" に変更して、 MDK-ARM v5.15 を取り寄せます。 現状では、この方法で MDK-ARM v5.15 を取り寄せられますが、将来にわたって通用する保証はありませんのでご注意ください。

MDK-ARM をインストールする

インストール開始

インストーラを取り寄せたら実行します。 表示にしたがって、インストールを進めます。


デバイスドライバのインストール

インストールの終盤で、デバイスドライバがインストールされます。 これは、 ULINK と呼ばれる ARM の開発ツール・ハードウェアのドライバで、今回の実験では使用されませんが、ついでにインストールしてしまいます。


インストール完了

ドライバがインストールされたら、インストールは完了です。 最後の画面で "Finish" をクリックして、インストーラを終了します。

続いて Pack Installer の調整

Pack Installer 起動

インストールが終わると同時に "Pack Installer" が動き始めます。 "Pack Installer" とは、多種多様なデバイスに関する情報を集めたデータベースを管理するプログラムです。 情報は、ネットワークを介して "Pack Installer" が適切にダウンロードしてきます。 まず、説明ダイアログの "OK" ボタンをクリックして閉じます。


データベースの更新

しばらくの間、ネットワーク上の情報を確認すると、このような画面に切り替わります。 右半分には、すでにデータベースに登録されている情報が列挙されています。 このリストの "Update" と書いてあるのが、データベースの情報が古くなっている事が確認された項目です。 これらの "Update" ボタンをクリックして、データベースを更新します。


データベースの更新完了

下のステータス・バーに "Completed requested actions" と表示されたら、データベースの更新完了です。

PSoC 42xx を登録する

MDK v4 Legacy Support

じつは、今回使用する PSoC 42xx シリーズは、 "Pack Installer" では登録されません。 これは、 PSoC 42xx は、古いバージョンにのみ対応しているのが原因のようです。 PSoC 42xx シリーズは、 MDK v4 Legacy Support という所に収蔵されています。


MDKCM515.exe

このページで "MDKCM515.exe" をダウンロードして実行します。


Legacy Support のインストール

表示にしたがって、インストールを進めます。


Legacy Support インストール終盤

最後の画面で "Finish" をクリックすると、インストール終了です。

以上で MDK_ARM に関連したインストールが終わりました。 次回は、 PSoC Creator のプロジェクトを MDK-ARM に引き渡す方法を紹介します。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

PSoC 4200M は、 "Legacy Support" にも含まれていないので、さらに工夫が必要です。


PSoC 4 を MDK-ARM で使いたい (1) [PSoC]このエントリーを含むはてなブックマーク#

MDK-ARM スプラッシュ

本シリーズのテーマは、 ARM が提供している KeilMDK-ARM を使用して PSoC 4 のソフトウェア開発を行う事です。

PSoC Creator でプロジェクトを作成する

ターゲット・ボードの選択

MDK-ARM でソフトウェア開発をする場合でも、プロジェクトの作成には PSoC Creator を使用します。 数ある中から、 PSoC Creator 3.3 CP1 を使用しています。 また、ターゲットには CY8CKIT-042 PSoC 4 PIONEER Kit を使用します。


空の回路図を作成する

スクラッチのプロジェクトから始めるので、 "Empty Schematic" を選択します。 このあと、 "Workspace" と "Project" の場所と名前を指定して、プロジェクトを作成します。

回路図と端子の割り当て

おためし回路図

この記事では、 MDK-ARM を使用してみる事を目的としているので、見た目で確認できる簡単な回路図を書いてみました。 周波数の異なるふたつの矩形波を XOR 論理に与える事で、周期的に幅が変化するパルス列が生成されます。 このパルスを P1[6] に接続されている LED に与えると、緩やかに明るさが変化するように見えるという仕組みです。 PSoC 4 のランダムロジックには、クロックをじかに接続する事が出来ません。 このため、 Toggle Flip Flop で二分周したクロックを接続しています。 これで、 500Hz と 501Hz の矩形波が XOR に入り、これらの周波数の差である 1Hz のビートが LED の明るさの変化として観測されます。

PSoC Creator から書き込んでみる

書き込みボタン

CY8CKIT-042 PIONEER Kit を接続して、 Program ボタンをクリックすると、ファームウェアが書き込まれて、そのまま実行されます。 赤い LED の明るさが緩やかに変化したら、完成です。

と、今回はここまで。 次回、いよいよ MDK-ARM の登場です。

関連商品

PSoC 4200M CY8CKIT-043 Prototyping Kit

PSoC 4200M CY8CKIT-043 Prototyping Kit

  • 出版社/メーカー: スイッチサイエンス
  • メディア: エレクトロニクス

この記事は、 CY8CKIT-042 PSoC 4 PIONEER Kit を使用していますが、 PSoC 4200M 向けにプロジェクトを作成しても同様の実験ができます。


リップルカウンタを作ってみたはずなのに [PSoC]このエントリーを含むはてなブックマーク#

リップルカウンタ?の出力

Bridge Control Panel でロジアナって、便利。 と、いう話題で書いた前回の記事ですが、もともと何をしたかったのかを追記します。 題して、「RTLで書いたのと違うじゃん。」です。

前回までのあらすじ

PSoC で作るロジアナみたいなもの

PSoC でロジック信号をつかまえて、 I2CUART のインターフェイスで出力すると、ロジアナみたいな事ができる事がわかりました。 前回の例では、同期カウンタを使ったきれいな波形を読み取らせたのですが、きれいな波形だったら手計算でシミュレーションだってできます。 本当に見たかったのは、こんなきれいな物じゃななかったのです。

リップルカウンタが出来ちゃう?

リップルカウンタの回路図

きっかけは、某所で見かけた「カスケード接続された TFF」でした。 TFF をカスケード接続するとリップルカウンタと呼ばれる非同期カウンタを構成する事が出来ます。 たとえば、こんな回路です。 前段の TFF の出力の立ち下がりを次の段の TFF のクロックに使うと、バイナリカウンタが出来上がります。

論理合成をかじった方ならわかると思いますが、この回路は厳密には PSoC 4 では実現できるはずがないのです。 それは、 PSoC 4 ではクロックを4本しか持てないからです。 この回路を真面目に実現すると、5種類のクロックが必要なのです。 これが、合成できるという事は、何か真面目に実装していないからに違いないのです。

ロジアナで観測したら

リップルカウンタ?の出力

そこで、リップルカウンタの出力を観測してみました。 すると、各ビットの立下りが同時に発生するはずなのに、ステージごとに1クロックずつ遅れて出力が変化しているのがわかります。 これでは、リップルカウンタではありません。


リソースの使用状況

"Resource Meter" で、リソースの使用状況を見た所、やはりクロックは二つしか使っていません。 では、それぞれの TFF は、どのクロックを使用しているのでしょうか。

前段の出力はクロックとして使われていない

そこで、合成時のレポートを確認してみました。 以下のレポートは、 "rpt" ファイルの "Final Placement Summary" → "Final Placement Details" → "Component Details" にある、 Q3 を作っているマクロセルに関するレポートです。

LAB@[UDB=(0,1)][LB=0] #macrocells=1, #inputs=0, #pterms=0
{
    [McSlotId=0]:     MacroCell: Name=Q3, Mode=(T-Register) @ [UDB=(0,1)][LB=0][MC=0]
        Total # of inputs        : 0
        Total # of product terms : 0
        List of special equations: 
            Clock  = (CK_digital) => Global
            Clock Enable: NegEdge(Q2)
        Main Equation            : 0 pterms
        !(
            0
        );
        Output = Q3 (fanout=1)
        Properties               : 
        {
        }

    [McSlotId=1]: (empty)
    [McSlotId=2]: (empty)
    [McSlotId=3]: (empty)
}

このレポートによると、 Q3 出力を作っている TFF のクロックは、 Q0 を作っている TFF で使用されている "CK" であり、 "Clock Enable" と称してなぞの入力 "NegEdge(Q2)" が接続されています。 どうやら、原理的に1クロックの遅延を生ずる Q2 の立ち下がり検出回路を TFF のイネーブル入力に使用して、疑似的に TFF が構成されているようです。 つまり、 TFF という記号が使用されてはいますが、これは TFF とは違う実装になっているようです。

分周されたクロックを入れてみたら

分周されたクロックを入れたリップルカウンタ

どうやら、 TFF は分周前のクロックを使用したエッジ検出を行っているらしいことがわかってきました。 そこで、リップルカウンタに与えるクロックに4分周回路を追加してみました。 元のクロックの周波数を4倍にしたので、最終的な出力に変わりはないはずです。


クロックを分周した波形

ところが、各ステージでの遅延が少なくなりました。 合成結果は省略しますが、それぞれの TFF の駆動クロックの周波数が4倍になったのが原因です。 このように、元のクロック周波数が十分に高ければ、 TFF の出力はリップルカウンタ出力として見なせるという事がわかりました。 それでも、決してリップルカウンタとはなりませんので、注意が必要です。

本日の結論

  • PSoC の論理合成では、 TFF をカスケードに接続する事もできますが、正確な実装ではありません。
  • 元のクロック周波数が高い場合は、リップルカウンタのように見なす事も出来ますが、リップルカウンタとは動作が異なります。
  • 何よりリップルカウンタを作らせないような回路を作る事が大事です。

関連文献

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

  • 作者: 古平 晃洋
  • 出版社/メーカー: CQ出版
  • 発売日: 2012/05
  • メディア: 単行本

CY8CKIT-042 でロジアナを作った ~UART編~ [PSoC]このエントリーを含むはてなブックマーク#

波形の表示

前回の記事では、取得した論理信号の情報を EZI2C で PC に引き渡すことで、毎秒 390 サンプルの速度を出していました。 今回は、 UART を使って、表示させてみます。

回路図は、こうなった

UART版ロジアナ回路

前回同様、PSoC 4 PIONEER Kit (CY8CKIT-042) の中にロジアナと観測対象 (DUT) の回路の両方を入れてしまいました。 変更されたのは、 EXI2C コンポーネントが UART コンポーネントになった所と周期割り込みの仕掛けが追加された事です。

UART コンポーネントは、 115200 bit/s の速度で送信専用に設定されています。 理論的には、毎秒 10k バイトぐらいは出せるのですが、受信側が追い付かない事がわかりました。 そのため、サンプリング周期を割り込みで作り出して、順次、送信します。 この回路の場合、サンプリング周期は毎秒 1000 サンプルになっています。

前回の EZI2C を使った通信方式では、 PC 側でサンプリングタイミングを作り出していました。 ところが、ハンドシェイクを使わない UART の場合には、送信側が一方的にデータを送り出す仕組みなので、 PSoC 側でサンプリングタイミングを作ります。 これが、前回との大きな違いとなっています。

ファームウェアは、こうなった

UART版ロジアナのファームウェア

ファームウェアは、ちょっとだけ複雑になりました。 割り込み処理ルーチン (Interrupt Service Routine; ISR) を宣言して、割り込みが発生したらフラグ "Int_Sample_flag" を立てます。 メインループで、このフラグを検出して、サンプリングの処理を行います。 これで、周期的にサンプリングを行い、データを送出できるようになりました。

データの受信と表示

UARTからのデータ受信

今回もデータの受信には、 Bridge Control Panel (BCP) を使用します。 CY8CKIT-042USB-UART に割り当てられた COM ポートを選択すると、 UART の受信が出来るようになります。 ボーレートなどのプロトコルの設定は、 "Tools" → "Protocol Configuration" で設定できます。


UARTデータ受信構文

データの受信に使用される構文は、 "rx8 @0Probe" でおしまいです。 この実装の場合、送られてきた8ビットのデータを延々と受信するだけなので、データの始まりも終わりもありません。 複数バイトの受信が必要な場合には、ヘッダやトレーラを設定する事も出来ます。


サンプル数設定

データを受信した後は、表示を行いますが、サンプル速度が変わったので一画面当たりのサンプル数を変更する必要があります。 UART 版では、サンプル速度が割り込みによって比較的正確に決められています。 ここでは、一画面に22クロック(22秒)を表示するために一画面当たり22000サンプルに設定します。


波形の表示

これで、波形が表示できるようになりました。 今度は、 PC の画面表示能力にボトルネックが現れたように思われます。

プロジェクトアーカイブ

この記事を書くために作成したプロジェクトは、このファイルの拡張子を ZIP に変更して展開すると再現できます。

関連商品

Cypress PSoC4-PIONEER KIT CY8CKIT-042

Cypress PSoC4-PIONEER KIT CY8CKIT-042

  • 出版社/メーカー: Cypress
  • メディア: その他

関連文献

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

  • 作者: 古平 晃洋
  • 出版社/メーカー: CQ出版
  • 発売日: 2012/05
  • メディア: 単行本

CY8CKIT-042 でロジアナを作った ~I2C編~ [PSoC]このエントリーを含むはてなブックマーク#

波形の表示 PSoC 4 では、 UDB を使ったカスタムロジックの設計ができます。 でも、出来上がった回路の動作を観測するためには、ロジックアナライザだのオシロスコープが必要になります。 「無ければ作ったらいいじゃない。」

ロジアナって何?

ロジックアナライザ(ロジアナ)は、論理信号を観測するための装置です。 本格的な物は、トリガをかける条件などを設定できるのですが、ここでは値を観測して表示できるところまで考えます。

ロジアナに必要なのは、論理信号を取りこむ機能とそれを記録して表示する機能です。 このうち、 PSoC に出来そうなのは論理信号を取りこむ事です。 取りこんだデータは、通信機能によって PC に送出し、記録と表示は PC に任せてしまいます。

回路図は、こうなった

ロジアナ回路

PSoC 4 PIONEER Kit (CY8CKIT-042) の中にロジアナと観測対象 (DUT) の回路の両方を入れてしまいました。 左側の回路は、4ビットの同期カウンタです。 ロジアナ部分のサンプリング速度がそれほど上げられないだろうと考えて、クロック周波数は 1Hz としています。

右側の回路が、ロジアナ部分です。 論理信号を Status Register で受けて、 EZI2C コンポーネントを介して PC に送り出します。 EZI2C は、スレーブ I2C インターフェイスなので、取りこむタイミングは PC に依存します。

これらの回路の間をつなぐのがディジタル入出力端子です。 DUT で作られた論理信号は、一旦、この端子に出力されます。 そして、端子の入力機能でロジアナに取り込まれます。

ファームウェアは、こうなった

ロジアナのファームウェア

この回路の DUT 部分は、ファームウェアの関与を必要としません。 クロックもカウンタも、勝手に自走します。 一方、ロジアナ部分は、 EZI2C の初期化とそのバッファにデータを配置するため、ファームウェアが必要です。 逆に言うと、ファームウェアに書く内容は、それだけです。

EZI2C コンポーネントの初期化の大部分は、 Componenet Configuration で行っていますので、ファームウェアでの記述はほとんどありません。

データの受信と表示

Bridge Control Panel でデータの受信

データを I2C で送り出す仕掛けは出来ましたが、このデータを受信して表示する仕掛けが必要です。 独自アプリケーションを作る気はないので、何か考えなくてはなりません。 ここでは、 PSoC Programmer と共にインストールされるユーティリティアプリケーション Bridge Control Panel (BCP) を使用します。


I2C データ受信構文

BCPEZI2C のデータを引き出すには、このような構文を使用します。 まず、 "w 36 00 p" で、取り出そうとしているデータのアドレスを指定します。 次に "r 36 @0Probe p" で、データを取り出して変数 "Probe" に格納します。 変数名 "Probe" の前についている "0" は、データの格納位置を表します。 "0" は、最下位の8ビット部分に受信したデータを格納する事を示しています。


変数の設定

変数は、メニューの "Chart" → "Variable Settings" からダイアログを開いて、この図のように "long int" で設定します。 "long int" は、32ビットの変数を意味しています。


フラグの設定

変数 "Probe" には、各ビットに複数の論理信号が割り当てられます。 これを論理信号に分解するのが、フラグという概念です。 "Flags" のタブで、5個の論理信号を定義して、論理信号を取り出すべき変数 "Probe" 内でのビット位置を "Bit Mask" で指定します。


波形の表示

最後に "Chart" → "Flags Chart Settings" でフラグを "Chart" に表示する際のパラメータを指定します。 ここでは、表示するY軸方向の位置を指定して、波形が見やすいようにしています。 準備が出来たら、 "Repeat" ボタンを使ってデータを表示させます。 すると、論理信号の動きが次々と表示されます。

私の実験では、毎秒 390 サンプルほど表示されていました。 クロック周波数を 100Hz ぐらいに上げても波形が観測できる計算です。 ただ、この性能は、データを受信する側の PC の性能に依存してしまうので、実際のサンプル速度については、一概には言えません。

プロジェクトアーカイブ

この記事を書くために作成したプロジェクトは、このファイルの拡張子を ZIP に変更して展開すると再現できます。

関連商品

Cypress PSoC4-PIONEER KIT CY8CKIT-042

Cypress PSoC4-PIONEER KIT CY8CKIT-042

  • 出版社/メーカー: Cypress
  • メディア: その他

関連文献

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

  • 作者: 古平 晃洋
  • 出版社/メーカー: CQ出版
  • 発売日: 2012/05
  • メディア: 単行本

Keil の 8051 コンパイラは、変数をどこに割り当てるのか。 [PSoC]このエントリーを含むはてなブックマーク#

変数宣言あれこれ

PSoC CreatorPSoC 3 のプログラムを作成する時、デフォルトでは Keil の 8051 コンパイラが使われます。 この記事では、このコンパイラが、どのように変数をメモリに割り当てるのかを調べます。

大域変数の割り当て

C のコンパイル単位は、 C のソースファイルです。 ソースファイルには、変数と関数を記述する事が出来ます。 関数の外にある変数を大域変数 (global variable) と呼んでいます。 たとえば、以下のように変数を宣言します。

#include <project.h>

static uint16   static_var;
uint16          global_var;
static uint16   static_var_init = 0x1234;
uint16          global_var_init = 0x1234;

int func()
{

大域変数は、デフォルトの状態では別のコンパイル単位から "extern" 修飾子を使ってアクセスする事が出来ます。 別のコンパイル単位からのアクセスを禁止するためには、 "static" 修飾子を使って、このコンパイル単位だけがアクセス出来るようにします。 このアクセス可能な範囲の事をスコープ (scope) と呼んでいます。

メモリ上の変数の割り当て場所は、 MAP ファイルに記述されています。

      VALUE       REP       CLASS    TYPE      SYMBOL NAME
      ====================================================
      02000056H   PUBLIC    XDATA    WORD      global_var
      02000058H   PUBLIC    XDATA    WORD      global_var_init
      0200005AH   SYMBOL    XDATA    WORD      static_var
      0200005CH   SYMBOL    XDATA    WORD      static_var_init

これらの変数は、永続的に使用されますので、他の変数とは重ならない独立したアドレスに割り当てられます。 PUBLIC と表示されている変数が、別のコンパイル単位からもアクセス可能な変数です。 初期値を設定したものとしていないもので、区別することなくメモリ上に割り当てられていますが、これは、初期値を設定していない大域変数はゼロに初期設定されるので、すべての領域が初期設定される事に代わりがないからです。

局所変数の割り当て

関数の内部で宣言された変数は、局所変数と呼ばれます。 これらのスコープは、関数内に限られますので、別の場所で宣言された名前との衝突を気にする必要がありません。

int func()
{
    uint16          default_local_var;
    auto uint16     auto_local_var;
    static uint16   static_local_var;
    
    static_var++;
    static_var_init++;
    {
        auto uint16 auto_block1_var = 0x2345;
        default_local_var++;
        auto_local_var++;
        static_local_var++;
        auto_block1_var++;
    }
    {
        auto uint16 auto_block2_var = 0x3456;
        default_local_var++;
        auto_local_var++;
        static_local_var++;
        auto_block2_var++;
    }
    return auto_local_var;
}

局所変数は、デフォルトでは自動変数 (auto variable) と呼ばれる変数として宣言されます。 自動変数を明示的に指定するには、 "auto" を付けます。

静的変数を宣言するために "static" を付けます。 この "static" 修飾子は、大域変数で使用した "static" がスコープを表していたのとは意味が異なります。

では、 "auto" と "static" の違いは何かと言うと、 "auto" は、一時的にメモリが割り当てられるのに対して、 "static" の方はメモリ上に永続的に存在し続けます。 このため、 "static" で宣言された変数の内容は、関数を出た後でも保存されます。

自動変数は、カーリーブラケット{}で囲まれたブロック内部で宣言する事も出来ます。 この場合、変数のスコープはブロックの内部だけです。

これらの変数は、以下のように割り当てられます。

      VALUE       REP       CLASS    TYPE      SYMBOL NAME
      ====================================================
      02000052H   SYMBOL    XDATA    WORD      static_local_var
      0200005EH   SYMBOL    XDATA    WORD      default_local_var
      02000060H   SYMBOL    XDATA    WORD      auto_local_var
      02000062H   SYMBOL    XDATA    WORD      auto_block1_var
      02000062H   SYMBOL    XDATA    WORD      auto_block2_var

"static" 宣言された変数の格納場所は、大域変数と同じ場所が使われます。 これに対して、自動変数は動的に格納場所を確保するため、スタックに配置されるのが一般的です。 ところが、 8051 CPU の場合、スタックの容量が小さいため、スタックを使わずに固定アドレスに自動変数を配置します。

アドレスマップ上では、 0200005EH から上のアドレスが、自動変数のために確保される領域となっています。

ブロック内で宣言された変数 auto_block1_var と auto_block2_var は、どちらも同じアドレスに割り当てられています。 これは、これらの変数のスコープ範囲が重なっていないので、同時に使用される事がないためです。 このように、同一アドレスのメモリを複数の自動変数で共有することで、実際に必要なメモリ容量を減少させています。

int func_2nd()
{
    auto uint16 auto_local_2nd_var;
    auto_local_2nd_var++;
    return auto_local_2nd_var;
}

この自動変数によるメモリの共有は、複数の関数の間でも行われます。 例えば、上のような自動変数の宣言を含んだ関数を作成してみます。

      VALUE       REP       CLASS    TYPE      SYMBOL NAME
      ====================================================
      0200005EH   SYMBOL    XDATA    WORD      auto_local_2nd_var

すると、変数 auto_local_2nd_var は、関数 "func()" の default_local_var と同じアドレス 0200005EH に割り当てられています。 これも、双方のスコープが重ならない事を利用したものです。

スコープが重なるかも知れない時は

以上の関数は、理論的にスコープが重ならない事が自明な場合の変数割り当てです。 ところが、割り込み処理ルーチン内で関数が使用される場合、スコープの入れ子が可能になるので、同じメモリを共有する事が出来なくなってしまいます。

このような場合には、自動変数をスタック上に割り当てさせるための修飾子 CYREENTRANT を使用します。

int func_reent() CYREENTRANT
{
    uint16          reent_default_local_var;
    auto uint16     reent_auto_local_var;
    static uint16   reent_static_local_var;
    
    global_var++;
    global_var_init++;
    {
        auto uint16 reent_auto_block1_var = 0x2345;
        reent_default_local_var++;
        reent_auto_local_var++;
        reent_static_local_var++;
        reent_auto_block1_var++;
    }
    {
        auto uint16 reent_auto_block2_var = 0x3456;
        reent_default_local_var++;
        reent_auto_local_var++;
        reent_static_local_var++;
        reent_auto_block2_var++;
    }
    return reent_auto_local_var;
}

このように関数を宣言すると、 "static" 宣言された変数以外は、スタック上に配置されます。

      VALUE       REP       CLASS    TYPE      SYMBOL NAME
      ====================================================
      02000054H   SYMBOL    XDATA    WORD      reent_static_local_var
      00000000H   SYMBOL    XSTK     WORD      reent_default_local_var
      00000002H   SYMBOL    XSTK     WORD      reent_auto_local_var
      00000004H   SYMBOL    XSTK     WORD      reent_auto_block1_var
      00000004H   SYMBOL    XSTK     WORD      reent_auto_block2_var

スタック上に配置される変数同士でも、 reent_auto_block1_var と reent_auto_block2_var のようにスコープが重ならないものは、同一メモリに割り当てられます。

8051 CPU では、スタックの容量が小さいためにデフォルトで変数が固定アドレスに割り当てられていましたが、通常のコンパイラでは、自動変数はスタックに割り当てられます。

プロジェクトアーカイブ

この記事を書くために作成したプロジェクトは、このファイルの拡張子を ZIP に変更して展開すると再現できます。

関連文献

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

シリーズ最強!PSoC3ボード+デバッグ・ボード (トライアルシリーズ)

  • 作者: 古平 晃洋
  • 出版社/メーカー: CQ出版
  • 発売日: 2012/05
  • メディア: 単行本

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。