SSブログ

Sinc フィルタ [PSoC]このエントリーを含むはてなブックマーク#

Sinc2 Filter

IIR フィルタに見えたけど、実際は FIR フィルタなのね。

参考文書

PSoC TRM, Document No. 001-14463 Rev. *H

MBR3 で、静電容量センサの実験 (2) [PSoC]このエントリーを含むはてなブックマーク#

MBR3102 実験基板

MBR3 で作った静電容量ボタンは、静電容量センサに触れたときに ON/OFF します。 センサの感度をもっと高くすると、近づいただけで ON/OFF できる「近接センサ」に利用できます。

MBR3 を近接センサに設定する

近接センサの数を設定する

近接センサの実験をするために、新しいプロジェクトを作成しました。 ここでは、 "Design002" というプロジェクトを作成しています。

静電容量センサを近接センサ (Proximity sensor) として使用するには、 EZ-Click 2.0 の "CapSense sensor configuration" タブで、設定をおこないます。 まず、 "Number of proximity sensors" で、近接センサに使用する静電容量センサの数を "1" に設定します。


静電容量センサの設定

すると、 "Proximity sensors" セクションが現れます。 このタブで、以下の設定を行います。

  1. "Proximity sensors" セクションの "Pin" カラムを "CS0/PS0" に設定します。
  2. "Sensor" カラムのセンサ名を "BTN0" に変更します。
  3. "Buttons" セクションの "CS1/PS1/GPO0/SH" で "Enable" カラムをチェックします。
  4. 同じく "Sensor" カラムのセンサ名を "BTN1" に変更します。

他の部分は、デフォルトのままにしておきます。 前回と同様に "Save Project" でプロジェクトを保存し、 "Select Target Device" で接続相手を探し、 "Apply Current Configuration" で設定を書き込みます。 これで、 "BTN0" が近接センサとして、 "BTN1" が静電容量ボタンとして動作するようになります。

近接センサの動作を確認する

近接センサの動作

近接センサの動作は、前回と同様に "CapSense output" タブで確認する事ができます。 "Button" に "BTN0" を選択すると、近接センサの動作が現れます。 指を近づけると、センサが反応を始め、5センチメートルの距離で "Button status" が "Proximity" に変化して接近を検出したことを知らせます。 さらに指を近づけて、静電容量センサに触れると、 "Button status" は "On" に変化します。

静電容量ボタンとの違いについて

静電容量ボタンの動作

近接センサも静電容量ボタンと同じように静電容量の変化をとらえて、接近を検出しています。 それでは、静電容量ボタンと近接センサの違いは、どこにあるのでしょうか。 比較するために、 "BTN1" の動作もグラフにしてみました。

  1. Base line の値が大きい

    静電容量ボタンの Base line は、約1530です。 これに対して近接センサの Base line は、約52660です。 つまり、30倍以上の違いがある事がわかります。

  2. Raw count の振れ幅が大きい

    静電容量ボタンに触れた時、 Raw count は300ほど高くなります。 これに対して、近接センサに触れた時、 Raw count は5000ほど高くなります。

これらの差は、静電容量センサのいわゆる「感度」による違いです。 「感度」は、単位容量に対する Raw count の差として定義されます。 近接センサの感度が30倍高いので、指を触れていない時の静電容量の Raw count も30倍大きくなっています。 結果として、 Base line が30倍大きな値になったというわけです。

振れ幅の差も「感度」が影響したものです。 指が触れた時に増加する静電容量に対する Raw count の増分も感度が高くなれば大きくなります。 近接センサは、 Raw count の振れ幅が大きいことで、近接状態の検知と接触状態の検知を行う事ができます。

アプリケーションで使うには

近接センサの状態を一般的なアプリケーションで利用する場合も、 I2C インターフェイスを介してレジスタにアクセスし、状態を知ることができます。 具体的には、アドレス 0xAE の下位1ビットが "BTN0" 近接センサの状態を示しています。

参考文献

CY3280-MBR3 CapSense® MBR3 Evaluation Kit

MBR3 の具体的な使い方は、このキットに関連した文書として提供されています。 特に "CY3280-MBR3 User Guide" が、キットを使わない場合でも役に立ちます。

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

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

CY8CMBR3xxx CapSense® Design Guide

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


MBR3 で、静電容量センサの実験 (1) [PSoC]このエントリーを含むはてなブックマーク#

MBR3102 実験基板

さきごろ、 CapSense MBR3 というシリーズのチップが発表されました。 MBR は、 "Mechanical Button Replacement" つまり、「機械式ボタンを置き換えよう」という意味を持っています。 では、さっそく使ってみますか。

実験に使った回路図

実験基板の回路図

今回使用する実験基板は、このような回路になっています。 左側にあるのは、 MiniProg3 を接続するための5ピンコネクタです。 ここから電源を取り入れ、さらに I2C で通信を行います。 右側にあるのが、静電容量ボタンのセンサです。 単に抵抗を介して電極がつながっているだけですが、これだけの構造で静電容量の変化を検出し、ボタンとして動作します。

MBR3 の設定方法

MBR3 は、プログラムを開発する必要がありません。 その代わりに、 I2C を介して動作を設定する事ができます。 設定に必要なのは、 EZ-Click 2.0 というアプリケーションと、電気的に I2C の信号をやり取りするための USB-I2C Bridge というハードウェアです。 EZ-Click 2.0 が対応する USB-I2C Bridge には、以下のようなものがあります。

この他に多少の改造が必要になりますが、 CY8CKIT-042 PSoC® 4 Pioneer Kit などに搭載されている KitProgUSB-I2C Bridge 機能を使用する事もできます。 また、現在のところ、 USB-Serial Bridge Controller には対応していません。 この記事では、 MiniProg3 を使って、設定を行っていきます。

EZ-Click 2.0 で静電容量ボタンを設定する

EZ-Click 2.0

EZ-Click 2.0 を起動したら、最初に設定ファイルの作成場所を指定します。 メニューから "File"→"New Project..." を選択して、 "New Project" ダイアログを呼び出します。


New Project ダイアログ

このダイアログでは、プロジェクト名("Project Name")、プロジェクトの配置場所("Project Path")、そして使用するデバイス("Target Device")を指定します。 ここでは、 CY8CMBR3102-SX1I を使用する "Design001" というプロジェクトを作成しています。


CapSense sensor configuration タブ

"CapSense sensor configuration" タブでは、ふたつのボタンをイネーブルします。 "Enable" カラムのふたつのチェックボックスにチェックを入れるだけで、静電容量ボタンが動作するようになります。 ここでは、ボタンの名前を回路図の名前に合わせて "BTN0" と "BTN1" に変更しています。 他の設定は、デフォルトのままにしておきます。


プロジェクトの保存

プロジェクトが完成したので、 "Save Project" でプロジェクトを保存します。


Select Target Device ボタン

ここで、 MBR3MiniProg3 を接続し、さらに PC に接続します。 そして、 "Select Target Device" で接続相手の MBR3 を探しにいきます。


Select I2C Target ダイアログ

"Select I2C Target" ダイアログが開いたら、 "Ports" で "MiniProg3" を選択します。 そして、 "Power" で MiniProg3 から供給する電源電圧を指定します。 ここでは、 5.0V の電源を供給します。 さらに、 "I2C Speed" で、 I2C インターフェイスで使用するクロックの周波数を指定します。 ここでは、 400kHz で通信を行います。

ここまで設定できたら、 "Devices" に通信可能なデバイスの一覧が現れますので、目当てのデバイスを選択します。 この例では、デバイスは一つだけ見つかりました。


設定の適用

これで準備完了です。 "Apply Current Configuration" で MBR3 に設定を書き込むと、すぐに静電容量ボタンの動作が始まります。

静電容量ボタンの確認

CapSense 出力

静電容量ボタンの動作の確認も、 EZ-Click 2.0 から行います。

  1. CapSense output タブを開きます。
  2. "Select view" で "Button output" を選びます。
  3. "Button" で ボタン "BTN0" または "BTN1" を選びます。
  4. "Graph" で "Raw count vs Base Line" を選びます。
  5. "Start" ボタンをクリックします。

これで、グラフが表示されるようになります。 ここでは、 "Raw count" と "Base Line" という二つの二つの値が表示されています。 "Raw count" は、静電容量センサがとらえた容量値を表しています。 また、 "Base Line" は、「静電容量センサに指が触れていない時の推定容量値」を表しています。 静電容量センサに指が触れると、これらの値が乖離(かいり)して、指が触れた事を検知するという仕組みです。

グラフの上の方にある "Button status" は、「MBR3 が、指が触れたと判断しているか」を On/Off で示します。 また、 Cp (pF) は、「静電容量センサに指が触れていない時の推定容量値」をピコファラッド単位で計算した値です。

アプリケーションで使うには

実際のアプリケーションでは、 "Button status" で表示されているボタンの状態だけを知りたい場合がほとんどであろうと思われます。 その場合、 I2C インターフェイスを介してレジスタを読み出す事で、ボタンの状態を知ることが出来ます。 具体的には、アドレス 0xAA の下位2ビットが、ふたつのボタンの状態を表しています。

他には、ボタンの状態を MBR3 の出力端子により直接知らせる機能もあります。 この機能を使うためには、 EZ-Click 2.0 で、今回とは違う設定を使用する必要があります。

参考文献

CY3280-MBR3 CapSense® MBR3 Evaluation Kit

MBR3 の具体的な使い方は、このキットに関連した文書として提供されています。 特に "CY3280-MBR3 User Guide" が、キットを使わない場合でも役に立ちます。

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

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

CY8CMBR3xxx CapSense® Design Guide

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


PSoC 3 で、かけ算器を作った [PSoC]このエントリーを含むはてなブックマーク#

かけ算器

全国一千万人のデータパスファンの皆様、お待たせいたしました。 すっかり、公開を忘れていましたが、ついに、かけ算器までたどり着きました。 といっても、まだまだ、チューニングが必要ですね。 ひとまず、公開。

かけ算器の考え方

10倍増器のデータフロー

以前、「PSoC 3 で、16ビット演算器を作った」で定数倍のかけ算器を作りました。 このデータフローでは、アキュムレータのデータをシフトしながら、時々、データレジスタの値を足しこむことで、10倍の値を生成していました。 この「データレジスタの値を足しこむ」タイミングを制御すると、任意の倍数の「かけ算器」に改造することができます。


かけ算器のデータフロー

そのタイミングの制御の方法ですが、三つ目のデータパスを追加してシフトレジスタとして使い、その MSB の値にしたがって、データレジスタの値を足すか足さないかを決定します。 dp1 の A0 をシフトレジスタとして使用し、 dp0 の D0 レジスタに入った値を10倍にして dp0 の A0 に求める計算を行っています。

実際には、 dp1 に8ビットの値を入れてかけ算を行うため、かけ算の手順は、 STEP1 から STEP8 までが必要です。 このデータフローでは、下位4ビットの STEP5 から STEP8 までを記述しています。

シフトレジスタ部分の構成

シフトレジスタ部分の設定

シフトレジスタ dp1 で使用する機能そのものは、 dp0 の機能と揃えました。 そのため、ステートマシンと機能コードデコーダは、 dp0 と dp1 で同じものが使えます。

Verilog の記述

Verilog 記述は、一つの Verilog ファイルに三つのデータパスを記述しているので、かなり長くなってしまいました。 また、 dp0 で使用される機能コードの最下位ビットには、 dp1 の MSB を直接取り入れて、機能コードデコーダを簡略化しています。

//`#start header` -- edit after this line, do not edit this line
// ========================================
//
// Copyright noritan.org, 2013
// All Rights Reserved
// UNPUBLISHED, LICENSED SOFTWARE.
//
// CONFIDENTIAL AND PROPRIETARY INFORMATION
// WHICH IS THE PROPERTY OF NORITAN.ORG.
//
// ========================================
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 06/08/2013 at 16:32
// Component: Multiply16
module Multiply16 (
	output  drq,
	input   clock,
	input   en,
	input   reset
);

//`#start body` -- edit after this line, do not edit this line

// State code declaration
localparam      ST_IDLE     = 4'b0000;
localparam      ST_GET      = 4'b0001;
localparam      ST_STEP1    = 4'b1000;
localparam      ST_STEP2    = 4'b1001;
localparam      ST_STEP3    = 4'b1010;
localparam      ST_STEP4    = 4'b1011;
localparam      ST_STEP5    = 4'b1100;
localparam      ST_STEP6    = 4'b1101;
localparam      ST_STEP7    = 4'b1110;
localparam      ST_STEP8    = 4'b1111;
localparam      ST_WAIT     = 4'b0011;
localparam      ST_PUT      = 4'b0010;

// MSB part of Datapath function
localparam      CS_IDLE     = 2'b00;
localparam      CS_CLEAR    = 2'b01;
localparam      CS_SL       = 2'b10;
localparam      CS_ADD      = 2'b11;

// Wire declaration
wire[3:0]       state;          // State code
wire            f0_empty;       // DP1.F0 is EMPTY
wire[1:0]       f1_full;        // DP0.F1 is FULL
wire[1:0]       so;             // Shift out of DP0_A
wire[1:0]       co;             // Carry out of DP0_A
wire            dp1_msb;        // MSB of DP1

// Pseudo register
reg[1:0]        addr;           // MSB part of Datapath function
reg             d0_load;        // LOAD D0 from F0
reg             f1_load;        // LOAD F1

// State machine behavior
reg [3:0]       state_reg;
always @(posedge reset or posedge clock) begin
    if (reset) begin
                state_reg <= ST_IDLE;
    end else casez (state)
        ST_IDLE:
            if (en & ~f0_empty) begin
                state_reg <= ST_GET;
            end
        ST_GET:
                state_reg <= ST_STEP1;
        ST_STEP1:
                state_reg <= ST_STEP2;
        ST_STEP2:
                state_reg <= ST_STEP3;
        ST_STEP3:
                state_reg <= ST_STEP4;
        ST_STEP4:
                state_reg <= ST_STEP5;
        ST_STEP5:
                state_reg <= ST_STEP6;
        ST_STEP6:
                state_reg <= ST_STEP7;
        ST_STEP7:
                state_reg <= ST_STEP8;
        ST_STEP8:
                state_reg <= ST_WAIT;
        ST_WAIT:
            if (~f1_full[1] & ~f1_full[0]) begin
                state_reg <= ST_PUT;
            end
        /*ST_PUT*/ default:
                state_reg <= ST_IDLE;
    endcase
end
assign      state = state_reg;

// Internal control signals
always @(state) begin
    casez (state)
        ST_IDLE: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_GET: begin
            addr    = CS_CLEAR;
            d0_load = 1'b1;
            f1_load = 1'b0;
        end
        ST_STEP1, ST_STEP2, ST_STEP3, ST_STEP4,
        ST_STEP5, ST_STEP6, ST_STEP7: begin
            addr    = CS_SL;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_STEP8: begin
            addr    = CS_ADD;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_WAIT: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        /*ST_PUT*/ default: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b1;
        end
    endcase
end

reg drq_reg;
always @(posedge clock) begin
    casez (state)
        ST_PUT:     drq_reg <= 1'b1;
        default:    drq_reg <= 1'b0;
    endcase
end
assign          drq = drq_reg;

cy_psoc3_dp16 #(.cy_dpconfig_a(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0:   IDLE0 : Do nothing*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:   IDLE1 : Do nothing*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2:   CLEAR0 : A0 <= ALU <= (A0 XOR A0);*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3:   CLEAR1 : A0 <= ALU <= (A0 XOR A0);*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM4:   SL0 : A0 <= ALU <= (A0) << 1;*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM5:   SL1 : A0<= ALU <= (A0 + D0) << 1;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM6:   ADD0 : A0 <= ALU <= (A0);*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM7:   ADD1 : A0 <= ALU <= (A0 + D0);*/
    8'hFF, 8'h00,  /*CFG9:     */
    8'hFF, 8'hFF,  /*CFG11-10:     */
    `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
    `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
    `SC_SI_A_DEFSI, /*CFG13-12:     */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_ALU, `SC_FIFO0_BUS,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_NOCHN, `SC_CMP1_NOCHN,
    `SC_CMP0_NOCHN, /*CFG15-14:     */
    10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:     */
}
), .cy_dpconfig_b(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0:   IDLE0 : Do nothing*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:   IDLE1 : Do nothing*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2:   CLEAR0 : A0, A1 <= ALU <= (A0 XOR A0);*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3:   CLEAR1 : A0, A1 <= ALU <= (A0 XOR A0);*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM4:   SL0 : A0 <= ALU <= (A0) << 1;*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM5:   SL1 : A0 <= ALU <= (A0 + A1) << 1;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM6:   ADD0 : A0 <= ALU <= (A0);*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM7:   ADD1 : A0 <= ALU <= (A0 + A1);*/
    8'hFF, 8'h00,  /*CFG9:     */
    8'hFF, 8'hFF,  /*CFG11-10:     */
    `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
    `SC_CI_A_CHAIN, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
    `SC_SI_A_CHAIN, /*CFG13-12:     */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_ALU, `SC_FIFO0_BUS,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_NOCHN, `SC_CMP1_NOCHN,
    `SC_CMP0_NOCHN, /*CFG15-14:      */
    10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:     */
}
)) dp0(
        /*  input           */  .reset(reset),
        /*  input           */  .clk(clock),
        /*  input   [02:00] */  .cs_addr({addr[1:0], dp1_msb}),
        /*  input           */  .route_si(1'b0),
        /*  input           */  .route_ci(1'b0),
        /*  input           */  .f0_load(1'b0),
        /*  input           */  .f1_load(f1_load),
        /*  input           */  .d0_load(d0_load),
        /*  input           */  .d1_load(1'b0),
        /*  output  [01:00] */  .ce0(),
        /*  output  [01:00] */  .cl0(),
        /*  output  [01:00] */  .z0(),
        /*  output  [01:00] */  .ff0(),
        /*  output  [01:00] */  .ce1(),
        /*  output  [01:00] */  .cl1(),
        /*  output  [01:00] */  .z1(),
        /*  output  [01:00] */  .ff1(),
        /*  output  [01:00] */  .ov_msb(),
        /*  output  [01:00] */  .co_msb(),
        /*  output  [01:00] */  .cmsb(),
        /*  output  [01:00] */  .so(),
        /*  output  [01:00] */  .f0_bus_stat(),
        /*  output  [01:00] */  .f0_blk_stat(),
        /*  output  [01:00] */  .f1_bus_stat(),
        /*  output  [01:00] */  .f1_blk_stat(f1_full[1:0])
);

cy_psoc3_dp8 #(.cy_dpconfig_a(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0:   IDLE0 : Do nothing*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1:   IDLE1 : Do nothing*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2:   CLEAR0 : A0 <= F0;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3:   CLEAR1 : A0 <= F0;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM4:   SL0 : A0 <= (A0 << 1);*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM5:   SL1 : A0 <= (A0 << 1);*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM6:   ADD0 : A0 <= A0;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM7:   ADD1 : A0 <= A0;*/
    8'hFF, 8'h00,  /*CFG9:    */
    8'hFF, 8'hFF,  /*CFG11-10:    */
    `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
    `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
    `SC_SI_A_DEFSI, /*CFG13-12:    */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_NOCHN, `SC_CMP1_NOCHN,
    `SC_CMP0_NOCHN, /*CFG15-14:    */
    10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:    */
}
)) dp1(
        /*  input                   */  .reset(reset),
        /*  input                   */  .clk(clock),
        /*  input   [02:00]         */  .cs_addr({addr[1:0], 1'b0}),
        /*  input                   */  .route_si(1'b0),
        /*  input                   */  .route_ci(1'b0),
        /*  input                   */  .f0_load(1'b0),
        /*  input                   */  .f1_load(1'b0),
        /*  input                   */  .d0_load(1'b0),
        /*  input                   */  .d1_load(1'b0),
        /*  output                  */  .ce0(),
        /*  output                  */  .cl0(),
        /*  output                  */  .z0(),
        /*  output                  */  .ff0(),
        /*  output                  */  .ce1(),
        /*  output                  */  .cl1(),
        /*  output                  */  .z1(),
        /*  output                  */  .ff1(),
        /*  output                  */  .ov_msb(),
        /*  output                  */  .co_msb(),
        /*  output                  */  .cmsb(),
        /*  output                  */  .so(dp1_msb),
        /*  output                  */  .f0_bus_stat(),
        /*  output                  */  .f0_blk_stat(f0_empty),
        /*  output                  */  .f1_bus_stat(),
        /*  output                  */  .f1_blk_stat()
);
//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

かけ算器の使い方

これまでのかけ算器は、 dp0 の FIFO にデータが入ったら、それをトリガにして計算を開始していました。 今回は、 dp1 の FIFO への書き込みをトリガにしています。 そのため、かけ算器に値を書き込むときには、 dp0 への書き込みの後、 dp1 への書き込みをおこないます。

                for (i = 0; i < N_BATCH; i++) {
                    CY_SET_REG16(Multiply16_INPUT_PTR, alub[i]);
                    CY_SET_REG8(Multiply16_MULTIPLIER_PTR, alua[i]);
                }

かけ算の結果は、前回の「PSoC 3 で、 DMA 対応倍増器を作った」と同様に DMA で自動的に取り出されます。 プログラムの部分は、前回同様、計算結果を自動的に検証して、検証結果を LCD に表示させるようにしています。

プロジェクトアーカイブ

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

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

CY8CKIT-049 Prototyping Kit で簡単に Bootloader を呼び出す [PSoC]このエントリーを含むはてなブックマーク#

PSoC 4200 Prototyping Kit (CY8CKIT-049-42xx)

PSoC 42xx シリーズを使って、簡単に工作を楽しめる PSoC 42xx Prototyping Kit (CY8CKIT-049) が提供されています。 このボードは、 Bootloader という仕組みを使って、ユーザが作成したアプリケーションを導入する事ができます。 この記事では、この基板で Bootloader を使う際の「チョットイイ小技」をご紹介します。

Bootloader でアプリケーションを導入する仕組み

分割された Prototyping Kit

この基板は、 CY8C4245AXI-483 (PSoC 4) を搭載した大きい基板と CY7C65211-24LTXI (USB-Serial) を搭載した小さい基板で構成されており、この写真のように二つに切り離して使うことができます。 小さい基板は、 PC の USB 端子に接続する事で、 UART シリアルインターフェイスとして働きます。 さらに、小さい基板と大きい基板の間は、電源と UART の RX/TX の計4本の配線で接続されています。 大きい基板の PSoC 4 に搭載されている Bootloader プログラムは、この UART シリアルインターフェイスを介して、 PC からアプリケーションを受信し、 Flash ROM の内容を書き換えます。

このように Flash ROM を書き換えるためには、 Bootloader が動作している状態で PC との通信を開始しなくてはなりません。 ところが、ユーザが作成したアプリケーションは、 Flash ROM を書き換えるために作られている訳ではありませんので、 Flash ROM を書き換える仕掛けを持っているとは限りません。 アプリケーションを書き換えるためには、何とかして Bootloader を呼び出さなくてはならないのです。 さて、困った。

Bootloader を呼び出す標準的な方法

器用さが要求される Bootloader 呼び出し

Bootloader を呼び出す方法は、標準では一つだけ用意されています。 それは、 SW1 を押しながら、 PSoC 4 にリセットをかける事です。 この基板には、リセット・スイッチは搭載されてないため、基板を無改造で使用する場合には、電源を再投入することによるパワー・オン・リセットによる方法しかありません。 さらに、 PSoC 4 の電源は小さい基板を介して USB コネクタの VBUS に接続されています。 このような事情により「Bootloader を呼び出すには、 SW1 を押しながら USB コネクタに抜き差しする。」というのが、標準的な手順になっています。

ところが、この方法には問題があります。 それは、 USB コネクタの信頼性です。 小さい基板は、 USB コネクタを基板パターンで作成しています。 そのため、ちょっと荷重をかけると、 USB コネクタの VBUS が切れてしまうのです。 つまり、 SW1 を押すだけでも VBUS が切れてしまいます。 これでは、パワー・オン・リセットをかけるどころではありません。

ふたつの基板を切り離して配線で接続すると、 SW1 を押しても USB コネクタに力がかからないようになります。 これで、 SW1 を押しながら、小さい基板を抜き差しして、確実に Bootloader を呼び出す事ができます。 これでも良いのですが、小さい基板を抜き差しする事によって、シリアルインターフェイスが途切れてしまう事態は避けられません。 もっと、簡単で確実な方法はないものでしょうか。

サイプレス公式 BLOG で紹介された手法

長押し検出回路

実は、すでに Bootloader 呼び出す手間を減らすための手法が考案されていて、公式 BLOG で紹介されています。 キーは、この回路です。

この Timer コンポーネントには、 "reload" "start" "stop" の三つの制御端子が追加されて、すべての端子が SW1 に接続されています。 「あれっ? "start" と "stop" に同じ信号が入ったら、動かないんじゃないの?」 ごもっともです。 秘密は、それぞれの端子の「モード」にあります。


InputMode
reloadFalling edge
startFalling edge
stopRising edge

それぞれの制御端子のモードは、この表のように設定されています。 まず、 Timer を起動したときには、カウンタは止まっています。 そこから SW1 を押すと、 SW1 には立ち下がりエッジが発生します。 立ち下がりエッジでは、 "reload" と "start" イベントが働きますので、ダウンカウンタに初期値が設定され、カウントが始まります。 しばらく SW1 が押されたままになっていると、ダウンカウンタがゼロに達して、割り込みを発生させます。 この時の時間は、 Timer に接続されたクロックの周波数とダウンカウンタの初期値によります。 この例では、 12kHz のクロックを 24000 回数えているので、2秒の設定になっています。

ダウンカウンタがゼロになる前に SW1 が離されると、 SW1 に立ち上がりエッジが発生します。 そのため、 "stop" イベントが働き、ダウンカウンタは停止し、割り込みは発生しません。

割り込みサービスルーチンでは、 Bootloadable を経由して Bootloader を呼び出すコードが記述されています。 こうして、 SW1 を2秒以上押し続けると Bootloader を呼び出す仕掛けが出来ました。

リソースがもったいないじゃないか

この仕掛けは、記述するコードの量が、ごく少ないので簡単に実装できますが、ハードウェアタイマを一つ占有してしまうので、もったいない感があります。 ここは、ソフトウェアで実装して、必要なハードウェアリソースを少なくしましょう。

クロックを使った周期割り込みタイマ

周期割り込みタイマ

一つ目の実装で使うのは、 Interrupt コンポーネントにクロックを直結したタイマです。 PSoC 3PsoC 5LP では、本当にクロックを直結する事が出来たのですが、 PSoC 4 では、このようにフリップフロップを使わなくてはなりません。 これは、内部配線の都合でクロックとロジック信号を明確に区別する必要があるためです。 200Hz のクロックをフリップフロップで2分周したので、1秒間に100回割り込みが発生します。

// PIT 割り込みサービスルーチン
CY_ISR(int_Pit_isr) {
    LedMachine();
    BootMachine();
}

割り込みサービスルーチンは、このようになっています。 呼び出されているふたつの関数は、前回まで PSoC 40xxBootloader を実験していた時に使用していたものと同じです。

// Bootloader への移行を確認するステートマシン
// 移行確認時間を3秒とする
#define         BOOT_VALIDATION     (3*INT_FREQ)

uint32          boot_count = 0;

void BootMachine(void) {
    if (SW1_Read()) {
        // SW1 が RELEASE されている
        boot_count = 0;
    } else {
        // SW1 が PUSH されている
        if (++boot_count >= BOOT_VALIDATION) {
            // 確認期間を超えて SW1 が押され続けた
            Bootloadable_Load();
        }
    }
}

実は、前回までの記事で作成した Bootloadable application には、ブートに入るための仕掛けが、すでに入っていたのでした。 ここでの実装では、 SW1 を3秒間押し続けると Bootloader が呼び出されるようになっています。

        // PIT 割り込みの初期設定
        int_Pit_StartEx(int_Pit_isr);

初期設定も簡単です。 Interrupt コンポーネントに割り込みサービスルーチンを登録して、おしまいです。

    // UART の起動
    // これが無いと Bootloader 移行直後の接続がうまくいかない
    UART_Start();
    
    // 割り込み許可
    CyGlobalIntEnable;

最後に割り込みを許可するのですが、一つだけ、やらなくてはならない事がありました。 それは、 UART コンポーネントを起動する事です。 このアプリケーションでは、 UART は、いっさい使っていません。 それでも、 UART の起動が必要な理由は、 USB-Serial に接続されている端子 P4[0] および P4[1] を不定にしないためです。

何度か Bootloadable アプリケーションをプログラムして試してみたのですが、 Bootloadable アプリケーションでは、少なくとも TX 端子 (P4[1]) を High にしておかないと、うまく Bootloader を呼び出す事が出来なくなってしまいました。 ここでは、 UART コンポーネントを配置してしまって、これらの端子の状態を確定させています。

SysTick を使った実装

二つ目は、 SysTick を使った実装です。 一つ目の周期割り込みでは、クロックの周期がそのまま割り込み周期になっていましたが、 SysTick の場合には、コードで周期を明記する必要があります。

// Systick 周期の定数
#define         SYSTICK_PERIOD      (24000000/INT_FREQ)

PSoC 42xx をデフォルトの状態で使用しているので、 SYS_CLK は、 24MHz です。 よって、 SysTick に与える分周比は、このように計算できます。

// SysTick 割り込みサービスルーチン
CY_ISR(SysTick_isr) {
    LedMachine();
    BootMachine();
}

割り込みサービスルーチンは、周期割り込みを使った場合とまったく同じです。

        // SysTick 割り込みサービスルーチンの初期設定
        CyIntSetSysVector(SysTick_IRQn + 16, SysTick_isr);
        
        // SysTick 割り込み周期の設定
        SysTick_Config(SYSTICK_PERIOD);

初期設定部分も、「PSoC 40xx で SysTick を使ってみる」で使ったものと同じです。

ウォッチドッグ・タイマを使った実装

一番大きく異なっているのは、ウォッチドッグ・タイマを使った場合の実装です。 コンポーネントの使い方は同じですが、「PSoC 40xx でウォッチドッグ・タイマを使ってみる」で紹介したものとは、コードがかなり異なっています。

// Watchdog 周期の定数
#define         WDT_PERIOD          (32768/INT_FREQ)

まず、割り込み周期の計算方法が異なっています。 これは、 PSoC 42xx に内蔵されている ILO クロックの周波数が、標準で 32768Hz であるためです。

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

割り込みサービスルーチンも異なっています。 まず、割り込みフラグをクリアする時にクリアすべきカウンタを指定しています。 これは、 PSoC 42xx のウォッチドッグ・タイマが三つの16ビットカウンタで構成されているためです。 三つのカウンタは、設定により、並列に動作させる事も、カスケードに接続して長周期タイマとして使用する事もできます。 ここでは、16ビットのカウンタひとつで十分なので、 COUNTER0 だけを使っています。

さらに、 PSoC 40xx に内蔵されていたウォッチドッグ・タイマは、フリーランニングタイマでしたが、 PSoC 42xx に内蔵されているものは、リロード機能付きのタイマです。 そのため、次に期待される割り込み時刻を再設定する必要がなく、コードが簡単になっています。

        // Watchdog 割り込みの初期設定
        int_Wdt_StartEx(int_Wdt_isr);
        
        // Watchdog 割り込み周期の設定
        CySysWdtUnlock();
        CySysWdtWriteMode(CY_SYS_WDT_COUNTER0, CY_SYS_WDT_MODE_INT);
        CySysWdtWriteMatch(CY_SYS_WDT_COUNTER0, WDT_PERIOD);
        CySysWdtWriteClearOnMatch(CY_SYS_WDT_COUNTER0, 1);
        CySysWdtEnable(CY_SYS_WDT_COUNTER0_MASK);
        CySysWdtLock();

初期設定部分は、大幅に複雑になっています。 これは、先に述べたように、ウォッチドッグ・タイマが三つのカウンタで構成されているため、それぞれの設定を行う必要があるためです。 また、このウォッチドッグ・タイマは、安全のため、ウォッチドッグ・タイマにロックを掛ないと設定が変更できないようになっています。

プロジェクトアーカイブ

この記事で作成したプロジェクトは、このファイルの拡張子を "zip" に変更すると再現できるようになります。 今回は、一つのプロジェクトに三つの機能を盛り込んでしまい、以下のマクロ宣言でどの機能を有効にするかを決める方法をとっています。

// 3種類のタイマから周期タイマを選ぶ
#define         PIT                 (0)         // Periodit Interrupt Timer
#define         WDT                 (1)         // WatchDog Timer
#define         SYSTICK             (0)         // SysTick Timer

参考文書

PSoC® 4: PSoC 4200 Family Datasheet
PSoC 42xx シリーズのデータシートです。 電気的な特性に関しては、いちばん詳しい情報が掲載されています。
PSoC 4100 and 4200 Family: PSoC® 4 Architecture Technical Reference Manual (TRM)
PSoC 41xx シリーズと PSoC 42xx シリーズの内部構造を開設した文書です。 ウォッチドッグ・タイマについても、この文書に書かれていますが、あまり理解しやすいとはいえません。
AN90799 - PSoC® 4 Interrupts
PSoC 4 の割り込みについて書かれた文書です。 この中でウォッチドッグ・タイマが解説されていますが、やっぱり、物足りないですね。

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

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

ウォッチドッグ・タイマを周期タイマに使う

前回の記事では、専用タイマモジュールである TCPWM を使わずに SysTick を使って周期的なタイマを実現しました。 今回は、もうひとつのタイマ Watchdog Timer (WDT: ウォッチドッグ・タイマ)を使って周期的なタイマを実現します。

ウォッチドッグ・タイマの役割

ウォッチドッグ・タイマのブロック図

ウォッチドッグ・タイマのそもそもの役割は、プログラムが動作しているかどうかを確認する事です。 ユーザが書いたプログラムは、定期的にウォッチドッグ・タイマに「生きてるよ」と知らせます。 もちろん、声をかけるのではなく、ウォッチドッグ・タイマのレジスタに特定の値を書きこむ事で、プログラムが生きている事を通知します。

ウォッチドッグ・タイマは、プログラムからの「生きてるよ」通知を監視していて、ある一定の時間を超えて通知が来なくなったら、「プログラムが暴走を始めた」と判断してリセットをかけます。 すると、リセットをかけられたプログラムは、再び、正常な動作をするようになるという仕組みです。

ウォッチドッグ・タイマは、内蔵低速発振器 (Internal Low-speed Oscillator: ILO) で駆動される16ビットのタイマを持っていて、このタイマで割り込みを発生させます。 ILO の発振周波数は、標準値で 40kHz なので、ウォッチドッグ・タイマが発生する割り込み間隔は、1.6秒ほどです。 実際には、割り込みまでの時間を設定する仕組みも入っていますので、「最大1.6秒」ということになります。

ウォッチドッグ・タイマが発生した割り込みが処理されず、ふたたび割り込みを発生する状態になると、ウォッチドッグ・タイマは「暴走」を確信し、リセットをかけます。

ここまでの解説は、ウォッチドッグ・タイマの本来の使い方です。 もし、割り込みが発生したところで、確実に割り込み処理を行ったとすると、周期的に動作するタイマとして動作させる事ができます。

ILO クロックの周波数は、いくつだろう?

データシートにある ILO の解説

この記事を書いている時点では、PSoC 40xx シリーズのデータシートによると、 ILO の発振周波数は、標準値で 40kHz となっています。 ところが、 Architecture Technical Reference ManualRegisters Technical Reference Manual と呼ばれる文書では、 32kHz が標準となっています。 確認したところ 40kHz の方が正しい値のようです。

ただし、 ILO の発振周波数の仕様は、 20kHz から 80kHz とかなり広範囲にわたっています。 そのため、 32kHz と書いてあっても誤差範囲内に入ってしまいます。 あまり、信用しすぎないようにした方がよさそうです。

WDT を扱うコンポーネント

WDT出力を持つ Global Signal Reference コンポーネント

PSoC Creator のコンポーネント・カタログを探しても、ウォッチドッグ・タイマのコンポーネントはありません。 では、どうやってウォッチドッグ・タイマを使うかというと、 Global Signal Reference コンポーネントで取り扱います。

Global Signal Reference コンポーネントを回路図上に配置すると、一緒に Interrupt コンポーネントが付いてきます。 Global Signal Reference コンポーネントは、デフォルトの状態でウォッチドッグ・タイマの割り込み信号である WDT 信号が出力されます。 これだけで、ウォッチドッグ・タイマの割り込みを受け付ける仕組みが出来上がります。

ウォッチドッグ・タイマのコード

ウォッチドッグ・タイマの初期設定

ウォッチドッグ・タイマは、コンポーネントとして提供されるわけではないので、初期設定などをコードとして書かなくてはなりません。 初期設定は、このようになっています。


  1. ウォッチドッグ割り込みを受けとる Interrupt コンポーネントを初期設定する。
  2. 割り込みまでのカウント数を MATCH レジスタに書き込む。
  3. ウォッチドッグ割り込みを許可する。
  4. ウォッチドッグ・タイマを起動する。

この後、全体の割り込みを許可すると、 MATCH レジスタに設定した時間が経過した後、ウォッチドッグ割り込みが発生します。


割り込み周期の設定

このファームウェアでは、前回の SysTick モジュールと同様、1秒間に100回割り込みが発生するように設定しています。 ただし、使用するクロックが、 40kHz ですので、割り込み間隔カウント数の計算部分が異なっています。


割り込み処理ルーチン

割り込み処理ルーチンは、 TCPWMSysTick を使用する場合と少々異なっています。 これは、ウォッチドッグ・タイマが、フリーランニングタイマという種類に分類されるからです。 たとえば、 SysTick を使う場合、カウンタがある値に一致したら、カウンタの値が自動的にリロードされます。 そのため、特別にタイマを操作する必要はありません。

ところが、フリーランニングカウンタの場合、カウンタ値が一致してもリロードは行われません。 また、カウンタの値をソフトウェアから書き換える事もできません。 そのため、周期的なタイマとして使う場合には、次に割り込みを発生させるべきカウンタ値を MATCH レジスタに書き込んで、割り込み周期を規定します。

その他、ウォッチドッグ割り込みに関連する割り込みフラグを確実にクリアする必要があります。 これも、ウォッチドッグ・タイマがコンポーネントとして提供されていないがゆえに必要な操作です。

プロジェクトアーカイブ

この記事で作成したプロジェクトは、このファイルの拡張子を "zip" に変更すると再現できるようになります。 これまで同様、 LED の点滅パターンを少々変更しています。

参考文書

PSoC® 4: PSoC 4000 Family Datasheet: Programmable System-on-Chip (PSoC®)
PSoC 40xx シリーズのデータシートです。 電気的な特性に関しては、いちばん詳しい情報が掲載されています。
PSoC 4000 Family: PSoC® 4 Architecture Technical Reference Manual (TRM)
PSoC 40xx シリーズの内部構造を開設した文書です。 ウォッチドッグ・タイマについても、この文書に書かれていますが、あまり理解しやすいとはいえません。

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 40xx で SysTick を使ってみる [PSoC]このエントリーを含むはてなブックマーク#

SysTick を使う場合の回路図

前回の記事では、 PSoC 40xx シリーズに Bootloader を導入してみました。 Bootloadable として準備したのは、 PWM を使用して LED を駆動し、 CPU をフルに使って押しボタンの状態を監視していました。 PSoC 40xx シリーズは、用意されているリソースが少ないので、このような使い方は、かなり贅沢です。 今回は、 "SysTick" と呼ばれる周期タイマを使って、 LED の駆動と押しボタンの監視を行わせます。

SysTick って何だっけ?

SysTick は、 CPUである ARM Cortex-M0 に近い場所にある周期タイマで、他の Cortex-M0 搭載プロセッサでも内蔵されています。 SysTick は、もちろん PSoC Creator からも使う事ができます。 ただし、残念な事にコンポーネントとして提供されているわけえはないので、普通のマイコンで割り込みを扱う時と同じように、すべてプログラムから制御してやる必要があります。 SysTick を使うために必要なのは、以下のステップです。

  1. SysTick 割り込みに対応する割り込みベクタに割り込みサービスルーチン (Interrupt Service Routine; ISR) を割り当てます。
  2. SysTick の周期を設定します。
  3. 割り込みを許可します。

SysTick の初期設定

これだけで、 SysTick が周期割り込みとして作用します。 それぞれのステップは、 PSoC Creator の API 関数またはマクロで定義されているので非常に簡単です。 ここでは、 CPU が 12MHz クロックで動作していることを前提として、1秒当たりの割り込み回数 SYSTICK_FREQ から1周期のクロック数 SYSTICK_PERIOD を計算させています。


割り込み周期の設定

ここで紹介するファームウェアでは、すべての処理を ISR の中で行っています。 そのため、 main 関数には無限ループのみ残っています。 CPU は、ほぼ遊んでいますので、お好きな処理を加えてやってください。

割り込み処理ルーチンに実装した二つのステートマシン

割り込み処理ルーチン

SysTick により周期的に呼び出す処理内容は、このように二つのステートマシン BootMachine と LedMachine で構成されています。


Boot 検出ステートマシン

BootMachine では、押しボタンの監視を行っています。 このステートマシンでは、押しボタンが押され続けた時間を boot_count という変数で計測していきます。 そして、 BOOT_VALIDATION で決められた長さに達したら、 API 関数 Bootloadable_Load を使って、 Bootloader を呼び出します。 BOOT_VALIDATION の長さは、2秒に設定しています。


LED 駆動ステートマシン

LedMachine では、 LED の駆動を行っています。 led_state という変数を使用して、赤を2回、緑を2回点滅させるパターンを繰り返させています。 このステートマシンの周期 LED_PERIOD は、2秒に設定しています。

Bootloadable の書き込み

いつでも開発できます

Bootloadable プロジェクトをビルドしたところ、 5856 バイトと前回のプロジェクトよりも若干小さくなりました。 理由は、深く追い求めてはいません。

Bootloadable の書き込みは、前回と同様に I2C インターフェイスを介して行います。 前回の Bootloadable を書込んでいたら、押しボタンを2秒以上押し、Bootloader を呼び出してから Bootloader Host アプリケーションで CYACD ファイルを書込みます。 前回から、 LED の点滅パターンが変化したのがわかるでしょうか。

プロジェクトアーカイブ

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

参考文書

Using the SysTick Timer in PSoC® 4 – KBA91374
PSoC 4 で SysTick を使う方法について述べています。 コンポーネントにしてくれれば、もっとありがたいのですが。

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 40xx に Bootloader を導入してみた [PSoC]このエントリーを含むはてなブックマーク#

PSoC 40xx 実験中

PSoC に 40xx シリーズなるものが出てきました。 本当に使えるんだろうかていうぐらい内部リソースを削られてしまっています。 そんなリソースの無い PSoC 40xx シリーズで、 Bootloader は使えるのか、また、使う意味が有るのだろうか検証してみます。

PSoC 40xx のメモリ容量

PSoC 40xx シリーズには、いくつか種類が有ります。 現在のところ、 CY8C4013 と CY8C4014 という番号にサフィックスが付いた型番になっています。 Flash ROM のサイズは、 CY8C4013 が 8kB で、 CY8C4014 が 16kB になっています。 Bootloader が使用可能であるかを検証するためには、 8kB の CY8C4013 で試さなくてはならないでしょう。 ここでは、8ピン SOIC の CY8C4013SXI-410 を題材に使います。

Bootloader で使用可能な通信インターフェイス

PSoC 40xx のブロック図

Bootloader に対応したプロジェクトの作成手順は、 PSoC 3 等の他の PSoC と同様です。 PSoC CreatorApplication Type を指定してプロジェクトを作成し、 Bootloader または Bootloadable コンポーネントを配置するだけです。 問題になりそうなのは、 Bootloader で使用する通信線をどこに確保するか、 Bootloader を入れたら Bootloadable アプリケーションが入る隙間が無くなるんじゃないかという点です。

PSoC 40xx シリーズでは、リソースを大胆に削ってしまったおかげで、使用可能な通信線は I2C しか残されていません。 そのため、自分で通信プロトコルを記述する以外では、 I2C のほかに選択肢がありません。 そのため、 Bootloader では、必然的に I2C を選ぶことになります。

コンポーネントとしては、ソフトウェア UART も存在しますが、この UART は、送信専用であるため、送受信の機能が必要な Bootloader には使用できません。 あきらめて、 I2C を使う事にしましょう。

Bootloader プロジェクト

Bootloader の回路図

通信線が決まった所で、 Bootloader プロジェクトを作成します。 Application TypeBootloader を指定してプロジェクトを作成したら、 I2C コンポーネントと Bootloader コンポーネントを配置します。

Bootloader コンポーネントで通信に使用するコンポーネントに I2C を選択したら、他はデフォルトのままにしておきます。

8ピン SOIC の場合、端子数が極端に限られてくるため、 I2C を選んだ時点で SWD によるデバッグをあきらめざるをえません。 CYDWR ファイルで、デバッグ端子の特性を GPIO するのをお忘れなく。

他には、 Bootloader を呼び出すためのスイッチと状態を表示させるための LED 端子とそれを駆動する PWM を配置しました。


Bootloader のコード

ソースコードには、リセット直後にスイッチが0.1秒以上の間押されていたら Bootloader を呼び出すコードを記述しました。 スイッチが押されていない場合には、可能であれば Bootloadable を呼び出します。

ソースコードの最初の方では、 I2C のポート "SCL" と "SDA" をプルアップ付きポートに設定しています。 これで、外付けプルアップ抵抗を省略する事ができます。 ところで、この設定は有効なのだろうか?


Debug 設定でビルド

プロジェクトをビルドしたところ、 "Debug" 設定のままでは Flash ROM の消費量が 8kiB を 184 バイトほど超えてしまいました。


Release 設定でビルド

そこで、 "Release" 設定で試したところ、消費量はで 4608 バイトとなりました。 どうやら、この CY8C4013 で Bootloader を使用する時には、 "Release" 設定を使用しなくてはならないようです。

Bootloadable プロジェクト

Bootloadable の回路図

Bootloader プロジェクトが出来たら、次は、 Bootloadable プロジェクトを作成します。 これも、 Bootloader と同様に Application TypeBootloadable を指定してプロジェクトを作成した後、 I2C コンポーネントと Bootloadable コンポーネントを配置します。

I2C コンポーネントは、配置はしましたが、アプリケーションでは使用しません。 Bootloadable コンポーネントでは、先ほどビルドした Bootloader プロジェクトの HEX ファイルおよび ELF ファイルを指定して、 Bootloader 側の情報を読み取らせます。

他には、 Bootloader を呼び出すためのスイッチと状態を表示させる LED 端子と駆動用 PWM を配置してあります。 見ためではわかりませんが、 LED の点灯パターンは、 Bootloader のパターンから変更しているので、 LED の点灯パターンを見て、現在の状態を判断する事ができます。


Bootloadable のコード

Bootloadable のソースコードでは、2秒以上スイッチが押されたら Bootloader を呼び出すコードを記述しました。 これで、リセットや電源遮断をすることなく、簡単に Bootloader を呼び出す事ができます。


Bootloadable のビルド

ビルドをすると、Flash ROM の消費量は 5912 バイトとなりました。 この容量には、 Bootloader の部分も含みますので、 Bootloadable の正味消費量は 1304 バイトです。 これなら、もっと色々な事ができそうです。

Bootloadable の書き込み

Bootloader Host でプログラム

Bootloadable がビルドできたら、 Bootloader Host アプリケーションを使って Bootloadable をプログラムします。 今回は、 I2C インターフェイスを使用するので、 USB-I2C ブリッジが必要です。 てもとに適当な USB-I2C ブリッジが無いので MiniProg3 で書き込みます。


実験基板
どこでも開発できます

あれ? MiniProg3 が使えるんだったら、 Bootloader なんて必要ないんだな。

プロジェクトアーカイブ

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

参考文書

AN86526 - PSoC® 4 I2C Bootloader
PSoC 4I2C インターフェイスを使用した Bootloader を使う方法について解説しています。

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC Creator でも GitHub がしたい (4) [PSoC]このエントリーを含むはてなブックマーク#

Git Gui

前回まで、 GuiHub にプロジェクトを登録したり取り出したりする手順について考えました。 Git は、リビジョン管理ツールですから、当然、プロジェクトを変更したうえで、 GitHub に再度登録する機能もあります。

プロジェクトを変更する

変更後のプロジェクト

プロジェクトの開発を想定して、まず、プロジェクトを変更します。 ある種のリビジョン管理ツールでは、一度に一人だけがプロジェクトを変更できるようにするため、リポジトリを「ロック」してから変更をおこないます。 Git では、同時に複数の人がリポジトリを変更する事を許可しているので、リポジトリを「ロック」する必要がありません。 PSoC Creator でプロジェクトを開いて、そのまま変更します。 ここでは、 PWM を2出力に変更し、ソースコード main.c も少々変更しました。 変更が終わったら、プロジェクトのすべてのファイルを Save し、 Build します。

ローカルリポジトリに Commit

リポジトリの再スキャン

プロジェクトの変更が終わったら、 Git Gui でリポジトリを開きます。 Open Rescent Repository のリストから選ぶか、 Open Existing Repository でローカルリポジトリの場所を指定します。

Git Gui でリポジトリを開いたままにしていた場合には、 Rescan ボタンをクリックして、変更されたファイルを探しにいきます。

いずれの場合も、 Unstaged Changes に変更されたファイルが並びます。 リポジトリを作成した時と同様に、すべてのファイルが必要な訳ではありません。 そのため、ここから必要なファイルを選びます。


Stage Changed Files To Commit

ここで変更されたファイルを一つ一つ選択していっても良いのですが、もっと便利な方法があります。 メニュー "Commit"→"Stage Changed Files To Commit" を選択して、変更されたファイルを一括で選びます。 このメニューアイテムは、 Unstages Changes に並んだファイルを Staged Changes に移動するものですが、このあと追加のダイアログが現れます。


未登録ファイルを登録するか

このダイアログでは、「未管理のファイル 52 個を管理するか」を聞いています。 ここで候補として挙げられた 52 個のファイルは、 Git の管理に入っていないが、ワークスペースに存在するファイルです。 主に、 PSoC Creator によって、新たに作成されたファイルが、これに相当します。

今回の場合、 PSoC Creator で必要となるすべてのファイルは、リポジトリの作成時に管理するように指定しましたので、他のファイルを新たに管理下に置く必要はありません。 「いいえ」をクリックして、既存のファイルだけを選択します。


Commit

ファイルが選択されて Staged Changes に並んだら、次はコメントを書きます。 GitGui では、 Commit するときのコメントは必須になっています。 コメントを書込んだら、 Commit ボタンをクリックして、ローカルリポジトリに登録します。 正常に登録されたら、ダイアログが出てきますので、 Close で閉じます。

GitHub に Push

Push

Commit では、ローカルリポジトリに登録されます。 これをリモートリポジトリである GitHub に登録するのが Push です。 Push ボタンをクリックします。


Push の設定

Push は、ローカルリポジトリの変更箇所をリモートリポジトリに登録します。 次のダイアログでは、リポジトリの送り元と送り先を指定します。 送り元の "master" は、ローカルリポジトリに先ほど Commit した版を表します。 送り先の "origin" は、 GitHub のリモートリポジトリに付けた名前です。

Push をクリックすると、ローカルリポジトリの内容が GitHub に転送されます。


Push の詳細

Push が終わったら、転送の詳細を示すダイアログが表示されます。 Success が表示されたら、転送成功です。


リポジトリの確認

メニュー "Repository"→"Visualize All Branch History" を選択すると、リポジトリに記録された履歴が表示されます。 ローカルリポジトリの "master" とリモートリポジトリの "remotes/origin/master" が、同じバージョンになっていることがわかります。

GitHub の WEB ページで確認

リポジトリのページ

上で確認したリモートリポジトリの履歴は、実はローカルリポジトリにコピーされたリモートリポジトリの状態を見ているにすぎません。 本当に GitHub のリポジトリが変更されたかどうかは、 GitHub の WEB ページに直接確認した方が確実です。 そこで、 WEB ページ https://github.com/noritan/Design109_3p0_3 から、 "xx commits" と記述してあるリンクをクリックすると、リポジトリが Commit された履歴を見ることが出来ます。


Commit の履歴

すると、これまでに Commit してきた履歴が表示されます。 Git Gui から確認したのと同様に三つのバージョンが確認できます。 これで、変更後のプロジェクトが共有できるようになりました。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本
開発ツール徹底攻略 (WEB+DB PRESS plus)

開発ツール徹底攻略 (WEB+DB PRESS plus)

  • 作者: Junio C Hamano
  • 出版社/メーカー: 技術評論社
  • 発売日: 2013/04/10
  • メディア: 大型本

PSoC Creator でも GitHub がしたい (3) [PSoC]このエントリーを含むはてなブックマーク#

Git Gui

前回まで、 PSoC Creator のワークスペースを GitHub のリポジトリとして保存する方法について調べてきました。 今回は、保存したリポジトリを参照して、ローカルリポジトリにワークスペースを再現し、 PSoC Creator から使ってみます。

リポジトリの場所を確認する

リポジトリの場所

まず、リポジトリの場所を示す URL をクリップボードにコピーします。 GitHub の WEB ページ(ここでは https://github.com/noritan/Design109_3p0_3 )に行き、右下あたりにある "SSH clone URL" の "copy to clipboard" ボタンをクリックします。 これで、リポジトリの場所を示す URL が、クリップボードにコピーされました。

Git Gui で、リポジトリのコピーを作る

Git Gui を開く

次に新たに Git Gui を開きます。 Git Gui は、一つのリポジトリを扱う事しかできません。 そのため、別のリポジトリを操作しようとするときには、別の Git Gui を開きます。

Git Gui が開いたら、 "Clone Existing Repository" をクリックして、 GitHub に存在するリポジトリをローカルリポジトリにコピーする準備をします。


リポジトリをコピーする

このダイアログでは、リポジトリのコピー元とコピー先を指定します。 まず、クリップボードに入っているコピー元の情報を Source Location にペーストします。 次に、 Target Directory に新たにローカルリポジトリを作成するディレクトリを指定します。

この例では、 Git がデフォルトで使用するホームディレクトリの "git" というディレクトリに "Design109_3p0_3" というリポジトリを作成しています。 指定し終わったら、 Clone ボタンをクリックして、リポジトリのコピーを開始します。


コピー完了

コピーが終わったら、 Git Gui のウィンドウが開きます。 まだ、リポジトリをコピーしただけの状態なので、変更箇所は一切なく、何も表示されていません。

PSoC Creator で開いてみる

WS000870.png

ここで作成したローカルリポジトリは、ワークスペースそのものでしたので、 PSoC Creator から直接開くことが出来ます。 PSoC Creator からプロジェクトファイルを指定して開きます。


コピーされたプロジェクト

開いたプロジェクトは、そのまま Build して、プログラムもすることができます。 これで、 GitHub に登録されたプロジェクトをローカルなワークスペースで使用する事が出来るようになりました。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 3 で、 HID Bootloader を試す (5) [PSoC]このエントリーを含むはてなブックマーク#

CY8CKIT-030

これまで、単純に Bootloader を実装してきましたが、 MiniProg3 などを使って SWD でアクセスすると、フラッシュ ROM の内容が丸見えになってしまいます。 大事なファームウェアを保護するには、フラッシュ ROM にプロテクトをかけます。

フラッシュ ROM には、4レベルのプロテクションレベルがある

フラッシュ ROM には、 256 バイトの row 単位でプロテクトを設定できるようになっています。 それぞれの領域に設定可能なプロテクションレベルは、以下の4種類です。

プロテクションレベル概要
U - Unprotected 全くプロテクトがかかっていない状態です。 内蔵ファームウェアからも外部プログラマからも読み書きができます。
F - Factory Upgrade 内蔵ファームウェアからの読み書きと外部プログラマからの書き込みは出来ますが、外部プログラマからの読み出しが禁止されます。
R - Field Upgrade 内蔵ファームウェアからは読み書きができますが、外部プログラマからは読み書きともに禁止されます。
W - Full Protection 外部プログラマからのアクセスおよび内蔵ファームウェアからの消去書き込みが禁止されます。

BootloaderBootloadable は、互いに使用方法が異なっていますので、これらの領域ごとにプロテクションレベルを考えて設定します。

Bootloader が破壊されては困る

Bootloader は、ここで想定しているシステムの要(かなめ)です。 ホストから新たな Bootloadable アプリケーションを受け付けられるのも、 Bootloader が存在すればこそです。 そこで、 Bootloader 部分には、なにがあっても破壊されない、最も強力なプロテクトをかけます。

そこで、この領域には、 "W - Full Protection" のプロテクションレベルを適用します。 このプロテクションレベルが設定されると、 Bootloader の領域は、フラッシュ ROM の一括消去以外では、破壊されなくなります。 また、外部プログラマから読み出される事もないため、情報が安全に守られます。

Bootloadable は、 Bootloader によって書き換えられる

Bootloadable の領域は、内部ファームウェアである Bootloader によって書き換えることができます。 しかし、外部プログラマからのアクセスは、情報を守るために禁止しなくてはなりません。

そこで、この領域には、 "R - Field Upgrade" のプロテクションレベルを適用します。 このプロテクションレベルが設定されると、 Bootloadable の領域は、 Bootloader からの読み書きができます。 ただし、外部プログラマからのアクセスが禁止され、外部からの読み出しおよび破壊・改ざんなどから情報を守る事ができます。

フラッシュ ROM のプロテクト設定

MAP ファイル

それぞれの領域のプロテクションレベルを決定したので、 PSoC Creator で実際に設定します。 まずは、 Bootloader が、フラッシュ ROM のどこまでの範囲に配置されるかを調べます。

Bootloader の配置アドレスを調べるには、 "Workspace Explorer" の "Results" タブから Bootloader プロジェクトの ".map" ファイルを開きます。 ここでは、 "USB_Bootloader.map" を開きます。


フラッシュの最終アドレス

フラッシュ ROM は、 "CODE MEMORY" というセグメントに配置され、このファイルでは、後に "XDATA MEMORY" が続いています。 ここで作成した "USB_Bootloader" の場合、 000000H から 003759H までの範囲に配置されます。 フラッシュ ROM は、256バイトの row 単位で消去・書き込み・プロテクションレベルの設定ができます。 そのため、実際に Bootloader のために確保されるのは、 000000H から 0037FFH までの 56rows の範囲です。


Flash Security の設定

プロテクションレベルは、 "USB_Bootloader.cydwr" の "Flash Security" タブで設定する事ができます。 このタブで row ごとにプロテクションレベルを設定しますが、一つずつ設定するのは大変です。


プロテクションレベルの設定ボタン

そこで、タブの上方にある一括設定ボタンを使用します。


領域From rowto設定
Bootloader (0000-37FF)055W - Full Protection
Bootloadable (3800-FFFF)56255R - Field Upgrade

上記の設定のあと Set ボタンをクリックすると、プロテクションレベルを設定できます。

デバッガからのアクセスを禁止する

デバッガからメモリにアクセスする

フラッシュ ROM にプロテクトをかけると、確かにプログラマからのアクセスが禁止されますが、実は、もう一つ抜け道が残っています。 それは、デバッガです。 PSoC 3 がデバッガからの接続を受け付けている限り、デバッガはメモリに対してアクセスする事ができます。 "USB_Bootloader" の場合も、このままではファームウェアが丸見えです。


Enable Debug Protection

この状態に対処するためには、デバッガが接続できないように設定しなくてはなりません。 "USB_Bootloader.cydwr" の "System" タブで "Enable Device Protection" をチェックします。 これで、デバッガからの接続は拒否され、ファームウェアを保護する事が出来るようになります。

デバッガの接続を受け付けられないという事は、アプリケーションのデバッグも出来ないという事を意味します。 しかしながら、 Bootloadable のプロジェクトでは、デバッガを起動する事がそもそもできないので、デバッガを使ったデバッグを行う事が出来ません。 開発段階でデバッガを使う必要がある場合には、 "Enable Device Protection" の設定を元に戻すほか、 "Project"→"Build Settings..." で "Application Type" をBootloadable から Normal に変更し、回路図に配置した "Bootloadable" の Property で CY_REMOVE を "true" に設定し、ソースコードで "Bootloadable" を参照している部分を変更します。 これで、通常のアプリケーションとしてデバッグを行うことが出来ます。

Bootloadable の NVL 設定は、 Bootloader に合わせる

フラッシュ ROM にプロテクションを設定するため、 "USB_Bootloader.cydwr" の "System" タブの設定を変更してきました。 この "System" タブの設定は、ファームウェアから設定されるものではなく、 Non Volatile Latch (NVL) と呼ばれる特殊な不揮発性メモリに記録されリセット後に自動的に設定されます。 もちろん、 NVL は、一つの PSoC 3 には、一つしかありません。 そのため、 Bootloadable が実行される場合でも、 Bootloader から Bootloadable が呼び出され時にソフトウェアリセットが発生し、 Bootloader で設定した NVL の値が使用されます。 このような事情から、 Bootloadable の開発を行う場合でも、 "System" タブの設定を Bootloader で設定した値と同じにする必要があります。 設定が異なっている場合には、初期状態が違うためにポートが動作しないなどの症状が出る場合がありますので、気を付けてください。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC Creator でも GitHub がしたい (2) [PSoC]このエントリーを含むはてなブックマーク#

Git GUI

前回は、 Git Gui を使用して、 PSoC Creator のプロジェクトをローカルのリポジトリに Commit しました。 今回は、いよいよ PSoC Creator プロジェクトを GitHub サーバに登録します。

GitHub でリポジトリを作成する

新しいリポジトリを作るボタン

最初に GitHub サーバに新しいリポジトリを作成します。 GitHub の WEB ページで、 Create new repo ボタンをクリックします。


リポジトリの設定

すると、 WEB ページが変化し、リポジトリの設定を行う画面に遷移します。 ここでは、 Repository name にローカルリポジトリと同じ名前を入れます。 そして、このプロジェクトの内容を示す Description を記入します。 下の方に "Initialize this repository with a README" (README を付けてリポジトリを初期設定する)というチェックボックスが有りますが、これをチェックしてしまうと、ローカルリポジトリとの整合がややこしくなるので、チェックせずにおきます。 最後に Create repository ボタンをクリックすると、リポジトリが作成されます。


リポジトリ作成完了

リポジトリの作成直後は、中身が無い状態なので、このような画面に遷移します。 この画面で必要なのは、リポジトリの場所を示す Location です。 右にある "copy to clipboard" ボタンをクリックして、クリップボードに記憶します。

ローカルリポジトリを GitHub と関連付ける

リモートリポジトリを紐付ける

この段階まで、ワークスペースに作成したローカルなリポジトリと、 GitHub に作成したリモートリポジトリは、全く関係のない別々のリポジトリです。 次は、これらのリポジトリを関連付けていきます。 まず、 Git Gui のメニュー "Remote"→"Add..." を選択して、ダイアログを呼び出します。


リモートリポジトリの追加

このダイアログでは、リモートリポジトリの名前と場所を入れます。 "Location:" には、 GitHub の WEB ページでクリップボードに入れておいたリポジトリの場所を入れます。 "Name:" には、 "origin" という名前を付けました。 Git では、リモートリポジトリに名前を付けることによって、ローカルリポジトリと区別しています。 "Add" をクリックすると、 ”Fetch Immediately" にチェックを入れてあるので、 GitHub のリポジトリがローカルにコピーされます。


Fetch 完了

といっても、 GitHub にリポジトリを作成する時に、あえて何もファイルを作らなかったので、コピーされる物はありません。 処理が終わったら、 "Success" と表示されます。 Close ボタンをクリックして、ダイアログを閉じます。

ローカルリポジトリをリモートに登録する

Push する

準備が出来たので、いよいよローカルリポジトリを GitHub のリポジトリに登録します。 Git GuiPush ボタンをクリックして、ダイアログを呼び出します。


Push の設定

ダイアログでは、送り元であるローカルリポジトリと送り先であるリモートリポジトリを指定します。 今回は、ローカルリポジトリを示す "master" から、GitHub のリポジトリを示す "origin" に送り込みます。 Push ボタンをクリックしたら、リポジトリのコピーが始まります。


リポジトリの登録完了

ダイアログが変わって、 "Success" と表示されたら、リポジトリの登録は完了です。 Close をクリックして、ダイアログを閉じます。


GitHub に登録された

GitHub の WEB ページでリポジトリ名をクリックすると、ローカルリポジトリが登録されたのを確認する事ができます。 これで、 GitHub への登録は、完了です。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC Creator でも GitHub がしたい (1) [PSoC]このエントリーを含むはてなブックマーク#

Git GUI

PSoC Creator でも GitHub が使えるようになったら、便利に違いない。 今回は、どうやったら PSoC Creator プロジェクトと GitHub が共存できるかについて考えました。

PSoC Creator には、リビジョン管理の機能が無い

PSoC Creator は、 PSoC 3/4/5LP のファームウェア開発を行うための統合開発環境 (Integrated Development Environmend; IDE) です。 ファームウェア開発をする時には、当然、プロジェクトのリビジョン管理を行うべきですが、 PSoC Creator そのものには、リビジョン管理をするための仕掛けは入っていません。 これは、プロジェクトを管理する上では、マズイ状態です。

では、どの様にプロジェクト管理を行うかというと、 "Revision Control for PSoC® Creator™ Projects - KBA86358" に記述のある通り、ユーザが用意した "Revision Control System" で、必要なファイルだけ管理する方式が推奨されています。 ところが、 PSoC Creator と併用して、リビジョン管理を行うための具体的な方法について書かれた文書は見つかりません。 そこで、この文書では、 GitHub でプロジェクトを管理する方法を具体的に手順を追って解説します。

使用するソフトウェアは、 http://git-scm.com/ で提供されている Git Gui クライアントです。 なお、この記事では、 GitHub および Git の細かい使い方については、言及していませんので、ご了承ください。

新しいリポジトリを GitHub に作るには

GitHub でリポジトリを作成した

GitHub の WEB サイトでリポジトリを作成すると、この図のように「次に何をすべきか」という指示が表示されます。 この記事では、このうち、 "Create a new repository on the command line" の手順に従った方法を git-gui を使って操作していきます。

いつも通りにプロジェクトを作成する

プロジェクト作成ダイアログ

最初にいつも通りにプロジェクトを作成します。 ワークスペースを作成する場所は、どこでも構いません。 ここでは、マイドキュメントの "PSoC Creator" というディレクトリにワークスペースを新たに作成しています。 将来、このワークスペースがリポジトリとして機能します。


適当な回路図

プロジェクトが作成できたら、適当な回路図と適当なソースコードを書いて Build しておきます。 これで、リポジトリに登録すべきファイルが、準備できました。

ワークスペースをリポジトリとして仕立てる

新しいリポジトリを作成

次は、作成したワークスペースをリポジトリに仕立て上げます。 このために使用するアプリケーションが Git Gui です。 "Create New Repository" をクリックして、リポジトリの作成を開始します。


リポジトリの場所を指定する

リポジトリを作成する場所をたずねるダイアログが開くので、さきほど作成したワークスペースの場所を指定します。 ここで、 "Create" をクリックすると、ワークスペース内にリポジトリとして機能するのに必要なファイルがいくつか追加されて、ワークスペースがリポジトリとして機能するようになります。

.gitignore ファイルを用意する

Git Gui の初期画面

Git Gui の画面が開いたら、 Unstaged Changes という所に多くのファイルが並んでいます。 これらのファイルが、リポジトリに Commit すべきファイルの候補です。 ところが、必要のないものが大量に並んでいますので、必要でないものはリストされないように設定ファイルを追加します。


.gitignore ファイル

ここで作成するのは、 ".gitignore" という名前のテキストファイルです。 これをリポジトリの直下に置きます。 内容は、以下の通りです。

#
# .gitignore file for PSoC Creator 3.0
#

# Log files
*.log
*.rpt

# Temp directry
*.cydsn/codegentemp/*

# Results
*.cydsn/DP8051/*

Rescan ボタン

"Rescan" ボタンをクリックすると、ファイルの数が、ずいぶん少なくなりました。 それでも、まだ、沢山ありますね。 "Generated_Source" ディレクトリというのは、 PSoC Creator が生成したファイルなので、本来は Commit しなくても良いはずです。 しかし、割り込みハンドラなどでは、ユーザがこれらのファイルを直接変更する場合が有ります。 このような場合、 Commit することによってユーザの変更箇所を保存しなくてはなりません。 このような事情から、 "Generated_Source" ディレクトリは、リストされるようにしています。

必要なファイルだけ Commit する

必要なファイルを Commit

PSoC Creator は、操作中に多くのファイルを生成しますが、プロジェクトに必須なファイルと言うのは、実はほんの数個に過ぎません。 このことは、 "Revision Control for PSoC® Creator™ Projects - KBA86358" にも記述されています。 次の作業は、必須ファイルを選択して、 Commit することです。 ここでは、以下の5個のファイルを選択して、メニュー "Commit"→"Stage To Commit" を選択して、 "Staged Changes" にこれらのファイルを移動します。


  • .gitignore :

    先ほど作成した、 Commit すべきでないファイルを示すファイルです。

  • <Project Name>.cydsn/<Project Name>.cydwr :

    "Design Wide Resource" と呼ばれるファイルで、 PSoC Creator でプロジェクトのさまざまな属性を保存するために使用されます。

  • <Project Name>.cydsn/<Project Name>.cyprj :

    使用しているコンポーネントなど、プロジェクトに関連した情報を格納したファイルです。

  • <Project Name>.cydsn/TopDesign/TopDesign.cysch :

    プロジェクトで使用している、トップレベルの回路図を格納したファイルです。

  • <Project Name>.cydsn/main.c :

    C のソースコードが格納された、トップレベルのファイルです。

この他に、ユーザが作成したソースファイルやヘッダファイルなどが有れば、いっしょに "Staged Changes" に入れておきます。 そして、 Commit ボタンをクリックすると、リポジトリに登録されます。


履歴を確認する

メニュー "Repository"→"Visualize All Branch History" を選択すると、リポジトリに記録された履歴が表示されます。 確かに、5個のファイルが、登録されているのが、わかります。

ひとまず、ローカルなリポジトリができたので、今日はここまで。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 3 で、 HID Bootloader を試す (4) [PSoC]このエントリーを含むはてなブックマーク#

CY8CKIT-030

ここまで作成してきた Bootloader は、どんな場合でも、とりあえずブート用の通信が有効になります。 ブートの有無を制御できないでしょうか。

Bootloader のシーケンス

Bootloader のシーケンス

これまでの Bootloader のシーケンスでは、まず、ブート通信用のポートを開き、 Bootloader Host からの通信を待ちます。 Bootloadable が書き込まれていた場合には、既定の待ち時間が経過するまで、通信を待ち続けていました。 待ち時間は、デフォルトでは2秒でしたが、この実験では10秒に延長しています。

Bootloadable を書込んだ時に Bootloader Host の動きを観測すると、待ち時間を見ることができます。 PSoC 3 にリセットをかけた直後、 Bootloader Host の画面上のポートリストには、 "USB Human Interface Device (04B4_B71D)" が並びます。 ところが、10秒間、そのまま放っておくと、 "USB Human Interface Device (04B4_B71D)" の表示が消えて、 LED が点滅を開始します。

この動きを見て分かるように、ユーザが真に必要としている Bootloable の処理が始まるまで、10秒の待ち時間が生じてしまいます。 かといって、待ち時間を短くしてしまうと、 Bootloader Host からコマンドを送信する前に待ち時間を使い切って、書き込み動作を実行する事ができません。 USB の場合には、特に接続に手間取るので、2秒では短いと感じています。

今回の目標は、確実に Bootloader を呼び出し、かつ、 Bootloadable の起動までの時間を短くすることです。

タクトスイッチでブートに入れる

タスクスイッチを追加した Bootloader

PSoC 3 のどこかにタクトスイッチが付いていれば、リセット時にタクトスイッチを操作することで Bootloadable ではなく Bootloader を呼び出す事ができます。 まずは、回路図上にタクトスイッチを接続するための Digital Input Pin を追加します。 ここでは、 P6[1] に接続されている "SW2" のタクトスイッチを使用します。 入力端子の Pull-Up 設定と P6[1] への端子割り当てをお忘れなく。


待ち時間なしの Bootloader

次に Bootloader コンポーネントで接続待ちを行わないように設定します。 具体的には、 Wait for command のチェックをはずします。


タクトスイッチ対応 main.c

最後に、 "main.c" ファイルを変更して、 "SW2" が、リセット後 100ミリ秒の間、継続して押し続けられていたら、 Bootloader を呼び出すように変更します。 一方、 "SW2" スイッチが押されていない場合、ほぼ遅延なく、ユーザアプリケーションである Bootloadable プログラムが呼び出されます。 いずれの場合でも、 Bootloadable が書き込まれていない場合には、 Bootloadable が呼び出されます。

この方法では、タクトスイッチの追加が必要になってきます。 もちろん、アプリケーションで使用するスイッチを流用させることも出来ますが、その場合、機器のエンドユーザも Bootloader を呼び出すための操作が可能になってしまいます。 そのため、 Bootloader の呼び出しに複雑なシーケンスを組まなければならなくなり、ファームウェア開発が重くなる可能性もあります。

Bootloadable から Bootloader を呼び出せるようにする

Bootloadable から Bootloader を呼び出す

上で実現した方法は、外部にタクトスイッチの実装が必要でした。 ハードウェアの追加なしに Bootloadable から Bootloader を呼び出すのが、次に述べる方法です。

まず、 Bootloadable プロジェクトの回路図で、 Bootloader を呼び出すためのタクトスイッチを配置します。 この例では、タクトスイッチと言うハードウェアを人間が操作する事をきっかけとして、 Boootloader を呼び出す動作を実現します。 実際のアプリケーションでは、ハードウェアを追加するまでも無く、 Bootloadable アプリケーションに Bootloader を呼び出す仕掛けを追加するだけで実現できます。


Bootloader を呼び出す Bootloadable ファームウェア

ファームウェアでは、メインループでタクトスイッチを監視して、2秒の間、継続して押されていたら、 API "Bootloadable_Load()" を呼び出して、 PSoC 3 のソフトウェアリセットの後、 Bootloader の通信待ちに入ります。

この方式を使うと、 Bootloader での通信待ちを行わないようにしていても、 Bootloadable を通じて、確実に Bootloader を呼び出す事ができます。 ただし、 Bootloadable からの Bootloader の呼び出しに失敗してしまうと、二度と Bootloadable を書き換えられなくなってしまいます。 そのために、 Bootloadable の開発には、細心の注意が必要になってきます。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 3 で、 HID Bootloader を試す (3) [PSoC]このエントリーを含むはてなブックマーク#

CY8CKIT-030

Bootloader を書込んで、 Bootloadable の書き込みまでできました。 今回は、 Bootloader サイズについて考えます。

Bootloader のサイズ

Bootloader のメモリ配置

Bootloader は、最初にフラッシュ ROM に書き込まれて、アプリケーションが書き込まれた後でも常駐し続けます。 常駐する時に、どのくらいのメモリ容量を占めているかを確認するために、 MAP ファイルを確認しました。

このファイルは、プロジェクトを Build する際にリンカが作成するファイルで、メモリのどの範囲を使用しているかがわかります。 この Bootloader プロジェクトの場合には、 CODECONST の二つのセグメントがフラッシュに配置され、合計 0x4814 (18,452) Byte の容量を消費しています。 なんと、 30% 近くの容量が、アプリケーションとは関係ない、 Bootloader で消費されているのです。 もったいない。

Bootloader が、これだけの容量を消費する理由は、他のプロトコルに比べると複雑な USB インターフェイスを使用したからです。 他のプロトコルを使えば、もっと小さくなります。 ただ、 USB を使いたいということであれば、いたしかたありません。 何とか、サイズを抑える方法を考えましょう。

コンパイルオプションで最適化する

最適化後のメモリマップ

コンパイルの結果を簡単に小さくするには、コンパイラの最適化オプションを指定します。 これまでの実験では、 Build Configuration を "Debug" にしたままで行ってきましたが、これを "Release" に変更してみます。 最適化レベルは、ライセンスを必要としない範囲で最高の "5" です。

すると、 0x37B5 (14,261) Bytes と 22% ほどの消費に抑えることができました。

外付け水晶発振子を使う

外付け水晶発振子を使用する

最初の Bootloader プロジェクトは、ホストから到着する USB 通信信号で PSoC の内蔵発振器の周波数を微調整していました。 これを外付けの水晶発振器を使う方式にしたらどうなるか、試してみました。 クロック設定画面で XTAL をイネーブルし、 IMO を XTAL から供給するように変更しています。


水晶発振子を使った場合のメモリマップ

変更後のコンパイル結果が、これです。 0x3816 (14,358) Bytes と、少し増えてしまいました。 逆効果だったようです。

Bootloader の機能を削減する

Bootloader の機能を削減

これまで、 Bootloader コンポーネントの設定は、通信コンポーネントの指定以外はデフォルトの値を使っていました。 設定ダイアログのうち、右側の Optional commands の部分は、追加機能として提供されているもので、必要が無ければ Bootloader に実装しなくてもかまいません。 そこで、 Bootloader のサイズを削減する事を目的として、すべてのチェックを外してみました。


機能を削減した場合のメモリマップ

その結果、サイズは 0x36CA (14,026) Bytes まで削減できました。 この設定で、 Bootloader Host を使おうとしましたが、使用できませんでした。 これは、このツールに必要なコマンドが実装されていないためです。


コマンドを三つだけ実装

Bootloader のデータシートによると、このツールを使うためには、最低限 Get flash size, Send data, Verify row の三つのコマンドが必要です。 そこで、これら三つのコマンドだけ実装して、サイズを確認してみました。


Bootloader Host に対応

その結果、 0x375D (14,173) Bytes となりました。 ちゃんと、 Bootloader Host で、書き込みも出来ました。 ただし、 Erase All だけは、コマンドが実装されていないので実行できませんでした。 このあたりが限界でしょうか。

Bootloadable は、どうなっているのか

Bootloadable のメモリマップ

ところで、 Bootloadable のメモリマップは、どの様になっているのでしょうか。 "Bootloadable001" プロジェクトのメモリマップを確認したところ、 CODECONST の両セグメントは、 0x3800 からの領域に割り当てられていました。 この PSoC 3 の場合、 256Bytes の row 単位でフラッシュ ROM の消去を行う事ができます。 そのため、 256Bytes の区切りである 0x3800 からの 0xC800 (51,200) Bytes のフラッシュ ROM が Bootloadable 用の領域として使用されます。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 3 で、 HID Bootloader を試す (2) [PSoC]このエントリーを含むはてなブックマーク#

2013-12-31 14.42.30.jpg

前回は、 Bootloader プロジェクトを作成して、 PSoC 3 に書き込みました。 今回は、 Bootloadable プロジェクトを作成し、 Bootloader 機能を使ってプログラムしてみます。

Bootloadable プロジェクトを作成する

Bootloadable プロジェクトの作成

Bootloadable プロジェクトも Bootloader プロジェクトと同様に、 PSoC Creator から作成する事ができます。 前回作成したワークスペース "Design102" に、新たにプロジェクトを追加作成します。 やはり、通常のプロジェクトを作成するのとは、少し異なっています。


  1. プロジェクト名を明確に付ける

    Bootloader プロジェクトを作成するときと同様に、プロジェクト名を明示的に指定しておく必要があります。 ここでは、 "Bootloadable001" というプロジェクト名を付けます。

  2. Application Type を指定する

    Application Type に "Bootloadable" を指定します。 これにより、 Bootloadable 用プロジェクトのひな形が使用されて、初期プロジェクトが作成されます。

Bootloadable プロジェクトを Bootloader プロジェクトとは別のワークスペースに置いても、 Bootloadable の開発は出来ます。 しかしながら、後で説明する理由により、同じワークスペースに置いた方が扱いやすくなります。

コンポーネントを配置する

コンポーネントの配置

回路図が開いたら、通常のプロジェクトと同様に、必要なコンポーネントを並べていきます。 今回作成する Bootloadable プロジェクトでは、クロックを LED 端子に直結して点滅を行わせます。 必要なのは、 Bootloadable プロジェクトとしての振る舞いをさせるための Bootloadable コンポーネントと LED を点滅させるために必要な Clock コンポーネントと Digital Output Pin コンポーネントの三つです。 それぞれのインスタンス名を "Bootloadable" "Clock_2Hz" および "LED4_P6_3" に変更します。

Bootloadable コンポーネントの設定

Bootable コンポーネントの設定

Bootloadable プロジェクトを作成するには、このプロジェクトを取り扱う Bootloader プロジェクトのバイナリーファイルが必要です。 Bootloadable コンポーネント設定画面の Dependencies タブで、 Bootloader プロジェクトにある HEX ファイルと ELF ファイルを指定します。 具体的には USB_Bootloader.cydsn\DP8051\DP8051_Keil_951\Debug\ にファイルが有るはずです。

ここで指定したファイルは、コンポーネントの中では、相対パスとして格納されます。 そのため、同じワークスペースに双方のプロジェクトを入れてしまった方が、アーカイブなどの時に便利です。

Bootloadable では、これらのバイナリーファイルが必要になります。 したがって、既存の Bootloader に対して Bootloadable アプリケーションを作成する場合には、最低限、これらのバイナリーファイルを入手する必要があります。

クロックと出力の設定

残りのコンポーネントは、単純に LED を点滅させるために使用されます。 クロックの周波数を 2Hz に設定し、出力端子を P6[3] に関連付けるだけで、 LED4 が 2Hz で点滅するアプリケーションができあがります。 もちろん、 "main.c" に記述をする必要もありません。 設定できたら、プロジェクトを Build します。

Bootloadable をプログラムする

Bootloader Host の起動

Build できたら、 PSoC に書き込みます。 ただし、今回は、プログラマを使わずに USB 通信線だけを使います。

CY8CKIT-030 を通信用 USB コネクタで接続します。 そして、 PSoC Creator のメニュー "Tools"→"Bootloader Host..." で、 Bootloader Host アプリケーションツールを呼び出します。 まず、 "File:" フィールドに Application File を指定します。 指定すべきファイルは、以下の場所にあります。

<Workspace Path>\Design102\Bootloadable001.cydsn\DP8051\DP8051_Keil_951\Debug\Bootloadable001.cyacd

このファイルは、 Bootloadable プロジェクトを Build した際に作成されるファイルです。


Bootloadable のプログラム

次にPorts: リストから "USB Human Interface Device (04B4_B71D)" を選択したら、 Program をクリックして、プログラムを開始します。 プログラムが書き込まれたら、自動的にリセットがかかり、 Botloadable が走り始め、 LED4 が点滅を始めます。 これで、最初の Bootloadable は、完成です。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 3 で、 HID Bootloader を試す (1) [PSoC]このエントリーを含むはてなブックマーク#

CY8CKIT-030

PSoC 3 を題材に Bootloader の使い方を試します。 使用するハードウェアは、 CY8CKIT-030 です。

Bootloader って何だろう

通常、 PSoC のプログラミングを行う場合には、プログラマと呼ばれる専用のハードウェアが必要です。 PSoC 3 の開発を行う場合には、 MiniProg 3 と呼ばれるプログラマが使われ、プログラマを接続するための専用コネクタが基板上に装備されます。 プログラマを使う方法は、開発時には良いのですが、いったん、 PSoC が装置に組み込まれてしまうと、このコネクタにたどり着くことができません。 また、開発時にしか使わないコネクタなど、省略されてしまうこともしばしばです。

そこで、専用のコネクタを装備しなくても、どこかの既存の通信線を通じて PSoC のプログラムを行う方法があると便利です。 Bootloader は、専用のプログラマやコネクタを使わなくてもプログラムを行う事が出来る手法の一つです。


Bootloader システム

PSoC 3 で提供する Bootloader は、この図のように三つの部分から構成されます。


Application (Bootloadable)
ユーザが最終的に必要とするプログラムです。 このプログラムは、 Bootloader によってプログラムする事ができます。
Bootloader
Bootloader は、フラッシュ ROM に常駐するプログラムです。 このプログラム自体の役割は、 Bootloadable をフラッシュ ROM の残りの部分に書き込むことです。 また、システムのリセット直後に、 Bootloader として振る舞うか、 Bootloadable として振る舞うかの判断も行います。
Application File
Bootloader は、ホストと通信線を介して Bootloadable を受信し、フラッシュ ROM に書き込みます。 この時に使用されるフォーマットが、 Application File です。 Application File は、 PSoC Creator で簡単に作成する事ができます。

このモデルでは、 Bootloader を最初に作成して、そのあと、 Bootloadable を作成し、 Application File として配布します。 まずは、 Bootloader から作成します。

Bootloader プロジェクトを作成する

Bootloader プロジェクトの作成

Bootloader プロジェクトを作成するには、通常通りに PSoC Creator で新規プロジェクトを作成します。 その際に、通常のプロジェクトとは、異なる設定を行います。


  1. プロジェクト名とワークスペース名を明確に分ける

    通常、 Name: フィールドにプロジェクト名を記述すると、ワークスペース名には、そのコピーが入ります。 Bootloader を使用する場合には、通常、ひとつの Bootloader プロジェクトと複数の Bootloadable プロジェクトを共通のワークスペースに混在させて使用します。 ここでは、 "Design102" というワークスペースに "USB_Bootloader" というプロジェクトを作成しています。

  2. Application Type を指定する

    Application Type に "Bootloader" 指定します。 これにより、 Bootloader 用プロジェクトのひな形が使用されて、初期プロジェクトが作成されます。

指定したら、 OK をクリックして、プロジェクトを作成させます。

コンポーネントを準備する

コンポーネントを並べる

回路図が開いたら、通常のプロジェクトと同様に、必要なコンポーネントを並べていきます。 今回作成する Bootloader プロジェクトでは、 USBHID インターフェイスを用いて、プログラムを行うプログラムを作成します。 必要なのは、 Bootloader コンポーネントと USBFS コンポーネントです。 それぞれのインスタンス名を "Bootloader" および "USBFS" に変更します。

Bootloader コンポーネントの設定

Bootloader の設定

Bootloader コンポーネントでは、 Communication component フィールドに "USBFS" を指定します。 このフィールドで指定した通信機能を使ってホストとの通信を行います。

この設定画面では、もう一つ、変更した所があります。 それは、 Wait for command time (ms) フィールドで、 10000(10秒)に変更しています。 リセット直後、 Bootloader は、このフィールドで指定された待ち時間だけ、ホストからの接続要求を待ちます。 そして、時間切れになったら、 Bootloadable を呼び出して、通常のアプリケーションとしての動作に移行します。 ここで待ち時間を初期値の2000(2秒)から増やしたのは、後の実験で、 Bootloadable への移行時間を見えやすくするためです。

USBFS の設定

Bootloader設定の呼び出し

USBFS コンポーネントを設定するためには、一般にかなり深い知識が必要になってきます。 ところが、 Bootloader を作成するだけであれば、ユーザが苦労をする必要はありません。 それは、すでに、 Bootloader のための設定ファイルが用意されているからです。 設定画面で Import ボタンをクリックすると、設定ファイル選択ダイアログが開きます。


USB Bootloader 設定ファイルの選択

ここで選択するのは、 "bootloader.root.xml" というファイルで、 PSoC Creator のインストールディレクトリ配下の以下の場所にあります。

<PSoC Creator InstallDir>\psoc\content\cycomponentlibrary\CyComponentLibrary.cylib\USBFS_v2_60\Custom\template\

USBFS コンポーネントのバージョンは、お使いのコンポーネントのバージョンに合わせてください。


不要な設定の削除

これで、 Bootloader のための設定が追加されました。 まだ、 USBFS の初期設定が残されていますので、ふたつある "Device Descriptor" の上の方を選択し、 "Delete" ボタンをクリックして、削除しておきます。 これで、 USBFS コンポーネントの準備が出来ました。

クロックの設定

クロックの設定一覧

この Bootloader は、 USBFS を使用するために、厳密にクロックの設定を行う必要があります。 もちろん、 PSoC Creator であれば、簡単に設定できます。

Workspace Explorer から "USB_Bootloader.cydwr" を開いて、 "Clocks" タブを選択すると、このような画面が開きます。 二つばかり「赤いビックリマーク」が付いていますので、修正が必要です。 "Edit Clock..." をクリックして、クロック設定ダイアログを開きます。


クロック設定ダイアログ

クロック設定ダイアログでは、以下の4点を修正します。


  1. IMO

    IMO の設定を "24.000MHz ±4%" に変更します。 USB の規格で要求とされているのは、 4% どころではない、もっと高精度なクロックです。 ところが、 PSoC には、接続先の USB ホストに合わせて内部クロックを自動的に調整する機能が有ります。 この機能を生かすための設定が、 "24.000MHz ±4%" なのです。

  2. ILO

    ILO の設定を "100kHz" に変更します。 先に説明した IMO を USB ホストに合わせて調整する機能は、 ILO のタイミングで実行されます。 適切な範囲に調整するために、 ILO を "100kHz" に設定します。

  3. USB

    USB のチェックボックスをクリックして、 USB ブロックにクロックを供給します。 与えるクロックは、 IMO のクロックをクロックダブラに通した "IMOx2 - 48.000MHz" です。

  4. PLL

    PLL の出力周波数を "66MHz" に設定します。 USBFS コンポーネントを使用する場合、 USB に与えるクロックよりも速いクロックを "MASTER_CLK" に与える必要があります。 ここでは、 "48MHz" のクロックで十分という事になりますが、許容される最高周波数である "66MHz" を与えています。

これで、クロックの設定は、完了です。

コードを書く

main.c

コンポーネントが整ったら、次は、 "main.c" にソースコードを書きます。 ところが、ここでは、何もする必要がありません。 既に必要な記述 "CyBtLdr_Start();" が入っています。

ひとつだけ修正しなくてはならない個所が有りました。 "main()" の戻り値の型を "void" から "int" に変更しておいてください。 どうも、テンプレートの段階から間違っていたようです。

Bootloader の書き込み

Bootloader の書き込み

Bootloader は、通常のプロジェクトと同様に、プログラマを使って書き込みます。 CY8CKIT-030 の場合には、オンボードのプログラマを使って書き込むことができます。

Bootloader との通信

Bootloader と接続

書き込みが終わったら、接続してみましょう。 Bootloader と通信を行うためには、 USB コネクタを通信用のコネクタに接続しなおして、 PSoC Creator のメニュー "Tools"→"Bootloader Host..." で Bootloader Host というアプリケーションツールを呼び出します。

すると、上のスクリーンショットのように、 "USB Human Interface Device (04B4_B71D)" がポートリストに並びます。 この VID/PID は、 Bootloader の設定ファイルに記述されていたものです。 これで、 Bootloader プログラムと通信できるようになりました。

参考文献

関連文献

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

開発編 ARM PSoCで作るMyスペシャル・マイコン (トライアルシリーズ)

  • 作者: 圓山 宗智
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/12/24
  • メディア: 単行本

PSoC 3 で、 DMA 対応倍増器を作った [PSoC]このエントリーを含むはてなブックマーク#

DMA 対応倍増器

前回まで、 CPU から FIFO に対して読み書きをするコンポーネントを作成してきました。 今回は、 DMA を接続して、自動的に出力を受け取らせる仕組みを取り入れてみました。

題材とするコンポーネントは、10倍増器です。 コンポーネント自身には、 ST_PUT 状態の時に演算の終了を示すためにアサートされる "drq" (DMA Request) 出力を追加してあります。 この信号の立ち上がりを DMA が検出したら、 FIFO から計算結果を取り出して、 RAM に転送します。

DMA Wizard への対応

DMA Wizard (1)

PSoC Creator で、 DMA を使うためには、かなり長文のソースコードを書かなくてはなりません。 この DMA の設定を簡単に生成するためのツールが、 DMA Wizard です。

DMA Wizard は、コンポーネントの情報、例えばレジスタのアドレスをリストに表示します。 ユーザは、提示された選択肢の中から転送元と転送先を選んで、簡単に DMA 設定コードを得る事ができます。 ただし、そのためには、コンポーネントが DMA Wizard に対応している必要があります。


DMA Wizard 対応コンポーネント

DMA に対応させるために必要なのが、拡張子 "cydmacap" の付いた "DMA Capability File" です。 このファイルは、 XML 形式で記述されており、 DMA の転送元および転送先のレジスタアドレスや、そのビット長などの情報が記述されています。

今回は、拡張子 "h" の付いた API ヘッダファイルも作成しています。 DMA Wizard では、レジスタアドレスを指定しますが、何か名前を付けておいた方が理解しやすくなります。 ここでは、ヘッダファイルにマクロ定義 #define を記述することによって、名前を付けています。

DMA Capability File の記述

DMA Capability File の作成

コンポーネントのコンテキストメニューから "Add Component Item" でダイアログを呼び出して、 "DMA Capability" を選択すると、 DMA Capability File のテンプレートが作成されます。 このテンプレートのコメント以外の部分を以下のように変更しています。

<DMACapability>

  <Category name="Primary" 
            enabled="`=$EnableDMA`" 
            bytes_in_burst="2"
            bytes_in_burst_is_strict="true" 
            spoke_width="2" 
            inc_addr="false" 
            each_burst_req_request="true">
    <Location name="`$INSTANCE_NAME`_INPUT_16BIT_PTR" enabled="true" direction="destination"/>
    <Location name="`$INSTANCE_NAME`_OUTPUT_16BIT_PTR" enabled="true" direction="source"/>
  </Category>
  
</DMACapability>

DMA Capability File の書き方については、現在のところ、マニュアルもドキュメントもありません。 唯一、手掛かりとなるのは、ファイルとして作成されるテンプレートに記述されたコメントだけです。 以下、記入すべき項目について、列挙します。


項目名属性名内容
Category Category には、レジスタへのアクセス方法に関する情報が記述されます。 レジスタのビット幅が異なるなどの複数の Category を併記することも出来ます。
name name には、複数の Category を区別するための名前が記述されます。 DMA Wizard では、複数の Category が存在する時に限り Category を選択するリストが表示されます。 今回のように Category が一つだけの場合には、設定しても使用されません。
enabled enabled は、 true または false の値をとります。 この値が false の時には、 DMA Wizard から認識されないので、リストに表示されなくなります。 主にコンポーネントの設定ダイアログから指定する目的で使われます。 今回の例でも、設定プロパティ EnableDMA の値を使っています。
bytes_in_burst この値は、 DMA の一度の転送で送受信されるデータのバイト数を指定します。 今回の例では、 FIFO から2バイトずつ演算結果が出力されますので、 "2" を指定します。
bytes_in_burst_is_strict この値は、 true または false の値をとります。 先の属性 "bytes_in_burst" で指定された値は、 DMA Wizard で変更することも出来ます。 これを変更不可能にするのが、この属性です。 この値が true の時には、属性 "bytes_in_burst" で決められた値は、 DMA Wizard では変更できません。 今回の例では、必ず2バイトの演算結果を出力しますので、 "true" を指定します。
spoke_width この値には、レジスタの接続されるスポークのビット幅をバイト単位で指定します。 スポークのビット幅は、ハードウェアにより、接続されるブロックごとに決定されます。 今回の場合は、 UDB の FIFO に読み書きを行うため、16ビットのスポークに接続されます。 そのため、設定する値は、 "2" となります。
inc_addr この値は、 true または false の値をとり、一回の転送の後、アドレスを増加させるかどうかを指定します。 今回の場合は、特定の FIFO レジスタに対して読み書きを行うため、アドレスは固定されます。 そのため、設定する値は、 "false" となります。
each_busrt_req_request この値は、 true または false の値をとり、 DMA 要求が到着した時に複数回の転送を行うかどうかを指定します。 今回の場合は、2バイトの演算結果ごとに DMA 要求が到着する仕組みになっていますので、 "true" を指定します。
Location Location には、転送元または転送先に関する情報が記述されます。 レジスタアドレスごとに一つの Location が記述されます。
name name には、レジスタのアドレスを示す名前が記述されます。 DMA Wizard では、この名前がリストに表示されます。 この例では、 API ヘッダファイルに定義したレジスタアドレスを指定しています。
enabled enabled は、 true または false の値をとります。 この値が false の時には、 DMA Wizard から認識されないので、リストに表示されなくなります。 この属性も、コンポーネントの設定ダイアログなどから指定する目的で使われます。 今回の例では、 "true" を指定して、常に DMA Wizard から認識されるようになっています。
direction この値は、レジスタのアクセス方向を示します。 "source" が指定された時、このアドレスは転送元として扱われます。 また、 "destination" が指定されたら、転送先として扱われます。 さらに、 "both" が指定されたら、転送元または転送先の両方に使えるものとして扱われます。 今回の例では、レジスタに応じて、 "source" または "destination" を指定しています。

API ヘッダファイルの記述

API ヘッダファイルは、以下のように記述しています。

#define `$INSTANCE_NAME`_INPUT_16BIT_PTR ((reg16 *)`$INSTANCE_NAME`_dp_u0__16BIT_F0_REG)
#define `$INSTANCE_NAME`_INPUT_PTR ((reg8 *)`$INSTANCE_NAME`_dp_u0__F0_REG)

#define `$INSTANCE_NAME`_OUTPUT_16BIT_PTR ((reg16 *)`$INSTANCE_NAME`_dp_u0__16BIT_F1_REG)
#define `$INSTANCE_NAME`_OUTPUT_PTR ((reg8 *)`$INSTANCE_NAME`_dp_u0__F1_REG)

今回作成したプロジェクトでは、入り口の FIFO のアドレスと出口の FIFO のアドレスを、 DMA からのアクセスおよび CPU からのアクセスについて別々に登録しています。 これは、 DMA からアクセスする場合には16ビットバスを、 CPU からアクセスする場合には8ビットバスを使用するという制約によります。 "16BIT" が付いた名前を DMA で使い、もう一つの方を CPU で使います。

DMA Wizard の設定

DMA Wizard (2)

DMA Wizard 対応のコンポーネントが出来たら、 DMA Wizard で DMA 設定コードを生成させます。 DMA Wizard を起動したら、まず、プロジェクトと転送に使用する DMA コンポーネントを指定します。


DMA Wizard (3)

次は、転送元と転送先のコンポーネントを指定します。 今回の場合には、カスタムコンポーネントの Times10 から出力された演算結果を SRAM に転送するのが目的です。 そこで、 "Source" には Times10 を、 "Destination" には SRAM を指定します。

一転送あたりのバイト数などの設定は、 DMA Capability File からコピーされてきます。 転送を繰り返して行いたいので、 Loop にチェックを入れておきます。


DMA Wizard (4)

次は、 Transaction Descriptor の詳細を設定します。 今回のプロジェクトは、 PSoC 3 で16ビットレジスタを操作するため、 FIFO レジスタのエンディアンとコンパイラが想定するエンディアンが異なります。 そのため、 Endian の設定を "2" に変更して、16ビットアクセスができるようにしています。

"result" は、 main.c ソースコードで使用する予定の SRAM 上の配列の名前です。 この例のように、配列名を使って表現すると、わざわざ、配列の長さを数える手間が省けます。


DMA Wizard (5)

設定項目を入力したら、コードが生成されます。 これを "main.c" ソースコードの適切な場所にコピーすると、 DMA の初期設定を行う事ができます。

テストプログラム

テストプログラムは、16ビットの範囲内で計算が実現できるすべての組み合わせをテストして、その結果を LCD モジュールに表示します。 コンセプトは、前回のプログラムと同じなのですが、今回は、 FIFO からデータを取り出す操作を CPU ではなく DMA が行っています。 以下では、 "main.c" のコードを抜粋しながら解説していきます。

uint16      result[64];

計算結果は、配列 "result" に DMA で転送されます。 DMA Wizard の設定で "Loop" にチェックを入れたので、配列の最後まで達したら、元のアドレスに戻って転送を行います。

/* Variable declarations for DMA */
uint8 DMA_Chan;
uint8 DMA_TD[1];

/* DMA Configuration for DMA */
#define DMA_BYTES_PER_BURST 2
#define DMA_REQUEST_PER_BURST 1
#define DMA_SRC_BASE (CYDEV_PERIPH_BASE)
#define DMA_DST_BASE (CYDEV_SRAM_BASE)

DMA Wizard で生成されるコードは、変数宣言、マクロ宣言、初期設定の三つの部分に分かれています。 この例では、まず変数宣言とマクロ宣言を行っています。 このため、変数は、グローバルな変数として宣言されることになります。

/* Initialize the DMA channel */
void DMA_Init(void) {
    DMA_Chan = DMA_DmaInitialize(DMA_BYTES_PER_BURST, DMA_REQUEST_PER_BURST, 
        HI16(DMA_SRC_BASE), HI16(DMA_DST_BASE));
    DMA_TD[0] = CyDmaTdAllocate();
    CyDmaTdSetConfiguration(DMA_TD[0], sizeof result, DMA_TD[0], TD_SWAP_EN | TD_INC_DST_ADR);
    CyDmaTdSetAddress(DMA_TD[0], LO16((uint32)Times10_OUTPUT_16BIT_PTR), LO16((uint32)result));
    CyDmaChSetInitialTd(DMA_Chan, DMA_TD[0]);
    CyDmaChEnable(DMA_Chan, 1);
}

残りの初期設定部分は、関数 DMA_Init として定義しました。 詳細については、ここでは解説しません。

void main()
{
    uint8           i;
    uint8           j;
    uint16          a;
    uint16          a10;
    volatile uint16 w = 0;
    CYBIT           good;
        
    LCD_Start();
    DMA_Init();

初期設定部分で、 LCD とDMA の初期設定を行っています。 これで、倍増器の演算が終わり次第、自動的に配列 result に結果が転送されます。

    CR1_Write(1);
    good = 1;
    a = 0;
    for (i = 0; i < 102; i++) {
        for (j = 0; j < 64; j++) {
            CY_SET_REG16(Times10_INPUT_PTR, a + j);
        }
        for (j = 0; j < 64; j++) {
            a10 = (a + j) * 10;
            w = result[j];
            if (a10 != w) {
                good = 0;
                goto escape;
            }
        }
        a += 64;
    }
    escape:

プログラムは、これまでと同様に倍増器の FIFO0 に16ビットの値を書込み、その演算結果を CPU で検証するという流れになっています。

これまでと異なっているのは、 FIFO0 に64個の値をまとめて書き込み、 DMA によって配列 result に出力された演算結果を64個ずつ検証している点です。 このため、プログラムは、64個の値の書き込みを102セット、つまり "0" から "6527" までの 64×102=6528 個の値を書込むようになっています。

    LCD_Position(0, 0);
    if (good) {
        LCD_PrintString("GOOD");
    } else {
        LCD_PrintString("BAD");
        LCD_Position(1, 0);
        LCD_PrintNumber(a + j);
        LCD_Position(1, 8);
        LCD_PrintNumber(w);
    }

演算の検証の結果、すべて正しければ、 LCD に "GOOD" が表示されます。 もし、誤りが見つかったら、"BAD" の表示と共に FIFO0 に書き込んだ値と演算結果が表示されます。

    CyDmaChDisable(DMA_Chan);
    a = 0;
    CY_SET_REG16(Times10_INPUT_PTR, a++);
    CY_SET_REG16(Times10_INPUT_PTR, a++);

    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        CY_SET_REG16(Times10_INPUT_PTR, a++);
        w = CY_GET_REG16(Times10_OUTPUT_PTR);
    }
}

一通り、検証が終わったら、 DMA の動きを止めて、前回同様の CPU で書き込んで CPU で読み出すループに入ります。 DMA に対応はしましたが、 DMA 専用ではないので、これまで通りの動作をさせることができます。

プロジェクトアーカイブ

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

関連文献

トランジスタ技術 2013年 07月号 [雑誌]

トランジスタ技術 2013年 07月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2013/06/10
  • メディア: 雑誌

PSoC 3 で、16ビット演算器を作った [PSoC]このエントリーを含むはてなブックマーク#

16ビット演算器

前回まで、8ビットの演算器を作ってきましたが、10倍の倍増器などを実験していると、すぐに上限値である256を超えてしまいます。 そこで、今回は、データパスをもう一つ追加して、16ビットの演算器を作ります。

8ビットから16ビットへ

16ビット倍増器のデータフロー

ここでは、例として16ビットの10倍増器を作ります。 データフローを見て分かるように、二組のデータパスを使って、演算を行います。 二つのデータパスには、 "_a" と "_b" のサフィックスが付いていて、順番に下位 (LSB) と上位 (MSB) を表しています。

そして、二つのデータパスの間には、繰上りを表現するための "Carry" とデータパスをまたぐビットシフトを表現するための "SIO" が配置されています。 これらのデータパスは、お互いを信号線で結ぶことから、物理的に近くに割り当てる必要があります。 PSoC Creator の論理合成では、二組のデータパスを確保する "cy_psoc3_dp16" を使うと、連続するデータパスを割り当ててくれるます。 このため、合わせて16ビットのデータパスとして使用することができるようになります。

しかしながら、二つのデータパスは、独立して設定できますので、連続したデータパスであったとしても、全く別の動作を行わせることも出来ます。

16ビット対応の設定

今回作成する10倍増器は、二つのデータパスを使いますが、それぞれのデータパスの設定は8ビットの時とほぼ同じです。 異なっているのは、上位 ("_b") 側の Carry/SIO 入力の部分です。

下位の設定

下位 ("_a") のデータパスの設定


上位の設定

上位 ("_b") のデータパスの設定


キャリー信号を下位から取り入れるために、 "CI SELA" の設定を "ARITH" から "CHAIN" に変更しています。 また、シフト信号を下位から取り入れるために、 "SI SELA" の設定を "DEFSI" から "CHAIN" に変更しています。 これで、二つの ALU が16ビットの ALU として動作するようになります。

Verilog 記述

Verilog 記述は、前回の倍増器とほぼ同じですが、データパスが二つになったために、一部の制御信号が2本になっています。

//`#start header` -- edit after this line, do not edit this line
// ========================================
//
// Copyright noritan.org, 2013
// All Rights Reserved
// UNPUBLISHED, LICENSED SOFTWARE.
//
// CONFIDENTIAL AND PROPRIETARY INFORMATION
// WHICH IS THE PROPERTY OF NORITAN.ORG.
//
// ========================================
`include "cypress.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 05/25/2013 at 18:50
// Component: Times10
module Times10 (
	input   clock,
	input   en,
	input   reset
);

//`#start body` -- edit after this line, do not edit this line

// State code declaration
localparam      ST_IDLE     = 3'b000;
localparam      ST_GET      = 3'b001;
localparam      ST_STEP1    = 3'b010;
localparam      ST_STEP2    = 3'b011;
localparam      ST_STEP3    = 3'b100;
localparam      ST_WAIT     = 3'b101;
localparam      ST_PUT      = 3'b110;

// Datapath function declaration
localparam      CS_IDLE     = 3'b000;
localparam      CS_CLEAR    = 3'b001;
localparam      CS_SL0      = 3'b100;
localparam      CS_SL1      = 3'b101;
localparam      CS_ADD0     = 3'b110;
localparam      CS_ADD1     = 3'b111;

// Wire declaration
wire[2:0]       state;          // State code
wire[1:0]       f0_empty;       // F0 is EMPTY
wire[1:0]       f1_full;        // F1 is FULL
wire[1:0]       so;
wire[1:0]       co;

// Pseudo register
reg[2:0]        addr;           // Datapath function
reg             d0_load;        // LOAD D0 from F0
reg             f1_load;        // LOAD F1

// State machine behavior
reg [2:0]       state_reg;
always @(posedge reset or posedge clock) begin
    if (reset) begin
                state_reg <= ST_IDLE;
    end else casez (state)
        ST_IDLE:
            if (en & ~f0_empty[1] & ~f0_empty[0]) begin
                state_reg <= ST_GET;
            end
        ST_GET:
                state_reg <= ST_STEP1;
        ST_STEP1:
                state_reg <= ST_STEP2;
        ST_STEP2:
                state_reg <= ST_STEP3;
        ST_STEP3:
                state_reg <= ST_WAIT;
        ST_WAIT:
            if (~f1_full[1] & ~f1_full[0]) begin
                state_reg <= ST_PUT;
            end
        /*ST_PUT*/ default:
                state_reg <= ST_IDLE;
    endcase
end
assign      state = state_reg;

// Internal control signals
always @(state) begin
    casez (state)
        ST_IDLE: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_GET: begin
            addr    = CS_CLEAR;
            d0_load = 1'b1;
            f1_load = 1'b0;
        end
        ST_STEP1: begin
            addr    = CS_SL1;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_STEP2: begin
            addr    = CS_SL0;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_STEP3: begin
            addr    = CS_SL1;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        ST_WAIT: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b0;
        end
        /*ST_PUT*/ default: begin
            addr    = CS_IDLE;
            d0_load = 1'b0;
            f1_load = 1'b1;
        end
    endcase
end

cy_psoc3_dp16 #(.cy_dpconfig_a(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0: IDLE : Do nothing*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1: CLEAR : A0 <= ALU <= (A0 XOR A0);*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2: */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3: */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM4: SL0 : A0 <= ALU <= (A0) << 1;*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM5: SL1 : A0<= ALU <= (A0 + D0) << 1;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM6: ADD0 : A0 <= ALU <= (A0);*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM7: ADD1 : A0 <= ALU <= (A0 + D0);*/
    8'hFF, 8'h00,  /*CFG9: */
    8'hFF, 8'hFF,  /*CFG11-10: */
    `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
    `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
    `SC_SI_A_DEFSI, /*CFG13-12: */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_ALU, `SC_FIFO0_BUS,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_NOCHN, `SC_CMP1_NOCHN,
    `SC_CMP0_NOCHN, /*CFG15-14: */
    10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16: */
}
), .cy_dpconfig_b(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0: IDLE : Do nothing*/
    `CS_ALU_OP__XOR, `CS_SRCA_A0, `CS_SRCB_A0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1: CLEAR : A0 <= ALU <= (A0 XOR A0);*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2: */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3: */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM4: SL0 : A0 <= ALU <= (A0) << 1;*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SL, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM5: SL1 : A0 <= ALU <= (A0 + D0) << 1;*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM6: ADD0 : A0 <= ALU <= (A0);*/
    `CS_ALU_OP__ADD, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM7: ADD1 : A0 <= ALU <= (A0 + D0);*/
    8'hFF, 8'h00,  /*CFG9: */
    8'hFF, 8'hFF,  /*CFG11-10: */
    `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
    `SC_CI_A_CHAIN, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
    `SC_SI_A_CHAIN, /*CFG13-12: */
    `SC_A0_SRC_ACC, `SC_SHIFT_SL, 1'h0,
    1'h0, `SC_FIFO1_ALU, `SC_FIFO0_BUS,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_NOCHN, `SC_CMP1_NOCHN,
    `SC_CMP0_NOCHN, /*CFG15-14:  */
    10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16: */
}
)) dp(
        /*  input           */  .reset(reset),
        /*  input           */  .clk(clock),
        /*  input   [02:00] */  .cs_addr(addr[2:0]),
        /*  input           */  .route_si(1'b0),
        /*  input           */  .route_ci(1'b0),
        /*  input           */  .f0_load(1'b0),
        /*  input           */  .f1_load(f1_load),
        /*  input           */  .d0_load(d0_load),
        /*  input           */  .d1_load(1'b0),
        /*  output  [01:00] */  .ce0(),
        /*  output  [01:00] */  .cl0(),
        /*  output  [01:00] */  .z0(),
        /*  output  [01:00] */  .ff0(),
        /*  output  [01:00] */  .ce1(),
        /*  output  [01:00] */  .cl1(),
        /*  output  [01:00] */  .z1(),
        /*  output  [01:00] */  .ff1(),
        /*  output  [01:00] */  .ov_msb(),
        /*  output  [01:00] */  .co_msb(),
        /*  output  [01:00] */  .cmsb(),
        /*  output  [01:00] */  .so(),
        /*  output  [01:00] */  .f0_bus_stat(),
        /*  output  [01:00] */  .f0_blk_stat(f0_empty[1:0]),
        /*  output  [01:00] */  .f1_bus_stat(),
        /*  output  [01:00] */  .f1_blk_stat(f1_full[1:0])
);

//`#end` -- edit above this line, do not edit this line
endmodule
//`#start footer` -- edit after this line, do not edit this line
//`#end` -- edit above this line, do not edit this line

FIFO は、それぞれのデータパスに8ビットずつ配置されています。 そのため、双方のデータパスの FIFO にデータが準備できた時にデータを取り出す事ができるという判断をしています。

テストプログラム

今回は、16ビットの範囲内で表現可能なすべての組み合わせを検査し、最終結果を LCD に表示するようなテストプログラムとしました。

/* ========================================
 *
 * Copyright noritan.org, 2013
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF NORITAN.ORG.
 *
 * ========================================
*/
#include <device.h>

void main()
{
    uint16          a;
    uint16          a10;
    volatile uint16 w = 0;
    CYBIT           good;
        
    LCD_Start();

    CR1_Write(1);
    good = 1;
    for (a = 0; a < 6554; a++) {
        CY_SET_REG16(Times10_dp_u0__F0_REG, a);
        a10 = a * 10;
        w = CY_GET_REG16(Times10_dp_u0__F1_REG);
        if (a10 != w) {
            good = 0;
            break;
        }
    }
    
    LCD_Position(0, 0);
    if (good) {
        LCD_PrintString("GOOD");
    } else {
        LCD_PrintString("BAD");
        LCD_Position(1, 0);
        LCD_PrintNumber(a);
        LCD_Position(1, 8);
        LCD_PrintNumber(w);
    }

    a = 0;
    CY_SET_REG16(Times10_dp_u0__F0_REG, a++);
    CY_SET_REG16(Times10_dp_u0__F0_REG, a++);

    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        CY_SET_REG16(Times10_dp_u0__F0_REG, a++);
        w = CY_GET_REG16(Times10_dp_u0__F1_REG);
    }
}

/* [] END OF FILE */

データを入れるための FIFO0 には、下位 (u0) 側の F0 レジスタのアドレスを指定して、16ビットのアクセスをしています。

        CY_SET_REG16(Times10_dp_u0__F0_REG, a);

上位のデータパスは、下位の隣に配置されています。 そのため、上位データパスの F0 レジスタは、この次のアドレスに配置されています。 このような構造のために、下位の F0 レジスタのアドレスが、16ビットレジスタへのアドレスとして使用できるのです。

また、読み出し側のアドレス指定も同様に下位側のアドレスを指定します。

        w = CY_GET_REG16(Times10_dp_u0__F1_REG);

計算結果が、すべて正しければ、 "GOOD" が表示されます。 また、計算結果が合わなかった場合には、 "BAD" の表示とともに、与えた値と計算結果が表示されて、デバッグを支援するようになっています。

プロジェクトアーカイブ

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

関連文献


続・PSoC 3 で、倍増器を作った [PSoC]このエントリーを含むはてなブックマーク#

10倍増器

前回は、 FIFO を応用して入ってきたデータを2倍にして返す倍増器を作りました。 今回も倍増器を作ります。 ただし、倍率が前回とは違っています。

今度は、10倍

10倍増データフロー

今回は、10倍に挑戦します。

値を10倍にする仕組みをデータフローで表現しています。 足し算とシフトを駆使して、 D0 に入った値 "d" を10倍にしています。 10倍の値を得るために必要なクロック数は、5クロックに伸びました。 それぞれのステップで ALU の振る舞いを変化させて、計算を行っています。


ここでは、演算部分だけを解説します。

  • STEP1

    このステップでは、前回と同様、 "SL1" 操作を行います。 A0 には、 FIFO0 から取り出した値 "d" の二倍の値 "2d" が入ります。

  • STEP2

    このステップでは、新たに定義した "SL0" 操作を行います。 "SL1" とは違い、シフターを通す前に "D0" を加算しません。 そのため、A0 には、値 "d" の4倍の値 "4d" が入ります。

  • STEP3

    このステップでは、ふたたび、 "SL1" 操作を行います。 A0 に "d" を足してからシフターを通すので、A0 には、値 "d" の10倍の値 "10d" が入ります。 これで、元の値の10倍の値を得る事ができました。

以上の3ステップで、 D0 の値は10倍されます。 "SL1" と "SL0" の適用順序を変えると、違う倍率の倍増器を構成することも出来ます。

Verilog の実装のコンセプトは、前回とほぼ同じです。 ここでは省略しますが、興味があれば、プロジェクトを開いて確認してください。

データパスの実装

10倍増器のデータパス設定

データパスの設定は、 "SL1" が追加されました。 また、後の戦略の都合上、 "SL0" を Reg4 に、 "SL1" を Reg5 に割り当てています。 "SL1" と "SL0" の違いは、 ALU の演算設定だけで、 ADD の部分が PASS になっています。

10倍増器の動作

10倍増器の使い方は、前回の倍増器と同じです。 そのため、テストプログラムも同じものが使えます。 ただし、倍増器のインスタンス名が変わっていますので、レジスタアクセスに使用する名前を変更する必要があります。

/* ========================================
 *
 * Copyright noritan.org, 2013
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF NORITAN.ORG.
 *
 * ========================================
*/
#include <device.h>

void main()
{
    uint8           i = 0;
    uint8           v[16];
    volatile uint8  w = 0;
    
    CR1_Write(1);
    CY_SET_REG8(Times10_dp_u0__F0_REG, w++);
    for (i = 0; i < 16; i++) {
        CY_SET_REG8(Times10_dp_u0__F0_REG, w++);
        v[ i] = CY_GET_REG8(Times10_dp_u0__F1_REG);
    }

    LCD_Start();
    LCD_Position(0, 0);
    for (i = 0; i < 8; i++) {
        LCD_PrintInt8(v[i]);
    }
    LCD_Position(1, 0);
    for (i = 8; i < 16; i++) {
        LCD_PrintInt8(v[i]);
    }

    CY_SET_REG8(Times10_dp_u0__F0_REG, i++);
    CY_SET_REG8(Times10_dp_u0__F0_REG, i++);

    /* CyGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */
    for(;;)
    {
        CY_SET_REG8(Times10_dp_u0__F0_REG, i++);
        w = CY_GET_REG8(Times10_dp_u0__F1_REG);
    }
}

/* [] END OF FILE */

奇数倍の場合

WS000536.png

データパスの設定 "SL0" と "SL1" を使う事によってすべての倍率に対応できるようになったかかというと、そうはいきません。 "SL0" も "SL1" も、最後にシフターを通しているので、偶数倍の値しか出てきません。

5倍増器のデータフロー

5倍増器のデータフロー

では、どうするかというと、最後の1ステップのために、シフターを通さない操作を追加します。 このデータフロー図は、5倍増器の場合です。 10倍増器の STEP3 の演算で、 "SL1" の代わりに "ADD1" が使用されています。 "ADD1" は、 A0 と D0 を加算して、シフターを通さずにそのまま A0 に格納する操作です。 この操作を使う事で、奇数倍の場合にも対応することができます。

5倍増器対応のデータパス設定

5倍増器対応データパスの設定

データパスは、奇数倍に対応させるために、さらに操作を追加しています。 上の5倍増器のデータフローでは、 "ADD1" のみが必要になっていましたが、ここでは、後のことを考えて、 "ADD0" という操作も追加しています。 この操作は、 D0 の加算も行わず、シフターも通さないで、 A0 を単に書き直すという操作になっています。

さあ、これで任意の値に対応する役者が揃いました。

プロジェクトアーカイブ

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

関連文献


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