关于友善之臂出的这款contexA9开发板,目前在网络上的资源较少,特别是内核的,非常之少,鉴于这种情况,我将会写一个系列的驱动来做关于tiny4412这款板子开发的总结。

简单介绍一下:

Tiny4412是一款高性能的四核Cortex-A9核心板,由广州友善之臂设计、生产和发行销售。它采用三星Exynos4412作为主处理器,运行主频可高达1.5GHz,Exynos4412内部集成了Mali-400 MP高性能图形引擎,支持3D图形流畅运行,并可播放1080P大尺寸高清视频。三星旗舰智能手机Galaxy S3即是采用此CPU设计。

我用的是普通版.也就是只有一个串口的.但是核心板是一样的。

好了,介绍完毕,前面的文章我们已经说过了如何编写一个字符设备的驱动程序,这里就不再继续扯字符驱动怎么写,非常简单了,看看就懂了。

我们进入整题,今天,我们需要实现一个LED的驱动程序。在友善之臂的核心板上,有4颗LED灯,如何编写一个驱动程序,让它亮起来,首先我们来看看核心板:

LED灯就位于右上角,第一个和第二个都是电源指示灯,我们不需要管它,我们只管后面那4个LED灯。

如何编写?

1、首先找到板子的原理图,找到对应的引脚。

2、接着打开数据手册,找到对应的寄存器。

3、开始编写LED驱动程序

4、编写makefile

5、插入模块insmod xxx.ko

6、查询主设备号 cat /proc/devices

7、创建设备节点 mknod /dev/xxx c x x

8、执行应用程序app

对应的原理图:

从这里我们可以得出一个结论,LED灯是低电平点亮的,也就是往对应的端口里写0,LED灯就亮了。从最下面一幅图可以知道,我们要找的寄存器是GPIO的GPM4开头的这个寄存器,现在我们进入查数据手册的阶段.

查手册:

我们找到手册的第288页GPIO章节的GPMCON这里:

这是我们要配置端口的模式的IO口,端口有以上的一些状态,在这里我们只考虑输出,也就是只要配置Output那一项就可以了。

我们要配的寄存器有GPM4CON[0],GPM4CON[1],GPM4CON[2],GPM4CON[3],这四位,分别配置成output输出模式.

接下来再看一个GPM4DAT,这个是端口的状态寄存器,对状态寄存器就是写0或者写1,那么LED就被驱动了,我们来看看:

好了,寄存器我们已经找到了,接下来,可以进入写代码的阶段了:

首先编写LED驱动程序:

<span style="font-size:18px;">#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
//这个是设备的名称,也就是对应在/dev/test-dev
#define DEV_NAME	"test-dev"
//LED灯IO口的地址,也就是刚刚我们在上面的芯片手册看到的Address
#define GPM4COM     0x110002E0
//定义配置模式的指针变量
volatile unsigned long *led_config = NULL ;
//定义配置状态的指针变量
volatile unsigned long *led_dat = NULL ;
//open方法,对LED灯进行初始化
int led_open(struct inode *inode, struct file *filp)
{
	printk("led_open\n");//上层程序对LED进行Open操作的时候会执行这个函数
	//先对LED的端口进行清0操作
	*led_config &= ~(0xffff);
	//将4个IO口16位都设置为Output输出状态
	*led_config |= (0x1111);
	return 0;
}
//write方法
int led_write(struct file *filp , const char __user *buf , size_t count , loff_t *f_pos)
{
	int val ;
	//注意,这里是在内核中进行操作,我们需要使用copy_from_user这个函数将用户态的内容拷贝到内核态
	copy_from_user(&val , buf , count);
	//以下就是当val是哪个值的时候,led就执行相应的操作,这里不多说
	switch(val)
	{
		case 0 :
		        //对状态寄存器进行赋值,以下雷同
				printk(KERN_EMERG"led1_on\n");
				*led_dat &= ~0x1 ;
				break ;
		case 1 :
				printk(KERN_EMERG"led2_on\n");
				*led_dat &= ~0x2 ;
				break ;
		case 2 :
				printk(KERN_EMERG"led3_on\n");
				*led_dat &= ~0x4 ;
				break ;
		case 3 :
				printk(KERN_EMERG"led4_on\n");
				*led_dat &= ~0x8 ;
				break ;
		case 4 :
				printk(KERN_EMERG"ledall_on\n");
				*led_dat &= ~0xf ;
				break ;
		case 5 :
				printk(KERN_EMERG"ledall_off\n");
				*led_dat |= 0xf ;
				break ;

	}
}
//close方法
int led_close(struct inode *inode, struct file *filp)
{
	printk("led_close\n");
	*led_dat |= 0xf ;  //全灭,因为高电平是灭的,0xf ----> 1111
	return 0;
}
//用ioctl这个方法也可以实现LED的操作的,自己去实现吧
#if 0
long led_ioctl(struct file *filp, unsigned int request, unsigned long arg)
{
	switch(request)
	{
		case 0:
			printk(KERN_EMERG"led1 on\n");
			*led_dat &=~0x1 ;
			break;

		case 1:
			printk(KERN_EMERG"led2 on\n");
			*led_dat &=~0x2 ;
			break;

		case 3:
			printk(KERN_EMERG"led3 on\n");
			*led_dat &=~0xf ;
			break;

		case 4:
			printk(KERN_EMERG"led4 on\n");
			*led_dat &=~0x8 ;
			break ;
		default :
			*led_dat |= 0xf ;
	}
}
#endif
//对方法进行初始化
struct file_operations fops = {
	.owner = THIS_MODULE ,
	.open = led_open,
	.release = led_close,
//	.unlocked_ioctl = led_ioctl,
	.write = led_write,
};
//主设备号
int major ;
//启动函数
static __init int test_init(void)
{
	printk("led_init\n");
	major = register_chrdev(major, DEV_NAME, &fops);
	led_config = (volatile unsigned long *)ioremap(GPM4COM , 16);
	led_dat = led_config + 1 ;
	return 0;
}
//注销函数
static __exit void test_exit(void)
{
	printk("led_exit\n");
	unregister_chrdev(major, DEV_NAME);
	iounmap(led_config);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Y.X.YANG");
MODULE_VERSION("2016.1.15");</span>

以上就是led这个设备驱动的编写框架。看不懂的可以去学学linux内核设备驱动再来看就很简单了。其实跟单片机的编程差不了多少的,只不过内核驱动是按照框架来编写的,有所驱动就在这里。

驱动程序编写完了,接下来我们编写上层应用层的程序:

<span style="font-size:18px;">#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
	int fd;
	int val = 0 ;
	//打开对应的设备
	fd = open("/dev/test-dev",O_RDWR) ;
	if(-1 == fd)
	{
		printf("open fair!\n");
		return -1 ;
	}
	while(1){
		val = 0 ;
		//写write方法就会调用到驱动程序的led_write
		//最后我们能看到的结果是led灯做流水灯的实现,然后全灭,再周而复始
		write(fd , &val , 4);
		sleep(1);
		val = 1 ;
		write(fd , &val , 4);
		sleep(1);
		val = 2 ;
		write(fd , &val , 4);
		sleep(1);
		val = 3 ;
		write(fd , &val , 4);
		sleep(1);
		val = 5 ;
		write(fd , &val , 4);
		sleep(1);
	}
	return 0;
}</span>

好了,程序已经写完了,我们来看看makefile怎么写.

<span style="font-size:18px;">#将你所写的驱动程序编译成模块形式
obj-m	+= leds.o
#你需要的文件系统
ROOTFS = /disk/A9/filesystem
#你需要的内核
KERNEL = /disk/A9/linux-3.5/
#模块编译
all:
	make -C $(KERNEL) M=`pwd` modules
#模块清除
clean:
	make -C $(KERNEL) M=`pwd` clean
	rm -rf my_led
#模块更新
install:
	make -C $(KERNEL) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS)
#编译上层app应用程序
my_led:
	arm-linux-gcc my_led.c -o my_led</span>

好了,所有的一切都编写完成,我们来看看接下来的操作:

1、先编译整个工程:执行make命令

2、编译app

3、启动minicom,打开开发板的电源,开发板bootload开始启动

4、开发板内核启动

5、进入文件系统,执行insmod插入模块和显示插入后的模块的操作

6、看看主设备号

从这里可以看到,我们的设备test-dev的主设备号是250

所以我们创建设备节点 : mknod /dev/test-dev c 250 0 ,创建完成后

7、执行app应用程序

在这里,我们可以看到程序开始跑起来了,我们来看看开发板上的led是怎么变化的:

好,写了好多,也整理了不少东西出来了,有点累了,以后还要慢慢写.....今天就到这里...

基于ARM_contexA9 led驱动编程的更多相关文章

  1. 全志A33 linux led驱动编程(附实测参考代码)

    开发平台 * 芯灵思SinlinxA33开发板 淘宝店铺: https://sinlinx.taobao.com/ 嵌入式linux 开发板交流 QQ:641395230 开发平台 * 芯灵思Sinl ...

  2. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  3. 驱动编程思想之初体验 --------------- 嵌入式linux驱动开发之点亮LED

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

  4. 基于sys文件系统的LED驱动的移植【原创】

    基于RK3188平台LED驱动程序的移植的移植.如有不正确之处,欢迎大家指点. 本文的LED驱动程序不是通过打开设备节点来访问和控制LED的,是通过sys文件系统来控制LED. 板子上有四盏灯以及对应 ...

  5. 基于S3C2440的linux-3.6.6移植——LED驱动【转】

    本文转载自:http://www.voidcn.com/blog/lqxandroid2012/article/p-625005.html 目前的linux版本的许多驱动都是基于设备模型,LED也不例 ...

  6. at91sam9x5 linux 4.1.0下dts驱动编程模型

    测试环境:  CPU: AT91SAM9X35      Linux: Atmel提供的linux-at91-linux4sam_5.3 (Linux-4.1.0) 转载请注明: 凌云物网智科嵌入式实 ...

  7. 简单的led驱动程序设计

    基于ok6410: led驱动程序: vim led.c #include<linux/kernel.h>#include<linux/module.h>#include< ...

  8. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  9. SPI_FLASH时序描述及驱动编程

    推荐 分享一个朋友的人工智能教程,零基础!通俗易懂!希望你也加入到人工智能的队伍中来! http://www.captainbed.net/strongerhuang Ⅰ.写在前面 前面文章讲述过关于 ...

随机推荐

  1. FLAnimatedImage -ios gif图片加载框架介绍

    简介 FLAnimatedImage 是 Flipboard 团队开发的在它们 App 中渲染 GIF 图片使用的库. 后来 Flipboard 将 FLAnimatedImage 开源出来供大家使用 ...

  2. 干货:MySQL 索引原理及慢查询优化

    MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓"好马配好鞍",如何能够更好的使用它,已经成为开发工程师的必修 ...

  3. 1.Cocos2dx 3.2中vector,ValueMap,Touch触摸时间的使用.iconv字符编解码

     Cocos2dx3.2以后使用Vector<T>代替了CCArray.案例如下: 头文件:T02Vector.h #ifndef __T02Vector_H__ #define __ ...

  4. Spark-1.6.0中的Sort Based Shuffle源码解读

    从Spark-1.2.0开始,Spark的Shuffle由Hash Based Shuffle升级成了Sort Based Shuffle.即Spark.shuffle.manager从Hash换成了 ...

  5. 指令汇B新闻客户端开发(五) ShareSdk的使用

    ShareSdk是一个分享按钮的开源框架,我们首先可以去mob的官网下载这个控件.mob官网,然后找到sdk下载那一栏, 下载下来之后点击这个.jar文件就会有一个弹窗,填写自己的应用包名和要哪些分享 ...

  6. linux C++多线程操作文件&输出加锁

    下述demo将指定目录下文件存入vector,然后一分为二交给两个线程(threadTask1,threadTask2)去分别处理,对输出函数printDirent加锁防止紊乱. #include & ...

  7. 算法之路(二)呈现O(logN)型的三个算法

    典型时间复杂度 我们知道算法的执行效率,可以从它的时间复杂度来推算出一二.而典型的时间复杂度有哪些类型呢? 由上图,可以看出,除了常数时间复杂度外,logN型的算法效率是最高的.今天就介绍三种非常ea ...

  8. Linux下which、whereis、locate、find 命令查找文件

     转自:http://blog.csdn.net/gh320/article/details/17411743 我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索 ...

  9. C++对象模型的那些事儿之四:拷贝构造函数

    前言 对于一个没有实例化的空类,编译器不会给它默认生成任何函数,当实例化一个空类后,编译器会根据需要生成相应的函数.这类函数包括一下几个: 构造函数 拷贝构造函数 析构函数 赋值运算符 在上一篇博文C ...

  10. little kernel中如何决定app目录下应该包含哪个app

    lk中是会为每个app建立一个thread,所以的app都是放在app这个路径下,那是在哪里决定的呢?一般是通过在project下面的MODULE决定的,例如下面这个例子就只用app下面的aboot这 ...