SPI使用指南
SPI 使用指南*
第一章 概述*
SPI,Serial Peripheral Interface Bus
,是一种同步串行数据协议,由 Motorola 在 1970 年代开发。该协议旨在取代并行总线并提供短距离的高速数据传输。
它是一个全双工协议,标准的SPI需要四个信号:时钟
、主输出/从输入
、主输入/从输出
和从选择
。数据同时发送和接收。借助 SPI,单个主设备可以控制多个从设备,每个从设备都有自己的从设备选择线。典型应用于外部存储 FLASH,外部SPI屏幕等。
apus可以支持到Quad-SPI,采用 6 个引脚与外部器件相连。
第二章 APUS SPI 功能描述*
2.1 功能特性*
支持三组 SPI:
描述 | 最大频率 | 主从支持 | 备注 | 片选数量 | |
---|---|---|---|---|---|
SPI0 | Flash spi | 100MHZ | Master | 内部带flash控制器,专用于挂接flash | 1 |
SPI1 | PSRAM spi | 80MHZ | Master | 专用于接PSRAM,或者接其它slaver设备 | 1 |
SPI2 | General spi | 50MHZ | Master/Slaver | 通用SPI,做主设备的话,可以同时外接三个从设备 | 3 |
-
全双工通信
-
支持主 / 从模式操作
-
支持四线模式 SPI 接口
-
可编程的时钟极性和相位
-
32 位深度 FIFO
-
完整的协议灵活性,能够任意选择传输消息数据长度、内容及目标
-
AHB 总线接口,数据总线位宽 32 位,支持配置 8 / 32 bit 数据位宽传输
-
支持 DMA 控制器接口,使用握手接口实现传输请求,通过总线与 DMA 控制器连接
-
主模式下,接收到的串行数据位 (RXD) 的采样时间延时可编程,从而实现更高的串行数据比特率
-
支持串行数据位 (RXD) 的可编程采样:上升沿或下降沿采样
-
支持多种串行接口模式 (Motorola、Texas Instruments、National Semiconductor Microwire)
2.2 扩展使用*
- SPI0 只能外挂flash用。SDK有支持默认的flash的列表供客户参考。用户可以选择不同的flash型号,国芯会安排驱动的支持。
- SPI1芯片内部已经使用,内包了一个2MB的PSRAM,当做内存使用。用户无法再外挂设备使用。
- SPI2 用户可以外挂其它设备使用。
- SPI0 芯片已经内部使用,内包了flash。
- SPI1 用户可以外挂其它设备使用。
- SPI2 用户可以外挂其它设备使用。
2.3 扩展 SPI 协议*
协议 | 数据线数及功能 | 通讯方式 | 是否支持 |
---|---|---|---|
Single SPl(标准SPI) | 1根发送,1根接收 | 全双工 | 支持 |
Dual SPl(双线SPI) | 收发共用2根数据线 | 半双工 | 支持 |
Qual SPl(四线SPI) | 收发共用4根数据线 | 半双工 | 支持 |
Octal SPl(八线SPI) | 收发共用8根数据线 | 半双工 | 不支持 |
第三章 SPI API 接口使用*
/**
* @brief SPI初始化配置参数
*/
typedef struct gx_hal_spi_config {
uint32_t reg_base; /*!< SPI CS 控制寄存器地址 */
uint32_t cs_reg_base; /*!< SPI 寄存器基地址 */
uint32_t freq_src; /*!< 时钟源频率 */
uint32_t max_speed_hz; /*!< 最大频率 */
uint8_t is_master; /*!< 是否为主模式 */
uint8_t bits_per_word; /*!< word 的 bit 数 */
void *controller_state; /*!< 控制器状态 */
uint32_t sample_delay; /*!< 采样延时 */
uint8_t no_auto_cs; /*!< 不使用 spi 控制器自动控制 CS */
uint8_t clk_mode; /*!< 时钟模式 */
SPI_CS_INIT_FUNC cs_init_func; /*!< CS 初始化函数 */
SPI_CS_CTRL_FUNC cs_ctrl_func; /*!< CS 控制函数 */
uint8_t cs_num; /*!< CS 片选号 */
}GX_HAL_SPI_CFG_T;
- max_speed_hz 设置通讯的最大频率。
- is_master 设置主模式还是从模式,1是主模式,0是从模式。
- bits_per_word 支持最小传输单位的位宽bit,有 8,16,24,32 四种选择。在传输数据时的transfer中的bits_per_word实际起作用。
- sample_delay 采样延时,不建议用户改动。
- no_auto_cs 是否使用 spi 控制的硬件自动片选功能,为 0 表示由 spi 控制器自动控制,为 1 表示通过 cs 控制函数手动软件控制。
- clk_mode 时钟模式,见下图的描述
- cs_num CS 片选号,需要参考硬件确认。比如接的是 SPI2_CS0 ,那么这个值就是0;SPI2_CS1 ,那么这个值就是1。
第四章 SPI主设备参考例子*
下面以8302B_dev开发板,接的SPI LCD屏幕为例子,说明SPI的接口使用。
4.1 示例框图*
4.2 初始化*
void drv_spi_init(void)
{
struct gx_hal_spi_config hal_spi_config;
memset(&hal_spi_config, 0, sizeof(struct gx_hal_spi_config));
gx_hal_clk_mod_set_gate(GX_HAL_CLK_MOD_QSPI2, ENABLE); // SPI2 时钟开启
hal_spi_config.reg_base = GX_REG_BASE_SPI2; // 使用 SPI2
hal_spi_config.cs_reg_base = GX_REG_BASE_SPI2_CS;
hal_spi_config.freq_src = gx_hal_clk_mod_get_freq(GX_HAL_CLK_MOD_QSPI2);
hal_spi_config.is_master = 1; // 做主设备
hal_spi_config.max_speed_hz = 32*1000*1000; // 最大频率32MHZ,依赖hal_spi_config.freq_src,只能偶数倍分频
hal_spi_config.sample_delay = ((1 % 2) << 16) | (1 / 2);;
hal_spi_config.clk_mode = 0; // 模式0
hal_spi_config.no_auto_cs = 0;
hal_spi_config.cs_init_func = gx_hal_spi_ctrl_cs_init;
hal_spi_config.cs_ctrl_func = gx_hal_spi_ctrl_cs_control;
gx_dma_init(); // 如果要使用DMA方式传输,这个必须要初始化
gx_hal_spi_hw_init(&hal_dws_qspi, &hal_spi_config);
chip_os_sem_init(&spi_sem, "spi_sem", 0);
}
4.3 Single SPI 同步通讯*
#define GX_HAL_SPI_STAND 0x0 //!< standard SPI formal
#define GX_HAL_SPI_DUAL 0x1 //!< Dual SPI formal
#define GX_HAL_SPI_QUAD 0x2 //!< Quad SPI formal
#define GX_HAL_SPI_OCTAL 0x3 //!< Octal SPI formal
int drv_spi_tx(const uint8_t *buf, uint32_t len, uint8_t cs_take, uint8_t cs_release)
{
int ret = 0;
struct gx_hal_spi_tran trans;
if(cs_take)
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
trans.bits_per_word = 8;
trans.tx_len = len;
trans.tx_buf = buf;
trans.bus_mode = GX_HAL_SPI_STAND; // single mode
trans.rx_buf = NULL;
trans.trans_mode = GX_HAL_SPI_TRANS_TX_ONLY;
ret = gx_hal_spi_transfer_prepare(&hal_dws_qspi, &trans);
gx_hal_spi_set_tx_fifo_threshold(&hal_dws_qspi, 0); // 软件cs时,发送fifo启动阈值设置为0,避免tx fifo underflow的出现导致transfer无法启动发送
ret |= gx_hal_spi_transfer(&hal_dws_qspi, &trans);
while(gx_hal_spi_tf_is_empty(&hal_dws_qspi) == 0);
while(gx_hal_spi_is_busy(&hal_dws_qspi) == 1);
if(cs_release)
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
return ret;
}
接收例子
int drv_spi_rx(const uint8_t *buf, uint32_t len, uint8_t cs_take, uint8_t cs_release)
{
int ret = 0;
struct gx_hal_spi_tran trans;
if(cs_take)
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
trans.bits_per_word = 8;
trans.tx_len = 0;
trans.tx_buf = NULL;
trans.bus_mode = GX_HAL_SPI_STAND; // single mode
trans.rx_len = len;
trans.rx_buf = buf;
trans.trans_mode = GX_HAL_SPI_TRANS_RX_ONLY;
ret = gx_hal_spi_transfer_prepare(&hal_dws_qspi, &trans);
ret |= gx_hal_spi_transfer(&hal_dws_qspi, &trans);
while(gx_hal_spi_rf_is_empty(&hal_dws_qspi) == 0);
while(gx_hal_spi_is_busy(&hal_dws_qspi) == 1);
if(cs_release)
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
return ret;
}
接发同时进行的例子
int drv_spi_tx_rx(const uint8_t *rx_buf, uint32_t rx_len, const uint8_t *tx_buf, uint32_t tx_len, uint8_t cs_take, uint8_t cs_release)
{
int ret = 0;
struct gx_hal_spi_tran trans;
if(cs_take)
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
trans.bits_per_word = 8;
trans.tx_len = tx_len;
trans.tx_buf = tx_buf;
trans.bus_mode = GX_HAL_SPI_STAND; // single mode
trans.rx_len = rx_len;
trans.rx_buf = rx_buf;
trans.trans_mode = GX_HAL_SPI_TRANS_TX_AND_RX;
ret = gx_hal_spi_transfer_prepare(&hal_dws_qspi, &trans);
ret |= gx_hal_spi_transfer(&hal_dws_qspi, &trans);
while(gx_hal_spi_rf_is_empty(&hal_dws_qspi) == 0);
while(gx_hal_spi_tf_is_empty(&hal_dws_qspi) == 0);
while(gx_hal_spi_is_busy(&hal_dws_qspi) == 1);
if(cs_release)
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
return ret;
}
4.4 Qual SPl 同步通讯*
发送例子
int drv_qspi_tx_data(const uint8_t *buf, uint32_t len)
{
int ret = 0;
struct gx_hal_spi_tran trans;
uint32_t left = len;
const uint8_t *send_buf = buf;
uint32_t size;
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
while(left) {
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
size = left >65536?65536:left;
trans.bits_per_word = 8;
trans.tx_len = size -1;
trans.bus_mode = GX_HAL_SPI_QUAD; // qspi mode
trans.rx_buf = NULL;
trans.tx_buf = &send_buf[1];
trans.trans_mode = GX_HAL_SPI_TRANS_TX_ONLY;
trans.instruction.size = 8;
trans.instruction.content = (uint16_t)send_buf[0];
trans.instruction.spi_lines = trans.bus_mode*2;
trans.address.size = 0;
trans.address.content = 0;
trans.address.spi_lines = trans.bus_mode*2;
gx_dcache_clean_range((uint32_t *)buf, len);
ret = gx_hal_qspi_transfer_prepare(&hal_dws_qspi, &trans);
ret |= gx_hal_spi_transfer(&hal_dws_qspi, &trans);
while(gx_hal_spi_tf_is_empty(&hal_dws_qspi) == 0);
while(gx_hal_spi_is_busy(&hal_dws_qspi) == 1);
left -= size;
send_buf += size;
}
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
return ret;
}
接收例子
int drv_qspi_rx_data(const uint8_t *buf, uint32_t len)
{
int ret = 0;
struct gx_hal_spi_tran trans;
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
trans.bits_per_word = 8;
trans.tx_len = 0;
trans.rx_len = len;
trans.bus_mode = GX_HAL_SPI_QUAD; // qspi mode
trans.rx_buf = buf;
trans.tx_buf = NULL;
trans.trans_mode = GX_HAL_SPI_TRANS_RX_ONLY;
trans.instruction.size = 0;
trans.instruction.content = 0;
trans.instruction.spi_lines = trans.bus_mode*2;
trans.address.size = 0;
trans.address.content = 0;
trans.address.spi_lines = trans.bus_mode*2;
ret = gx_hal_qspi_transfer_prepare(&hal_dws_qspi, &trans);
ret |= gx_hal_spi_transfer(&hal_dws_qspi, &trans);
while(gx_hal_spi_rf_is_empty(&hal_dws_qspi) == 0);
while(gx_hal_spi_is_busy(&hal_dws_qspi) == 1);
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
return ret;
}
4.5 Qual SPl异步 DMA 通讯*
发送例子
static void qspi_tx_done(void * param)
{
int ret = 0;
uint32_t count = 0;
struct gx_hal_spi_tran trans;
uint32_t left, send_size;
struct dw_spi_async_tran *trans_param = (struct dw_spi_async_tran *)param;
left = tran_param.msg_len - tran_param.msg_offset;
const uint8_t *send_buf = tran_param.tx_buffer + tran_param.msg_offset;
while(gx_hal_spi_tf_is_empty(&hal_dws_qspi) == 0)
{
if(count++ > 10000)
{
printf("gx_hal_spi_tf_is_empty time out\n");
goto __TRANS_COMPLETE__;
}
}
count = 0;
while(gx_hal_spi_is_busy(&hal_dws_qspi) == 1)
{
if(count++ > 10000)
{
printf("gx_hal_spi_is_busy time out\n");
goto __TRANS_COMPLETE__;
}
}
if(left) {
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
send_size = left >65536?65536:left;
trans.bits_per_word = 8;
trans.tx_len = send_size -1;
trans.bus_mode = GX_HAL_SPI_QUAD;
trans.rx_buf = NULL;
trans.tx_buf = &send_buf[1];
trans.trans_mode = GX_HAL_SPI_TRANS_TX_ONLY;
trans.instruction.size = 8;
trans.instruction.content = (uint16_t)send_buf[0];
trans.instruction.spi_lines = trans.bus_mode*2;;
trans.address.size = 0;
trans.address.content = 0;
trans.address.spi_lines = trans.bus_mode*2;
trans.dma_cfg = GX_HAL_SPI_DMA_TX_EN;
trans.dma_msize = 8;
ret = gx_hal_qspi_transfer_prepare(&hal_dws_qspi, &trans);
tran_param.msg_offset = trans_param->msg_offset + send_size;
GX_DMA_AHB_CH_CONFIG dma_config;
dma_config.trans_width = GX_DMA_AHB_TRANS_WIDTH_8;
dma_config.src_addr_update = GX_DMA_AHB_CH_CTL_L_INC;
dma_config.src_hs_select = GX_DMA_AHB_HS_SEL_HW;
dma_config.src_master_select = GX_DMA_AHB_MASTER_3;
dma_config.src_msize = GX_DMA_AHB_BURST_TRANS_LEN_32;
dma_config.src_hs_per = 0;
dma_config.dst_addr_update = GX_DMA_AHB_CH_CTL_L_NOINC;
dma_config.dst_hs_select = GX_DMA_AHB_HS_SEL_HW;
dma_config.dst_master_select = GX_DMA_AHB_MASTER_2;
dma_config.dst_msize = GX_DMA_AHB_BURST_TRANS_LEN_8;
dma_config.dst_hs_per = DMA_HS_PRE_SPI2_TX;
dma_config.flow_ctrl = GX_DMA_AHB_TT_FC_MEM_TO_PER_DMAC;
dma_config.fifo_mode = 1;
gx_dcache_clean_range((uint32_t*)trans.tx_buf, send_size);
gx_dma_register_complete_callback(dma.tx_channel, qspi_tx_done, &tran_param);
if(gx_dma_xfer_int((void*)(GX_REG_BASE_SPI2 + GX_HAL_SPI_TXDR), (void*)((unsigned int)(trans.tx_buf)& 0x0FFFFFFF), send_size-1, dma.tx_channel, &dma_config)) {
gx_dcache_clean_invalid_range((uint32_t*)trans.tx_buf, send_size);
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
return ;
}
} else {
__TRANS_COMPLETE__:
#if 0
irq_state = gx_lock_irq_save();
gx_hal_dw_dma_ahb_release_channel(&dma_dev, dma.tx_channel);
gx_unlock_irq_restore(irq_state);
#else
gx_dma_release_channel(dma.tx_channel);
#endif
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
if(trans_param->tx_complete_cb) {
trans_param->tx_complete_cb(param);
}
tx_done = 1;
chip_os_sem_give(&spi_sem);
}
}
int drv_qspi_tx_async(const uint8_t *buf, uint32_t len, void (*func)(void *), void* param)
{
int ret = 0;
struct gx_hal_spi_tran trans;
const uint8_t *send_buf = buf;
uint32_t size;
#if MODULE_GUI_DEBUG
unsigned long long start_us = gx_get_time_us();
#endif
while(!tx_done) {
chip_os_sem_take(&spi_sem, CHIP_OS_TIME_FOREVER);
}
#if MODULE_GUI_DEBUG
unsigned long long use_time = gx_get_time_us() - start_us;
dma_wait_time += use_time;
#endif
tx_done = 0;
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
size = len >65536?65536:len;
trans.bits_per_word = 8;
trans.tx_len = size -1;
trans.bus_mode = GX_HAL_SPI_QUAD;
trans.rx_buf = NULL;
trans.tx_buf = &send_buf[1];
trans.trans_mode = GX_HAL_SPI_TRANS_TX_ONLY;
trans.instruction.size = 8;
trans.instruction.content = (uint16_t)send_buf[0];
trans.instruction.spi_lines = trans.bus_mode*2;
trans.address.size = 0;
trans.address.content = 0;
trans.address.spi_lines = trans.bus_mode*2;
trans.dma_cfg = GX_HAL_SPI_DMA_TX_EN;
trans.dma_msize = 8;
ret = gx_hal_qspi_transfer_prepare(&hal_dws_qspi, &trans);
tran_param.msg_len = len;
tran_param.msg_offset = size;
tran_param.tx_complete_cb = func;
tran_param.tx_buffer = send_buf;
tran_param.param = param;
GX_DMA_AHB_CH_CONFIG dma_config;
dma.rx_channel = gx_dma_select_channel();
if (dma.rx_channel < 0) {
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
return -1;
}
dma_config.trans_width = GX_DMA_AHB_TRANS_WIDTH_8;
dma_config.src_addr_update = GX_DMA_AHB_CH_CTL_L_INC;
dma_config.src_hs_select = GX_DMA_AHB_HS_SEL_HW;
dma_config.src_master_select = GX_DMA_AHB_MASTER_3;
dma_config.src_msize = GX_DMA_AHB_BURST_TRANS_LEN_32;
dma_config.src_hs_per = 0;
dma_config.dst_addr_update = GX_DMA_AHB_CH_CTL_L_NOINC;
dma_config.dst_hs_select = GX_DMA_AHB_HS_SEL_HW;
dma_config.dst_master_select = GX_DMA_AHB_MASTER_2;
dma_config.dst_msize = GX_DMA_AHB_BURST_TRANS_LEN_8;
dma_config.dst_hs_per = DMA_HS_PRE_SPI2_TX;
dma_config.flow_ctrl = GX_DMA_AHB_TT_FC_MEM_TO_PER_DMAC;
dma_config.fifo_mode = 1;
gx_dcache_clean_range((uint32_t *)buf, len);
gx_dma_register_complete_callback(dma.tx_channel, qspi_tx_done, &tran_param);
if(gx_dma_xfer_int((void*)(GX_REG_BASE_SPI2 + GX_HAL_SPI_TXDR), (void *)((unsigned int)trans.tx_buf & 0x0FFFFFFF), trans.tx_len, dma.tx_channel, &dma_config)) {
gx_dcache_clean_invalid_range((uint32_t *)buf, len);
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
return -1;
}
return ret;
}
接收例子
// 未验证
static void qspi_rx_done(void *pdata)
{
struct dw_spi_async_tran *device = (struct dw_spi_async_tran *)pdata;
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
gx_dma_release_channel(dma.rx_channel);
dma.rx_channel = -1;
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_DISABLE);
if(device->rx_complete_cb)
device->rx_complete_cb(param);
rx_done = 1;
chip_os_sem_give(&spi_sem_rx);
}
int drv_qspi_rx_async(const uint8_t *buf, uint32_t len, void (*func)(void *), void* param)
{
int ret = 0;
struct gx_hal_spi_tran trans;
while(!rx_done) {
chip_os_sem_take(&spi_sem_rx, CHIP_OS_TIME_FOREVER);
}
rx_done = 0;
gx_hal_spi_cs_control(&hal_dws_qspi, 0, GX_HAL_SPI_ENABLE);
memset(&trans, 0x00, sizeof(struct gx_hal_spi_tran));
trans.bits_per_word = 8;
trans.tx_len = 0;
trans.rx_len = len;
trans.bus_mode = GX_HAL_SPI_QUAD;
trans.rx_buf = buf;
trans.tx_buf = NULL;
trans.trans_mode = GX_HAL_SPI_TRANS_RX_ONLY;
trans.instruction.size = 0;
trans.instruction.content = 0;
trans.instruction.spi_lines = trans.bus_mode*2;
trans.address.size = 0;
trans.address.content = 0;
trans.address.spi_lines = trans.bus_mode*2;
trans.dma_cfg = GX_HAL_SPI_DMA_RX_EN;
trans.dma_msize = 8;
ret = gx_hal_qspi_transfer_prepare(&hal_dws_qspi, &trans);
tran_param.msg_len = len;
tran_param.msg_offset = 0;
tran_param.tx_complete_cb = func;
tran_param.tx_buffer = buf;
tran_param.param = param;
GX_DMA_AHB_CH_CONFIG dma_config;
dma.tx_channel = gx_dma_select_channel();
if (dma.tx_channel < 0) {
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
return -1;
}
dma_config.trans_width = GX_DMA_AHB_TRANS_WIDTH_8;
dma_config.src_addr_update = GX_DMA_AHB_CH_CTL_L_INC;
dma_config.src_hs_select = GX_DMA_AHB_HS_SEL_HW;
dma_config.src_master_select = GX_DMA_AHB_MASTER_3;
dma_config.src_msize = GX_DMA_AHB_BURST_TRANS_LEN_32;
dma_config.src_hs_per = 0;
dma_config.dst_addr_update = GX_DMA_AHB_CH_CTL_L_NOINC;
dma_config.dst_hs_select = GX_DMA_AHB_HS_SEL_HW;
dma_config.dst_master_select = GX_DMA_AHB_MASTER_2;
dma_config.dst_msize = GX_DMA_AHB_BURST_TRANS_LEN_8;
dma_config.dst_hs_per = DMA_HS_PRE_SPI2_RX;
dma_config.flow_ctrl = GX_DMA_AHB_TT_FC_MEM_TO_PER_DMAC;
dma_config.fifo_mode = 1;
gx_dcache_clean_range((uint32_t *)buf, len);
gx_dma_register_complete_callback(dma.tx_channel, qspi_rx_done, &tran_param);
if(gx_dma_xfer_int((void*)(GX_REG_BASE_SPI2 + GX_HAL_SPI_TXDR), (void *)((unsigned int)trans.rx_buf & 0x0FFFFFFF), trans.rx_len, dma.tx_channel, &dma_config)) {
gx_dcache_clean_invalid_range((uint32_t *)buf, len);
gx_hal_spi_set_dma(&hal_dws_qspi, 0, 8);
return -1;
}
return ret;
}
4.6 多设备支持*
第五章 SPI从设备参考例子*
SPI2 可以做从设备。
5.1 Slaver API 接口*
/** \addtogroup <scpu>
* @{
*/
#ifndef __GX_SPI_SLAVE_H__
#define __GX_SPI_SLAVE_H__
#include <common.h>
#include <spi.h>
#define START_STATE (0)
#define RUNNING_STATE (1)
#define DONE_STATE (2)
#define ERROR_STATE (-1)
typedef void (*completion_cb)(struct spi_device *spi);
/**
* @brief 初始化SPI slave 模块
*
* @param spi slave设备, 详细说明请参考 gxdocref spi_device
*
* @return int 初始化是否成功
* @retval 0 成功
* @retval -1 失败
*/
int spi_slave_init(struct spi_device* spi);
/**
* @brief SPI slave 接收数据
*
* @param spi slave设备, 详细说明请参考 gxdocref spi_device
*
* @param buffer 发送数据地址
*
* @param len 发送数据长度, byte为单位
*
* @param slave_completetion_cb 发送完成回调接口,如果为NULL, 则使用阻塞模式发送; 回调函数在中断中执行;
*
* @return int 发送是否成功
* @retval 0 成功
* @retval -1 失败
*/
int spi_slave_rx(struct spi_device *spi, void* buffer, int len, completion_cb slave_completetion_cb);
/**
* @brief SPI slave 发送数据
*
* @param spi slave设备, 详细说明请参考 gxdocref spi_device
*
* @param buffer 接收数据地址
*
* @param len 接收数据长度, byte为单位
*
* @param slave_completetion_cb 接收完成回调接口,如果为NULL, 则使用阻塞模式接收; 回调函数在中断中执行;
*
* @return int 接收是否成功
* @retval 0 成功
* @retval -1 失败
*/
int spi_slave_tx(struct spi_device *spi, const void* buffer, int len, completion_cb slave_completetion_cb);
/**
* @brief 终止SPI传输
*
* @param spi slave设备, 详细说明请参考 gxdocref spi_device
*
* @return int 是否成功
* @retval 0 成功
* @retval -1 失败
*/
int spi_slave_abort(struct spi_device *spi);
#endif /* __GX_SPI_SLAVE_H__ */
/** @}*/
5.2 使用示例*
#include <stdio.h>
#include <osal.h>
#include <stdio.h>
#include <gx_hal_clk_apus.h>
#include <osal.h>
#include <spi.h>
#include <gx_padmux.h>
#include <gx_spi_slave.h>
#include <gx_dcache.h>
#include <pad_def.h>
/* 4线SPI模式 */
#define QUAD_SPI_SLAVE_MODE
#define SPI_SLAVE_CS P0_0 /* CLK */
#define SPI_SLAVE_CLK P0_2 /* CS */
#define SPI_SLAVE_IO0_PIN P0_1 /* MOSI */
#define SPI_SLAVE_IO1_PIN P0_3 /* MISO */
#ifdef QUAD_SPI_SLAVE_MODE
#define SPI_SLAVE_IO2_PIN P3_1
#define SPI_SLAVE_IO3_PIN P2_1
#endif
static struct spi_device g_spi_slave = {
.master = NULL,
.chip_select = 0,
.max_speed_hz = 1 * 1000 * 1000,
#ifdef QUAD_SPI_SLAVE_MODE
.mode = 2,
#else
.mode = 0,
#endif
.bits_per_word = 8, /* 最小帧数据,可选 8,16,24,32 */
#ifdef QUAD_SPI_SLAVE_MODE
.data_format = 2, /* < 数据格式,stand(0),dual(1),quad(2),octal(3) */
#else
.data_format = 0, /* < 数据格式,stand(0),dual(1),quad(2),octal(3) */
#endif
};
int main(void)
{
printf("Spi Slave Test App!\t\n");
int iRet = -1;
padmux_set(SPI_SLAVE_CS, 76);
padmux_set(SPI_SLAVE_CLK, 77);
padmux_set(SPI_SLAVE_IO0_PIN, 78);
padmux_set(SPI_SLAVE_IO1_PIN, 79);
#ifdef QUAD_SPI_SLAVE_MODE
padmux_set(SPI_SLAVE_IO2_PIN, 80);
padmux_set(SPI_SLAVE_IO3_PIN, 81);
#endif
if((iRet = spi_slave_init(&g_spi_slave)) != 0) {
printf("spi slave init fiald%d!\t\n", iRet);
return -1;
}
printf("init slave init successfull!\t\n");
return 0;
}
static unsigned char spi_test_buf[12] = {0};
#if 0
static void spi_slave_cb(struct spi_device * spi)
{
printf("%s: %d, slave callback\n", __FILE__, __LINE__);
}
#endif
/* spi slave send data */
static int do_spi_slave_tx(int argc, char * const argv[])
{
memset(spi_test_buf, 0x68, sizeof(spi_test_buf));
spi_test_buf[0] = 0x42;
spi_test_buf[1] = 0x55;
spi_test_buf[2] = 0x58;
spi_test_buf[3] = 0x58;
/* 同步阻塞发送数据 */
if(spi_slave_tx(&g_spi_slave, spi_test_buf, sizeof(spi_test_buf), NULL)) {
printf("spi slave write error, len:%d\r\n", sizeof(spi_test_buf));
return -1;
}
printf("spi slave write data successfull!\t\n");
return 0;
}
/* spi slave rcve data*/
static int do_spi_slave_rx(int argc, char * const argv[])
{
memset(spi_test_buf, 0, sizeof(spi_test_buf));
#if 0
gx_dcache_clean_invalid_range((uint32_t *)spi_test_buf, sizeof(spi_test_buf));
#endif
/* 同步阻塞接受数据 */
if(spi_slave_rx(&g_spi_slave, spi_test_buf, sizeof(spi_test_buf), NULL)) {
printf("spi slave read error, len:%d\r\n", sizeof(spi_test_buf));
return -1;
}
printf("spi slave read data successfull!\t\n");
printf("%X %X %X %X\n",spi_test_buf[0], spi_test_buf[1], spi_test_buf[2], spi_test_buf[3]);
return 0;
}
COMMAND_EXPORT_ALIAS(do_spi_slave_tx, do_spi_slave_tx, "do_spi_slave_tx");
COMMAND_EXPORT_ALIAS(do_spi_slave_rx, do_spi_slave_rx, "do_spi_slave_rx");