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四种传输模式,第一种与第三种使用广泛。
如下图所示:
SPI内部结构
主机与从机共用同一个时钟,达到同步的目的。
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