#include <stddef.h> #include <stdint.h> #include "uart.h" #include "global.h" void uart_init(uint32_t baud, uint32_t config) { // PL011 UART must be disabled before configuring wr32(PL011_UART_CR, 0); // GPIO pins used for UART should have pull up/down disabled // Procedure as described in BCM2835 ARM Peripherals wr32(GPPUD, 0); for (int i = 0; i < 150; i++) // delay for at least 150 cycles asm volatile("nop"); wr32(GPPUDCLK0, (1 << 14) | (1 << 15)); for (int i = 0; i < 150; i++) asm volatile("nop"); wr32(GPPUDCLK0, 0); wr32(GPPUD, 0); // Setting clock rate // As described in UART (PL011) Technical Reference Manual uint32_t int_part = DEFAULT_UART_CLOCK_RATE / (16 * baud); uint32_t rest = DEFAULT_UART_CLOCK_RATE % (16 * baud); uint32_t fract_part = (rest * 64 * 2 + 1) / (2 * 16 * baud); wr32(PL011_UART_IBRD, int_part); wr32(PL011_UART_FBRD, fract_part); // Set data transmission specified by caller // Don't enable FIFO to be able to receive interrupt every received // char, not every 2 chars wr32(PL011_UART_LCRH, config); // Set interrupt to come when transmit FIFO becomes ≤ 1/8 full // or receive FIFO becomes ≥ 1/8 full // (not really matters, since we disabled FIFOs) wr32(PL011_UART_IFLS, 0); // Enable UART receiving and transmitting, as well as UART itself wr32(PL011_UART_CR, (1 << 9) | (1 << 8) | 1); // At first, it's probably safer to disable interrupts :) uart_irq_disable(); // The above disables the entire uart irq; // Also disable single sources within it wr32(PL011_UART_IMSC, 0); } inline static _Bool can_transmit(void) { return !(rd32(PL011_UART_FR) & (1 << 5)); } inline static _Bool can_receive(void) { return !(rd32(PL011_UART_FR) & (1 << 4)); } void putchar(char c) { while (!can_transmit()); wr32(PL011_UART_DR, c); } char getchar(void) { while (!can_receive()); return rd32(PL011_UART_DR); } _Bool putchar_non_blocking(char c) { if (can_transmit()) { wr32(PL011_UART_DR, c); return 0; } return 1; } int getchar_non_blocking(void) { if (can_receive()) return rd32(PL011_UART_DR); return -1; }