第五十六讲 pinctrl子系统
第五十六讲 pinctrl子系统
文章目录
- 第五十六讲 pinctrl子系统
- 一、前言
- 二、iomuxc 节点
- 1、作用
- 2、节点信息
- 3、节点格式
- 4、引脚配置信息
- 三、imx_pinctrl和pinctrl_dev
- 1、流程分析
- 2、imx_pinctrl_probe分析
- 源码
- 四、pinctl rgb 实验
- 1、设备树添加 `penctl_reg_led` 节点
- 2、编译设备树
- 3、将设备树移到开发板上
- 4、覆盖开发板上的 dtbo 文件
- 5、编写测试代码
- pinctl_led.h
- pinctl_led.c
- Makefile
- 6、编译并将编译出来的ko文件放到开发板上
- 7、重启开发板
- 8、加载编译出来的模块
- 9、开始实验
一、前言
Linux 是一个庞大而完善的系统。我们在控制硬件外设的时候,需要先去获取控制硬件外设相关的引脚,然后通过数据手册查看gpio的操作方法。这样开发效率比较低,对于我们写程序也比较麻烦。所以引入 pinctrl
来简化我们的开发步骤。
pinctrl 子系统主要用于管理芯片的引脚。imx6ull 芯片拥有众多的片上外设,大多数外设需要通过芯片的引脚与外部设备(器件)相连实现相对应的控制。在驱动程序中我们需要手动设置每个引脚的复用功能,不仅增加了工作量,编写的驱动程序不方便移植,可重用性差等。pinctrl 子系统是由芯片厂商来实现的, 简单来说用于帮助我们管理芯片引脚并自动完成引脚的初始化,而我们要做的只是在设备树中按照规定的格式写出想要的配置参数即可。
二、iomuxc 节点
1、作用
- 汇总引脚配置信息
- 预存 iomux 节点信息
2、节点信息
iomuxc: iomuxc@20e0000 {compatible = "fsl,imx6ul-iomuxc";/*这里会跟pinctrl匹配*/reg = <0x20e0000 0x4000>;/*iomuxc基地址0x20e0000, 长度0x4000*/};
- compatible:修饰的是与平台驱动做匹配的名字, 这里则是与 pinctrl 子系统的平台驱动做匹
配。 - reg:表示的是引脚配置寄存器的基地址。
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;pinctrl_hog_1: hoggrp-1 {fsl,pins = <MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */>;};...
};
pinctrl-names = “default”;
表示硬件状态为默认状态,(pinctrl-0状态)
pinctrl-0 = <&pinctrl_hog_1>;
定义第0状态需要使用的引脚
pinctrl-1 =
定义第1状态需要使用的引脚
…
3、节点格式
pinctrl_<自定义名字>:<自定义名字>{fsl.pins=<引脚宏定义 (PAD)引脚属性>;
};
示例:
pinctrl_enet1: enet1grp {fsl,pins = <MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031>;};
4、引脚配置信息
引脚的配置信息一眼看去由两部分组成,一个宏定义和一个 16 进制数组成
在文件 arch/arm/boot/dts/imx6ul-pinfunc.h 中定义了许多引脚的宏
#define MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x00cc 0x0358 0x0000 0 0
每个宏定义后面有 5 个参数,名字依次为 mux_reg、conf_reg、input_reg、mux_mode、input_val。
如果将宏定义展开则在设备树中每条配置信息实际是 6 个参数,由于第 6 个参数设置较为复杂需要根据实际需要设置因此并没有把它放到宏定义里面。
- mux_reg 和 mux_mode :mux_reg 是引脚复用选择寄存器偏移地址,mux_mode 是引脚复用选择寄存器模式选择位的值。
- conf_reg ,引脚(PAD)属性控制寄存器偏移地址。与引脚复用选择寄存器不同,引脚属性寄
存器应当根据实际需要灵活的配置,所以它的值并不包含在宏定义中,它的值是我们上面所说的
“第六个”参数。 - input_reg 和 input_val ,input_reg 暂且称为输入选择寄存器偏移地址。input_val 是输入选择寄
存器的值。这个寄存器只有某些用作输入的引脚才有。 - 在 pinctrl 子系统中一条配置信息由一个宏定义和一个参数组成,将宏定义展开就是六个参数。pad 属性配置选项非常多,配置灵活。在 pinctrl 子系统中添加的 PAD 属性值就是引脚(PAD)属性设置寄存器的值(16 进制)。
含义
mux_reg | conf_reg | input_reg | mux_mode | input_val | 0x17059 |
---|---|---|---|---|---|
基地址偏移 0x0080 | 基地址偏移 0x030c |
0x0000 如果需要输入功能才使用 |
5 表示mux_reg设置的具体的值 |
0 设置input_reg的值 |
设置conf_reg的值 |
三、imx_pinctrl和pinctrl_dev
pinctrl子系统会预先确定引脚的数量和名字,并为每个引脚的信息分配内存,管理每个引脚的使用状态。
1、流程分析
imx6ul的pinctrl驱动位于文件
ebf_linux_kernel/drivers/pinctrl/freescale/pinctrl-imx6ul.c
看一个驱动函数首先看啥,当然是初始化函数呀,pinctrl的初始化函数为
static int __init imx6ul_pinctrl_init(void) {return platform_driver_register(&imx6ul_pinctrl_driver); }
可以看到,这里注册了一个平台设备
static struct platform_driver imx6ul_pinctrl_driver = {.driver = {.name = "imx6ul-pinctrl",.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),},.probe = imx6ul_pinctrl_probe, };
这里有一个imx6ul_pinctrl_of_match
static const struct of_device_id imx6ul_pinctrl_of_match[] = {{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },{ /* sentinel */ } };
'fsl,imx6ul-iomuxc’是不是很眼熟,这就是
iomuxc
的compatible
属性值imx6ul_pinctrl_probe
static int imx6ul_pinctrl_probe(struct platform_device *pdev) {const struct imx_pinctrl_soc_info *pinctrl_info;const struct of_device_id *match;pinctrl_info = of_device_get_match_data(&pdev->dev);if (!pinctrl_info)return -ENODEV;match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);if (!match)return -ENODEV;pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;return imx_pinctrl_probe(pdev, pinctrl_info); }
of_device_get_match_data
const void *of_device_get_match_data(const struct device *dev) {const struct of_device_id *match;match = of_match_device(dev->driver->of_match_table, dev);if (!match)return NULL;return match->data; }
of_match_device
/*** of_match_device - Tell if a struct device matches an of_device_id list* @ids: array of of device match structures to search in* @dev: the of device structure to match against** Used by a driver to check whether an platform_device present in the* system is in its list of supported devices.*/ const struct of_device_id *of_match_device(const struct of_device_id *matches,const struct device *dev) {if ((!matches) || (!dev->of_node))return NULL;return of_match_node(matches, dev->of_node); }
这样
of_device_get_match_data
的返回就不为空,就匹配上了(匹配规则)当
of_device_get_match_data
成功后,会执行of_match_device
细心的朋友可能会发现,在of_device_get_match_data
里面已经调用过一次of_match_device
了。of_match_node
/*** of_match_node - Tell if a device_node has a matching of_match structure* @matches: array of of device match structures to search in* @node: the of device structure to match against** Low level utility function used by device matching.*/ const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node) {const struct of_device_id *match;unsigned long flags;raw_spin_lock_irqsave(&devtree_lock, flags);match = __of_match_node(matches, node);raw_spin_unlock_irqrestore(&devtree_lock, flags);return match; }
__of_match_node
const struct of_device_id *__of_match_node(const struct of_device_id *matches,const struct device_node *node) {const struct of_device_id *best_match = NULL;int score, best_score = 0;if (!matches)return NULL;for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {score = __of_device_is_compatible(node, matches->compatible,matches->type, matches->name);if (score > best_score) {best_match = matches;best_score = score;}}return best_match; }
这里其实如果匹配的话就是返回
imx6ul_pinctrl_of_match
pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;
这里其实就是pinctrl_info = imx6ul_pinctrl_of_match[0].data
也就是 &imx6ul_pinctrl_info
static const struct imx_pinctrl_soc_info imx6ul_pinctrl_info = {.pins = imx6ul_pinctrl_pads,.npins = ARRAY_SIZE(imx6ul_pinctrl_pads),.gpr_compatible = "fsl,imx6ul-iomuxc-gpr", };
imx6ul_pinctrl_pads
/*** struct pinctrl_pin_desc - boards/machines provide information on their* pins, pads or other muxable units in this struct* @number: unique pin number from the global pin number space* @name: a name for this pin* @drv_data: driver-defined per-pin data. pinctrl core does not touch this*/ struct pinctrl_pin_desc {unsigned number;const char *name;void *drv_data; }; /* Pad names for the pinmux subsystem */ static const struct pinctrl_pin_desc imx6ul_pinctrl_pads[] = {IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE0),IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE1),IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE2),IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE3),IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE4),...};
drivers\pinctrl\freescale\pinctrl-imx.h
#define IMX_PINCTRL_PIN(pin) PINCTRL_PIN(pin, #pin)
ebf_linux_kernel\build_image\build\debian\hdrtmp\usr\src\linux-headers-4.19.35-carp-imx6\include\linux\pinctrl\pinctrl.h
#define PINCTRL_PIN(a, b) { .number = a, .name = b }
到这里,我们可以知道有硬件的引脚信息存放在imx6ul_pinctrl_pads这个结构体数组里面了
例如MX6UL_PAD_RESERVE0,第0号引脚,他们跟 mux_reg 的关系就是
MX6UL_PAD_RESERVE0 * 4 = mux_reg
2、imx_pinctrl_probe分析
源码
int imx_pinctrl_probe(struct platform_device *pdev,const struct imx_pinctrl_soc_info *info)
{struct regmap_config config = { .name = "gpr" };struct device_node *dev_np = pdev->dev.of_node;struct pinctrl_desc *imx_pinctrl_desc;struct device_node *np;struct imx_pinctrl *ipctl;struct resource *res;struct regmap *gpr;int ret, i;if (!info || !info->pins || !info->npins) {dev_err(&pdev->dev, "wrong pinctrl info\n");return -EINVAL;}if (info->gpr_compatible) {gpr = syscon_regmap_lookup_by_compatible(info->gpr_compatible);if (!IS_ERR(gpr))regmap_attach_dev(&pdev->dev, gpr, &config);}/* Create state holders etc for this driver */ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL);if (!ipctl)return -ENOMEM;if (!(info->flags & IMX8_USE_SCU)) {ipctl->pin_regs = devm_kmalloc(&pdev->dev, sizeof(*ipctl->pin_regs) *info->npins, GFP_KERNEL);if (!ipctl->pin_regs)return -ENOMEM;for (i = 0; i < info->npins; i++) {ipctl->pin_regs[i].mux_reg = -1;ipctl->pin_regs[i].conf_reg = -1;}/*获取resource*/res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*映射resource*/ipctl->base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(ipctl->base))return PTR_ERR(ipctl->base);/*** of_property_read_bool - Findfrom a property* @np: device node from which the property value is to be read.* @propname: name of the property to be searched.** Search for a property in a device node.* Returns true if the property exists false otherwise.*/if (of_property_read_bool(dev_np, "fsl,input-sel")) {np = of_parse_phandle(dev_np, "fsl,input-sel", 0);if (!np) {dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n");return -EINVAL;}/*映射*/ipctl->input_sel_base = of_iomap(np, 0);of_node_put(np);if (!ipctl->input_sel_base) {dev_err(&pdev->dev,"iomuxc input select base address not found\n");return -ENOMEM;}}}imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc),GFP_KERNEL);if (!imx_pinctrl_desc)return -ENOMEM;imx_pinctrl_desc->name = dev_name(&pdev->dev);imx_pinctrl_desc->pins = info->pins;imx_pinctrl_desc->npins = info->npins;imx_pinctrl_desc->pctlops = &imx_pctrl_ops;imx_pinctrl_desc->pmxops = &imx_pmx_ops;imx_pinctrl_desc->confops = &imx_pinconf_ops;imx_pinctrl_desc->owner = THIS_MODULE;/* for generic pinconf */imx_pinctrl_desc->custom_params = info->custom_params;imx_pinctrl_desc->num_custom_params = info->num_custom_params;/* platform specific callback */imx_pmx_ops.gpio_set_direction = info->gpio_set_direction;mutex_init(&ipctl->mutex);ipctl->info = info;ipctl->dev = &pdev->dev;platform_set_drvdata(pdev, ipctl);ret = devm_pinctrl_register_and_init(&pdev->dev,imx_pinctrl_desc, ipctl,&ipctl->pctl);if (ret) {dev_err(&pdev->dev, "could not register IMX pinctrl driver\n");goto free;}ret = imx_pinctrl_probe_dt(pdev, ipctl);if (ret) {dev_err(&pdev->dev, "fail to probe dt properties\n");goto free;}dev_info(&pdev->dev, "initialized IMX pinctrl driver\n");return pinctrl_enable(ipctl->pctl);free:imx_free_resources(ipctl);return ret;
}
这个函数主要就是实现以下功能(这里就不展开了,感兴趣可以看视频分析),这里我没进行分析并不代表不重要呀,因为视频的分析已经做的很到位了,我这里再写的话可能表达的不全面,也怕误人子弟。所以一定要去看呀!!!
- 获取resource
- /映射resource/
- 注册pinctrl驱动
- 使能pinctrl
四、pinctl rgb 实验
1、设备树添加 penctl_reg_led
节点
新增的节点名为“rgb_led”,名字任意选取,长度不要超过32个字符,最好能表达出节点的信息。 “pinctrl_rgb_led”节点标签,“pinctrl_”是固定的格式,后面的内容可以自定义,我们将通过这个标签引用这个节点。 在添加完pinctrl子节点后,系统会根据我们添加的配置信息将引脚初始化为GPIO功能。
pinctrl_rgb_led:rgb_led{fsl,pins=<MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1>;};
2、编译设备树
更改设备树之后,在内核源码下执行以下命令:
make ARCH=arm -j1 CROSS_COMPILE=arm-linux-gnueabihf- dtbsCALL scripts/checksyscalls.shDTC arch/arm/boot/dts/imx6ull-mmc-npi.dtb
3、将设备树移到开发板上
(这里就不做说明啦,方法有很多,比如 nfs、ftp…)
4、覆盖开发板上的 dtbo 文件
mv /home/debian/imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-imx6/
5、编写测试代码
pinctl_led.h
#include <linux/of.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/of_gpio.h>
#include <linux/io.h>
#include <linux/of_address.h>#define DEVICE_NAME "shadowrain_led"typedef struct
{struct device_node *device_node;void __iomem *virtual_CCM_CCGR;void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;void __iomem *virtual_DR;void __iomem *virtual_GDIR;
} rgbLedStr;dev_t rgbDev;
struct class *rgbClass;
rgbLedStr gRGBLedStr[3];
pinctl_led.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include "pinctl_led.h"int rgbLedOpen (struct inode *node, struct file *file)
{printk("\n open form driver \n");return 0;
}
ssize_t rgbLedWrite (struct file *file, const char __user *usr, size_t size, loff_t *loff)
{char lRecvBuf[10] = {0};int lRegData = 0;printk("<1> Led write");if(size >= 10){printk("<1> size error!");return -1;}if(copy_from_user(lRecvBuf, usr, size) < 0){printk("<1> copy error!");return -1;}printk("<1> recv is %s", lRecvBuf);if(strstr(lRecvBuf, "red on") != NULL){printk("red on");lRegData = ioread32(gRGBLedStr[0].virtual_DR);lRegData &= ~(0x01 << 4);iowrite32(lRegData, gRGBLedStr[0].virtual_DR);}else if(strstr(lRecvBuf, "red off") != NULL){lRegData = ioread32(gRGBLedStr[0].virtual_DR);lRegData |= (0x01 << 4);iowrite32(lRegData, gRGBLedStr[0].virtual_DR);}else if(strstr(lRecvBuf, "green on") != NULL){lRegData = ioread32(gRGBLedStr[1].virtual_DR);lRegData &= ~(0x01 << 20);iowrite32(lRegData, gRGBLedStr[1].virtual_DR);}else if(strstr(lRecvBuf, "green off") != NULL){lRegData = ioread32(gRGBLedStr[1].virtual_DR);lRegData |= (0x01 << 20);iowrite32(lRegData, gRGBLedStr[1].virtual_DR);}else if(strstr(lRecvBuf, "blue on") != NULL){lRegData = ioread32(gRGBLedStr[2].virtual_DR);lRegData &= ~(0x01 << 19);iowrite32(lRegData, gRGBLedStr[2].virtual_DR);}else if(strstr(lRecvBuf, "blue off") != NULL){lRegData = ioread32(gRGBLedStr[2].virtual_DR);lRegData |= (0x01 << 19);iowrite32(lRegData, gRGBLedStr[2].virtual_DR);}else{printk("error data %s", lRecvBuf);lRegData = ioread32(gRGBLedStr[0].virtual_DR);lRegData |= (0x01 << 4);iowrite32(lRegData, gRGBLedStr[0].virtual_DR);lRegData = ioread32(gRGBLedStr[1].virtual_DR);lRegData |= (0x01 << 20);iowrite32(lRegData, gRGBLedStr[1].virtual_DR);lRegData = ioread32(gRGBLedStr[2].virtual_DR);lRegData |= (0x01 << 19);iowrite32(lRegData, gRGBLedStr[2].virtual_DR);}return size;
}struct cdev gRGBLedCdev =
{.owner = THIS_MODULE,
};
struct file_operations gRGBLedFp =
{.open = rgbLedOpen,.write = rgbLedWrite,
};
static int rgbProbe(struct platform_device *pdv)
{struct device_node *lRGBLedDeviceNode;int lRegData = 0;/*查找设备树节点*/printk("<1> path is "DEVICE_NAME"");lRGBLedDeviceNode = of_find_node_by_path("/"DEVICE_NAME"");if(lRGBLedDeviceNode == NULL){printk("<1> Find node by path failed! \n");return -1;}/*获取rgb_led节点的灯子节点*/gRGBLedStr[0].device_node = of_find_node_by_name(lRGBLedDeviceNode, "rgb_led_red");if(gRGBLedStr[0].device_node == NULL){return -1;}/*获取 reg 属性并转化为虚拟地址*/gRGBLedStr[0].virtual_CCM_CCGR = of_iomap(gRGBLedStr[0].device_node, 0);gRGBLedStr[0].virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(gRGBLedStr[0].device_node, 1);gRGBLedStr[0].virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(gRGBLedStr[0].device_node, 2);gRGBLedStr[0].virtual_DR = of_iomap(gRGBLedStr[0].device_node, 3);gRGBLedStr[0].virtual_GDIR = of_iomap(gRGBLedStr[0].device_node, 4);/*---------------------------------------------------------------------------------*/gRGBLedStr[1].device_node = of_find_node_by_name(lRGBLedDeviceNode, "rgb_led_green");if(gRGBLedStr[1].device_node == NULL){return -1;}/*获取 reg 属性并转化为虚拟地址*/gRGBLedStr[1].virtual_CCM_CCGR = of_iomap(gRGBLedStr[1].device_node, 0);gRGBLedStr[1].virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(gRGBLedStr[1].device_node, 1);gRGBLedStr[1].virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(gRGBLedStr[1].device_node, 2);gRGBLedStr[1].virtual_DR = of_iomap(gRGBLedStr[1].device_node, 3);gRGBLedStr[1].virtual_GDIR = of_iomap(gRGBLedStr[1].device_node, 4);/*---------------------------------------------------------------------------------*/gRGBLedStr[2].device_node = of_find_node_by_name(lRGBLedDeviceNode,"rgb_led_blue");if (gRGBLedStr[2].device_node == NULL){printk(KERN_ERR "\n get rgb_led_blue_device_node failed ! \n");return -1;}/*获取 reg 属性并转化为虚拟地址*/gRGBLedStr[2].virtual_CCM_CCGR = of_iomap(gRGBLedStr[2].device_node, 0);gRGBLedStr[2].virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(gRGBLedStr[2].device_node, 1);gRGBLedStr[2].virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(gRGBLedStr[2].device_node, 2);gRGBLedStr[2].virtual_DR = of_iomap(gRGBLedStr[2].device_node, 3);gRGBLedStr[2].virtual_GDIR = of_iomap(gRGBLedStr[2].device_node, 4);if( alloc_chrdev_region(&rgbDev, 0, 1, DEVICE_NAME) < 0){printk("<1> alloc_chrdev_region failed \n");return -1;} cdev_init(&gRGBLedCdev, &gRGBLedFp);cdev_add(&gRGBLedCdev, rgbDev, 1);rgbClass = class_create(THIS_MODULE, DEVICE_NAME);device_create(rgbClass, NULL, rgbDev, NULL, DEVICE_NAME);return 0;
}static const struct of_device_id rgbLed[] =
{{.compatible = "fire,rgb_led"},{/* sentinel */}
};
struct platform_driver rgbPlatformDriver =
{.probe = rgbProbe,.driver = {.name = "rgb_led_platform",.owner = THIS_MODULE,.of_match_table = rgbLed,},};
static __init int RGBInit(void)
{int driverState;driverState = platform_driver_register(&rgbPlatformDriver);printk(KERN_ALERT "\tDriverState is %d\n", driverState);return 0;
}static __exit void RGBExit(void)
{}module_init(RGBInit);
module_exit(RGBExit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yeyu");
MODULE_DESCRIPTION("RGBModule");
MODULE_ALIAS("RGBModule");
Makefile
KERNEL_DIR=../../ebf_linux_kernel/build_image/build/ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH CROSS_COMPILEobj-m := pinctl_led.oall:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules.PHONE:clean copyclean:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean copy:sudo cp *.ko /home/dragon/nfsshare
6、编译并将编译出来的ko文件放到开发板上
7、重启开发板
8、加载编译出来的模块
sudo insmod pinctl_led.ko
9、开始实验
sudo sh -c "echo 'red on'>/dev/shadowrain_led"
灯亮了,试验成功!!!
第五十六讲 pinctrl子系统相关推荐
- 高等数学学习笔记——第五十六讲——空间直线及其方程
1. 问题引入--高楼大厦的轮廓及电视塔的主题结构 2. 直线的参数方程 3. 直线的标准方程(直线对称式方程) 4. 直线的向量方程 5. 直线的两点式方程 6. 直线的一般式方程 7. 过直线的平 ...
- 名词解释第五十六讲:矿工费
这里是王团长区块链学院,与最优秀的区块链人一起成长!今天给大家讲讲矿工费. 比特币.以太坊等数字货币在通过钱包转账的过程中都会涉及到"矿工费",这个"矿工费"还 ...
- OpenCV学习笔记(五十六)——InputArray和OutputArray的那些事core OpenCV学习笔记(五十七)——在同一窗口显示多幅图片 OpenCV学习笔记(五十八)——读《Mast
OpenCV学习笔记(五十六)--InputArray和OutputArray的那些事core 看过OpenCV源代码的朋友,肯定都知道很多函数的接口都是InputArray或者OutputArray ...
- 三个子系统_「正点原子Linux连载」第五十八章Linux INPUT子系统实验(一)
1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 第五十八章Linux INPUT子系统实 ...
- 达芬奇密码 第五十五章 第五十六章
达芬奇密码 第五十五章 第五十六章[@more@] 第五十五章 索菲靠着兰登坐在长沙发上,喝着茶吃着烤饼,享受着食物的美味.雷·提彬爵士微笑着,在炉火前面笨拙地踱来踱去.假肢敲在地面上,发出" ...
- 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)
本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/16384009 作者:毛星 ...
- 第十六讲 java进阶-API
第十六讲 java进阶-重写equals方法的深度解读 1 接口的补充 接口为什么不能new对象?有没有必要new对象?或者是说如果能new对象,new出来的是什么东西?有什么内容,是否符合对象的特征 ...
- 中国五十六个民族简介
56个民族是中华人民共和国灿烂星空中(五十六个星座). 中华民族共包括56个民族,汉族是中国的主体民族,占全部人口的91.51%,其他还有55个民族,占8.49%(第六次人口普查).汉族和55个少数民 ...
- 56.深度解密五十六:详解DSP营销推广及实战中的相关问题
网络营销推广技术.技巧深度解密(五十六)指南: 1.本文档适合零基础以及互联网营销推广工作者,主要讲解DSP广告在实战中的相关问题. 2.原创版权文档,任何抄袭或者全部.部分模仿都是侵权行为. 3.敬 ...
最新文章
- python使用matplotlib可视化线图(line plot)、使用arrow函数在matplotlib可视化图像中添加箭头(drawing arrows in matplotlib)
- iphone怎么分屏_问答 | Mac 应用商店中无法”获取“软件怎么办?
- 如何查看linux系统的密码是多少,如何在Linux系统查询SAM密码
- this 和super关键字
- python学习list_python学习之list
- 作者:朱扬勇,博士,复旦大学计算机科学技术学院教授、学术委员会主任,上海市数据科学重点实验室主任。...
- Machine Learning学习计划
- 中俄国际社区大佬共话Java,苦Lambda表达式久矣?
- Heartbeat(v1、v2、pacemaker)集群组件概述
- Matlab期货量化交易特征选取,【策略分享】Matlab量化交易策略源码分享
- easyui datagrid添加合计行
- 电路城 电路方案 51单片机开发板(原理图+PCB+学习程序源文件)
- django -- 过滤器
- 【回溯】有蹩脚的马踏棋盘——思路巨清晰!!!
- java 字符补位_JavaScript 字符串数字左补位,右补位,取固定长度,截位扩展函数代码...
- [硬核教程]如何解决电脑假死问题——适用于调用GPU有声音,无画面的情况。
- 微信支付V3-企业转账至零钱1/2
- 四川贝利思科技:拼多多开店的优势所在
- css设overflow:hiden行内元素会发生偏移的现象
- 医疗行业:容灾备份平台建设及运维难点
热门文章
- 《广东省智慧高速公路建设指南(试行)》发布(附下载)
- ipad触控笔推荐平价,五大电容笔排行榜
- C++课程设计——猜谜语小游戏
- 谷粒商城-人人开源前端npm install下载依赖的时候出现问题
- echarts+vue实现简单的中国地图且数据下钻(简洁适合小白版)
- 考验平衡性 安卓出色物理游戏反重力小球
- ElasticSearch7 学习笔记
- could not automatically detect an adb binary.some emulator functionality will not work until a custo
- 笑傲江湖ol手游java,《笑傲江湖OL》经典服,不充钱也能笑傲江湖
- 解决feign调用无法解析IPage问题