Linux Platform总线+SPI总线分析

2023-05-16


2015/07/1 11:20

本文以MPC8308(powerpc架构),HX软件包为依据,详细内容可参考源码

CPU:   e300c3MPC8308 400MHz 

BOARD: Freescale MPC8308ERDB

DRAM:  256M

NAND:  1024M

 

 

一、SPI原理模型


如上图所示,主设备对应SOC芯片中的SPI控制器,通常,一个SOC中可能存在多个SPI控制器,像上面的例子所示,SOC芯片中有3SPI控制器。每个控制器下可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外3个信号引脚:SCKMISOMOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SPI控制器)通信,其它的从设备处于等待状态,并且它们的3个引脚必须处于高阻状态。

 

二、SPI驱动模型

2.6的Linux内核中,SPI的驱动架构分为如下三个层次:硬件抽象层、平台依赖层和用户接口层。


硬件抽象层
spi.c
为其主体框架代码,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平 台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
平台依赖层
SPI
总线Master就是一条SPI总线的控制器(所谓控制是相对于本CPU来说的),在物理上连接若干SPI从设备。在Linux驱动中,每种处理器 平台有自己的控制器驱动,属于平台移植相关层。PowerPC平台来说,其是spi_mpc83xx.c。其按照核心层定义的接口实现了 spi_master。
用户接口层
设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。

 

平台依赖层-总线控制器驱动
总线控制器驱动,本质上就是实现了具体的总线传输算法并向核心层注册了控制器。主要分为三个层面,platform device,platform driver及与SPI core的接口层。

Linux内核的所有SPI控制器驱动程序都在driver/SPI/下面,MPC8xxx驱动是spi_mpc83xx.c。



 

一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USBPCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口platform总线对加入到该总线的设备和驱动分别封装了两个结构体——platform_deviceplatform_driver。并且提供了对应的注册函数。






platform总线上注册设备和驱动,只要定义指定的结构体后调用platform给出的注册函数就可以了。

 

下面就介绍一下platform总线、设备和驱动

三、platform驱动模型分析

1、platform总线:

do_basic_setup(void)

--------->driver_init();

    ---------->platform_bus_init(platform总线初始化)

        ---------->bus_register(&platform_bus_type)

 

structbus_type platform_bus_type = {

 .name ="platform",                   //定义了总线名字为platform,总线注册后新建目录sys/bus/platform

 .dev_attrs= platform_dev_attrs,

 .match = platform_match,

 .uevent= platform_uevent,

 .pm= PLATFORM_PM_OPS_PTR,

};

 

static int platform_match(structdevice *dev, struct device_driver *drv)

{

 structplatform_device *pdev;

 

 pdev= container_of(dev, struct platform_device, dev);

 return(strcmp(pdev->name, drv->name) == 0);     //配对函数检验名字是否一致

}


 

2、platform设备:

 

(/include/linux/platform_device.h)

struct platform_device {

 const char * name;

 int id;

 struct device dev;

 u32 num_resources;

 struct resource * resource;

};

可以看到,platform_device的封装就是指定了一个目录的名字name,并且内嵌device

platform_device的注册和注销使用以下函数:

(drivers/base/platform.c)

322 intplatform_device_register(struct platform_device *pdev) //需要判断返回值

337 voidplatform_device_unregister(struct platform_device *pdev)

注册后,同样会在/sys/device/目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。

源码分析

machine_device_initcall (mpc830x_edd, mpc830x_spi_init); 

/arch/powerpc/platforms/83xx/mpc830x_edd.c

-->platform_device_alloc ("mpc83xx_spi", i)-->设备树spi设备信息获取-->platform_device_alloc("mpc83xx_spi",i);-->platform_device_add

 

3、platform驱动:

(linux/platform_device.h)

50 structplatform_driver {

51     int(*probe)(struct platform_device *);

52     int(*remove)(struct platform_device *);

53     void(*shutdown)(struct platform_device *);

54     int(*suspend)(struct platform_device *, pm_message_t state);

55     int(*suspend_late)(struct platform_device *, pm_message_t state);

56     int(*resume_early)(struct platform_device *);

57     int(*resume)(struct platform_device *);

58     struct device_driverdriver;

59 };

可以看到,platform_driver结构体内嵌了device_driver,并且实现了probremove等操作。其实,当内核需要调用probe函数时,它会调用driver->probe,在dricer->probe中再调用platform_driver->probe

platform_driver的注册和注销函数:

/*drivers/base/platform.c*/

492 intplatform_driver_register(struct platform_driver *drv)

。。。。。

513 voidplatform_driver_unregister(struct platform_driver *drv)

注册成功后内核会在/sys/bus/platform/driver/目录下创建一个名字为driver->name的目录。

源码分析:

mpc83xx_spi_init

/drivers/spi/spi_mpc83xx.c

 

static struct platform_driver mpc83xx_spi_driver ={

 .remove = __exit_p(mpc83xx_spi_remove),

 .driver = {

  .name = "mpc83xx_spi",

  .owner = THIS_MODULE,

 },

};

-->platform_driver_probe-->platform_driver_register-->driver_register

 

4.源码具体分析:

1)platform设备

 /arch/powerpc/platform/83xx/mpc830x_edd.c 

machine_device_initcall(mpc830x_edd, mpc830x_spi_init);

mpc830x_spi_init-->fsl_spi_init(&mpc830x_spi_boardinfo,1, NULL, NULL);

static struct spi_board_info mpc830x_spi_boardinfo = {

 .bus_num = 0x7000,

 .chip_select = 0,

 .mode = SPI_MODE_3,

 .max_speed_hz = 25000000,

 .modalias = "spidev",

};

(arch/powerpc/sysdev/fsl_soc.c)

fsl_spi_init

    --> ret = of_fsl_spi_probe(NULL,"fsl,spi", sysclk, board_infos, 

          num_board_infos,activate_cs, deactivate_cs);

 


struct fsl_spi_platform_data pdata = {

   max_chipselect = 1,

   pdata.bus_num = 0x7000,

   activate_cs = NULL,

   deactivate_cs = NULL,

  };

 

 

  pdev = platform_device_alloc("mpc83xx_spi",i); //以mpc83xx_spi为name申请platform device,后续的platform driver将以mpc83xx_spi为匹配因子

  ret = platform_device_add_data(pdev,&pdata, sizeof(pdata)); //将pdata等特定的属性添加到platform device中,以供相应的platform 

  ret = platform_device_add_resources(pdev,res, ARRAY_SIZE(res)); //将SPI相关的platform device添加到platform bus上

 

  ret = platform_device_add(pdev);

----->  pdev->dev.parent =&platform_bus; 

            pdev->dev.bus= &platform_bus_type;

             device_add()

 

(

int platform_device_register(struct platform_device *pdev)
 321{

 322       device_initialize(&pdev->dev);
 323       return platform_device_add(pdev);
 324}

注册一个platform device分为了两部分,初始化这个platform_device,然后将此platform_device添加到platform总线中。输入参数platform_device可以是静态的全局设备。

)

动态申请platform_device_alloc一个platform_device设备,然后通过platform_device_add_resourcesplatform_device_add_data等添加相关资源和属性。

 

2)platform驱动--spi实例

mpc83xx_spi_driver注册时会扫描platform bus上的所有设备,匹配因子是mpc83xx_spi,匹配成功后调用mpc83xx_spi_probe将设备和驱动绑定起来

 

struct platform_driver {

 int (*probe)(struct platform_device *);

 int (*remove)(struct platform_device *);

 void (*shutdown)(struct platform_device *);

 int (*suspend)(struct platform_device *, pm_message_t state);

 int (*suspend_late)(struct platform_device *, pm_message_t state);

 int (*resume_early)(struct platform_device *);

 int (*resume)(struct platform_device *);

 struct device_driver driver;

};

 

struct device_driver {

 const char *name;                                                                     “mpc83xx_spi”<----platform_driver

 struct bus_type *bus;

 struct module *owner;                                                                 "THIS_MODULE"<---platform_driver

 const char *mod_name; /* used for built-in modules */

 int (*probe) (struct device *dev);                                                  platform_drv_probe<---platform_driver

 int (*remove) (struct device *dev);

 void (*shutdown) (struct device *dev);

 int (*suspend) (struct device *dev, pm_message_t state);

 int (*resume) (struct device *dev);

 struct attribute_group **groups;

 struct dev_pm_ops *pm;

 struct driver_private *p;

};

device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载

 

device_driver结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。内核正是通过这个一致性来为驱动程序找到资源,即 platform_device中的resource

 

(drivers/spi/spi_mpc83xx.c)

 

MODULE_ALIAS("platform:mpc83xx_spi");

static struct platform_driver mpc83xx_spi_driver = {

 .remove = __exit_p(mpc83xx_spi_remove),

 .driver = {

  .name = "mpc83xx_spi",

  .owner = THIS_MODULE,

 },

};

 

static int __init mpc83xx_spi_init(void)

{

 return platform_driver_probe(&mpc83xx_spi_driver,mpc83xx_spi_probe);

}

 

int __init_or_module platform_driver_probe(structplatform_driver *drv,

  int (*probe)(struct platform_device *))

{

 int retval, code;

 

 /* temporary section violation during probe() */

 drv->probe = probe;

 retval = code = platform_driver_register(drv);

 

。。。

}

 

 

int platform_driver_register(struct platform_driver *drv)

{

 drv->driver.bus = &platform_bus_type;

 if (drv->probe)

  drv->driver.probe = platform_drv_probe;

 if (drv->remove)

  drv->driver.remove = platform_drv_remove;

 if (drv->shutdown)

  drv->driver.shutdown = platform_drv_shutdown;

 if (drv->suspend)

  drv->driver.suspend = platform_drv_suspend;

 if (drv->resume)

  drv->driver.resume = platform_drv_resume;

 return driver_register(&drv->driver);

}

driver_register--->bus_add_driver--->driver_attach--->__driver_attach

 

static int __driver_attach(struct device*dev, void *data)

{

 struct device_driver *drv = data;

 

 if (drv->bus->match &&!drv->bus->match(dev, drv)) 

  return 0;

 

 if (dev->parent) /* Needed for USB */

  down(&dev->parent->sem);

 down(&dev->sem);

 if (!dev->driver)

  driver_probe_device(drv, dev);

 up(&dev->sem);

 if (dev->parent)

  up(&dev->parent->sem);

 

 return 0;

}

driver_probe_device--->really_probe--->drv->probe(dev);

此处的drv是struct device_driver *

而其probe是platform_drv_probe

 

static int platform_drv_probe(struct device*_dev)

{

 struct platform_driver *drv =to_platform_driver(_dev->driver);

 struct platform_device *dev =to_platform_device(_dev);

 

 return drv->probe(dev);

}

此处的drv是platform_driver *

其实就是回到了

platform_driver_probe(&mpc83xx_spi_driver,mpc83xx_spi_probe);

中的mpc83xx_spi_probe

 

四、spi驱动模型分析

1.spi驱动模型分析

 


 2.spi总线

 

static int __init spi_init(void)

postcore_initcall(spi_init);

struct bus_type spi_bus_type = {

 .name = "spi",

 .dev_attrs = spi_dev_attrs,

 .match = spi_match_device,

 .uevent = spi_uevent,

 .suspend = spi_suspend,

 .resume = spi_resume,

};

static struct class spi_master_class = {

 .name = "spi_master",

 .owner = THIS_MODULE,

 .dev_release = spi_master_release,

};

static int __init spi_init(void)

{

 int status;

 buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); 

 status = bus_register(&spi_bus_type); 

 status = class_register(&spi_master_class); 

 return 0; 

。。。

}

3.spi设备分析

spi_device对应着SPI总线上某个特定的slave。每个slave都有特定的大小端、速率及传输位宽,各个slave相互之间无干扰。

static int __init mpc83xx_spi_probe(structplatform_device *dev)

 struct spi_master *master;

 struct mpc83xx_spi *mpc83xx_spi;

。。。

 master = spi_alloc_master(&dev->dev,sizeof(struct mpc83xx_spi));

 platform_set_drvdata(dev, master);

 pdata = dev->dev.platform_data;

r = platform_get_resource(dev, IORESOURCE_MEM, 0);

 master->setup = mpc83xx_spi_setup;--------初始化配置

 master->transfer = mpc83xx_spi_transfer;----指定中断下文的数据处理队列

 master->cleanup = mpc83xx_spi_cleanup;

 

 mpc83xx_spi = spi_master_get_devdata(master);

 

 mpc83xx_spi->base = ioremap(r->start,r->end - r->start + 1);

 mpc83xx_spi->irq = platform_get_irq(dev, 0);

 ret = request_irq(mpc83xx_spi->irq,mpc83xx_spi_irq,

     0, "mpc83xx_spi",mpc83xx_spi);

 

 INIT_WORK(&mpc83xx_spi->work,mpc83xx_spi_work);

 

 ret = spi_register_master(master);

。。。

 

struct spi_master {

 struct device  dev;

 s16 bus_num;       --->0x7000

 u16 num_chipselect;

 int (*setup)(struct spi_device *spi);

 int (*transfer)(struct spi_device *spi,

  struct spi_message *mesg);

 void (*cleanup)(struct spi_device *spi);

};

 

Spi_master
spi_master
是对某一条SPI总线的抽象,是特定总线的相关属性的集合。

 

每一个SPI master都要实现setuptransfercleanup等。

 

 

spi_register_master-->scan_boardinfo

 

static struct spi_board_info mpc830x_spi_boardinfo = {

 .bus_num = 0x7000,

 .chip_select = 0,

 .mode = SPI_MODE_3,

 .max_speed_hz = 25000000,

 .modalias = "spidev",

};

 

-->spi_new_device-->spi_alloc_device

 

struct spi_device *spi_alloc_device(struct spi_master*master)

{

 struct spi_device *spi;

 struct device *dev = master->dev.parent;

 

 if (!spi_master_get(master))

  return NULL;

 

 spi = kzalloc(sizeof *spi, GFP_KERNEL);

 if (!spi) {

  dev_err(dev, "cannot allocspi_device\n");

  spi_master_put(master);

  return NULL;

 }

 

 spi->master = master;

 spi->dev.parent = dev;

 spi->dev.bus = &spi_bus_type;

 spi->dev.release = spidev_release;

 device_initialize(&spi->dev);

 return spi;

}

struct spi_device {
    structdevice        dev;

    structspi_master    *master;
    u32           max_speed_hz;
    u8           chip_select;
    u8           mode;
。。。
    u8           bits_per_word;

    int           irq;
    void           *controller_state;
    void           *controller_data;
。。。
};

-->spi_add_device-->device_add

 

4.spi驱动分析

 

struct spi_driver {

    int           (*probe)(struct spi_device *spi);
    int           (*remove)(struct spi_device *spi);

    void           (*shutdown)(struct spi_device *spi);
    int           (*suspend)(struct spi_device *spi,pm_message_t mesg);
    int           (*resume)(struct spi_device *spi);
    structdevice_driver    driver;
};
Driver是为device服务的,SPI_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定。

 

module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)

设备与设备驱动匹配时候调用的probe方法spidev_probe

设备驱动的操作函数集file_operations--->spidev_fops

 

open方法spidev_open
进行检查, 重点是以后三条语句,其他的见下面代码注释:

spidev->users++; //spidev_data使用者计数++ 
filp->private_data = spidev; //spidev_data放在文件的私有数据里 
nonseekable_open(inode, filp);  //设置文件的打开模式(文件读写指针不会跟随读写操作移动)

 

read方法spidev_read
spidev =filp->private_data;=========>>status = spidev_sync_read(spidev,count);===========>>

spidev_sync(spidev,&m)-->spi->master->transfer(spi, message);

mpc83xx_spi_transfer-->

 list_add_tail(&m->queue,&mpc83xx_spi->queue);

 queue_work(mpc83xx_spi->workqueue,&mpc83xx_spi->work);


wait_for_completion(&done);========>>到了这一步是重点,spi_async()方法中,使用以下语句将要做的事情加到workqueue

此后所有的处理程序便转移到在之前初始化的work方法中

mpc83xx_spi_work-->

  m->status = status;

  m->complete(m->context);

设置完成标志


整个platform、spi总线设备和驱动的结构如下图:


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux Platform总线+SPI总线分析 的相关文章

  • Linux shell 根据第二列对文件进行排序?

    我有一个这样的文件 FirstName FamilyName Address PhoneNumber 如何按 FamilyName 排序 如果这是 UNIX sort k 2 file txt 您可以使用多个 k用于对多列进行排序的标志 例
  • 内核驱动程序从用户空间读取正常,但写回始终为 0

    因此 我正在努力完成内核驱动程序编程 目前我正在尝试在应用程序和内核驱动程序之间构建简单的数据传输 我使用简单的字符设备作为这两者之间的链接 并且我已成功将数据传输到驱动程序 但我无法将有意义的数据返回到用户空间 内核驱动程序如下所示 in
  • 如何通过 makefile 在 Linux 上安装程序? [复制]

    这个问题在这里已经有答案了 可能的重复 Linux Unix make install 应该包含什么 https stackoverflow com questions 528399 what should linux unix make
  • PIL 的 Image.show() 带来*两个*不同的查看器

    在 python shell 中处理图像时 我使用 image show 其中 image 是 Image 的实例 很久以前什么也没发生 但在定义了一个名为 xv 的 Mirage 符号链接后 我很高兴 最近几天 show 将显示 Imag
  • Apache 端口转发 80 到 8080 并访问 Apache (80) 中托管的应用程序,即 phpMyadmin 和 Tomcat (8080)

    我想访问托管在 tomcat 服务器 8080 中的应用程序 myapp 当前可以通过以下方式访问http example com 8080 myapp http example com 8080 myapp in http example
  • 任何退出 bash 脚本但不退出终端的方法

    当我使用exitshell 脚本中的命令 该脚本将终止终端 提示符 有什么方法可以终止脚本然后停留在终端中吗 我的剧本run sh预计通过直接获取或从另一个脚本获取来执行 编辑 更具体地说 有两个脚本run2 sh as run sh ec
  • 如何在C(Linux utf8终端)中打印“盒子抽屉”Unicode字符?

    我正在尝试显示 方框图范围 2500 257F 中的 Unicode 字符 它应该是标准 utf8 Unicode 标准 版本 6 2 我根本做不到 我首先尝试使用旧的 ASCII 字符 但 Linux 终端以 utf8 显示 并且没有显示
  • 在 scapy 中通过物理环回发送数据包

    我最近发现了 Scapy 它看起来很棒 我正在尝试查看 NIC 上物理环回模块 存根上的简单流量 但是 Scapy sniff 没有给出任何结果 我正在做的发送数据包是 payload data 10 snf sniff filter ic
  • 如何从 Bash 命令行在后台 Vim 打开另一个文件?

    我正在从使用 Gvim 过渡到使用控制台 Vim 我在 Vim 中打开一个文件 然后暂停 Vim 在命令行上运行一些命令 然后想返回到 Vim Ctrl Z 在正常模式下 暂停 Vim 并返回到控制台 fg可用于将焦点返回到 Vim job
  • Docker忽略limits.conf(试图解决“打开文件太多”错误)

    我正在运行一个 Web 服务器 该服务器正在处理数千个并发 Web 套接字连接 为了实现这一点 在 Debian linux 我的基本镜像是 google debian wheezy 在 GCE 上运行 上 打开文件的默认数量设置为 100
  • 如何在 Linux 和 C 中使用文件作为互斥体?

    我有不同的进程同时访问 Linux 中的命名管道 并且我想让此访问互斥 我知道可以使用放置在共享内存区域中的互斥体来实现这一点 但作为一种家庭作业 我有一些限制 于是 我想到的是对文件使用锁定原语来实现互斥 我做了一些尝试 但无法使其发挥作
  • BASH:输入期间按 Ctrl+C 会中断当前终端

    我的 Bash 版本是 GNU bash version 4 3 11 1 release x86 64 pc linux gnu 我有一段这样的代码 while true do echo n Set password read s pas
  • LINUX:如何锁定内存中进程的页面

    我有一个 LINUX 服务器 运行一个具有大量内存占用的进程 某种数据库引擎 该进程分配的内存太大 需要将其中一部分换出 换出 我想做的是将所有其他进程 或正在运行的进程的子集 的内存页面锁定在内存中 以便只有数据库进程的页面被换出 例如
  • 如何从 C++ 程序中重新启动 Linux?

    我有一个 Qt 4 GUI 我需要在下拉菜单中提供一个选项 允许用户选择重新启动计算机 我意识到这对于以其他方式重新启动计算机的能力来说似乎是多余的 但选择需要保留在那里 我尝试使用 system 来调用以下内容 suid root she
  • Linux无法删除文件

    当我找到文件时 我在删除它们时遇到问题 任务 必须找到带有空格的文件并将其删除 我的尝试 rm find L root grep i 但我有错误 rm cannot remove root test No such file or dire
  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • Linux 中 m 标志和 o 标志将存储在哪里

    我想知道最近收到的路由器通告的 m 标志和 o 标志的值 从内核源代码中我知道存储了 m 标志和 o 标志 Remember the managed otherconf flags from most recently received R
  • SSH,运行进程然后忽略输出

    我有一个命令可以使用 SSH 并在 SSH 后运行脚本 该脚本运行一个二进制文件 脚本完成后 我可以输入任意键 本地终端将恢复到正常状态 但是 由于该进程仍在我通过 SSH 连接的计算机中运行 因此任何时候它都会登录到stdout我在本地终

随机推荐

  • vscode如何打开settings.json

    解决方案步骤 xff1a 打开vscode编辑器 xff0c 本文演示的vscode是英文版 点击左下角齿轮状的图标 在弹出的菜单中选择 Settings Settings点击后 xff0c 会出现一个设置窗口 在Settings窗口中点击
  • Failed to execute ‘createObjectURL‘ on ‘URL‘ Overload resolution failed

    vue使用二进制流下载文件 xff0c 使用 link href 61 window URL createObjectURL blob 报错 xff1a Failed to execute createObjectURL on URL Ov
  • openwrt安装docker并启动

    在软件包中下载docker和dockerd 也可以自行下载ipk文件安装 安装成功后启动xshell连上openwrt 执行 etc init d dockerd 启动docker的daemon服务 若要dockerd自启动则执行 ln s
  • Mysql环境变量配置

    一 mysql的环境变量配置步骤 1 1 在桌面选择 计算机 的图标 xff0c 右键 gt 属性 gt 点击 高级系统设置 gt 点击 环境变量 2 2 新建MYSQL HOME变量 xff0c 并将值设置为C Program Files
  • MySQL安装配置教程(超级详细)

    一 下载MySQL Mysql官网下载地址 xff1a MySQL Download MySQL Installer Archived Versions 1 选择要安装的版本 xff0c 本篇文章选择的是5 7 31版本 xff0c 点击D
  • mysql字符切割的四种方式

    1 从左开始截取字符串 left xff08 str length xff09 说明 xff1a left xff08 被截取字段 xff0c 截取长度 xff09 select left 39 如果暴力不是为了杀戮 xff0c 那将变得毫
  • Podman 使用指南

    原文链接 xff1a Podman 使用指南 Podman 原来是 CRI O 项目的一部分 xff0c 后来被分离成一个单独的项目叫 libpod Podman 的使用体验和 Docker 类似 xff0c 不同的是 Podman 没有
  • ClickHouse查询语句详解

    ClickHouse查询语句兼容大部分SQL语法 xff0c 并且进行了更加丰富的扩展 xff0c 查询语句模板如下 xff1a WITH expr list subquery SELECT DISTINCT ON column1 colu
  • Mysql和Redis如何保证数据一致性

    文章目录 前言一 先更新数据库 xff0c 再更新redis二 先更新redis xff0c 在更新数据库三 先更新数据库 xff0c 再删除redis四 先删除redis xff0c 再更新数据库总结 前言 如何保证数据库和缓存双写一致
  • SQL——左连接(Left join)、右连接(Right join)、内连接(Inner join)

    文章目录 前言 一 概念 二 例子 总结 前言 最近在做SQL相关的练习 发现以前那么自信的SQL放久了不碰也变得棘手起来 特别是这一块表之间的内外连接 所以这篇是关于这个内外连接的整理 一 概念 首先还是介绍一下这三个的定义 1 Left
  • UCOSII之项目实战总结

    电子IT行业博大精深 xff0c 没有人能够用笔记本天天记录自己所学的知识 xff0c 于是乎 xff0c 撰写博客就成了每个 IT民工 的专长 再者 xff0c 写一篇博客 xff0c 其意义与不但记录了自己所需的知识 xff0c 更提高
  • 明白了一句话:“加速度信号对高频敏感,位移信号对低频敏感”

    以前听别人说这些 xff0c 然后记住了 但是一直不大理解 最近在调试IEPE传感器 xff0c 正好要算位移 速度 加速度 对于相同的速度 xff0c 频率越高 xff0c 加速度值就越大 因为从公式就能看出来 xff0c 对于固定频率的
  • ubuntu 16.04使用IntelRealSense D435i调用realsense ROS包时,报symbol lookup error和undefined symbol错误的解决办法

    在ubuntu 16 04使用IntelRealSense D435i调用realsense ROS包时 xff0c 运行 roscore roslaunch realsense2 camera rs rgbd launch 出现错误 xf
  • Android浪潮

    Google的Android手机就要席卷世界了 xff01 IT技术的发展常常太出人意料 xff0c 我也想不太清楚Google的Android平台究竟吸引人在哪里 xff0c 但我相信Android会很快改变手机平台的格局 新的形势会出人
  • 卡尔曼滤波相关介绍及优缺点

    1 卡尔曼滤波算法为什么会叫滤波算法 xff1f 以一维卡尔曼滤波为例 xff0c 如果我们单纯的相信测量的信号 xff0c 那么这个信号是包含噪声的 xff0c 是很毛糙的 xff0c 但是当我们运行卡尔曼滤波算法去做估计 xff0c 我
  • STM32单片机(五)-寄存器地址理解和控制LED闪烁

    芯片 xff1a stm32f103zet6 1 存储单元一般应具有存储数据和读写数据的功能 一般以8位二进制作为一个存储单元 也就是一个字节 每个单元有一个地址 是一个整数编码 可以表示为二进制整数 2 stm32是32位单片机 xff0
  • 跨平台构建 Docker 镜像新姿势,x86、arm 一把梭

    点击 34 阅读原文 34 可以获得更好的阅读体验 在工作和生活中 xff0c 我们可能经常需要将某个程序跑在不同的 CPU 架构上 xff0c 比如让某些不可描述的软件运行在树莓派或嵌入式路由器设备上 特别是 Docker 席卷全球之后
  • 正点原子STM32F4笔记

    使用寄存器操作 xff0c 不错的博客 xff1a https blog csdn net w471176877 article category 1230060 https blog csdn net w471176877 article
  • JAVA中this用法小结

    我知道很多朋友都和我一样 xff1a 在 JAVA 程序中似乎经常见到 this xff0c 自己也偶尔用到它 xff0c 但是到底 this 该怎么用 xff0c 却心中无数 xff01 很多人一提起它 xff0c 就说 当前对象 xff
  • Linux Platform总线+SPI总线分析

    2015 07 1 11 20 本文以MPC8308 powerpc架构 xff0c HX软件包为依据 xff0c 详细内容可参考源码 CPU e300c3MPC8308 400MHz BOARD Freescale MPC8308ERDB