把嵌入式设备驱动比相亲,那总线就是红娘,设备是男方,驱动是女方.....

传感器技术 2020-03-02 00:00


一、platform 驱动的工作过程


platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations(他包含的操作函数接口)、dev_t(设备号)、设备文件(/dev)等,因为用platform机制编写的字符驱动,它的本质是字符驱动。


我们要记住,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳。


在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:


a -- 实现platform驱动 

b -- 实现platform设备


然而在实现这两个工作的过程中还需要实现其他的很多小工作,在后面介绍。platform模型驱动的实现过程核心架构就很简单,如下所示:

 

platform驱动模型三个对象:platform总线、platform设备、platform驱动。

platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)

platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregister)

platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregister)

       

那具体platform驱动的工作过程是什么呢:


设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;


如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;


如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。

 


二、实现platform 驱动与设备的详细过程




1、思考问题?

在分析platform 之前,可以先思考一下下面的问题:

a -- 为什么要用 platform 驱动?不用platform驱动可以吗?

b -- 设备驱动中引入platform 概念有什么好处?


现在先不回答,看完下面的分析就明白了,后面会附上总结。

 


2、platform_device 结构体 VS   platform_driver 结构体

这两个结构体分别描述了设备和驱动,二者有什么关系呢?先看一下具体结构体对比



前面提到,实现platform模型的过程就是总线对设备和驱动的匹配过程 。打个比方,就好比相亲,总线是红娘,设备是男方,驱动是女方:


a -- 红娘(总线)负责男方(设备)和女方(驱动)的撮合;    

b -- 男方(女方)找到红娘,说我来登记一下,看有没有合适的姑娘(汉子)—— 设备或驱动的注册

c -- 红娘这时候就需要看看有没有八字(二者的name 字段)匹配的姑娘(汉子)——match 函数进行匹配,看name是否相同;

d -- 如果八字不合,就告诉男方(女方)没有合适的对象,先等着,别急着乱做事 —— 设备和驱动会等待,直到匹配成功;

e -- 终于遇到八字匹配的了,那就结婚呗!接完婚,男方就向女方交代,我有多少存款,我的房子在哪,钱放在哪等等( struct resource    *resource),女方说好啊,于是去房子里拿钱,去给男方买菜啦,给自己买衣服、化妆品、首饰啊等等(int (*probe)(struct platform_device *) 匹配成功后驱动执行的第一个函数),当然如果男的跟小三跑了(设备卸载),女方也不会继续待下去的(  int (*remove)(struct platform_device *))。

 


3、设备资源结构体

在struct platform_device 结构体中有一重要成员 struct resource *resource


[cpp] view plain copy

1. struct resource {  

2.     resource_size_t start;  资源起始地址     

3.     resource_size_t end;   资源结束地址  

4.     const char *name;        

5.     unsigned long flags;   区分是资源什么类型的  

6.     struct resource *parent, *sibling, *child;  

7. };  

8.   

9. #define IORESOURCE_MEM        0x00000200  

10. #define IORESOURCE_IRQ        0x00000400     


flags 指资源类型,我们常用的是 IORESOURCE_MEM、IORESOURCE_IRQ  这两种。start 和 end 的含义会随着 flags而变更,如


a -- flags为IORESOURCE_MEM 时,start 、end 分别表示该platform_device占据的内存的开始地址和结束值; 

b -- flags为 IORESOURCE_IRQ   时,start 、end 分别表示该platform_device使用的中断号的开始地址和结束值; 


下面看一个实例:


[cpp] view plain copy

1. static struct  resource beep_resource[] =  

2. {  

3.     [0] = {  

4.             .start = 0x114000a0,  

5.         .end = 0x114000a0+0x4,  

6.             .flags = IORESOURCE_MEM,  

7.     },  

8.   

9.     [1] = {  

10.             .start = 0x139D0000,  

11.             .end = 0x139D0000+0x14,  

12.             .flags = IORESOURCE_MEM,  

13.     },  

14. };   


好酒好礼,五粮液十五酱!(点击进入)




4、将字符设备添加到platform的driver中


前面我们提到platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳,下面我们看一下添加的过程:


[cpp] view plain copy

1. static struct file_operations hello_ops=  

2. {  

3.     .open = hello_open,  

4.     .release = hello_release,  

5.     .unlocked_ioctl = hello_ioctl,  

6. };  

7.   

8. static int hello_remove(struct platform_device *pdev)  

9. {  

10.     注销分配的各种资源  

11. }  

12.   

13. static int hello_probe(struct platform_device *pdev)  

14. {  

15.     1.申请设备号  

16.     2.cdev初始化注册,&hello_ops  

17.     3.从pdev读出硬件资源  

18.     4.对硬件资源初始化,ioremap,request_irq( )  

19. }  

20.   

21. static int hello_init(void)  

22. {  

23.     只注册 platform_driver  

24. }  

25.   

26. static void hello_exit(void)  

27. {  

28.     只注销 platform_driver  

29. }  


可以看到,模块加载和卸载函数仅仅通过paltform_driver_register()、paltform_driver_unregister() 函数进行 platform_driver 的注册和注销,而原先注册和注销字符设备的工作已经被移交到 platform_driver 的 probe() 和 remove() 成员函数中。



5、platform是如何匹配device和driver


这时就该总线出场了,系统为platform总线定义了一个bus_type 的实例platform_bus_type,其定义如下:


[cpp] view plain copy

1. struct bus_type platform_bus_type = {  

2.     .name        = "platform",  

3.     .dev_groups    = platform_dev_groups,  

4.     .match        = platform_match,  

5.     .uevent        = platform_uevent,  

6.     .pm        = &platform_dev_pm_ops,  

7. };  


其又是怎样工作的呢?在platform.c (e:\linux-3.14-fs4412\drivers\base)    31577    2014/3/31 中可以看到



[cpp] view plain copy

1. __platform_driver_register()  

2. {  

3.     drv->driver.bus = &platform_bus_type;     536行  

4. }  



在platform_bus_type 中调用 了platform_match:


[cpp] view plain copy

1. static int platform_match(struct device *dev, struct device_driver *drv)  

2. {  

3.     struct platform_device *pdev = to_platform_device(dev);  

4.     struct platform_driver *pdrv = to_platform_driver(drv);  

5.   

6.     匹配设备树信息,如果有设备树,就调用 of_driver_match_device() 函数进行匹配  

7.     if (of_driver_match_device(dev, drv))  

8.         return 1;  

9.   

10.   

11.     匹配id_table  

12.     if (pdrv->id_table)  

13.         return platform_match_id(pdrv->id_table, pdev) != NULL;  

14.   

15.     最基本匹配规则  

16.     return (strcmp(pdev->name, drv->name) == 0);  

17. }  



 6、解决问题

现在可以回答这两个问题了

a -- 为什么要用 platform 驱动?不用platform驱动可以吗?

b -- 设备驱动中引入platform 概念有什么好处?


引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;


设备驱动中引入platform 概念,隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体匹配信息,而在驱动中,只需要通过API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

 


三、实例



这是一个蜂鸣器的驱动,其实前面已经有解析 Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动, 下面来看一下,套上platform 外壳后的程序:



1、device.c



[cpp] view plain copy

1. #include <linux/module.h>  

2. #include <linux/device.h>  

3. #include <linux/platform_device.h>  

4. #include <linux/ioport.h>  

5.   

6. static struct resource beep_resource[] =  

7. {  

8.     [0] ={  

9.         .start = 0x114000a0,  

10.         .end =  0x114000a0 + 0x4,  

11.         .flags = IORESOURCE_MEM,  

12.     },  

13.   

14.     [1] ={  

15.         .start = 0x139D0000,  

16.         .end =  0x139D0000 + 0x14,  

17.         .flags = IORESOURCE_MEM,  

18.     }  

19. };  

20.   

21. static void hello_release(struct device *dev)  

22. {  

23.     printk("hello_release\n");  

24.     return ;  

25. }  

26.   

27.   

28.   

29. static struct platform_device hello_device=  

30. {  

31.     .name = "bigbang",  

32.     .id = -1,  

33.     .dev.release = hello_release,  

34.     .num_resources = ARRAY_SIZE(beep_resource),  

35.     .resource = beep_resource,  

36. };  

37.   

38. static int hello_init(void)  

39. {  

40.     printk("hello_init");  

41.     return platform_device_register(&hello_device);  

42. }  

43.   

44. static void hello_exit(void)  

45. {  

46.     printk("hello_exit");  

47.     platform_device_unregister(&hello_device);  

48.     return;  

49. }  

50.   

51. MODULE_LICENSE("GPL");  

52. module_init(hello_init);  

53. module_exit(hello_exit);  


 


2、driver.c



[cpp] view plain copy

1. #include <linux/module.h>  

2. #include <linux/fs.h>  

3. #include <linux/cdev.h>  

4. #include <linux/device.h>  

5. #include <linux/platform_device.h>  

6. #include <asm/io.h>  

7.   

8. static int major = 250;  

9. static int minor=0;  

10. static dev_t devno;  

11. static struct class *cls;  

12. static struct device *test_device;  

13.            

14. #define TCFG0         0x0000                 

15. #define TCFG1         0x0004                              

16. #define TCON          0x0008               

17. #define TCNTB0        0x000C            

18. #define TCMPB0        0x0010             

19.   

20. static unsigned int *gpd0con;  

21. static void *timer_base;  

22.   

23. #define  MAGIC_NUMBER    'k'  

24. #define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)  

25. #define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)  

26. #define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)  

27.   

28. static void fs4412_beep_init(void)  

29. {     

30.     writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);  

31.     writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);   

32.     writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );   

33.   

34.     writel (500, timer_base +TCNTB0  );  

35.     writel (250, timer_base +TCMPB0 );  

36.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );   

37. }  

38.   

39. void fs4412_beep_on(void)  

40. {  

41.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );  

42. }  

43.   

44. void fs4412_beep_off(void)  

45. {  

46.     writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );  

47. }  

48.   

49. static void beep_unmap(void)  

50. {  

51.         iounmap(gpd0con);  

52.         iounmap(timer_base);  

53. }  

54.   

55. static int beep_open (struct inode *inode, struct file *filep)  

56. {  

57.     fs4412_beep_on();  

58.     return 0;  

59. }  

60.   

61. static int beep_release(struct inode *inode, struct file *filep)  

62. {  

63.      fs4412_beep_off();  

64.      return 0;  

65. }  

66.   

67. #define BEPP_IN_FREQ 100000  

68. static void beep_freq(unsigned long arg)  

69. {  

70.     writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );  

71.     writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );  

72.   

73. }  

74.   

75. static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)  

76. {  

77.     switch(cmd)  

78.     {  

79.         case BEEP_ON:  

80.             fs4412_beep_on();  

81.             break;  

82.         case BEEP_OFF:  

83.             fs4412_beep_off();  

84.             break;  

85.         case BEEP_FREQ:  

86.             beep_freq( arg );  

87.             break;  

88.         default :  

89.             return -EINVAL;  

90.     }  

91.     return 0;  

92. }  

93.   

94. static struct file_operations beep_ops=  

95. {  

96.     .open     = beep_open,  

97.     .release = beep_release,  

98.     .unlocked_ioctl      = beep_ioctl,  

99. };  

100.   

101. static int beep_probe(struct platform_device *pdev)  

102. {  

103.     int ret;      

104.     printk("match ok!");  

105.       

106.     gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);  

107.     timer_base = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start);  

108.   

109.     devno = MKDEV(major,minor);  

110.     ret = register_chrdev(major,"beep",&beep_ops);  

111.   

112.     cls = class_create(THIS_MODULE, "myclass");  

113.     if(IS_ERR(cls))  

114.     {  

115.         unregister_chrdev(major,"beep");  

116.         return -EBUSY;  

117.     }  

118.   

119.     test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello  

120.     if(IS_ERR(test_device))  

121.     {  

122.         class_destroy(cls);  

123.         unregister_chrdev(major,"beep");  

124.         return -EBUSY;  

125.     }  

126.       

127.     fs4412_beep_init();  

128.       

129.     return 0;  

130. }  

131.   

132. static int beep_remove(struct platform_device *pdev)  

133. {  

134.     beep_unmap();  

135.     device_destroy(cls,devno);  

136.     class_destroy(cls);   

137.     unregister_chrdev(major,"beep");  

138.   

139.     return 0;  

140. }  

141.   

142.   

143. static struct platform_driver beep_driver=  

144. {  

145.     .driver.name = "bigbang",  

146.     .probe = beep_probe,  

147.     .remove = beep_remove,  

148. };  

149.    

150.   

151. static int beep_init(void)  

152. {  

153.     printk("beep_init");  

154.       

155.     return platform_driver_register(&beep_driver);  

156. }  

157.   

158. static void beep_exit(void)  

159. {  

160.     printk("beep_exit");  

161.     platform_driver_unregister(&beep_driver);  

162.       

163.     return;  

164. }  

165.   

166.   

167. MODULE_LICENSE("GPL");  

168. module_init(beep_init);  

169. module_exit(beep_exit); 




3、makefile   



[cpp] view plain copy

1. ifneq  ($(KERNELRELEASE),)  

2. obj-m:=device.o driver.o  

3. $(info "2nd")  

4. else  

5. #KDIR := /lib/modules/$(shell uname -r)/build  

6. KDIR := /home/fs/linux/linux-3.14-fs4412  

7. PWD:=$(shell pwd)  

8. all:  

9.     $(info "1st")  

10.     make -C $(KDIR) M=$(PWD) modules  

11. clean:  

12.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  

13. endif  


 


4、test.c




[cpp] view plain copy

1. #include <sys/types.h>  

2. #include <sys/stat.h>  

3. #include <fcntl.h>  

4. #include <stdio.h>  

5.   

6. main()  

7. {  

8.     int fd,i,lednum;  

9.   

10.     fd = open("/dev/beep",O_RDWR);  

11.     if(fd<0)  

12.     {  

13.         perror("open fail \n");  

14.         return ;  

15.     }  

16.       

17.     sleep(10);  

18.     close(fd);  

19. } 


  来源:玩转单片机


免责声明:本文系网络转载,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请第一时间告知,我们将根据您提供的证明材料确认版权并按国家标准支付稿酬或立即删除内容!本文内容为原作者观点,并不代表本公众号赞同其观点和对其真实性负责。

 
  

为您发布产品,请点击“阅读原文”

传感器技术 制造业的未来是智能化,智能化的基础就是传感器; 互联网的方向是物联网,物联网的基石也是传感器; 关注传感器技术,获得技术资讯、产品应用、市场机会,掌握最黑科技,为中国工业导航。
评论
  • 随着各行各业对可靠、高效电子元件的需求不断增长,国产光耦合器正成为全球半导体市场的重要参与者。这些元件利用先进的制造工艺和研究驱动的创新,弥补了高性能和可负担性之间的差距。本文探讨了国产光耦合器日益突出的地位,重点介绍了其应用和技术进步。 关键技术进步国产光耦合器制造商在提高性能和多功能性方面取得了重大进展。高速光耦合器现在能够处理快速数据传输,使其成为电信和工业自动化中不可或缺的一部分。专为电力电子设计的栅极驱动器光耦合器可确保电动汽车和可再生能源逆变器等高压系统的精确控制。采用碳化
    克里雅半导体科技 2024-12-06 16:34 53浏览
  • DIP开关(双列直插式封装开关)是一种常见的机电设备,广泛应用于各种电子设计中。其多样的形式、配置、尺寸和开关机制使得设计师可以灵活地满足不同的需求。什么是DIP开关?DIP开关由多个开关单元组成,通常并排排列(图1)。这些开关需要手动操作以设置其功能。它们主要安装在PCB上,用于配置设备的操作模式。DIP开关以其可靠性、灵活性和经济性成为原始设备制造商(OEM)和最终客户的首选之一。图1.常见的DIP开关配置极点与掷数:DIP开关的基本规格极点(Pole): 指开关的输入数量。掷数(
    大鱼芯城 2024-12-06 10:36 46浏览
  • 光耦合器以其提供电气隔离的能力而闻名,广泛应用于从电源到通信系统的各种应用。尽管光耦合器非常普遍,但人们对其特性和用途存在一些常见的误解。本文将揭穿一些最常见的误解,以帮助工程师和爱好者做出更明智的决策。 误解1:光耦合器的使用寿命较短事实:虽然光耦合器内部的LED会随着时间的推移而退化,但LED材料和制造工艺的进步已显著提高了其使用寿命。现代光耦合器的设计使用寿命为正常工作条件下的数十年。适当的热管理和在推荐的电流水平内工作可以进一步延长其使用寿命。误解2:光耦合器对于现代应用来说太
    腾恩科技-彭工 2024-12-06 16:29 56浏览
  • CS5466AUUSB-C  (2lanes)to HDMI2.1 8K@30HZ(4K@144) +PD3.1  CS5563DP  (4lanes) to HDMI2.1 10k@60Hz CS5565USB-C  (4lanes) to HDMI2.1 10k@60Hz CS5569USB-C (4lanes) to HDMI2.1 10k@60Hz +PD3.1CS5228ANDP++ to HDMI(4K
    QQ1540182856 2024-12-05 15:56 90浏览
  • ~同等额定功率产品尺寸小一号,并保证长期稳定供应~全球知名半导体制造商ROHM(总部位于日本京都市)在其通用贴片电阻器“MCR系列”产品阵容中又新增了助力应用产品实现小型化和更高性能的“MCRx系列”。新产品包括大功率型“MCRS系列”和低阻值大功率型“MCRL系列”两个系列。在电子设备日益多功能化和电动化的当今世界,电子元器件的小型化和性能提升已成为重要课题。尤其是在汽车市场,随着电动汽车(xEV)的普及,电子元器件的使用量迅速增加。另外,在工业设备市场,随着设备的功能越来越多,效率越来越高,
    电子资讯报 2024-12-05 17:03 73浏览
  • RK3506单板机(卡片电脑)是一款高性能三核Cortex-A7处理器,内部集成Cortex-M0核心,RK3506单板机具有接口丰富、实时性高、显示开发简单、低功耗及多系统支持等特点,非常适合于工业控制、工业通信、人机交互等应用场景。 多核异构3xCortex-A7+Cortex-M0 外设接口丰富,板载网络、串口、CAN总线 支持Buildroot、Yocto系统,支持AMP混合部署 支持2D硬件加速,适用于轻量级HMI目前RK3506主要分为3种型号
    万象奥科 2024-12-05 16:59 76浏览
  • 光耦合器对于确保不同电路部分之间的电气隔离和信号传输至关重要。通过防止高压干扰敏感元件,它们可以提高安全性和可靠性。本指南将指导您使用光耦合器创建一个简单的电路,介绍其操作的基本原理和实际实施。光耦合器的工作原理光耦合器包含一个LED和一个光电晶体管。当LED接收到信号时,它会发光,激活光电晶体管,在保持隔离的同时传输信号。这使其成为保护低功耗控制电路免受高压波动影响的理想选择。组件和电路设置对于这个项目,我们将使用晶体管输出光耦合器(例如KLV2002)。收集以下组件:光耦合器、1kΩ电阻(输
    克里雅半导体科技 2024-12-06 16:34 57浏览
  • 学习如何在 MYIR 的 ZU3EG FPGA 开发板上部署 Tiny YOLO v4,对比 FPGA、GPU、CPU 的性能,助力 AIoT 边缘计算应用。(文末有彩蛋)一、 为什么选择 FPGA:应对 7nm 制程与 AI 限制在全球半导体制程限制和高端 GPU 受限的大环境下,FPGA 成为了中国企业发展的重要路径之一。它可支持灵活的 AIoT 应用,其灵活性与可编程性使其可以在国内成熟的 28nm 工艺甚至更低节点的制程下实现高效的硬件加速。米尔的 ZU3EG 开发板凭借其可重
    米尔电子嵌入式 2024-12-06 15:53 45浏览
  • 在阅读了《高速PCB设计经验规则应用实践》后,对于PCB设计的布局经验有了更为深入和系统的理解。该书不仅详细阐述了高速PCB设计中的经验法则,还通过实际案例和理论分析,让读者能够更好地掌握这些法则并将其应用于实际工作中。布局是走线的基础,预先的规划再到叠层的选择,电源和地的分配,信号网络的走线等等,对布局方面也是非常的关注。布局规划的重要性: 在PCB设计中,布局规划是至关重要的一步。它直接影响到后续布线的难易程度、信号完整性以及电磁兼容性等方面。因此,在进行元件布局之前,我们必须对PCB的平
    戈壁滩上绽放 2024-12-05 19:43 89浏览
  • 应用环境与客户需求蓝牙设备越来越普及,但在高密度使用环境下,你知道里面潜藏的风险吗?用户在使用蓝牙配件(如键盘、鼠标和耳机)时,经常面临干扰问题,这主要是因为蓝牙设备使用的2.4GHz频段与许多其他无线设备(如Wi-Fi、Thread等)重迭,导致频段拥挤,进而增加干扰的可能性。【常见干扰情境】客服中心:客服中心通常有大量的工作站,每个工作站可能都配备有蓝牙键盘、鼠标和耳机。由于这些设备都使用4GHz频段,客服中心内部的频段拥挤会增加讯号干扰的可能性。再加上中心内部可能有多个无线网络设备和其他电
    百佳泰测试实验室 2024-12-05 16:17 76浏览
  • 自20世纪60年代问世以来,光耦合器彻底改变了电子系统实现电气隔离和信号传输的方式。通过使用光作为传输信号的媒介,光耦合器消除了直接电气连接的需求,确保了安全性和可靠性。本文记录了光耦合器技术的发展,重点介绍了关键创新、挑战以及这一不可或缺组件的未来发展。 过去:起源和早期应用光耦合器的发明源于处理高压或嘈杂环境的系统对安全电气隔离的需求。早期的光耦合器由LED和光电晶体管的简单组合组成,可提供可靠的隔离,但具有明显的局限性:低速:早期的光耦合器速度慢,频率响应有限,不适合高速数字通信
    腾恩科技-彭工 2024-12-06 16:28 51浏览
  • 在Python中,线程的启动和管理是一个复杂而关键的过程。通过上述代码截图,我们可以深入了解Python中线程启动和处理的具体实现。以下是对图1中内容的详细解析: 4.3.3、启动线程 当调用`rt_thread_startup`函数时,该函数负责将指定线程的状态更改为就绪状态,并将其放入相应优先级的队列中等待调度。这一步骤确保了线程能够被操作系统识别并准备执行。如果新启动的线程的优先级高于当前正在运行的线程,系统将立即切换到这个高优先级线程,以保证重要任务的及时执行。 //```c /*
    丙丁先生 2024-12-06 12:30 39浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦