return ;
}
module_init(led_init);
module_exit(led_exit);
Makefile
[cpp] view plain copy
print?
obj-m := led_driver.o
KDIR :=/home/workdir/kernel/linux-2.6.38
all:
make -C $(KDIR) M=$(shell pwd) modules
install:
cp driver_led.ko /tftpboot/
clean:
make -C $(KDIR) M=$(shell pwd) clean
Test Files
test_led.c
[cpp] view plain copy
print?
#include #include #include #include #define LED_OFF 0 #define LED_ON 1 #define LED_1_ON 2 #define LED_1_OFF 3 #define LED_2_ON 4 #define LED_2_OFF 5 #define LED_3_ON 6 #define LED_3_OFF 7 #define LED_4_ON 8 #define LED_4_OFF 9 int main (void) { int i=0; int fd; char buf[10]={ LED_ON , LED_OFF , LED_1_ON, LED_1_OFF, LED_2_ON, LED_2_OFF, LED_3_ON, LED_3_OFF, LED_4_ON, LED_4_OFF, }; fd = open("/dev/led",O_RDWR); if (fd < 0) { printf ("Open /dev/led file errorn"); return -1; } while(i<10) { write(fd,&buf[i],4); sleep(1); i++; } close (fd); return 0; } If the above compilation is OK, you can download it to the board for testing. ------------------------------------------------------------------------------------------ #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "leds" /* After loading mode, execute the "cat /proc/devices" command to see the device name*/ #define LED_MAJOR 231 /* Main device number*/ /* The second parameter when the application executes ioctl(fd, cmd, arg)*/ #define IOCTL_LED_ON 1 #define IOCTL_LED_OFF 0 /* Used to specify the GPIO pin used by the LED */ static unsigned long led_table[] = { S3C2410_GPB5, S3C2410_GPB6, S3C2410_GPB7, S3C2410_GPB8, }; /* Used to specify the function of the GPIO pin: output*/ static unsigned int led_cfg_table[] = { S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP, }; struct leds_type { struct cdev cdev; }; struct leds_type *my_leds_dev; /* When the application executes open(...) on the device file /dev/EmbedSky-leds, * The EmbedSky_leds_open function will be called */ static int EmbedSky_leds_open(struct inode *inode, struct file *file) { int i; for (i = 0; i < 4; i++) { // Set the function of the GPIO pin: The GPIO pin involved in the LED in this driver is set to output function s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); } return 0; } /* When the application executes ioclt(...) on the device file /dev/EmbedSky-leds, * The EmbedSky_leds_ioctl function will be called */ static int EmbedSky_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if (arg > 4) { return -EINVAL; } switch(cmd) { case IOCTL_LED_ON: // Set the output level of the specified pin to 0 s3c2410_gpio_setpin(led_table[arg], 0); return 0; case IOCTL_LED_OFF: // Set the output level of the specified pin to 1 s3c2410_gpio_setpin(led_table[arg], 1); return 0; default: return -EINVAL; } } /* This structure is the core of the character device driver * The open, read, write and other functions called when the application operates the device file. * The corresponding function specified in this structure will eventually be called */ static struct file_operations EmbedSky_leds_fops = { .owner = THIS_MODULE, /* This is a macro that pushes to the __this_module variable that is automatically created when compiling the module*/ .open = EmbedSky_leds_open, .ioctl = EmbedSky_leds_ioctl, }; static char __initdata banner[] = "TQ2440/SKY2440 LEDS, (c) 2008,2009 www.embedsky.net/n"; static struct class *led_class; /* * This function will be called when executing the "insmod EmbedSky_leds.ko" command */ static int __init EmbedSky_leds_init(void) { int ret; dev_t devno=MKDEV(LED_MAJOR,0); printk("init led/n"); printk(banner); /* Register character device driver * The parameters are the major device number, device name, and file_operations structure; * In this way, the major device number is associated with the specific file_operations structure. * When operating the device file whose main device is LED_MAJOR, the relevant member functions in EmbedSky_leds_fops will be called ret = register_chrdev_region(devno, 1,DEVICE_NAME); //Get the device number my_leds_dev = kmalloc(sizeof(struct leds_type),GFP_KERNEL); /*This must be present, otherwise the error "Unable to handle kernel NULL pointer dereference at virtual addres 00000000" will appear when loading the module. This is because my_leds_dev is just a pointer here, and there is no allocated memory of the corresponding size, so an error will occur when using it. It is quite troublesome to find this error*/ if(!my_leds_dev) { ret=-ENOMEM; goto fail_malloc; } memset(my_leds_dev,0,sizeof(struct leds_type)); cdev_init(&(my_leds_dev->cdev),&EmbedSky_leds_fops); ret = cdev_add(&(my_leds_dev->cdev),devno,1); /*Note: Different from the earlier device registration method, the earlier direct register_chrdev() method was sufficient. */ if(ret)printk(KERN_NOTICE"ERROR %d",ret); //Register a class so that mdev can create device nodes under the "/dev/" directory led_class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(led_class)) { printk("Err: failed in EmbedSky-leds class. /n"); return -1; } //Create a device node named DEVICE_NAME device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME); printk(DEVICE_NAME " initialized/n"); return 0; fail_malloc: unregister_chrdev_region(devno,1); return ret; } /* * This function will be called when executing the "rmmod EmbedSky_leds.ko" command */ static void __exit EmbedSky_leds_exit(void) { /* Uninstall the driver */ unregister_chrdev(LED_MAJOR, DEVICE_NAME); device_destroy(led_class, MKDEV(LED_MAJOR, 0)); //Delete the device node class_destroy(led_class); //Deregister class } /* These two lines specify the initialization function and uninstallation function of the driver */ module_init(EmbedSky_leds_init); module_exit(EmbedSky_leds_exit); /* Describe some information about the driver, not required */ MODULE_AUTHOR("http://www.embedsky.net"); // Driver author MODULE_DESCRIPTION("TQ2440/SKY2440 LED Driver"); // Some description information MODULE_LICENSE("GPL"); // Comply with the agreement In the above code, the led_table array is equivalent to the index of the four IO ports of GPB. Through these four values, relevant operations are performed on these four IO ports. For example: S3C2410_GPB5 = S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5) = S3C2410_GPIO_BANKB + 5 = 32*1 + 5 In s3c2410_gpio_setpin(S3C2410_GPB5,0), the function first obtains the virtual address and offset address of GPB through S3C2410_GPB5, and then operates the GPBDAT register of GPB5. void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) { void __iomem *base = S3C2410_GPIO_BASE(pin); unsigned long offs = S3C2410_GPIO_OFFSET(pin); unsigned long flags; unsigned long dat; local_irq_save(flags); dat = __raw_readl(base + 0x04); //Read GPIO DAT data to dat dat &= ~(1 << offs); //First pull the IO port to be set low dat |= to << offs; //Assign the to value of the parameter to dat __raw_writel(dat, base + 0x04); //Finally write the DAT value into GPIO's DAT local_irq_restore(flags); } The above function calls two sub-functions, which are defined as follows #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO) #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) The definition of S3C24XX_VA_GPIO is as follows: #define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000) #define S3C2410_ADDR(x) (0xF0000000 + (x)) Here, the base address of S3C2410_ADDR is 0xF0000000(??), which is the base address of the virtual addresses of all registers of 2440. 0x00E00000 indicates the offset address of GPIO of 2440, which means that the first address of the virtual address of its GPIO is 0xF0E00000. Let's look at the definition of S3C2410_GPIO_BASE(pin). We can put the value of S3C2410_GPB5 into the calculation, and we can get (S3C2410_GPB5&~31)=32. The purpose is to remove the offset value of GPB, and then shift it right by one bit, and add it to the first address of the GPIO virtual address. Therefore, S3C2410_GPIO_BASE(pin) only represents the virtual address of the corresponding GPIO group. For example, the virtual address of GPB is 10000(B)+0xF0E00000=0xF0E00010. By analogy, the offset addresses of all GPIOs can be obtained, as shown in the following table: BANK (pin&~31) (pin&~31)>>1
Load the driver insmod led_driver.ko
Create the device file mknod /dev/led c 243 0 Where 243 should be consistent with the device number in the driver file
Run the test file ./test_led
to complete.
References:
Previous article:Tips for getting started with ARM and embedded systems
Next article:Samsung S3C2440 ARM920T core processor register arrangement-very useful information
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- Rambus Launches Industry's First HBM 4 Controller IP: What Are the Technical Details Behind It?
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- [TI mmWave Radar Evaluation]_5_AWR1843BOOST Corridor Ranging Corner Reflector Test
- ARM working mode
- Shanghai Optoelectronics IC Company needs analog IC design engineers
- MakeCode now supports ESP32 and ESP32-S2
- When planning for Industry 4.0, what do you see from these two buying cases?
- Sensor board provides solutions for Bluetooth applications in IoT
- uf2 bootloader for nRF52840DK development board
- MSP430 MCU Register Summary - CPU Register
- A New Method of Expanding SRAM Externally in MCU
- CircuitPython 6.0.0 Beta 0 released!