【Linux驱动】Pinctrl子系统 | GPIO子系统 | 基于子系统的LED驱动程序

news2024/10/5 18:56:35

🐱作者:一只大喵咪1201
🐱专栏:《Linux驱动》
🔥格言:你只管努力,剩下的交给时间!
图

目录

  • 🛷Pinctrl子系统
    • 🥅设备树中的Pinctrl子系统
  • 🛷GPIO子系统
    • 🥅设备树中的GPIO子系统
    • 🥅驱动程序中使用GPIO子系统
  • 🛷基于子系统的LED驱动程序
    • 🥅驱动程序
    • 🥅设备树文件
  • 🛷总结

在前面的LED驱动程序中,有三种实现方式:

  • 硬件操作绑定到驱动程序中。
  • platform_device提供硬件信息,platform_driver获取硬件资源,并进行操作。
  • 使用设备树提供硬件信息,platform_driver获取硬件资源,并进行操作。

无论使用哪种方式,都需要通过ioremap函数将物理地址映射为虚拟地址,这几种方式存在两个问题:

  • 和硬件强相关,需要去查看芯片手册,更像是"寄存器"编程。
  • 驱动程序不通用,无法做到一套驱动程序适用于所有开发板。

在Linux中,针对开发板的引脚有两个子系统:GPIO子系统和Pinctrl子系统。

🛷Pinctrl子系统

图
如上图所示,开发板中几乎所有引脚都支持功能复用:

  • 要想让pinApinB作为通用GPIO口去使用,就需要设置IOMUX模块,配置这两个引脚的复用功能为GPIO功能模块。
  • 要想让pinApinB作为I2C功能去使用,就需要设置IOMUX模块,配置这两个引脚的复用功能为I2C功能模块。
  • GPIO模块和I2C模块与可复用的其他功能地位是相等的。

在将引脚复位为某种功能时,首先就需要配置IOMUX模块,有的时候还需要配置引脚的上拉,下拉,开漏等模式。

大多数芯片是没有单独的IOMUX模块的,需要配置其他寄存器模块来实现功能复用。

我们现在使用的芯片,动辄上百个引脚,而且配置复用功能的方式和步骤不尽相同,如果我们在编程的时候去对照芯片手册寻找这些寄存器和设置方式的话,无疑会把我们逼疯。

  • 所以,各个芯片厂家的BSP工程师就设计了一个Pinctrl子系统。

Pinctrl子系统是一个软件层面的子系统,它将配置引脚复用为各个功能的操作都放在这个子系统中,我们写驱动程序时,只需要直接去使用Pinctrl子系统就可以实现对不同功能的复用。

🥅设备树中的Pinctrl子系统

图
如上图所示设备树中代码所示,分为左右两部分:

  • 左半部分是pin_controller引脚控制子系统,这部分设备树的代码是用来使用Pinctrl子系统的。
  • 右半部分是client_device客户设备,是由我们写驱动程序的"客户"实现的。

client_device:

上图右半部分中client_device节点的名字是device,代称一个具体设备,如ledkey等等。

  • pinctrl-names:用来表示设备状态,此时有default默认状态和sleep状态两种。
  • pinctrl-0:表示第0种状态default时,该客户设备要设置的引脚复用功能,属性值是<&state_0_node_a>
  • pinctrl-1:表示第1种状态sleep时,该客户设备要设置的引脚复用功能,属性值是<&state_1_node_a>

如何理解呢?比如一个UART设备,它在默认状态default下是工作的,此时串口用到的引脚就要复用为UART功能。

在休眠状态下,为了省电,可以把用到的引脚复用为GPIO功能,或者直接把它输出高电平。

pin_controller:

上图左半部分中pin controller节点的名字是pincontroller,代称功能复用节点,如uartpwm等等。

  • function:表示要复用成什么功能,属性值是uart0,表示要复用成串口0。
  • groups:表示该功能要用到的所有引脚,这些引脚归为一个组groups
  • 配置信息:如上图中的output-high属性,表示配置为输出高电平。

上图客户设备中的pinctrl-names 里定义了 2 种状态:default、sleep:

  • 第 0 种状态用到的引脚在pinctrl-0 中定义,它是 state_0_node_a节点, 位于pincontroller 节点中的子节点。
  • 第 1 种状态用到的引脚在 pinctrl-1 中定义,它是 state_1_node_a节点, 位于pincontroller 节点中的子节点。

当客户设备处于default状态时,pinctrl子系统会自动根据上述信息把所用引脚复用为uart0功能。

当这个设备处于sleep 状态时,pinctrl子系统会自动根据上述信息把所用引脚配置为高电平。

  • cilent_device节点的格式是有标准的,我们必须按照标准去写,才能正确使用Pinctrl子系统中的功能复用节点。
  • 但是pin controller节点的格式却没有统一的标准,甚至上面的属性值groupfunction也不一定有。

虽然Pinctrl子系统在设备树中的实现不尽相同,但是都是遵循配置功能(function),设置组(groups),引脚设置(高低电平等)的规则来实现的。

甚至我们都不用知道是如何根据设备树中的Pinctrl子系统节点去配置开发板的寄存器的,因为具体的寄存器配置也由BSP工程师在Pinctrl子系统中完成了。

图
如上图对default状态的处理,主要是在platform_deviceplatform_driver匹配的过程中实现的:

  • 先将引脚复用设置为某个状态,复用为某个功能,不用我们自己去调用这些代码,在匹配过程中会自己调用。
  • 复用完成以后,才会调用platform_driver中的probe函数。

如果非要自己去实现复用功能的配置,也有相应的函数:

  • devm_pinctrl_get_select_default(struct device* dev),使用default状态的引脚。
  • pinctrl_get_select(struct device* dev, const char* name),根据name旋转某种状态的引脚。
  • pinctrl_put(struct pinctrl* p),不再使用引脚,退出时调用。

但是,我们写驱动程序时基本不用管是如何使用Pinctrl子系统的,只要知道在切换设备状态时,对应的Pinctrl就会被调用,实现相应的配置。

总之,Pinctrl子系统就用来实现引脚的功能复用的。

🛷GPIO子系统

要让引脚作为通用GPIO引脚,在通过Pinctrl子系统配置好引脚的功能后,还需要配置:

  • 引脚方向:输入还是输出。
  • 读取引脚值:获取电平状态是高还是低。
  • 控制输出:输出高电平还是低电平。

以前我们也是通过操作GPIO的相关寄存器来实现的,对于不同的开发板,它的驱动程序代码也是不一样的。

为了实现和Pinctrl子系统一样的分离,BSP工程时还实现了一个GPIO子系统,使得驱动程序适用于任何开发板,此时我们就可以:

  • 在设备树里指定GPIO引脚。
  • 在驱动程序中,使用GPIO子系统提供的标准函数获得GPIO引脚,设置方向,读取或者设置GPIO的值。

🥅设备树中的GPIO子系统

几乎所有的ARM芯片,GPIO都会分为几组,每组中都有若干个引脚,所以在使用GPIO子系统前,要先确定:

  • 使用的GPIO是哪组?GPIO1还是GPIO2等等。
  • 使用哪个引脚?GPIOx_IO1还是GPIOx_IO2等等。

图
如上图所示由芯片厂家的BSP工程师提供的imx6ull.dtsi设备树文件,存在多个GPIO组,也就是GPIO Controller,芯片有多少组GPIO,在该文件中就存在多少个这样的节点。

暂时我们只需要关心GPIO子系统节点中的两个属性:

  • gpio-contriller:表示这是一个GPIO Controller,该组GPIO中有很多引脚。
  • #gpio-cells = <2>:表示该控制器下每一个引脚要用2个32位的数(cell)来描述。

图
如上图所示我们自己写的dts设备树文件,虽然GPIO子系统的定义是厂家的事,但是使用具体哪个引脚还是由我们在设备树文件中决定的:

  • [<name>-]gpios:该属性就是表明该节点要使用哪组GPIO中的哪个引脚。
  • 属性值:<&gpio5 3 GPIO_ACTIVE_LOW>表示要使用GPIO5_IO3引脚,并且是低电平有效。
  • 这部分代码也是放在客户设备节点中的。

可以看到,属性值中有三个值,为什么是三个?

  • 因为在dtsi中的GPIO子系统中,定义了#gpio-cells = <2>,表示用两个数来描述引脚。
  • 使用哪个引脚必须指明所属的GPIO组,所以第一个数&gpio5是必须有的,不算在这两个数中。
  • 那么这两个数自然就是指表示引脚编号的3和,表示有效电平的GPIO_ACTIVE_LOW了。

🥅驱动程序中使用GPIO子系统

此时在设备树中已经指定了好了GPIO引脚,接下就是在驱动程序中使用这些引脚了,需要调用GPIO子系统提供的标准函数接口来获取引脚信息:

  • 新的基于描述符的(descriptor-based)接口:该套接口都有前缀gpiod_,它使用gpio_desc结构体来表示一个引脚。
  • 老的(legacy)接口:该套接口都有前缀gpio_,它使用一个整数来表示一个引脚。

要操作一个引脚,首先要get引脚,然后设置方向,读值,写值。

获得GPIO引脚:

descriptor-basedlegacy
gpiod_get
gpiod_get_index
gpio_request
gpiod_get_arraygpio_request_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array

设置方向:

descriptor-basedlegacy
gpiod_direction_inputgpio_direction_input
gpiod_direction_outputgpio_direction_output

读值、写值:

descriptor-basedlegacy
gpiod_get_valuegpio_get_value
gpiod_set_valuegpio_set_value

释放GPIO:

descriptor-basedlegacy
gpio_free
gpiod_put
gpio_free
gpiod_put_arraygpio_free_array

有前缀devm_的含义是设备资源管理(Managed Device Resource), 这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。

在 Linux 开发过程中,先申请了 GPIO,再申请内存;如果内存申请失败,那么在返回之前就需要先释放 GPIO 资源。如果使用 devm 的相关函数,在内存申请失败时可以直接返回:

  • 设备的销毁函数会自动地释放已经申请了的 GPIO 资源。

  • descriptor-based接口

图
如上图所示,假设现在设备树中有这样一个节点,用来操作红绿蓝三个LED灯,在驱动程序中可以使用以下函数来获取引脚:

图
如上图,使用gpiod_get_index来获取引脚信息。

在写和读引脚值时,gpiod_set_value设置的是逻辑值,而逻辑值并不一定等于物理值。

图
如上图所示,LED2在GPIO5_3是低电平时才亮,高电平时不亮,所以在我们写的设备树节点中led-gpios属性中的第三个参数就要写成GPIO_ACTIVE_LOW

此时使用gpiod_set_value(desc, x)时:

  • 当第二个参数为1,就会将该引脚设置成有效电平GPIO_ACTIVE_LOW低电平,LED灯就亮。
  • 当第二个参数为0,就不会将该引脚设置成有效电平,灯就不亮。

说白了就是,该函数的第二个参数为1,灯就会亮,0就会灭,如果电路上是高电平灯亮,就需要在设备树中将有效电平该为GPIO_ACTIVE_HIGH,此时驱动程序中的代码不用改变,该函数的第二个参数仍然是1。

  • 建议使用新的前缀为gpiod_的这套函数接口。
  • legacy接口:

旧的gpio_函数是没办法根据设备树信息获得引脚的,它需要先知道引脚号,然后才能初始化描述引脚信息的整数。

图

如上图,在sysfs中访问GPIO,获得引脚号:

  • 在开发板的/sys/class/gpio目录下,找到各个gpiochipXXX目录,这样的每一个目录表示GPIO子系统中的一组GPIO。
  • 然后进入某个gpiochip目录下,查看label的内容。
  • 根据label的内容来对比设备树。

label 内容来自设备树,存放有该组GPIO的基地址。用来跟设备树(dtsi 文件) 比较,就可以知道这对应哪一个 GPIO Controller

图

如上图,gpiochip96目录中的label中的GPIO基地址是0x020a8000,对dtsi文件中,发现该地址对应的是gpio4这组GPIO,所以可以得出结论:

  • GPIO4的基准引脚号就是96。
  • 假设引脚是GPIO4_IO14,那么该引脚的引脚号整数就是base number + n = 96 + 14 = 110

可以看到,老的一套接口并不是很好用,远不如新接口使用起来方便。

🛷基于子系统的LED驱动程序

有了Pinctrl子系统和GPIO子系统,对于引脚的配置完全在设备树文件中就可以搞定,驱动程序可以做到完全和硬件分离。

🥅驱动程序

我们知道,设备树中的节点会被转化成platfrom_deviceplatform_driver进行匹配,所以我们要做的就是完成platform_driver的相关代码:

  • 注册一个platform_driver结构体,在probe函数中,获取引脚,注册file_operations结构体。
  • file_operations中,设置方向、进行写值。
  • 此时驱动层不再分为上下两层,而是只有一层,file_operations结构体的注册也在probe中完成。

注册platform_driver结构体:

图
如上图所示,定义platform_driver结构体并进行初始化:

  • 初始化proberemove两个函数指针成员。
  • 初始化driver成员中的namesof_match_table

of_match_table是一个指针,指向struct of_device_id类型的数组,在该数组中的每一个元素都有一个compatible成员:

  • platform_deviceplatform_driver匹配过程中,看的就是这个属性,初始化为Big_Miaomi,led_drv,表示该驱动只支持这一个设备节点。
  • 由于使用的是设备树,所以匹配规则不会用到name成员,这里随意初始化为Big_Miaomi_leds

然后就是在入口函数led_init中使用platform_driver_register注册前面定义的chip_gpio_driver结构体,在出口函数中使用platform_driver_unregister再将该结构体移除,最后再完善以下设备信息。

现在总体框架已经有了,接下来就是实现probe函数和remove函数了。


probe函数:

tu
如上图所示chip_gpio_probe函数,在platform_deviceplatform_driver匹配成功以后,内核会自动调用probe函数:

  • 使用gpiod_get函数,从GPIO子系统中获取引脚信息,存入到全局变量led_gpio中,这是一个struct gpio_desc*结构体,用来描述引脚信息。
  • 使用register_chrdev函数注册file_operations结构体,在注册之前,定义该结构体并且初始化。
  • 创建设备类led_class,并且在/dev目录下创建Big_Miaomi_led0设备节点。

此时file_operations的注册工作放在了probe函数中,不再像之前一样放在驱动成的上层了,所以该结构体的定义也放在这里。

remove函数:

图
如上图所示chip_gpio_remove函数,在卸载驱动程序时,内核会自动调用remove函数:

  • 按照顺序,销毁设备节点led_class,销毁设备类led_class,移除file_operations的注册。卸载顺序和安装时相反。
  • 最后使用gpiod_put释放获取到的GPIO引脚。

open和write:

此时驱动程序的框架是完善了,接下来就是实现file_operations结构体中的openwrite函数了,在应用层调用openwrite系统调用时,会调用到驱动层的这两个函数。

图
如上图led_drv_openled_drv_write代码所示:

  • open:使用GPIO子系统中的gpiod_direction_output函数初始化该引脚的方向,0表示输出。
  • write:使用GPIO子系统中的gpiod_set_value像该引脚写值,status来自应用层,1表示灯亮,0表示灯灭,这是一个逻辑值。

ledtest.c:

体
如上图所示测试程序,仍然使用以前的LED程序,在命令行中输入./ledtest /dev/Big_Miaomi_led0 on灯亮,输入./ledtest /dev/Big_Miaomi_led0 off灯灭。

Makefile:

图
使用上图Makefile文件,将驱动程序应用层测试程序编译成ledtest可执行程序,将驱动程序编译成led_drv.ko文件。

图
如上图所示,将这些文件上传到Linux服务器上进行编译,然后拷贝到网络根文件系统中。

🥅设备树文件

Pinctrl信息:

首先是要使用Pinctrl子系统,将引脚配置为GPIO功能,所以要在我们的设dts设备树文件中创建一个Pinctrl子节点,通过该子节点才能使用到Pinctrl子系统。

但是,这个Pinctrl子节点该怎么写呢?对于IMX6ULL开发板,NXP公司提供了图形化界面工具来配置引脚功能:

图
如上图所示,选择好要使用的引脚后,并且配置和要使用的功能,此时就会生成一个设备树文件代码。

图
如上图所示,会生成一个dtsi文件,该文件中的代码如上,很多都是该开发板中所必须的,但是红色框中的部分是根据我们图形化界面的配置生成的。

  • 将红色框中的代码,复制到开发板所用的dts文件中相应的位置。

图
如上图,打开我们自己的dts设备树文件,找到&iomuxc_snvs节点,在该节点中将前面图形化界面生成的Pinctrl节点代码复制过来,形成一个新的子节点。

  • 节点名称写成Big_Miaomi_leds

如果你使用的开发板没有图形化工具,那么还有几种方式来写Pinctrl节点:

  • 参考芯片厂家提供的文档。
  • 参考别人写的dts文件。
  • 在网上搜索怎么写。

设备节点信息:

虽然使用了Pinctrl子系统,但是真正的LED节点还没有创建,所以在dts设备树文件的根节点下创建LED子节点:

tu
如上图所示,在根节点下创建Big_Miaomi_LED子节点,用来表示LED设备:

  • compatible:属性值必须和驱动程序中of_device_id数组中的值一样,所以是"Big_Miaomi,led_drv",用来匹配驱动程序。
  • pinctrl-names:属性值只有一个default,表示该设备只有一个默认状态。
  • pinctrl-0:属性值是<&Big_Miomi_leds>,表示0号default默认状态,使用前面我们增加的pinctrl节点来实现功能选择,选择为GPIO功能。
  • led-gpios:属性值是<&gpio5 3 GPIO_ACTIVE_LOW>,表示使用GPIO5_3引脚,有效电平是低电平。
    • 使用几个cells来描述引脚是由该组GPIO所在的GPIO子系统中的#gpio-cells属性值决定的,可以查看dtsi文件。

此时我们的设备树文件就写好了,使用Pinctrl子系统选择为GPIO功能,使用GPIO子系统选择GPIO5_3引脚为输出引脚,并且是低电平有效。

图
如上图所示,将设备树文件编译好的dtb文件拷贝到网络根文件目录下。

图
如上图,在开发板上,将我们编译好的dtb文件拷贝到开发板的/boot目录下,然后重启开发板,使用新的设备树文件。

图
如上图,此时在开发板的/sys/firmware/devicetree/base/目录下就有了我们在设备树文件中定义的Big_Miaomi_LED@0 节点。

图
如上图所示,在开发板上挂载网络文件系统后,进入/mnt目录下,安装驱动程序,然后在/dev/路径下就有了字符设备文件Big_Miaomi_led0,再执行测试程序,可以成功控制LED灯的亮灭,本喵这里就不贴图了。

🛷总结

要知道Pinctrl子系统的用来选择复用功能的,GPIO子系统是用来控制某个引脚的,现在不必纠结它的原理,只需要知道怎么用就行。

要会在设备树文件中添加pincontroller节点和设备节点,还要会驱动程序中调用GPIO子系统的标志接口函数来获取引脚信息,以及操作引脚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1366668.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ComfyUI报错AttributeError: module ‘cv2.gapi.wip.draw‘ has no attribute ‘Text‘

ComfyUI在安装comfyui-reactor-node插件,然后启动之后突然报错: AttributeError: module cv2.gapi.wip.draw has no attribute Text 这是怎么回事呢? 于是四处搜寻答案。 总之就是opencv-python版本的问题导致的。 我将有可能解决办法的方法进行了总结。 下面列出所有解…

强化学习的数学原理学习笔记 - 时序差分学习(Temporal Difference)

文章目录 概览&#xff1a;RL方法分类时序差分学习&#xff08;Temporal Difference&#xff0c;TD&#xff09;TD for state values&#x1f7e6;Basic TD&#x1f7e1;TD vs. MC &#x1f7e6;Sarsa (TD for action values)Basic Sarsa变体1&#xff1a;Expected Sarsa变体2&…

动态SLAM 开源方案汇总及介绍(一)

参考https://zhuanlan.zhihu.com/p/673614739及https://zhuanlan.zhihu.com/p/673615788 具体来说&#xff0c;当SLAM系统在前一帧的动态物体上提取了特征点时&#xff0c;如果将这个特征点投影到当前帧&#xff0c;由于目标已经移动&#xff0c;这个点找到的匹配点必然是错误…

【python】爬取知乎热榜Top50保存到Excel文件中【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、导入必要的模块&#xff1a; 这篇博客将介绍如何使用Python编写一个爬虫程序&#xff0c;从斗鱼直播网站上获取图片信息并保存到本地。我们将使用requests模块发送HTTP请求和接收响应&#xff0c;以及os模块处理文件…

MySQL 8.0 InnoDB Tablespaces之Temporary Tablespaces(临时表空间)

文章目录 MySQL 8.0 InnoDB Tablespaces之Temporary Tablespaces&#xff08;临时表空间&#xff09;会话临时表空间会话临时表空间的磁盘分配和回收会话临时表空间的创建创建临时表和查看临时表信息会话临时表空间相关的设置参数innodb_temp_tablespaces_dir 全局临时表空间查…

「MCU」SD NAND芯片之国产新选择优秀

文章目录 前言 传统SD卡和可贴片SD卡 传统SD卡 可贴片SD卡 实际使用 总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以…

RT-Thread 线程管理

线程管理 在日常生活中&#xff0c;我们要完成一个大任务&#xff0c;一般会将它分解成多个简单、容易解决的小问题&#xff0c;小问题逐个被解决&#xff0c;大问题也就随之解决了。 在多线程操作系统中&#xff0c;也同样需要开发人员把一个复杂的应用分解成多个小的、可调…

C#上位机与三菱PLC的通信01--搭建仿真环境

1、三菱PLC介绍 三菱PLC是三菱电机生产的主力产品。 它采用一类可编程的存储器&#xff0c;用于其内部存储程序&#xff0c;执行逻辑运算、顺序控制、定时、计数与算术操作等面向用户的指令&#xff0c;并通过数字或模拟式输入/输出控制各种类型的机械或生产过程。三菱PLC在中国…

软件测试|深入理解Python的encode()和decode()方法

简介 在Python中&#xff0c;字符串是不可变的序列对象&#xff0c;它由Unicode字符组成。当我们需要在字符串和字节之间进行转换时&#xff0c;Python提供了两个非常重要的方法&#xff1a;encode()和decode()。这两个方法允许我们在Unicode字符和字节之间进行相互转换&#…

网络报文分析程序的设计与实现(2024)

1.题目描述 在上一题的基础上&#xff0c;参照教材中各层报文的头部结构&#xff0c;结合使用 wireshark 软件&#xff08;下载地址 https://www.wireshark.org/download.html#releases&#xff09;观察网络各层报文捕获&#xff0c;解析和分析的过程&#xff08;如下 图所示&a…

计算机毕业设计——SpringBoot 个人博客管理系统(附源码)

1&#xff0c;绪论 1.1 背景调研 在互联网飞速发展的今天&#xff0c;互联网已经成为人们快速获取、发布和传递信息的重要渠道&#xff0c;它在人们政治、经济、生活等各个方面发挥着重要的作用。互联网上发布信息主要是通过网站来实现的&#xff0c;获取信息也是要在互联网中…

智能网联汽车安全相关标准汇总

目录 1.标准方向分析 2.智能驾驶域相关标准 3.智能座舱域相关标准 3.汽车通用规范 1.标准方向分析 当前汽车行业的内卷态势已经蔓延至项目立项&#xff0c;导致如今开发模式都尽可能地左移&#xff0c;例如瑞萨提出的虚拟ECU开发模式可以极大节省ECU的实车验证资源&#xf…

Open3D 基于统计滤波去除噪点(5)

Open3D 基于统计滤波去除噪点&#xff08;5&#xff09; 一、什么是统计滤波二、具体实现1.代码 一、什么是统计滤波 统计滤波是一种常用的点云滤波方法&#xff0c;用于去除噪声和异常点。在统计滤波中&#xff0c;通过计算每个点邻域内的统计特征&#xff08;如平均值和标准…

院士专家齐聚 京彩未来联合重点研究院创建数字空间联合实验室

1月6日&#xff0c;京彩未来与北京大学数字中国研究院华南分院暨广东省数字广东研究院共同创建的“数字空间共同体联合室验室”正式挂牌运营。 著名经济学家管清友博士、北京大学数字中国研究院华南分院暨广东省数字广东研究院常务副院长李鹰教授&#xff0c;广东省数字广东研…

MFC Socket和合信CTMC M266ES 运动控制型PLC通信进行数据交换

前言 1、前两篇文章通过对Snap7和S7-1200/S7-1500PLC的通信进行了详细的介绍。Snap7的优点开源性强、使用方便易于上手&#xff0c;跨平台和可移植性性强。但是Snap7也有个缺点就是只能访问PLC的DB、MB、I、Q区进行数据读写&#xff0c;不能对V区进行读写,有人说可以读写V区&am…

Spring AOP(详解)

目录 1.AOP概述 2.AOP相关术语 3.Spring AOP的原理机制 3.1JDK动态代理 3.2 CGLIB动态代理 3.3简单代码展示 3.3.1JDK动态代理 3.3.2CGLIB动态代理 4.Spring的AOP配置 4.1pom.xml 4.2增强方法 4.3切点 4.4切面 5.基于注解的AOP配置 5.1.创建工程 5.2.增强 5.3AOP…

抖去推账号矩阵+无人直播+文案引流系统开发搭建--开源

核心技术 1. AI自动直播&#xff1a; 智能系统通过丰富可定制的文案库&#xff0c; 拥有有料有趣的灵魂。不仅能自动语音讲解内容&#xff0c;还可以在直播中和用户灵活互动。直播中可将团购商品同话术自动上下架。 2. AI剪辑 可一键智能批量成片&#xff0c;也可跟着模板剪…

苹果IOS如何支持微信小程序分享

各位同学们好&#xff01;我是咕噜铁蛋&#xff01;&#xff0c;我们经常需要与读者分享有关移动应用的使用方法和技巧。微信小程序是一种便捷的应用形式&#xff0c;可以在微信内部直接使用&#xff0c;而无需下载和安装。本文铁蛋讲详细介绍iOS苹果支持微信小程序类型分享的使…

简单几步,实现餐厅扫码点餐

越来越多的人选择外出就餐&#xff0c;而餐厅的点餐方式也随着科技的发展而不断进步。其中&#xff0c;扫码点餐是最为常见的一种方式&#xff0c;它不仅方便快捷&#xff0c;还能节省人力成本。本文将介绍一种简单易行的餐厅扫码点餐解决方案。 打开乔拓云平台&#xff0c;登录…

【React】02-如何理解React通过对DOM的模拟,最大限度地减少与DOM的交互

如何理解React通过对DOM的模拟&#xff0c;最大限度地减少与DOM的交互 背景分析关于虚拟DOM 背景 在学习React的过程中&#xff0c;发现很多文档上关于React的高效都有这么一句话的描述——React通过对DOM的模拟&#xff0c;最大限度地减少与DOM的交互&#xff0c;对于我这种前…