2016年5月31日火曜日

NucleoF401RE(mbed)シーケンサー(途中)とPSoC4のSPI Slaveのテスト。

mbedでシーケンサーのひな型を作ってSPI通信でPSoCに受信させようと思ったがPSoC側のプログラムがどれがどれだか迷った。

やはり、1週間前に自分が何をやってたか忘れているっぽい(^q^;

寝ながらブログを見返していてPSoC4でSPI受信プログラムを書いたのを思い出した。「Nucleo F401RE(mbed)をMasterにしてPSoC4とSPI通信する

配線図


mbedのプログラム
https://developer.mbed.org/users/ryood/code/SpiSequencerSender_Test/

mbedのリポジトリにあげるとクラス化した部分はライブラリとしてカプセル化されてしまうようだ。

PSoC CreatorのProject
https://github.com/ryood/PSoC4_SPI_Slave_Test/tree/master/PSoC(Slave)/PSoC4_SPI_Slave_Test/PSoC4_SPI_Slave_Test_for_NucleoF401RE.cydsn

実行結果


上がmbed側、下がPSoC 4側のUART出力。

メモ:

自分流でクラス化していくと行く先が正しいか正しくないかわからない。

だが、クラス化すると単体テストがしやすくなるので拡張性は高くなりそう。

演算子のオーバーロードはできればしたくないが(頭が悪いので訳がわかなくなる)、getter, setter式の記述と単なる変数の代入が混在すると一瞬あれ?っとなる。

コンストラクタで変数の初期化をずらずら並べるとやっぱり後で訳がわかなくなりそう。

Windowsのプログラムみたいにからっぽのクラスの実体化と、オブジェクトの生成(init()とかで)分けた方がいいのか。

CASIO fx-JP500買ってみた。

定番の関数電卓のCASIO fx-375ESに水をかけてしまって、液晶の1ラインが表示されなくなってしまった。

がまんすれば使えなくはないが、もともとあんまり視認性が良くないと思っていたので、500円ぐらい高いが高精細液晶をうたい文句にしているfx-JP500を買ってみた。


確かに解像度は高くなっているが、文字サイズが小さくなっているので視認性の良さは期待したほどではなかった。

キーの取り付け精度はかなりよくなっている。

関数電卓の機能は大して使っていなかったが、メニューがわかりやすくなっているので遊びながらいろいろ試せそうな感じだ。

2016年5月30日月曜日

mbedのRtosTimerをClassの中で使う。

クラスを定義して、メンバ関数をRtosTimerのtaskとして登録しようと思うと、

RtosTimer (void(*task)(void const *argument), os_timer_type type=osTimerPeriodic, void *argument=NULL)

の第一引数のtaskにメンバ関数をそのまま書くとコンパイルエラーになる。

staticなメンバ関数を一旦かましてやればビルド&実行OKだった。

参考:http://stackoverflow.com/questions/29007191/using-an-rtostimer-inside-a-class

#include "mbed.h"
#include "rtos.h"

class TestClass
{
public:
    TestClass(const char* _instanceName, uint32_t _updateTime) :
        updateTime(_updateTime),
        timer(&TestClass::threadHelper, osTimerPeriodic, (void *)this)
    {
        //NOTE: The RTOS hasn't started yet, so we can't create the internal thread here
        strncpy(instanceName, _instanceName, 20);
    }

    void run() {
        timer.start(updateTime);
    }

    void stop() {
        timer.stop();
    }

private:
    char instanceName[20];
    uint32_t updateTime;
    RtosTimer timer;

    static void threadHelper(const void* arg) {
        printf("In threadHelper()\r\n");

        //Cast the argument to a TestClass instance pointer
        TestClass* instance = (TestClass*)arg;

        //Call the thread method for the TestClass instance
        instance ->threadMethod();
    }

    void threadMethod() {
        printf("In threadMethod()\t%s\r\n", instanceName);
    }
};

int main()
{
    printf("\n\n\r** Using An RtosTimer inside a class test **\r\n");
    
    while (true) {
        printf("In main loop\r\n");
        TestClass test1("test1", 1000);
        TestClass test2("test2", 500);
        test1.run();
        test2.run();
        Thread::wait(10000);
        /*
        test1.stop();
        test2.stop();
        */
    }
}

ちょっとめんどくさいが、staticなメンバ関数をそのまま使うと、インスタンスごとのprivateなメンバ変数にアクセスできない。

mbed repository
https://developer.mbed.org/users/ryood/code/Nucleo_UsingAnRtosTimerInsideAClass_Test/

NucleoF401REのSPI1とSPI2

Nokia5110との接続

SPI1

SPI2

接続表

2016年5月28日土曜日

Nucleo F401RE(mbed)でシーケンサーを作るつもりで入出力デバイスのテスト。


配線図
<追記:2016.05.30> 配線図のNokia5110とMorpho Headerの接続が間違っていたので修正 </追記>


ロータリーエンコーダー、タクトスイッチ(トグル動作、押し下げ検知)、可変抵抗(普通のPOTとアナログ・ジョイスティック)、LCD(Nokia5110)の組み合わせで、Nucleo F401REにつないでテストしてみた。

入出力デバイスだけのテストで、それ以外は何もしていない。

↓mbedはGitでソース管理できないようなのでmbedのやつを使ってみた。

https://developer.mbed.org/users/ryood/code/Nucleo_rtos_UI_Test/

ロータリーエンコーダー


Shinichiro Nakamuraさんが公開されているRotaryEncoderクラスを一部変更して使った。(mbedオンライン・コンパイラのインポート/ライブラリからインポートできる)

NucleoF401REではグローバルスコープで実体化した場合、Tickerクラスのattach_us()が失敗する(参考:「Nucleo F401RE(mbed)でDebouncerInライブラリを使う。」)ようなので、

    void setInterval(timestamp_t t) {
        ticker.attach_us(this, &RotaryEncoder::func_ticker, t);
    }

というメンバ関数を追加して後からattachできるようにした。

また、RotaryEncoderクラスのオブジェクトを2個作ると互いに干渉するので、void func_ticker()内でstaticで定義されていた「uint8_t code」をメンバ変数として定義するようにした。

タクトスイッチ


チャタリング対策にPinDetectクラスを使用。(参考:「Nucleo F401RE(mbed)でRotary Encoderとタクトスイッチのチャタリング対策」)

PinDetectクラスと同じ作者さんのDebouncerInクラスの違いは、ポーリングで使うか割込みで使うかの差だと思うが、PinDetectの方がイベント・ドリブンっぽいので(Visual Basicみたいな感じに書ける)PinDetectを使ってみた。

可変抵抗


このあいだ作ったAnalogInを移動平均するクラスを使った。(「mbedのAnalogInでPotentiometerの読み取り値のふらつきを抑える。」)

LCD


mbedのオンライン・コンパイラでNokia5110のライブラリはいくつか見つかるが、今回は「N5110」ライブラリを使った。前使った5110_Displayライブラリ(
参考:「Nucleo F401RE(mbed)でNokia5110を動かす。」)は直線を引くメソッド(その他もろもろ)がなかったので。

RTOS


ARMのRTOSのスレッドの扱いはよくわからないが、ものは試しでポーリングするところを別スレッドにして読み取りのタイミングを調整するようにしてみた。

分けたスレッドは4つ。

  • メインスレッド(LCD表示)
  • ロータリーエンコーダー読み取り
  • POT読み取り
  • Lチカ

2016年5月27日金曜日

aitendoのFSTN液晶モジュール(SPI) [CH12864F-SPI]をNucleo F401RE(mbed)で動かす。

大き目サイズのSPIグラフィックLCDを仕入れてみたのでテスト。

配線図


LEDA(11Pin)は基板上に電流制限抵抗が載っているようだが、一応100Ωの抵抗を入れてVDD(+3.3V)につないだ。

mbedのライブラリはMasato YAMANISHIさんが公開されているst7565LCDクラスを利用した。

main.cpp

#include "mbed.h"
#include "st7565LCD.h"

int main()
{
    //ST7565(PinName mosi, PinName sclk, PinName cs, PinName rst, PinName a0);
    ST7565 gLCD(SPI_MOSI, SPI_SCK, SPI_CS, D9, D8);
    
    gLCD.begin(0x10);
    
    while (true) {
        gLCD.drawstring(0, 0, "Hello World.");
        gLCD.display();
        wait(0.1);
    }
}

ST7565::display()関数を呼び出すことでLCDへの書き込みがおこなわれる。

aitendoのSPI接続のグラフィックLCD



左から、CH12864F-SPI(128x64/SPI)、F12864G25412P1701(128x64/SPI)、Nokia LCD5110(84x48/SPI)。

2016年5月25日水曜日

mbedのAnalogInでPotentiometerの読み取り値のふらつきを抑える。

mbedのAnalogInを使ってみたが、読み取り値がふらつく。値が変動したのをトリガーとして何らかの処理を行いたい場合、少しでも値が変動すると困る。

自作の可変両電源のAVRのプログラミングでは、移動平均をとるのと、ADCの変換中に他の処理をストップさせてノイズを減らすということをやったが、POTの読み取りごときでいちいち止めていられないので移動平均をとって改善するかどうか見ることにした。

「移動平均」というと何かかっこよさそうですが、前に読み取った値をいくつか覚えておいてその間の平均をとって今の値として採用するというだけ。


前作ったぴゅんぴゅんコントローラー2号で実験してみた。POT3個とアナログジョイスティック 1個(POTが縦横で2個)でアナログ素子は5個。Arduino 互換のHeaderのA0~A4につないだ。(タクトスイッチは使っていない。)

#include "mbed.h"

class AverageAnalogIn {
public:
    AverageAnalogIn(PinName _pin, int _bufferSize=16) : m_AnalogIn(_pin), bufferSize(_bufferSize), index(0) {
        buffer = new unsigned short[bufferSize];
    }
    
    ~AverageAnalogIn() {
        delete buffer;
    }
    
    unsigned short read_u16() {
        buffer[index] = m_AnalogIn.read_u16();
        index++;
        if (index == bufferSize) {
            index = 0;
        }
        unsigned int sum = 0;
        for (int i = 0; i < bufferSize; i++) {
            sum += buffer[i];
        }
        return sum / bufferSize;
    }

private:
    AnalogIn m_AnalogIn;
    int bufferSize;
    int index;
    unsigned short *buffer;
};
 
AverageAnalogIn Pots[] = {
    AverageAnalogIn(A0, 4),
    AverageAnalogIn(A1, 8),
    AverageAnalogIn(A2, 16),
    AverageAnalogIn(A3, 32),
    AverageAnalogIn(A4, 64),
};

#define POTS_SIZE (sizeof(Pots)/sizeof(Pots[0]))

int main() {
    static uint16_t prevMeas[POTS_SIZE];

    printf("\r\nAnalogIn example\r\n");
    
    while(1) {
        unsigned int error = 0;
        for (int i = 0; i < POTS_SIZE; i++) {
            unsigned short meas = Pots[i].read_u16() >> 6; // 10bit width
            printf("%u,", meas);
            if (prevMeas[i] != meas) {
                prevMeas[i] = meas;
                error |= (1 << i);
            }
        }
        printf("%x\r\n", error);
        
        wait(0.01); // 10 ms
    }
}


一応、C++らしくクラス化してみた。(ちゃんとテストしていないのでお気をつけて。)

<追記:2016.06.06>

早速ですが、デストラクタの「delete buffer」は「delete[] buffer」じゃないと作ったり壊したりしたらメモリー・リークする(^q^;

</追記>

オブジェクト指向というのは、STL(Windowsではないほう)、WindowsのMFC、DirectX、Java EEなどで使っていたのでどの流儀がいいのか記法レベルで迷う。組み込みだし、コストも気にしないといけないのでなかなか悩みどころが多い。

かと言って平文(?)でダラダラ書いてると、もう以前自分が何のつもりで書いてたのかわからなくなってきたので何か流儀を見つけないといけないなと思っています。

10bit精度で値のふらつきをみる。



AnalogInのread_u16は[0..0xFFFF]の値を返すので、ソースの通りunsigned 16bitで読み取った値を6bit右シフトして10bit精度に落としてCPLTでグラフ化してみた。


ch1(黒) POT1 4個の平均
ch2(青) POT2 8個の平均
ch3(緑) POT3 16個の平均
ch4(暗緑) アナログジョイスティック 32個の平均
ch5(赤) アナログジョイスティック 64個の平均
ch6(紫) エラー値

グラフの一番下の棒グラフ状になっているところを見ると10bit精度では常に値が変動していそうだ。bitごとに1 or 0にしているので紫のグラフの背が高いところがエラーが多いというわけではない。

ch4(暗緑)は32個の平均をとって変動しているのに、64個の平均をとったch5(赤)はほぼ変動していない。

経験上このアナログジョイスティックは普通のPOTよりかなり精度が悪いのがわかっているので、64個も平均を取れば大体のPOTは大丈夫そうだ。

ただし、かなり反応が鈍くなる。

NucleoF401REのMorpho Headerは一部未接続(メモ)

Morpho HeaderでDigital Inが動作しないピンがあるようなので、調べてるとMorpho Headerの一部は未接続になっているようだ。




基板の裏側のところにハンダを盛れば使えるようになりそうだが、


デフォルトでオープンになってる理由がわからないのでこの辺のヘッダピンはひとまず使わない方向で。

NucleoF401REでQEIライブラリを使う。

QEIというQuadrature Encoderのライブラリがあったのでロータリーエンコーダーで使ってみた。

QEI:
https://developer.mbed.org/cookbook/QEI

Rotary Encoderと書いてあるが手で回すロータリーエンコーダーではなく、タイヤとかの回転角度・回転数を計測する用みたいだ。

手回しのロータリーエンコーダでも一応使える。

配線図

#include "QEI.h"

Serial pc(USBTX, USBRX);
//Use X4 encoding.
//QEI wheel(p29, p30, NC, 624, QEI::X4_ENCODING);
//Use X2 encoding by default.
//QEI wheel (p29, p30, NC, 624);
QEI wheel (PB_4, PB_5, NC, 624, QEI::X2_ENCODING);

int main() {

    while(1){
        wait(0.1);
        pc.printf("Pulses is: %i\r\n", wheel.getPulses());
    }

}

QEIクラスはピンの変化の検知に外部割り込み(InterruputIn)を使っていて、これがprivateなのでInterruptIn::mode()で内部プルアップの設定ができない。仕方がないので、ロータリーエンコーダーのA/Bピンをブレッドボード上でプルアップしている。

秋月で売っているAlpsのロータリーエンコーダーは1デテント(クリック感のあるところ)で数値が2個進む感じなので、QEI::X2_ENCODINGを指定するとデテント1個でPulsesが2つずつ増減する。(QEI::X4_ENCODINGだと4つずつ増減する)

624という指定は1回転数当たりのパルス数で、Exampleのデフォルトのままで特に意味は無い。

NucleoF401REの外部割り込み


mbed公式のInterrutptInのページを見るとmbed NXP LPC1768の外部割り込みで使えるピンの注意書きがあるが、NucleoF401REは16本の割り込みをPinに割り当てて使うようになっているようだ。

Port はバラバラでもいいが、Pin番号がかぶるとダメ。

参考:
http://letanphuc.net/2015/03/stm32f0-tutorial-3-external-interrupts/

STMicroelectronics RM0368 Reference manual

なので、紛れがないように今回はPB_4、PB_5に割り当てた。

ちなみにArduino Uno/ATMega328Pの外部割り込みは固定でINT0、INT1の2本しかない。

メモ:


QEIライブラリは手回しのロータリーエンコーダでも使えるが、使いにくいかな。

2016年5月24日火曜日

Nucleo F401RE(mbed)でDebounceInライブラリを使う。

Nucleo F401REではDebounceInクラスのexampleが動作しない。

DebounceInをグローバル・スコープで実体化すると内部のTimer割込みで使われているコールバック関数が呼び出されないようだ。

#include "mbed.h"
#include "DebounceIn.h"

DebounceIn d(D2);
DigitalOut led1(LED1);

int main()
{
    d.set_debounce_us(1000);   
    d.mode(PullUp);
    while(1) {
        led1 = !d;
     }
}

main関数内でDebouceIn::set_debouce_us()を呼び出してやれば動作した(コールバック関数が登録されなおす)。DebouceInのコンストラクタをローカルスコープで呼び出しても動作する。

参考:https://developer.mbed.org/forum/mbed/topic/5234/

2016年5月22日日曜日

Nucleo F401REのファームウェアアップデート。(メモ)

USBケーブルを挿し直したり、自作の5V/3.3V安定化電源を使うとプログラムが起動しなくて困ったが、nucleoのファームウェアをアップデートしたら外部電源でも動作するようになった(^q^/

mbed公式のnucleo F401REのページの下の方

「Nucleo ST-LINK/V2 driver installation and firmware upgrade」の囲み記事からファームウェアのアップデータをダウンロードできた。

「ST-LINK/V2」を最初にインストールしろと書いてあるが、上記からダウンロードしたドライバ(en.stsw-audio003.zip)はWindows10ではインストールできなかった。ST-LINK
(USBドライバ)はもともと入っていた。いつ入れたかははっきり覚えていない(^q^;

mbedのRTOS TimerでSPI DACのMCP4922を動かす。

Tickerクラスの割り込みではなく、RTOSのTimerでタイミングをとってMCP4922を動作させてみた。

配線図
MCP4922は3.3Vで動作させている。


main.cpp

#include "mbed.h"
#include "rtos.h"

#define SPIM_TIMER_PERIOD   (1)
#define SPIM_RATE           (8000000)   // 8MHz
#define SPIM_WAIT           /*(wait_us(1))*/

#define DCO_PACKET_HEADER   (0x55)
#define SPI_DUMMY_DATA      (0xaa)      // テスト用ダミーデータ

// ピン・アサイン
#define PIN_CHECK       PA_10
#define PIN_DCO_CS      PB_6
#define PIN_DCF_CS      PC_7
#define PIN_DAC_CS      PA_9
#define PIN_DAC_LDAC    PA_8

SPI SpiM(SPI_MOSI, SPI_MISO, SPI_SCK); 
DigitalOut DcoCs(PIN_DCO_CS);
DigitalOut DcfCs(PIN_DCF_CS);
DigitalOut DacCs(PIN_DAC_CS);
DigitalOut DacLdac(PIN_DAC_LDAC);

DigitalOut CheckPin(PIN_CHECK);

void writeToDco()
{
    DcoCs = 0;
    // DCO Header
    SpiM.write(DCO_PACKET_HEADER);
    // Wave Form
    SpiM.write(SPI_DUMMY_DATA);
    // Pulse Width
    SpiM.write(SPI_DUMMY_DATA);
    // Frequency
    SpiM.write(SPI_DUMMY_DATA);
    SpiM.write(SPI_DUMMY_DATA);
    SPIM_WAIT;
    DcoCs = 1;
}

void writeToDcf()
{
    // Filter Q
    DcfCs = 0;
    SpiM.write(0);  // address
    SpiM.write(SPI_DUMMY_DATA);
    SPIM_WAIT;
    DcfCs = 1;
    // Filter CutOff
    DcfCs = 0;
    SpiM.write(1);  // address
    SpiM.write(SPI_DUMMY_DATA);
    SPIM_WAIT;
    DcfCs = 1;
    DcfCs = 0;
    SpiM.write(3);  // address
    SpiM.write(SPI_DUMMY_DATA);
    SPIM_WAIT;
    DcfCs = 1;
}

void writeToDac()
{
    static int v = 0;
    int nv;
    
    nv = 4095 - v; // Data for Channel B
    
    DacLdac = 1;
    
    // Channel A
    DacCs = 0;
    SpiM.write((v >> 8) | 0x30);    // 0x30: DAC_A(0) | Vref Unbuffered(0) | Vout 1x(1) | !SHDN(1)
    SpiM.write(v & 0xff);
    SPIM_WAIT;
    DacCs = 1;
    
    // Channel B
    DacCs = 0;
    SpiM.write((nv >> 8) | 0xB0);    // 0xB0: DAC_B(1) | Vref Unbuffered(0) | Vout 1x(1) | !SHDN(1)
    SpiM.write(nv & 0xff);  
    SPIM_WAIT;
    DacCs = 1;
    
    DacLdac = 0;
    
    v++;
    if (v == 4095) {
        v = 0;
    }
}
    
void writeToSpi(void const *n)
{
    CheckPin = !CheckPin;
    
    // DCO
    writeToDco();
    
    // DCF
    writeToDcf();
    
    // DCO
    writeToDac();
}

int main()
{
    RtosTimer SpiM_timer(writeToSpi, osTimerPeriodic);
    
    SpiM.format(8, 0);
    SpiM.frequency(SPIM_RATE);
    SpiM_timer.start(SPIM_TIMER_PERIOD);
    
    Thread::wait(osWaitForever);
}


MCP4922はChannel Aにフルスケールのノコギリ波、channel Bは逆位相のノコギリ波を出力。

MCP4922への出力以外に同じSPIバスを使ったデバイスを想定して、ダミーデータを出力させている。(writeToDco()、wirteToDcf())

出力波形


ch1:Channnel A ch2:Channel B

SPIの信号


ch1:SPI_MOSI ch2:SPI_SCK

最初の5byteがDCO、次の6byteがDCF、最後の4byteがMCP4922

DAC


ch1:PIN_DAC_CS ch2:SPI_SCK


ch1:PIN_DAC_LDAC ch2:SPI_SCK

DCO


ch1:PIN_DCO_CS ch2:SPI_SCK

DCF


ch1:PIN_DCF_CS ch2:SPI_SCK

mbedのRTOSとprintf(メモ)

#include "mbed.h"
#include "rtos.h"

#define TICKER_PERIOD   (0.5)

Ticker toggle_pin_ticker;
Timer timer;
DigitalOut led(LED1);

//RTOSを使ったプログラムでSerialオブジェクトを実体化すると割り込み(toggle_pin())が効かなくなる。
//Thread(led_thread())は動作する。
//Serial pc(SERIAL_TX, SERIAL_RX);

void led_thread(void const *argument)
{
    while (true) {
        Thread::signal_wait(0x1);
        //Serialのメソッドではなくstdioのprintf()を使う。
        printf("in led_thread: %f\r\n", timer.read());
    }
}

void toggle_pin()
{
    led = !led;
    //ISR内でprintf()は使わないこと。
    //printf("in toggle_pin() isr: %f\r\n", timer.read());
}

int main()
{
    Thread thread(led_thread);
    timer.start();
    toggle_pin_ticker.attach(&toggle_pin, TICKER_PERIOD);
    while (true) {
        Thread::wait(1000);
        thread.signal_set(0x1);
    }
}

2016年5月19日木曜日

Nucleo F401RE(mbed)でRotary Encoderとタクトスイッチのチャタリング対策

ブレッドボード図

main.cpp

/*
 * Rotary Encoderとタクトスイッチのトグル動作のテスト
 *
 * 2016.05.19
 *
 */
#include "mbed.h"
#include "PinDetect.h"

Serial pc(SERIAL_TX, SERIAL_RX);  

PinDetect pin1(D2, PullUp);
BusIn re(A1, A0);

DigitalOut led1(D8);
BusOut leds(D9, D10, D11);

void keyPressed1()
{
    led1 = !led1;
}

//-------------------------------------------------
// ロータリーエンコーダの読み取り (Alps@akizuki)
// return: ロータリーエンコーダーの回転方向
//         0:変化なし 1:時計回り -1:反時計回り
//
int readRE()
{
    static uint8_t index;
    int retval = 0;

    index = (index << 2) | re;
    //pc.printf("%d\r\n", index);
    index &= 0b1111;

    switch (index) {
    case 0b1101:
        retval = 1;
        break;
    case 0b1000:
        retval = -1;
        break;
    }
    return retval;
}

int main()
{
    int cnt = 0;
    
    pc.printf("Nucleo Rotary Encoder & Toggle SW.\r\n");
    
    pin1.attach_asserted(&keyPressed1);
    pin1.setAssertValue(0);
    pin1.setSampleFrequency();
    
    re.mode(PullUp);
    
    while(1) {
        cnt += readRE();
        leds = cnt & 0x07;
        pc.printf("cnt:\t%d\r\n", cnt);
    }
}
    

動作としては、タクトスイッチを押すと緑色のLEDがトグル動作で点灯する。ロータリーエンコーダを回すと3bit値で赤色LEDが点灯。

チャタリング対策には「PinDetect」というライブラリを使った。ロータリーエンコーダのライブラリもあったがすぐには動かせなかったのでベタ書きにした。←実際使うときはクラス化したほうがよさそう。

C++だとデバイスも普通の変数みたいに書けるので楽だけど、変数名に規則性を持たせるとかしないと何やってるかわかりにくいかな?

Nucleo F401RE(mbed)をMasterにしてPSoC4とSPI通信する。

ArduinoのSPIライブラリではSlaveになれないようなのでPSoC 4 Pioneer KitをSPI Slaveにして通信させてみた。

配線図

PSoC 4 Pioneer Kitのボード上のジャンパを3.3V駆動に設定して、信号レベルを両方とも3.3Vにした。

Nucleo F401RE(SPI Master)


SPI MasterのNucleo F401REはmbedのオンライン・コンパイラで、SPIクラスと割り込み処理にTickerクラスを使った。

mbedのmain.cpp


#include "mbed.h"

#define SPIM_TICKER_PERIOD  (0.01)
#define SPIM_RATE           (8000000)   // 8MHz
#define SPI_PACKET_HEADER   (0x55)

// ピン・アサイン
#define PSOC4_DCO_CS    PB_6

SPI spim(SPI_MOSI, SPI_MISO, SPI_SCK);
Ticker spim_write_ticker;

DigitalOut PSoC4_DCO_CS(PSOC4_DCO_CS);

void spim_tx() {
    static uint8_t i = 0;
    
    PSoC4_DCO_CS = 0;
    spim.write(SPI_PACKET_HEADER);
    spim.write(i);
    spim.write(i);
    spim.write(i);
    spim.write(i);
    wait_us(1);
    PSoC4_DCO_CS = 1;
    
    i++;
}
 
int main() {
    spim.format(8, 0);
    spim.frequency(SPIM_RATE);
    spim_write_ticker.attach(&spim_tx, SPIM_TICKER_PERIOD);
    for (;;) {
        //spim_tx();
        //wait(0.1);
    }
}
 

spim_tx()が割り込み処理ルーチンで、SPI Masterから送信している。
wait_us(1);
とPSoC4_DCO_CSをHにする前にウェイトをかけているのは、タイミングなどの条件によってSCKの送信が完了する前にCSがHになってしまう場合があったから。

また、mbedオフィシャルのTickerのページ

No blocking code in ISR
In ISR you should avoid any call to wait, infinitive while loop, or blocking calls in general.
No printf, malloc, or new in ISR
In ISR you should avoid any call to bulky library functions. In particular, certain library functions (like printf, malloc and new) are non re-entrant and their behaviour could be corrupted when called from an ISR.
RTOS Timer
Consider using the mbed RTOS Timer instead of a Ticker. In this way your periodic function will not be executed in a ISR, giving you more freedom and safety in your code.

とあるので、ほんとはwaitをかけるのは良くないのかもしれないし、SPIのコマンドを発行するのも良くないのかもしれない。RTOSクラスのTimerを使え、と書いてあるのでそうしするつもり。ただし、RTOS Timerの間隔指定はミリ秒単位のようだ(クロックで言うと1kHz)。

SPIの通信状態



ch1:MOSI ch2:SCK


ch1:CS ch2:SCK

mbedのプログラムでSPIのクロックは8MHzを指定しているが、実際は5MHz程度になっている。いくつか値を試してみたが設定通りのクロックになることはまずなかった。タイミングがシビアなときには確認が必要だと思う。

PSoC 4 Pioneer Kit(SPI Slave)


PSoCのTop Design

PWMコンポーネントは受け取った値でPulse Widthを変えて様子を見るために使っている。Master側のSPIの送信速度が速くなるとUARTでは処理落ちしてしまうため。

main.c


#define UART_TRACE  (1)

#include <project.h>
#include <stdio.h>
#define RX_PACKET_SIZE      (5)
#define RX_PACKET_HEADER    (0x55)

int main()
{
    uint8_t rxBuffer[RX_PACKET_SIZE];
    char strBuffer[100];
    int i;
    
    CyGlobalIntEnable; /* Enable global interrupts. */
    UART_Start();
    UART_UartPutString("PSoC 4 SPI Slave Test\r\n");
    
    SPIS_Start();
    PWM_Start();

    for(;;)
    {
        // Check Packet Header
        if (RX_PACKET_SIZE <= SPIS_SpiUartGetRxBufferSize()) {
            rxBuffer[0] = SPIS_SpiUartReadRxData();
            if (RX_PACKET_HEADER != rxBuffer[0]) {
                break;
            }
            for (i = 1; i < RX_PACKET_SIZE; i++) {
                rxBuffer[i] = SPIS_SpiUartReadRxData();
            }
            PWM_WriteCompare(rxBuffer[1]);
#if(UART_TRACE)
            sprintf(strBuffer, "%d\t%d\t%d\t%d\t%d\r\n",
                rxBuffer[0], rxBuffer[1], rxBuffer[2], rxBuffer[3], rxBuffer[4]);
            UART_UartPutString(strBuffer);
#endif     
        }
    }
}

SPIのパケットの先頭byteのヘッダ(0x55)を見て、パケットごとに処理するようにしている。

Github:
https://github.com/ryood/PSoC4_SPI_Slave_Test/tree/master/PSoC(Slave)/PSoC4_SPI_Slave_Test/PSoC4_SPI_Slave_Test_for_NucleoF401RE.cydsn

2016年5月18日水曜日

Nucleo F401RE(mbed)でNokia5110を動かす。

NucleoF401REでNokiaのSPI LCDのLCD5110を動作させてみた。

ブレッドボード図


5V->3.3Vのレベル変換には74HC4050を使った。(参考「ロジックICでSPIの5V->3.3Vレベルシフト Nokia LCD5110を動かす」)

<追記:2016.05.19>

Nucleoは3.3V駆動のようで5V->3.3Vのレベル変換は不要でした。3.3VのLCDに直結して大丈夫です。

</追記>

mbedのオンライン・コンパイラで、「5110_Display」というプログラムを見つけてインポートして使った。

main.cppのN_5110_Displayクラスのコンストラクタで、引数をブレッドボード図の接続の通りNucleoのPin名(Arduino互換名)に変更。

//N_5110_Display Display( p5, p6, p7, p8, p9, p10 );
N_5110_Display Display( D11, NC, D13, D10, D9, D8 );

また、cmsis.hが見つからないというエラーが出たが、mbedライブラリをアップデートしたらコンパイルできた。


プログラムワークスペースで「mbed」の上で右クリック。

メモ:


このN_5110_DisplayクラスはmbedのSPIを使っているようだ。

無償のSTM32用のIDEに「CooCox」意外にも「AC6 SW4STM32」というのがあるようだ。

ARM Cortex-M4でIOピンも多いし安いし(\1,500@秋月)シーケンサーはArduinoからNucleoに移行しようかな…

ToDo:


タイマー割り込み、PSoCとSPI通信、ロータリーエンコーダー、タクトスイッチのチャタリング対策

2016年5月17日火曜日

Arduino UnoをシーケンサーにしてPSoC 4 DCOを制御する。

音が出たので一旦まとめ。

配線図

シーケンサーの表示

Propellerhead ReasonのMatrixというシーケンサーを参考にした。

音出し

Tie(二つの音符の音程をなめらかにつなぐ)を使ってうにょうにょさせてみた。

シーケンサーとオシレーターだけなので情けないですが(^q^;

Arduino Sequencer
https://github.com/ryood/Arduino_Sequencer/tree/master/Arduino_Sequencer_Nokia5110

PSoC 4 DCO
https://github.com/ryood/PSoC4_DCO/tree/master/PSoC/PSoC4_DCO_for_Arduino_Sequencer_Nokia5110.cydsn

メモ:

PSoC 4のDCOの処理が追い付いていないのか、音が飛ぶときある。DCOをもう少し低スぺなプログラムにするかPSoC 5LPにするか。

シーケンサーのSPIにArduinoで作ったDCFとVCA用のDACをぶら下げる予定なので、Arduino UnoだとUI(ロータリーエンコーダやタクトスイッチ)のピン数が足りない。UI系だけ別にしてこれもSPIでつなげようか。

aitendoのあちゃんでいいのを秋月のFT232RL USBシリアル変換モジュールで使う。

配線図


接続表


FT232RLキットのジャンパー
J1: 2-3ショート VIO VCCから供給(5V)
J2: 1-2ショート USBバスからVCCに5V供給

メモ:

Arduino Pro Mini 328 3.3Vの場合とほとんど同じ。ただしCTS#はなし。

aitendoの福袋2016に入っていたPL2303を使ったTTL-USB変換キットはDTRピンがなくてArduino IDEから認識されなかった。

2016年5月15日日曜日

PSoC 4 Prototyping Kitを外部クロックで動かす。

動作クロックの精度を上げるために、PSoC 4 Prototyping Kitを外部クロックで動作させてみた。

ATMega(88V、 328P)やPSoC 5LP Prototyping Kitは水晶振動子を外付けすればよかったが、PSoC4はクロック信号を与えないといけないようだ。P0_6にクロックを入力する。

24MHzの水晶発信器を使う


ブレッドボード図


Top Design

TopDesignに配置したコンポーネントはPSoC 4 Prototyping Kitのお約束のBootloadble(参考「PSoC 4 Pioneer Kit用のProjectをPSoC 4 Prototyping Kitで使う方法 (PSoC Creator 3.3)」)とCPUの駆動クロックのSYSCLKを出力するPINだ。

PSoC 4ではクロックを直接Output Pinに繋げないのでこうしている(参考「PSoC4のClockとTimerを調べる

クロックの設定は*.cydwrのクロック・タブで以下のように設定。


EXTCLKが24MHzでそのままSYSCLKにつながる。

PSoC4の場合PSoC 5LPとは違ってPLLで逓倍して外部クロックより高い周波数を使う、ということはできないようだ。

「PLL1_Sel」とかPLLが使えそうに思えるがよくわからない。そもそもPLLが何かということもよくわかっていない(^q^;

main.c

#include <project.h>

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

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

    for(;;)
    {
        /* Place your application code here. */
        // タン・タ・タンのLチカ
        Pin_LED_Write(1u);
        CyDelay(100);
        Pin_LED_Write(0u);
        CyDelay(500);
        Pin_LED_Write(1u);
        CyDelay(100);
        Pin_LED_Write(0u);
        CyDelay(100);
        Pin_LED_Write(1u);
        CyDelay(100);
        Pin_LED_Write(0u);
        CyDelay(500);
    }
}


PSoC4 Prototyping KitはプログラミングするときにUserボタンを押しながらUSBにつないで、青色LEDがチカチカすると「書き込みモードでつながりました」という合図になるので、普通に書き込んだプログラムが動作している状態を表すために「タン・タ・タン」というタイミングで青色LEDがチカチカするようにしてみた。

どっちのモードでつながってるかはまあまあわかりやすいと思う(^q^;

クロック出力(PSoC4の内蔵クロックで動作させた場合)



ch1:24MHzの水晶発信器の出力 ch2:PSoC4のSYSCLK

クロック出力(水晶発信器のクロックで動作させた場合)



ch1:24MHzの水晶発信器の出力 ch2:PSoC4のSYSCLK

外部クリスタルをEXTCLKとして使うと、きっちり同期して24MHzで動作しているようだ。

12MHzの水晶とロジックICを使って発振回路を作ってみる


74HCU04(アンバッファ・インバータ)と水晶振動子を使って発振回路を作ってPSoC4に入力してみた

ブレッドボード図


水晶からGNDに落としているCは22pF、水晶と並列に入れているRは1MΩにした。(ブレッドボード図のカラーコードは直し忘れました)

クロックの出力

ch1:12MHzの水晶回路の出力 ch2:PSoC4のSYSCLK

PSoCのプログラムは変更していないが、12MHzで動作しているようだ。


48MHzで動作させる


PSoC4の最大駆動周波数は48MHzなので48MHzの水晶発振器に差し替えればすぐに動くかと思ったが、そうでもなかった。

1) ブレッドボードからクロック源(水晶発振器等)を取り去って、PSoC4を内蔵クロックで48MHz駆動にするように設定して動作確認。

2) 外部クロックの48MHzを使うように設定してプログラム。

3) USBを引っこ抜く

4) 48MHzの発振器をブレッドボード上に設置してPSoC4のP0_6に接続

5) 全体に電源を供給

という手順でやっと動いてくれた。



ch1:P0_6(ext_clkの入力) ch2:P0_0(SYSCLKの出力)


メモ:


DIP28PinのARM Cortex-M0のLPCが秋月で値上がりしてディスコンになるかもしれないという情報。

mbedは使えないけどPSoC4 Prototyping KitもCortex-M0で、まだ600円@秋月。

水晶発振器を間違えて上下逆刺ししたらめちゃくちゃ発熱した(@@; しばらく触れないぐらい。生きてただけありがたい