




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式。相关通讯设备可工作于m/s模式。主设备发起数据帧,允许多个从设备的存在。每个从设备有独立的片选信号,SPI一般来说是四线串行总线结构。接口:SCLKSerial Clock(output from master时钟(主设备发出MOSI/SIMOMaster Output, Slave Input(output from master数据信号线mosi(主设备发出MISO/SOMIMaster Input,Slave Outpu(output from slave数据信号线(从设备SSSlave Select(act
2、ive low;output from master片选信号下面来看一下Linux中的SPI驱动。在Linux设备驱动框架的设计中,有一个重要的主机,外设驱动框架分离的思想,如下图。外设a,b,c的驱动与主机控制器A,B,C的驱动不相关,主机控制器驱动不关心外设,而外设驱动也不关心主机,外设只是访问核心层的通用的API进行数据的传输,主机和外设之间可以进行任意的组合。如果我们不进行如图的主机和外设分离,外设a,b,c和主机A,B,C进行组合的时候,需要9种不同的驱动。设想一共有个主机控制器,n个外设,分离的结构是需要m+n个驱动,不分离则需要m*n个驱动。下面介绍spi子系统的数据结构:在Li
3、nux中,使用spi_master结构来描述一个SPI主机控制器的驱动。view plain1. "font-size:18px;">struct spi_master 2. struct device dev;/*总线编号,从0开始*/ 3. s16 bus_num;/*支持的片选的数量,从设备的片选号不能大于这个数量*/ 4. u16 num_chipselect;&
4、#160; 5. u16 dma_alignment;/*改变spi_device的特性如:传输模式,字长,时钟频率*/ 6. int (*setup(struct spi_device *spi;/*添加消息到队列的方法,这个函数不可睡眠,他的任务是安排发生的传送并且调用注册的回调函数complete(*/ 7. int (*transfer(struct spi_device *spi,struct spi_message
5、*mesg; 8. void (*cleanup(struct spi_device *spi; 9. ; 分配,注册和注销的SPI主机的API由SPI核心提供:view plain1. struct spi_master *spi_alloc_master(struct device *host, unsigned size; 2. int spi_register_master(s
6、truct spi_master *master; 3. void spi_unregister_master(struct spi_master *master; 在Linux中用spi_driver来描述一个SPI外设驱动。view plain1. struct spi_driver 2. int (*probe(struct spi_device *spi;
7、60;3. int (*remove(struct spi_device *spi; 4. void (*shutdown(struct spi_device *spi; 5. int (*suspend(struct spi_device *spi, pm_message_t mesg; 6. int (*resume(str
8、uct spi_device *spi; 7. struct device_driver driver; 8. ; 可以看出,spi_driver结构体和platform_driver结构体有极大的相似性,都有probe(,remove(,suspend(,resume(这样的接口。Linux用spi_device来描述一个SPI外设设备。view plain1. struct spi_device 2. struct
9、160;device dev; 3. struct spi_master *master; /对应的控制器指针u32 4. max_speed_hz; /spi通信的时钟u8
10、0; 5. chip_select; /片选,用于区分同一总线上的不同设备 6. u8 mode; 7. #define SPI_CPHA 0x01 /* clock phase */ 8. #defin
11、e SPI_CPOL 0x02 /* clock polarity */ 9. #define SPI_MODE_0 (0|0 /* (ori
12、ginal MicroWire */#define SPI_MODE_1 (0|SPI_CPHA 10. #define SPI_MODE_2 (SPI_CPOL|0 11. #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA#define SPI_CS_HIGH 0x04 &
13、#160; /* chipselect active high? */ 12. #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ 13. #define
14、60; SPI_3WIRE 0x10 /* SI/SO signals shared */ 14. #define SPI_LOOP 0x20
15、; /* loopback mode */ 15. u8 bits_per_word; /每个字长的比特数 16. int irq; /使用的中断
16、60;17. void *controller_state; 18. void *controller_data; 19. char modalias32; /名字 20. ; 如下图,看这三个结构的关系,这里spi_device与spi_master是同一个父设备,
17、这是在spi_new_device函数中设定的,一般这个设备是一个物理设备。这里的spi_master_class,spi_bus_type又是什么呢,看下边两个结构体:view plain1. struct bus_type spi_bus_type = 2. .name = "spi", 3.
18、60;.dev_attrs = spi_dev_attrs, 4. .match = spi_match_device, 5. .uevent = spi_uevent, 6. .suspend = spi_suspend, 7
19、. .resume = spi_resume, 8. ; 9. static struct class spi_master_class = 10. .name &
20、#160;= "spi_master", 11. .owner = THIS_MODULE, 12. .dev_release = spi_master_release, 13. ;
21、60; spi_bus_type对应spi中的spi bus总线,spidev的类定义如下:view plain1. static struct class *spidev_class; 创建这个类的主要目的是使mdev/udev能在/dev下创建设备节点/dev/spiB.C。B代表总线,C代表片外设备的片选号。下边来看两个板级的结构,其中spi_board_info用来初始化spi_device,s3c2410_spi_info用来初始化spi_master。这两个板级的结构需要在移植的时候在arch/a
22、rm/mach-s3c2440/mach-smdk2440.c中初始化。view plain1. struct spi_board_info 2. char modalias32; /设备与驱动匹配的唯一标识 3. const void *platform_data; 4. void *controlle
23、r_data; 5. int irq; 6. u32 max_speed_hz; 7. u16 bus_num; /设备所归属的总线编号 8. u16
24、; chip_select; 9. u8 mode; 10. ; 11. struct s3c2410_spi_info 12. int pin_cs; /芯片选择管脚 13. unsigned
25、60;int num_cs; /总线上的设备数 14. int bus_num; /总线号 15. void (*gpio_setup(struct s3c2410_spi_info *
26、spi, int enable; /spi管脚配置函数 16. void (*set_cs(struct s3c2410_spi_info *spi, int cs, int pol; 17. ; boardinfo是用来管理spi_board_info的结构,spi_board_info通过spi_register_board_info(struct spi
27、_board_info const *info, unsigned n交由boardinfo来管理,并挂到board_list链表上,list_add_tail(&bi->list,&board_list;view plain1. struct boardinfo 2. /*用于挂到链表头board_list上*/ 3. struct list_head list; 4. /*管理的spi_board_info的数量*/
28、; 5. unsigned n_board_info; 6. /*存放结构体spi_board_info*/ 7. struct spi_board_info board_info0; 8. ; s3c24xx_spi是S3C2440的SPI控制器在Linux内核中的具体描述,该结构包含spi_bitbang内嵌结构,控制器时钟频率和占用的中断资源等重要成员,其中spi_bitbang具体负责SPI数据的传输
29、。view plain1. struct s3c24xx_spi 2. /* bitbang has to be first */ 3. struct spi_bitbang bitbang; 4. struct completion done; 5. void _iomem
30、0;*regs; 6. int irq; 7. int len; 8. int co
31、unt; 9. void (*set_cs(struct s3c2410_spi_info *spi, int cs, int pol; 10. /* data buffers */const unsigned char *tx; 11. unsigned char
32、0; *rx; 12. struct clk *clk; 13. struct resource *ioarea; 14. struct spi_master *master; 15. struct spi_devi
33、ce *curdev; 16. struct device *dev; 17. struct s3c2410_spi_info *pdata; 18. ; 为了解决多个不同的SPI设备共享SPI控制器而带来的访问冲突,spi_bitbang使用内核提供的工作队列(workqueue。workqueue是Linux内核中定义的一种回调处理方式。采用这种方式需要传输数
34、据时,不直接完成数据的传输,而是将要传输的工作分装成相应的消息(spi_message,发送给对应的workqueue,由与workqueue关联的内核守护线程(daemon负责具体的执行。由于workqueue会将收到的消息按时间先后顺序排列,这样就是对设备的访问严格串行化,解决了冲突。view plain1. "font-size:18px;">struct spi_bitbang 2. struct workqueue_struct *workqueue;
35、0; /工作队列头 3. struct work_struct work; /每一次传输都传递下来一个spi_message,都向工作队列头添加一个 4. workspinlock_t lock; 5. struct list_hea
36、d queue; /挂接spi_message,如果上一次的spi_message还没有处理完,接下来的spi_message就挂接在queue上等待处理 6. u8 busy;
37、; /忙碌标志 7. u8 use_dma; 8. u8 flags; 9. struct spi_master *master;/*一下3个函数都是在函数s3c24xx_spi_probe(中被初始化*/ &
38、#160;10. int (*setup_transfer(struct spi_device *spi,struct spi_transfer *t; /设置传输模式 11. void (*chipselect(struct spi_device *spi, int is_on;
39、60; /片选 12. #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ 13. #define BITBANG_CS_INACTIVE 0/*传输函数,由s3c
40、24xx_spi_txrx来实现*/ 14. int (*txrx_bufs(struct spi_device *spi, struct spi_transfer *t; 15. u32 (*txrx_word4(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits; 16. ;
41、160; 下面来看看spi_message:view plain1. struct spi_message 2. struct list_head transfers; /此次消息的传输队列,一个消息可以包含多个传输段 3. struct spi_device *spi; /传输的目的设备 4.
42、unsigned is_dma_mapped:1; /如果为真,此次调用提供dma和cpu虚拟地址 5. void (*complete(void *context; /异步调用完成后的回调函数 6. void
43、0;*context; /回调函数的参数 7. unsigned actual_length;
44、0;/此次传输的实际长度 8. int status; /执行的结果,成功被置0,否则是一个负的错误码 9. struct list_head
45、queue; 10. void *state; 11. ; 在有消息需要传递的时候,会将spi_transfer通过自己的transfer_list字段挂到spi_message的transfers链表头上。spi_message用来原子的执行spi_transfer表示的一串数组传输请求。这个传输队列是原子的,这意味着在这个消息完成之前不会有其他消息占用总线。消息的执行总是按照FIFO的
46、顺序。下面看一看spi_transfer:view plain1. struct spi_transfer 2. const void *tx_buf; /要写入设备的数据(必须是dma_safe,或者为NULL 3. void *rx_buf; /要读取的数据缓冲(必须是dma_safe,或者为NULL 4. unsigned
47、len; /tx和rx的大小(字节数,这里不是指它的和,而是各自的长度,他们总是相等的 5. dma_addr_t tx_dma; /如果spi_message.is_dma_mapped是真,这个是tx的dma地址 6. dma_addr_t rx_dma; /如果spi_message.is_dma_mapped是真,这个是rx的dma地址
48、160;7. unsigned cs_change:1; /影响此次传输之后的片选,指示本次tranfer结束之后是否要重新片选并调用setup改变设置,这个标志可以较少系统开销u8 8. bits_per_word; /每个字长的比特数,如果是0,使用默认值 9. u16 delay_usecs;
49、 /此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息 10. u32 speed_hz; /通信时钟。如果是0,使用默认值 11. struct list_head transfer_list; /用来连接的双向链表节点 12. ; 嵌入式微处理器访问SPI设
50、备有两种方式:使用GPIO模拟SPI接口的工作时序或者使用SPI控制器。使用GPIO模拟SPI接口的工作时序是非常容易实现的,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低,容易成为系统瓶颈。这里主要分析使用SPI控制器的情况。这个是由sys文件系统导出的spi子系统在内核中的视图了。首先了解一下Linux内核中的几个文件:spi.c也就是spi子系统的核心了,spi_s3c24xx.c是s3c24xx系列芯片的SPI controller驱动,它向更上层的SPI核心层(spi.c提供接口用来控制芯片的SPI controller,是一个被其他驱动使用的驱动。而spidev.
51、c是在核心层基础之上将SPI controller模拟成一个字符型的驱动,向文件系统提供标准的文件系统接口,用来操作对应的SPI controller。下面我们来看看spi子系统是怎么注册进内核的:view plain1. static int _init spi_init(void 2. 3. int status; 4. buf = kmalloc(SPI_BUFSIZ
52、, GFP_KERNEL; 5. if (!buf 6. status = -ENOMEM; 7. goto err0; 8. 9.
53、160; status = bus_register(&spi_bus_type; 10. if (status < 0 11. goto err1; 12. status = class_register(&am
54、p;spi_master_class; 13. if (status < 0 14. goto err2; 15. return 0; 16. err2: 17. bus_unregister(
55、&spi_bus_type; 18. err1: 19. kfree(buf; 20. buf = NULL; 21. err0: 22. return status; 23. 24. postcore_initcall(spi_init;
56、;这里注册了一个spi_bus_type,也就是一个spi总线,和一个spi_master的class。分别对应上图中sys/bus/下的spi目录和sys/class/下的spi_master目录。下面来分析SPI controller驱动的注册与初始化过程,首先执行的是s3c24xx_spi_init。view plain1. static int _init s3c24xx_spi_init(void 2. 3. re
57、turn platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe; 4. platform_driver_probe中完成了s3c24xx_spi_driver这个平台驱动的注册,相应的平台设备在devs.c中定义,在smdk2440_devices中添加&s3c_device_spi0,&s3c_device_spi1,这就生成了图中所示的s3c24xx-spi.0与s3c24xx-spi.1,当然了这图是在网上找的,不是我画的,所以是6
58、410的。这里s3c24xx-spi.0表示s3c2440的spi controller的0号接口,s3c24xx-spi.1表示s3c2440的spi controller的1号接口。注册了s3c24xx_spi_driver后,赋值了平台驱动的probe函数为s3c24xx_spi_probe。所以当match成功后,调用s3c24xx_spi_probe,这里看其实现:view plain1. "font-size:18px;">static int _init s3c24xx_spi_probe(struct platf
59、orm_device *pdev 2. 3. struct s3c2410_spi_info *pdata; 4. struct s3c24xx_spi *hw; 5. struct spi_master *master; 6.
60、0;struct resource *res; 7. int err = 0; 8. /*分配struct spi_master+struct s3c24xx_spi大小的数据,把s3c24xx_spi设为spi_master的私有数据*/ 9. master = spi_alloc_master(
61、&pdev->dev, sizeof(struct s3c24xx_spi; 10. if (master = NULL 11. dev_err(&pdev->dev, "No memory for spi_mastern" 12.
62、60; err = -ENOMEM; 13. goto err_nomem; 14. 15. /*从master中获得s3c24xx_spi*/ 16. hw
63、 = spi_master_get_devdata(master; 17. memset(hw, 0, sizeof(struct s3c24xx_spi; 18. 19. 20. hw->master = spi_master_get(master; 21. /*
64、驱动移植的时候需要实现的重要结构,初始化为&s3c2410_spi0_platdata*/ 22. hw->pdata = pdata = pdev->dev.platform_data; 23. hw->dev = &pdev->dev; 24. 25. 26.
65、 if (pdata = NULL 27. dev_err(&pdev->dev, "No platform data suppliedn" 28. err = -ENOENT; 29
66、. goto err_no_pdata; 30. 31. /*设置平台的私有数据为s3c24xx_spi*/ 32. platform_set_drvdata(pdev, hw; 33. init_co
67、mpletion(&hw->done; 34. 35. 36. /* setup the master state. */ 37. /*该总线上的设备数*/ 38. master->num_chipselect = hw->pdata->n
68、um_cs; 39. /*总线号*/ 40. master->bus_num = pdata->bus_num; 41. 42. 43. /* setup the state for the bitbang driv
69、er */ 44. /*spi_bitbang专门负责数据的传输*/ 45. hw->bitbang.master = hw->master; 46. hw->bitbang.setup_transfer = s3c24xx_
70、spi_setupxfer; 47. hw->bitbang.chipselect = s3c24xx_spi_chipsel; 48. hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; 49.
71、hw->bitbang.master->setup = s3c24xx_spi_setup; 50. 51. 52. dev_dbg(hw->dev, "bitbang at %pn", &hw->bitbang; 53. 54.
72、0; 。 55. 56. /*初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置*/ 57. s3c24xx_spi_initialsetup(hw; 58. 59. 60. /* register
73、our spi controller */ 61. 62. 63. err = spi_bitbang_start(&hw->bitbang; 64. 。 65. 66. spi controller的register在spi_bitbang_
74、start函数中实现: 67. int spi_bitbang_start(struct spi_bitbang *bitbang 68. 69. int status; 70. 71. 72. if (!bitbang->master | !bitbang->chipselect&
75、#160; 73. return -EINVAL; 74. /*动态创建一个work_struct结构,它的处理函数是bitbang_work*/ 75. INIT_WORK(&bitbang->work, bitbang_work; 76. spi
76、n_lock_init(&bitbang->lock; 77. INIT_LIST_HEAD(&bitbang->queue; 78. /*spi的数据传输就是用这个方法*/ 79. if (!bitbang->master->transfer 80.
77、160; bitbang->master->transfer = spi_bitbang_transfer; 81. if (!bitbang->txrx_bufs 82. bitbang->use_dma = 0; 83.
78、0; /*spi_s3c24xx.c中有spi_bitbang_bufs方法,在bitbang_work中被调用*/ 84. bitbang->txrx_bufs = spi_bitbang_bufs; 85. if (!bitbang->master->setup
79、160; 86. if (!bitbang->setup_transfer 87. bitbang->setup_transfer = 88.
80、; spi_bitbang_setup_transfer; 89. /*在spi_s3c24xx.c中有setup的处理方法,在spi_new_device中被调用*/ 90.
81、0; bitbang->master->setup = spi_bitbang_setup; 91. bitbang->master->cleanup = spi_bitbang_cleanup; 92.
82、 93. else if (!bitbang->master->setup 94. return -EINVAL; 95. 96. 97. /* this
83、160;task is the only thing to touch the SPI bits */ 98. bitbang->busy = 0; 99. /调用create_singlethread_workqueue创建单个工作线程/ 100. bitban
84、g->workqueue = create_singlethread_workqueue( 101. dev_name(bitbang->master->dev.parent; 102. if (bitbang->workqueue = NULL 103.
85、 status = -EBUSY; 104. goto err1; 105. 106. status = spi_register_master(bitbang->master;
86、; 107. if (status < 0 108. goto err2; 109. return status; 110. err2: 111. destroy_workqueue(bitbang->
87、;workqueue; 112. err1: 113. return status; 114. 然后看这里是怎样注册spi主机控制器驱动的:view plain1. int spi_register_master(struct spi_master *master 2. 3. 。 4.
88、60; /*将spi添加到内核,这也是sys/class/Spi_master下产生Spi0,Spi1的原因*/ 5. dev_set_name(&master->dev, "spi%u", master->bus_num; 6. status = device_add(&master->dev; 7.
89、160; scan_boardinfo(master; 8. 这里跟踪scan_boardinfo函数:view plain1. static void scan_boardinfo(struct spi_master *master 2. 3. struct boardinfo *bi; 4. mutex_lock(
90、&board_lock; 5. /*遍历所有挂在board_list上的struct boardinfo*/ 6. list_for_each_entry(bi, &board_list, list 7. struct spi_board_info &
91、#160;*chip = bi->board_info; 8. unsigned n; 9. /*遍历每个boardinfo管理的spi_board_info,如果设备的总线号与控制器的总线好相等,则创建新设备*/ 10.
92、; for (n = bi->n_board_info; n > 0; n-, chip+ 11. if (chip->bus_num != master->bus_num 12. &
93、#160; continue; 13. (void spi_new_device(master, chip; 14. 15.
94、160; 16. mutex_unlock(&board_lock; 17. 在移植的时候我们会在mach-smdk2440.c中的smdk2440_machine_init中添加spi_register_board_info这个函数完成了将spi_board_info交由boardinfo管理,并把boardinfo挂载到board_list链表上。也就是说在系统初始化的时候将spi_device交由到挂在board_list上的bo
95、ardinfo管理,在spi controller的driver注册的时候不但注册这个主机控制器的驱动,还要遍历这个主机控制器的总线上的spi_device,将总线上的spi_device全部注册进内核。当注册进内核并且spi_driver已经注册的时候,如果总线match成功,则会调用spi_driver的probe函数,这个将在后边进行分析。view plain1. "font-size:18px;">int _init 2. spi_register_board_info(struct spi_board_info
96、60;const *info, unsigned n 3. 4. struct boardinfo *bi; 5. 6. 7. bi = kmalloc(sizeof(*bi + n * sizeof *info, GFP
97、_KERNEL; 8. if (!bi 9. return -ENOMEM; 10. bi->n_board_info = n; 11. memcpy(bi->board_info, info, n&
98、#160;* sizeof *info; 12. 13. 14. mutex_lock(&board_lock; 15. list_add_tail(&bi->list, &board_list; 16. mutex_unlock(&board_lock;
99、60;17. return 0; 18. 看一下创建新设备的函数:view plain1. "font-size:18px;">struct spi_device *spi_new_device(struct spi_master *master, 2.
100、0; struct spi_board_info *chip 3. 4. struct spi_device *proxy; 5. int status; 6.
101、 proxy = spi_alloc_device(master; 7. if (!proxy 8. return NULL; 9. 10. 11. WARN_ON(strlen(chip->modalias >
102、;= sizeof(proxy->modalias; 12. /*初始化spi_device的各个字段*/ 13. proxy->chip_select = chip->chip_select; 14. proxy->max_speed_hz = chip->max_speed_hz;
103、15. proxy->mode = chip->mode; 16. proxy->irq = chip->irq; 17. /*这里获得了spi_device的名字,这个modalias也是在我们移植时在mach-smdk2440.c中的s3c2410_spi0_board中设定的*/ 18.
104、60; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias; 19. proxy->dev.platform_data = (void * chip->platform_data; 20. proxy->controller_data = chip->
105、controller_data; 21. proxy->controller_state = NULL; 22. /*主要完成将spi_device添加到内核*/ 23. status = spi_add_device(proxy; 24. if (status&
106、#160;< 0 25. spi_dev_put(proxy; 26. return NULL; 27. 28. 29. 30. ret
107、urn proxy; 31. 下面来看分配spi_alloc_device的函数,主要完成了分配spi_device,并初始化spi->dev的一些字段。view plain1. struct spi_device *spi_alloc_device(struct spi_master *master 2. 3. struct spi_device *spi
108、; 4. struct device *dev = master->dev.parent; 5. if (!spi_master_get(master 6. return NULL;
109、;7. spi = kzalloc(sizeof *spi, GFP_KERNEL; 8. if (!spi 9. dev_err(dev, "cannot alloc spi_devicen" 10. &
110、#160; spi_master_put(master; 11. return NULL; 12. 13. spi->master = master; 14. spi->
111、dev.parent = dev; 15. /*设置总线是spi_bus_type,下面会讲到spi_device与spi_driver是怎样match上的*/ 16. spi->dev.bus = &spi_bus_type; 17. spi->dev.release = spidev_release;
112、160; 18. device_initialize(&spi->dev; 19. return spi; 20. 下面来看分配的这个spi_device是怎样注册进内核的:view plain1. int spi_add_device(struct spi_device *spi 2. 3. static DEFINE_MUTEX(spi_add_lock;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025至2030中国药物涂层气管导管行业市场占有率及投资前景评估规划报告
- 建档立卡贫困学生心理疏导帮扶计划
- 俯卧位通气的皮肤护理与综合管理
- 女性妇科健康认知
- 护理情绪管理与情商提高
- 健康教育理念解读
- 信息安全相关标准
- 五年级下册语文复习课程计划
- 养老机构医疗废物医疗污水处置措施
- 骨科无栓病房护理
- 护士急救流程培训课件
- 护工危重病人护理培训:学习如何处理危重病人和应对突发状况的培训课程课件
- 《健康教育评价》课件
- 偏执性精神障碍
- 附件1:上海市新增医疗服务项目价格申请受理表
- 2022年陕西二级造价工程师造价管理考试真题及答案
- 《服务设计》课程教学大纲
- 消防维保方案(消防维保服务)(技术标)
- 阿勒泰布尔津县高校毕业生“三支一扶”计划招募考试题库
- 少儿硬笔书法启蒙教学30讲PPT课件配套教案
- 岩棉施工方案改
评论
0/150
提交评论