USART hal prototype
parent
fc6fc5b2ca
commit
661eeb368f
|
|
@ -176,7 +176,10 @@ if(CMAKE_CROSSCOMPILING)
|
||||||
# generate linker map file
|
# generate linker map file
|
||||||
target_link_options(firmware PUBLIC -Wl,-Map=firmware.map)
|
target_link_options(firmware PUBLIC -Wl,-Map=firmware.map)
|
||||||
|
|
||||||
target_sources(firmware PRIVATE src/main.cpp src/hal/avr/cpu.cpp src/modules/protocol.cpp)
|
target_sources(
|
||||||
|
firmware PRIVATE src/main.cpp src/hal/avr/cpu.cpp src/modules/protocol.cpp
|
||||||
|
src/hal/avr/usart.cpp
|
||||||
|
)
|
||||||
|
|
||||||
else()
|
else()
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "../usart.h"
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
uint8_t hal::USART::Read() {
|
||||||
|
uint8_t c = 0;
|
||||||
|
this->rx_buf.ConsumeFirst(c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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);
|
||||||
|
|
||||||
|
ISR(USART1_RX_vect) {
|
||||||
|
usart1.ISR_RX();
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(USART1_UDRE_vect) {
|
||||||
|
usart1.ISR_UDRE();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
// circle_buffer.hpp
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
/*****************************************************************************/
|
||||||
|
// general circular buffer
|
||||||
|
// you can never use entire size
|
||||||
|
// because write position (end) cannot be equal to begin
|
||||||
|
// because begin == end == empty
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
class CircleBuffer {
|
||||||
|
public:
|
||||||
|
using Elem = T;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
T data[SIZE];
|
||||||
|
volatile size_t begin; // position of first element
|
||||||
|
volatile size_t end; // position behind last element == write position
|
||||||
|
volatile size_t pushed;
|
||||||
|
static void incrementIndex(volatile size_t &index) { index = (index + 1) % SIZE; }
|
||||||
|
static void decrementIndex(volatile size_t &index) { index = (index + SIZE - 1) % SIZE; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
CircleBuffer()
|
||||||
|
: begin(0)
|
||||||
|
, end(0)
|
||||||
|
, pushed(0) {}
|
||||||
|
|
||||||
|
void push_back(T elem);
|
||||||
|
bool push_back_DontRewrite(T elem);
|
||||||
|
size_t Count() const { return (end + SIZE - begin) % SIZE; }
|
||||||
|
bool IsEmpty() const { return begin == end; }
|
||||||
|
bool CanPush() const {
|
||||||
|
size_t index = begin;
|
||||||
|
incrementIndex(index);
|
||||||
|
return (index != end);
|
||||||
|
}
|
||||||
|
size_t PushedCount() const { return pushed; }
|
||||||
|
|
||||||
|
constexpr size_t Size() const { return SIZE; }
|
||||||
|
|
||||||
|
bool ConsumeFirst(T &elem); // data must be processed before next push_back
|
||||||
|
bool ConsumeLast(T &elem); // data must be processed before next push_back
|
||||||
|
const T &GetFirstIfAble() const; // data must be processed before next push_back, must not be empty
|
||||||
|
const T &GetLastIfAble() const; // data must be processed before next push_back, must not be empty
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
void CircleBuffer<T, SIZE>::push_back(T elem) {
|
||||||
|
data[end] = elem;
|
||||||
|
incrementIndex(end);
|
||||||
|
if (begin == end) { //begin just was erased, set new begin
|
||||||
|
incrementIndex(begin);
|
||||||
|
}
|
||||||
|
++pushed;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
bool CircleBuffer<T, SIZE>::push_back_DontRewrite(T elem) {
|
||||||
|
size_t index = begin;
|
||||||
|
incrementIndex(index);
|
||||||
|
if (index != end) {
|
||||||
|
push_back(elem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
bool CircleBuffer<T, SIZE>::ConsumeFirst(T &elem) {
|
||||||
|
if (IsEmpty())
|
||||||
|
return false;
|
||||||
|
elem = GetFirstIfAble();
|
||||||
|
incrementIndex(begin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
bool CircleBuffer<T, SIZE>::ConsumeLast(T &elem) {
|
||||||
|
if (IsEmpty())
|
||||||
|
return false;
|
||||||
|
elem = GetLastIfAble();
|
||||||
|
decrementIndex(end);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
const T &CircleBuffer<T, SIZE>::GetFirstIfAble() const {
|
||||||
|
return data[begin];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, size_t SIZE>
|
||||||
|
const T &CircleBuffer<T, SIZE>::GetLastIfAble() const {
|
||||||
|
size_t index = end;
|
||||||
|
decrementIndex(index);
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/// UART interface
|
|
||||||
/// @@TODO decide, if this class will behave like a singleton, or there will be multiple classes
|
|
||||||
/// for >1 UART interfaces
|
|
||||||
|
|
||||||
namespace hal {
|
|
||||||
class UART {
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @returns current character from the UART without extracting it from the read buffer
|
|
||||||
uint8_t Peek() const;
|
|
||||||
/// @returns true if there are no bytes to be read
|
|
||||||
bool ReadEmpty() const;
|
|
||||||
/// @returns current character from the UART and extracts it from the read buffer
|
|
||||||
uint8_t Read();
|
|
||||||
|
|
||||||
/// @param c character to be pushed into the TX buffer (to be sent)
|
|
||||||
void Write(uint8_t c);
|
|
||||||
/// @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;
|
|
||||||
/// blocks until the TX buffer was successfully transmitted
|
|
||||||
void Flush();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// implementation of the receive ISR's body
|
|
||||||
void ISR_RX();
|
|
||||||
/// implementation of the transmit ISR's body
|
|
||||||
void ISR_TX();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace hal
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
#pragma once
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "circle_buffer.hpp"
|
||||||
|
/// USART interface
|
||||||
|
/// @@TODO decide, if this class will behave like a singleton, or there will be multiple classes
|
||||||
|
/// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct USART_InitTypeDef {
|
||||||
|
hal::gpio::GPIO_pin rx_pin;
|
||||||
|
hal::gpio::GPIO_pin tx_pin;
|
||||||
|
uint32_t baudrate;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @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();
|
||||||
|
|
||||||
|
/// @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (tx_buf.IsEmpty())
|
||||||
|
husart->UCSRxB &= ~(1 << 5); // disable UDRE interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
USART(USART_TypeDef *husart)
|
||||||
|
: husart(husart) {};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// IO base address
|
||||||
|
USART_TypeDef *husart;
|
||||||
|
bool _written;
|
||||||
|
|
||||||
|
CircleBuffer<uint8_t, 32> tx_buf;
|
||||||
|
CircleBuffer<uint8_t, 32> rx_buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hal
|
||||||
|
|
||||||
|
#define USART1 ((hal::USART::USART_TypeDef *)&UCSR1A)
|
||||||
|
extern hal::USART usart1;
|
||||||
86
src/main.cpp
86
src/main.cpp
|
|
@ -1,42 +1,65 @@
|
||||||
#include "logic/mm_control.h"
|
#include "logic/mm_control.h"
|
||||||
#include "hal/gpio.h"
|
#include "hal/gpio.h"
|
||||||
#include "hal/spi.h"
|
#include "hal/spi.h"
|
||||||
|
#include "hal/usart.h"
|
||||||
#include "pins.h"
|
#include "pins.h"
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
/// One-time setup of HW and SW components
|
/// One-time setup of HW and SW components
|
||||||
/// Called before entering the loop() function
|
/// Called before entering the loop() function
|
||||||
void setup() {
|
void setup() {
|
||||||
using namespace hal;
|
using namespace hal;
|
||||||
|
|
||||||
spi::SPI_InitTypeDef spi_conf = {
|
// spi::SPI_InitTypeDef spi_conf = {
|
||||||
.miso_pin = gpio::GPIO_pin(TMC2130_SPI_MISO_PIN),
|
// .miso_pin = gpio::GPIO_pin(TMC2130_SPI_MISO_PIN),
|
||||||
.mosi_pin = gpio::GPIO_pin(TMC2130_SPI_MOSI_PIN),
|
// .mosi_pin = gpio::GPIO_pin(TMC2130_SPI_MOSI_PIN),
|
||||||
.sck_pin = gpio::GPIO_pin(TMC2130_SPI_SCK_PIN),
|
// .sck_pin = gpio::GPIO_pin(TMC2130_SPI_SCK_PIN),
|
||||||
.ss_pin = gpio::GPIO_pin(TMC2130_SPI_SS_PIN),
|
// .ss_pin = gpio::GPIO_pin(TMC2130_SPI_SS_PIN),
|
||||||
.prescaler = 2, //4mhz
|
// .prescaler = 2, //4mhz
|
||||||
.cpha = 1,
|
// .cpha = 1,
|
||||||
.cpol = 1,
|
// .cpol = 1,
|
||||||
};
|
// };
|
||||||
spi::Init(SPI0, &spi_conf);
|
// spi::Init(SPI0, &spi_conf);
|
||||||
|
|
||||||
// SPI example
|
// // SPI example
|
||||||
gpio::Init(gpio::GPIO_pin(GPIOC, 6), gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::high));
|
// gpio::Init(gpio::GPIO_pin(GPIOC, 6), gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::high));
|
||||||
uint8_t dat[5];
|
// uint8_t dat[5];
|
||||||
gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low);
|
// gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low);
|
||||||
spi::TxRx(SPI0, 0x01);
|
// spi::TxRx(SPI0, 0x01);
|
||||||
spi::TxRx(SPI0, 0x00);
|
// spi::TxRx(SPI0, 0x00);
|
||||||
spi::TxRx(SPI0, 0x00);
|
// spi::TxRx(SPI0, 0x00);
|
||||||
spi::TxRx(SPI0, 0x00);
|
// spi::TxRx(SPI0, 0x00);
|
||||||
spi::TxRx(SPI0, 0x00);
|
// spi::TxRx(SPI0, 0x00);
|
||||||
gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high);
|
// gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high);
|
||||||
gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low);
|
// gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low);
|
||||||
dat[0] = spi::TxRx(SPI0, 0x00);
|
// dat[0] = spi::TxRx(SPI0, 0x00);
|
||||||
dat[1] = spi::TxRx(SPI0, 0x00);
|
// dat[1] = spi::TxRx(SPI0, 0x00);
|
||||||
dat[2] = spi::TxRx(SPI0, 0x00);
|
// dat[2] = spi::TxRx(SPI0, 0x00);
|
||||||
dat[3] = spi::TxRx(SPI0, 0x00);
|
// dat[3] = spi::TxRx(SPI0, 0x00);
|
||||||
dat[4] = spi::TxRx(SPI0, 0x00);
|
// dat[4] = spi::TxRx(SPI0, 0x00);
|
||||||
gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high);
|
// gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high);
|
||||||
(void)dat;
|
// (void)dat;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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");
|
||||||
|
// usart1.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main loop of the firmware
|
/// Main loop of the firmware
|
||||||
|
|
@ -59,11 +82,8 @@ void loop() {
|
||||||
int main() {
|
int main() {
|
||||||
setup();
|
setup();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
using namespace hal::gpio;
|
if (!usart1.ReadEmpty())
|
||||||
WritePin(GPIO_pin(GPIOB, 5), Level::low);
|
usart1.Write(usart1.Read());
|
||||||
TogglePin(GPIO_pin(GPIOB, 6));
|
|
||||||
if (hal::gpio::ReadPin(GPIO_pin(GPIOB, 7)) == hal::gpio::Level::low)
|
|
||||||
break;
|
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue