2017年7月24日月曜日

PSoC 5LP内蔵DACとMCP4922の処理能力を見てみる。


ブレッドボード配線図

デバッグ情報表示用にキャラクタ液晶を接続している。P2[6:0]。

80MHz駆動するために、20MHzの水晶と22pFのCを接続。水晶をつなぐPinは固定で、P15[0]、P15[1]。

MCP4922はSPIの信号線を割り当てた。SCK P12[0]、MOSI P12[2]、CS P12[3]。

処理タイミングを計測するPinを割り当てた。P0[0]。

内蔵DACの出力はP3[0]。

内蔵DAC(VDAC8)


TopDesign

PSoC 5LPの内蔵DACはVDAC8というコンポーネントを使う。8bit精度。VDAC8の出力は弱いのでOPAMP(Follower)で増強する。

<main.c>

/* ========================================
 *
 * Copyright YOUR COMPANY, THE YEAR
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF your company.
 *
 * ========================================
*/
#include "project.h"
#include <stdio.h>
#include <math.h>

#define TITLE_STR1  ("VDAC Test")
#define TITLE_STR2  ("20170723")

#define PI        (3.141592653589793238462)
#define AMPLITUDE (1.0)    // x * 3.3V
#define PHASE     (PI * 1) // 2*pi is one period
#define RANGE     (0x7FFF)
#define OFFSET    (0x7FFF)

// Configuration for wave output
#define BUFFER_SIZE (360)
uint16_t buffer_sine[BUFFER_SIZE];

char strBuffer[80];
    
// Create the wave buffer
void calculate_sinewave(void){
    for (int i = 0; i < BUFFER_SIZE; i++) {
        double rads = (PI * i)/180.0; // Convert degree in radian
        buffer_sine[i] = (uint16_t)(AMPLITUDE * (RANGE * (sin(rads + PHASE))) + OFFSET);
    }
}

int main(void)
{
    uint8_t cnt = 0;
    
    CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    LCD_Char_Start();
    UART_Start();
    VDAC8_1_Start();
    Opamp_1_Start();
    
    LCD_Char_PrintString(TITLE_STR1);
    LCD_Char_Position(1, 0);
    LCD_Char_PrintString(TITLE_STR2);
    
    sprintf(strBuffer, "\r\n%s %s\r\n", TITLE_STR1, TITLE_STR2);
    UART_PutString(strBuffer);

    calculate_sinewave();
    
    for(;;)
    {
        /* Place your application code here. */
        for (int i = 0; i < BUFFER_SIZE; i++) {
            Pin_Check1_Write(1);
            VDAC8_1_SetValue(buffer_sine[i] >> 8);
            Pin_Check1_Write(0);
        }
        
        cnt++;
    }
}

/* [] END OF FILE */

プログラムはNucleo F303で試したものとほぼおなじ処理で、事前にサイン波テーブルを生成しておいて、値を連続してDACに出力している。

DACに出力する前後でPin_Check1をH/Lさせているので、Highの時間を見れば処理に要した時間がわかる。

浮動小数点演算を行う場合、math libraryをリンクする必要がある。

メニューのProjectから[Build Setting...]を選択。ダイアログでコマンドラインオプションに「-lm」を設定する。


MCP4922


TopDesign

<main.c>

/* ========================================
 *
 * Copyright YOUR COMPANY, THE YEAR
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF your company.
 *
 * ========================================
*/
#include "project.h"
#include <stdio.h>
#include <math.h>

#define TITLE_STR1  ("MCP4922 Test")
#define TITLE_STR2  ("20170723")

#define PI        (3.141592653589793238462)
#define AMPLITUDE (1.0)    // x * 3.3V
#define PHASE     (PI * 1) // 2*pi is one period
#define RANGE     (0x7FFF)
#define OFFSET    (0x7FFF)

// Configuration for wave output
#define BUFFER_SIZE (360)
uint16_t buffer_sine[BUFFER_SIZE];

char strBuffer[80];
   
// Create the wave buffer
void calculate_sinewave(void){
    for (int i = 0; i < BUFFER_SIZE; i++) {
        double rads = (PI * i)/180.0; // Convert degree in radian
        buffer_sine[i] = (uint16_t)(AMPLITUDE * (RANGE * (sin(rads + PHASE))) + OFFSET);
    }
}

// Write to MCP4922
// v: 0..4096
void MCP4922_write_u16(uint16 v)
{
    SPIM_WriteTxData((v >> 8) | 0x30);
    SPIM_WriteTxData(v & 0xff);
    while (! (SPIM_ReadTxStatus() & SPIM_STS_SPI_DONE))
        ;
}

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

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    LCD_Start();
    SPIM_Start();
    UART_Start();
   
    LCD_Position(0u,0u);
    LCD_PrintString(TITLE_STR1);
    LCD_Position(1u,0u);
    LCD_PrintString(TITLE_STR2);
   
    sprintf(strBuffer, "%s %s \r\n", TITLE_STR1, TITLE_STR2);
    UART_PutString(strBuffer);
   
    CyDelay(1000u);
    //LCD_ClearDisplay();
   
    calculate_sinewave();
   
    /*
    for (int i = 0; i < BUFFER_SIZE; i++) {
        sprintf(strBuffer, "%u\r\n", buffer_sine[i]);
        UART_PutString(strBuffer);
    }  
    UART_PutString("Dump end\r\n");
    */

    for(;;)
    {
        for (int i = 0; i < BUFFER_SIZE; i++) {
            Pin_Check1_Write(1);
            MCP4922_write_u16(buffer_sine[i] >> 4);
            Pin_Check1_Write(0);
        }
        //CyDelay(1);
    }
}

/* [] END OF FILE */

処理時間の比較


VDAC8

ch2:Pin_Check1の出力

周波数は1.702MHzで1ループは約587.5ns。
DAC出力の処理時間は350.0ns

MCP4922

ch2:Pin_Check1の出力

周波数は491.0kHzで1ループは約2037ns。
DAC出力の処理時間は1800ns

DAC LOOP(ns) DAC(ns) LOOP-DAC
VDAC8 587.5 350.0 237.5
MCP4922 2037 1800 237

PSoC 5LPはCoretex-M3でCortex-M4のNucleo F446と比較すると、
Nucleo F446の内蔵DAC(12bit精度)は700nsだったので、PSoC 5LPの内蔵DACは8bit精度ではあるが処理が速い。

SPI接続のMCP4922はNucleo F446でSPIクロックが22.44MHzのとき6.74us(6740ns)だったので、こちらもかなり速い。

PSoC Creatorはグラフィカルで使い易いが、ネイティブなので高速なコードを生成してくれるようだ。

出力波形

VDAC8

サイン波の周波数:4.726kHz

MCP4922

サイン波の周波数:1.336kHz

WaveSpectraで測定

VDAC8

MCP4922

周波数が違うので単純に比較はできないが、VDAC8は高調波歪がはっきり出ている。

MCP4922のSPI信号


SCK : MOSI

ch1:MOSI ch2:SCK

SPI Masterコンポーネントのクロックに40MHzを入れているがSCKは8.676MHzになっている。

SCK : CS

ch1:CS ch2:SCK

CSがアクティブ(L)の区間は目盛りから読み取って約840ns。1周期は約2026nsでPin_Check1で測定した周期とほぼ同じ。

Github:
VDAC8
https://github.com/ryood/PSoC-Creator-4.1-Test/tree/master/VDAC_Test.cydsn

MCP4922
https://github.com/ryood/PSoC-Creator-4.1-Test/tree/master/MCP4922_Test.cydsn

<追記:2017.07.26>

SCKを拡大して測定し直したら、20MHzだった。


ch1:SCK ch2:CS

8.676MHzになっている画像も、SCKは200ns×4マス=800nsで16クロックなので

800ns / 16clk = 50ns
1 / 50ns = 20MHz

になり、たまたま少ない周波数が表示されていただけだと思う。

また、SPI Masterコンポーネントに「Enable High Speed Mode」というプロパティがあり、これをチェックしない場合最大クロック9MHz、チェックすると最大クロック18MHzまで対応する。
←したがって、SCK 20MHzは仕様外。

</追記>

0 件のコメント:

コメントを投稿