https://blog.csdn.net/alangaixiaoxiao/article/details/84146885 SPI协议通信
CPOL:Clock Polarity,就是时钟的极性
CPHA:Clock Phase,就是时钟的相位
如果主机在上升沿输出数据到MOSI上,从机就只能在下降沿去采样这个数据了。反之如果一方在下降沿输出数据,那么另一方就必须在上升沿采样这个数据
CPHA=1,就表示数据的输出是在一个时钟周期的第一个沿上,至于这个沿是上升沿还是下降沿,这要是CPOL的值而定,CPOL=1那就是下降沿,反之就是上升沿。那么数据的采样自然就是在第二个沿上了
CPHA=0,就表示数据的采样是在一个时钟周期的第一个沿上,同样它是什么沿由CPOL决定。那么数据的输出自然就在第二个沿上了。
也就是说,CPOL为1 CPHA为1 的时候,
代表着,clk是低电平开始数据采样,高电平空闲,MOSI在第一个时钟周期下降沿的时候输出,MISO在第一个周期的上升沿采集。
https://blog.csdn.net/Guet_Kite/article/details/77918002 嵌入式Linux驱动笔记(十二)------通俗易懂式分析了解spi框架
所以说,spi_sync函数是发起一个同步传输的阻塞API,它会通过master->transfer把spi_message结构挂在spi_master的queue字段下,
然后启动专门为spi传输准备的内核工作线程spi_pump_messages,把该spi_message从队列中移除,
然后调用master->prepare_transfer_hardware回调来让控制器驱动准备必要的硬件资源,
最后 master->transfer_one_message来实际处理message的传输工作,然后等待传输的完成后调用mesg->complete,表明传输完成,
以通知协议驱动程序准备下一帧数据,wait_for_completion不需要再等待了。
https://blog.csdn.net/droidphone/article/details/24663659 Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化
定义一个spi_message结构;
用spi_message_init函数初始化spi_message;
定义一个或数个spi_transfer结构,初始化并为数据准备缓冲区并赋值给 spi_ transfer 相应的字段(tx_buf,rx_buf等);
通过spi_message_init函数把这些spi_transfer挂在spi_message结构下;
如果使用同步方式,调用spi_sync(),如果使用异步方式,调用spi_async();
https://blog.csdn.net/orz415678659/article/details/8617647 spi 子系统(spidev.c)
sys/bus/spi
sys/class/spi_master
先看spi-gpio.c
spi_gpio_driver
spi_gpio_probe
spi_gpio_probe_dt
pdata->sck = of_get_named_gpio(np, "gpio-sck", 0);
pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
pdata->num_chipselect = of_property_read_u32(np, "num-chipselects", &tmp);
spi_gpio_request
spi_alloc_master //这里会绑定spi.c的groups 并分配给 master
device_initialize(&master->dev);
master->dev.class = &spi_master_class; //这里就是
spi_master_set_devdata(master, &master[1]);
master->setup = spi_gpio_setup;
master->cleanup = spi_gpio_cleanup;
spi_gpio->bitbang.master = master; //将master 给到bitbang
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
} else {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
}
spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;
status = spi_bitbang_start(&spi_gpio->bitbang);//注册bitbang
注意spi mode(0-3)
只看其中一个
spi_gpio_spec_txrx_word_mode0
return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits);
最终调用到如下函数,主要作用是通过SPI的时序实现SPI的传输
u32 oldbit = (!(word & (1<<(bits-1)))) << 31;//将传入的word和位数的最高位相与后取反后左移31位
/* clock starts at inactive polarity */
for (word <<= (32 - bits); likely(bits); bits--) {
/* setup MSB (to slave) on trailing edge */
if ((flags & SPI_MASTER_NO_TX) == 0) {// 如果spi支持TX,对每一位进行移位比较
if ((word & (1 << 31)) != oldbit) {// 设置传输
setmosi(spi, word & (1 << 31));
oldbit = word & (1 << 31);
}
}
spidelay(nsecs); /* T(setup) */
setsck(spi, !cpol);
spidelay(nsecs);
/* sample MSB (from slave) on leading edge */
word <<= 1;
if ((flags & SPI_MASTER_NO_RX) == 0)
word |= getmiso(spi);
setsck(spi, cpol);
}
return word;
所以重点看 spi_bitbang_start 函数
spi_bitbang_start
master->transfer_one = spi_bitbang_transfer_one;
bitbang->txrx_bufs = spi_bitbang_bufs;
ret = spi_register_master(spi_master_get(master));
status = of_spi_register_master(master);
cs[i] = of_get_named_gpio(np, "cs-gpios", i);
master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
init_completion(&master->xfer_completion);
init_waitqueue_head(&x->wait);
spi_master_initialize_queue(master);
master->transfer = spi_queued_transfer;
master->transfer_one_message = spi_transfer_one_message;//可自己实现
ret = spi_init_queue(master);
init_kthread_worker(&master->kworker);
master->kworker_task = kthread_run(kthread_worker_fn, &master->kworker, "%s, dev_name(&master->dev));//创建并启动内核线程
init_kthread_work(&master->pump_messages, spi_pump_messages);//注意spi_pump_messages
if (master->rt) {//如果系统要求实时性,就执行如下函数
sched_setscheduler(master->kworker_task, SCHED_FIFO, ¶m);
}
ret = spi_start_queue(master);
queue_kthread_work(&master->kworker, &master->pump_messages);
spi_pump_messages
__spi_pump_messages(master, true);/* 检查是否存在spi meg在队列中,如果是初始化硬件并传输这些数据 */
master->unprepare_transfer_hardware(master)
master->cur_msg =list_first_entry(&master->queue, struct spi_message, queue);//找到并删除这个消息队列
list_del_init(&master->cur_msg->queue);
ret = master->prepare_transfer_hardware(master);
ret = spi_map_msg(master, master->cur_msg);//dma传输的一些映射
ret = master->transfer_one_message(master, master->cur_msg);
spi_transfer_one_message
ret = master->transfer_one(master, msg->spi, xfer);
spi_bitbang_transfer_one
ms = wait_for_completion_timeout(&master->xfer_completion,
msecs_to_jiffies(ms));//注意 调用mesg->complete,使得wait_for_completion不需要再阻塞等待
真的传输动作在如下函数
spi_bitbang_transfer_one
status = bitbang->setup_transfer(spi, transfer);// spi_bitbang_setup_transfer
spi_bitbang_setup_transfer
cs->txrx_bufs = bitbang_txrx_8;
cs->txrx_bufs = bitbang_txrx_16;
cs->txrx_bufs = bitbang_txrx_32;
status = bitbang->txrx_bufs(spi, transfer);
word = txrx_word(spi, ns, word, bits);
spi_gpio_spec_txrx_word_mode0
综上来看,对于SPI总线驱动的注册主要通过 spi_bitbang_start 进行注册,并需要完成相关回调函数的填充,如txrx_word,setup_transfer等
下面开始看SPI传输的设备端如何实现
spidev.c 注释:Simple synchronous userspace interface to SPI devices
spidev_init
status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
spidev_class = class_create(THIS_MODULE, "spidev");
status = spi_register_driver(&spidev_spi_driver);
spidev_probe
device_create
spi_register_driver
__spi_register_driver(THIS_MODULE, driver)
sdrv->driver.bus = &spi_bus_type;
spi_driver->driver.probe = spi_drv_probe; //注意 spi_driver.probe = spidev_probe,
driver_register(&sdrv->driver);
再看一个spi 设备的实现
sprd_spi_intf.c
sprd_spi_probe
ret = spi_register_driver(&spi_drv);
spi_device_register(spi, &pdev->dev);
sprd_spi_dev_probe
sprd_spi_config(spi->panel);
spi_setup(spi_dev);
sprd_spi_refresh
sprd_spi_transfer
wait_for_completion(&spi->spi_complete);
sprd_spi_cmd(panel, 0);
sprd_spi_data(panel, 1);
spi_message_add_tail
spi_sync
从上面可以知道,通过spi_register_driver对spi总线上寻找匹配属性即可匹配spi,从而拿到 spi->spi_dev = spi_dev; 然后调用master里面的kthread_work.kthread_work_func_t 即spi_pump_messages
那么这个总线是怎么来的呢
spi.c
spi_init
status = bus_register(&spi_bus_type);// bus_register - register a driver-core subsystem
status = class_register(&spi_master_class);
spi通过设备树的compatible属性确定了spi master是哪个dev节点,从而在调用
spi_message_add_tail
spi_sync
的时候,就直接指向的是注册master的kworker
用户空间spi发送接收测试
需要将spidev.c使能,该文件将spi总线暴露给用户空间
测试程序
spidev_test.c
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
static void transfer(int fd)
{
int ret;
uint8_t tx[] = { //要发送的数据数组
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
uint8_t rx[ARRAY_SIZE(tx)] = {0, }; //接收的数据数据
struct spi_ioc_transfer tr = { //声明并初始化spi_ioc_transfer结构体
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
//SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //ioctl默认操作,传输数据
if (ret < 1)
pabort("can't send spi message");
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收缓冲区
if (!(ret % 6)) //6个数据为一簇打印
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
static void print_usage(const char *prog) //参数错误则打印帮助信息
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word \n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = { //参数命令表
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
if (c == -1)
break;
switch (c) {
case 'D': //设备名
device = optarg;
break;
case 's': //速率
speed = atoi(optarg);
break;
case 'd': //延时时间
delay = atoi(optarg);
break;
case 'b': //每字含多少位
bits = atoi(optarg);
break;
case 'l': //回送模式
mode |= SPI_LOOP;
break;
case 'H': //时钟相位
mode |= SPI_CPHA;
break;
case 'O': //时钟极性
mode |= SPI_CPOL;
break;
case 'L': //lsb 最低有效位
mode |= SPI_LSB_FIRST;
break;
case 'C': //片选高电平
mode |= SPI_CS_HIGH;
break;
case '3': //3线传输模式
mode |= SPI_3WIRE;
break;
case 'N': //没片选
mode |= SPI_NO_CS;
break;
case 'R': //从机拉低电平停止数据传输
mode |= SPI_READY;
break;
default: //错误的参数
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv); //解析传递进来的参数
fd = open(device, O_RDWR); //打开设备文件
if (fd < 0)
pabort("can't open device");
/*
* spi mode //设置spi设备模式
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); //写模式
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); //读模式
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word //设置每个字含多少位
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); //写 每个字含多少位
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); //读 每个字含多少位
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz //设置速率
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); //写速率
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); //读速率
if (ret == -1)
pabort("can't get max speed hz");
//打印模式,每字多少位和速率信息
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
transfer(fd); //传输测试
close(fd); //关闭设备
return ret;
}