SPI接口及驱动

article/2025/8/23 8:00:10

1. 简介

  • SPI接口是Motorola 首先提出的全双工三线同步串行外围接口,采用主从模式(Master Slave)架构。支持多slave模式应用,一般仅支持单Master。时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first)。SPI接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平。
  • SPI接口优点:
    • 支持全双工操作
    • 操作简单
    • 数据传输速率较高(相对的)
  • SPI接口缺点:
    • 多个spi设备需要占用主机较多的管脚(每个从机都需要一根片选线)
    • 只支持单个主机
    • 没有指定的流控制,没有应答机制确认是否接收到数据

2. 接口

2.1 总线结构

在这里插入图片描述

2.2 硬件接口

在这里插入图片描述

  • SPI接口共有4根信号线,分别是:设备选择线、时钟线、串行输出数据线、串行输入数据线。
    • MOSI:主器件数据输出,从器件数据输入
    • MISO:主器件数据输入,从器件数据输出
    • SCLK: 时钟信号,由主器件产生
    • S S ˉ \bar{SS} SSˉ: 从器件使能信号,由主器件控制

2.3 SPI工作模式

在这里插入图片描述

  • SPI有四种工作模式,具体由CPOL(Clock Polarity 时钟极性),CPHA(Clock Phase时钟相位)决定
    • 当CPOL为0时,空闲的时候SCLK电平是低电平
    • 当CPOL为1时,空闲的时候SCLK电平是高电平
    • 当CPHA为0时,采集数据发生在时钟周期的前边缘(第一个边缘,可能是上升缘也可能是下降缘,由CPOL决定),这同时意味着输出数据发生在后边缘
    • 当CPHA为1时,采集数据发生在时钟周期的后边缘(第二个边缘,可能是上升缘也可能是下降缘,由CPOL决定),这同时意味着输出数据发生在前边缘

在这里插入图片描述

3. Linux驱动代码

  • Linux SPI驱动框架主要分为

    • 核心层
    • 控制器驱动层
    • 设备驱动层
      在这里插入图片描述
  • 最下层是硬件空间,SPI总线控制器,总线控制器负责硬件上的数据交互。内核空间中,需要有对应的控制器驱动,对硬件进行操作。

  • 核心层的作用:向控制器驱动层提供注册SPI控制器驱动的接口,并提供一些需要控制器驱动实现的回调函数。核心层向上,对SPI设备驱动,提供标准的SPI收发API,以及设备注册函数。

  • 当有SPI设备驱动发起一次传输时,设备驱动会调用SPI核心层的收发函数(spi_sync/spi_async),核心层的收发函数,会回调控制器驱动层实现的硬件相关的发送回调函数。从而实现SPI数据的收发。

  • 我们编写SPI接口的设备驱动程序的时候,最需要关心的就是SPI控制器的部分和SPI设备采用的是那种模式,确定模式后,我们得将SPI控制器配置成一样的模式才能正常工作

  • SPI驱动代码:位于 kernel/drivers/spi目录下 (这个目录和一些层次比较明显的驱动目录布局不同,全放在这个文件夹下,因此还是只好通过看Kconfig 和 Makefile来找找思路)

  • 相关数据结构

    • spi_master:SPI控制器
    • spi_driver:SPI设备驱动
    • spi_device:SPI设备
    • spi_message:SPI传输数据结构体
    • spi_transfer:该结构体是spi_message下的子单元
  • spi_driver和spi_device的关系

    • spi_driver对应一套驱动方法,包含probe,remove等方法。spi_device对应真实的物理设备,每个spi设备都需要一个spi_device来描述。spi_driver与spi_device是一对多的关系,一个spi_driver上可以支持多个同类型的spi_device
  • spi_master和spi_device

    • spi_master 与 spi_device 的关系和硬件上控制器与设备的关系一致,即spi_device依附于spi_master
  • spi_message和spi_transfer

    • spi传输数据是以 spi_message 为单位的,我们需要传输的内容在 spi_transfer 中。spi_transfer是spi_message的子单元
    • 将本次需要传输的 spi_transfer 以 spi_transfer->transfer_list 为链表项,连接成一个transfer_list链表,挂接在本次传输的spi_message spi_message->transfers链表下
    • 将所有等待传输的 spi_message 以 spi_message->queue 为链表项,连接成个链表挂接在queue下
      在这里插入图片描述
  • 相关API函数

//分配一个spi_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)//注册和注销spi_master
int spi_register_master(struct spi_master *master)
void spi_unregister_master(struct spi_master *master)//注册和注销spi_driver
int spi_register_driver(struct spi_driver *sdrv)
void spi_unregister_driver(struct spi_driver *sdrv)//初始化spi_message
void spi_message_init(struct spi_message *m)
//向spi_message添加transfers
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
//异步发送spi_message
int spi_async(struct spi_device *spi, struct spi_message *message)
//同步发送spi_message
int spi_sync(struct spi_device *spi, struct spi_message *message)//spi同步写(封装了上面的函数)
int spi_write(struct spi_device *spi, const void *buf, size_t len)
//spi同步读(封装了上面的函数)
int spi_read(struct spi_device *spi, void *buf, size_t len)
//同步写并读取(封装了上面的函数)
int spi_write_then_read(struct spi_device *spi,const void *txbuf, unsigned n_tx,void *rxbuf, unsigned n_rx)

3.1 SPI核心层

  • 实现代码:kernel/drivers/spi/spi.c (头文件位于: kernel/include/linux/spi/spi.h)
  • 功能:(实现函数:spi_init)
    • 对SPI子系统进行初始化工作
    • 注册SPI总线
    • 注册一个spi master(控制器)类
    • 提供SPI设备驱动对SPI总线进行操作的API
struct bus_type spi_bus_type = {.name		= "spi",.dev_groups	= spi_dev_groups,.match		= spi_match_device,.uevent		= spi_uevent,
};static struct class spi_master_class = {.name		= "spi_master",.owner		= THIS_MODULE,.dev_release	= spi_master_release,.dev_groups	= spi_master_groups,
};
  • spi_bus_type为spi总线类型,通过bus_register()函数将SPI 总线注册进总线,成功注册后,在/sys/bus 下即可找到spi 文件目录
  • spi_master_class为spi控制器设备类,通过调用class_register()函数注册设备类,成功注册后,在/sys/class目录下即可找到spi_master文件目录
  • spi子系统初始化函数spi_init,仅仅是注册了spi bus,以及spi_master class。
static int __init spi_init(void)
{int	status;buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);if (!buf) {status = -ENOMEM;goto err0;}status = bus_register(&spi_bus_type);			/*注册spi bus*/if (status < 0)goto err1;status = class_register(&spi_master_class);		/* 注册spi_master类 */if (status < 0)goto err2;if (IS_ENABLED(CONFIG_OF_DYNAMIC))WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));if (IS_ENABLED(CONFIG_ACPI))WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));return 0;
err2:bus_unregister(&spi_bus_type);
err1:kfree(buf);buf = NULL;
err0:return status;
}

3.2 SPI控制器驱动

  • SPI控制器驱动:即SPI硬件控制器对应的驱动,核心部分需要实现硬件SPI数据收发功能。这样SPI设备驱动,才能通过SPI读写数据
  • SPI是一种平台特定的资源,所以它是以platform device的方式注册进内核的,因此它的struct platform_device结构是已经静态定义好了的,现在只待它的struct platform_driver注册,然后和platform_device匹配。
  • 描述SPI控制器的设备树节点:
		spi0: spi@0xA5004000 {compatible = "my,my-spi";   /* 描述,和驱动匹配 */reg = <0 0xA5004000 0 0x1000>; /* 控制器IO地址 */clocks = <&spi0_mclk>;  /* 控制器时钟 */clock-names = "spi_mclk";  /* 时钟名 */interrupt-parent = <&gic>;interrupts = <0 33 4>;   /* 控制器中断 */resets = <&rst 0x50 4>;  /* 复位寄存器 */reset-names = "spi0";     pinctrl-names = "default";pinctrl-0 = <&spi0_func>;   /* 引脚复用功能配置 */status = "disabled";     /* 暂时disable */#address-cells = <1>;#size-cells = <0>;};
  • 实现一个platform_driver
static struct platform_driver my_spi_driver = {.probe = my_spi_probe,.remove = my_spi_remove,.driver = {.name = MY_SPI_NAME,.of_match_table = my_spi_of_match,.pm = &my_spi_dev_pm_ops,},
};

3.2.1 probe函数功能

  • 申请struct spi_master内存以及私有数据内存(struct my_spi)
  • 将struct spi_master设置为platform_device的private_data
  • 从设备树获取IO地址,对应设备树reg节点
  • 将IO内存映射为虚拟地址
  • 从设备树获取中断,对应设备树interrupts节点
  • 申请中断,设置中断服务函数
  • 设置核心层回调的片选使能函数,设置spi_master的transfer_one函数,如果控制器驱动不实现transfer和transfer_one_message,内核会自动填充默认的,最终控制器驱动只需要实现transfer_one。
  • 根据时钟名,从设备树获取时钟
  • 根据设备树的reset节点,操作寄存器spi控制器对应的BIT进行复位
  • 调用devm_spi_register_master,把spi_master->device注册到设备模型中。核心层篇中以及详细介绍了devm_spi_register_master函数,

3.2.2 数据收发

  • 数据收发部分主要有两个函数
  • 1)控制收发的函数,即spi_master的spi_transfer_one函数
    - 以看到全志这里代码的逻辑,一个是初始化完成量。然后进行一系列硬件操作后,睡眠等待完成信号,同时设置超时时间,超时了没有得到信号量,证明硬件出问题了,不应该一直等待,应该返回错误。
    - 这个完成信号由中断来发送,硬件上会有发送完成中断。
  • 2)配合发送的中断服务函数
    • 读取中断状态寄存器,判断发送完成的BIT是否置位,如果置位,则表示硬件已经发送完数据了,那么发送完成信号量,给正在睡眠等待的spi_transfer_one函数。spi_transfer_one函数接收到信号量后被唤醒,知道硬件以及发送完数据了,返回0(表示发送成功)

3.3 SPI设备驱动

  • 实现一个spi_driver, 并注册到SPI核心层
  • 直接调用spi核心层提供的函数注册,也就是说它不需要关心是哪个控制器来实现最终的spi数据传输。从这里也可以看出核心层的作用,分隔了控制器和设备驱动的关联性,主要两边的驱动都容易实现
  • 以内核中spidev设备驱动为例,对基于设备树的SPI设备驱动进行说明
  • spidev驱动代码位于kernel/drivers/spi/spidev.c
  • 设备树部分
dac0: dh2228@2 {compatible = "rohm,dh2228fv";reg = <2>;spi-max-frequency = <100000>;
};
  • 设备树部分很简单,只是写了compatible描述,用于和驱动匹配。这里reg的意思是spi cs引脚序号。并不像其他平台设备一样是IO地址,或者像I2C一样是从机地址

3.3.1 spidev_init

static int __init spidev_init(void)
{int status;/* Claim our 256 reserved device numbers.  Then register a class* that will key udev/mdev to add/remove /dev nodes.  Last, register* the driver which manages those device numbers.*/BUILD_BUG_ON(N_SPI_MINORS > 256);status = register_chrdev(SPIDEV_MAJOR, "spidev_test", &spidev_fops);if (status < 0)return status;spidev_class = class_create(THIS_MODULE, "spidev");if (IS_ERR(spidev_class)) {unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);return PTR_ERR(spidev_class);}status = spi_register_driver(&spidev_spi_driver);if (status < 0) {class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);}return status;
}
module_init(spidev_init);static void __exit spidev_exit(void)
{spi_unregister_driver(&spidev_spi_driver);class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
module_exit(spidev_exit);
  • 这里设置的spidev的file_operations(spidev_fops),设备操作函数后文详细介绍。然后创建了spidev的class,创建完成后在用户空间/sys/class/下可以看到spidev目录结构。然后调用spi_register_driver注册spi从设备。

3.3.2 spi_driver

static const struct of_device_id spidev_dt_ids[] = {{ .compatible = "rohm,dh2228fv" },{ .compatible = "lineartechnology,ltc2488" },{ .compatible = "ge,achc" },{ .compatible = "semtech,sx1301" },{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
static struct spi_driver spidev_spi_driver = {.driver = {.name =		"spidev",.of_match_table = of_match_ptr(spidev_dt_ids),.acpi_match_table = ACPI_PTR(spidev_acpi_ids),},.probe =	spidev_probe,.remove =	spidev_remove,/* NOTE:  suspend/resume methods are not necessary here.* We don't do anything except pass the requests to/from* the underlying controller.  The refrigerator handles* most issues; the controller driver handles the rest.*/
};
  • 描述,与设备树相对应,会调用probe函数
  • probe函数,匹配时调用

3.3.3 probe

static int spidev_probe(struct spi_device *spi)
{struct spidev_data	*spidev;int			status;unsigned long		minor;/** spidev should never be referenced in DT without a specific* compatible string, it is a Linux implementation thing* rather than a description of the hardware.*/if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");WARN_ON(spi->dev.of_node &&!of_match_device(spidev_dt_ids, &spi->dev));}spidev_probe_acpi(spi);/* Allocate driver data */spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);if (!spidev)return -ENOMEM;/* Initialize the driver data */spidev->spi = spi;spin_lock_init(&spidev->spi_lock);mutex_init(&spidev->buf_lock);INIT_LIST_HEAD(&spidev->device_entry);/* If we can allocate a minor number, hook up this device.* Reusing minors is fine so long as udev or mdev is working.*/mutex_lock(&device_list_lock);minor = find_first_zero_bit(minors, N_SPI_MINORS);if (minor < N_SPI_MINORS) {struct device *dev;spidev->devt = MKDEV(SPIDEV_MAJOR, minor);dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);status = PTR_ERR_OR_ZERO(dev);} else {dev_dbg(&spi->dev, "no minor number available!\n");status = -ENODEV;}if (status == 0) {set_bit(minor, minors);list_add(&spidev->device_entry, &device_list);}mutex_unlock(&device_list_lock);spidev->speed_hz = spi->max_speed_hz;if (status == 0)spi_set_drvdata(spi, spidev);elsekfree(spidev);return status;
}
  • spidev的probe函数,申请了私有结构体spidev的内存空间,初始化了spidev的一些成员,自旋锁、互斥锁等。并将spidev指针设置为struct spi_device的私有数据(private_data)
  • 调用device_create创建了/dev/下的spidev节点,如spi总线0上cs1设备,则设备名为/dev/spidev0.1,其他以此类推
  • 在编写spi、i2c等驱动时,所做的操作也和spidev相差无几。初始化一些自己需要的资源等。但是一般情况下,这类涉及外设的驱动,会读取一下SPI、I2C从设备的ID寄存器等,来判断硬件是否就位,spi总线能否读写数据

3.3.4 spidev_fops

static const struct file_operations spidev_fops = {.owner =	THIS_MODULE,.write =	spidev_write,.read =		spidev_read,.unlocked_ioctl = spidev_ioctl,.compat_ioctl = spidev_compat_ioctl,.open =		spidev_open,.release =	spidev_release,.llseek =	no_llseek,
};
  • open和release:判断是否申请txbuff和rxbuff内存,如果没申请就申请。并维护了一个user count,用户空间每打开一次就+1,关闭一次就-1。所有都关闭后release释放申请的内存。

3.3.4.1 spidev_write

static ssize_t
spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
{struct spidev_data	*spidev;ssize_t			status = 0;unsigned long		missing;if (count > bufsiz)return -EMSGSIZE;spidev = filp->private_data;mutex_lock(&spidev->buf_lock);missing = copy_from_user(spidev->tx_buffer, buf, count);		(1)if (missing == 0)status = spidev_sync_write(spidev, count);					(2)elsestatus = -EFAULT;mutex_unlock(&spidev->buf_lock);return status;
}static inline ssize_t
spidev_sync_write(struct spidev_data *spidev, size_t len)
{struct spi_transfer	t = {.tx_buf		= spidev->tx_buffer,.len		= len,.speed_hz	= spidev->speed_hz,};struct spi_message	m;spi_message_init(&m);spi_message_add_tail(&t, &m);return spidev_sync(spidev, &m);									(3)
}static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{int status;struct spi_device *spi;spin_lock_irq(&spidev->spi_lock);spi = spidev->spi;spin_unlock_irq(&spidev->spi_lock);if (spi == NULL)status = -ESHUTDOWN;elsestatus = spi_sync(spi, message);							(4)if (status == 0)status = message->actual_length;return status;
}
  • copy_from_user:从用户空间拷贝要发送的数据到内核空间
  • 调用spidev_sync_write函数发送数据,这个函数实现代码也在上面贴出来了
  • spidev_sync_write代码实现,定义spi_transfer和spi_message,然后通过spidev_sync发送
  • spidev_sync中仅仅是判断了spi_device是否为空,不为空则调用spi_sync函数将spi_message发送出去
  • spidev_read函数与write函数如出一辙,不同的是write先从用户空间拷贝数据,然后调用发送函数发出去。struct spi_transfer填充的是tx_buff。而read则是先调用接受函数,struct spi_transfer填充的是rx_buff,然后将接收到的rx_buff,通过copy_to_user拷贝给用户空间。

3.3.4.2 ioctl

#define SPI_IOC_MAGIC			'k'/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
#define SPI_IOC_RD_MODE			_IOR(SPI_IOC_MAGIC, 1, __u8)
#define SPI_IOC_WR_MODE			_IOW(SPI_IOC_MAGIC, 1, __u8)/* Read / Write SPI bit justification */
#define SPI_IOC_RD_LSB_FIRST		_IOR(SPI_IOC_MAGIC, 2, __u8)
#define SPI_IOC_WR_LSB_FIRST		_IOW(SPI_IOC_MAGIC, 2, __u8)/* Read / Write SPI device word length (1..N) */
#define SPI_IOC_RD_BITS_PER_WORD	_IOR(SPI_IOC_MAGIC, 3, __u8)
#define SPI_IOC_WR_BITS_PER_WORD	_IOW(SPI_IOC_MAGIC, 3, __u8)/* Read / Write SPI device default max speed hz */
#define SPI_IOC_RD_MAX_SPEED_HZ		_IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ		_IOW(SPI_IOC_MAGIC, 4, __u32)/* Read / Write of the SPI mode field */
#define SPI_IOC_RD_MODE32		_IOR(SPI_IOC_MAGIC, 5, __u32)
#define SPI_IOC_WR_MODE32		_IOW(SPI_IOC_MAGIC, 5, __u32)

3.3.5 spidev总结

  • spidev是内核中用来测试spi的驱动,不是专门针对某一SPI硬件设备做的驱动,只是简单的注册字符设备,用户空间通过read、write、ioctl,直接对spi进行操作,相当于是用户空间和spi设备的桥梁
  • 用户空间操作时,打开设备节点,然后通过IOCTL设置模式、速度等参数,然后就可以调用read、write进行操作了
  • 根据设备树获取节点信息
spi_register_master->of_register_spi_devices->of_register_spi_device->of_spi_parse_dt
static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,struct device_node *nc)
{u32 value;int rc;/* Mode (clock phase/polarity/etc.) */if (of_property_read_bool(nc, "spi-cpha"))spi->mode |= SPI_CPHA;if (of_property_read_bool(nc, "spi-cpol"))spi->mode |= SPI_CPOL;if (of_property_read_bool(nc, "spi-cs-high"))spi->mode |= SPI_CS_HIGH;if (of_property_read_bool(nc, "spi-3wire"))spi->mode |= SPI_3WIRE;if (of_property_read_bool(nc, "spi-lsb-first"))spi->mode |= SPI_LSB_FIRST;/* Device DUAL/QUAD mode */if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {switch (value) {case 1:break;case 2:spi->mode |= SPI_TX_DUAL;break;case 4:spi->mode |= SPI_TX_QUAD;break;default:dev_warn(&ctlr->dev,"spi-tx-bus-width %d not supported\n",value);break;}}if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {switch (value) {case 1:break;case 2:spi->mode |= SPI_RX_DUAL;break;case 4:spi->mode |= SPI_RX_QUAD;break;default:dev_warn(&ctlr->dev,"spi-rx-bus-width %d not supported\n",value);break;}}if (spi_controller_is_slave(ctlr)) {if (strcmp(nc->name, "slave")) {dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",nc);return -EINVAL;}return 0;}/* Device address */rc = of_property_read_u32(nc, "reg", &value);if (rc) {dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",nc, rc);return rc;}spi->chip_select = value;/* Device speed */rc = of_property_read_u32(nc, "spi-max-frequency", &value);if (rc) {dev_err(&ctlr->dev,"%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);return rc;}spi->max_speed_hz = value;return 0;
}

http://chatgpt.dhexx.cn/article/wDYUSj2V.shtml

相关文章

SPI 接口

SPI 接口的全称是“Serial Peripheral Interface”意为串行外围接口&#xff0c;是 Motorola 首先在其 MC68HCxx 系列处理器上定义的。SPI 接口主要应用于 EEPROM、FLASH、实时时钟、AD转换器&#xff0c;还有数字信号处理器和数字信号解码器之间。 SPI 接口是在 CPU 和外围低…

ESP32 SPI 接口的应用

总体介绍 1. ESP32 共有 4 个 SPI 控制器 SPI0、SPI1、SPI2、SPI3&#xff0c;用于连接支持 SPI 协议的设备。 SPI0 控制器作为 cache 访问外部存储单元接口使用;SPI1 作为主机使用;SPI2 和 SPI3 控制器既可作为主机使用又可作为从机使用。作主机使用时&#xff0c;每个 SPI 控…

SPI接口简介-Piyu Dhaker

SPI接口简介 作者&#xff1a; Piyu Dhaker 串行外设接口(SPI)是微控制器和外围IC&#xff08;如传感器、ADC、DAC、移位寄存器、SRAM等&#xff09;之间使用最广泛的接口之一。本文先简要说明SPI接口&#xff0c;然后介绍ADI公司支持SPI的模拟开关与多路转换器&#xff0c;以…

弄懂SPI接口

SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是Motorola公司提出的一种同步串行数据传输标准&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;在很多器件中被广泛应用。 SPI相关缩写 SS: Slave Select&…

SPI接口

SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是Motorola公司提出的一种同步串行数据传输标准&#xff0c;在很多器件中被广泛应用。 1. 接口 SPI接口经常被称为4线串行总线&#xff0c;SPI协议是主从模式&#xff1a;从机不主动发起访问&…

SPI接口简介

串行外设接口(SPI)是微控制器和外围IC&#xff08;如传感器、ADC、DAC、移位寄存器、SRAM等&#xff09;之间使用最广泛的接口之一。本文先简要说明SPI接口&#xff0c;然后介绍ADI公司支持SPI的模拟开关与多路转换器&#xff0c;以及它们如何帮助减少系统电路板设计中的数字GP…

SPI接口详解

一、SPI接口简介 SPI 是英语Serial Peripheral interface的缩写&#xff0c;顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。 SPI&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用…

【科普贴】SPI接口详解

一、SPI接口简介 SPI接口是一种同步串行总线&#xff08;Serial Peripheral Interface&#xff09;多用于Flash存储器&#xff08;如NOR Flash&Nand Flash&#xff09;&#xff0c;ADC、LCD控制器等外围器件的通讯接口。大大增强了处理器的外设扩展能力。 SPI接口缩写 SSE…

第四章 - 程序计数器

文章目录 1.PC 寄存器介绍2.PC 寄存器的作用3.代码示例4.两个常见面试题5.CPU时间片 官网文档 1.PC 寄存器介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。…

运行时数据区 - 程序计数器

① 介绍 JVM中的程序计数寄存器(Program Counter Register)中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。 PC寄存器用来存储指向下一条指令的地址(即将要执行的指令代码)&#xff0c;由执行…

操作系统-程序计数器

查考资料&#xff1a;https://blog.csdn.net/xxb2008/article/details/42145649 程序计数器是用于存放下一条指令所在单元的地址的地方。 冯 诺伊曼计算机体系结构的主要内容之一就是“程序预存储&#xff0c;计算机自动执行”&#xff01; 处理器要执行的程序&#xff08;指…

04-程序计数器(PC计数器)

程序计数器 介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。 这里&#xff0c;并非是广义上所指的物理寄存器&…

三、程序计数器(PC寄存器)

文章目录 1.PC Register 介绍介绍作用 2.举例说明3.两个常见问题使用PC寄存器存储字节码指令的地址有什么用&#xff1f;PC寄存器为什么会被设定为线程私有的&#xff1f; 1.PC Register 介绍 介绍 JVM中的程序计时器&#xff08;Program Counter Register&#xff09;中&…

汇编语言、寄存器分类及程序计数器

目录 一、计算机语言 汇编语言 寄存器分类 二、程序计数器 一、计算机语言 计算机是由二进制构成的&#xff0c;它只能听懂二进制也就是机器语言&#xff0c;但是普通人是无法看懂机器语言的&#xff0c;这个时候就需要一种电脑既能识别&#xff0c;人又能理解的语言&…

Java --- JVM程序计数器(PC寄存器)

目录 一、程序计数器(PC寄存器) 二、主要功能作用 三、PC Register 四、PC寄存器面试题 五、CPU时间片 一、程序计数器(PC寄存器) JVM中的程序计数寄存器(Program Counter Register)中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。C…

计数器

文章目录 【 1. 同步计数器 】① 同步二进制计数器同步二进制加法计数器74161 十六进制四位二进制加法计数器74163 同步二进制减法计数器同步二进制加/减计数器单时钟方式 74191双时钟方式 74193 ② 同步十进制计数器同步十进制加法计数器7416074162 同步十进制减法计数器同步十…

4 程序计数器

1、输入日期&#xff0c;转化为毫秒数&#xff1a; 用calendar方法&#xff08;calendar.getTime&#xff09; 代码&#xff1a; public static void main(String[] args) {Calendar calendar Calendar.getInstance();calendar.set(2017, 6, 27, 15, 9, 0);System.out.print…

程序计数器(PC)

1.PC中存放下一次访存的地址。 2.PC自增的确是PC取出指令的长度。 3.PC每次自增都是固定的字长。 ———————————————————————————— 1.程序计数器的工作原理 大概解释一下程序计数器吧&#xff0c;用一个最简单的例子。 首先第一点&#xff1a;PC中存…

4、程序计数器PC

介绍 JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。这里&#xff0c;并非是广义上所指的物理寄存器&#xff0c;或许…

程序计数器介绍

JVM中的程序计数寄存器&#xff08;Program Counter Register&#xff09;中&#xff0c;Register的命名源于CPU的寄存器&#xff0c;寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。这里&#xff0c;并非是广义上所指的物理寄存器&#xff0c;或许将其翻…