rtthread中的spi驱动外设设备的挂载知识点:
在scons工具中添加spi的设备注册,可以看到
spi的驱动文件drv_spi,使用spi需要把rtthread中的drivede中的spi_core,spi_dev文件添加进去
在spi_core中可以看到以下函数
rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus,
const char *name,
const struct rt_spi_ops *ops)
{
rt_err_t result;
result = rt_spi_bus_device_init(bus, name);
if (result != RT_EOK)
return result;
/* initialize mutex lock */
rt_mutex_init(&(bus->lock), name, RT_IPC_FLAG_FIFO);
/* set ops */
bus->ops = ops;
/* initialize owner */
bus->owner = RT_NULL; //一开hi初始化为null
/* set bus mode */
bus->mode = RT_SPI_BUS_MODE_SPI;
return RT_EOK;
}
rt_spi_bus_register注册函数中可以看到传参的时候有几个参数
struct rt_spi_bus
{
struct rt_device parent;
rt_uint8_t mode;
const struct rt_spi_ops *ops;
struct rt_mutex lock;
struct rt_spi_device *owner;
};
结合参数和rt_spi_bus可以看出注册总线其实就是在初始化总线的结构体
通过把ops实现好的结构体付给bus总线,其中在rt_spi_bus_register函数可以看到,bus->owner为RT_NULL(这个变量很重要)
rt_spi_ops这个结构体的参数主要就是实现两个函数指针
一个是对spi设备的配置->configure,一个是对发送接收的实现接口->xfer,函数的实现在drv_spi.c文件接下来我们可以看到将spi设备绑定到spi总线上的函数rt_spi_bus_attach_device
rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
const char *name,
const char *bus_name,
void *user_data)
{
rt_err_t result;
rt_device_t bus;
/* get physical spi bus */
bus = rt_device_find(bus_name);
if (bus != RT_NULL && bus->type == RT_Device_Class_SPIBUS)
{
device->bus = (struct rt_spi_bus *)bus;
/* initialize spidev device */
result = rt_spidev_device_init(device, name);
if (result != RT_EOK)
return result;
rt_memset(&device->config, 0, sizeof(device->config));
device->parent.user_data = user_data;
return RT_EOK;
}
/* not found the host bus */
return -RT_ERROR;
}
这个函数的重点就是在于rt_spidev_device_init绑定设备的spi名称到总线上
#define SPI_BUS_NAME "spi0"
#define SPI_dac104s085_DEVICE_NAME "spi01"
#define CS_PIN 29
/*
STM32F303VET6 SPI0 default GPIOs
PA4------SPI0NSS
PA5------SPI0SCK
PA6------SPI0MISO
PA7------SPI0MOSI
*/
struct stm32_hw_spi_cs
{
rt_uint32_t pin;
};
static struct rt_spi_device spi_dev_dac104s085;
static struct stm32_hw_spi_cs spi_cs;
int rt_hw_dac104s085_config(void)
{
rt_err_t res;
spi_cs.pin = CS_PIN;
rt_pin_mode(spi_cs.pin, GPIO_MODE_OUT_PP);
rt_pin_write(CS_PIN, PIN_HIGH);
res = rt_spi_bus_attach_device(&spi_dev_dac104s085, SPI_dac104s085_DEVICE_NAME, SPI_BUS_NAME, (void*)&spi_cs);
if (res != RT_EOK)
{
OLED_TRACE("rt_spi_bus_attach_device!\r\n");
return res;
}
spi_dev_dac104s085.bus->owner = &spi_dev_dac104s085;
{
struct rt_spi_configuration cfg;
cfg.data_width = 16;
cfg.mode = RT_SPI_MODE_1 | RT_SPI_MSB;
cfg.max_hz = 40 * 1000 *1000; /* 40M,SPI max 42MHz, 3-wire spi */
if(spi_dev_dac104s085.bus->owner==RT_NULL)
{
rt_kprintf("RT_OK\r\n");
}
rt_kprintf("owner= %d\r\n",*spi_dev_dac104s085.bus->owner);
rt_spi_configure(&spi_dev_dac104s085, &(cfg));
}
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_dac104s085_config);
从上面的函数可以看到我们是在配置spi设备的结构体
其中你可以看到中间多了一句spi_dev_dac104s085.bus->owner = &spi_dev_dac104s085;
这个的主要作用就是将bus->owner变量赋值为自身
因为上面讲到总线注册的时候这里是初始化为null的
假如这里不进行赋值,你可以看到spi的配置不成功,原因就在于spi_core.c中有一个配置的函数rt_spi_configure
rt_err_t rt_spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *cfg)
{
rt_err_t result;
RT_ASSERT(device != RT_NULL);
/* set configuration */
device->config.data_width = cfg->data_width;
device->config.mode = cfg->mode & RT_SPI_MODE_MASK ;
device->config.max_hz = cfg->max_hz ;
if (device->bus != RT_NULL)
{
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
if (device->bus->owner == device) //判断不等就不初始化
{
device->bus->ops->configure(device, &device->config);
}
/* release lock */
rt_mutex_release(&(device->bus->lock));
}
}
return RT_EOK;
}
里面有对device->bus->owner进行判断,假如为null,这里就执行不到device->bus->ops->configure(device, &device->config);
这个ops中的配置函数了,
然后你在外部进行调用rt_spi_send函数对spi进行发送的时候就会发现只有这个发送函数在任意的地方调用了才配置spi设备成功
rt_err_t dac104s085_write_data(const rt_uint16_t data,const int lendth)
{
rt_size_t len;
len = rt_spi_send(&spi_dev_dac104s085, &data, lendth);
if (len != lendth)
{
OLED_TRACE("dac104s085_write_data error. %d\r\n",len);
return -RT_ERROR;
}
else
{
return RT_EOK;
}
}
原因就在rt_spi_send里面的实现机制了
跳进rt_spi_send可以看到里面调用的是rt_spi_transfer
rt_size_t rt_spi_transfer(struct rt_spi_device *device,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
rt_err_t result;
struct rt_spi_message message;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
result = RT_EOK;//rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */ //与之前的绑定设备不同就重新初始化一遍设备
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
rt_set_errno(-RT_EIO);
result = 0;
goto __exit;
}
}
/* initial message */
message.send_buf = send_buf;
message.recv_buf = recv_buf;
message.length = length;
message.cs_take = 1;
message.cs_release = 1;
message.next = RT_NULL;
/* transfer message */
result = device->bus->ops->xfer(device, &message);
if (result == 0)
{
rt_set_errno(-RT_EIO);
goto __exit;
}
}
else
{
rt_set_errno(-RT_EIO);
return 0;
}
__exit:
// rt_mutex_release(&(device->bus->lock));
return result;
}
rt_spi_transfer里面有一段代码就是
device->bus->owner != device
判断device->bus->owner不等于之前注册的spi设备,就会重新去配置一下,所以在一开始如果不对device->bus->owner配置
就会出现只有在调用了发送函数才可以正常配置spi设备
问题点思考:
然后我这里比较郁闷,既然rtthread的rt_spi_transfer,rt_spi_configure里面要去判断device->bus->owner是否是注册了的设备,
为什么不再rt_spi_bus_attach_device这个函数中就帮用户进行初始化device->bus->owner呢?
另外在spi的发送函数rt_spi_transfer中可以看到里面是调用了rt_mutex_take,和rt_mutex_release导致不能再中断中使用
spi的发送函数,不知道网上各位大神有没有什么号的方式去改善这个spi的发送函数在中断中调用的机制呢?
我这里是直接把这两个给注销掉了,然后就可以在中断中调用了
另外在rtthread的控制台中输入list_device出现
msh />list_device
device type ref count
-------- -------------------- ----------
spi01 SPI Device 0
i2c0 I2C Bus 0
timer2 Timer Device 1
timer1 Timer Device 1
pin Miscellaneous Device 0
spi1 SPI Bus 0
spi0 SPI Bus 0
uart1 Character Device 0
uart0 Character Device 3
出现bus总线数目为0,设备为0 ,不知道是不是正常的,但是程序确实是正常工作的
另外spi的设备如果数目想不是0 的话要做的工作就对spi进行查找rt_device_find()并打开rt_device_open() spi的设备,就可以正常看到spi的设备不为0了