SSブログ

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

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

nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

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