You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
6.1 KiB
271 lines
6.1 KiB
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include "stm32f4xx.h"
|
|
#include "gpio.h"
|
|
#include "spi.h"
|
|
|
|
struct spi_device {
|
|
SPI_TypeDef *base;
|
|
|
|
int apb;
|
|
uint32_t rcc;
|
|
|
|
SPI_InitTypeDef conf;
|
|
|
|
uint32_t cs;
|
|
void (*setup)(struct spi_device *);
|
|
};
|
|
|
|
/* query APB1 clocks */
|
|
static uint32_t __PCLK1()
|
|
{
|
|
RCC_ClocksTypeDef clks;
|
|
|
|
RCC_GetClocksFreq(&clks);
|
|
|
|
return clks.PCLK1_Frequency;
|
|
}
|
|
|
|
static uint16_t __clk_prescale(int bps)
|
|
{
|
|
uint16_t prescale = SPI_BaudRatePrescaler_4;
|
|
uint32_t clk;
|
|
|
|
clk = __PCLK1();
|
|
|
|
if (bps >= (clk / 2)) {
|
|
prescale = SPI_BaudRatePrescaler_2;
|
|
} else if ((bps >= (clk / 4)) && (bps < (clk / 2))) {
|
|
prescale = SPI_BaudRatePrescaler_4;
|
|
} else if ((bps >= (clk / 8)) && (bps < (clk / 4))) {
|
|
prescale = SPI_BaudRatePrescaler_8;
|
|
} else if ((bps >= (clk / 16)) && (bps < (clk / 8))) {
|
|
prescale = SPI_BaudRatePrescaler_16;
|
|
} else if ((bps >= (clk / 32)) && (bps < (clk / 16))) {
|
|
prescale = SPI_BaudRatePrescaler_32;
|
|
} else if ((bps >= (clk / 64)) && (bps < (clk / 32))) {
|
|
prescale = SPI_BaudRatePrescaler_64;
|
|
} else if ((bps >= (clk / 128)) && (bps < (clk / 64))) {
|
|
prescale = SPI_BaudRatePrescaler_128;
|
|
} else if (bps < (clk / 128)) {
|
|
prescale = SPI_BaudRatePrescaler_256;
|
|
}
|
|
return prescale;
|
|
}
|
|
|
|
void spi_set_speed(spi_t spi, int bps)
|
|
{
|
|
if (bps > 0) {
|
|
SPI_Cmd(spi->base, DISABLE);
|
|
spi->conf.SPI_BaudRatePrescaler = __clk_prescale(bps);
|
|
SPI_Init(spi->base, &spi->conf);
|
|
SPI_Cmd(spi->base, ENABLE);
|
|
}
|
|
}
|
|
|
|
int spi_setup(spi_t spi, uint32_t cs, int mode, int bps)
|
|
{
|
|
uint16_t prescale = SPI_BaudRatePrescaler_4;
|
|
|
|
if (spi->setup)
|
|
spi->setup(spi);
|
|
|
|
spi->cs = cs;
|
|
if (cs > 0) {
|
|
gpio_init(spi->cs, GPIO_OUTPUT | GPIO_FLAG_PD | GPIO_FLAG_PU | GPIO_SPEED_FAST);
|
|
gpio_set(spi->cs);
|
|
}
|
|
|
|
switch (spi->apb) {
|
|
case 1:
|
|
RCC_APB1PeriphClockCmd(spi->rcc, ENABLE);
|
|
break;
|
|
case 2:
|
|
RCC_APB2PeriphClockCmd(spi->rcc, ENABLE);
|
|
break;
|
|
}
|
|
|
|
SPI_DeInit(spi->base);
|
|
|
|
|
|
SPI_StructInit(&spi->conf);
|
|
spi->conf.SPI_Mode = SPI_Mode_Master;
|
|
|
|
if (bps > 0) {
|
|
prescale = __clk_prescale(bps);
|
|
}
|
|
|
|
spi->conf.SPI_BaudRatePrescaler = prescale;
|
|
|
|
if (mode & 0x1) {
|
|
spi->conf.SPI_CPHA = SPI_CPHA_2Edge;
|
|
} else {
|
|
spi->conf.SPI_CPHA = SPI_CPHA_1Edge;
|
|
}
|
|
|
|
if (mode & 0x2) {
|
|
spi->conf.SPI_CPOL = SPI_CPOL_High;
|
|
} else {
|
|
spi->conf.SPI_CPOL = SPI_CPOL_Low;
|
|
}
|
|
spi->conf.SPI_NSS = SPI_NSS_Soft;
|
|
spi->conf.SPI_CRCPolynomial = 7;
|
|
|
|
SPI_Init(spi->base, &spi->conf);
|
|
SPI_Cmd(spi->base, ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void spi_set_mode(spi_t spi, int mode)
|
|
{
|
|
if (mode & 0x1) {
|
|
spi->conf.SPI_CPHA = SPI_CPHA_2Edge;
|
|
} else {
|
|
spi->conf.SPI_CPHA = SPI_CPHA_1Edge;
|
|
}
|
|
|
|
if (mode & 0x2) {
|
|
spi->conf.SPI_CPOL = SPI_CPOL_High;
|
|
} else {
|
|
spi->conf.SPI_CPOL = SPI_CPOL_Low;
|
|
}
|
|
SPI_Cmd(spi->base, DISABLE);
|
|
SPI_Init(spi->base, &spi->conf);
|
|
SPI_Cmd(spi->base, ENABLE);
|
|
}
|
|
|
|
void spi_close(spi_t spi)
|
|
{
|
|
switch (spi->apb) {
|
|
case 1:
|
|
RCC_APB1PeriphClockCmd(spi->rcc, DISABLE);
|
|
break;
|
|
case 2:
|
|
RCC_APB2PeriphClockCmd(spi->rcc, DISABLE);
|
|
break;
|
|
}
|
|
|
|
SPI_Cmd(spi->base, DISABLE);
|
|
SPI_DeInit(spi->base);
|
|
}
|
|
|
|
void spi_cs(spi_t spi, int cs)
|
|
{
|
|
gpio_write(spi->cs, cs);
|
|
}
|
|
|
|
uint8_t spi_txrx(spi_t spi, uint8_t data)
|
|
{
|
|
/*!< Loop while DR register in not empty */
|
|
while (SPI_I2S_GetFlagStatus(spi->base, SPI_I2S_FLAG_TXE) == RESET);
|
|
|
|
/*!< Send byte through the SPI1 peripheral */
|
|
SPI_I2S_SendData(spi->base, data);
|
|
|
|
/*!< Wait to receive a byte */
|
|
while (SPI_I2S_GetFlagStatus(spi->base, SPI_I2S_FLAG_RXNE) == RESET);
|
|
|
|
/*!< Return the byte read from the SPI bus */
|
|
return SPI_I2S_ReceiveData(spi->base);
|
|
}
|
|
|
|
uint8_t spi_send_byte(spi_t spi, uint8_t data)
|
|
{
|
|
uint8_t dt;
|
|
|
|
gpio_clear(spi->cs);
|
|
|
|
dt = spi_txrx(spi, data);
|
|
|
|
gpio_set(spi->cs);
|
|
return (dt);
|
|
}
|
|
|
|
int spi_transfer(spi_t spi, const uint8_t *tx, uint8_t *rx, int len)
|
|
{
|
|
int got = 0;
|
|
uint8_t dt;
|
|
const uint8_t *end = tx + len;
|
|
|
|
if (!tx) {
|
|
return -1;
|
|
}
|
|
if (rx)
|
|
got = 1;
|
|
|
|
gpio_clear(spi->cs);
|
|
while (tx < end) {
|
|
dt = spi_txrx(spi, *tx++);
|
|
if (got)
|
|
*rx++ = dt;
|
|
}
|
|
gpio_set(spi->cs);
|
|
|
|
return (len);
|
|
}
|
|
|
|
#if (TARGET_HAS_SPI1)
|
|
void __attribute__((weak)) target_spi1_setup()
|
|
{
|
|
GPIO_InitTypeDef conf;
|
|
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //开启时钟
|
|
|
|
conf.GPIO_Mode = GPIO_Mode_AF; //引脚初始化
|
|
conf.GPIO_OType = GPIO_OType_PP;
|
|
conf.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
|
|
conf.GPIO_PuPd = GPIO_PuPd_UP;
|
|
conf.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(GPIOA, &conf);
|
|
|
|
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
|
|
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
|
|
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
|
|
}
|
|
|
|
static void spi1_setup(struct spi_device *spi)
|
|
{
|
|
target_spi1_setup();
|
|
}
|
|
|
|
struct spi_device spi0 = {
|
|
.base = SPI1,
|
|
.apb = 2,
|
|
.rcc = RCC_APB2Periph_SPI1,
|
|
.cs = 0,
|
|
.setup = spi1_setup
|
|
};
|
|
#endif
|
|
|
|
#if (TARGET_HAS_SPI2)
|
|
void __attribute__((weak)) target_spi2_setup()
|
|
{
|
|
GPIO_InitTypeDef conf;
|
|
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //开启时钟
|
|
|
|
conf.GPIO_Mode = GPIO_Mode_AF; //引脚初始化
|
|
conf.GPIO_OType = GPIO_OType_PP;
|
|
conf.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
|
|
conf.GPIO_PuPd = GPIO_PuPd_UP;
|
|
conf.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(GPIOB, &conf);
|
|
|
|
GPIO_PinAFConfig(GPIOB,GPIO_PinSource13, GPIO_AF_SPI2); //打开引脚的复用功能
|
|
GPIO_PinAFConfig(GPIOB,GPIO_PinSource14, GPIO_AF_SPI2);
|
|
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15, GPIO_AF_SPI2);
|
|
}
|
|
|
|
static void spi2_setup(struct spi_device *spi)
|
|
{
|
|
target_spi2_setup();
|
|
}
|
|
|
|
struct spi_device spi1 = {
|
|
.base = SPI2,
|
|
.apb = 1,
|
|
.rcc = RCC_APB1Periph_SPI2,
|
|
.cs = 0,
|
|
.setup = spi2_setup
|
|
};
|
|
#endif
|
|
|
|
|