2016年5月19日木曜日

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