SPI message基础
Contents
.Part 1 - Linux SPI系统概览
.Part 2 - SPI message 基础
.Part 3 - 异步写
LinuxSPI 通信基础
在SPI总线上是通过封装一系列的spi_transfer到一个spi_message中,然后将spi_message提交到SPI子系统去。
下面是spi_transfer结构:
struct spi_transfer { const void*tx_buf; //驱动提供的发送缓冲区dma, void *rx_buf; //接收缓冲区 unsigned len; dma_addr_ttx_dma; //发送dma,controller使用 dma_addr_t rx_dma; //接收dma unsigned cs_change:1; //片选位 u8 bits_per_word; //每字长度 u16 delay_usecs; //延迟 u32 speed_hz; //速度 struct list_headtransfer_list; //transfer 链表 };
tx_dma和rx_dma可通过dma_map_single()来初始化或者让controller来填充这些域。如果你手动的映射这些dma,就必须将spi_message.is_dma_mappped置为1,来告知controller你已经做好映射了。既然手动的做映射也必须手的unmapped掉。并不是所有的transfer都会使用dma,比如,有的控制器在transfer小于8字节时会使用PIO(ProgrammingI/O,cpu执行I/O端口指令进行读写,大数据交换是会提高cpu占有率)模式。
cs_change置1会引起CS线在spi_message序列中transfers间变高。通常,CS会在第一个transfer前变低并一直保持直至spi_message序列中最后一个transfer到来(这是本次spi_message中最后一个)。这种反转在和某些设备通信时会非常有用。
bit_per_word对于单个spi_transfer可用来覆盖spi_device.bits_per_word域。清零不做覆盖。
delay_usecs会在spi_message序列中一个transfer之后,CS状态改变前或者下一个transfer修正延迟。
speed_hz允许这个transfer可以有高于spi_device.max_speed_hz的速率。清零不作变化。
transfer_listspi_message序列中用于维护transfer的链表。
在spi_transfer中时常更改的域也许只有len,tx_buf和rx_buf。剩下的当以0来初始化。
单个spi_transfer可表示一次读,一次写或者是一次读写。在SPIcontroller驱动下,所有操作常是全双工的。向spi_transfer中rx_buf传递一个NULL,这就是一次只写操作,会丢弃MISO线上的数据。同样向tx_buf传递一个NULL,这就是一次只读操作了。spi_transfer中len域代表(已经多少字节数据流过总线了)howmany bytes to clock the bus。
spi_message结构:
struct spi_message { struct list_head transfers; struct spi_device *spi; unsigned is_dma_mapped:1; void (*complete)(void*context); void *context; unsigned actual_length; int status; struct list_head queue; void *state; };
is_dma_mappedspi_transfer中tx_dma和rx_dma是否已经mapped。
complete回调函数
context 提供给complete的可选参数
actual_lengthspi_message已经传输了的字节数
status 出错与否,错误时返回errorcode
queue 和state 供controller驱动内部使用
在每次使用spi_message可以使用函数:
void spi_message_init(structspi_message *m);
来初始化。
向spi_message添加transfers可以使用spi_message_add_tail()函数:
void spi_message_add_tail(structspi_transfer *t, struct spi_message *m);
一旦你准备好了spi_message,就可以使用spi_async()来向SPI系统提交了:
int spi_async(struct spi_device *spi,struct spi_message *message);
因为是异步的,一提交就立马返回了,这也就是说需要同步机制(complete就是了)。他确保不会睡眠,可安全的在中断handler或其他不可休眠的代码中调用。稍后会念念他的好的。
使用spi_async()需要注意的是,在complete()未返回前不要轻易访问你一提交的spi_transfer中的buffer。也不能释放SPI系统正在使用的buffer。一旦你的complete返回了,这些buffer就又是你的了。
使用完成回调机制稍显复杂,可以使用SPI系统提供的另一个同步版本:spi_sync():
int spi_sync(struct spi_device *spi,struct spi_message *message);
因为是同步的,spi_sync提交完spi_message后不会立即返回,会一直等待其被处理。一旦返回就可以重新使用buffer了。spi_sync()在drivers/spi/spi.c中实现,其调用了spi_async(),并休眠直至complete返回。