首先借用一张图说明linux应用程序和内核的关系
成都创新互联自成立以来,一直致力于为企业提供从网站策划、网站设计、成都网站制作、成都做网站、外贸营销网站建设、电子商务、网站推广、网站优化到为企业提供个性化软件开发等基于互联网的全面整合营销服务。公司拥有丰富的网站建设和互联网应用系统开发管理经验、成熟的应用系统解决方案、优秀的网站开发工程师团队及专业的网站设计师团队。
与裸机程序不同,linux下的应用开发并不直接访问硬件,而是由应用程序调用驱动来访问硬件,这是linux的系统结构,具体可参考《嵌入式linux应用开发完全手册》。
回到am335x,已经在uboot实现LED的操作,基本原理一致,设置GPIO输出,低电平点亮LED。如果想通过内核空间操作文件的方式访问GPIO,可参考 BeagleBone的GPIO控制
linux3.2中driver/leds目录下存放有led相关文件,在此编写led驱动模块。代码参照 Am335x 下GPIO控制实例
添加文件leds-run.c
```
#include#include #include #include #include #include #include #include #include #include #define TEST_IO_NUM 89 #define NAME_MISC "GpioTest" #define NAME_MOUDULE "GpioTest1" #define USE_MISC_MODE 1 static int major = 251; void GpioTest(void); static long GpioIOctl(struct file *filp, unsigned cmd, unsigned long arg) { GpioTest(); return 1; } void GpioTest(void) { int iCount = 0; for(iCount = 0; iCount <=20; iCount++ ) { if(iCount%2 == 0) { gpio_direction_output(TEST_IO_NUM, 1); printk(KERN_INFO"#######LED statu is high.\r\n"); } else { gpio_direction_output(TEST_IO_NUM, 0); printk(KERN_INFO"#######LED statu is low.\r\n"); } mdelay(3000); } printk(KERN_INFO"#######App run over!"); } static int GpioOpen(struct inode *inode, struct file *file) { int iRen = -1; iRen = gpio_request(TEST_IO_NUM, "LED"); if(iRen < 0) { printk(KERN_INFO"#######Failed to request the LED!"); }else { printk(KERN_INFO"#######Success to request the LED"); } return iRen; } static int GpioClose(struct inode *inode, struct file *file) { printk(KERN_INFO"#######Free the LED"); gpio_free(TEST_IO_NUM); return 1; } //****entry point for TEST GPIO module static const struct file_operations gpio_test_driver = { .owner = THIS_MODULE, .unlocked_ioctl= GpioIOctl, .llseek = no_llseek, .open = GpioOpen, .release = GpioClose, }; #if USE_MISC_MODE static struct miscdevice gpiotest_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = NAME_MISC, .fops = &gpio_test_driver, }; #endif static int __init GpioTestInit(void) { int iRet; printk(KERN_INFO"#######GpioTest modules is install!\r\n"); #if USE_MISC_MODE iRet = misc_register(&gpiotest_misc_device); if (iRet) { printk(KERN_INFO"#######unable to register a misc device\r\n"); return iRet; } #else iRet = register_chrdev(major, NAME_MOUDULE, &gpio_test_driver); if (iRet < 0) { printk(KERN_INFO"#######unable to register a chr device\r\n"); return iRet; } #endif return iRet; } static void __exit GpioTestExit(void) { #if USE_MISC_MODE misc_deregister(&gpiotest_misc_device); #else unregister_chrdev(major, NAME_MOUDULE); #endif printk(KERN_INFO"#######GpioTest modules is exit!\r\n"); } module_init(GpioTestInit); module_exit(GpioTestExit); MODULE_AUTHOR("XXXXXXXXXXXX"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("System status led");
```
driver/leds目录下修改Kconfig,添加一项:
config GPIO_LED bool "Gpio LED test" help Just test the Gpio LED status
同目录下Makefile文件修改:
obj-$(CONFIG_GPIO_LED)+=leds-run.o
通过make menuconfig,将驱动模块GPIO_LED编译进内核。
应用程序app.c
#include#include #include #include #include #include #include #include int main(int argc, char *argv) { int fd; fd = open("/dev/GpioTest", O_RDWR); if(fd < 0) { printf("***Can't open the gpiotest!\r\n"); return -1; } ioctl(fd, 0, 0); close(fd); printf("***App run over!\r\n"); return 1; }
烧写内核,内核启动后打印“#######GpioTest modules is install!”,运行app,可以正常运行,可led没有按照程序进行亮灭操作。
细想整个过程发现,am335x的IO口是复用的,需要先完成mux设置。
再回到linux内核,需要明确linux的运行流程,此次参考了 linux内核代码分析1 TI am335x:
Board-am335xevm.c(./arch/arm/mach-omap2)中开始执行,
MACHINE_START(AM335XEVM, "am335xevm") /* Maintainer: Texas Instruments */ .atag_offset= 0x100, .map_io= am335x_evm_map_io, .init_early= am33xx_init_early, .init_irq= ti81xx_init_irq, .handle_irq = omap3_intc_handle_irq, .timer= &omap3_am33xx_timer, .init_machine= am335x_evm_init, MACHINE_END
转到启动程序am335x_evm_init:
am33xx_cpuidle_init(); am33xx_mux_init(board_mux); omap_serial_init(); am335x_evm_i2c_init(); am335x_evm_setup(); omap_sdrc_init(NULL, NULL); usb_musb_init(&musb_board_data);
static void am335x_evm_setup() { setup_general_purpose_evm(); }
static void setup_general_purpose_evm(void) { _configure_device(boardid,invt_evm_dev_cfg, -1); }
所以初始化开发板的设置,重点就是invt_evm_dev_cfg
static struct evm_dev_cfginvt_evm_dev_cfg[] = { {enable_ecap0, DEV_ON_BASEBOARD, PROFILE_NONE}, {lcdc_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {mfd_tscadc_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {rmii1_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {rmii2_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {usb0_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {usb1_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {evm_nand_init,DEV_ON_BASEBOARD,PROFILE_NONE}, {mmc0_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {spi0_init, DEV_ON_BASEBOARD, PROFILE_NONE}, {led_init, DEV_ON_BASEBOARD, PROFILE_NONE}, //添加led操作 };
static void led_init(int evm_id, int profile) { int err; setup_pin_mux(gpio_led_mux); err = platform_device_register(&leds_gpio); if (err) pr_err("failed to register gpio led device\n"); }
static struct pinmux_config gpio_led_mux[] = { {"lcd_ac_bias_en.gpio2_25", OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT}, //LED GPIO 配置 {NULL, 0}, };
修改完成后,重新编译内核,烧写内核,运行程序,成功实现LED的操作。