Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat pwm spi #92

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
381 changes: 381 additions & 0 deletions src/common/hardware_specific/samd21.cpp

Large diffs are not rendered by default.

261 changes: 261 additions & 0 deletions src/common/hardware_specific/samd21_AdvancedSPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#include "samd21_AdvancedSPI.h"
#include <wiring_private.h>


class SSGuard
{
public:
SSGuard(SAMDAdvancedSPI & spi) : spi(spi), isHoldingGuard(false)
{
if(!spi.hardwareSS && !spi.isSoftwareSSLow)
{
digitalWrite(spi.pinSS, LOW);
spi.isSoftwareSSLow = true;
isHoldingGuard = true;
}
}
~SSGuard()
{
if(isHoldingGuard)
{
digitalWrite(spi.pinSS, HIGH);
spi.isSoftwareSSLow = false;
}
}

SAMDAdvancedSPI & spi;
bool isHoldingGuard;

};


SAMDAdvancedSPI::SAMDAdvancedSPI(SercomChannel sercomChannel, uint8_t pinMOSI, uint8_t pinMISO, uint8_t pinSCK, uint8_t pinSS, bool hardwareSS) :
sercomChannel(sercomChannel), pinMOSI(pinMOSI), pinMISO(pinMISO), pinSCK(pinSCK), pinSS(pinSS), hardwareSS(hardwareSS)
{

}

bool getPad(const SamdPinDefinition * pinDef, SercomChannel sercomChannel, SercomPad & pad, EPioType & peripheral)
{
if(pinDef->sercom == sercomChannel)
{
pad = pinDef->sercom_pad;
peripheral = EPioType::PIO_SERCOM;
return true;
}
if(pinDef->sercom_alt == sercomChannel)
{
pad = pinDef->sercom_pad_alt;
peripheral = EPioType::PIO_SERCOM_ALT;
return true;
}
return false;
}
int SAMDAdvancedSPI::init(SercomSpiClockMode spi_mode, uint32_t clock_speed)
{
debugPrint(F("MOSI: "));
defMOSI = getSamdPinDefinition(pinMOSI);
debugPrint(F("MISO: "));
defMISO = getSamdPinDefinition(pinMISO);
debugPrint(F("SCK : "));
defSCK = getSamdPinDefinition(pinSCK);
debugPrint(F("SS : "));
defSS = getSamdPinDefinition(pinSS);

if(defMOSI == nullptr || defMISO == nullptr || defSCK == nullptr || (hardwareSS && defSS == nullptr))
{
return -1;
}

sercomCfg = getSercom(sercomChannel);

sercomCfg.sercom->SPI.CTRLA.bit.ENABLE = 0;
while(sercomCfg.sercom->SPI.SYNCBUSY.bit.ENABLE);

// Setting NVIC
NVIC_EnableIRQ(sercomCfg.irq);
NVIC_SetPriority (sercomCfg.irq, SERCOM_NVIC_PRIORITY); /* set Priority */

//Setting clock
GCLK_CLKCTRL_Type clckctrl{.reg = 0};
clckctrl.bit.ID = sercomCfg.clockId ;
clckctrl.bit.GEN = GCLK_CLKCTRL_GEN_GCLK0_Val;
clckctrl.bit.CLKEN = 0b1;
GCLK->CLKCTRL.reg = clckctrl.reg;

while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY );

if(!hardwareSS)
{
pinMode(pinSS, OUTPUT);
pinPeripheral(pinSS, EPioType::PIO_OUTPUT);
digitalWrite(pinSS, HIGH);
isSoftwareSSLow = false;
}

//Setting the CTRLB register
SERCOM_SPI_CTRLB_Type ctrlb{.reg = 0};
ctrlb.bit.CHSIZE = SPI_CHAR_SIZE_8_BITS;
ctrlb.bit.RXEN = 0b1;
ctrlb.bit.MSSEN = hardwareSS ? 0b1 : 0b0;
sercomCfg.sercom->SPI.CTRLB.reg = ctrlb.reg;
while(sercomCfg.sercom->SPI.SYNCBUSY.bit.CTRLB);


//Synchronous arithmetic
sercomCfg.sercom->SPI.BAUD.reg = SERCOM_FREQ_REF / (2 * clock_speed) - 1;

//Setting the CTRLA register
SERCOM_SPI_CTRLA_Type ctrla{.reg = 0};
switch (spi_mode)
{
case SERCOM_SPI_MODE_0:
ctrla.bit.CPOL = 0b0;
ctrla.bit.CPHA = 0b0;
break;
case SERCOM_SPI_MODE_1:
ctrla.bit.CPOL = 0b0;
ctrla.bit.CPHA = 0b1;
break;
case SERCOM_SPI_MODE_2:
ctrla.bit.CPOL = 0b1;
ctrla.bit.CPHA = 0b0;
break;
case SERCOM_SPI_MODE_3:
ctrla.bit.CPOL = 0b1;
ctrla.bit.CPHA = 0b1;
break;
default:
debugPrint(F("SPI sercom CLOCK configuration error!"));
return -1;
}

SercomPad MOSI_pad, MISO_pad, SCK_pad, SS_pad = NO_PAD;
EPioType peripheralMOSI, peripheralMISO, peripheralSCK, peripheralSS;


if( getPad(defMOSI, sercomChannel, MOSI_pad, peripheralMOSI )
&& getPad(defMISO, sercomChannel, MISO_pad, peripheralMISO )
&& getPad(defSCK, sercomChannel, SCK_pad , peripheralSCK )
&& (ctrlb.bit.MSSEN == 0 || getPad(defSS, sercomChannel, SS_pad , peripheralSS )))
{

pinPeripheral(pinMOSI, peripheralMOSI);
pinPeripheral(pinMISO, peripheralMISO);
pinPeripheral(pinSCK, peripheralSCK);
if(ctrlb.bit.MSSEN)
pinPeripheral(pinSS, peripheralSS);
}
else
{
debugPrint(F("SPI sercom CHANNEL configuration error!"));
debugPrintf_P(PSTR("desired sercom %d, got [peripheral, pad]: MOSI [%d, %d], MISO [%d, %d], SCK [%d, %d], SS [%d, %d]\n\r"), sercomChannel, peripheralMOSI, MOSI_pad, peripheralMISO, MISO_pad, peripheralSCK, SCK_pad, peripheralSS, SS_pad);

return -1;
}


debugPrintf_P(PSTR("Using sercom [peripheral, pad]: MOSI [%d, %d], MISO [%d, %d], SCK [%d, %d], SS [%d, %d]\n\r"), peripheralMOSI, MOSI_pad, peripheralMISO, MISO_pad, peripheralSCK, SCK_pad, peripheralSS, SS_pad);

if(MOSI_pad == PAD_0 && SCK_pad == PAD_1
&& (ctrlb.bit.MSSEN ? (SS_pad == PAD_2 && MISO_pad == PAD_3) : (MISO_pad == PAD_3 || MISO_pad == PAD_2)))
{
ctrla.bit.DOPO = 0x0;
ctrla.bit.DIPO = MISO_pad;
}
else if(MOSI_pad == PAD_2 && SCK_pad == PAD_3
&& (ctrlb.bit.MSSEN ? (SS_pad == PAD_1 && MISO_pad == PAD_0) : (MISO_pad == PAD_0 || MISO_pad == PAD_1)))
{
ctrla.bit.DOPO = 0x1;
ctrla.bit.DIPO = MISO_pad;
}
else if(MOSI_pad == PAD_3 && SCK_pad == PAD_1
&& (ctrlb.bit.MSSEN ? (SS_pad == PAD_2 && MISO_pad == PAD_0) : (MISO_pad == PAD_0 || MISO_pad == PAD_2)))
{
ctrla.bit.DOPO = 0x2;
ctrla.bit.DIPO = MISO_pad;
}
else if(MOSI_pad == PAD_0 && SCK_pad == PAD_3
&& (ctrlb.bit.MSSEN ? (SS_pad == PAD_1 && MISO_pad == PAD_2) : (MISO_pad == PAD_2 || MISO_pad == PAD_1)))
{
ctrla.bit.DOPO = 0x3;
ctrla.bit.DIPO = MISO_pad;
}
else
{
debugPrint(F("SPI sercom PADS configuration invalid!"));
return -1;
}

debugPrintf_P(PSTR("Using sercom DOPO %d and DIPO %d\n\r"), ctrla.bit.DOPO, ctrla.bit.DIPO);

ctrla.bit.MODE = SERCOM_SPI_CTRLA_MODE_SPI_MASTER_Val;
ctrla.bit.DORD = SercomDataOrder::MSB_FIRST;


ctrla.bit.ENABLE = 0b1; //***enables the SPI***

sercomCfg.sercom->SPI.CTRLA.reg = ctrla.reg;
while(sercomCfg.sercom->SPI.SYNCBUSY.bit.ENABLE);


return 0;

}

byte SAMDAdvancedSPI::spiCalcEvenParity(word value)
{
byte cnt = 0;
byte i;

for (i = 0; i < 16; i++)
{
if (value & 0x1) cnt++;
value >>= 1;
}
return cnt & 0x1;
}


uint8_t SAMDAdvancedSPI::transfer(uint8_t data)
{
SSGuard g(*this);
sercomCfg.sercom->SPI.DATA.bit.DATA = data; // Writing data into Data register
while( sercomCfg.sercom->SPI.INTFLAG.bit.RXC == 0 );
return sercomCfg.sercom->SPI.DATA.bit.DATA; // Reading data
}




uint16_t SAMDAdvancedSPI::transfer16(uint16_t data)
{
union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } t;


SSGuard g(*this);

t.val = data;

if (sercomCfg.sercom->SPI.CTRLA.bit.DORD == SercomDataOrder::LSB_FIRST) {
t.lsb = transfer(t.lsb);
t.msb = transfer(t.msb);
} else {
t.msb = transfer(t.msb);
t.lsb = transfer(t.lsb);
}


return t.val;
}

/**
* Closes the SPI connection
* SPI has an internal SPI-device counter, for each init()-call the close() function must be called exactly 1 time
*/
void SAMDAdvancedSPI::close(){
//Setting the Software Reset bit to 1
sercomCfg.sercom->SPI.CTRLA.bit.SWRST = 1;
//Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0
while(sercomCfg.sercom->SPI.CTRLA.bit.SWRST || sercomCfg.sercom->SPI.SYNCBUSY.bit.SWRST);
}
43 changes: 43 additions & 0 deletions src/common/hardware_specific/samd21_AdvancedSPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once
#include <Arduino.h>

#ifdef _SAMD21_
#include "samd_mcu.h"

class SAMDAdvancedSPI
{
public:

SAMDAdvancedSPI(SercomChannel sercomChannel, uint8_t pinMOSI, uint8_t pinMISO, uint8_t pinSCK, uint8_t pinSS, bool hardwareSS);

int init(SercomSpiClockMode spi_mode, uint32_t clock_speed);

byte spiCalcEvenParity(word value);

uint8_t transfer(uint8_t data);

uint16_t transfer16(uint16_t data);

void close();

// private:


SercomChannel sercomChannel;
const uint8_t pinMOSI;
const uint8_t pinMISO;
const uint8_t pinSCK;
const uint8_t pinSS;
const bool hardwareSS;
bool isSoftwareSSLow;

const SamdPinDefinition * defMOSI;
const SamdPinDefinition * defMISO;
const SamdPinDefinition * defSCK;
const SamdPinDefinition * defSS;
SercomConfig sercomCfg;

friend class SSGuard;
};

#endif
83 changes: 83 additions & 0 deletions src/common/hardware_specific/samd_mcu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include "samd_mcu.h"

#include <SPI.h>

SercomSpiClockMode from_SPI_MODE(int spi_mode)
{
switch (spi_mode)
{
case SPI_MODE0:
return SERCOM_SPI_MODE_0;
case SPI_MODE1:
return SERCOM_SPI_MODE_1;
case SPI_MODE2:
return SERCOM_SPI_MODE_2;
case SPI_MODE3:
return SERCOM_SPI_MODE_3;
default:
return SERCOM_SPI_MODE_0;
}
}

uint32_t computeDSTADDR(uint8_t * startAddress, uint32_t STEPSEL, uint32_t STEPSIZE, uint32_t BEATSIZE, uint32_t BTCNT)
{
/*
p.283 When destination address incrementation is configured (BTCTRL.DSTINC is one), SRCADDR must be set to the
destination address of the last beat transfer in the block transfer. The destination address should be calculated as
follows:
DSTADDR = DSTADDRSTART + BTCNT ⋅ ( BEATSIZE + 1 ) ⋅ 2 STEPSIZE , where BTCTRL.STEPSEL is zero
DSTADDR = DSTADDRSTART + BTCNT ⋅ ( BEATSIZE + 1 ) , where BTCTRL.STEPSEL is one
- DSTADDRSTART is the destination address of the first beat transfer in the block transfer
- BTCNT is the initial number of beats remaining in the block transfer
- BEATSIZE is the configured number of bytes in a beat
- STEPSIZE is the configured number of beats for each incrementation
*/
uint32_t factor = STEPSEL == 0 ? (1 << STEPSIZE) /*2^STEPSIZE*/: 1;

return (uint32_t)(startAddress + BTCNT * (BEATSIZE + 1) * factor); // end address
}


const SamdPinDefinition * getSamdPinDefinition(int arduinoPin)
{
if(arduinoPin < 0)
{
debugPrint(F("getSamdPinDefinition() : pin < 0"));
return nullptr;
}
if((uint32_t)arduinoPin > (PINS_COUNT-1))
{
debugPrintf_P(PSTR("getSamdPinDefinition() : arduino pin %d above %d"), arduinoPin, PINS_COUNT-1);
return nullptr;
}

EPortType port = g_APinDescription[arduinoPin].ulPort;
uint8_t port_pin = g_APinDescription[arduinoPin].ulPin;

if(port_pin > 31)
{
debugPrint(F("getSamdPinDefinition() : port_pin > 31"));
return nullptr;
}

if(port_pin > 31)
{
debugPrint(F("getSamdPinDefinition() : port_pin > 31"));
return nullptr;
}

int8_t index = g_SamdMapPortPin[port][port_pin];

if(index < 0)
{
debugPrint(F("getSamdPinDefinition() : index < 0, the pin doesn't exist!"));
return nullptr;
}

const SamdPinDefinition * rv = &g_SamdPinDefinitions[index];

debugPrintf_P(PSTR("getSamdPinDefinition(): Arduino pin: %d, PORT%c%d, SERCOM%d:PAD[%d], SERCOM_ALT%d:PAD[%d] \n\r"), arduinoPin, 'A' + port, port_pin, rv->sercom, rv->sercom_pad, rv->sercom_alt, rv->sercom_pad_alt);

return rv;
}

Loading