1) Experimental platform: punctual atom STM32MP157 development board
2) Purchase link: https://item.taobao.com/item.htm?&id=629270721801
3) Full set of experimental source code + manual + video download address: http://www.openedv.com/thread-318813-1-1.html
4) Official station B of punctual atom: https://space.bilibili.com/394620890
5) Punctual atom STM32MP157 technical exchange group: 691905614
Chapter 24 LED driving experiment under equipment tree
In the previous chapter, we explained the device tree syntax and the OF functions commonly used in driver development in detail. In this chapter, we began the first Linux driver experiment based on device tree. This chapter is completed on the basis OF the experiment in Chapter 22, only changing its driver development to the form OF device tree.
24.1 device Tree LED driving principle
In Chapter 22 new character device driver experiment, we directly in the driver file newchrled C, and then use io_remap function performs memory mapping to obtain the corresponding virtual address. Finally, operate the virtual address corresponding to the register to complete the initialization OF GPIO. This chapter is completed on the basis OF the experiment in Chapter 42. In this chapter, we use the device tree to pass the relevant register physical address to the Linux kernel. The linux driver file uses the OF function explained in the previous chapter to obtain the required attribute value from the device tree, and then uses the obtained attribute value to initialize the relevant IO. The experiment in this chapter is relatively simple. The key contents OF this chapter are as follows:
① , at stm32mp157d ATK Create the corresponding device node in the DTS file.
② Write the driver (based on the experiment in Chapter 22) to obtain the relevant attribute values in the device tree.
③ . use the obtained relevant attribute values to initialize the GPIO used by the LED.
24.2 hardware schematic diagram analysis
Refer to section 21.2 for the hardware principle of this experiment.
24.3 preparation of experimental procedures
24.3.1 modify the equipment tree file
Create a child node named "stm32mp1_led" under the root node "/", and open stm32mp157d ATK DTS file, enter the following content at the end of the root node "/":
Example code 44.3.1.1 stm32mp1_led node 1 stm32mp1_led { 2 compatible = "atkstm32mp1-led"; 3 status = "okay"; 4 reg = <0X50000A28 0X04 /* RCC_MP_AHB4ENSETR */ 5 0X5000A000 0X04 /* GPIOI_MODER */ 6 0X5000A004 0X04 /* GPIOI_OTYPER */ 7 0X5000A008 0X04 /* GPIOI_OSPEEDR */ 8 0X5000A00C 0X04 /* GPIOI_PUPDR */ 9 0X5000A018 0X04 >; /* GPIOI_BSRR */ 10 };
Line 2, attributes compatible set up stm32mp1_led Node compatibility is“ atkstm32mp1-led". Line 3, attributes status Set the status to“ okay". fourth~9 that 's ok, reg Attribute, very important! reg Property sets the physical address of the register to be used in the driver, such as "0" in line 4 X50000A28 0X04"express STM32MP1 of RCC_MP_AHB4ENSETR Register, where the register address is 0 X50000A28,The length is 4 bytes.
After modifying the device tree, enter the following command to recompile stm32mp157d ATK dts:
make dtbs
After compiling, stm32mp157d-atk DTB, using the new stm32mp157d ATK DTB starts the Linux kernel. After Linux starts successfully, enter the / proc / device tree / directory to check whether there is a "stm32mp1_led" node. The results are shown in figure 24.3.1.1:
Figure 24.3.1.1 stm32mp1_led node
If there is no "stm32mp1_led" node, please focus on the following two points:
① Check whether the modification of the device tree is successful, that is, stm32mp1_ Whether the LED node is a child of the root node "/".
② Check whether the Linux kernel started with the new device tree.
You can enter stm32mpl in figure 24.3.1.1_ In the LED directory, check the attribute files. The results are shown in figure 24.3.1.2:
Figure 24.3.1.2 stm32mp1_led node file
You can use the cat command to check whether the attribute values such as compatible and status are consistent with our settings.
24.3.2 LED driver programming
After the device tree is ready, you can write the driver. The experiment in this chapter is in the experiment driver file newchrled in Chapter 22 C. Create a new folder named "4_dtsled" and then_ Create a vscode project in the dtsled folder, and the workspace is named "dtsled". After the project is created, create a new dtsled C file, in dtsled C enter the following contents:
Example code 24.3.2.1 dtsled.c Document content 1 #include <linux/types.h> 2 #include <linux/kernel.h> 3 #include <linux/delay.h> 4 #include <linux/ide.h> 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/errno.h> 8 #include <linux/gpio.h> 9 #include <linux/cdev.h> 10 #include <linux/device.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <asm/mach/map.h> 14 #include <asm/uaccess.h> 15 #include <asm/io.h> 16 17 /*************************************************************** 18 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 19 File name : dtsled.c 20 Author : Punctual atomic Linux team 21 edition : V1.0 22 Description : LED driver file. 23 other : nothing 24 Forum : www.openedv.com 25 journal : First edition v1.0 0 2020 / 12 / 19 punctual atomic Linux team creation 26 ***************************************************************/ 27 #define DTSLED_CNT one /* Number of equipment numbers */ 28 #define DTSLED_NAME "dtsled" /* name */ 29 #define LEDOFF 0 / * turn off the light */ 30 #define LEDON 1 / * turn on the light */ 31 32 /* Mapped register virtual address pointer */ 33 static void __iomem *MPU_AHB4_PERIPH_RCC_PI; 34 static void __iomem *GPIOI_MODER_PI; 35 static void __iomem *GPIOI_OTYPER_PI; 36 static void __iomem *GPIOI_OSPEEDR_PI; 37 static void __iomem *GPIOI_PUPDR_PI; 38 static void __iomem *GPIOI_BSRR_PI; 39 40 /* dtsled Equipment structure */ 41 struct dtsled_dev{ 42 dev_t devid; /* Equipment number */ 43 struct cdev cdev; /* cdev */ 44 struct class *class; /* class */ 45 struct device *device; /* equipment */ 46 int major; /* Main equipment No */ 47 int minor; /* Secondary equipment No */ 48 struct device_node *nd; /* Device node */ 49 }; 50 51 struct dtsled_dev dtsled; /* led equipment */ 52 53 /* 54 * @description : LED On / off 55 * @param - sta : LEDON(0) Turn on the LED and LEDOFF(1) turns off the LED 56 * @return : nothing 57 */ 58 void led_switch(u8 sta) 59 { 60 u32 val = 0; 61 if(sta == LEDON) { 62 val = readl(GPIOI_BSRR_PI); 63 val |= (1 << 16); 64 writel(val, GPIOI_BSRR_PI); 65 }else if(sta == LEDOFF) { 66 val = readl(GPIOI_BSRR_PI); 67 val|= (1 << 0); 68 writel(val, GPIOI_BSRR_PI); 69 } 70 } 71 72 /* 73 * @description : Unmap 74 * @return : nothing 75 */ 76 void led_unmap(void) 77 { 78 /* Unmap */ 79 iounmap(MPU_AHB4_PERIPH_RCC_PI); 80 iounmap(GPIOI_MODER_PI); 81 iounmap(GPIOI_OTYPER_PI); 82 iounmap(GPIOI_OSPEEDR_PI); 83 iounmap(GPIOI_PUPDR_PI); 84 iounmap(GPIOI_BSRR_PI); 85 } 86 87 /* 88 * @description : open device 89 * @param – inode : inode passed to driver 90 * @param - filp : The device file has a file structure called private_ Member variable of data 91 * Generally, private is used when open ing_ Data points to the device structure. 92 * @return : 0 success; Other failures 93 */ 94 static int led_open(struct inode *inode, struct file *filp) 95 { 96 filp->private_data = &dtsled; /* Set private data */ 97 return 0; 98 } 99 100 /* 101 * @description : Read data from device 102 * @param - filp : Device file to open (file descriptor) 103 * @param - buf : Data buffer returned to user space 104 * @param - cnt : Length of data to read 105 * @param - offt : Offset relative to the first address of the file 106 * @return : The number of bytes read. If it is negative, it indicates that the read failed 107 */ 108 static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 109 { 110 return 0; 111 } 112 113 /* 114 * @description : Write data to device 115 * @param - filp : A device file that represents an open file descriptor 116 * @param - buf : Data to be written to the device 117 * @param - cnt : Length of data to write 118 * @param - offt : Offset relative to the first address of the file 119 * @return : The number of bytes written. If it is negative, it indicates that the write failed 120 */ 121 static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 122 { 123 int retvalue; 124 unsigned char databuf[1]; 125 unsigned char ledstat; 126 127 retvalue = copy_from_user(databuf, buf, cnt); 128 if(retvalue < 0) { 129 printk("kernel write failed!\r\n"); 130 return -EFAULT; 131 } 132 133 ledstat = databuf[0]; /* Get status value */ 134 135 if(ledstat == LEDON) { 136 led_switch(LEDON); /* Turn on the LED */ 137 } else if(ledstat == LEDOFF) { 138 led_switch(LEDOFF); /* Turn off the LED */ 139 } 140 return 0; 141 } 142 143 /* 144 * @description : Turn off / release the device 145 * @param – filp : Device file to close (file descriptor) 146 * @return : 0 success; Other failures 147 */ 148 static int led_release(struct inode *inode, struct file *filp) 149 { 150 return 0; 151 } 152 153 /* Device operation function */ 154 static struct file_operations dtsled_fops = { 155 .owner = THIS_MODULE, 156 .open = led_open, 157 .read = led_read, 158 .write = led_write, 159 .release = led_release, 160 }; 161 162 /* 163 * @description : Drive exit function 164 * @param : nothing 165 * @return : nothing 166 */ 167 static int __init led_init(void) 168 { 169 u32 val = 0; 170 int ret; 171 u32 regdata[12]; 172 const char *str; 173 struct property *proper; 174 175 /* Get the attribute data in the device tree */ 176 /* 1,Get device node: stm32mp1_led */ 177 dtsled.nd = of_find_node_by_path("/stm32mp1_led"); 178 if(dtsled.nd == NULL) { 179 printk("stm32mp1_led node nost find!\r\n"); 180 return -EINVAL; 181 } else { 182 printk("stm32mp1_lcd node find!\r\n"); 183 } 184 185 /* 2,Get the content of the compatible property */ 186 proper = of_find_property(dtsled.nd, "compatible", NULL); 187 if(proper == NULL) { 188 printk("compatible property find failed\r\n"); 189 } else { 190 printk("compatible = %s\r\n", (char*)proper->value); 191 } 192 193 /* 3,Get the content of the status attribute */ 194 ret = of_property_read_string(dtsled.nd, "status", &str); 195 if(ret < 0){ 196 printk("status read failed!\r\n"); 197 } else { 198 printk("status = %s\r\n",str); 199 } 200 201 /* 4,Get reg property content */ 202 ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 12); 203 if(ret < 0) { 204 printk("reg property read failed!\r\n"); 205 } else { 206 u8 i = 0; 207 printk("reg data:\r\n"); 208 for(i = 0; i < 12; i++) 209 printk("%#X ", regdata[i]); 210 printk("\r\n"); 211 } 212 213 /* Initialize LED */ 214 /* 1,Register address mapping */ 215 MPU_AHB4_PERIPH_RCC_PI = of_iomap(dtsled.nd, 0); 216 GPIOI_MODER_PI = of_iomap(dtsled.nd, 1); 217 GPIOI_OTYPER_PI = of_iomap(dtsled.nd, 2); 218 GPIOI_OSPEEDR_PI = of_iomap(dtsled.nd, 3); 219 GPIOI_PUPDR_PI = of_iomap(dtsled.nd, 4); 220 GPIOI_BSRR_PI = of_iomap(dtsled.nd, 5); 221 222 /* 2,Enable PI clock */ 223 val = readl(MPU_AHB4_PERIPH_RCC_PI); 224 val &= ~(0X1 << 8); /* Clear previous settings */ 225 val |= (0X1 << 8); /* Set new value */ 226 writel(val, MPU_AHB4_PERIPH_RCC_PI); 227 228 /* 3,Set the general output mode of PI0.*/ 229 val = readl(GPIOI_MODER_PI); 230 val &= ~(0X3 << 0); /* bit0:1 Clear */ 231 val |= (0X1 << 0); /* bit0:1 Setting 01 */ 232 writel(val, GPIOI_MODER_PI); 233 234 /* 3,Set PI0 to push-pull mode.*/ 235 val = readl(GPIOI_OTYPER_PI); 236 val &= ~(0X1 << 0); /* bit0 Reset to pull-up*/ 237 writel(val, GPIOI_OTYPER_PI); 238 239 /* 4,Set PI0 to high speed.*/ 240 val = readl(GPIOI_OSPEEDR_PI); 241 val &= ~(0X3 << 0); /* bit0:1 Clear */ 242 val |= (0x2 << 0); /* bit0:1 Set to 10 */ 243 writel(val, GPIOI_OSPEEDR_PI); 244 245 /* 5,Set PI0 as pull-up.*/ 246 val = readl(GPIOI_PUPDR_PI); 247 val &= ~(0X3 << 0); /* bit0:1 Clear */ 248 val |= (0x1 << 0); /* bit0:1 Set to 01 */ 249 writel(val,GPIOI_PUPDR_PI); 250 251 /* 6,Default off LED */ 252 val = readl(GPIOI_BSRR_PI); 253 val |= (0x1 << 0); 254 writel(val, GPIOI_BSRR_PI); 255 256 /* Register character device driver */ 257 /* 1,Create device number */ 258 if (dtsled.major) { /* Defines the equipment number */ 259 dtsled.devid = MKDEV(dtsled.major, 0); 260 ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME); 261 if(ret < 0) { 262 pr_err("cannot register %s char driver [ret=%d]\n",DTSLED_NAME, DTSLED_CNT); 263 goto fail_map; 264 } 265 } else { /* No device number defined */ 266 ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* Application equipment No */ 267 if(ret < 0) { 268 pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DTSLED_NAME, ret); 269 goto fail_map; 270 } 271 dtsled.major = MAJOR(dtsled.devid); /* Get the master device number of the assigned number */ 272 dtsled.minor = MINOR(dtsled.devid); /* Get the secondary equipment number of the assigned number */ 273 274 } 275 printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor); 276 277 /* 2,Initialize cdev */ 278 dtsled.cdev.owner = THIS_MODULE; 279 cdev_init(&dtsled.cdev, &dtsled_fops); 280 281 /* 3,Add a cdev */ 282 ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT); 283 if(ret < 0) 284 goto del_unregister; 285 286 /* 4,Create class */ 287 dtsled.class = class_create(THIS_MODULE, DTSLED_NAME); 288 if (IS_ERR(dtsled.class)) { 289 goto del_cdev; 290 } 291 292 /* 5,Create device */ 293 dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME); 294 if (IS_ERR(dtsled.device)) { 295 goto destroy_class; 296 } 297 298 return 0; 299 300 destroy_class: 301 class_destroy(dtsled.class); 302 del_cdev: 303 cdev_del(&dtsled.cdev); 304 del_unregister: 305 unregister_chrdev_region(dtsled.devid, DTSLED_CNT); 306 fail_map: 307 led_unmap(); 308 return -EIO; 309 } 310 311 /* 312 * @description : Drive exit function 313 * @param : nothing 314 * @return : nothing 315 */ 316 static void __exit led_exit(void) 317 { 318 /* Unmap */ 319 led_unmap(); 320 321 /* Unregister character device driver */ 322 cdev_del(&dtsled.cdev); /* Delete cdev */ 323 unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /*cancellation*/ 324 325 device_destroy(dtsled.class, dtsled.devid); 326 class_destroy(dtsled.class); 327 } 328 329 module_init(led_init); 330 module_exit(led_exit); 331 MODULE_LICENSE("GPL"); 332 MODULE_AUTHOR("ALIENTEK"); 333 MODULE_INFO(intree, "Y");
dtsled.c contents in the document and newchrled in Chapter 22 The contents in the C file are basically the same, except dtsled C contains the code for processing the device tree. Let's focus on this part of the code.
Line 48, dtsled in equipment structure_ The member variable nd is added to dev, and Nd is device_node structure type pointer, indicating the device node. If we want to read the attribute value of a node in the device tree, we must first get the node, and generally add device to the device structure_ Node pointer variable to store this node.
Lines 177-183, pass OF_ find_ node_ by_ The path function gets stm32mp1_led node, and other subsequent OF functions need to use device_node.
Lines 186-191, pass of_ find_ The property function gets stm32mp1_ For the compatible attribute of LED node, the return value is the pointer variable of property structure type, and the member variable value of property represents the attribute value.
Lines 194-199, pass of_ property_ read_ The string function gets stm32mp1_ The status attribute value of the LED node.
Lines 202-211, pass of_ property_ read_ u32_ The array function gets stm32mp1_ The reg attribute of the LED node has all values, and the obtained values are stored in the regdata array. Line 209 outputs the obtained reg attribute values to the terminal in turn.
Lines 215-220, use of_iomap function reads reg attribute and memory mapping at one time, OF_ The iomap function is the OF function recommended for the device tree.
24.3.3 write test APP
This chapter directly uses the test APP in Chapter 22 and the ledapp in the previous chapter C file can be copied to the experimental project of this chapter.
24.4.1 compiling driver and testing APP
1. Compile driver
Write the Makefile file. The Makefile file of the experiment in this chapter is basically the same as the experiment in Chapter 20, except that the value of obj-m variable is changed to dtsled o. The contents of Makefile are as follows:
Example code 24.4.1.1 Makefile file
1 KERNELDIR := /home/zuozhongkai/linux/my_linux/linux-5.4.31 ...... 4 obj-m := dtsled.o ...... 11 clean: 12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
In line 4, set the value of obj-m variable to dtsled o.
Input the following command to compile the driver module file:
make -j32
After successful compilation, a driver module file named "dtsled.ko" will be generated.
2. Compile test APP
Enter the following command to compile the test ledapp C this test procedure:
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp
After the compilation is successful, the ledApp application will be generated.
24.4.2 operation test
Dtsled compiled from the previous section Copy Ko and ledApp files to rootfs/lib/modules/5.4.31 directory, restart the development board, enter the directory lib/modules/5.4.31, and enter the following command to load dtsled Ko drive module:
depmod / / this command needs to be run when the driver is loaded for the first time
modprobe dtsled / / load driver
After the driver is loaded successfully, some information will be output in the terminal, as shown in figure 24.4.2.1:
Figure 24.4.2.1 information output after successful driver loading
As can be seen from figure 24.4.2.1, stm32mp1_ The node LED is found, and the value of the compatible attribute is "atkstm32mp1 led", the value of the status attribute is "okay", and the value of the reg attribute is "0X50000A28 0X4 0X5000A000 0X4 0X5000A004 0X4 0X5000A008 0X4 0X5000A00C 0X4 0X5000A018 0X4", which are consistent with the device tree we set.
After the driver is loaded successfully, you can use ledApp software to test whether the driver works normally. Enter the following command to turn on the LED:
. / ledApp /dev/dtsled 1 / / turn on the LED
After entering the above command, observe whether the red LED light on the development board is on. If it is on, it indicates that the drive works normally. Turn off the LED after entering the following command:
. / ledApp /dev/dtsled 0 / / turn off the LED
After entering the above command, observe whether the red LED light on the development board is off. If you want to unload the driver, you can enter the following command:
rmmod dtsled.ko