FreeRTOS SPI 时序以及模拟SPI时序

SPI(Serial Peripheral Interface),顾名思义就是串行外围设备接口。SPI,是一种高速的全双工同步的通信总线,并且在芯片的管脚上只占用四根线(MISO, MOSI, CLK, CS)可以不用CS片选引脚也是三线式,SPI有时候可以不用MISO, MOSI,中的一个,但CLK的引脚一定需要存在。SPI节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。

Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO(不存在片选引脚);用于 CPU与各种外围器件进行全双工、同步串行通讯

SPI存在四种工作模式分别由POL(极性)与PHA(相位)决定。
POL = 0(CLK时钟的空闲电平为低电平)
POL = 1(CLK时钟的空闲电平为高电平)
PHA = 0(CLK时钟下第一个跳变沿进行数据采集
PHA = 1(CLK时钟下第二个跳边沿进行数据采集
以下是SPI四种传输模式第一种与第三种使用广泛。
如下图所示:
FreeRTOS SPI 时序以及模拟SPI时序_第1张图片SPI内部结构
主机与从机共用同一个时钟,达到同步的目的。
FreeRTOS SPI 时序以及模拟SPI时序_第2张图片SPI编程实现

struct spi_config_data {
    unsigned int id;        /* SPI 控制器 ID */
    unsigned int cs_pin;    /* 指定作为 cs 脚的 GPIO */
    char *name;             /* name 仅作为一个标识 */
    unsigned int clk_rate;  /* 时钟频率(默认为 1*1000*1000) */

    enum spi_cs_valid_level cs_valid_level; /* 有效电平 */

    /* 数据传输的大小端模式选择,具体可选类型为 spi_data_endian 所定义的类型 */
    enum spi_data_endian tx_endian;
    enum spi_data_endian rx_endian;

    /* bits_per_word 代表数据的位宽.
        例如:bits_per_word = 32 时,SPI传输过程中的最小数据单位为32bit. */
    unsigned int bits_per_word;

    /**
     * 极性 spi_pol :
     * 当spi_pol=0,在时钟空闲即无数据传输时,clk电平为低电平
     * 当spi_pol=1,在时钟空闲即无数据传输时,clk电平为高电平
     * 相位 spi_pha :
     * 当spi_pha=0,表示在第一个跳变沿开始采集数据
     * 当spi_pha=1,表示在第二个跳变沿开始采集数据
     */
    unsigned int spi_pol;
    unsigned int spi_pha;

    unsigned int loop_mode; /* 循环模式,可用于测试 */
};

struct spi_device {
    struct spi_config_data config;
    struct list_head spi_drv_node;
};

/*获取时钟,初始化对应的IO口
*/
void soc_spi_init_driver(void);
/*初始化片选引脚,并将设备添加到链表中
*/
void soc_spi_register(struct spi_config_data *config);
/*传输msg
*/
void soc_spi_transfer(struct spi_device *spi, struct spi_message *msg, int count);
/*释放IO口,删除对应的节点
*/
void soc_spi_unregister(struct spi_device *spi);

其实spi传输数据,并不是只传输8,18,32位,这种特殊位的数据。优秀的应该做到2~32可调,并以 8, 16, 32位对齐。少于的都属于无效位。
以模拟gpio spi为例(以下是我个人以为比较有好的代码)
接口框架以上边相同(统一接口)

/*读采集通过传char数据,然后直接读取数据。
*/
unsigned int read_sample(const unsigned char *value, int bytes_per_sample);
/*写采集
*/
void write_sample(const unsigned char *rx_buf, int bytes_per_sample, unsigned int value);
/*读写采集
*/
unsigned int spi_write_read_sample(struct spi_gpio_config_data *config, int bits, unsigned int value);
/*spi传输协议每次之传输一个bit
*/
int spi_write_read_bit(struct spi_gpio_config_data *config, int bit);

static unsigned int spi_write_read_sample(struct spi_gpio_config_data *config, int bits, unsigned int value)
{
	int bit;
    int end = 0;
    int delta = -1;
    int start = bits;
    int i = start;
    unsigned int ret = 0;

    while (1) {
        bit = spi_write_read_bit(config, value & (1 << i));

        if (bit)
            ret |= (1 << i);
        i += delta;
		
        if (i == end)
            break;
    }

    return ret;
}

今天用错一个互斥锁,在注册spi设备时,用互斥锁进行保护设备节点添加到全局链表。不过多个spi所用的互斥锁并不是同一把锁,所以会导致第二把锁不能正常上锁。
解决方法
1、每个spi都定义一个自己的链表
2、用同一把锁进行上锁
3、使用临界区

#打卡第二天#有需要的可以联系博主QQ:1667869702

你可能感兴趣的:(FreeRTOS SPI 时序以及模拟SPI时序)