Нарисовал еще одну реализацию.
И прием, и передача идет через DMA. Используется двойная буферизация как на передачу, так и на прием (чтобы не копировать приемный буфер куда-то еще). Концом приема строки считается получение символа '\n' (используется прерывание character match), либо же переполнение буфера (тогда выставляется флаг overflow и буфер заполняется заново).