接上一篇(基础概念),为ap autosar 做准备。(两篇linux的入门点灯 + 两篇 C++ 基本操作 正式开始 AP AUTOSAR)
下面我们开始试着去实现梦的开始 -- 点灯!!
硬件原理图
官方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 查出 相关时钟配置寄存器
时钟CCM 寄存器具体配置bit位 含义
所以这里如果我们想打开 IOMUXC_CSI_HSYNC_GPIO4_IO20的时钟。就需要对地址0x020C4074 的12-13位 用11b去取或。
引脚复用
所有pin角名称:CSI_HSYNC(上面已经说明了)
- 把引脚复用成需要的功能
这里需要对地址0x020E01E0进行操作
2. 对pin角控制寄存器进行配置
这里需要对地址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 |
如何计算管角号(下面需要定义管角号)
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");
完结撒花。内容参考书本例程,过程纯手打,图来自芯片手册,yeah. 希望对 想学习 ap autosar 的小伙伴 有 一丢丢丢丢丢丢丢丢帮助。
内容来源:零束开发者论坛