在RTT下为什么推荐使用RTT统一标准的SPI接口
在《【龙芯1c库】封装硬件SPI接口和使用示例》http://blog.csdn.net/caogos/article/details/78353988中提到了,龙芯1c的每路SPI有4个片选,在多任务的实时系统中需要注意互斥的问题。龙芯1C库中的SPI接口仅仅是把龙芯1C的SPI硬件相关特别封装成了一个一个的软件接口。而RT-Thread中统一的标准SPI接口刚好解决了互斥的问题。即只要把龙芯1c库中的SPI接口稍加修改,与RTT中统一的标准SPI接口适配好后,就可以实现使用RTT标准SPI接口编程,而不需考虑互斥的问题。下面以函数 rt_spi_transfer()为例来看看RTT是如何实现互斥的。
程序的大概意思是,在SPI收发之前首先获取锁,以此实现互斥。然后判断上一次SPI通信的从设备是否与当前从设备为同一个,对应代码为if (device->bus->owner != device),如果不是,需要调用evice->bus->ops->configure()函数重新配置SPI的时钟频率,极性和相位等,最后才是调用device->bus->ops->xfer()实现具体的SPI收发。
其实,在刚开始接触RTT和龙芯1C时,不用关注这么多细节,我已经把RTT统一的标准的SPI接口移植到龙芯1C上了,只需要知道怎么使用这些接口即可。如果之前在STM32上用过RTT的SPI,那么我告诉你,在龙芯1C上也是一样的。因为是RTT统一的SPI接口和具体的芯片“无关”。下面来看看RTT提供了几个统一的SPI接口,都有什么功能。
RTT统一的标准的SPI接口简介
注册SPI总线
函数原型
/*
* 初始化并注册龙芯1c的spi总线
* @SPI SPI总线,比如LS1C_SPI_0, LS1C_SPI_1
* @spi_bus_name 总线名字
* @ret
*/
rt_err_t ls1c_spi_bus_register(rt_uint8_t SPI, const char *spi_bus_name)
函数ls1c_spi_bus_register()调用RTT的统一标准接口rt_spi_bus_register(),实现SPI总线的注册
使用示例
// SPI模块编号
#define LS1C_SPI_0 (0)
#define LS1C_SPI_1 (1)
#define LS1C_SPI0_BUS_NAME ("spi0")
ls1c_spi_bus_register(LS1C_SPI_1, LS1C_SPI0_BUS_NAME);
挂接SPI从设备到SPI总线
函数原型
rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
const char *name,
const char *bus_name,
void *user_data)
这个直接就是RTT的统一标准接口rt_spi_bus_attach_device()。
使用示例
#define LS1C_SPI0_BUS_NAME ("spi0")
#define TM7705_SPI_DEVICE_NAME ("tm7705")
struct rt_spi_device tm7705_spi_device;
static struct ls1c_spi_cs spi_cs;
// 把从设备(tm7705)挂在SPI总线上
spi_cs.cs = LS1C_SPI_CS_1;
result = rt_spi_bus_attach_device(&tm7705_spi_device,
TM7705_SPI_DEVICE_NAME,
LS1C_SPI0_BUS_NAME,
&spi_cs);
if (RT_EOK != result)
{
rt_kprintf("[%s] attach spi device tm7705 fail.\n", __FUNCTION__);
return ;
}
注意,这里把片选信息以入参的形式传递给RTT。变量tm7705_spi_device和spi_cs常定义为static类型,我这里把tm7705_spi_device定义为全局变量了,也是可以的。目的是不能把这两个变量作为函数的临时变量,所占的空间不能被自动释放。
片选信息的结构体定义在头文件“bsp\ls1cdev\drivers\drv_spi.h”里,如下
struct ls1c_spi_cs
{
unsigned char cs; // LS1C_SPI_CS_0, LS1C_SPI_CS_1, LS1C_SPI_CS_2 or LS1C_SPI_CS_3
};
片选的几个可选值在头文件“bsp\ls1cdev\libraries\ls1c_spi.h”中已定义好了,如下
// 片选
#define LS1C_SPI_CS_0 (0)
#define LS1C_SPI_CS_1 (1)
#define LS1C_SPI_CS_2 (2)
#define LS1C_SPI_CS_3 (3)
注意,这几个片选宏的值不能随意更改,函数ls1c_spi_set_cs()使用这几个值来设置龙芯1C的SPI片选控制寄存器的。实际上也没必要修改这几个宏的值,直接调用即可。
配置SPI
函数原型
t_err_t rt_spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *cfg)
配置信息的结构体为
/**
* SPI configuration structure
*/
struct rt_spi_configuration
{
rt_uint8_t mode;
rt_uint8_t data_width;
rt_uint16_t reserved;
rt_uint32_t max_hz;
};
注意,龙芯1C只关注其中的mode和max_hz就可以了。data_width和reserved可以忽略。
使用示例
struct rt_spi_device tm7705_spi_device;
struct rt_spi_configuration cfg;
// 配置SPI
cfg.mode = RT_SPI_MODE_3;
cfg.max_hz = 100*1000;
rt_spi_configure(&tm7705_spi_device, &cfg);
几个常用的SPI收发函数
rt_spi_send()
函数原型
rt_inline rt_size_t rt_spi_send(struct rt_spi_device *device,
const void *send_buf,
rt_size_t length)
使用示例
struct rt_spi_device tm7705_spi_device;
const unsigned char send_buf[4] = {0xFF};
rt_spi_send(&tm7705_spi_device, send_buf, 4);
rt_spi_recv()
函数原型
rt_inline rt_size_t rt_spi_recv(struct rt_spi_device *device,
void *recv_buf,
rt_size_t length)
使用示例
struct rt_spi_device tm7705_spi_device;
Unsigned char recv_buf[4] = {0};
rt_spi_recv(&tm7705_spi_device, recv_buf, 4);
rt_spi_send_then_recv()
函数原型
rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
const void *send_buf,
rt_size_t send_length,
void *recv_buf,
rt_size_t recv_length)
使用示例
struct rt_spi_device tm7705_spi_device;
unsigned char send_buf[1] = {0};
unsigned char recv_buf[2] = {0};
rt_spi_send_then_recv(&tm7705_spi_device, send_buf, 1, recv_buf, 2);
RTT还提供了rt_spi_send_then_send()、rt_spi_transfer()等,具体查看源码,应该很容易看懂的。
设置引脚复用(不是必须的)
可能需要设置引脚复用,引脚复用接口使用头文件“bsp\ls1cdev\libraries\ls1c_pin.h”中的pin_set_remap()即可。例如
// spi复用
#define LS1C_SPI_1_CS_0_GPIO (49) // gpio49/spi1_cs0/CAMHSYNC
#define LS1C_SPI_1_CS_1_GPIO (50) // gpio50/spi1_cs1/CAMDATA0
#define LS1C_SPI_1_CS_2_GPIO (51) // gpio51/spi1_cs2/CAMDATA1
#define LS1C_SPI_1_CS_3_GPIO (52) // gpio52/spi1_cs3/CAMDATA2
#define LS1C_SPI_1_MISO_GPIO (47) // gpio47/spi1_miso/CAMCLKOUT
#define LS1C_SPI_1_MOSI_GPIO (48) // gpio48/spi1_mosi/CAMVSYNC
#define LS1C_SPI_1_CLK_GPIO (46) // gpio46/spi1_clk/CAMPCLKIN
// SPI1 CS1
pin_set_remap(LS1C_SPI_1_MISO_GPIO, PIN_REMAP_THIRD);
pin_set_remap(LS1C_SPI_1_MOSI_GPIO, PIN_REMAP_THIRD);
pin_set_remap(LS1C_SPI_1_CLK_GPIO, PIN_REMAP_THIRD);
pin_set_remap(LS1C_SPI_1_CS_1_GPIO, PIN_REMAP_THIRD); // cs1
综合应用示例——在龙芯1C上接双路16位AD芯片TM7705
本示例为3d打印机中使用tm7705+NTC热敏电阻实现温度测量的那部分代码,有兴趣的可以移步到
《【龙印】在龙芯1c上用TM7705+NTC热敏电阻实现温度测量》http://blog.csdn.net/caogos/article/details/531266