PSoCカスタムコンポーネントにDMAを実装する
Implement DMA on PSoC custom components
PSoCの標準DMAコンポーネントとやりとりする手段を用意さえすれば、カスタムコンポーネントでDMAによるデータ転送を実装することができます。
標準DMAコンポーネントに自身の仕様を伝えるためには、「DMA Capability」というXMLで構成された宣言ファイルを作成して、コンポーネントに追加する必要があります。このファイルを作成するには、「Add Component Item」より「DMA Capability」を選択します。
pa_in.cydmacap
<?xml version="1.0" encoding="us-ascii"?>
<DMACapability>
<Category name="pa_in_fifo"
enabled="true"
bytes_in_burst="1"
bytes_in_burst_is_strict="true"
spoke_width="1"
inc_addr="false"
each_burst_req_request="true">
<Location name="`$INSTANCE_NAME`_DMA_PTR" enabled="true" direction="source"/>
</Category>
</DMACapability>
値 | 内容 |
Categoryタグ | パラメーターを格納する。複数記述すれば、DMAコンポーネントで個別の値を適用させることができるようになる。 |
name | カテゴリーを識別するために使う名称 |
enabled | このカテゴリーを利用可能にする |
bytes_in_burst | 一度(バースト)に転送するメモリーサイズ(byte単位) |
bytes_in_burst_is_strict | bytes_in_burstの値を強制し、ユーザーにオプション指定をできないようにさせる。 |
spoke_width | レジスタのサイズ(byte単位)。例えば標準FFタイマーコンポーネントは16bitサイズなので"2"が適用されている |
inc_addr | 転送ごとにアドレスの値を増やしていくか |
each_burst_req_request | バースト転送ごとにDMAの要求を行うか。falseなら初回だけ要求を出し、あとは自動で転送が行われる。 |
Locationタグ | 転送元/転送先の定義を記述 |
name | 対象のアドレス名。APIヘッダファイルで定義したものを使う。 |
enabled | このアドレスを使用可能にする |
direction | アドレスが転送元(Source)か転送先(Destination)か、あるいは両方(Both)か |
並列入力のサンプルではレジストが8bitなので、上記コードの値を使用しています。
APIヘッダーではFIFOレジスタのアドレスポインタを取得するマクロを定義します。
pa_in.h
#ifndef __`$INSTANCE_NAME`_H__
#define __`$INSTANCE_NAME`_H__
#define `$INSTANCE_NAME`_DMA_PTR ((reg8*)`$INSTANCE_NAME`_dp__F0_REG)
#endif
トップデザインの編集ではカスタムコンポーネントとInterruptコンポーネントの間にDMAコンポーネントを挟みます。これにより、DMAコンポーネントによる転送が完了するとNRQ信号が発生し、割り込みが実行されます。
DRQ(DMAリクエスト)端子を表示するにはDMAコンポーネントの「Hardware Request」を「Disable」以外にします。「Hardware Termination」を有効にすると、DMA転送の中止信号を受け取るための端子が表示されます。
「Hardware Request」のオプションには「Derived」「Rising Edge」「Level」の3つがあります。イベントなど不定期に転送が実行される場合は「Rising Edge」を、I2Sのように継続的に転送が実行される場合は「Level」を選択するとよいでしょう。「Derived」はPSoCが最適な接続を選びますが、カスタムコンポーネントでは原則として「Rising Edge」が適用されます。
デザインが完了したら次はC言語の記述といくわけですが、PSoC CreatorにはDMAに特化したコード生成ツールが用意されているので、こちらを積極的に使っていきましょう。
まずは、コードを生成する元になるプロジェクトとDMAコンポーネントを選びます。
続いて転送元と転送先を指定します。自作のコンポーネントがリストに無かったり、「Set Manually」で希望する値を指定できないようであれば、対象の
「cydmacap」ファイルが正しい状態にあるかを確認しましょう。
「Number of TDs」の「TD」は「トランザクションディスクリプタ」を意味し、ここに転送情報が格納されます。これを複数個作成し、それぞれを連結することでDMAチェーンを作成し、ダブルバッファのように扱うことができます。
「Loop」を選択すると、転送アドレスが末端に到達すると先頭アドレスへと自動で戻り、転送が続行されます。「Single Chain」であれば、末端に到達すると転送が終了します。
プログラム実行中にどのTDにデータが転送されているかを知るには「CyDmaChStatus」を使用します(第2引数に現在のTD番号が代入される)。
最後に個々のTDの設定を行います。
TD# | TDの番号 |
Endian | エンディアンの変換(バイトスワップ) |
Enable trq | trq信号の受信による転送中止の許可 |
Enable nrq | 転送完了時にnrq信号を送信するかどうか |
Length | DMAが転送するバイトサイズ |
Source | 転送元アドレス |
Inc | 転送元アドレスの値を増やしていくかどうか |
Destination | 転送先アドレス |
Inc | 転送先アドレスの値を増やしていくかどうか |
Auto Next | プログラムの指示なしに自動で次のTDを実行するか |
Next TD | 次に連結するTDの番号。最後であれば「End」を選択 |
これでDMAの初期化コードが生成されるので、メインコードにこの文章を貼り付けます。ただし、define文や永続的に使用する変数がごっちゃになっているので、適切に振り分ける必要があります。
dma_init.c
/* Defines for pa_in_dma */
#define pa_in_dma_BYTES_PER_BURST 1
#define pa_in_dma_REQUEST_PER_BURST 1
#define pa_in_dma_SRC_BASE (CYDEV_PERIPH_BASE)
#define pa_in_dma_DST_BASE (CYDEV_SRAM_BASE)
/* Variable declarations for pa_in_dma */
/* Move these variable declarations to the top of the function */
uint8 pa_in_dma_Chan;
uint8 pa_in_dma_TD[1];
uint8 res_buffer[32];
void InitDma()
{
/* DMA Configuration for pa_in_dma */
pa_in_dma_Chan = pa_in_dma_DmaInitialize(pa_in_dma_BYTES_PER_BURST, pa_in_dma_REQUEST_PER_BURST,
HI16(pa_in_dma_SRC_BASE), HI16(pa_in_dma_DST_BASE));
pa_in_dma_TD[0] = CyDmaTdAllocate();
CyDmaTdSetConfiguration(pa_in_dma_TD[0], 32, pa_in_dma_TD[0], pa_in_dma__TD_TERMOUT_EN | CY_DMA_TD_INC_DST_ADR);
CyDmaTdSetAddress(pa_in_dma_TD[0], LO16((uint32)pa_in_DMA_PTR), LO16((uint32)res_buffer));
CyDmaChSetInitialTd(pa_in_dma_Chan, pa_in_dma_TD[0]);
CyDmaChEnable(pa_in_dma_Chan, 1);
}
あとはInterruptコンポーネントの初期化関数を追加すれば完成です。表示される内容は以前のサンプルと同じですが、32バイト単位で一度に表示されていきます。
main.c
CY_ISR(OnIntrupptPaInDma)
{
char str[10];
for(int n = 0; n < 32; n++){
sprintf(str, "%x ", res_buffer[n]);
UART_PutString(str);
}
}
int main(void)
{
CyGlobalIntEnable;
isr_dma_StartEx(OnIntrupptPaInDma);
InitDma();
UART_Start();
for(;;)
{
}
}
2019/06/18