modify spi slave driver

This commit is contained in:
jiangxiangbing 2019-03-14 16:15:23 +08:00
parent a8f5ea747e
commit a454a6e4d2
2 changed files with 381 additions and 87 deletions

View File

@ -157,18 +157,47 @@ typedef enum _spi_chip_select
SPI_CHIP_SELECT_MAX,
} spi_chip_select_t;
typedef enum _spi_slave_event
{
SPI_EV_TRANS,
SPI_EV_RECV,
} spi_slave_event_t;
typedef enum {
WRITE_CONFIG,
READ_CONFIG,
WRITE_DATA_BYTE,
READ_DATA_BYTE,
WRITE_DATA_BLOCK,
READ_DATA_BLOCK,
} spi_slave_command_e;
typedef struct _spi_slave_handler
typedef struct
{
void (*on_receive)(uint32_t data);
uint32_t (*on_transmit)(uint32_t data);
spi_slave_event_t (*on_event)(uint32_t data);
} spi_slave_handler_t;
uint8_t cmd;
uint8_t err;
uint32_t addr;
uint32_t len;
} spi_slave_command_t;
typedef enum
{
IDLE,
COMMAND,
TRANSFER,
} spi_slave_status_e;
typedef int (*spi_slave_receive_callback_t)(void *ctx);
typedef struct _spi_slave_instance
{
uint8_t int_pin;
uint8_t ready_pin;
dmac_channel_number_t dmac_channel;
uint8_t dfs;
uint8_t slv_oe;
uint8_t work_mode;
size_t data_bit_length;
volatile spi_slave_status_e status;
volatile spi_slave_command_t command;
volatile uint8_t *config_ptr;
uint32_t config_len;
spi_slave_receive_callback_t callback;
} spi_slave_instance_t;
extern volatile spi_t *const spi[4];
@ -186,16 +215,6 @@ extern volatile spi_t *const spi[4];
void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_format_t frame_format,
size_t data_bit_length, uint32_t endian);
/**
* @brief Set spi slave configuration
*
* @param[in] data_bit_length Spi data bit length
* @param[in] handler Handle of spi slave interrupt function.
*
* @return Void
*/
void spi_slave_config(size_t data_bit_length, const spi_slave_handler_t *handler);
/**
* @brief Set multiline configuration
*
@ -419,6 +438,21 @@ void spi_dup_send_receive_data_dma(dmac_channel_number_t dma_send_channel_num,
spi_device_num_t spi_num, spi_chip_select_t chip_select,
const uint8_t *tx_buf, size_t tx_len, uint8_t *rx_buf, size_t rx_len);
/**
* @brief Set spi slave configuration
*
* @param[in] int_pin SPI master starts sending data interrupt.
* @param[in] ready_pin SPI slave ready.
* @param[in] dmac_channel Dmac channel number for block.
* @param[in] data_bit_length Spi data bit length
* @param[in] data SPI slave device data buffer.
* @param[in] len The length of SPI slave device data buffer.
* @param[in] callback Callback of spi slave.
*
* @return Void
*/
void spi_slave_config(uint8_t int_pin, uint8_t ready_pin, dmac_channel_number_t dmac_channel, size_t data_bit_length, uint8_t *data, uint32_t len, spi_slave_receive_callback_t callback);
#ifdef __cplusplus
}
#endif

View File

@ -16,16 +16,10 @@
#include "spi.h"
#include "fpioa.h"
#include "utils.h"
#include "gpiohs.h"
#include "sysctl.h"
#include <stddef.h>
#include <stdlib.h>
typedef struct _spi_slave_context
{
size_t data_bit_length;
const spi_slave_handler_t *handler;
} spi_slave_context_t;
static spi_slave_context_t spi_slave_context;
volatile spi_t *const spi[4] =
{
@ -35,6 +29,8 @@ volatile spi_t *const spi[4] =
(volatile spi_t *)SPI3_BASE_ADDR
};
static spi_slave_instance_t g_instance;
static spi_frame_format_t spi_get_frame_format(spi_device_num_t spi_num)
{
uint8_t frf_offset;
@ -150,66 +146,6 @@ void spi_init(spi_device_num_t spi_num, spi_work_mode_t work_mode, spi_frame_for
spi_adapter->endian = endian;
}
int spi_slave_irq(void *userdata)
{
spi_slave_context_t *context = (spi_slave_context_t *)userdata;
volatile spi_t *spi_adapter = spi[2];
uint8_t isr = spi_adapter->isr;
uint32_t data;
uint32_t transmit_data;
uint8_t dfs_offset = 16, slv_oe = 10, work_mode_offset = 6;
if (isr & 0x10)
{
data = spi_adapter->dr[0];
if(context->handler->on_event(data) == SPI_EV_RECV)
{
context->handler->on_receive(data);
}
if(context->handler->on_event(data) == SPI_EV_TRANS)
{
transmit_data = context->handler->on_transmit(data);
spi_adapter->ssienr = 0x00;
spi_adapter->ctrlr0 = (0x0 << work_mode_offset) | (0x0 << slv_oe) | ((context->data_bit_length - 1) << dfs_offset);
spi_set_tmod(2, SPI_TMOD_TRANS);
spi_adapter->ssienr = 0x01;
spi_adapter->dr[0] = transmit_data;
spi_adapter->dr[0] = transmit_data;
spi_adapter->imr = 0x00000001;
}
}
if (isr & 0x01)
{
spi_adapter->ssienr = 0x00;
spi_adapter->ctrlr0 = (0x0 << work_mode_offset) | (0x1 << slv_oe) | ((context->data_bit_length - 1) << dfs_offset);
spi_set_tmod(2, SPI_TMOD_RECV);
spi_adapter->imr = 0x00000010;
spi_adapter->ssienr = 0x01;
}
return 0;
}
void spi_slave_config(size_t data_bit_length, const spi_slave_handler_t *handler)
{
sysctl_reset(SYSCTL_RESET_SPI2);
sysctl_clock_enable(SYSCTL_CLOCK_SPI2);
sysctl_clock_set_threshold(SYSCTL_THRESHOLD_SPI2, 0);
spi_slave_context.data_bit_length = data_bit_length;
spi_slave_context.handler = handler;
uint8_t dfs_offset = 16, slv_oe = 10, work_mode_offset = 6;
volatile spi_t *spi_adapter = spi[2];
spi_adapter->ssienr = 0x00;
spi_adapter->ctrlr0 = (0x0 << work_mode_offset) | (0x1 << slv_oe) | ((data_bit_length - 1) << dfs_offset);
spi_adapter->txftlr = 0x00000000;
spi_adapter->rxftlr = 0x00000000;
spi_adapter->imr = 0x00000010;
spi_adapter->ssienr = 0x01;
plic_set_priority(IRQN_SPI_SLAVE_INTERRUPT, 1);
plic_irq_enable(IRQN_SPI_SLAVE_INTERRUPT);
plic_irq_register(IRQN_SPI_SLAVE_INTERRUPT, spi_slave_irq, &spi_slave_context);
}
void spi_init_non_standard(spi_device_num_t spi_num, uint32_t instruction_length, uint32_t address_length,
uint32_t wait_cycles, spi_instruction_address_trans_mode_t instruction_address_trans_mode)
{
@ -1036,3 +972,327 @@ void spi_fill_data_dma(dmac_channel_number_t channel_num, spi_device_num_t spi_n
spi_handle->ssienr = 0x00;
}
static int spi_slave_irq(void *ctx)
{
volatile spi_t *spi_handle = spi[2];
spi_handle->imr = 0x00;
*(volatile uint32_t *)(spi_handle->icr);
if (g_instance.status == IDLE)
g_instance.status = COMMAND;
return 0;
}
static void spi_slave_idle_mode(void)
{
volatile spi_t *spi_handle = spi[2];
uint32_t data_width = g_instance.data_bit_length / 8;
g_instance.status = IDLE;
spi_handle->ssienr = 0x00;
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x1 << g_instance.slv_oe) | ((g_instance.data_bit_length - 1) << g_instance.dfs);
spi_handle->rxftlr = 0x08 / data_width - 1;
spi_handle->dmacr = 0x00;
spi_handle->imr = 0x10;
spi_handle->ssienr = 0x01;
gpiohs_set_pin(g_instance.ready_pin, GPIO_PV_HIGH);
}
static void spi_slave_command_mode(void)
{
volatile spi_t *spi_handle = spi[2];
uint8_t cmd_data[8], sum = 0;
spi_transfer_width_t frame_width = spi_get_frame_size(g_instance.data_bit_length - 1);
uint32_t data_width = g_instance.data_bit_length / 8;
spi_device_num_t spi_num = SPI_DEVICE_2;
switch(frame_width)
{
case SPI_TRANS_INT:
for (uint32_t i = 0; i < 8 / 4; i++)
((uint32_t *)cmd_data)[i] = spi_handle->dr[0];
break;
case SPI_TRANS_SHORT:
for (uint32_t i = 0; i < 8 / 2; i++)
((uint16_t *)cmd_data)[i] = spi_handle->dr[0];
break;
default:
for (uint32_t i = 0; i < 8; i++)
cmd_data[i] = spi_handle->dr[0];
break;
}
for (uint32_t i = 0; i < 7; i++)
{
sum += cmd_data[i];
}
if (cmd_data[7] != sum)
{
spi_slave_idle_mode();
return;
}
g_instance.command.cmd = cmd_data[0];
g_instance.command.addr = cmd_data[1] | (cmd_data[2] << 8) | (cmd_data[3] << 16) | (cmd_data[4] << 24);
g_instance.command.len = cmd_data[5] | (cmd_data[6] << 8);
if (g_instance.command.len == 0)
g_instance.command.len = 65536;
if ((g_instance.command.cmd < WRITE_DATA_BLOCK) && (g_instance.command.len > 8))
{
spi_slave_idle_mode();
return;
}
g_instance.status = TRANSFER;
spi_handle->ssienr = 0x00;
if (g_instance.command.cmd == WRITE_CONFIG)
{
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x1 << g_instance.slv_oe) | ((g_instance.data_bit_length - 1) << g_instance.dfs);
spi[2]->rxftlr = g_instance.command.len / data_width - 1;
spi_handle->imr = 0x00;
spi_handle->ssienr = 0x01;
}
else if (g_instance.command.cmd == READ_CONFIG)
{
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x0 << g_instance.slv_oe) | ((g_instance.data_bit_length - 1) << g_instance.dfs);
spi_set_tmod(2, SPI_TMOD_TRANS);
spi_handle->txftlr = 0x00;
spi_handle->imr = 0x00;
spi_handle->ssienr = 0x01;
switch(frame_width)
{
case SPI_TRANS_INT:
for (uint32_t i = 0; i < g_instance.command.len / 4; i++)
{
spi_handle->dr[0] = ((uint32_t *)&g_instance.config_ptr[g_instance.command.addr])[i];
}
break;
case SPI_TRANS_SHORT:
for (uint32_t i = 0; i < g_instance.command.len / 2; i++)
{
spi_handle->dr[0] = ((uint16_t *)&g_instance.config_ptr[g_instance.command.addr])[i];
}
break;
default:
for (uint32_t i = 0; i < g_instance.command.len; i++)
{
spi_handle->dr[0] = ((uint8_t *)&g_instance.config_ptr[g_instance.command.addr])[i];
}
break;
}
}
else if (g_instance.command.cmd == WRITE_DATA_BYTE)
{
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x1 << g_instance.slv_oe) | ((g_instance.data_bit_length - 1) << g_instance.dfs);
spi[2]->rxftlr = g_instance.command.len / data_width - 1;
spi_handle->imr = 0x00;
spi_handle->ssienr = 0x01;
}
else if (g_instance.command.cmd == READ_DATA_BYTE)
{
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x0 << g_instance.slv_oe) | ((g_instance.data_bit_length - 1) << g_instance.dfs);
spi_set_tmod(2, SPI_TMOD_TRANS);
spi_handle->txftlr = 0x00;
spi_handle->imr = 0x00;
spi_handle->ssienr = 0x01;
switch(frame_width)
{
case SPI_TRANS_INT:
for (uint32_t i = 0; i < g_instance.command.len / 4; i++)
{
spi_handle->dr[0] = ((uint32_t *)g_instance.command.addr)[i];
}
break;
case SPI_TRANS_SHORT:
for (uint32_t i = 0; i < g_instance.command.len / 2; i++)
{
spi_handle->dr[0] = ((uint16_t *)g_instance.command.addr)[i];
}
break;
default:
for (uint32_t i = 0; i < g_instance.command.len; i++)
{
spi_handle->dr[0] = ((uint8_t *)g_instance.command.addr)[i];
}
break;
}
}
else if (g_instance.command.cmd == WRITE_DATA_BLOCK)
{
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x1 << g_instance.slv_oe) | ((32 - 1) << g_instance.dfs);
spi_handle->dmacr = 0x01;
spi_handle->imr = 0x00;
spi_handle->ssienr = 0x01;
sysctl_dma_select(g_instance.dmac_channel, SYSCTL_DMA_SELECT_SSI0_RX_REQ + spi_num * 2);
dmac_set_single_mode(g_instance.dmac_channel, (void *)(&spi_handle->dr[0]), (void *)(g_instance.command.addr & 0xFFFFFFF0), DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, g_instance.command.len * 4);
}
else if (g_instance.command.cmd == READ_DATA_BLOCK)
{
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x0 << g_instance.slv_oe) | ((32 - 1) << g_instance.dfs);
spi_set_tmod(2, SPI_TMOD_TRANS);
spi_handle->dmacr = 0x02;
spi_handle->imr = 0x00;
spi_handle->ssienr = 0x01;
sysctl_dma_select(g_instance.dmac_channel, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_num * 2);
dmac_set_single_mode(g_instance.dmac_channel, (void *)(g_instance.command.addr & 0xFFFFFFF0), (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, g_instance.command.len * 4);
}
else
{
spi_slave_idle_mode();
return;
}
gpiohs_set_pin(g_instance.ready_pin, GPIO_PV_LOW);
}
static void spi_slave_transfer_mode(void)
{
spi_transfer_width_t frame_width = spi_get_frame_size(g_instance.data_bit_length - 1);
uint32_t command_len = 0;
switch(frame_width)
{
case SPI_TRANS_INT:
command_len = g_instance.command.len / 4;
break;
case SPI_TRANS_SHORT:
command_len = g_instance.command.len / 2;
break;
default:
command_len = g_instance.command.len;
break;
}
volatile spi_t *spi_handle = spi[2];
g_instance.command.err = 0;
if (g_instance.command.cmd == WRITE_CONFIG || g_instance.command.cmd == WRITE_DATA_BYTE)
{
if (spi_handle->rxflr < command_len - 1)
g_instance.command.err = 1;
}
else if (g_instance.command.cmd == READ_CONFIG || g_instance.command.cmd == READ_DATA_BYTE)
{
if (spi_handle->txflr != 0)
g_instance.command.err = 2;
} else if (g_instance.command.cmd == WRITE_DATA_BLOCK || g_instance.command.cmd == READ_DATA_BLOCK)
{
if (dmac->channel[g_instance.dmac_channel].intstatus != 0x02)
g_instance.command.err = 3;
}
else
{
spi_slave_idle_mode();
return;
}
if (g_instance.command.err == 0)
{
if (g_instance.command.cmd == WRITE_CONFIG)
{
switch(frame_width)
{
case SPI_TRANS_INT:
for (uint32_t i = 0; i < command_len; i++)
{
((uint32_t *)&g_instance.config_ptr[g_instance.command.addr])[i] = spi_handle->dr[0];
}
break;
case SPI_TRANS_SHORT:
for (uint32_t i = 0; i < command_len; i++)
{
((uint16_t *)&g_instance.config_ptr[g_instance.command.addr])[i] = spi_handle->dr[0];
}
break;
default:
for (uint32_t i = 0; i < command_len; i++)
{
((uint8_t *)&g_instance.config_ptr[g_instance.command.addr])[i] = spi_handle->dr[0];
}
break;
}
}
else if (g_instance.command.cmd == WRITE_DATA_BYTE)
{
switch(frame_width)
{
case SPI_TRANS_INT:
for (uint32_t i = 0; i < command_len; i++)
{
((uint32_t *)g_instance.command.addr)[i] = spi_handle->dr[0];
}
break;
case SPI_TRANS_SHORT:
for (uint32_t i = 0; i < command_len; i++)
{
((uint16_t *)g_instance.command.addr)[i] = spi_handle->dr[0];
}
break;
default:
for (uint32_t i = 0; i < command_len; i++)
{
((uint8_t *)g_instance.command.addr)[i] = spi_handle->dr[0];
}
break;
}
}
}
if(g_instance.callback != NULL)
{
g_instance.callback((void *)&g_instance.command);
}
spi_slave_idle_mode();
}
static void spi_slave_cs_irq(void)
{
if (g_instance.status == IDLE)
spi_slave_idle_mode();
else if (g_instance.status == COMMAND)
spi_slave_command_mode();
else if (g_instance.status == TRANSFER)
spi_slave_transfer_mode();
}
void spi_slave_config(uint8_t int_pin, uint8_t ready_pin, dmac_channel_number_t dmac_channel, size_t data_bit_length, uint8_t *data, uint32_t len, spi_slave_receive_callback_t callback)
{
g_instance.status = IDLE;
g_instance.config_ptr = data;
g_instance.config_len = len;
g_instance.work_mode = 6;
g_instance.slv_oe = 10;
g_instance.dfs = 16;
g_instance.data_bit_length = data_bit_length;
g_instance.ready_pin = ready_pin;
g_instance.int_pin = int_pin;
g_instance.callback = callback;
g_instance.dmac_channel = dmac_channel;
sysctl_reset(SYSCTL_RESET_SPI2);
sysctl_clock_enable(SYSCTL_CLOCK_SPI2);
sysctl_clock_set_threshold(SYSCTL_THRESHOLD_SPI2, 9);
uint32_t data_width = data_bit_length / 8;
volatile spi_t *spi_handle = spi[2];
spi_handle->ssienr = 0x00;
spi_handle->ctrlr0 = (0x0 << g_instance.work_mode) | (0x1 << g_instance.slv_oe) | ((data_bit_length - 1) << g_instance.dfs);
spi_handle->dmatdlr = 0x04;
spi_handle->dmardlr = 0x03;
spi_handle->dmacr = 0x00;
spi_handle->txftlr = 0x00;
spi_handle->rxftlr = 0x08 / data_width - 1;
spi_handle->imr = 0x10;
spi_handle->ssienr = 0x01;
gpiohs_set_drive_mode(g_instance.ready_pin, GPIO_DM_OUTPUT);
gpiohs_set_pin(g_instance.ready_pin, GPIO_PV_HIGH);
gpiohs_set_drive_mode(g_instance.int_pin, GPIO_DM_INPUT_PULL_UP);
gpiohs_set_pin_edge(g_instance.int_pin, GPIO_PE_RISING);
gpiohs_set_irq(g_instance.int_pin, 3, spi_slave_cs_irq);
plic_set_priority(IRQN_SPI_SLAVE_INTERRUPT, 4);
plic_irq_enable(IRQN_SPI_SLAVE_INTERRUPT);
plic_irq_register(IRQN_SPI_SLAVE_INTERRUPT, spi_slave_irq, NULL);
}