2016/12/20

[PSoC Advent Calendar 2016 23日目] PSoC 3/5LPでDMA

PSoC 3/5LPには24チャンネルのDMAが搭載されていますが、それをSPI接続センサのデータ受信に使ってみました。
DMAに関する情報はアプリケーションノート
にありますが、今回はSPI通信への応用です。
PSoC 3/5LPでのDMAを利用したSPI通信についての情報は以前はExample Project EP562734で、現在はCode Example CE95376で提供されています。 
作製しているデータロガーHPA_Naviシリーズでは100spsでSPI接続の加速度、ジャイロ、地磁気、気圧センサの情報を取得していますが、この部分の処理に時間がかかってしまうと通信等の他の処理に影響が出てしまいます。
試作時にはSPIMモジュールのSPIM_PutArray関数を利用して通信を行っていましたが、ロジアナで確認した限りではバイト間に隙間ができるようで、フルスピードでの通信ができていない様子が確認できました。
そこで、DMAを用いたSPI通信の高速化を行いました。
 モジュールの配置は以下の図のようになります。
SPIモジュールとDMAの配置。3つのセンサを用いるのでChip Select信号とDMAの切り替えを行っている。
プログラムは以下のようになります。
FreeRTOSのタスクとしてSPIセンサの処理部分を実装しています。
計500行程度のプログラムですが、DMAの初期化部分が350行ほどで大部分を占め、センサの初期化部分は50行ほど、メインの処理は100行ほどです。
センサから取得したデータは、拡張Sylphideフォーマットに従って配列pageA, pageMに格納されます。
DMAの転送順を制御することで格納の順序をプログラムで交換する作業を最小限にしています。

はじめに加速度・ジャイロセンサMPU-6000のDMA設定を示します。
//******************************************************************************
//! @brief 6軸センサの送信DMA設定
//******************************************************************************
#define DMA_TX_DOF_BYTES_PER_BURST       (1)
#define DMA_TX_DOF_REQUEST_PER_BURST     (1)
#define DMA_TX_DOF_SRC_BASE              (DOF_txBuffer)
#define DMA_TX_DOF_DST_BASE              (CYDEV_PERIPH_BASE)

uint8_t DOF_txBuffer[16] = {0x80 | 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

static void DMA_TX_DOF_Configuration()
{
    uint8_t DOF_TxChannel;
    uint8_t DOF_TxTD[2];

    SPIM_SNS_TX_STATUS_MASK_REG &= (~SPIM_SNS_INT_ON_TX_EMPTY);    //SPIモジュールのTX_FIFO_EMPTY割り込みを止める

    DOF_txBuffer[15] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
        
    DOF_TxChannel = DMA_TX_DOF_DmaInitialize(DMA_TX_DOF_BYTES_PER_BURST, DMA_TX_DOF_REQUEST_PER_BURST, 
                                        HI16(DMA_TX_DOF_SRC_BASE), HI16(DMA_TX_DOF_DST_BASE));

    DOF_TxTD[0] = CyDmaTdAllocate();
    DOF_TxTD[1] = CyDmaTdAllocate();

    CyDmaTdSetConfiguration(DOF_TxTD[0], 15, DOF_TxTD[1], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(DOF_TxTD[1], 1, DOF_TxTD[0], 0);
    
    CyDmaTdSetAddress(DOF_TxTD[0], LO16((uint32_t)DOF_txBuffer), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(DOF_TxTD[1], LO16((uint32_t)(DOF_txBuffer + 15)), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));

    CyDmaChSetInitialTd(DOF_TxChannel, DOF_TxTD[0]);
    
    CyDmaChEnable(DOF_TxChannel, 1);
}

//******************************************************************************
//! @brief 6軸センサの受信DMA設定
//******************************************************************************
#define DMA_RX_DOF_BYTES_PER_BURST       (1)
#define DMA_RX_DOF_REQUEST_PER_BURST     (1)
#define DMA_RX_DOF_SRC_BASE              (CYDEV_PERIPH_BASE)
#define DMA_RX_DOF_DST_BASE              (pageA)

static void DMA_RX_DOF_Configuration()
{
    uint8_t DOF_RxChannel;
    uint8_t DOF_RxTD[15];
    uint8_t i;

    DOF_RxChannel = DMA_RX_DOF_DmaInitialize(DMA_RX_DOF_BYTES_PER_BURST, DMA_RX_DOF_REQUEST_PER_BURST,
                                     HI16(DMA_RX_DOF_SRC_BASE), HI16(DMA_RX_DOF_DST_BASE));

    for (i = 0; i < 15; i++) {
        DOF_RxTD[i] = CyDmaTdAllocate();
    }

    for (i = 0; i < 14; i ++) {
        CyDmaTdSetConfiguration(DOF_RxTD[i], 1, DOF_RxTD[i + 1], TD_INC_DST_ADR);
    }
    CyDmaTdSetConfiguration(DOF_RxTD[14], 1, DOF_RxTD[0], TD_INC_DST_ADR);

    CyDmaTdSetAddress(DOF_RxTD[0], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
    CyDmaTdSetAddress(DOF_RxTD[1], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+7));    //ACC
    CyDmaTdSetAddress(DOF_RxTD[2], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+8));
    CyDmaTdSetAddress(DOF_RxTD[3], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+10));    //
    CyDmaTdSetAddress(DOF_RxTD[4], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+11));
    CyDmaTdSetAddress(DOF_RxTD[5], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+13));    //
    CyDmaTdSetAddress(DOF_RxTD[6], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+14));
    CyDmaTdSetAddress(DOF_RxTD[7], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+31));    //TEMP
    CyDmaTdSetAddress(DOF_RxTD[8], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+30));
    CyDmaTdSetAddress(DOF_RxTD[9], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+16));    //GYRO
    CyDmaTdSetAddress(DOF_RxTD[10], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+17));
    CyDmaTdSetAddress(DOF_RxTD[11], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+19));    //
    CyDmaTdSetAddress(DOF_RxTD[12], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+20));
    CyDmaTdSetAddress(DOF_RxTD[13], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+22));    //
    CyDmaTdSetAddress(DOF_RxTD[14], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)pageA+23));

    CyDmaChSetInitialTd(DOF_RxChannel, DOF_RxTD[0]);

    CyDmaChEnable(DOF_RxChannel, 1);
}
基本的には、PSoC CreatorのDMAウィザードで生成した設定を雛型として使いますが、必要な分の送信を行った後にSPI送信をストップするために、送信データの最後でSPIMモジュールに割り込み禁止の設定を送っています。
また、受信側では最初のバイトにゴミが入るのでそれをダミーバッファに送り込む、Sylphideフォーマットに合わせて転送順を変えるなどの処理を行っています。

次に地磁気センサHMC-5983のDMA設定を示します。
//******************************************************************************
//! @brief 地磁気センサの送信DMA設定
//******************************************************************************
#define DMA_TX_MAG_BYTES_PER_BURST       (1)
#define DMA_TX_MAG_REQUEST_PER_BURST     (1)
#define DMA_TX_MAG_SRC_BASE              (MAG_txBuffer)
#define DMA_TX_MAG_DST_BASE              (CYDEV_PERIPH_BASE)

uint8_t MAG_txBuffer[11] = {HMC5983_SPI_Read | HMC5983_SPI_Multiple | HMC5983_RA_DATAX_H, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
                            HMC5983_RA_MODE, HMC5983_MODE_SINGLE, 0x00};

static void DMA_TX_MAG_Configuration()
{
    uint8_t MAG_TxChannel;
    uint8_t MAG_TxTD[4];

    SPIM_SNS_TX_STATUS_MASK_REG &= (~SPIM_SNS_INT_ON_TX_EMPTY);    //SPIモジュールのTX_FIFO_EMPTY割り込みを止める

    MAG_txBuffer[7] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
    MAG_txBuffer[10] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
    
    MAG_TxChannel = DMA_TX_MAG_DmaInitialize(DMA_TX_MAG_BYTES_PER_BURST, DMA_TX_MAG_REQUEST_PER_BURST, 
                                        HI16(DMA_TX_MAG_SRC_BASE), HI16(DMA_TX_MAG_DST_BASE));

    MAG_TxTD[0] = CyDmaTdAllocate();
    MAG_TxTD[1] = CyDmaTdAllocate();
    MAG_TxTD[2] = CyDmaTdAllocate();
    MAG_TxTD[3] = CyDmaTdAllocate();

    CyDmaTdSetConfiguration(MAG_TxTD[0], 7, MAG_TxTD[1], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(MAG_TxTD[1], 1, MAG_TxTD[2], DMA_TX_MAG__TD_TERMOUT_EN);
    CyDmaTdSetConfiguration(MAG_TxTD[2], 2, MAG_TxTD[3], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(MAG_TxTD[3], 1, MAG_TxTD[0], DMA_TX_MAG__TD_TERMOUT_EN);
    
    CyDmaTdSetAddress(MAG_TxTD[0], LO16((uint32_t)MAG_txBuffer), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(MAG_TxTD[1], LO16((uint32_t)MAG_txBuffer+7), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));
    CyDmaTdSetAddress(MAG_TxTD[2], LO16((uint32_t)MAG_txBuffer+8), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(MAG_TxTD[3], LO16((uint32_t)MAG_txBuffer+10), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));

    CyDmaChSetInitialTd(MAG_TxChannel, MAG_TxTD[0]);
    
    CyDmaChEnable(MAG_TxChannel, 1);
}

//******************************************************************************
//! @brief 地磁気センサの受信DMA設定
//******************************************************************************
#define DMA_RX_MAG_BYTES_PER_BURST       (1)
#define DMA_RX_MAG_REQUEST_PER_BURST     (1)
#define DMA_RX_MAG_SRC_BASE              (CYDEV_PERIPH_BASE)
#define DMA_RX_MAG_DST_BASE              (pageM)

static void DMA_RX_MAG_Configuration()
{ 
    uint8_t MAG_RxChannel;
    uint8_t MAG_RxTD[36];
    uint8_t i;

    MAG_RxChannel = DMA_RX_MAG_DmaInitialize(DMA_RX_MAG_BYTES_PER_BURST, DMA_RX_MAG_REQUEST_PER_BURST,
                                     HI16(DMA_RX_MAG_SRC_BASE), HI16(DMA_RX_MAG_DST_BASE));
    
    for (i = 0; i < 36; i++) {
        MAG_RxTD[i] = CyDmaTdAllocate();
    }

    for (i = 0; i < 35; i ++) {
        CyDmaTdSetConfiguration(MAG_RxTD[i], 1u, MAG_RxTD[i + 1], TD_INC_DST_ADR);    
    }
    CyDmaTdSetConfiguration(MAG_RxTD[35], 1, MAG_RxTD[0], TD_INC_DST_ADR);

    for (i = 0; i < 4; i ++) {
        CyDmaTdSetAddress(MAG_RxTD[0 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
        CyDmaTdSetAddress(MAG_RxTD[1 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)(pageM + 10 + 6 * i)));    //センサX
        CyDmaTdSetAddress(MAG_RxTD[2 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)(pageM + 11 + 6 * i)));
        CyDmaTdSetAddress(MAG_RxTD[3 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)(pageM + 12 + 6 * i)));    //Z
        CyDmaTdSetAddress(MAG_RxTD[4 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)(pageM + 13 + 6 * i)));
        CyDmaTdSetAddress(MAG_RxTD[5 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)(pageM +  8 + 6 * i)));    //Y
        CyDmaTdSetAddress(MAG_RxTD[6 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)(pageM +  9 + 6 * i)));
        CyDmaTdSetAddress(MAG_RxTD[7 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
        CyDmaTdSetAddress(MAG_RxTD[8 + 9 * i], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
    }

    CyDmaChSetInitialTd(MAG_RxChannel, MAG_RxTD[0]);

    CyDmaChEnable(MAG_RxChannel, 1);
}
こちらも基本的には加速度・ジャイロセンサの場合と同じですが、毎回測定開始命令を送る都合で、送信側で2度SPIの割り込みが止まるようになっています。
また、4回のデータを1つのページとして記録するので、受信側ではデータ転送先の異なる4組のDMA設定を行っています。

最後に気圧センサMS5611のDMA設定を示します。
//******************************************************************************
//! @brief 圧力センサの送信DMA設定
//******************************************************************************
#define DMA_TX_PRS_BYTES_PER_BURST       (1)
#define DMA_TX_PRS_REQUEST_PER_BURST     (1)
#define DMA_TX_PRS_SRC_BASE              (PRS_txBuffer)
#define DMA_TX_PRS_DST_BASE              (CYDEV_PERIPH_BASE)

uint8_t PRS_txBuffer[14] = {MS561101BA_ADC, 0xFF, 0xFF, 0xFF, 0x00,
                            MS561101BA_D2 | MS561101BA_OSR_4096, 0x00,
                            MS561101BA_ADC, 0xFF, 0xFF, 0xFF, 0x00,
                            MS561101BA_D1 | MS561101BA_OSR_4096, 0x00};    //D2が最初。変換コマンドなので。

static void DMA_TX_PRS_Configuration()
{
    uint8_t PRS_TxChannel;
    uint8_t PRS_TxTD[8];
    uint8_t i;
    
    SPIM_SNS_TX_STATUS_MASK_REG &= (~SPIM_SNS_INT_ON_TX_EMPTY);    //SPIモジュールのTX_FIFO_EMPTY割り込みを止める

    PRS_txBuffer[4] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
    PRS_txBuffer[6] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
    PRS_txBuffer[11] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
    PRS_txBuffer[13] = SPIM_SNS_TX_STATUS_MASK_REG;    //割り込みが止まった設定のレジスタをコピー
   
    PRS_TxChannel = DMA_TX_PRS_DmaInitialize(DMA_TX_PRS_BYTES_PER_BURST, DMA_TX_PRS_REQUEST_PER_BURST, 
                                        HI16(DMA_TX_PRS_SRC_BASE), HI16(DMA_TX_PRS_DST_BASE));

    for (i = 0; i < 8; i ++) {
        PRS_TxTD[i] = CyDmaTdAllocate();
    }

    CyDmaTdSetConfiguration(PRS_TxTD[0], 4, PRS_TxTD[1], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(PRS_TxTD[1], 1, PRS_TxTD[2], DMA_TX_PRS__TD_TERMOUT_EN);
    CyDmaTdSetConfiguration(PRS_TxTD[2], 1, PRS_TxTD[3], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(PRS_TxTD[3], 1, PRS_TxTD[4], DMA_TX_PRS__TD_TERMOUT_EN);
    CyDmaTdSetConfiguration(PRS_TxTD[4], 4, PRS_TxTD[5], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(PRS_TxTD[5], 1, PRS_TxTD[6], DMA_TX_PRS__TD_TERMOUT_EN);
    CyDmaTdSetConfiguration(PRS_TxTD[6], 1, PRS_TxTD[7], TD_INC_SRC_ADR);
    CyDmaTdSetConfiguration(PRS_TxTD[7], 1, PRS_TxTD[0], DMA_TX_PRS__TD_TERMOUT_EN);
    
    CyDmaTdSetAddress(PRS_TxTD[0], LO16((uint32_t)PRS_txBuffer), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(PRS_TxTD[1], LO16((uint32_t)(PRS_txBuffer+4)), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));
    CyDmaTdSetAddress(PRS_TxTD[2], LO16((uint32_t)(PRS_txBuffer+5)), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(PRS_TxTD[3], LO16((uint32_t)(PRS_txBuffer+6)), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));
    CyDmaTdSetAddress(PRS_TxTD[4], LO16((uint32_t)(PRS_txBuffer+7)), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(PRS_TxTD[5], LO16((uint32_t)(PRS_txBuffer+11)), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));
    CyDmaTdSetAddress(PRS_TxTD[6], LO16((uint32_t)(PRS_txBuffer+12)), LO16((uint32_t)SPIM_SNS_TXDATA_PTR));
    CyDmaTdSetAddress(PRS_TxTD[7], LO16((uint32_t)(PRS_txBuffer+13)), LO16((uint32_t)&SPIM_SNS_TX_STATUS_MASK_REG));

    CyDmaChSetInitialTd(PRS_TxChannel, PRS_TxTD[0]);
    
    CyDmaChEnable(PRS_TxChannel, 1);
}

//******************************************************************************
//! @brief 圧力センサの受信DMA設定
//******************************************************************************
#define DMA_RX_PRS_BYTES_PER_BURST       (1)
#define DMA_RX_PRS_REQUEST_PER_BURST     (1)
#define DMA_RX_PRS_SRC_BASE              (CYDEV_PERIPH_BASE)
#define DMA_RX_PRS_DST_BASE              (PRS_rxBuffer)

uint32_t PRS_rxBuffer[2];

static void DMA_RX_PRS_Configuration()
{
    uint8_t PRS_RxChannel;
    uint8_t PRS_RxTD[10];
    uint8_t i;

    PRS_RxChannel = DMA_RX_PRS_DmaInitialize(DMA_RX_PRS_BYTES_PER_BURST, DMA_RX_PRS_REQUEST_PER_BURST,
                                     HI16(DMA_RX_PRS_SRC_BASE), HI16(DMA_RX_PRS_DST_BASE));
                                     
    for (i = 0; i < 10; i ++) {
        PRS_RxTD[i] = CyDmaTdAllocate();
    }

    CyDmaTdSetConfiguration(PRS_RxTD[0], 1, PRS_RxTD[1], TD_INC_DST_ADR);
    CyDmaTdSetConfiguration(PRS_RxTD[1], 1, PRS_RxTD[2], 0);
    CyDmaTdSetConfiguration(PRS_RxTD[2], 1, PRS_RxTD[3], 0);
    CyDmaTdSetConfiguration(PRS_RxTD[3], 1, PRS_RxTD[4], 0);
    CyDmaTdSetConfiguration(PRS_RxTD[4], 1, PRS_RxTD[5], TD_INC_DST_ADR);
    CyDmaTdSetConfiguration(PRS_RxTD[5], 1, PRS_RxTD[6], TD_INC_DST_ADR);
    CyDmaTdSetConfiguration(PRS_RxTD[6], 1, PRS_RxTD[7], 0);
    CyDmaTdSetConfiguration(PRS_RxTD[7], 1, PRS_RxTD[8], 0);
    CyDmaTdSetConfiguration(PRS_RxTD[8], 1, PRS_RxTD[9], 0);
    CyDmaTdSetConfiguration(PRS_RxTD[9], 1, PRS_RxTD[0], TD_INC_DST_ADR);

    CyDmaTdSetAddress(PRS_RxTD[0], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
    CyDmaTdSetAddress(PRS_RxTD[1], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)((uint8*)&PRS_rxBuffer[0] + 2)));
    CyDmaTdSetAddress(PRS_RxTD[2], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)((uint8*)&PRS_rxBuffer[0] + 1)));
    CyDmaTdSetAddress(PRS_RxTD[3], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)((uint8*)&PRS_rxBuffer[0])));
    CyDmaTdSetAddress(PRS_RxTD[4], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
    CyDmaTdSetAddress(PRS_RxTD[5], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));
    CyDmaTdSetAddress(PRS_RxTD[6], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)((uint8*)&PRS_rxBuffer[1] + 2)));
    CyDmaTdSetAddress(PRS_RxTD[7], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)((uint8*)&PRS_rxBuffer[1] + 1)));
    CyDmaTdSetAddress(PRS_RxTD[8], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)((uint8*)&PRS_rxBuffer[1])));
    CyDmaTdSetAddress(PRS_RxTD[9], LO16((uint32_t)SPIM_SNS_RXDATA_PTR), LO16((uint32_t)DMY_rxBuffer));

    CyDmaChSetInitialTd(PRS_RxChannel, PRS_RxTD[0]);

    CyDmaChEnable(PRS_RxChannel, 1);
}
こちらも基本的にはこれまでに示したセンサの設定と同じです。
送信側では、温度センサと気圧センサを交互に読み込むため、2種類の変換開始命令に対応して、割り込みストップが4回登場します。
受信側も、温度・気圧センサの2つに対応した2種類の転送先を設定しています。

ここまででDMAの設定が終わりました。
残りはセンサの初期設定部分と10msごとに行うデータ取得部分です。

センサの初期設定には時間がかかっても構わないので、SPIMモジュールのPutArray関数を用いて行います。
//******************************************************************************
//! @brief SPIタスクの初期化関数
//! @memo HMC5983はcontモード+DRDY監視なしよりsingleでデータ取得したほうが取りこぼしが少ない
//! @memo MPU6000: 90 - 100kHz, 0.9 - 1.1MHz, 18 - 22MHz Typ
//! @memo HMC5983: 8 MHz Max
//! @memo MS5611: 20MHz Max
//! @maemo マスタークロックを分周して使う。16 MHzの倍数。
//******************************************************************************
#define SPI_DOF (0)
#define SPI_MAG (1)
#define SPI_PRS (2)

void TaskSPI_init()
{
    uint8_t init_DOF[][2] = {{0x6B, 0x80}, {0x6B, 0x00}, {0x19, 0x09}, {0x1A, 0x03}, {0x1B, 0x00}, {0x1C, 0x00}};
    uint8_t init_MAG[][2] = {{0x00, 0x9C}, {0x01, 0x00}, {0x02, 0x01}};
    uint8_t i;

    Clock_SPI_SetDividerValue(32); // 64 / 32 = 2 MHz SPI 1 Mbps
    SPIM_SNS_Start();
    
    //MPU-6000の初期化
    Control_Reg_SNS_Write(SPI_DOF);     //CS選択
    for (i = 0; i < 6; i ++) {
        SPIM_SNS_PutArray(init_DOF[i], sizeof(init_DOF[i]));
        while(!(SPIM_SNS_ReadTxStatus() & SPIM_SNS_STS_SPI_DONE));
        CyDelay(1);
    }
    
    //HMC5983の初期化
    Control_Reg_SNS_Write(SPI_MAG);     //CS選択
    for (i = 0; i < 3; i ++) {
        SPIM_SNS_PutArray(init_MAG[i], sizeof(init_MAG[i]));
        while(!(SPIM_SNS_ReadTxStatus() & SPIM_SNS_STS_SPI_DONE));
    }
   
    //MS5611の初期化
    Control_Reg_SNS_Write(SPI_PRS);     //CS選択
    MS561101BA_initialize();
    
    MS561101BA_startConversion(MS561101BA_D1 | MS561101BA_OSR_4096); //初回の読み込み用

    //クロック切り替え
    Clock_SPI_SetDividerValue(2); // 64 / 2 = 32 MHz SPI 16 Mbps

    //TX側DMAの初期化
    DMA_TX_DOF_Configuration();
    DMA_TX_MAG_Configuration();
    DMA_TX_PRS_Configuration();
    
    SPIM_SNS_ClearRxBuffer();    //初期化で少しゴミが受信バッファに入るのでそれを消しておく
    
    //RX側DMAの初期化
    DMA_RX_DOF_Configuration();
    DMA_RX_MAG_Configuration();
    DMA_RX_PRS_Configuration();    
}
初期化コマンドを配列に入れて一度にセンサに転送しています。
初期設定終了後、DMAの初期化を行います。

最後に、センサデータ取得部分を示します。
DMAにデータ転送に関する部分をかなり押し付けているので、センサデータ取得部分はDMA開始命令を送り、符号変換等の細かな処理を行うのみです。
取得したデータは、SDカード・USBUART等の処理を含む出力ストリーム関数に送ります。
//******************************************************************************
//! @breif SPI DMAの開始
//******************************************************************************
static void DMAStart( void )
{
    SPIM_SNS_TX_STATUS_MASK_REG |= (SPIM_SNS_INT_ON_TX_EMPTY);    //割り込みを有効にしてDMA転送を開始
    CyDelayUs(1); //CSのアサート前にピンの状態を読んでしまうことの対策
    while(!Status_Reg_CS_Read());
}

//******************************************************************************
//! @brief SPIタスク
//! @memo SPIM_PutArrayではフルスピードが出ない。DMAを使う
//******************************************************************************
void TaskSPI( void )
{
    extern uint32_t itow;
    uint8_t i;
    uint16_t temp;
    uint32_t temp32;
    static uint8_t LoopCount = 0;
    static uint32_t tempTemp = 0, tempPres = 0;
    
    LoopCount ++;

    //pageAログ整形
    pageA[1] = (uint8_t)(xTaskGetTickCount() / 10);    //内部時刻の書き込み
    *(uint32_t*)(pageA + 2) = itow;
    
    //MPU-6000処理
    Control_Reg_SNS_Write(SPI_DOF);    //CS選択
    DMAStart();
    for (i = 0; i < 6; i++) {
        pageA[7 + 3 * i] ^= 0x80;    //符号付きから符号なしへの変換。
    }
    
    //HMC5983処理
    Clock_SPI_SetDividerValue(4); // 64 / 4 = 16 MHz SPI 8 Mbps
    Control_Reg_SNS_Write(SPI_MAG);     //CS選択                
    DMAStart();  //データ読み込み
    DMAStart();  //single modeに入れる
    if ( !(LoopCount % 4) ) {    //4つたまったら処理
        *(pageM + 3) = (uint8_t)(xTaskGetTickCount() / 10);    //内部時刻の書き込み
        *(uint32_t*)(pageM + 4) = itow;
        for (i = 0; i < 4; i ++) {    //Z軸に負の符号をつける
            temp = *((uint16_t *)(pageM + 12 + 6 * i));
            temp = ~CYSWAP_ENDIAN16(temp) + 1;    //マクロなのでtempに退避
            *((uint16_t *)(pageM + 12 + 6 * i)) = CYSWAP_ENDIAN16(temp);    //マクロなのでtempに退避 
        }
        outStream(pageM);
    }

    //MS5611処理
    Clock_SPI_SetDividerValue(2); // 64 / 2 = 32 MHz SPI 16 Mbps
    Control_Reg_SNS_Write(SPI_PRS);     //CS選択
    DMAStart();  //データ読み込み
    DMAStart();  //AD変換開始

    if ( LoopCount % 2 ){
        temp32 = MS561101BA_Pressure(PRS_rxBuffer[0]);
        if ( (tempPres > temp32 && tempPres - temp32 < 10000) || 
        (tempPres < temp32 && temp32 - tempPres < 10000)) {
            pageA[24] = (uint8_t)(temp32 >> 16);
            pageA[25] = (uint8_t)(temp32 >> 8);
            pageA[26] = (uint8_t)temp32;
        }
        tempPres = temp32; 
    }
    else{
        temp32 = MS561101BA_Tempareture(PRS_rxBuffer[1]);
        temp32 += 4000;    //マイナスが出ないようにオフセット。+40 degree C
        if ( (tempTemp > temp32 && tempTemp - temp32 < 1000) || 
        (tempTemp < temp32 && temp32 - tempTemp < 1000)) {
            pageA[27] = (uint8_t)(temp32 >> 16);
            pageA[28] = (uint8_t)(temp32 >> 8);
            pageA[29] = (uint8_t)temp32;
        }
        tempTemp = temp32;
    }    
    
    outStream(pageA);
}
以上のプログラムで、SPI接続センサとの通信にかかる時間を最小限にすることができ、ロジアナでも歯抜けのない通信が確認できました。
ここで生み出した余裕はその他の計算やワイヤレスモジュールとの通信等に使えるようになり、同じロガーでも多くの機能を持たせることができるようになります。
この他にもHPA_Naviでは8チャンネル・16ビットのAD変換データをDMA転送するなどして、できる限りデータ転送にはCPUを使わないような工夫を行っています。