메모리를 보면 UART 장치에 UART, UARTE 2종류 사용할수 있는데 UARTE에는 EasyDMA가 붙어있다.
nRF52는 다른 회사 제품과 다르게 주변 장치에 DMA가 각각 붙어있다.
UART는 Deprecated로 표시되어있어 UARTE를 사용해보기로 했다.

주의사항

  • micro:bit v2 회로도에 UART 핀이 반대로 되어있음
  • easyDMA는 flash에 액세스 불가능
  • 수신중 STOPRX를 할 경우 데이터가 깨짐
  • RXD.AMOUNT는 ENDRX 이후에 업데이트됨

처음 생각한건 DMA 계속돌려 링버퍼 처럼 쓰는 구조 였지만 RXD.AMOUNT가 실시간으로 업데이트되지 않고 STOPRX를 할경우 데이터가 깨지는 문제로 간단하게 구현할 수 없었다.

위 방식을 사용하기 위해서는 PPI와 TIMER를 사용해서 RXDRDY가 발생한 수를 카운트 하는 방식으로 구현 하는것으로 보인다.

우선 UART 기능이 동작하는것부터 확인하기 위해 1byte로 echo하는 간단한 코드를 작성했다.

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use nrf52833_pac as pac;
use panic_halt as _;

fn clock_config(p: &pac::Peripherals) {
    let clock = &p.CLOCK;

    if clock.hfclkstat.read().src().is_xtal()
        && clock.hfclkstat.read().state().is_running()
    {
        return;
    }

    clock
        .tasks_hfclkstart
        .write(|w| w.tasks_hfclkstart().set_bit());

    while !clock.hfclkstat.read().src().is_xtal() {}
    while !clock.hfclkstat.read().state().is_running() {}
}

fn uart_config(dp: &pac::Peripherals) {
    let p0 = &dp.P0;
    let uarte = &dp.UARTE0;

    // micro:bit uart rxd: P1.8 txd: P0.6
    p0.outset.write(|w| w.pin6().set());
    p0.dirset.write(|w| w.pin6().set());

    uarte.psel.rxd.write(|w| {
        unsafe { w.port().set_bit().pin().bits(8) }
            .connect()
            .connected()
    });
    uarte.psel.txd.write(|w| {
        unsafe { w.port().clear_bit().pin().bits(6) }
            .connect()
            .connected()
    });

    uarte.baudrate.write(|w| w.baudrate().baud115200());
    uarte.enable.write(|w| w.enable().enabled());
}

fn uart_write(dp: &pac::Peripherals, data: &[u8]) {
    let uarte = &dp.UARTE0;

    uarte
        .txd
        .ptr
        .write(|w| unsafe { w.ptr().bits(data.as_ptr() as u32) });
    uarte
        .txd
        .maxcnt
        .write(|w| unsafe { w.maxcnt().bits(data.len() as u16) });

    uarte.events_endtx.reset();

    uarte.tasks_starttx.write(|w| w.tasks_starttx().trigger());

    while uarte.events_endtx.read().events_endtx().is_not_generated() {}
}

fn uart_read(dp: &pac::Peripherals, data: &mut [u8]) -> usize {
    let uarte = &dp.UARTE0;

    uarte
        .rxd
        .ptr
        .write(|w| unsafe { w.ptr().bits(data.as_ptr() as u32) });
    uarte
        .rxd
        .maxcnt
        .write(|w| unsafe { w.maxcnt().bits(data.len() as u16) });

    uarte.events_endrx.reset();

    uarte.tasks_startrx.write(|w| w.tasks_startrx().trigger());

    while uarte.events_endrx.read().events_endrx().is_not_generated() {}

    uarte.rxd.amount.read().amount().bits() as usize
}

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();

    clock_config(&dp);
    uart_config(&dp);

    let mut buf = [0_u8; 1];

    loop {
        let len = uart_read(&dp, &mut buf);
        uart_write(&dp, &buf[..len]);
    }
}

참고