Skip to content

Latest commit

 

History

History
723 lines (585 loc) · 28.7 KB

README-UartTx-ja.md

File metadata and controls

723 lines (585 loc) · 28.7 KB
title tags author slide
PSoC 5LPでUART送信機を再発明した
PSoC5 uart Datapath Verilog dma
noritan_org
false

PSoC 5LPでUART送信機を再発明した

UARTの受信性能を調べるためにUARTの信号を高速に生成する必要が生じました。 ソフトウェアが介在すると遅くなっちゃうので、ハードウェアだけで完結するUART送信機コンポーネントを再発明することにしました。

シンボル

シンボル図

今回作成するコンポーネントは、あくまでも治具としての用途を考えているため、可能な限りシンプルな構成にしました。

まず、同期回路で構成しているので、clockresetは必須です。 このコンポーネントでは、clockで与えられた信号そのものをビットクロックとして使用します。

tx出力は、UARTの信号出力です。 この信号を直接出力端子に接続すると、送信機として使えます。

dreq (Data Request)は、このコンポーネントがデータを受け入れ可能であることを示す信号です。 このコンポーネントは、このコンポーネントが持つレジスタに書き込まれた値をUART信号に変換して出力します。 レジスタにはFIFOを装備しますが、FIFOがいっぱいになっていない事をdreq出力で示しています。

Verilog記述

ハードウェアを表現するためのVerilog記述は、いくつかの部分から構成されています。

module NtanUartTx_v1_0 (
	output  dreq,
	output  tx,
	input   clock,
	input   reset
);

//`#start body` -- edit after this line, do not edit this line

// State code declaration
localparam      ST_IDLE     = 4'b0000;
localparam      ST_START    = 4'b0100;
localparam      ST_SHIFT0   = 4'b1000;
localparam      ST_SHIFT1   = 4'b1001;
localparam      ST_SHIFT2   = 4'b1010;
localparam      ST_SHIFT3   = 4'b1011;
localparam      ST_SHIFT4   = 4'b1100;
localparam      ST_SHIFT5   = 4'b1101;
localparam      ST_SHIFT6   = 4'b1110;
localparam      ST_SHIFT7   = 4'b1111;
localparam      ST_STOP     = 4'b0001;

// Datapath function
localparam      CS_IDLE     = 3'b000;
localparam      CS_LOAD     = 3'b001;
localparam      CS_SR       = 3'b010;

module宣言のあと、定数の定義が続きます。

このコンポーネントには、ステートマシンが一つあります。 4ビットの状態コードは、プリフィックスとしてST_を付けています。 UARTを普通に作ろうとすると、ビットカウンタを外につけたくなるのですが、このコンポーネントでは、ベタに状態遷移だけで表現する方法をとっています。

このコンポーネントでは、データパスをひとつ使用しています。 データパスには、最大8種類の動作を設定する事ができ、外部からどの動作を行わせるかを決定することができます。 このコンポーネントでは、8個のうち3個の設定だけを使っています。 プリフィックスとしてCS_を付けた定数でそれぞれの設定を示しています。

// Wire declaration
wire[3:0]       state;          // State code
wire            f0_empty;       // F0 is EMPTY
wire            f0_not_full;    // F0 is not FULL
wire            so;             // Shift out

// Pseudo register
reg[2:0]        addr;           // MSB part of Datapath function

// Output signal buffer
reg             tx_reg;         // DFF for output

内部で使用するwireregを宣言しています。

状態コードを示すstateのほかにFIFOの状態を示すf0_emptyf0_not_full信号、そしてデータパスからのシフト出力を表すsoが宣言されています。

addrは、データパスの動作を示すコードが入る疑似レジスタです。

tx出力には、DFFを追加して、完全にクロックに同期したグリッチの無い出力が得られるようになっています。

// State machine behavior
reg [3:0]       state_reg;
always @(posedge reset or posedge clock) begin
    if (reset) begin
                state_reg <= ST_IDLE;
    end else casez (state)
        ST_IDLE:
            if (~f0_empty) begin
                state_reg <= ST_START;
            end
        ST_START:
                state_reg <= ST_SHIFT0;
        ST_SHIFT0:
                state_reg <= ST_SHIFT1;
        ST_SHIFT1:
                state_reg <= ST_SHIFT2;
        ST_SHIFT2:
                state_reg <= ST_SHIFT3;
        ST_SHIFT3:
                state_reg <= ST_SHIFT4;
        ST_SHIFT4:
                state_reg <= ST_SHIFT5;
        ST_SHIFT5:
                state_reg <= ST_SHIFT6;
        ST_SHIFT6:
                state_reg <= ST_SHIFT7;
        ST_SHIFT7:
                state_reg <= ST_STOP;
        ST_STOP:
            if (~f0_empty) begin
                state_reg <= ST_START;
            end else begin
                state_reg <= ST_IDLE;
            end
        default:
                state_reg <= ST_IDLE;
    endcase
end
assign      state = state_reg;

ステートマシンは、状態遷移だけが定義されています。 FIFOにデータが到着したら(~f0_empty)UARTの信号生成シーケンスが走ります。 一つの状態が一つのビットクロックに対応しているので、ベタなぶん記述は多くなりますが簡単です。

ST_STOPで最後のSTOPビットを送ったあと、次のデータがFIFOに到着していればST_STARTに遷移するので、二つのキャラクタの間に隙間を入れずに送信することができます。

// Internal control signals
always @(state) begin
    casez (state)
        ST_IDLE: begin
            addr    = CS_IDLE;
        end
        ST_START: begin
            addr    = CS_LOAD;
        end
        ST_SHIFT0, ST_SHIFT1, ST_SHIFT2, ST_SHIFT3,
        ST_SHIFT4, ST_SHIFT5, ST_SHIFT6, ST_SHIFT7: begin
            addr    = CS_SR;
        end
        ST_STOP: begin
            addr    = CS_IDLE;
        end
        default: begin
            addr    = CS_IDLE;
        end
    endcase
end

ステートマシンの状態によって内部信号が決定されます。

このコンポーネントの内部信号は、データパスの動作を決めるaddrだけになりました。 ST_STARTCS_LOAD動作によってFIFOからA0レジスタにデータを取り出します。 A0に取り出されたデータは、ST_SHIFT0からST_SHIFT7まで1ビットずつシフトされて送信に使用されます。

// TX output behavior
// Implemented in negative logic
always @(posedge reset or posedge clock) begin
    if (reset) begin
            tx_reg <= 1'b0;  // MARK
    end else casez (state)
        ST_IDLE:
            tx_reg <= 1'b0;  // MARK
        ST_START:
            tx_reg <= 1'b1;  // SPACE
        ST_SHIFT0, ST_SHIFT1, ST_SHIFT2, ST_SHIFT3,
        ST_SHIFT4, ST_SHIFT5, ST_SHIFT6, ST_SHIFT7:
            tx_reg <= ~so;
        ST_STOP:
            tx_reg <= 1'b0;  // MARK
        default:
            tx_reg <= 1'b0;  // MARK
    endcase
end
assign tx = ~tx_reg;

// DREQ output behavior
assign dreq = f0_not_full;

ふたつの出力txdreqの振る舞いがここで決められます。

すでに述べたように、tx出力は、レジスタtx_regで受けることによってクロック同期にしています。 この時、tx出力をそのままtx_regに入れると、リセットの時にtx_reg1になってしまいます。 これに対して、PSoCUDBでは、DFFの初期状態が0であるためにリセット直後に立ち上がりエッジが現れてしまいます。 これを簡単に解決するために、tx_regの論理を負論理にしてしまいました。 具体的にはリセット状態で0になるようにしています。 これによって、リセット後にエッジが観測されなくなりました。

dreq出力には、FIFOの状態を表すf0_not_full信号がそのまま使用できます。

cy_psoc3_dp #(.cy_dpconfig(
{
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM0: IDLE*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC___F0, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM1: LOAD - A0 <= F0*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP___SR, `CS_A0_SRC__ALU, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM2: SR: A0 <= A0 >> 1*/
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM3:  */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM4:  */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM5:  */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM6:  */
    `CS_ALU_OP_PASS, `CS_SRCA_A0, `CS_SRCB_D0,
    `CS_SHFT_OP_PASS, `CS_A0_SRC_NONE, `CS_A1_SRC_NONE,
    `CS_FEEDBACK_DSBL, `CS_CI_SEL_CFGA, `CS_SI_SEL_CFGA,
    `CS_CMP_SEL_CFGA, /*CFGRAM7:  */
    8'hFF, 8'h00,  /*CFG9:  */
    8'hFF, 8'hFF,  /*CFG11-10:  */
    `SC_CMPB_A1_D1, `SC_CMPA_A1_D1, `SC_CI_B_ARITH,
    `SC_CI_A_ARITH, `SC_C1_MASK_DSBL, `SC_C0_MASK_DSBL,
    `SC_A_MASK_DSBL, `SC_DEF_SI_0, `SC_SI_B_DEFSI,
    `SC_SI_A_DEFSI, /*CFG13-12:  */
    `SC_A0_SRC_ACC, `SC_SHIFT_SR, 1'h0,
    1'h0, `SC_FIFO1_BUS, `SC_FIFO0_BUS,
    `SC_MSB_DSBL, `SC_MSB_BIT0, `SC_MSB_NOCHN,
    `SC_FB_NOCHN, `SC_CMP1_NOCHN,
    `SC_CMP0_NOCHN, /*CFG15-14:  */
    10'h00, `SC_FIFO_CLK__DP,`SC_FIFO_CAP_AX,
    `SC_FIFO_LEVEL,`SC_FIFO__SYNC,`SC_EXTCRC_DSBL,
    `SC_WRK16CAT_DSBL /*CFG17-16:  */
}
)) dp(
        /*  input                   */  .reset(reset),
        /*  input                   */  .clk(clock),
        /*  input   [02:00]         */  .cs_addr(addr[2:0]),
        /*  input                   */  .route_si(1'b0),
        /*  input                   */  .route_ci(1'b0),
        /*  input                   */  .f0_load(1'b0),
        /*  input                   */  .f1_load(1'b0),
        /*  input                   */  .d0_load(1'b0),
        /*  input                   */  .d1_load(1'b0),
        /*  output                  */  .ce0(),
        /*  output                  */  .cl0(),
        /*  output                  */  .z0(),
        /*  output                  */  .ff0(),
        /*  output                  */  .ce1(),
        /*  output                  */  .cl1(),
        /*  output                  */  .z1(),
        /*  output                  */  .ff1(),
        /*  output                  */  .ov_msb(),
        /*  output                  */  .co_msb(),
        /*  output                  */  .cmsb(),
        /*  output                  */  .so(so),
        /*  output                  */  .f0_bus_stat(f0_not_full),
        /*  output                  */  .f0_blk_stat(f0_empty),
        /*  output                  */  .f1_bus_stat(),
        /*  output                  */  .f1_blk_stat(),
        
        /* input                    */  .ci(1'b0),     // Carry in from previous stage
        /* output                   */  .co(),         // Carry out to next stage
        /* input                    */  .sir(1'b0),    // Shift in from right side
        /* output                   */  .sor(),        // Shift out to right side
        /* input                    */  .sil(1'b0),    // Shift in from left side
        /* output                   */  .sol(),        // Shift out to left side
        /* input                    */  .msbi(1'b0),   // MSB chain in
        /* output                   */  .msbo(),       // MSB chain out
        /* input [01:00]            */  .cei(2'b0),    // Compare equal in from prev stage
        /* output [01:00]           */  .ceo(),        // Compare equal out to next stage
        /* input [01:00]            */  .cli(2'b0),    // Compare less than in from prv stage
        /* output [01:00]           */  .clo(),        // Compare less than out to next stage
        /* input [01:00]            */  .zi(2'b0),     // Zero detect in from previous stage
        /* output [01:00]           */  .zo(),         // Zero detect out to next stage
        /* input [01:00]            */  .fi(2'b0),     // 0xFF detect in from previous stage
        /* output [01:00]           */  .fo(),         // 0xFF detect out to next stage
        /* input [01:00]            */  .capi(2'b0),   // Software capture from previous stage
        /* output [01:00]           */  .capo(),       // Software capture to next stage
        /* input                    */  .cfbi(1'b0),   // CRC Feedback in from previous stage
        /* output                   */  .cfbo(),       // CRC Feedback out to next stage
        /* input [07:00]            */  .pi(8'b0),     // Parallel data port
        /* output [07:00]           */  .po()          // Parallel data port
);
//`#end` -- edit above this line, do not edit this line
endmodule

最後は、データパスです。 以下の入出力信号を接続しています。

端子名 信号名 概要
.reset reset リセット信号
.clk clock クロック信号
.cs_addr addr[2:0] 動作コード
.so so シフト出力
.f0_bus_stat f0_not_full バスから見たFIFOの状態
.f0_blk_stat f0_empty コンポーネントから見たFIFOの状態

データパスの動作はDatapath Configuration Toolを使用しています。

データパスの設定

APIファイル

このコンポーネントのAPIは、ヘッダファイルとソースファイルに記述されています。 ここでは、インスタンスに展開後の記述を紹介します。

#if !defined(NTANUARTTX_UartTx_H)
#define NTANUARTTX_UartTx_H
    
#include <cytypes.h>

#define UartTx_INPUT_REG (* (reg8 *)UartTx_dp__F0_REG)
#define UartTx_INPUT_PTR (  (reg8 *)UartTx_dp__F0_REG)

extern void UartTx_WriteValue(uint8 value);

#endif // NTANUARTTX_UartTx_H

ヘッダファイル "UartTx.h" の前半では、送信データを受け付けるレジスタとそのアドレスが定義されています。 これらはAPI内部で使用されるほか、DMAの設定でも使用されます。

後半では、送信データを書き込むための関数UartTx_WriteValue()が宣言されています。 実態は、以下のソースファイルにあります。

#include "UartTx.h"

void UartTx_WriteValue(uint8 value) {
    UartTx_INPUT_REG = value;
}

ソースファイル (UartTx.c) には、API関数の実体だけが記述されています。 与えられたデータvalueをレジスタに書き込むだけの関数です。

DMA Capabilityファイル

DMA Capability File (NtanUartTx_v1_0.cydmacap) は、DMA Wizardでソースコードのひな型を生成する際に参照される情報を格納したファイルです。

<DMACapability>

  <Category name="" 
            enabled="true" 
            bytes_in_burst="1"
            bytes_in_burst_is_strict="true" 
            spoke_width="2" 
            inc_addr="false" 
            each_burst_req_request="true">
    <Location name="`$INSTANCE_NAME`_INPUT_PTR" enabled="true" direction="destination"/>
  </Category>
  
</DMACapability>

ヘッダファイルで宣言されたアドレスが、ここで使用されています。

"DMA Wizard"への対応と使い方については、「PSoC 3 で、 DMA 対応倍増器を作った」で書きました。

テストプロジェクト(1) - UartTxTest01

コンポーネントをテストするためのプロジェクトを作成しました。最初は、ソフトウェアで直接書き込むモデルです。

回路図

テスト回路(1)

このコンポーネントは、出力信号を生成する目的で作れられていますので、信号をオシロスコープか何かで観測しなくてはなりません。 しかし、ご安心ください。 PSoC 5LPの豊富なハードウェアの力によって、テスト回路内部にLogic Analyzerまで内蔵してしまいました。 詳しくは「CY8CKIT-042 でロジアナを作った ~UART編~」で書きました。

内蔵のLogic Analyzerで観測する関係から、UARTの通信速度は、20bpsに設定しています。

プログラム

"main.c" のプログラムは、以下のようになりました。

#include "project.h"

// SR1 status register bit assignment
#define SR1_DREQ (1)
#define SR1_SW1  (2)

このプロジェクトは、Status Register SR1を使ってSW1の状態とdreq信号を参照しながら動作します。 ここでは、SR1の二つの入力のビット位置を定義しています。

// Statemachine declaration
#define ST_IDLE (0)
#define ST_SEND (1)
#define ST_WAIT (2)

uint32 state = ST_IDLE;

プログラム内でもステートマシンを使ってUARTへのデータ書き込みを管理しています。 ステートマシンは、三つの状態を持っており、状態変数stateを使っています。

// Data packet to be sent
const char phrase[] = "The quick brown fox jumps over the lazy dog. ";
uint8 packet[8];

このテストプロジェクトでは、UARTから送信するデータは何でもよかったのですが、何でもいいデータを作成するために、あるパターンの言葉を決められた大きさのパケットに詰め込むプログラムを作成してしまいました。 これで、どんな大きさのパケットにも対応できます。

// Interrupt handling
volatile CYBIT int_Sample_flag = 0;

CY_ISR(int_Sample_isr) {
    int_Sample_flag = 1;
}

周期割り込みを受けるInterrupt Service Routine (ISR)を定義しています。 周期割り込みは、Logic Analyzerで使用されます。 いっしょに周期割り込みで使用されるフラグが定義されています。 このフラグは、メインループのなかで参照されます。

// The main-loop
int main(void)
{
    uint32 index = 0;
    
    CyGlobalIntEnable; /* Enable global interrupts. */

    // Initialize the logic analyzer task
    Probe_UART_Start();
    int_Sample_StartEx(int_Sample_isr);

main()関数では、最初にLogic Analyzerで使用されるProbe_UARTint_Sampleのふたつのコンポーネントが初期設定されます。

    // Initialize the statemachine task
    state = ST_IDLE;

    // Initialize the packet[]
    // Copy the phrase[] into packet[] as possible
    for (uint32 k = 0; k < sizeof packet; ) {
        for (uint32 i = 0; i < sizeof phrase; i++) {
            packet[k++] = phrase[i];
            if (k >= sizeof packet) break;
        }
    }

ステートマシンの初期設定のあと、送信データに使用されるパケットが作成されます。

    for(;;)
    {
        // Statemachine dispatcher
        switch (state) {
            case ST_IDLE:
                // Wait for the SW1 pushed
                if (!(SR1_Read() & SR1_SW1)) {
                    // SW1 pushed
                    index = 0;
                    state = ST_SEND;
                }
                break;
            case ST_SEND:
                // Send a packet until exhausted
                if ((SR1_Read() & SR1_DREQ)) {
                    UartTx_WriteValue(packet[index++]);
                }
                if (index >= sizeof packet) {
                    state = ST_WAIT;
                }
                break;
            case ST_WAIT:
                // Wait for the SW1 released
                if (SR1_Read() & SR1_SW1) {
                    state = ST_IDLE;
                }
                break;
            default:
                state = ST_IDLE;
        }

メインループには、二つのdispatcherが入っています。 ひとつめは、ステートマシンのdispatcherです。

ST_IDLE状態では、SW1ボタンの押下を検出します。 ボタンが押されたらST_SEND状態に遷移します。

ST_SEND状態では、パケットからデータを送信します。 dreqを確認して、UartTxの受付が可能であれば、UartTx_WriteValue()関数でデータを1バイト送信します。 パケットのデータを送信し終えたらST_WAIT状態に遷移します。

ST_WAIT状態では、SW1ボタンが離されたのを確認してST_IDLE状態に戻ります。 debounceの機能が必要であれば、Pin_SW1SR1の間にDebounceコンポーネントを入れてください。

        // Logic analyzer dispatcher
        if (int_Sample_flag) {
            int_Sample_flag = 0;
            Probe_UART_PutChar(Probe_Read());
        }
    }
}

二つ目のdispatcherでは、Logic Analyzerの処理を行っています。 といっても、周期割り込みのフラグが立っていたら、Probeコンポーネントの値をそのままUART出力に流すだけです。

実行結果

実行結果をBridge Control Panelで観測しました。 波形は上から"SW1" "DREQ" "TX"の順です。

実行結果(1)

切れ目なく8バイトのデータが送信されていることが分かります。 また、"DREQ"の動きから、4バイト目を送信し始めるタイミングでFIFOにすべてのデータを送り終えたことがわかります。

テストプロジェクト(2) - UartTxTest02

ふたつめのプロジェクトでは、DMAを使用してデータを送ります。

回路図

テスト回路(2)

テストプロジェクト(1)にDMAコンポーネントが追加されました。 DMAdrq入力は”Level"入力に設定してあります。

プログラム

プログラムは、テストプロジェクト(1)のプログラムから少しだけ変更されています。

// Defines for DMA_UartTx
#define DMA_UartTx_BYTES_PER_BURST 1
#define DMA_UartTx_REQUEST_PER_BURST 1
#define DMA_UartTx_SRC_BASE (CYDEV_SRAM_BASE)
#define DMA_UartTx_DST_BASE (CYDEV_PERIPH_BASE)

// Variable declarations for DMA_UartTx
// Move these variable declarations to the top of the function
uint8 DMA_UartTx_Chan;
uint8 DMA_UartTx_TD[1];

定数と変数の宣言部にDMAに特有な宣言が追加されました。 これらの記述は、"DMA Wizard"から持ってきたものをそのまま使っています。

    // DMA Configuration for DMA_UartTx
    DMA_UartTx_Chan = DMA_UartTx_DmaInitialize(
        DMA_UartTx_BYTES_PER_BURST, DMA_UartTx_REQUEST_PER_BURST, 
        HI16(DMA_UartTx_SRC_BASE), HI16(DMA_UartTx_DST_BASE));
    DMA_UartTx_TD[0] = CyDmaTdAllocate();
    CyDmaTdSetConfiguration(DMA_UartTx_TD[0], sizeof packet, CY_DMA_DISABLE_TD, CY_DMA_TD_INC_SRC_ADR);
    CyDmaTdSetAddress(DMA_UartTx_TD[0], LO16((uint32)packet), LO16((uint32)UartTx_INPUT_PTR));
    CyDmaChSetInitialTd(DMA_UartTx_Chan, DMA_UartTx_TD[0]);

main()関数の初期設定部分にDMAの初期設定が追加されました。 これらも"DMA Wizard"の出力からの転記ですが、最後のCyDmaChEnable()関数の呼び出しだけは、後で行います。

        // Statemachine dispatcher
        switch (state) {
            case ST_IDLE:
                // Wait for the SW1 pushed
                if (!(SR1_Read() & SR1_SW1)) {
                    // SW1 pushed
                    CyDmaChEnable(DMA_UartTx_Chan, 1);
                    state = ST_SEND;
                }
                break;
            case ST_SEND:
                // Send a packet until exhausted
                {
                    uint8 dmaState;
                    CyDmaChStatus(DMA_UartTx_Chan, NULL, &dmaState);
                    if (!(dmaState & CY_DMA_STATUS_CHAIN_ACTIVE)) {
                        state = ST_WAIT;
                    }
                }
                break;
            case ST_WAIT:
                // Wait for the SW1 released
                if (SR1_Read() & SR1_SW1) {
                    state = ST_IDLE;
                }
                break;
            default:
                state = ST_IDLE;
        }

ステートマシンのdispatcherには、いくつか変更点があります。

まず、ST_IDLE状態でSW1の押下を検出した時、テストプロジェクト(1)では状態遷移をするだけでしたが、今回はCyDmaChEnable()を呼び出してDMAを起動しています。 これ以降、データ転送は自動的に行われます。

ST_SEND状態では、DMAが勝手にデータを送り込んでくれるため何もすることがありません。 ただひとつ、DMAのデータ転送がすべて終わった事を検出してST_WAIT状態に遷移する仕事が残っています。 DMAの状態の検出には、CyDmaChStatus()関数を使用しています。 ただし、この関数でわかるのは、UARTのデータが転送されたタイミングではなく、FIFOがすべてのデータを受信したタイミングです。 このため、UARTの転送中にSW1を何度も押していると、データをさらに送り出してしまう場合があります。

実行結果

このプロジェクトでの実行結果は、DMAで処理を行っているだけですのでテストプロジェクト(1)と同じです。画像は省略します。

テストプロジェクト(3) - UartTxTest03

Logic Analyzerを使った出力を見てもうまくいっているようなので、USBUARTに突っ込んでみましょう。

回路図

回路図(3)

Logic Analyzer部分を取り払って、UartTxの出力を直接Tx_1出力に接続しました。 これで、USBUARTを介してPCでデータを観測できます。

ボーレートは、115200bpsを使います。 USBUARTがこれより高いところに対応していないようなので。

プログラム

このプロジェクトでは、"Tera Term"を使って出力を観測するので、パケットのサイズを256バイトに拡張しています。

// Data packet to be sent
const char phrase[] = "The quick brown fox jumps over the lazy dog. ";
uint8 packet[256];

ステータスレジスタSR1を取り外してしまったので、SW1は、直接読み出すことになりました。

        // Statemachine dispatcher
        switch (state) {
            case ST_IDLE:
                // Wait for the SW1 pushed
                if (!Pin_SW1_Read()) {
                    // SW1 pushed
                    CyDmaChEnable(DMA_UartTx_Chan, 1);
                    state = ST_SEND;
                }
                break;
            case ST_SEND:
                // Send a packet until exhausted
                {
                    uint8 dmaState;
                    CyDmaChStatus(DMA_UartTx_Chan, NULL, &dmaState);
                    if (!(dmaState & CY_DMA_STATUS_CHAIN_ACTIVE)) {
                        state = ST_WAIT;
                    }
                }
                break;
            case ST_WAIT:
                // Wait for the SW1 released
                if (Pin_SW1_Read()) {
                    state = ST_IDLE;
                }
                break;
            default:
                state = ST_IDLE;
        }

実行結果

実行結果(3)

SW1を押すと、256バイトずつ文字が流れてきます。

関連記事