
之前的程序说到底只是对字符设备的一种简化写法,使用设备树进行寄存器的地址定义然后使用gpio子系统对寄存器进行设置。但是这些程序都是直接写了一个驱动对Linux进行操作,在实际的工程应用中这种做法的风险很大,如果有多个应用程序希望对同一个设备进行操作,此时会发生两种可能,一种是误操作,一种时堵塞。在实际应用中会有一个中间层负责调度,从而解决这个问题。 ![]() 接下来我们要做的就是把新字符设备的驱动例程拆分为两部分:驱动程序、设备程序中间通过platform接口进行链接。 新建文件leddevice.c,用于存放设备程序,其中主要为寄存器相关程序。 头文件需要添加 : #include<linux/platform_device.h> 然后将寄存器相关复制进去: #definePERIPH_BASE (0x40000000) //查阅手册第158页,外设总线地址为AB1-AB5其地址为0x4000 0000 – 0X5FFF FFFF。 #defineMPU_AHB4_PERIPH_BASE (PERIPH_BASE+ 0x10000000) //查阅手册第158页,gpios总线AHB4,地址为0X5000 0000 - 0x5001 FFFF。 #defineRCC_BASE (MPU_AHB4_PERIPH_BASE + 0x0000) //查阅手册第162页,gpios时钟,地址为0X50000000-0X5000 0FFFF 。 #defineRCC_MP_AHB4ENSETR (RCC_BASE+ 0XA28) //查阅手册第866页,RCC_MP_AHB4ENSETR寄存器的偏移地址为0XA28,其负责GPIOA-K的时钟开关 #defineGPIOI_BASE (MPU_AHB4_PERIPH_BASE+ 0x2000) //查阅手册第162页,GPIOA地址为0X5000 2000-0X5000 23FF 。 //接下来是GPIO相关的寄存器,查阅手册第1086页,可以查阅到各个寄存器的偏移地址。 #defineGPIOI_MODER (GPIOI_BASE + 0x0000) #defineGPIOI_OTYPER (GPIOI_BASE + 0x0004) #defineGPIOI_OSPEEDR (GPIOI_BASE + 0x0008) #defineGPIOI_PUPDR (GPIOI_BASE + 0x000C) #defineGPIOI_BSRR (GPIOI_BASE+ 0x0018) 之后定义platform结构体用来调用寄存器: staticstruct platform_device leddevice = { .name = "xhy-led", .id = -1, .dev = { .release = &led_release, }, .num_resources =ARRAY_SIZE(led_resources), .resource = led_resources, }; 结构体中调用的与两个,一个release和一个resources。&led_release为总线释放: staticvoid led_release(struct device *dev) { printk("led devicereleased!\r\n"); } resource时一个resources数组用于存放寄存器地址。 staticstruct resource led_resources[] = { [0] = { .start = RCC_MP_AHB4ENSETR, .end = (RCC_MP_AHB4ENSETR + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [1] = { .start = GPIOI_MODER, .end = (GPIOI_MODER + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [2] = { .start = GPIOI_OTYPER, .end = (GPIOI_OTYPER + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [3] = { .start = GPIOI_OSPEEDR, .end = (GPIOI_OSPEEDR + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [4] = { .start = GPIOI_PUPDR, .end = (GPIOI_PUPDR + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [5] = { .start = GPIOI_BSRR, .end = (GPIOI_BSRR + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, }; 接下来时接口程序:leddevice_init和leddevice_exit,分别是platform的egister和unregister: staticint __init leddevice_init(void) { returnplatform_device_register(&leddevice); } staticvoid __exit leddevice_exit(void) { platform_device_unregister(&leddevice); } 设备层的程序写好后需要由驱动程序来调用设备程序,驱动程序中的设备寄存器地址设置删除掉,.release由于在设备程序中已定义所以也需要删除。 接口程序和设备驱动一样为platform的egister和unregister,两程序一模一样。 对应设备程序的platform结构体这里也需要定义一个platform结构体: staticstruct platform_driver led_driver = { .driver ={ .name = "xhy-led", }, .probe =led_probe, .remove =led_remove, };//platform驱动结构体 .name和设备程序中一至,这里定义了两个函数:led_probe和led_remove, 其中led_remove存放原来的leddevice_exit程序,也就是取消映射以及设备的注销。led_probe放原来的leddevice_init用于对设备的初始化。 这里需要改动的时在寄存器映射之前需要从设备程序的leddevice中读取6个寄存器的地址,之后才能把地址映射出来:‘ 4 V) P4 `: i1 s* e1 pfor (i =0; i < 6; i++) { ledsource =platform_get_resource(dev, IORESOURCE_MEM, i); /* 依次MEM类型资源 */ if (!ledsource) { dev_err(&dev->dev,"No MEM resource for always on\n"); return -ENXIO; } ressize =resource_size(ledsource); } /*寄存器地址映射 */ MPU_AHB4_PERIPH_RCC_PI= ioremap(ledsource[0]->start, ressize[0]); GPIOI_MODER_PI =ioremap(ledsource[1]->start, ressize[1]); GPIOI_OTYPER_PI= ioremap(ledsource[2]->start, ressize[2]); GPIOI_OSPEEDR_PI =ioremap(ledsource[3]->start, ressize[3]); GPIOI_PUPDR_PI =ioremap(ledsource[4]->start, ressize[4]); GPIOI_BSRR_PI =ioremap(ledsource[5]->start, ressize[5]); ![]() |
基于STM32MP1和STM32MP2在嵌入式Linux平台上部署有效的安全保护机制
利用STM32MP1和STM32MP2为嵌入式Linux提供有效的安全措施:供当今决策者参考的3条宝贵经验
STM32MP1 WiFi连接
【STM32MP157】从ST官方例程中分析RPMsg-TTY/SDB核间通信的使用方法
【STM32MPU 安全启动】 TF-A BL2 TrustedBoot原理学习
《STM32MPU安全启动》学**结
《STM32MPU安全启动》学习笔记之optee 如何加载CORTEX-M核和使能校验
《STM32MPU安全启动》学习笔记之TF-A BL2校验optee和uboot的流程以及如何使能
《STM32MPU 安全启动》课程学习心得+开启一扇通往嵌入式系统安全领域深处的大门。
《STM32MPU安全启动》 课程学习心得