mirror of
https://github.com/sipeed/Maixduino.git
synced 2026-03-03 09:04:00 +01:00
400 lines
10 KiB
C++
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));
|
|
}
|
|
|