在龙芯1c上使用rt-thread统一标准的spi接口

在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只关注其中的modemax_hz就可以了。data_widthreserved可以忽略。

使用示例

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/53126628
《【龙印】龙芯1c上双路16位AD芯片TM7705的linux驱动》http://blog.csdn.net/caogos/article/details/53034196

这里直接贴源码了。

application.c

bsp\ls1cdev\applications\application.c

/*
 * File      : application.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006-2012, RT-Thread Develop Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date                Author         Notes
 * 2010-06-25          Bernard        first version
 * 2011-08-08          lgnq           modified for Loongson LS1B
 * 2015-07-06          chinesebear    modified for Loongson LS1C
 */

#include <rtthread.h>
#include "net/synopGMAC.h"
#include <lwip/api.h>
#include "test_spi.h"


// 测试用的线程
#define THREAD_TEST_PRIORITY                    (25)
#define THREAD_TEST_STACK_SIZE                  (4*1024)        // 4k
#define THREAD_TEST_TIMESLICE                   (10)

struct rt_thread thread_test;
ALIGN(8)
rt_uint8_t thread_test_stack[THREAD_TEST_STACK_SIZE];


// 测试用的线程的入口
void thread_test_entry(void *parameter)
{
    float temp_f;
    
    // tm7705初始化
    tm7705_init();
    rt_kprintf("[%s] tm7705 init ok!\r\n", __FUNCTION__);
    
    while (1)
    {
        temp_f = tm7705_get_temperature();
        
        rt_thread_delay(2*1000);
    }
}


void rt_init_thread_entry(void *parameter)
{
	/* initialization RT-Thread Components */
	rt_components_init();

    // 网口EMAC初始化
	rt_hw_eth_init();
}

int rt_application_init(void)
{
	rt_thread_t tid;
    rt_err_t result;

	/* create initialization thread */
	tid = rt_thread_create("init",
							rt_init_thread_entry, RT_NULL,
							4096, RT_THREAD_PRIORITY_MAX/3, 20);
	if (tid != RT_NULL)
		rt_thread_startup(tid);

    // 初始化测试用的线程
    result = rt_thread_init(&thread_test, 
                            "thread_test",
                            thread_test_entry,
                            RT_NULL,
                            &thread_test_stack[0],
                            sizeof(thread_test_stack),
                            THREAD_TEST_PRIORITY,
                            THREAD_TEST_TIMESLICE);
    if (RT_EOK == result)
    {
        rt_thread_startup(&thread_test);
    }
    else
    {
        return -1;
    }

	return 0;
}

test_spi.c

bsp\ls1cdev\applications\test_spi.c

// 测试硬件spi源文件

#include <rtthread.h>
#include <drivers/spi.h>
#include "../libraries/ls1c_public.h"
#include "../libraries/ls1c_pin.h"
#include "../libraries/ls1c_gpio.h"
#include "../libraries/ls1c_delay.h"
#include "../drivers/drv_spi.h"


// 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


// 通信寄存器bit定义
enum 
{
	// 寄存器选择  RS2 RS1 RS0
	TM7705_REG_COMM	        = (0 << 4), // 通信寄存器
	TM7705_REG_SETUP	    = (1 << 4), // 设置寄存器
	TM7705_REG_CLOCK	    = (2 << 4), // 时钟寄存器
	TM7705_REG_DATA	        = (3 << 4), // 数据寄存器
	TM7705_REG_TEST         = (4 << 4), // 测试寄存器
	TM7705_REG_OFFSET       = (6 << 4), // 偏移寄存器
	TM7705_REG_GAIN         = (7 << 4), // 增益寄存器
	
    // 读写操作
	TM7705_WRITE 		    = (0 << 3), // 写操作
	TM7705_READ 		    = (1 << 3), // 读操作

	// 通道
	TM7705_CH_1		        = 0,    // AIN1+  AIN1-
	TM7705_CH_2		        = 1,    // AIN2+  AIN2-
	TM7705_CH_3		        = 2,    // AIN1-  AIN1-
	TM7705_CH_4		        = 3     // AIN1-  AIN2-
};


/* 设置寄存器bit定义 */
enum
{
	TM7705_MD_NORMAL		= (0 << 6),	/* 正常模式 */
	TM7705_MD_CAL_SELF		= (1 << 6),	/* 自校准模式 */
	TM7705_MD_CAL_ZERO		= (2 << 6),	/* 校准0刻度模式 */
	TM7705_MD_CAL_FULL		= (3 << 6),	/* 校准满刻度模式 */

	TM7705_GAIN_1			= (0 << 3),	/* 增益 */
	TM7705_GAIN_2			= (1 << 3),	/* 增益 */
	TM7705_GAIN_4			= (2 << 3),	/* 增益 */
	TM7705_GAIN_8			= (3 << 3),	/* 增益 */
	TM7705_GAIN_16			= (4 << 3),	/* 增益 */
	TM7705_GAIN_32			= (5 << 3),	/* 增益 */
	TM7705_GAIN_64			= (6 << 3),	/* 增益 */
	TM7705_GAIN_128		    = (7 << 3),	/* 增益 */

	/* 无论双极性还是单极性都不改变任何输入信号的状态,它只改变输出数据的代码和转换函数上的校准点 */
	TM7705_BIPOLAR			= (0 << 2),	/* 双极性输入 */
	TM7705_UNIPOLAR		    = (1 << 2),	/* 单极性输入 */

	TM7705_BUF_NO			= (0 << 1),	/* 输入无缓冲(内部缓冲器不启用) */
	TM7705_BUF_EN			= (1 << 1),	/* 输入有缓冲 (启用内部缓冲器) */

	TM7705_FSYNC_0			= 0,    // 模拟调制器和滤波器正常处理数据
	TM7705_FSYNC_1			= 1		// 模拟调制器和滤波器不启用
};



/* 时钟寄存器bit定义 */
enum
{
	TM7705_CLKDIS_0	        = (0 << 4),		/* 时钟输出使能 (当外接晶振时,必须使能才能振荡) */
	TM7705_CLKDIS_1	        = (1 << 4),		/* 时钟禁止 (当外部提供时钟时,设置该位可以禁止MCK_OUT引脚输出时钟以省电 */

    TM7705_CLKDIV_0         = (0 << 3),     // 不分频
    TM7705_CLKDIV_1         = (1 << 3),     // 2分频,外部晶振为4.9152Mhz时,应2分频

    TM7705_CLK_0            = (0 << 2),     // 主时钟=1Mhz并且CLKDIV=0,主时钟=2Mhz并且CLKDIV=1
    TM7705_CLK_1            = (1 << 2),     // 主时钟=2.4576Mhz并且CLKDIV=0, 主时钟=4.9152Mhz并且CLKDIV=1

    // 注意输出更新率与clk位有关
    // 当TM7705_CLK_0时,输出更新率只能为20,25,100,200
    TM7705_UPDATE_20        = (0),
    TM7705_UPDATE_25        = (1),
    TM7705_UPDATE_100       = (2),
    TM7705_UPDATE_200       = (3),
    // 当TM7705_CLK_1时,输出更新率只能为50,60,250,500
    TM7705_UPDATE_50        = (0),
    TM7705_UPDATE_60        = (1),
    TM7705_UPDATE_250       = (2),
    TM7705_UPDATE_500       = (3)
};



#define TM7705_CHANNEL_NUM              (2)     // tm7705通道个数
#define TM7705_DRDY_PIN                 (87)    // GPIO87/I2S_DI   tm7705的引脚DRDY 
#define TM7705_RESET_PIN                (89)    // GPIO89/I2S_LRCK  tm7705的引脚RESET

#define TM7705_AD_MAX                   ((0x1 << 10) - 1)   // ad的最大值,只用了十位的精度
#define TM7705_IS_VALID_AD(ad)          ((TM7705_AD_MAX >= (ad)) && (0 <= (ad)))

// 返回值
#define TM7705_RET_TIMEOUT              (-1)    // 超时
#define TM7705_RET_OK                   (0)     // 正常返回
#define TM7705_RET_OTHER_ERR            (1)     // 其它错误

#define LS1C_SPI0_BUS_NAME              ("spi0")
#define TM7705_SPI_DEVICE_NAME          ("tm7705")


// 以下根据ntc热敏电阻参数用脚本生成的adc值与温度一一对应的表格
// 左边为adc值,右边为温度(单位:摄氏度)
// 详细请参考源码目录中的脚本"createTemperatureLookup.py"
// python createTemperatureLookup.py
// Thermistor lookup table for RepRap Temperature Sensor Boards (http://make.rrrf.org/ts)
// Made with createTemperatureLookup.py (http://svn.reprap.org/trunk/reprap/firmware/Arduino/utilities/createTemperatureLookup.py)
// ./createTemperatureLookup.py --r0=100000 --t0=25 --r1=0 --r2=4700 --beta=3950 --max-adc=1023
// r0: 100000
// t0: 25
// r1: 0
// r2: 4700
// beta: 3950
// max adc: 1023
#define TM7705_NTC_NUMTEMPS 40
const short tm7705_ntc_temptable[TM7705_NTC_NUMTEMPS][2] = {
   {1, 938},
   {27, 326},
   {53, 269},
   {79, 239},
   {105, 219},
   {131, 204},
   {157, 192},
   {183, 182},
   {209, 174},
   {235, 166},
   {261, 160},
   {287, 153},
   {313, 148},
   {339, 143},
   {365, 138},
   {391, 133},
   {417, 129},
   {443, 125},
   {469, 120},
   {495, 116},
   {521, 113},
   {547, 109},
   {573, 105},
   {599, 101},
   {625, 98},
   {651, 94},
   {677, 90},
   {703, 86},
   {729, 82},
   {755, 78},
   {781, 74},
   {807, 70},
   {833, 65},
   {859, 60},
   {885, 54},
   {911, 48},
   {937, 41},
   {963, 31},
   {989, 18},
   {1015, -8}
};



struct rt_spi_device tm7705_spi_device;


/*
 * 通过RESET引脚复位tm7705
 */
void tm7705_reset(void)
{
    gpio_set(TM7705_RESET_PIN, gpio_level_high);
    delay_ms(1);
    gpio_set(TM7705_RESET_PIN, gpio_level_low);
    delay_ms(2);
    gpio_set(TM7705_RESET_PIN, gpio_level_high);
    delay_ms(1);

    return ;
}


/*
 * 同步SPI接口时序
 */
void tm7705_sync_spi(void)
{
    const unsigned char send_buf[4] = {0xFF};
    
    // 在至少32个串行时钟内向TM7705的DIN脚写入逻辑'1'
    rt_spi_send(&tm7705_spi_device, send_buf, 4);

    return ;
}


/*
 * 等待内部操作完成, 时间较长,约180ms
 * @ret 成功 或者 超时
 */
int tm7705_wait_DRDY(void)
{
    int i = 0;
    int time_cnt = 500;

    for (i=0; i<time_cnt; i++)
    {
        if (gpio_level_low == gpio_get(TM7705_DRDY_PIN))
        {
            break;
        }
        delay_ms(1);
    }

    if (i >= time_cnt)
    {
        return TM7705_RET_TIMEOUT;
    }

    return TM7705_RET_OK;
}


/*
 * 自校准
 * @channel 通道
 */
void tm7705_calib_self(unsigned char channel)
{
    unsigned char send_buf[2] = {0};

    send_buf[0] = TM7705_REG_SETUP | TM7705_WRITE | channel;
    send_buf[1] = TM7705_MD_CAL_SELF | TM7705_GAIN_1 | TM7705_UNIPOLAR | TM7705_BUF_EN | TM7705_FSYNC_0;
    rt_spi_send(&tm7705_spi_device, send_buf, 2);

    // 等待内部操作完成, 时间较长,约180ms
    tm7705_wait_DRDY();

    delay_ms(50);

    return ;
}



/*
 * 配置tm7705的指定通道
 * @channel 通道
 */
void tm7705_config_channel(unsigned char channel)
{
    unsigned char send_buf[2] = {0};

    send_buf[0] = TM7705_REG_CLOCK | TM7705_WRITE | channel;
    send_buf[1] = TM7705_CLKDIS_0 | TM7705_CLKDIV_1 | TM7705_CLK_1 | TM7705_UPDATE_50;
    rt_spi_send(&tm7705_spi_device, send_buf, 2);

    // 自校准
    tm7705_calib_self(channel);

    return ;
}


/*
 * 复位tm7705并重新配置
 */
void tm7705_reset_and_reconfig(void)
{
    // 通过RESET引脚复位tm7705
    tm7705_reset();

    // 同步SPI接口时序
    delay_ms(5);
    tm7705_sync_spi();
    delay_ms(5);

    // 配置tm7705的指定通道
    tm7705_config_channel(TM7705_CH_1);

    return ;
}


/*
 * tm7705初始化
 */
void tm7705_init(void)
{
    static struct ls1c_spi_cs spi_cs;
    struct rt_spi_configuration cfg;
    rt_err_t result;
    
    // 初始化DRDY和RESET引脚
    gpio_init(TM7705_DRDY_PIN, gpio_mode_input);
    gpio_init(TM7705_RESET_PIN, gpio_mode_output);
    gpio_set(TM7705_RESET_PIN, gpio_level_high);

    // 注册SPI总线
    ls1c_spi_bus_register(LS1C_SPI_1, LS1C_SPI0_BUS_NAME);
//    ls1c_spi_bus_register(LS1C_SPI_0, LS1C_SPI0_BUS_NAME);

    // 把从设备(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 ;
    }

    // 配置SPI
    cfg.mode    = RT_SPI_MODE_3;
    cfg.max_hz  = 100*1000;
    rt_spi_configure(&tm7705_spi_device, &cfg);

    // 复用
    // 下面我选择了几路spi进行测试,都通过了的,有需要的可以把注释取消,进行测试
/*
    // SPI1 CS0
    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_0_GPIO, PIN_REMAP_THIRD);   // cs0
*/

    // 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


    // 复位tm7705并重新配置
    tm7705_reset_and_reconfig();

    return ;
}


/*
 * 读取指定通道的ad值
 * @channel 通道
 * @adc_p 读取的AD值
 * @ret 成功 或 失败
 */
int tm7705_read_channel(int channel, unsigned short *adc_p)
{
    int ret = TM7705_RET_TIMEOUT;
    unsigned char send_buf[1] = {0};
    unsigned char recv_buf[2] = {0};
    unsigned short ad = 0;

    // 等待转换完成
    ret = tm7705_wait_DRDY();
    if (TM7705_RET_OK != ret)
    {
        rt_kprintf("[%s] tm7705 timeout!\r\n", __FUNCTION__);
        return ret;
    }

    // 读
    send_buf[0] = TM7705_REG_DATA | TM7705_READ | channel;
    rt_spi_send_then_recv(&tm7705_spi_device, send_buf, 1, recv_buf, 2);
    ad = (recv_buf[0] << 8) + recv_buf[1];
    if (0xfff == ad)
    {
        rt_kprintf("[%s] ad=0xfff\r\n", __FUNCTION__);
        return TM7705_RET_OTHER_ERR;
    }

    *adc_p = ad;

    return TM7705_RET_OK;
}



/*
 * 获取tm7705输出的AD值
 * @channel 通道
 * @adc_p 读取的AD值
 * @ret 成功 或者 失败
 */
int tm7705_get_ad(int channel, unsigned short *adc_p)
{
    int i = 0;
    int ret = TM7705_RET_TIMEOUT;
    unsigned short ad = 0;

    // 连续读2次
    // 第一次读取的值为上一次采集的结果,第二次读取的才是当前的结果     *
    for (i=0; i<2; i++)
    {
        ret = tm7705_read_channel(channel, &ad);
        if (TM7705_RET_OK != ret)
        {
            // 失败,则重启tm7705并重新配置
            tm7705_reset_and_reconfig();
            rt_kprintf("[%s] tm7705 reset and reconfig!\r\n", __FUNCTION__);
            return ret;
        }

        // ls1c速度相对tm7705太快,延时一下避免在一次读完后DRDY还未及时改变状态
        delay_ms(1);
    }
//    spi_print_all_regs_info(&tm7705_spi_info);

    rt_kprintf("[%s] ad=0x%x\r\n", __FUNCTION__, ad);

    ad = ad >> 6;       // 只需要10位的精度
    *adc_p = ad;

    return TM7705_RET_OK;
}


/*
 * 根据ad值计算温度值
 * @ad 从tm7705读取的AD值
 * @ret 温度值
 *
 * ntc热敏电阻的阻值温度曲线被分为n段,每段可以近似为直线
 * 所以温度值的计算就转变为查表再计算
 */
float tm7705_calc_temp_from_ad(unsigned short ad)
{
    float temp_f;
    int i = 0;
    
    // 判断ad值是否在量程范围内
    if (!(TM7705_IS_VALID_AD(ad)))
    {
        return 0;
    }

    // 判断是否在表格所表示的范围内
    if (ad < tm7705_ntc_temptable[0][0])                        // 小于表格的最小adc
    {
        return tm7705_ntc_temptable[0][1];                      // 取最小值
    }
    if (ad > tm7705_ntc_temptable[TM7705_NTC_NUMTEMPS-1][0])    // 大于表格的最大adc
    {
        return tm7705_ntc_temptable[TM7705_NTC_NUMTEMPS-1][1];  // 取最大值
    }

    // 查表
    for (i=1; i<TM7705_NTC_NUMTEMPS; i++)
    {
        if (ad < tm7705_ntc_temptable[i][0])
        {
//            rt_kprintf("[%s] ad=%d\r\n", __FUNCTION__, ad);

            // t = t0 + (adc - adc0) * k
            temp_f = tm7705_ntc_temptable[i-1][1] +               // t0
                   (ad - tm7705_ntc_temptable[i-1][0]) *        // adc - adc0
                   ((float)(tm7705_ntc_temptable[i][1] - tm7705_ntc_temptable[i-1][1]) 
                    / (float)(tm7705_ntc_temptable[i][0] - tm7705_ntc_temptable[i-1][0]));
            rt_kprintf("[%s] temp_f=%d\r\n", __FUNCTION__, (int)temp_f);
            
            return temp_f;

        }
    }

    return 0;
}


/*
 * 获取温度值
 * @ret 温度值
 */
float tm7705_get_temperature(void)
{
    int ret = TM7705_RET_TIMEOUT;
    float temp_f = 0;
    unsigned short ad = 0;

    // 获取AD值
    ret = tm7705_get_ad(TM7705_CH_1, &ad);
    if (TM7705_RET_OK != ret)
    {
        rt_kprintf("[%s] tm7705_get_ad() fail, ret=%d\r\n", __FUNCTION__, ret);
        return ret;
    }

    // 计算温度值
    temp_f = tm7705_calc_temp_from_ad(ad);

    return temp_f;
}





把RTT统一的标准的SPI接口移植到龙芯1C上

这一部分主要讨论一下移植的具体细节和思路。整个移植最核心的也就是两个函数configure()和xfer(),这两个函数为struct rt_spi_ops的成员,也是RTT的SPI接口的核心所在。

在龙芯1c库的SPI接口中,已经实现了配置和收发功能,只是配置功能被拆分为多个函数而已。比如设置时钟调用ls1c_spi_set_clock(),设置spi模式调用ls1c_spi_set_mode()等。

源码行数不多,相信大家一看就能明白,这里直接贴源码了

drv_spi.c

bsp\ls1cdev\drivers\drv_spi.c

/*
 * File      : drv_spi.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-11-02     勤为本       first version
 */

#include <rtthread.h>
#include <drivers/spi.h>
#include "drv_spi.h"


//#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINTF(...)           rt_kprintf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...)
#endif


static rt_err_t configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration);
static rt_uint32_t xfer(struct rt_spi_device *device, struct rt_spi_message *message);


static struct rt_spi_ops ls1c_spi_ops = 
{
    .configure  = configure,
    .xfer       = xfer
};


static rt_err_t configure(struct rt_spi_device *device, 
                          struct rt_spi_configuration *configuration)
{
    struct rt_spi_bus *spi_bus = NULL;
    struct ls1c_spi *ls1c_spi = NULL;
    unsigned char SPIx = 0;
    void *spi_base = NULL;
    unsigned char cpol = 0;
    unsigned char cpha = 0;
    unsigned char val = 0;

    RT_ASSERT(NULL != device);
    RT_ASSERT(NULL != configuration);

    spi_bus = device->bus;
    ls1c_spi = (struct ls1c_spi *)spi_bus->parent.user_data;
    SPIx = ls1c_spi->SPIx;
    spi_base = ls1c_spi_get_base(SPIx);

    {
        // 使能SPI控制器,master模式,关闭中断
        reg_write_8(0x53, spi_base + LS1C_SPI_SPCR_OFFSET);

        // 清空状态寄存器
        reg_write_8(0xc0, spi_base + LS1C_SPI_SPSR_OFFSET);

        // 1字节产生中断,采样(读)与发送(写)时机同时
        reg_write_8(0x03, spi_base + LS1C_SPI_SPER_OFFSET);

        // 关闭SPI flash
        val = reg_read_8(spi_base + LS1C_SPI_SFC_PARAM_OFFSET);
        val &= 0xfe;
        reg_write_8(val, spi_base + LS1C_SPI_SFC_PARAM_OFFSET);

        // spi flash时序控制寄存器
        reg_write_8(0x05, spi_base + LS1C_SPI_SFC_TIMING_OFFSET);
    }
    
    // baudrate
    ls1c_spi_set_clock(spi_base, configuration->max_hz);

    // 设置通信模式(时钟极性和相位)
    if (configuration->mode & RT_SPI_CPOL)      // cpol
    {
        cpol = SPI_CPOL_1;
    }
    else
    {
        cpol = SPI_CPOL_0;
    }
    if (configuration->mode & RT_SPI_CPHA)      // cpha
    {
        cpha = SPI_CPHA_1;
    }
    else
    {
        cpha = SPI_CPHA_0;
    }
    ls1c_spi_set_mode(spi_base, cpol, cpha);

    DEBUG_PRINTF("ls1c spi%d configuration\n", SPIx);

    return RT_EOK;
}


static rt_uint32_t xfer(struct rt_spi_device *device, 
                        struct rt_spi_message *message)
{
    struct rt_spi_bus *spi_bus = NULL;
    struct ls1c_spi *ls1c_spi = NULL;
    void *spi_base = NULL;
    unsigned char SPIx = 0;
    struct ls1c_spi_cs *ls1c_spi_cs = NULL;
    unsigned char cs = 0;
    rt_uint32_t size = 0;
    const rt_uint8_t *send_ptr = NULL;
    rt_uint8_t *recv_ptr = NULL;
    rt_uint8_t data = 0;

    RT_ASSERT(NULL != device);
    RT_ASSERT(NULL != message);

    spi_bus = device->bus;
    ls1c_spi = spi_bus->parent.user_data;
    SPIx = ls1c_spi->SPIx;
    spi_base = ls1c_spi_get_base(SPIx);
    ls1c_spi_cs = device->parent.user_data;
    cs = ls1c_spi_cs->cs;
    size = message->length;

    DEBUG_PRINTF("[%s] SPIx=%d, cs=%d\n", __FUNCTION__, SPIx, cs);

    // take cs
    if (message->cs_take)
    {
        ls1c_spi_set_cs(spi_base, cs, 0);
    }

    // 收发数据
    send_ptr = message->send_buf;
    recv_ptr = message->recv_buf;
    while (size--)
    {
        data = 0xFF;
        if (NULL != send_ptr)
        {
            data = *send_ptr++;
        }

        if (NULL != recv_ptr)
        {
            *recv_ptr++ = ls1c_spi_txrx_byte(spi_base, data);
        }
        else
        {
            ls1c_spi_txrx_byte(spi_base, data);
        }
    }

    // release cs
    if (message->cs_release)
    {
        ls1c_spi_set_cs(spi_base, cs, 1);
    }

    return message->length;
}


#ifdef RT_USING_SPI0
struct ls1c_spi ls1c_spi0 = 
{
    .SPIx = LS1C_SPI_0,
};

static struct rt_spi_bus spi0_bus;
#endif


#ifdef RT_USING_SPI1
struct ls1c_spi ls1c_spi1 = 
{
    .SPIx = LS1C_SPI_1,
};

static struct rt_spi_bus spi1_bus;
#endif


/*
 * 初始化并注册龙芯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)
{
    struct rt_spi_bus *spi_bus = NULL;

#ifdef RT_USING_SPI0
    if (LS1C_SPI_0 == SPI)
    {
        spi_bus = &spi0_bus;
        spi_bus->parent.user_data = &ls1c_spi0;
    }
#endif

#ifdef RT_USING_SPI1
    if (LS1C_SPI_1 == SPI)
    {
        spi_bus = &spi1_bus;
        spi_bus->parent.user_data = &ls1c_spi1;
    }
#endif

    return rt_spi_bus_register(spi_bus, spi_bus_name, &ls1c_spi_ops);
}



drv_spi.h

bsp\ls1cdev\drivers\drv_spi.h

/*
 * File      : drv_spi.h
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-11-02     勤为本       first version
 */

#ifndef LS1C_DRV_SPI_H
#define LS1C_DRV_SPI_H


#include "../libraries/ls1c_spi.h"


struct ls1c_spi
{
    unsigned char SPIx;     // LS1C_SPI_0 or LS1C_SPI_1
};


struct ls1c_spi_cs
{
    unsigned char cs;       // LS1C_SPI_CS_0, LS1C_SPI_CS_1, LS1C_SPI_CS_2 or LS1C_SPI_CS_3
};



/*
 * 初始化并注册龙芯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);


#endif


ls1c_spi.c

bsp\ls1cdev\libraries\ls1c_spi.c

/*
 * File      : ls1c_spi.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-10-23     勤为本       first version
 */

// 硬件spi接口源文件


#include <string.h>
#include "ls1c_public.h"
#include "ls1c_regs.h"
#include "ls1c_clock.h"
#include "ls1c_spi.h"




/*
 * 获取指定SPI模块的基地址
 * @SPIx SPI模块的编号
 */
inline void *ls1c_spi_get_base(unsigned char SPIx)
{
    void *base = NULL;

    switch (SPIx)
    {
        case LS1C_SPI_0:
            base = (void *)LS1C_SPI0_BASE;
            break;

        case LS1C_SPI_1:
            base = (void *)LS1C_SPI1_BASE;
            break;

        default:
            base = NULL;
            break;
    }

    return base;
}


/*
 * 打印指定SPI模块的所有寄存器的值
 * @spi_base 基地址
 */
void ls1c_spi_print_all_regs_info(void *spi_base)
{
    rt_kprintf("[%s] SPCR=0x%x, SPSR=0x%x, SPER=0x%x, SFC_PARAM=0x%x, SFC_SOFTCS=0x%x, SFC_TIMING=0x%x\r\n",
              __FUNCTION__, 
              reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET),
              reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET),
              reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET),
              reg_read_8(spi_base + LS1C_SPI_SFC_PARAM_OFFSET),
              reg_read_8(spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET),
              reg_read_8(spi_base + LS1C_SPI_SFC_TIMING_OFFSET));

    return ;
}


/*
 * 根据SPI时钟频率计算分频系数
 * @max_speed_hz SPI最大通信速度
 * @ret 分频系数
 */
unsigned int ls1c_spi_get_div(unsigned int max_speed_hz)
{
    unsigned long clk = 0;
    unsigned int div = 0;
    unsigned int div_tmp = 0;
    unsigned int bit = 0;

    clk = clk_get_apb_rate();
    div = DIV_ROUND_UP(clk, max_speed_hz);

    if (div < 2)
        div = 2;

    if (div > 4096)
        div = 4096;

    bit = ls1c_fls(div) - 1;
    switch (1 << bit)
    {
        case 16:
            div_tmp = 2;
            if (div > (1 << bit))
            {
                div_tmp++;
            }
            break;

        case 32:
            div_tmp = 3;
            if (div > (1 << bit))
            {
                div_tmp += 2;
            }
            break;

        case 8:
            div_tmp = 4;
            if (div > (1 << bit))
            {
                div_tmp -= 2;
            }
            break;

        default:
            div_tmp = bit - 1;
            if (div > (1 << bit))
            {
                div_tmp++;
            }
            break;
    }
/*    
    rt_kprintf("[%s] clk=%ld, max_speed_hz=%d, div_tmp=%d, bit=%d\r\n", 
              __FUNCTION__, clk, max_speed_hz, div_tmp, bit);
*/
    return div_tmp;
}


/*
 * 设置时钟
 * @spi_base 基地址
 * @max_hz 最大频率,单位hz
 */
void ls1c_spi_set_clock(void *spi_base, unsigned long max_hz)
{
    unsigned int div = 0;
    unsigned char val = 0;

    // 获取分频系数
    div = ls1c_spi_get_div(max_hz);

    // 设置spr
    val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
    val &= (~LS1C_SPI_SPCR_SPR_MASK);                       // spr清零
    val |= (div & LS1C_SPI_SPCR_SPR_MASK);                  // 设置新的spr
    reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);

    // 设置spre
    val = reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET);
    val &= (~LS1C_SPI_SPER_SPRE_MASK);                      // spre清零
    val |= ((div >> 2) & LS1C_SPI_SPER_SPRE_MASK);        // 设置新的spre
    reg_write_8(val, spi_base + LS1C_SPI_SPER_OFFSET);

    return ;
}


/*
 * 设置通信模式(时钟极性和相位)
 * @spi_base 基地址
 * @cpol 时钟极性
 * @cpha 时钟相位
 */
void ls1c_spi_set_mode(void *spi_base, unsigned char cpol, unsigned char cpha)
{
    unsigned char val = 0;

    val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
    
    // 设置时钟极性--cpol
    val &= (~LS1C_SPI_SPCR_CPOL_MASK);                  // cpol清0
    val |= (cpol << LS1C_SPI_SPCR_CPOL_BIT);            // 写入新的cpol
    
    // 设置时钟相位--cpha
    val &= (~LS1C_SPI_SPCR_CPHA_MASK);                  // cpha清0
    val |= (cpha << LS1C_SPI_SPCR_CPHA_BIT);            // 写入新的cpha
    
    reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);

    return ;
}


/*
 * 设置指定片选为指定状态
 * @spi_base 基地址
 * @cs 片选
 * @new_status 片选引脚的新状态,取值为0或1,即高电平或低电平
 */
void ls1c_spi_set_cs(void *spi_base, unsigned char cs, int new_status)
{
    unsigned char val = 0;

    val = 0xf0 | (0x01 << cs);          // 全部csn=1,指定的csen=1
    if (new_status)         // cs = 1
    {
        val |= (0x10 << cs);            // 指定csn=1
    }
    else                    // cs = 0
    {
        val &= ~(0x10 << cs);           // 指定csn=0
    }
    reg_write_8(val, spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET);

    return ;
}


/*
 * 等待收发完成
 * @spi_base 基地址
 */
inline void ls1c_spi_wait_txrx_done(void *spi_base)
{
    int timeout = LS1C_SPI_TX_TIMEOUT;

    while (timeout--)
    {
        if (LS1C_SPI_SPSR_SPIF_MASK & reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET))
            break;
    }
    
    return ;
}


/*
 * 清中断和标志位
 * @spi_base 基地址
 */
inline void ls1c_spi_clear(void *spi_base)
{
    unsigned char val = 0;

    // 清中断
    val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
    val |= LS1C_SPI_SPSR_SPIF_MASK;
    reg_write_8(val, spi_base + LS1C_SPI_SPSR_OFFSET);

    // 清溢出标志位(Write-Collision Clear)
    val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
    if (LS1C_SPI_SPSR_WCOL_MASK & val)
    {
        rt_kprintf("[%s] clear register SPSR's wcol!\r\n");       // 手册和linux源码中不一样,加个打印看看
        reg_write_8(val & ~LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET);   // 写0,linux源码中是写0
//        reg_write_8(val | LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET);  // 写1,按照1c手册,应该写1
    }

    return ;
}



/*
 * 通过指定SPI发送接收一个字节
 * 注意,在多任务的系统中,此函数需要互斥。
 * 即保证在和某个从设备收发某个字节的过程中,不能被切换到其它任务同时与另外的在同一个SPI总线上的从设备通信
 * 因为龙芯1c的每路SPI上可能接有不同的从设备,通信频率、模式等可能不同
 * @spi_base 基地址
 * @tx_ch 待发送的数据
 * @ret 收到的数据
 */
unsigned char ls1c_spi_txrx_byte(void *spi_base, unsigned char tx_ch)
{
    unsigned char rx_ch = 0;

    // 收发数据
    reg_write_8(tx_ch, spi_base + LS1C_SPI_TxFIFO_OFFSET);      // 开始发送
    ls1c_spi_wait_txrx_done(spi_base);                          // 等待收发完成
    rx_ch = reg_read_8(spi_base + LS1C_SPI_RxFIFO_OFFSET);      // 读取收到的数据
    ls1c_spi_clear(spi_base);                                   // 清中断和标志位

    return rx_ch;
}




ls1c_spi.h

bsp\ls1cdev\libraries\ls1c_spi.h

/*
 * File      : ls1c_spi.h
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-10-23     勤为本       first version
 */

// 硬件spi接口的头文件

#ifndef __OPENLOONGSON_SPI_H
#define __OPENLOONGSON_SPI_H


// SPI模块编号
#define LS1C_SPI_0                      (0)
#define LS1C_SPI_1                      (1)

// 片选
#define LS1C_SPI_CS_0                   (0)
#define LS1C_SPI_CS_1                   (1)
#define LS1C_SPI_CS_2                   (2)
#define LS1C_SPI_CS_3                   (3)

// 时钟极性和相位
#define SPI_CPOL_1                      (1)
#define SPI_CPOL_0                      (0)
#define SPI_CPHA_1                      (1)
#define SPI_CPHA_0                      (0)


// 寄存器偏移
#define LS1C_SPI_SPCR_OFFSET                (0)     // 控制寄存器
#define LS1C_SPI_SPSR_OFFSET                (1)     // 状态寄存器
#define LS1C_SPI_TxFIFO_OFFSET              (2)     // 发送的数据寄存器,与接收数据寄存器的偏移相同
#define LS1C_SPI_RxFIFO_OFFSET              (2)     // 接收的数据寄存器,与发送数据寄存器的偏移相同
#define LS1C_SPI_SPER_OFFSET                (3)     // 外部寄存器
#define LS1C_SPI_SFC_PARAM_OFFSET           (4)     // 参数控制寄存器
#define LS1C_SPI_SFC_SOFTCS_OFFSET          (5)     // 片选控制寄存器
#define LS1C_SPI_SFC_TIMING_OFFSET          (6)     // 时序控制寄存器

// 寄存器SPCR中的位域
#define LS1C_SPI_SPCR_SPIE_BIT              (7)
#define LS1C_SPI_SPCR_SPIE_MASK             (0x01 << LS1C_SPI_SPCR_SPIE_BIT)
#define LS1C_SPI_SPCR_SPE_BIT               (6)
#define LS1C_SPI_SPCR_SPE_MASK              (0x01 << LS1C_SPI_SPCR_SPE_BIT)
#define LS1C_SPI_SPCR_CPOL_BIT              (3)
#define LS1C_SPI_SPCR_CPOL_MASK             (0x01 << LS1C_SPI_SPCR_CPOL_BIT)
#define LS1C_SPI_SPCR_CPHA_BIT              (2)
#define LS1C_SPI_SPCR_CPHA_MASK             (0x01 << LS1C_SPI_SPCR_CPHA_BIT)
#define LS1C_SPI_SPCR_SPR_BIT               (0)
#define LS1C_SPI_SPCR_SPR_MASK              (0x03 << LS1C_SPI_SPCR_SPR_BIT)

// 寄存器SPSR中的位域
#define LS1C_SPI_SPSR_SPIF_BIT              (7)
#define LS1C_SPI_SPSR_SPIF_MASK             (0x01 << LS1C_SPI_SPSR_SPIF_BIT)
#define LS1C_SPI_SPSR_WCOL_BIT              (6)
#define LS1C_SPI_SPSR_WCOL_MASK             (0x01 << LS1C_SPI_SPSR_WCOL_BIT)

// 寄存器SPER中的位域
#define LS1C_SPI_SPER_SPRE_BIT              (0)
#define LS1C_SPI_SPER_SPRE_MASK             (0x3 << LS1C_SPI_SPER_SPRE_BIT)

// 寄存器SFC_SOFTCS的位域
#define LS1C_SPI_SFC_SOFTCS_CSN_BIT         (4)
#define LS1C_SPI_SFC_SOFTCS_CSN_MASK        (0x0f << LS1C_SPI_SFC_SOFTCS_CSN_BIT)
#define LS1C_SPI_SFC_SOFTCS_CSEN_BIT        (0)
#define LS1C_SPI_SFC_SOFTCS_CSEN_MASK       (0x0f << LS1C_SPI_SFC_SOFTCS_CSEN_BIT)

// 发送超时的门限值
#define LS1C_SPI_TX_TIMEOUT                 (20000)



/*
 * 获取指定SPI模块的基地址
 * @SPIx SPI模块的编号
 */
inline void *ls1c_spi_get_base(unsigned char SPIx);


/*
 * 设置时钟
 * @spi_base 基地址
 * @max_hz 最大频率,单位hz
 */
void ls1c_spi_set_clock(void *spi_base, unsigned long max_hz);


/*
 * 设置通信模式(时钟极性和相位)
 * @spi_base 基地址
 * @cpol 时钟极性
 * @cpha 时钟相位
 */
void ls1c_spi_set_mode(void *spi_base, unsigned char cpol, unsigned char cpha);


/*
 * 设置指定片选为指定状态
 * @spi_base 基地址
 * @cs 片选
 * @new_status 片选引脚的新状态,取值为0或1,即高电平或低电平
 */
void ls1c_spi_set_cs(void *spi_base, unsigned char cs, int new_status);


/*
 * 通过指定SPI发送接收一个字节
 * 注意,在多任务的系统中,此函数需要互斥。
 * 即保证在和某个从设备收发某个字节的过程中,不能被切换到其它任务同时与另外的在同一个SPI总线上的从设备通信
 * 因为龙芯1c的每路SPI上可能接有不同的从设备,通信频率、模式等可能不同
 * @spi_base 基地址
 * @tx_ch 待发送的数据
 * @ret 收到的数据
 */
unsigned char ls1c_spi_txrx_byte(void *spi_base, unsigned char tx_ch);


/*
 * 打印指定SPI模块的所有寄存器的值
 * @spi_base 基地址
 */
void ls1c_spi_print_all_regs_info(void *spi_base);




#endif


感谢阅读!


猜你喜欢

转载自blog.csdn.net/caogos/article/details/78497533