Files
Maixduino/cores/arduino/SPI.cpp

400 lines
10 KiB
C++

#include "SPI.h"
#include "stdio.h"
#include "utils.h"
#include "fpioa.h"
#include "stdint.h"
#include "stdbool.h"
#include "./kendryte-standalone-sdk/lib/drivers/include/spi.h"
#include "SPI_hal.h"
#include "sysctl.h"
SPIClass SPI;
extern volatile spi_t *const spi[4];
static spi_transfer_width_t sipeed_spi_get_frame_size(size_t data_bit_length)
{
if (data_bit_length < 8)
return SPI_TRANS_CHAR;
else if (data_bit_length < 16)
return SPI_TRANS_SHORT;
return SPI_TRANS_INT;
}
static void sipeed_spi_set_tmod(uint8_t spi_num, uint32_t tmod)
{
configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2);
volatile spi_t *spi_handle = spi[spi_num];
uint8_t tmod_offset = 0;
switch(spi_num)
{
case 0:
case 1:
tmod_offset = 8;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
tmod_offset = 10;
break;
}
set_bit(&spi_handle->ctrlr0, 3 << tmod_offset, tmod << tmod_offset);
}
/**
*
* @param chip_select -1: not use cs
*/
void sipeed_spi_transfer_data_standard(spi_device_num_t spi_num, int8_t chip_select, const uint8_t *tx_buff,uint8_t *rx_buff, size_t len)
{
configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2);
configASSERT(len > 0);
size_t index, fifo_len;
size_t rx_len = len;
size_t tx_len = rx_len;
sipeed_spi_set_tmod(spi_num, SPI_TMOD_TRANS_RECV);
volatile spi_t *spi_handle = spi[spi_num];
uint8_t dfs_offset;
switch(spi_num){
case 0:
case 1:
dfs_offset = 16;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
dfs_offset = 0;
break;
}
uint32_t data_bit_length = (spi_handle->ctrlr0 >> dfs_offset) & 0x1F;
spi_transfer_width_t frame_width = sipeed_spi_get_frame_size(data_bit_length);
spi_handle->ctrlr1 = (uint32_t)(tx_len/frame_width - 1);
spi_handle->ssienr = 0x01;
spi_handle->ser = 1U << chip_select;
uint32_t i = 0;
while (tx_len)
{
fifo_len = 32 - spi_handle->txflr;
fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
switch(frame_width)
{
case SPI_TRANS_INT:
fifo_len = fifo_len / 4 * 4;
for (index = 0; index < fifo_len / 4; index++)
spi_handle->dr[0] = ((uint32_t *)tx_buff)[i++];
break;
case SPI_TRANS_SHORT:
fifo_len = fifo_len / 2 * 2;
for (index = 0; index < fifo_len / 2; index++)
spi_handle->dr[0] = ((uint16_t *)tx_buff)[i++];
break;
default:
for (index = 0; index < fifo_len; index++)
spi_handle->dr[0] = tx_buff[i++];
break;
}
tx_len -= fifo_len;
}
while ((spi_handle->sr & 0x05) != 0x04)
;
if(rx_buff)
{
i = 0;
while (rx_len)
{
fifo_len = spi_handle->rxflr;
fifo_len = fifo_len < rx_len ? fifo_len : rx_len;
switch(frame_width)
{
case SPI_TRANS_INT:
fifo_len = fifo_len / 4 * 4;
for (index = 0; index < fifo_len / 4; index++)
((uint32_t *)rx_buff)[i++] = spi_handle->dr[0];
break;
case SPI_TRANS_SHORT:
fifo_len = fifo_len / 2 * 2;
for (index = 0; index < fifo_len / 2; index++)
((uint16_t *)rx_buff)[i++] = (uint16_t)spi_handle->dr[0];
break;
default:
for (index = 0; index < fifo_len; index++)
rx_buff[i++] = (uint8_t)spi_handle->dr[0];
break;
}
rx_len -= fifo_len;
}
}
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
}
void sipeed_spi_deinit(spi_device_num_t spi_num)
{
volatile spi_t *spi_handle = spi[spi_num];
spi_handle->ssienr = 0x00;
sysctl_clock_disable( sysctl_clock_t(SYSCTL_CLOCK_SPI0 + spi_num));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct{
int8_t pin;
bool used;
} spi_ss_t;
static spi_ss_t g_ss_table[2][4]={
{//SPI0
{.pin = -1, .used = false},
{.pin = -1, .used = false},
{.pin = -1, .used = false},
{.pin = -1, .used = false}
},
{//SPI1
{.pin = -1, .used = false},
{.pin = -1, .used = false},
{.pin = -1, .used = false},
{.pin = -1, .used = false}
}
};
int8_t getSsByPin(uint8_t spi, int8_t pin)
{
uint8_t i;
if(pin<0)
return -1;
for(i=0; i<4; ++i)
{
if(g_ss_table[spi][i].used && g_ss_table[spi][i].pin == pin)
return i;
}
return -1;
}
int8_t getSsNumLast(uint8_t spi)
{
uint8_t i=0, count=0;
for(i=0; i<4; ++i)
{
if(!g_ss_table[spi][i].used)
++count;
}
return count;
}
bool checkSs(uint8_t spi, int8_t ss)
{
int8_t ssPeriph;
if( ss < 0) // not use ss
return true;
ssPeriph = getSsByPin(spi, ss);
if(ssPeriph >= 0)
return true;
if(getSsNumLast(spi) > 0)
return true;
return false;
}
int8_t setSs(uint8_t spi, int8_t pin)
{
uint8_t i=0;
for(i=0; i<4; ++i)
{
if(!g_ss_table[spi][i].used)
{
g_ss_table[spi][i].used = true;
g_ss_table[spi][i].pin = pin;
return i;
}
}
return -1;
}
/**
* @param miso -1: not use miso
* @param mosi must >= 0
* @param ss -1: not use hardware ss
*/
bool checkPinParam(uint8_t spi, int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
{
if(!checkSs(spi, ss))
return false;
//TODO:
return true;
}
bool checkFreq(uint32_t freq)
{
return true;
}
bool checkBitOder(uint8_t bitOrder)
{
//TODO: support LSB
if(bitOrder!=SPI_MSBFIRST)
return false;
return true;
}
bool checkDataMode(uint8_t dataMode)
{
if(dataMode > SPI_MODE3)
return false;
return true;
}
SPIClass::SPIClass(spi_id_t spi_bus)
:_spiNum(spi_bus), _freq(1000000),
_sck(-1), _miso(-1), _mosi(-1), _ss(-1),
_inTransaction(false),
_initPinsInConstruct(false)
{
configASSERT(_spiNum==SPI_SOFT || _spiNum==SPI0 || _spiNum==SPI1);
}
SPIClass::SPIClass(spi_id_t spi_bus, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint32_t freq)
:_spiNum(spi_bus), _freq(freq),
_sck(sck), _miso(miso), _mosi(mosi), _ss(ss),
_inTransaction(false),
_initPinsInConstruct(true)
{
}
/**
*
* @param ss -1: not use
*/
void SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
{
if(_initPinsInConstruct)
{
sck = _sck;
miso = _miso;
mosi = _mosi;
ss = _ss;
}
configASSERT(checkPinParam(_spiNum, sck, miso, mosi, ss));
if(_spiNum == SPI_SOFT)
{
configASSERT(!"Soft SPI Not Support Yet!");
return ;
}
//hardware SPI
if(_spiNum == SPI0)
{
fpioa_set_function(sck, FUNC_SPI0_SCLK);
if( ss >= 0)
{
fpioa_function_t a = (fpioa_function_t)(FUNC_SPI0_SS0+setSs(_spiNum, ss));
fpioa_set_function(ss, a);
}
fpioa_set_function(mosi, FUNC_SPI0_D0);
if(miso>=0)
fpioa_set_function(miso, FUNC_SPI0_D1);
}
else if(_spiNum == SPI1)
{
fpioa_set_function(sck, FUNC_SPI1_SCLK);
if( ss >= 0)
{
fpioa_set_function(ss, (fpioa_function_t)(FUNC_SPI1_SS0+setSs(_spiNum, ss)));
}
fpioa_set_function(mosi, FUNC_SPI1_D0);
if(miso>=0)
fpioa_set_function(miso, FUNC_SPI1_D1);
}
_mosi = mosi;
_miso = miso;
_sck = sck;
_ss = ss;
_ssPeriph = getSsByPin(_spiNum, ss);
if(_ssPeriph<0)
_ssPeriph = 0; // default to cs0 TODO: optimize?
spi_init(spi_device_num_t(_spiNum), spi_work_mode_t(_dataMode), SPI_FF_STANDARD, 8, 0);
spi_set_clk_rate(spi_device_num_t(_spiNum), _freq);
}
void SPIClass::end()
{
sipeed_spi_deinit(spi_device_num_t(_spiNum));
}
void SPIClass::setBitOrder(uint8_t bitOrder)
{
//TODO: support LSB
configASSERT(bitOrder==SPI_MSBFIRST);
}
void SPIClass::setDataMode(uint8_t dataMode)
{
_dataMode = dataMode;
spi_init(spi_device_num_t(_spiNum), spi_work_mode_t(_dataMode), SPI_FF_STANDARD, 8, 0);
}
void SPIClass::setFrequency(uint32_t freq)
{
_freq = freq;
spi_set_clk_rate(spi_device_num_t(_spiNum), _freq);
}
void SPIClass::beginTransaction(SPISettings settings)
{
SPI_MUTEX_LOCK();
_inTransaction = true;
if( settings._freq!=_freq || settings._dataMode!=_dataMode || settings._bitOrder!=_bitOrder)
{
_dataMode = settings._dataMode;
_bitOrder = settings._bitOrder; //TODO: bit order
_freq = settings._freq;
spi_init(spi_device_num_t(_spiNum), spi_work_mode_t(_dataMode), SPI_FF_STANDARD, 8, 0);
spi_set_clk_rate(spi_device_num_t(_spiNum), _freq);
}
}
void SPIClass::endTransaction(void)
{
_inTransaction = false;
SPI_MUTEX_UNLOCK();
}
void SPIClass::transfer(uint8_t * data, uint32_t size)
{
sipeed_spi_transfer_data_standard(spi_device_num_t(_spiNum), _ssPeriph, data, NULL, size);
}
uint8_t SPIClass::transfer(uint8_t data)
{
uint8_t temp;
sipeed_spi_transfer_data_standard(spi_device_num_t(_spiNum), _ssPeriph, &data, &temp, 1);
return temp;
}
void SPIClass::transferBytes(uint8_t * data, uint8_t * out, uint32_t size)
{
sipeed_spi_transfer_data_standard(spi_device_num_t(_spiNum), _ssPeriph, data, out, size);
}
SPISettings::SPISettings(uint32_t freq, uint8_t bitOrder, uint8_t dataMode)
:_freq(freq), _bitOrder(bitOrder), _dataMode(dataMode)
{
configASSERT(checkFreq(freq));
configASSERT(checkBitOder(bitOrder));
configASSERT(checkDataMode(dataMode));
}