From 6cb072ce796e8582427a3d2db8cdc04e65a0901a Mon Sep 17 00:00:00 2001 From: "D.R.racer" Date: Tue, 25 May 2021 11:55:28 +0200 Subject: [PATCH] Rebase onto main + clean up the code a bit --- CMakeLists.txt | 10 ++- src/hal/adc.cpp | 9 +++ src/hal/avr/usart.cpp | 124 +++++++++++++++++--------------- src/hal/usart.h | 159 ++++++++++++++++++++++-------------------- src/main.cpp | 36 +++++----- 5 files changed, 184 insertions(+), 154 deletions(-) create mode 100644 src/hal/adc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 239ae85..16f8f69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,8 +183,14 @@ target_include_directories(firmware PRIVATE include src) target_compile_options(firmware PRIVATE -Wdouble-promotion) target_sources( - firmware PRIVATE src/main.cpp src/hal/avr/cpu.cpp src/hal/avr/usart.cpp src/modules/protocol.cpp - src/modules/buttons.cpp src/modules/leds.cpp + firmware + PRIVATE src/main.cpp + src/hal/avr/cpu.cpp + src/hal/avr/usart.cpp + src/hal/adc.cpp + src/modules/protocol.cpp + src/modules/buttons.cpp + src/modules/leds.cpp ) set_property( diff --git a/src/hal/adc.cpp b/src/hal/adc.cpp new file mode 100644 index 0000000..c97b9c3 --- /dev/null +++ b/src/hal/adc.cpp @@ -0,0 +1,9 @@ +#include "adc.h" + +namespace hal { +namespace adc { + + uint16_t ReadADC(uint8_t adc) { return 0; } + +} // namespace adc +} // namespace hal diff --git a/src/hal/avr/usart.cpp b/src/hal/avr/usart.cpp index dc64b3e..1dac360 100644 --- a/src/hal/avr/usart.cpp +++ b/src/hal/avr/usart.cpp @@ -1,72 +1,82 @@ #include "../usart.h" #include -uint8_t hal::USART::Read() { - uint8_t c = 0; - this->rx_buf.ConsumeFirst(c); - return c; -} +namespace hal { +namespace usart { -void hal::USART::Write(uint8_t c) { - _written = true; - // If the buffer and the data register is empty, just write the byte - // to the data register and be done. This shortcut helps - // significantly improve the effective datarate at high (> - // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. - if (tx_buf.IsEmpty() && (husart->UCSRxA & (1 << 5))) { - husart->UDRx = c; - husart->UCSRxA |= (1 << 6); - return; + USART usart1; + + uint8_t USART::Read() { + uint8_t c = 0; + rx_buf.ConsumeFirst(c); + return c; } - // If the output buffer is full, there's nothing for it other than to - // wait for the interrupt handler to empty it a bit - while (!tx_buf.push_back_DontRewrite(c)) { - if (bit_is_clear(SREG, SREG_I)) { - // Interrupts are disabled, so we'll have to poll the data - // register empty flag ourselves. If it is set, pretend an - // interrupt has happened and call the handler to free up - // space for us. - if (husart->UCSRxA & (1 << 5)) - ISR_UDRE(); - } else { - // nop, the interrupt handler will free up space for us + void USART::Write(uint8_t c) { + _written = true; + // If the buffer and the data register is empty, just write the byte + // to the data register and be done. This shortcut helps + // significantly improve the effective datarate at high (> + // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. + if (tx_buf.IsEmpty() && (husart->UCSRxA & (1 << 5))) { + husart->UDRx = c; + husart->UCSRxA |= (1 << 6); + return; + } + + // If the output buffer is full, there's nothing for it other than to + // wait for the interrupt handler to empty it a bit + while (!tx_buf.push_back_DontRewrite(c)) { + if (bit_is_clear(SREG, SREG_I)) { + // Interrupts are disabled, so we'll have to poll the data + // register empty flag ourselves. If it is set, pretend an + // interrupt has happened and call the handler to free up + // space for us. + if (husart->UCSRxA & (1 << 5)) { + ISR_UDRE(); + } + } else { + // nop, the interrupt handler will free up space for us + } + } + + husart->UCSRxB |= (1 << 5); //enable UDRE interrupt + } + + void USART::Flush() { + // If we have never written a byte, no need to flush. This special + // case is needed since there is no way to force the TXC (transmit + // complete) bit to 1 during initialization + if (!_written) { + return; + } + + while ((husart->UCSRxB & (1 << 5)) || ~(husart->UCSRxA & (1 << 6))) { + if (bit_is_clear(SREG, SREG_I) && (husart->UCSRxB & (1 << 5))) + // Interrupts are globally disabled, but the DR empty + // interrupt should be enabled, so poll the DR empty flag to + // prevent deadlock + if (husart->UCSRxA & (1 << 5)) { + ISR_UDRE(); + } + } + // If we get here, nothing is queued anymore (DRIE is disabled) and + // the hardware finished tranmission (TXC is set). + } + + void USART::puts(const char *str) { + while (*str) { + Write(*str++); } } - husart->UCSRxB |= (1 << 5); //enable UDRE interrupt -} - -void hal::USART::Flush() { - // If we have never written a byte, no need to flush. This special - // case is needed since there is no way to force the TXC (transmit - // complete) bit to 1 during initialization - if (!_written) - return; - - while ((husart->UCSRxB & (1 << 5)) || ~(husart->UCSRxA & (1 << 6))) { - if (bit_is_clear(SREG, SREG_I) && (husart->UCSRxB & (1 << 5))) - // Interrupts are globally disabled, but the DR empty - // interrupt should be enabled, so poll the DR empty flag to - // prevent deadlock - if (husart->UCSRxA & (1 << 5)) - ISR_UDRE(); - } - // If we get here, nothing is queued anymore (DRIE is disabled) and - // the hardware finished tranmission (TXC is set). -} - -void hal::USART::puts(const char *str) { - while (*str) - this->Write(*str++); -} - -hal::USART usart1(USART1); +} // namespace usart +} // namespace hal ISR(USART1_RX_vect) { - usart1.ISR_RX(); + hal::usart::usart1.ISR_RX(); } ISR(USART1_UDRE_vect) { - usart1.ISR_UDRE(); + hal::usart::usart1.ISR_UDRE(); } diff --git a/src/hal/usart.h b/src/hal/usart.h index 06b91ce..080e5cc 100644 --- a/src/hal/usart.h +++ b/src/hal/usart.h @@ -8,92 +8,99 @@ /// for >1 USART interfaces namespace hal { -class USART { -public: - struct USART_TypeDef { - volatile uint8_t UCSRxA; - volatile uint8_t UCSRxB; - volatile uint8_t UCSRxC; - volatile uint8_t UCSRxD; - volatile uint16_t UBRRx; - volatile uint8_t UDRx; - }; +namespace usart { - struct USART_InitTypeDef { - hal::gpio::GPIO_pin rx_pin; - hal::gpio::GPIO_pin tx_pin; - uint32_t baudrate; - }; + class USART { + public: + struct USART_TypeDef { + volatile uint8_t UCSRxA; + volatile uint8_t UCSRxB; + volatile uint8_t UCSRxC; + volatile uint8_t UCSRxD; + volatile uint16_t UBRRx; + volatile uint8_t UDRx; + }; - /// @returns current character from the UART without extracting it from the read buffer - uint8_t Peek() const { - return rx_buf.GetFirstIfAble(); - } - /// @returns true if there are no bytes to be read - bool ReadEmpty() const { - return rx_buf.IsEmpty(); - } - /// @returns current character from the UART and extracts it from the read buffer - uint8_t Read(); + struct USART_InitTypeDef { + hal::gpio::GPIO_pin rx_pin; + hal::gpio::GPIO_pin tx_pin; + uint32_t baudrate; + }; - /// @param c character to be pushed into the TX buffer (to be sent) - void Write(uint8_t c); - /// @param str c string to be sent. NL is appended - void puts(const char *str); - /// @returns true if there is at least one byte free in the TX buffer (i.e. some space to add a character to be sent) - bool CanWrite() const { - return tx_buf.CanPush(); - } - /// blocks until the TX buffer was successfully transmitted - void Flush(); - - /// Initializes USART interface - __attribute__((always_inline)) inline void Init(USART_InitTypeDef *const conf) { - gpio::Init(conf->rx_pin, gpio::GPIO_InitTypeDef(gpio::Mode::input, gpio::Level::low)); - gpio::Init(conf->tx_pin, gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::low)); - husart->UBRRx = (((double)(F_CPU)) / (((double)(conf->baudrate)) * 8.0) - 1.0 + 0.5); - husart->UCSRxA = (1 << 1); // Set double baudrate setting. Clear all other status bits/flags - // husart->UCSRxC |= (1 << 3); // 2 stop bits. Preserve data size setting - husart->UCSRxD = 0; //disable hardware flow control. This register is reserved on all AVR devides with USART. - husart->UCSRxB = (1 << 3) | (1 << 4) | (1 << 7); // Turn on the transmission and reception circuitry and enable the RX interrupt - } - - /// implementation of the receive ISR's body - __attribute__((always_inline)) inline void ISR_RX() { - if (husart->UCSRxA & (1 << 4)) { - (void)husart->UDRx; - } else { - rx_buf.push_back_DontRewrite(husart->UDRx); + /// @returns current character from the UART without extracting it from the read buffer + uint8_t Peek() const { + return rx_buf.GetFirstIfAble(); } - } - /// implementation of the transmit ISR's body - __attribute__((always_inline)) inline void ISR_UDRE() { - uint8_t c = 0; - tx_buf.ConsumeFirst(c); - husart->UDRx = c; + /// @returns true if there are no bytes to be read + bool ReadEmpty() const { + return rx_buf.IsEmpty(); + } + /// @returns current character from the UART and extracts it from the read buffer + uint8_t Read(); - // clear the TXC bit -- "can be cleared by writing a one to its bit - // location". This makes sure flush() won't return until the bytes - // actually got written - husart->UCSRxA |= (1 << 6); + /// @param c character to be pushed into the TX buffer (to be sent) + void Write(uint8_t c); + /// @param str c string to be sent. NL is appended + void puts(const char *str); + /// @returns true if there is at least one byte free in the TX buffer (i.e. some space to add a character to be sent) + bool CanWrite() const { + return tx_buf.CanPush(); + } + /// blocks until the TX buffer was successfully transmitted + void Flush(); - if (tx_buf.IsEmpty()) - husart->UCSRxB &= ~(1 << 5); // disable UDRE interrupt - } + /// Initializes USART interface + __attribute__((always_inline)) inline void Init(USART_InitTypeDef *const conf) { + gpio::Init(conf->rx_pin, gpio::GPIO_InitTypeDef(gpio::Mode::input, gpio::Level::low)); + gpio::Init(conf->tx_pin, gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::low)); + husart->UBRRx = (((double)(F_CPU)) / (((double)(conf->baudrate)) * 8.0) - 1.0 + 0.5); + husart->UCSRxA = (1 << 1); // Set double baudrate setting. Clear all other status bits/flags + // husart->UCSRxC |= (1 << 3); // 2 stop bits. Preserve data size setting + husart->UCSRxD = 0; //disable hardware flow control. This register is reserved on all AVR devides with USART. + husart->UCSRxB = (1 << 3) | (1 << 4) | (1 << 7); // Turn on the transmission and reception circuitry and enable the RX interrupt + } - USART(USART_TypeDef *husart) - : husart(husart) {}; + /// implementation of the receive ISR's body + __attribute__((always_inline)) inline void ISR_RX() { + if (husart->UCSRxA & (1 << 4)) { + (void)husart->UDRx; + } else { + rx_buf.push_back_DontRewrite(husart->UDRx); + } + } + /// implementation of the transmit ISR's body + __attribute__((always_inline)) inline void ISR_UDRE() { + uint8_t c = 0; + tx_buf.ConsumeFirst(c); + husart->UDRx = c; -private: - // IO base address - USART_TypeDef *husart; - bool _written; + // clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written + husart->UCSRxA |= (1 << 6); - CircleBuffer tx_buf; - CircleBuffer rx_buf; -}; + if (tx_buf.IsEmpty()) + husart->UCSRxB &= ~(1 << 5); // disable UDRE interrupt + } + USART() = default; + void Init(USART_TypeDef *conf) { + husart = conf; + } + + private: + // IO base address + USART_TypeDef *husart; + bool _written; + + CircleBuffer tx_buf; + CircleBuffer rx_buf; + }; + + /// beware - normally we'd make a singleton, but avr-gcc generates suboptimal code for them, therefore we only keep this extern variable + extern USART usart1; + +} // namespace usart } // namespace hal #define USART1 ((hal::USART::USART_TypeDef *)&UCSR1A) -extern hal::USART usart1; diff --git a/src/main.cpp b/src/main.cpp index cb72bca..0667feb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,8 +13,6 @@ #include "logic/mm_control.h" -static hal::UART uart; - static modules::protocol::Protocol protocol; static modules::buttons::Buttons buttons; static modules::leds::LEDs leds; @@ -48,18 +46,18 @@ void TmpPlayground() { // if (hal::gpio::ReadPin(GPIO_pin(GPIOB, 7)) == hal::gpio::Level::low) // break; - sei(); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); - usart1.puts("1234567890\n"); + sei(); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); + hal::usart::usart1.puts("1234567890\n"); } /// One-time setup of HW and SW components @@ -76,12 +74,12 @@ void setup() { // @@TODO if the shift register doesn't work we really can't signalize anything, only internal variables will be accessible if the UART works - USART::USART_InitTypeDef usart_conf = { + hal::usart::USART::USART_InitTypeDef usart_conf = { .rx_pin = gpio::GPIO_pin(GPIOD, 2), .tx_pin = gpio::GPIO_pin(GPIOD, 3), .baudrate = 115200, - usart1.Init(&usart_conf); - + }; + hal::usart::usart1.Init(&usart_conf); leds.SetMode(3, false, modules::leds::Mode::on); // shr::Send(leds.Step(0)); @@ -115,8 +113,8 @@ void ProcessRequestMsg(const modules::protocol::RequestMsg &rq) { /// @returns true if a request was successfully finished bool CheckMsgs() { using mpd = modules::protocol::DecodeStatus; - while (!uart.ReadEmpty()) { - switch (protocol.DecodeRequest(uart.Read())) { + while (!hal::usart::usart1.ReadEmpty()) { + switch (protocol.DecodeRequest(hal::usart::usart1.Read())) { case mpd::MessageCompleted: // process the input message return true;