|
之前的程序说到底只是对字符设备的一种简化写法,使用设备树进行寄存器的地址定义然后使用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个寄存器的地址,之后才能把地址映射出来:‘ for (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]);
platform.rar
(34.85 KB, 下载次数: 0)
|
STM32串口自动识别波特率的原理
STM32MP135 MP157 MP257 CoreMark移植&&跑分对比&&多核心负载不均衡问题排查与解决
STM32--STM32 微控制器详解
为什么学了几天STM32一脸茫然?
STM32 不同时钟频率有什么不同的影响
STM32入门指南:从零开始,如何为你的首个项目选择最合适的MCU?
STM32MP157D-DK1-编译并运行第一个程序hello world
STM32MP157D-DK1-SDK包安装
STM32MP157D-DK1-ssh连接方式
STM32MP157D-DK1-stlink串口进入终端
微信公众号
手机版