NordicマイコンでUARTドライバを作ってみた

# やりたいこと
今回、送信処理・受信処理共に、ブロッキングではなくノンブロッキングで動作するドライバを書いてみたいと思います。

・CTS・RTSを使ったフロー制御は行わない。
・受信バッファを用意し、割り込みで取得したデータはバッファに蓄積する。
・全二重通信を同時に２ポートまで行える。
<font color="Red">・そして今回は、SDKは使用せずレジスタ直叩きでやってみる</font>


# ブロック図
<img width="518" alt="スクリーンショット 2020-02-02 0.47.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99085/0e488313-d5bb-ca5b-9412-524757336f7b.png">

UARTE[0..1]とありますね。UARTのインスタンスは2つまで使用できるようですが、
厳密には、
　・UART0のみ
　・UARTE0、UARTE1を使用
　・UART0、UARTE1を使用
の３パターンが設定できるとの事です。
UART1はUARTEのみ（EasyDMA）使用できます。



# ソースコード
```c:sample.c
#define UART_BUFFER_SIZE 256
#define UART_PORT0_TX_PIN NRF_GPIO_PIN_MAP(1, 1)
#define UART_PORT0_RX_PIN NRF_GPIO_PIN_MAP(1, 2)
#define UART_PORT1_TX_PIN NRF_GPIO_PIN_MAP(0, 6)
#define UART_PORT1_RX_PIN NRF_GPIO_PIN_MAP(0, 8)

typedef struct
{
    uint8_t buffer[UART_BUFFER_SIZE];
   volatile uint32_t head;
} ringBuffer;

ringBuffer rx_buffer0;
ringBuffer rx_buffer1;
ringBuffer tx_buffer0;
ringBuffer tx_buffer1;

// UARTE0の割り込みハンドラ
void UARTE0_UART0_IRQHandler(void)
{
    NRF_LOG_INFO("UARTE0_UART0_IRQHandler");

    if(NRF_UARTE0->EVENTS_CTS)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_CTS");
        NRF_UARTE0->EVENTS_CTS = 0;
        return;
    }

    if(NRF_UARTE0->EVENTS_NCTS)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_NCTS");
        return;
    }
    if(NRF_UARTE0->EVENTS_RXDRDY)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_RXDRDY");
        NRF_UARTE0->EVENTS_RXDRDY = 0;
        return;
    }
    if(NRF_UARTE0->EVENTS_ENDRX)
    {
        NRF_UARTE0->EVENTS_ENDRX = 0;
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_ENDRX");
        NRF_LOG_HEXDUMP_INFO(rx_buffer0.buffer,NRF_UARTE0->RXD.AMOUNT);
        NRF_LOG_INFO("bytes = %d",NRF_UARTE0->RXD.AMOUNT);
        return;
    }
    if(NRF_UARTE0->EVENTS_TXDRDY)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_TXDRDY");
        NRF_UARTE0->EVENTS_TXDRDY = 0;
        return;
    }
    if(NRF_UARTE0->EVENTS_ENDTX)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_ENDTX");
        NRF_UARTE0->EVENTS_ENDTX = 0;
        NRF_UARTE0->TASKS_STOPTX = 1;
        return;
    }
    if(NRF_UARTE0->EVENTS_ERROR)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_ERROR");
        return;
    }
    if(NRF_UARTE0->EVENTS_RXTO)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_RXTO");
        return;
    }
    if(NRF_UARTE0->EVENTS_RXSTARTED)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_RXSTARTED");
        NRF_UARTE0->EVENTS_RXSTARTED = 0;
        return;
    }
    if(NRF_UARTE0->EVENTS_TXSTARTED)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_TXSTARTED");
        NRF_UARTE0->EVENTS_TXSTARTED = 0;
        return;
    }
    if(NRF_UARTE0->EVENTS_TXSTOPPED)
    {
        NRF_LOG_INFO("NRF_UARTE0->EVENTS_TXSTOPPED");
        NRF_UARTE0->EVENTS_TXSTOPPED = 0;
        return;
    }
    return;
}

// UARTE1の割り込みハンドラ
void UARTE1_IRQHandler(void)
{
    NRF_LOG_INFO("UARTE1_IRQHandler");

    if(NRF_UARTE1->EVENTS_CTS)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_CTS");
        NRF_UARTE1->EVENTS_CTS = 0;
        return;
    }

    if(NRF_UARTE1->EVENTS_NCTS)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_NCTS");
        return;
    }
    if(NRF_UARTE1->EVENTS_RXDRDY)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_RXDRDY");
        NRF_UARTE1->EVENTS_RXDRDY = 0;
        return;
    }
    if(NRF_UARTE1->EVENTS_ENDRX)
    {
        NRF_UARTE1->EVENTS_ENDRX = 0;
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_ENDRX");
        NRF_LOG_HEXDUMP_INFO(rx_buffer1.buffer,NRF_UARTE1->RXD.AMOUNT);
        NRF_LOG_INFO("bytes = %d",NRF_UARTE1->RXD.AMOUNT);
        return;
    }
    if(NRF_UARTE1->EVENTS_TXDRDY)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_TXDRDY");
        NRF_UARTE1->EVENTS_TXDRDY = 0;
        return;
    }
    if(NRF_UARTE1->EVENTS_ENDTX)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_ENDTX");
        NRF_UARTE1->EVENTS_ENDTX = 0;
        NRF_UARTE1->TASKS_STOPTX = 1;
        return;
    }
    if(NRF_UARTE1->EVENTS_ERROR)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_ERROR");
        return;
    }
    if(NRF_UARTE1->EVENTS_RXTO)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_RXTO");
        return;
    }
    if(NRF_UARTE1->EVENTS_RXSTARTED)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_RXSTARTED");
        NRF_UARTE1->EVENTS_RXSTARTED = 0;
        return;
    }
    if(NRF_UARTE1->EVENTS_TXSTARTED)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_TXSTARTED");
        NRF_UARTE1->EVENTS_TXSTARTED = 0;
        return;
    }
    if(NRF_UARTE1->EVENTS_TXSTOPPED)
    {
        NRF_LOG_INFO("NRF_UARTE1->EVENTS_TXSTOPPED");
        NRF_UARTE1->EVENTS_TXSTOPPED = 0;
        return;
    }
    return;
}

// 初期化関数
void uart_init(void) {
    
    NRF_UARTE0->INTENCLR = 0xFFFFFFFF;
    NRF_UARTE0->INTENSET |= (0<<22) | (0<<20) | (0<<19) | (1<<8) | (0<<7) | (1<<4) | (0<<2);
    NVIC_ClearPendingIRQ(UARTE0_UART0_IRQn);
    NVIC_SetPriority(UARTE0_UART0_IRQn, NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY);
    NVIC_EnableIRQ(UARTE0_UART0_IRQn);
    NRF_UARTE0->PSEL.TXD = UART_PORT0_TX_PIN;
    NRF_UARTE0->PSEL.RXD = UART_PORT0_RX_PIN;
    NRF_UARTE0->PSEL.CTS = 0xFFFFFFFF;
    NRF_UARTE0->PSEL.RTS = 0xFFFFFFFF;

    struct Config_Register{
        uint16_t HWFC : 1;
        uint16_t PARITY : 3;
        uint16_t STOP : 1;
        uint16_t RESERVE : 11;
    };
    union {
        struct Config_Register bit;
        uint16_t byte;
    } config;
    
    config.bit.HWFC = 0; // フロー制御なし
    config.bit.PARITY = 0; // パリティなし
    config.bit.STOP = 0; // Stop bit 1
    NRF_UARTE0->CONFIG = config.byte;
    NRF_UARTE0->BAUDRATE = 0x01D60000;
    NRF_UARTE0->ENABLE = 0x00000008;

    NRF_UARTE0->EVENTS_CTS = 0;
    NRF_UARTE0->EVENTS_ENDTX = 0;
    NRF_UARTE0->EVENTS_TXDRDY = 0;
    NRF_UARTE0->EVENTS_TXSTARTED = 0;
    NRF_UARTE0->EVENTS_RXSTARTED = 0;


    NRF_UARTE1->INTENCLR = 0xFFFFFFFF;
    NRF_UARTE1->INTENSET |= (0<<22) | (0<<20) | (0<<19) | (1<<8) | (0<<7) | (1<<4) | (0<<2);
    NVIC_ClearPendingIRQ(UARTE1_IRQn);  // 割り込みクリア
    NVIC_SetPriority(UARTE1_IRQn, NRFX_UARTE_DEFAULT_CONFIG_IRQ_PRIORITY);
    NVIC_EnableIRQ(UARTE1_IRQn);
    NRF_UARTE1->PSEL.TXD = UART_PORT1_TX_PIN;
    NRF_UARTE1->PSEL.RXD = UART_PORT1_RX_PIN;
    NRF_UARTE1->PSEL.CTS = 0xFFFFFFFF;
    NRF_UARTE1->PSEL.RTS = 0xFFFFFFFF;
    
    config.bit.HWFC = 0; // フロー制御なし
    config.bit.PARITY = 0; // パリティなし
    config.bit.STOP = 0; // Stop bit 1
    NRF_UARTE1->CONFIG = config.byte;
    NRF_UARTE1->BAUDRATE = 0x01D60000;
    NRF_UARTE1->ENABLE = 0x00000008;

    NRF_UARTE1->EVENTS_CTS = 0;
    NRF_UARTE1->EVENTS_ENDTX = 0;
    NRF_UARTE1->EVENTS_TXDRDY = 0;
    NRF_UARTE1->EVENTS_TXSTARTED = 0;
    NRF_UARTE1->EVENTS_RXSTARTED = 0;

    return;
}

void uart_write_data0(unsigned char c)
{
  int i = (tx_buffer0.head + 1) % UART_BUFFER_SIZE;
 
  tx_buffer0.buffer[tx_buffer0.head] = c;
  tx_buffer0.head = i;

}

// データ送信
void uart_send_data0(uart_port_select_t port_select, uint8_t* p_send_data, uint8_t send_data_len) {
    NRF_LOG_INFO("uart_send_data0");
    
    tx_buffer0.head = 0;

    while( tx_buffer0.head < send_data_len )
    {
        uart_write_data0(*p_send_data);
        p_send_data++;
    }
    
    
    //send data
    NRF_UARTE0->EVENTS_ENDTX = 0;
    NRF_UARTE0->EVENTS_TXSTOPPED = 0;
    NRF_UARTE0->TXD.PTR = (uint32_t)&tx_buffer0.buffer[0]; //reset pointer to start of buffer
    NRF_UARTE0->TXD.MAXCNT = tx_buffer0.head;
    NRF_UARTE0->TASKS_STARTTX = 1;  //trigger start task to send data to host
    while(NRF_UARTE0->EVENTS_ENDTX == 0 && NRF_UARTE0->EVENTS_TXSTOPPED == 0){}

    NRF_LOG_INFO("NRF_UARTE0->EVENTS_ENDTX == 0 && NRF_UARTE0->EVENTS_TXSTOPPED == 0");
    
    return;
}

static void uart_write_data1(unsigned char c)
{
  int i = (tx_buffer1.head + 1) % UART_BUFFER_SIZE;
 
  tx_buffer1.buffer[tx_buffer1.head] = c;
  tx_buffer1.head = i;

}

void uart_send_data1(uart_port_select_t port_select, uint8_t* p_send_data, uint8_t send_data_len) {
    NRF_LOG_INFO("uart_send_data1");
    tx_buffer1.head = 0;

    while( tx_buffer1.head < send_data_len )
    {
        uart_write_data1(*p_send_data);
        p_send_data++;
    }
    
    
    //send data
    NRF_UARTE1->EVENTS_ENDTX = 0;
    NRF_UARTE1->EVENTS_TXSTOPPED = 0;
    NRF_UARTE1->TXD.PTR = (uint32_t)&tx_buffer1.buffer[0]; //reset pointer to start of buffer
    NRF_UARTE1->TXD.MAXCNT = tx_buffer1.head;
    NRF_UARTE1->TASKS_STARTTX = 1;  //trigger start task to send data to host
    while(NRF_UARTE1->EVENTS_ENDTX == 0 && NRF_UARTE1->EVENTS_TXSTOPPED == 0){}

    NRF_LOG_INFO("NRF_UARTE1->EVENTS_ENDTX == 0 && NRF_UARTE1->EVENTS_TXSTOPPED == 0");

    return true;
}



void uart_read(void) {
    NRF_LOG_INFO("uart_read");
    
    NRF_UARTE0->SHORTS = (UARTE_SHORTS_ENDRX_STARTRX_Enabled << UARTE_SHORTS_ENDRX_STARTRX_Pos);
    NRF_UARTE0->EVENTS_RXDRDY = 0;
    NRF_UARTE0->EVENTS_ENDRX = 0;
    NRF_UARTE0->EVENTS_RXTO = 0;
    NRF_UARTE0->RXD.MAXCNT = 10; // 10バイト受信したら割り込みしてくれます
    NRF_UARTE0->RXD.PTR = (uint32_t)&rx_buffer0; //reset pointer to start of buffer
    NRF_UARTE0->TASKS_STARTRX = 1;  //trigger start task to send data to host
    NRF_LOG_INFO("NRF_UARTE0->TASKS_STARTRX = 1");


    NRF_UARTE1->SHORTS = (UARTE_SHORTS_ENDRX_STARTRX_Enabled << UARTE_SHORTS_ENDRX_STARTRX_Pos);
    NRF_UARTE1->EVENTS_RXDRDY = 0;
    NRF_UARTE1->EVENTS_ENDRX = 0;
    NRF_UARTE1->EVENTS_RXTO = 0;
    NRF_UARTE1->RXD.MAXCNT = 10; // 10バイト受信したら割り込みしてくれます
    NRF_UARTE1->RXD.PTR = (uint32_t)&rx_buffer1; //reset pointer to start of buffer
    NRF_UARTE1->TASKS_STARTRX = 1;  //trigger start task to send data to host
    NRF_LOG_INFO("NRF_UARTE1->TASKS_STARTRX = 1");

}
```

# まとめ
上記のコードで送受信を割り込みで行える事が確認できました。
余程の事がない限り、nrfSDK5を使って実装する事になると思うのですが内部の仕様を知る機会になれば幸いです。