393 lines
9.5 KiB
C
393 lines
9.5 KiB
C
/*
|
||
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||
*
|
||
* SPDX-License-Identifier: Apache-2.0
|
||
*
|
||
* Change Logs:
|
||
* Date Author Notes
|
||
* 2021-12-15 JasonHu first version
|
||
*/
|
||
|
||
#include <rtthread.h>
|
||
#include <rthw.h>
|
||
#include <rtdevice.h>
|
||
|
||
#include "drv_spi.h"
|
||
|
||
#define DBG_TAG "DRV.SPI"
|
||
#define DBG_LVL DBG_LOG
|
||
#include <rtdbg.h>
|
||
#include <sunxi_hal_spi.h>
|
||
|
||
#ifdef BSP_USING_SPI
|
||
|
||
struct hw_spi_bus
|
||
{
|
||
struct rt_spi_bus parent;
|
||
hal_spi_master_port_t port;
|
||
};
|
||
|
||
#define BSP_SPI_MAX_HZ SPI_MAX_FREQUENCY
|
||
|
||
#if defined (BSP_USING_SPI0)
|
||
#define SPI0_BUS_NAME "spi0"
|
||
#define SPI0_BUS_DEVICE0_NAME "spi01"
|
||
|
||
struct hw_spi_bus spi0_bus;
|
||
|
||
#endif /* BSP_USING_SPI0 */
|
||
|
||
#if defined (BSP_USING_SPI1)
|
||
#define SPI1_BUS_NAME "spi1"
|
||
#define SPI1_BUS_DEVICE0_NAME "spi11"
|
||
|
||
struct hw_spi_bus spi1_bus;
|
||
|
||
#endif /* BSP_USING_SPI1 */
|
||
|
||
static rt_err_t hw_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
|
||
{
|
||
RT_ASSERT(cfg != RT_NULL);
|
||
RT_ASSERT(device != RT_NULL);
|
||
|
||
struct hw_spi_bus *spi_dev = RT_NULL;
|
||
hal_spi_master_config_t hal_cfg;
|
||
|
||
LOG_D("configure mode:%x, clk:%d\n", cfg->mode, cfg->max_hz);
|
||
|
||
/* trans cfg to hal cfg */
|
||
spi_dev = (struct hw_spi_bus *)(device->parent.user_data);
|
||
|
||
if (cfg->max_hz > BSP_SPI_MAX_HZ)
|
||
cfg->max_hz = BSP_SPI_MAX_HZ;
|
||
|
||
/* set default frequency */
|
||
hal_cfg.clock_frequency = cfg->max_hz;
|
||
hal_cfg.slave_port = HAL_SPI_MASTER_SLAVE_0; /* only support slave 0 */
|
||
|
||
/* byte order */
|
||
if (cfg->mode & RT_SPI_MSB)
|
||
{
|
||
hal_cfg.bit_order = HAL_SPI_MASTER_MSB_FIRST;
|
||
}
|
||
else
|
||
{
|
||
hal_cfg.bit_order = HAL_SPI_MASTER_LSB_FIRST;
|
||
}
|
||
|
||
if(cfg->mode & RT_SPI_CPOL)
|
||
{
|
||
hal_cfg.cpol = HAL_SPI_MASTER_CLOCK_POLARITY1;
|
||
}
|
||
else
|
||
{
|
||
hal_cfg.cpol = HAL_SPI_MASTER_CLOCK_POLARITY0;
|
||
}
|
||
|
||
if(cfg->mode & RT_SPI_CPHA)
|
||
{
|
||
hal_cfg.cpha = HAL_SPI_MASTER_CLOCK_PHASE1;
|
||
}
|
||
else
|
||
{
|
||
hal_cfg.cpha = HAL_SPI_MASTER_CLOCK_PHASE0;
|
||
}
|
||
|
||
if (cfg->mode & RT_SPI_NO_CS)
|
||
{
|
||
hal_cfg.csmode = HAL_SPI_MASTER_CS_AUTO;
|
||
}
|
||
else
|
||
{
|
||
hal_cfg.csmode = HAL_SPI_MASTER_CS_SOFT;
|
||
}
|
||
|
||
if (hal_spi_hw_config(spi_dev->port, &hal_cfg) != SPI_MASTER_OK)
|
||
{
|
||
return -RT_EIO;
|
||
}
|
||
|
||
return RT_EOK;
|
||
}
|
||
|
||
static rt_uint32_t hw_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
|
||
{
|
||
spi_master_status_t ret = SPI_MASTER_OK;
|
||
hal_spi_master_transfer_t tr;
|
||
struct hw_spi_bus *spi_dev = RT_NULL;
|
||
|
||
RT_ASSERT(device != RT_NULL);
|
||
RT_ASSERT(device->bus != RT_NULL);
|
||
RT_ASSERT(device->parent.user_data != RT_NULL);
|
||
|
||
spi_dev = (struct hw_spi_bus *)(device->parent.user_data);
|
||
|
||
tr.rx_buf = message->recv_buf;
|
||
tr.rx_len = message->recv_buf == RT_NULL ? 0 : message->length;
|
||
tr.tx_buf = message->send_buf;
|
||
tr.tx_len = message->send_buf == RT_NULL ? 0 : message->length;
|
||
tr.dummy_byte = 0;
|
||
tr.tx_single_len = message->send_buf == RT_NULL ? 0 : message->length;
|
||
tr.rx_nbits = SPI_NBITS_SINGLE;
|
||
tr.tx_nbits = SPI_NBITS_SINGLE;
|
||
|
||
if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS))
|
||
{
|
||
if (device->config.mode & RT_SPI_CS_HIGH)
|
||
{
|
||
hal_spi_cs(spi_dev->port, 1);
|
||
}
|
||
else
|
||
{
|
||
hal_spi_cs(spi_dev->port, 0);
|
||
}
|
||
}
|
||
|
||
if (message->length > 0)
|
||
{
|
||
ret = hal_spi_xfer(spi_dev->port, &tr);
|
||
}
|
||
|
||
// FIXME: GCC O3 maybe not execute.
|
||
if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS))
|
||
{
|
||
if (device->config.mode & RT_SPI_CS_HIGH)
|
||
{
|
||
hal_spi_cs(spi_dev->port, 0);
|
||
}
|
||
else
|
||
{
|
||
hal_spi_cs(spi_dev->port, 1);
|
||
}
|
||
}
|
||
|
||
if (ret != SPI_MASTER_OK)
|
||
{
|
||
LOG_E("xfer err ret:%ld", ret);
|
||
return -RT_EIO;
|
||
}
|
||
|
||
return message->length;
|
||
}
|
||
|
||
/**
|
||
* attach a spi device on bus
|
||
*/
|
||
rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name)
|
||
{
|
||
rt_err_t ret = RT_EOK;
|
||
struct hw_spi_bus *hw_spi = RT_NULL;
|
||
|
||
struct rt_spi_device *spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
|
||
RT_ASSERT(spi_device != RT_NULL);
|
||
#ifdef BSP_USING_SPI0
|
||
if (!rt_strcmp(bus_name, SPI0_BUS_NAME))
|
||
{
|
||
hw_spi = &spi0_bus;
|
||
}
|
||
else
|
||
#endif
|
||
#ifdef BSP_USING_SPI1
|
||
if (!rt_strcmp(bus_name, SPI0_BUS_NAME))
|
||
{
|
||
hw_spi = &spi1_bus;
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
return -RT_EIO;
|
||
}
|
||
/* TODO: attach device */
|
||
ret = rt_spi_bus_attach_device(spi_device, device_name, bus_name, hw_spi);
|
||
return ret;
|
||
}
|
||
|
||
static struct rt_spi_ops hw_spi_ops =
|
||
{
|
||
.configure = hw_spi_configure,
|
||
.xfer = hw_spi_xfer
|
||
};
|
||
|
||
#endif /* BSP_USING_SPI */
|
||
|
||
/* used to init spi bus */
|
||
static hal_spi_master_config_t default_hal_cfg = {
|
||
.bit_order = HAL_SPI_MASTER_MSB_FIRST,
|
||
.clock_frequency = 5000000,//SPI_MOD_CLK,
|
||
.cpha = HAL_SPI_MASTER_CLOCK_PHASE0,
|
||
.cpol = HAL_SPI_MASTER_CLOCK_POLARITY0,
|
||
.slave_port = HAL_SPI_MASTER_SLAVE_0,
|
||
.csmode = HAL_SPI_MASTER_CS_SOFT,
|
||
};
|
||
|
||
int rt_hw_spi_init(void)
|
||
{
|
||
#if defined (BSP_USING_SPI0)
|
||
spi0_bus.port = HAL_SPI_MASTER_0;
|
||
if (hal_spi_init(spi0_bus.port, &default_hal_cfg) != SPI_MASTER_OK)
|
||
{
|
||
LOG_E("hal init %s bus failed!", SPI0_BUS_NAME);
|
||
return -1;
|
||
}
|
||
rt_spi_bus_register(&spi0_bus.parent, SPI0_BUS_NAME, &hw_spi_ops);
|
||
|
||
rt_hw_spi_device_attach(SPI0_BUS_NAME, SPI0_BUS_DEVICE0_NAME);
|
||
#endif /* BSP_USING_SPI0 */
|
||
|
||
#if defined (BSP_USING_SPI1)
|
||
spi1_bus.port = HAL_SPI_MASTER_1;
|
||
if (hal_spi_init(spi1_bus.port, &default_hal_cfg) != SPI_MASTER_OK)
|
||
{
|
||
LOG_E("hal init %s bus failed!", SPI1_BUS_NAME);
|
||
return -1;
|
||
}
|
||
rt_spi_bus_register(&spi1_bus.parent, SPI1_BUS_NAME, &hw_spi_ops);
|
||
rt_hw_spi_device_attach(SPI1_BUS_NAME, SPI1_BUS_DEVICE0_NAME);
|
||
|
||
#endif /* BSP_USING_SPI1 */
|
||
return RT_EOK;
|
||
}
|
||
INIT_DEVICE_EXPORT(rt_hw_spi_init);
|
||
|
||
#define CFG_GPIO_PORT(p) ((p) - 'A' + 1)
|
||
|
||
int32_t hal_spi_gpio_cfg_count(const char *secname)
|
||
{
|
||
return 4;
|
||
}
|
||
|
||
static const user_gpio_set_t _spi_gpio_cfg[][4] = {
|
||
{// spi0
|
||
{
|
||
.gpio_name = "spi0_sclk",
|
||
.port = CFG_GPIO_PORT('C'),
|
||
.port_num = 2, // PC02
|
||
.mul_sel = 2,
|
||
.pull = 0, // no pull
|
||
.drv_level = 3,
|
||
.data = 0,
|
||
},
|
||
{
|
||
.gpio_name = "spi0_cs",
|
||
.port = CFG_GPIO_PORT('C'),
|
||
.port_num = 3, // PC03
|
||
.mul_sel = 2,
|
||
.pull = 1, // pull up
|
||
.drv_level = 3,
|
||
.data = 0,
|
||
},
|
||
{
|
||
.gpio_name = "spi0_mosi",
|
||
.port = CFG_GPIO_PORT('C'),
|
||
.port_num = 4, // PC04
|
||
.mul_sel = 2,
|
||
.pull = 0, // no pull
|
||
.drv_level = 3,
|
||
.data = 0,
|
||
},
|
||
{
|
||
.gpio_name = "spi0_miso",
|
||
.port = CFG_GPIO_PORT('C'),
|
||
.port_num = 5, // PC05
|
||
.mul_sel = 2,
|
||
.pull = 0, // no pull
|
||
.drv_level = 3,
|
||
.data = 0,
|
||
}
|
||
},
|
||
};
|
||
|
||
int hal_spi_gpio_cfg_load(user_gpio_set_t *gpio_cfg, int32_t GPIONum, int id)
|
||
{
|
||
int i;
|
||
|
||
if (id > sizeof(_spi_gpio_cfg) / sizeof(_spi_gpio_cfg[0]))
|
||
{
|
||
rt_kprintf("spi id %d>%d\n", id, sizeof(_spi_gpio_cfg) / sizeof(_spi_gpio_cfg[0]));
|
||
return -1;
|
||
}
|
||
|
||
/* twi0 */
|
||
for (i = 0; i < GPIONum; i++)
|
||
{
|
||
memcpy(gpio_cfg, &_spi_gpio_cfg[id][i], sizeof(user_gpio_set_t));
|
||
gpio_cfg++;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 程序清单:这是一个 SPI 设备使用例程
|
||
* 例程导出了 spi_w25q_sample 命令到控制终端
|
||
* 命令调用格式:spi_w25q_sample spi10
|
||
* 命令解释:命令第二个参数是要使用的SPI设备名称,为空则使用默认的SPI设备
|
||
* 程序功能:通过SPI设备读取 w25q 的 ID 数据
|
||
*/
|
||
|
||
#include <rtthread.h>
|
||
#include <rtdevice.h>
|
||
|
||
#define SPI_DEVICE_NAME "spi01"
|
||
|
||
static void spi_sample(int argc, char *argv[])
|
||
{
|
||
struct rt_spi_device *spi_dev;
|
||
static char cmd[200];
|
||
static char recv_data[200];
|
||
rt_uint8_t id[5] = {0};
|
||
int i;
|
||
rt_err_t err;
|
||
int type;
|
||
|
||
type = atoi(argv[1]);
|
||
|
||
for (i = 0; i < sizeof(cmd); i++)
|
||
{
|
||
cmd[i] = i + 1;
|
||
}
|
||
|
||
/* 查找 spi 设备获取设备句柄 */
|
||
spi_dev = (struct rt_spi_device *)rt_device_find(SPI_DEVICE_NAME);
|
||
if (!spi_dev)
|
||
{
|
||
rt_kprintf("spi sample run failed! can't find %s device!\n", SPI_DEVICE_NAME);
|
||
}
|
||
else
|
||
{
|
||
struct rt_spi_configuration spi_cfg;
|
||
spi_cfg.max_hz = 5000000;
|
||
spi_cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB;
|
||
spi_cfg.data_width = 8;
|
||
|
||
rt_spi_configure(spi_dev, &spi_cfg);
|
||
if (type == 1)
|
||
{
|
||
err = rt_spi_transfer(spi_dev, cmd, recv_data, sizeof(recv_data));
|
||
}
|
||
else if (type == 2)
|
||
{
|
||
err = rt_spi_send_then_recv(spi_dev, cmd, sizeof(cmd), recv_data, sizeof(recv_data));
|
||
}
|
||
else if (type == 3)
|
||
{
|
||
err = rt_spi_send(spi_dev, cmd, sizeof(cmd));
|
||
}
|
||
else if (type == 4)
|
||
{
|
||
err = rt_spi_send_then_send(spi_dev, cmd, sizeof(cmd), cmd, sizeof(cmd));
|
||
}
|
||
else if (type == 5)
|
||
{
|
||
err = rt_spi_recv(spi_dev, recv_data, sizeof(recv_data));
|
||
}
|
||
// err = rt_spi_send(spi_dev, cmd, sizeof(cmd));
|
||
if (err != sizeof(cmd))
|
||
{
|
||
rt_kprintf("spi send error:%d\n", err);
|
||
}
|
||
}
|
||
}
|
||
/* 导出到 msh 命令列表中 */
|
||
MSH_CMD_EXPORT(spi_sample, spi w25q sample);
|