Merge pull request #11 from prusa3d/leptun/hal/spi

SPI hal
pull/14/head
DRracer 2021-05-18 07:53:34 +02:00 committed by GitHub
commit c51067f197
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 128 additions and 36 deletions

View File

@ -125,7 +125,7 @@ endif()
if(CMAKE_CROSSCOMPILING)
# mcu related settings
set(MCU_FLAGS -mmcu=atmega32u4)
set(MCU_FLAGS -mmcu=atmega32u4 -DF_CPU=16000000L)
add_compile_options(${MCU_FLAGS})
add_link_options(${MCU_FLAGS})
@ -170,6 +170,8 @@ objcopy(firmware "ihex" ".hex")
add_custom_command(TARGET firmware POST_BUILD COMMAND avr-objdump ARGS -CSd firmware > firmware.txt)
add_custom_command(TARGET firmware POST_BUILD COMMAND avr-size ARGS -C --mcu=atmega32u4 firmware)
# generate linker map file
target_link_options(firmware PUBLIC -Wl,-Map=firmware.map)

View File

@ -2,3 +2,6 @@ Use a class whenever you need to store some context data along with the function
A typical scenario is the UART which uses some RX and TX buffers.
Use a simple C-style otherwise, but it is advised to wrap the interface into a namespace as proposed in existing header files.
`__attribute__((always_inline)) inline` was necessary for most functions because the generated code wasn't efficient enough otherwise.
It will be interesting when the STM32 hal will have to be included as well. Hopefully it won't be a nightmare :P

View File

@ -1,3 +1,4 @@
#pragma once
#include <inttypes.h>
#include <avr/io.h>
@ -30,9 +31,6 @@ namespace gpio {
Mode mode;
Pull pull;
Level level;
inline GPIO_InitTypeDef()
: mode(Mode::input)
, pull(Pull::none) {};
inline GPIO_InitTypeDef(Mode mode, Pull pull)
: mode(mode)
, pull(pull) {};
@ -41,36 +39,50 @@ namespace gpio {
, level(level) {};
};
inline void WritePin(GPIO_TypeDef *const port, const uint8_t pin, Level level) {
struct GPIO_pin {
GPIO_TypeDef *const port;
const uint8_t pin;
inline GPIO_pin(GPIO_TypeDef *const port, const uint8_t pin)
: port(port)
, pin(pin) {};
};
__attribute__((always_inline)) inline void WritePin(const GPIO_pin portPin, Level level) {
if (level == Level::high)
port->PORTx |= (1 << pin);
portPin.port->PORTx |= (1 << portPin.pin);
else
port->PORTx &= ~(1 << pin);
portPin.port->PORTx &= ~(1 << portPin.pin);
}
inline Level ReadPin(GPIO_TypeDef *const port, const uint8_t pin) {
return (Level)(port->PINx & (1 << pin));
__attribute__((always_inline)) inline Level ReadPin(const GPIO_pin portPin) {
return (Level)(portPin.port->PINx & (1 << portPin.pin));
}
inline void TogglePin(GPIO_TypeDef *const port, const uint8_t pin) {
port->PINx |= (1 << pin);
__attribute__((always_inline)) inline void TogglePin(const GPIO_pin portPin) {
portPin.port->PINx |= (1 << portPin.pin);
}
inline void Init(GPIO_TypeDef *const port, const uint8_t pin, GPIO_InitTypeDef GPIO_Init) {
__attribute__((always_inline)) inline void Init(const GPIO_pin portPin, GPIO_InitTypeDef GPIO_Init) {
if (GPIO_Init.mode == Mode::output) {
WritePin(port, pin, GPIO_Init.level);
port->DDRx |= (1 << pin);
WritePin(portPin, GPIO_Init.level);
portPin.port->DDRx |= (1 << portPin.pin);
} else {
port->DDRx &= ~(1 << pin);
WritePin(port, pin, (Level)GPIO_Init.pull);
portPin.port->DDRx &= ~(1 << portPin.pin);
WritePin(portPin, (Level)GPIO_Init.pull);
}
}
}
}
#define GPIOA ((hal::gpio::GPIO_TypeDef *)&PINA)
#define GPIOB ((hal::gpio::GPIO_TypeDef *)&PINB)
#define GPIOC ((hal::gpio::GPIO_TypeDef *)&PINC)
#define GPIOD ((hal::gpio::GPIO_TypeDef *)&PIND)
#define GPIOE ((hal::gpio::GPIO_TypeDef *)&PINE)
#define GPIOF ((hal::gpio::GPIO_TypeDef *)&PINF)
#define GPIOG ((hal::gpio::GPIO_TypeDef *)&PING)
#define GPIOH ((hal::gpio::GPIO_TypeDef *)&PINH)
#define GPIOJ ((hal::gpio::GPIO_TypeDef *)&PINJ)
#define GPIOK ((hal::gpio::GPIO_TypeDef *)&PINK)
#define GPIOL ((hal::gpio::GPIO_TypeDef *)&PINL)

View File

@ -1,11 +0,0 @@
#pragma once
/// Hardware Abstraction Layer for the CPU's features and peripherals
namespace hal {
namespace pins {
/// pin definitions
} // namespace pins
} // namespace hal

48
src/hal/spi.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <inttypes.h>
#include "gpio.h"
/// SPI interface
namespace hal {
namespace spi {
struct SPI_TypeDef {
volatile uint8_t SPCRx;
volatile uint8_t SPSRx;
volatile uint8_t SPDRx;
};
struct SPI_InitTypeDef {
hal::gpio::GPIO_pin miso_pin;
hal::gpio::GPIO_pin mosi_pin;
hal::gpio::GPIO_pin sck_pin;
hal::gpio::GPIO_pin ss_pin;
uint8_t prescaler;
uint8_t cpha;
uint8_t cpol;
};
__attribute__((always_inline)) inline void Init(SPI_TypeDef *const hspi, SPI_InitTypeDef *const conf) {
using namespace hal;
gpio::Init(conf->miso_pin, gpio::GPIO_InitTypeDef(gpio::Mode::input, gpio::Pull::none));
gpio::Init(conf->mosi_pin, gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::low));
gpio::Init(conf->sck_pin, gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::low));
gpio::Init(conf->ss_pin, gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::high)); //the AVR requires this pin to be an output for SPI master mode to work properly.
const uint8_t spi2x = (conf->prescaler == 7) ? 0 : (conf->prescaler & 0x01);
const uint8_t spr = ((conf->prescaler - 1) >> 1) & 0x03;
hspi->SPCRx = (0 << SPIE) | (1 << SPE) | (0 << DORD) | (1 << MSTR) | ((conf->cpol & 0x01) << CPOL) | ((conf->cpha & 0x01) << CPHA) | (spr << SPR0);
hspi->SPSRx = (spi2x << SPI2X);
}
__attribute__((always_inline)) inline uint8_t TxRx(SPI_TypeDef *const hspi, uint8_t val) {
hspi->SPDRx = val;
while (!(hspi->SPSRx & (1 << SPIF)))
;
return hspi->SPDRx;
}
}
}
#define SPI0 ((hal::spi::SPI_TypeDef *)&SPCR)

View File

@ -1,12 +1,42 @@
#include "logic/mm_control.h"
#include "hal/gpio.h"
#include "hal/spi.h"
#include "pins.h"
/// One-time setup of HW and SW components
/// Called before entering the loop() function
void setup() {
using namespace hal::gpio;
hal::gpio::Init(GPIOB, 1, GPIO_InitTypeDef(Mode::output, Level::low));
using namespace hal;
spi::SPI_InitTypeDef spi_conf = {
.miso_pin = gpio::GPIO_pin(TMC2130_SPI_MISO_PIN),
.mosi_pin = gpio::GPIO_pin(TMC2130_SPI_MOSI_PIN),
.sck_pin = gpio::GPIO_pin(TMC2130_SPI_SCK_PIN),
.ss_pin = gpio::GPIO_pin(TMC2130_SPI_SS_PIN),
.prescaler = 2, //4mhz
.cpha = 1,
.cpol = 1,
};
spi::Init(SPI0, &spi_conf);
// SPI example
gpio::Init(gpio::GPIO_pin(GPIOC, 6), gpio::GPIO_InitTypeDef(gpio::Mode::output, gpio::Level::high));
uint8_t dat[5];
gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::low);
spi::TxRx(SPI0, 0x01);
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::low);
dat[0] = spi::TxRx(SPI0, 0x00);
dat[1] = spi::TxRx(SPI0, 0x00);
dat[2] = spi::TxRx(SPI0, 0x00);
dat[3] = spi::TxRx(SPI0, 0x00);
dat[4] = spi::TxRx(SPI0, 0x00);
gpio::WritePin(gpio::GPIO_pin(GPIOC, 6), gpio::Level::high);
(void)dat;
}
/// Main loop of the firmware
@ -24,16 +54,15 @@ void setup(){
/// The idea behind the Step* routines is to keep each automaton non-blocking allowing for some “concurrency”.
/// Some FW components will leverage ISR to do their stuff (UART, motor stepping?, etc.)
void loop() {
}
int main() {
setup();
for (;;) {
using namespace hal::gpio;
WritePin(GPIOB, 5, Level::low);
TogglePin(GPIOB, 6);
if (hal::gpio::ReadPin(GPIOB, 7) == hal::gpio::Level::low)
WritePin(GPIO_pin(GPIOB, 5), Level::low);
TogglePin(GPIO_pin(GPIOB, 6));
if (hal::gpio::ReadPin(GPIO_pin(GPIOB, 7)) == hal::gpio::Level::low)
break;
loop();
}

9
src/pins.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "hal/gpio.h"
/// pin definitions
#define TMC2130_SPI_MISO_PIN GPIOB, 3
#define TMC2130_SPI_MOSI_PIN GPIOB, 2
#define TMC2130_SPI_SCK_PIN GPIOB, 1
#define TMC2130_SPI_SS_PIN GPIOB, 0