Por supuesto que es posible. Sin embargo, se necesitarán muchos más recursos de CPU que si un UART hace todo el tiempo de bajo nivel por usted.
Transmitir es más fácil que recibir porque tienes el reloj. Sabe cuándo está el borde inicial del bit de inicio, por lo que no tiene que hacer ninguna sincronización de subbits.
Para recibir, debe sincronizar con el borde anterior del bit de inicio. Un método que utilicé una vez es interrumpir el borde descendente cuando no está en un personaje. Esto luego configura un temporizador para interrumpir en medio de los bits subsiguientes. Tenga en cuenta que el primer período de interrupción es 1½ bits y los intervalos restantes 1 bit veces. Si conoce la latencia de interrupción, puede compensarla cargando el temporizador con un valor más corto. En la interrupción del último bit, apaga la interrupción del temporizador y vuelve a habilitar la interrupción del flanco descendente, lo que prepara al sistema para recibir el siguiente byte.
También hay chips UART externos. Estos pueden interactuar a través de IIC, SPI o líneas paralelas. Eso requiere más hardware, pero mucho menos CPU.
Hay muchos métodos, cada uno con sus propias compensaciones entre criterios.