// Copyright 2020 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include #include "pw_preprocessor/compiler.h" #include "pw_sys_io/sys_io.h" namespace { // Default core clock. This is technically not a constant, but since this app // doesn't change the system clock a constant will suffice. constexpr uint32_t kSystemCoreClock = 12000000; // UART status flags. constexpr uint32_t kTxFifoEmptyMask = 0b10000000; constexpr uint32_t kTxFifoFullMask = 0b1000000; constexpr uint32_t kRxFifoFullMask = 0b100000; constexpr uint32_t kRxFifoEmptyMask = 0b10000; constexpr uint32_t kTxBusyMask = 0b1000; // UART line control flags. // Default: 8n1 constexpr uint32_t kDefaultLineControl = 0x60; // UART control flags. constexpr uint32_t kUartEnableMask = 0x1; PW_PACKED(struct) UartBlock { uint32_t data_register; uint32_t receive_error; uint32_t reserved1[4]; uint32_t status_flags; uint32_t reserved2; uint32_t low_power; uint32_t integer_baud; uint32_t fractional_baud; uint32_t line_control; uint32_t control; uint32_t interrupt_fifo_level; uint32_t interrupt_mask; uint32_t raw_interrupt; uint32_t masked_interrupt; uint32_t interrupt_clear; }; // Declare a reference to the memory mapped block for UART0. volatile UartBlock& uart0 = *reinterpret_cast(0x4000C000U); constexpr uint32_t kRcgcUart0EnableMask = 0x1; volatile uint32_t& rcgc1 = *reinterpret_cast(0x400FE104U); // Calculate a baud rate multiplier such that we have 16 bits of precision for // the integer portion and 6 bits for the fractional portion. void SetBaudRate(uint32_t clock, uint32_t target_baud) { uint32_t divisor = target_baud * 16; uint32_t remainder = clock % divisor; uart0.integer_baud = (clock % divisor) & 0xffff; uart0.fractional_baud = (((remainder << 7) / divisor + 1) >> 1) & 0x3f; } } // namespace extern "C" void pw_sys_io_Init() { rcgc1 |= kRcgcUart0EnableMask; for (volatile int i = 0; i < 3; ++i) { // We must wait after enabling uart. } // Set baud rate. SetBaudRate(kSystemCoreClock, /*target_baud=*/115200); uart0.line_control = kDefaultLineControl; uart0.control |= kUartEnableMask; } namespace pw::sys_io { // Wait for a byte to read on UART0. This blocks until a byte is read. This is // extremely inefficient as it requires the target to burn CPU cycles polling to // see if a byte is ready yet. Status ReadByte(std::byte* dest) { while (true) { if (TryReadByte(dest).ok()) { return OkStatus(); } } } Status TryReadByte(std::byte* dest) { if (uart0.receive_error) { // Writing anything to this register clears all errors. uart0.receive_error = 0xff; } if (!(uart0.status_flags & kRxFifoFullMask)) { return Status::Unavailable(); } *dest = static_cast(uart0.data_register); return OkStatus(); } // Send a byte over UART0. Since this blocks on every byte, it's rather // inefficient. At the default baud rate of 115200, one byte blocks the CPU for // ~87 micro seconds. This means it takes only 10 bytes to block the CPU for // 1ms! Status WriteByte(std::byte b) { // Wait for TX buffer to be empty. When the buffer is empty, we can write // a value to be dumped out of UART. while (!(uart0.status_flags & kTxFifoEmptyMask)) { } uart0.data_register = static_cast(b); return OkStatus(); } // Writes a string using pw::sys_io, and add newline characters at the end. StatusWithSize WriteLine(const std::string_view& s) { size_t chars_written = 0; StatusWithSize result = WriteBytes(std::as_bytes(std::span(s))); if (!result.ok()) { return result; } chars_written += result.size(); // Write trailing newline. result = WriteBytes(std::as_bytes(std::span("\r\n", 2))); chars_written += result.size(); return StatusWithSize(result.status(), chars_written); } } // namespace pw::sys_io