0
点赞
收藏
分享

微信扫一扫

linux梦的开始.点灯(具体实现)

接上一篇(基础概念),为ap autosar 做准备。(两篇linux的入门点灯 + 两篇 C++ 基本操作 正式开始 AP AUTOSAR)

下面我们开始试着去实现梦的开始 -- 点灯!!

硬件原理图

linux梦的开始.点灯(具体实现)_引脚

官方SDK定义

#include "fsl_iomuxc.h" //file here. please find it in official SDK, 

#define IOMUXC_CSI_HSYNC_GPIO4_IO20                          0x020E01E0U, 0x5U, 0x00000000U, 0x0U, 0x020E046CU

**IOMUXC**: 引脚复用宏定义

**CSI_HSYNC**: 原理图引脚名称

**GPIO4_IO20**: 引脚的复用功能

时钟配置

芯片手册clock config module 查出 相关时钟配置寄存器

linux梦的开始.点灯(具体实现)_linux_02

时钟CCM 寄存器具体配置bit位 含义

linux梦的开始.点灯(具体实现)_#include_03

所以这里如果我们想打开 IOMUXC_CSI_HSYNC_GPIO4_IO20的时钟。就需要对地址0x020C4074 的12-13位 用11b去取或。

引脚复用

所有pin角名称:CSI_HSYNC(上面已经说明了)

  1. 把引脚复用成需要的功能

linux梦的开始.点灯(具体实现)_linux_04

这里需要对地址0x020E01E0进行操作

2. 对pin角控制寄存器进行配置

linux梦的开始.点灯(具体实现)_#include_05

这里需要对地址0x020E046C进行操作

这两个地址可以直接从官方SDK 里面找到,这里只是进行了芯片手册查询(很重要)。

ok到现在已经把前期准备工作做好了。下面实现linux 内核驱动点灯。

这里请结合上文的硬件解释 和 上一篇linux字符驱动基础知识 来完成下面代码。

GPIO4 相关寄存器地址

20A_8000

GPIO data register (GPIO4_DR)

32

R/W

0000_0000h

28.5.1/1358

20A_8004

               GPIO direction register (GPIO4_GDIR)

32

R/W

0000_0000h

28.5.2/1359

20A_8008

               GPIO pad status register (GPIO4_PSR)

32

R

0000_0000h

28.5.3/1359

20A_800C

               GPIO interrupt configuration register1 (GPIO4_ICR1)

32

R/W

0000_0000h

28.5.4/1360

20A_8010

               GPIO interrupt configuration register2 (GPIO4_ICR2)

32

R/W

0000_0000h

28.5.5/1364

20A_8014

               GPIO interrupt mask register (GPIO4_IMR)

32

R/W

0000_0000h

28.5.6/1367

20A_8018

               GPIO interrupt status register (GPIO4_ISR)

32

w1c

0000_0000h

28.5.7/1368

20A_801C

               GPIO edge select register (GPIO4_EDGE_SEL)

32

R/W

0000_0000h

28.5.8/136

linux梦的开始.点灯(具体实现)_linux_06

如何计算管角号(下面需要定义管角号)

GPIOn_IOx = (n-1)*32+x

所以例子中GPIO4_IO20 是 116

代码实现

/*  Include necessary linux header files */


#include <linux/init.h>


#include <linux/module.h>


#include <linux/cdev.h>


#include <linux/fs.h>


#include <linux/uaccess.h>


#include <linux/io.h>





#define DEV_NAME "led_chrdev" //define device name


#define DEV_CNT (1) //how many leds. here we only define 1





static dev_t devno;


struct class *led_chrdev_class;





/*led char device structure*/


struct led_chrdev{


struct cdev dev; // describe a char device structure


unsigned int __iomem *va_dr; // data register virtual address pointer


unsigned int __iomem *va_gdir; // input output direction register virtual address pointer


unsigned int __iomem *va_iomuxc_mux; // port multiplexer register virtual address pointer


unsigned int __iomem *va_ccm_ccgrx; // clock config register virtual address pointer


unsigned int __iomem *va_iomux_pad; // port pin property register virtual address pointer





// below parameters are physical address. you can check virtual address comment.


unsigned long pa_dr;


unsigned long pa_gdir;


unsigned long pa_iomuxc_mux;


unsigned long pa_ccm_ccgrx;


unsigned long pa_iomux_pad;





unsigned int led_pin; // led pin


unsigned int clock_offset; // clock offset address. base on CCM_CCGRx


};





// physical address, you can find in previous chapters. EACH ADDRESS can be found... CHUN SHOU DA...





static struct led_chrdev led_cdev[DEV_CNT] = {


{


.pa_dr = 0x020A8000, \


.pa_gdir = 0x020A8004, \


.pa_ccm_ccgrx = 0x020C4074, \


.pa_iomuxc_mux = 0x020E01E0, \


.pa_iomux_pad = 0x020E046C, \


.led_pin = 116, \


.clock_offset = 12,


}


};





/*


static struct led_chrdev led_cdev[DEV_CNT] = {


{


.pa_dr = 0x0209C000, \


.pa_gdir = 0x0209C004, \


.pa_ccm_ccgrx = 0x20C406C, \


.pa_iomuxc_mux = 0x20E006C, \


.pa_iomux_pad = 0x20E02F8, \


.led_pin = 4, \


.clock_offset = 26,


}


};


*/





static int led_chrdev_open(struct inode *inode, struct file *filp)


{


unsigned int val = 0;


struct led_chrdev *led_cdev = (struct led_chrdev *)container_of(inode->i_cdev, struct led_chrdev, dev);





filp->private_data = container_of(inode->i_cdev,struct led_chrdev, dev);





printk("Open \n");








led_cdev->va_dr = ioremap(led_cdev->pa_dr, 4);


led_cdev->va_gdir = ioremap(led_cdev->pa_gdir, 4);


led_cdev->va_iomuxc_mux = ioremap(led_cdev->pa_iomuxc_mux, 4);


led_cdev->va_ccm_ccgrx = ioremap(led_cdev->pa_ccm_ccgrx, 4);


led_cdev->va_iomux_pad = ioremap(led_cdev->pa_iomux_pad, 4);





val = ioread32(led_cdev->va_ccm_ccgrx);


val &= ~(3 << (led_cdev->clock_offset));


val |= (3 << (led_cdev->clock_offset));





iowrite32(val, led_cdev->va_ccm_ccgrx);


iowrite32(5, led_cdev->va_iomuxc_mux);


iowrite32(0x1F838, led_cdev->va_iomux_pad);





val = ioread32(led_cdev->va_gdir);


val &= ~(1 << (led_cdev->led_pin));


val |= (1 << (led_cdev->led_pin));


iowrite32(val, led_cdev->va_gdir);





val = ioread32(led_cdev->va_dr);


val |= (0x01 << (led_cdev->led_pin));


iowrite32(val, led_cdev->va_dr);





return 0;


}








static int led_chrdev_release(struct inode *inode, struct file *filp)


{


struct led_chrdev *led_cdev = (struct led_chrdev *) container_of(inode->i_cdev, struct led_chrdev, dev);





iounmap(led_cdev->va_dr);


iounmap(led_cdev->va_gdir);


iounmap(led_cdev->va_iomuxc_mux);


iounmap(led_cdev->va_ccm_ccgrx);


iounmap(led_cdev->va_iomux_pad);


return 0;


}








static ssize_t led_chrdev_write(struct file *filp, const char __user * buf, size_t count, loff_t * ppos)


{


unsigned long val = 0;


unsigned long ret = 0;





int tmp = count;





kstrtoul_from_user(buf,tmp,10,&ret);


struct led_chrdev *led_cdev = (struct led_chrdev *) filp->private_data;





val = ioread32(led_cdev->va_dr);


if(ret == 0)


{


val &= ~(0x01 << led_cdev->led_pin);


}


else


{


val |= (0x01 << led_cdev->led_pin);


}





iowrite32(val, led_cdev->va_dr);


*ppos += tmp;





return tmp;


}





static struct file_operations led_chrdev_fops = {


.owner = THIS_MODULE,


.open = led_chrdev_open,


.release = led_chrdev_release,


.write = led_chrdev_write,


};





//kernel module init(load) and exit(release)


static __init int led_chrdev_init(void)


{


int i = 0;


dev_t cur_dev;


printk("Led chrdev Init\n");


/**


* alloc_chrdev_region() - register a range of char device numbers


* @dev: output parameter for first assigned number


* @baseminor: first of the requested range of minor numbers


* @count: the number of minor numbers required


* @name: the name of the associated device or driver


*


* Allocates a range of char device numbers. The major number will be


* chosen dynamically, and returned (along with the first minor number)


* in @dev. Returns zero or a negative error code.


*/





alloc_chrdev_region(&devno,0,DEV_CNT,DEV_NAME); //dynamic apply device number, detail can be found in previous comment.





/*


* class_create - create a struct class structure


* @owner: pointer to the module that is to "own" this struct class


* @name: pointer to a string for the name of this class.


* @key: the lock_class_key for this class; used by mutex lock debugging


*


* This is used to create a struct class pointer that can then be used


* in calls to device_create().


*


* Returns &struct class pointer on success, or ERR_PTR() on error.


*


* Note, the pointer created here is to be destroyed when finished by


* making a call to class_destroy().


*/





/* 源码备注


THIS_MODULE


__visible struct module __this_module


__attribute__((section(".gnu.linkonce.this_module"))) = {


.name = KBUILD_MODNAME,


.init = init_led_chrdev_fopsmodule,


#ifdef CONFIG_MODULE_UNLOAD


.exit = cleanup_module,


#endif


.arch = MODULE_ARCH_INIT,


};


网上找的 对 THIS_MODULE 的 解释


#define THIS_MODULE (&__this_module)


是 一个struct module变量,代表当前模块,与那个著名的current有几分相似,可以通过THIS_MODULE宏来引用模块的struct module结构,比如使用THIS_MODULE->state可以获得当前模块的状态。现在你应该明白为啥在那个岁月里,你需要毫不犹豫毫不迟 疑的将struct usb_driver结构里的owner设置为THIS_MODULE了吧,这个owner指针指向的就是你的模块自己。那现在owner咋就说没就没了 那?这个说来可就话长了,咱就长话短说吧。不知道那个时候你有没有忘记过初始化owner,反正是很多人都会忘记,大家都把注意力集中到probe、 disconnect等等需要动脑子的角色上面了,这个不需要动脑子,只需要花个几秒钟指定一下的owner反倒常常被忽视,这个就是容易得到的往往不去 珍惜,不容易得到的往往日日思量着去争取。于是在2006年的春节前夕,在咱们都无心工作无心学习等着过春节的时候,Greg坚守一线,去掉了 owner,于是千千万万个写usb驱动的人再也不用去时刻谨记初始化owner了。咱们是不用设置owner了,可core里不能不设置,struct usb_driver结构里不是没有owner了么,可它里面嵌的那个struct device_driver结构里还有啊,设置了它就可以了。于是Greg同时又增加了usb_register_driver()这么一 层,usb_register()可以通过将参数指定为THIS_MODULE去调用它,所有的事情都挪到它里面去做。反正usb_register() 也是内联的,并不会增加调用的开销。





*/


led_chrdev_class = class_create(THIS_MODULE, "led_chrdev");





for(i=0; i<DEV_CNT;i++)


{


cdev_init(&led_cdev[i].dev, &led_chrdev_fops); //combine led_cdev to file operations


led_cdev[i].dev.owner = THIS_MODULE;


cur_dev = MKDEV(MAJOR(devno),MINOR(devno)+i); // get devno's major and minor number to cur_dev


cdev_add(&led_cdev[i].dev, cur_dev, 1); // add a device to system


device_create(led_chrdev_class,NULL,cur_dev,NULL,DEV_NAME "%d",i); // load and create a device.


}





return 0;


}





module_init(led_chrdev_init);





static __exit void led_chrdev_exit(void)


{


int i;


dev_t cur_dev;


printk("led chrdev exit\n");


for(i = 0; i<DEV_CNT;i++)


{


cur_dev = MKDEV(MAJOR(devno), MINOR(devno) +i);


device_destroy(led_chrdev_class,cur_dev);


cdev_del(&led_cdev[i].dev);


}





unregister_chrdev_region(devno, DEV_CNT);


class_destroy(led_chrdev_class);


}





module_exit(led_chrdev_exit);





MODULE_AUTHOR("caiji");


MODULE_LICENSE("GPL");

linux梦的开始.点灯(具体实现)_linux_07

完结撒花。内容参考书本例程,过程纯手打,图来自芯片手册,yeah. 希望对 想学习 ap autosar 的小伙伴 有 一丢丢丢丢丢丢丢丢帮助。

内容来源:零束开发者论坛

linux梦的开始.点灯(具体实现)_#include_08

举报

相关推荐

0 条评论