mirror of
https://github.com/WeActStudio/ArduinoCore-AT32F4.git
synced 2026-05-21 01:12:00 +00:00
431 lines
11 KiB
C++
431 lines
11 KiB
C++
/*
|
|
* MIT License
|
|
* Copyright (c) 2017 - 2022 _VIFEXTech
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
#include "SPI.h"
|
|
|
|
#define SPI1_CLOCK (F_CPU)
|
|
#define SPI2_CLOCK (F_CPU)
|
|
#define SPI3_CLOCK (F_CPU)
|
|
|
|
SPIClass::SPIClass(spi_type* spix)
|
|
: SPIx(spix)
|
|
, SPI_Clock(0)
|
|
{
|
|
memset(&spi_init_struct, 0, sizeof(spi_init_struct));
|
|
}
|
|
|
|
void SPIClass::SPI_Settings(
|
|
spi_master_slave_mode_type master_slave_mode,
|
|
spi_frame_bit_num_type frame_bit_num,
|
|
uint16_t SPI_MODEx,
|
|
spi_cs_mode_type cs_mode,
|
|
spi_mclk_freq_div_type mclk_freq_div,
|
|
spi_first_bit_type first_bit)
|
|
{
|
|
spi_clock_polarity_type clock_polarity;
|
|
spi_clock_phase_type clock_phase;
|
|
spi_enable(SPIx, FALSE);
|
|
|
|
switch(SPI_MODEx)
|
|
{
|
|
case 0:
|
|
clock_polarity = SPI_CLOCK_POLARITY_LOW;
|
|
clock_phase = SPI_CLOCK_PHASE_1EDGE;
|
|
break;
|
|
case 1:
|
|
clock_polarity = SPI_CLOCK_POLARITY_LOW;
|
|
clock_phase = SPI_CLOCK_PHASE_2EDGE;
|
|
break;
|
|
case 2:
|
|
clock_polarity = SPI_CLOCK_POLARITY_HIGH;
|
|
clock_phase = SPI_CLOCK_PHASE_1EDGE;
|
|
break;
|
|
case 3:
|
|
clock_polarity = SPI_CLOCK_POLARITY_HIGH;
|
|
clock_phase = SPI_CLOCK_PHASE_2EDGE;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
spi_default_para_init(&spi_init_struct);
|
|
spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
|
|
spi_init_struct.master_slave_mode = master_slave_mode;
|
|
spi_init_struct.frame_bit_num = frame_bit_num;
|
|
spi_init_struct.clock_polarity = clock_polarity;
|
|
spi_init_struct.clock_phase = clock_phase;
|
|
spi_init_struct.cs_mode_selection = cs_mode;
|
|
spi_init_struct.mclk_freq_division = mclk_freq_div;
|
|
spi_init_struct.first_bit_transmission = first_bit;
|
|
spi_init(SPIx, &spi_init_struct);
|
|
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::begin(void)
|
|
{
|
|
spi_i2s_reset(SPIx);
|
|
if(SPIx == SPI1)
|
|
{
|
|
SPI_Clock = SPI1_CLOCK;
|
|
crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);
|
|
pinMode(PA5, OUTPUT_AF_PP);
|
|
pinMode(PA6, OUTPUT_AF_PP);
|
|
pinMode(PA7, OUTPUT_AF_PP);
|
|
}
|
|
else if(SPIx == SPI2)
|
|
{
|
|
SPI_Clock = SPI2_CLOCK;
|
|
crm_periph_clock_enable(CRM_SPI2_PERIPH_CLOCK, TRUE);
|
|
pinMode(PB13, OUTPUT_AF_PP);
|
|
pinMode(PB14, OUTPUT_AF_PP);
|
|
pinMode(PB15, OUTPUT_AF_PP);
|
|
|
|
}
|
|
else if(SPIx == SPI3)
|
|
{
|
|
SPI_Clock = SPI3_CLOCK;
|
|
crm_periph_clock_enable(CRM_SPI3_PERIPH_CLOCK, TRUE);
|
|
pinMode(PB3, OUTPUT_AF_PP);
|
|
pinMode(PB4, OUTPUT_AF_PP);
|
|
pinMode(PB5, OUTPUT_AF_PP);
|
|
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
SPI_Settings(
|
|
SPI_MODE_MASTER,
|
|
SPI_FRAME_8BIT,
|
|
SPI_MODE0,
|
|
SPI_CS_SOFTWARE_MODE,
|
|
SPI_MCLK_DIV_8,
|
|
SPI_FIRST_BIT_MSB
|
|
);
|
|
}
|
|
|
|
void SPIClass::begin(uint32_t clock, uint16_t dataOrder, uint16_t dataMode)
|
|
{
|
|
begin();
|
|
setClock(clock);
|
|
setBitOrder(dataOrder);
|
|
setDataMode(dataMode);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::begin(SPISettings settings)
|
|
{
|
|
begin();
|
|
setClock(settings.clock);
|
|
setBitOrder(settings.bitOrder);
|
|
setDataMode(settings.dataMode);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::beginSlave(void)
|
|
{
|
|
begin();
|
|
SPI_Settings(
|
|
SPI_MODE_SLAVE,
|
|
SPI_FRAME_8BIT,
|
|
SPI_MODE0,
|
|
SPI_CS_SOFTWARE_MODE,
|
|
SPI_MCLK_DIV_16,
|
|
SPI_FIRST_BIT_MSB
|
|
);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::end(void)
|
|
{
|
|
spi_enable(SPIx, FALSE);
|
|
}
|
|
|
|
void SPIClass::setClock(uint32_t clock)
|
|
{
|
|
if(clock == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static const spi_mclk_freq_div_type mclk_freq_div_map[] =
|
|
{
|
|
SPI_MCLK_DIV_2,
|
|
SPI_MCLK_DIV_2,
|
|
SPI_MCLK_DIV_4,
|
|
SPI_MCLK_DIV_8,
|
|
SPI_MCLK_DIV_16,
|
|
SPI_MCLK_DIV_32,
|
|
SPI_MCLK_DIV_64,
|
|
SPI_MCLK_DIV_128,
|
|
SPI_MCLK_DIV_256,
|
|
SPI_MCLK_DIV_512,
|
|
SPI_MCLK_DIV_1024,
|
|
};
|
|
const uint8_t mapSize = sizeof(mclk_freq_div_map) / sizeof(mclk_freq_div_map[0]);
|
|
uint32_t clockDiv = SPI_Clock / clock;
|
|
uint8_t mapIndex = 0;
|
|
|
|
while(clockDiv > 1)
|
|
{
|
|
clockDiv = clockDiv >> 1;
|
|
mapIndex++;
|
|
}
|
|
|
|
if(mapIndex >= mapSize)
|
|
{
|
|
mapIndex = mapSize - 1;
|
|
}
|
|
|
|
spi_init_struct.mclk_freq_division = mclk_freq_div_map[mapIndex];
|
|
spi_init(SPIx, &spi_init_struct);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::setClockDivider(uint32_t Div)
|
|
{
|
|
if(Div == 0)
|
|
{
|
|
Div = 1;
|
|
}
|
|
#if SPI_CLASS_AVR_COMPATIBILITY_MODE
|
|
setClock(16000000 / Div); // AVR:16MHz
|
|
#else
|
|
setClock(SPI_Clock / Div);
|
|
#endif
|
|
}
|
|
|
|
void SPIClass::setBitOrder(uint16_t bitOrder)
|
|
{
|
|
spi_init_struct.first_bit_transmission = (bitOrder == MSBFIRST) ? SPI_FIRST_BIT_MSB : SPI_FIRST_BIT_LSB;
|
|
spi_init(SPIx, &spi_init_struct);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
/* Victor Perez. Added to test changing datasize from 8 to 16 bit modes on the fly.
|
|
* Input parameter should be SPI_CR1_DFF set to 0 or 1 on a 32bit word.
|
|
*
|
|
*/
|
|
void SPIClass::setDataSize(uint32_t datasize)
|
|
{
|
|
spi_init_struct.frame_bit_num = (spi_frame_bit_num_type)datasize;
|
|
spi_init(SPIx, &spi_init_struct);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::setDataMode(uint8_t dataMode)
|
|
{
|
|
/* Notes. As far as I can tell, the AVR numbers for dataMode appear to match the numbers required by the STM32
|
|
|
|
From the AVR doc http://www.atmel.com/images/doc2585.pdf section 2.4
|
|
|
|
SPI Mode CPOL CPHA Shift SCK-edge Capture SCK-edge
|
|
0 0 0 Falling Rising
|
|
1 0 1 Rising Falling
|
|
2 1 0 Rising Falling
|
|
3 1 1 Falling Rising
|
|
|
|
|
|
On the STM32 it appears to be
|
|
|
|
bit 1 - CPOL : Clock polarity
|
|
(This bit should not be changed when communication is ongoing)
|
|
0 : CLK to 0 when idle
|
|
1 : CLK to 1 when idle
|
|
|
|
bit 0 - CPHA : Clock phase
|
|
(This bit should not be changed when communication is ongoing)
|
|
0 : The first clock transition is the first data capture edge
|
|
1 : The second clock transition is the first data capture edge
|
|
|
|
If someone finds this is not the case or sees a logic error with this let me know ;-)
|
|
*/
|
|
spi_clock_polarity_type clock_polarity;
|
|
spi_clock_phase_type clock_phase;
|
|
spi_enable(SPIx, FALSE);
|
|
|
|
switch(dataMode)
|
|
{
|
|
case 0:
|
|
clock_polarity = SPI_CLOCK_POLARITY_LOW;
|
|
clock_phase = SPI_CLOCK_PHASE_1EDGE;
|
|
break;
|
|
case 1:
|
|
clock_polarity = SPI_CLOCK_POLARITY_LOW;
|
|
clock_phase = SPI_CLOCK_PHASE_2EDGE;
|
|
break;
|
|
case 2:
|
|
clock_polarity = SPI_CLOCK_POLARITY_HIGH;
|
|
clock_phase = SPI_CLOCK_PHASE_1EDGE;
|
|
break;
|
|
case 3:
|
|
clock_polarity = SPI_CLOCK_POLARITY_HIGH;
|
|
clock_phase = SPI_CLOCK_PHASE_2EDGE;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
spi_init_struct.clock_polarity = clock_polarity;
|
|
spi_init_struct.clock_phase = clock_phase;
|
|
spi_init(SPIx, &spi_init_struct);
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::beginTransaction(SPISettings settings)
|
|
{
|
|
SPISettings(settings.clock, settings.bitOrder, settings.dataMode);
|
|
|
|
setClock(settings.clock);
|
|
setBitOrder(settings.bitOrder);
|
|
setDataMode(settings.dataMode);
|
|
setDataSize(settings.dataSize);
|
|
|
|
spi_enable(SPIx, TRUE);
|
|
}
|
|
|
|
void SPIClass::beginTransactionSlave(void)
|
|
{
|
|
beginSlave();
|
|
}
|
|
|
|
void SPIClass::endTransaction(void)
|
|
{
|
|
spi_enable(SPIx, FALSE);
|
|
}
|
|
|
|
uint16_t SPIClass::read(void)
|
|
{
|
|
SPI_I2S_WAIT_RX(SPIx);
|
|
return (uint16_t)(SPI_I2S_RXDATA(SPIx));
|
|
}
|
|
|
|
void SPIClass::read(uint8_t *buf, uint32_t len)
|
|
{
|
|
if (len == 0)
|
|
return;
|
|
|
|
SPI_I2S_RXDATA_VOLATILE(SPIx);
|
|
SPI_I2S_TXDATA(SPIx, 0x00FF);
|
|
|
|
while((--len))
|
|
{
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
noInterrupts();
|
|
SPI_I2S_TXDATA(SPIx, 0x00FF);
|
|
SPI_I2S_WAIT_RX(SPIx);
|
|
*buf++ = (uint8_t)SPI_I2S_RXDATA(SPIx);
|
|
interrupts();
|
|
}
|
|
SPI_I2S_WAIT_RX(SPIx);
|
|
*buf++ = (uint8_t)SPI_I2S_RXDATA(SPIx);
|
|
}
|
|
|
|
void SPIClass::write(uint16_t data)
|
|
{
|
|
SPI_I2S_TXDATA(SPIx, data);
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_WAIT_BUSY(SPIx);
|
|
}
|
|
|
|
void SPIClass::write(uint16_t data, uint32_t n)
|
|
{
|
|
while ((n--) > 0)
|
|
{
|
|
SPI_I2S_TXDATA(SPIx, data); // write the data to be transmitted into the SPI_DR register (this clears the TXE flag)
|
|
SPI_I2S_WAIT_TX(SPIx); // wait till Tx empty
|
|
}
|
|
|
|
SPI_I2S_WAIT_BUSY(SPIx); // wait until BSY=0 before returning
|
|
}
|
|
|
|
void SPIClass::write(const uint8_t *data, uint32_t length)
|
|
{
|
|
while (length--)
|
|
{
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_TXDATA(SPIx, *data++);
|
|
}
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_WAIT_BUSY(SPIx);
|
|
}
|
|
|
|
void SPIClass::write(const uint16_t *data, uint32_t length)
|
|
{
|
|
while (length--)
|
|
{
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_TXDATA(SPIx, *data++);
|
|
}
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_WAIT_BUSY(SPIx);
|
|
}
|
|
|
|
uint8_t SPIClass::transfer(uint8_t wr_data) const
|
|
{
|
|
SPI_I2S_RXDATA_VOLATILE(SPIx);
|
|
SPI_I2S_TXDATA(SPIx, wr_data);
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_WAIT_BUSY(SPIx);
|
|
return (uint8_t)SPI_I2S_RXDATA(SPIx);
|
|
}
|
|
|
|
uint16_t SPIClass::transfer16(uint16_t wr_data) const
|
|
{
|
|
SPI_I2S_RXDATA_VOLATILE(SPIx);
|
|
SPI_I2S_TXDATA(SPIx, wr_data);
|
|
SPI_I2S_WAIT_TX(SPIx);
|
|
SPI_I2S_WAIT_BUSY(SPIx);
|
|
return (uint16_t)SPI_I2S_RXDATA(SPIx);
|
|
}
|
|
|
|
uint8_t SPIClass::send(uint8_t data)
|
|
{
|
|
this->write(data);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t SPIClass::send(uint8_t *buf, uint32_t len)
|
|
{
|
|
this->write(buf, len);
|
|
return len;
|
|
}
|
|
|
|
uint8_t SPIClass::recv(void)
|
|
{
|
|
return this->read();
|
|
}
|
|
|
|
#if SPI_CLASS_1_ENABLE
|
|
SPIClass SPI(SPI_CLASS_1_SPI);
|
|
#endif
|
|
|
|
#if SPI_CLASS_2_ENABLE
|
|
SPIClass SPI_2(SPI_CLASS_2_SPI);
|
|
#endif
|
|
|
|
#if SPI_CLASS_3_ENABLE
|
|
SPIClass SPI_3(SPI_CLASS_3_SPI);
|
|
#endif
|