心得:
(1).如果只偏重于硬件和计算,那么安卓系统可以看做一个外壳,一个注重于展现的外壳;
(2).linux下面有很多好玩的东西,这里的世界只有C和makefile,更单纯;
(3).ubuntu作为一种流行的linux系统,可以做很多原来认为不能做或者做不好的事情,比如安卓app的开发,我现在已经开始用ubuntu写ppt和发送邮件了,如果开始做应用程序的开发将会对linux的认识更深入一层。
(4).所谓的jni,也是一种代码,可以理解为android和linux交流的窗口。
(5).linux下的ARM设备的IO配置是升级版的单片机IO配置,要先找到寄存器名称、offset、对应位,然后配置模式,与cc3200(arm cortex-m4内核)的IO配置类似。
(6).Linux系统把任何设备都抽象位文件,设备文件分为:字符设备,网络设备、块设备,这些设备都以文件的形式存在于/dev路径下,若dev路径下不存在一个设备对应的文件,则表示该设备没有加载成功。所以驱动程序或者jni层的代码可一看到大量的open、read、write、close等形式的操作。
第一步:确定IO参数
由于前面开发时使用了spi0的cs引脚,因此本实验依然采用spi0_cs0引脚,端口号为PI10。
寄存器:PI_CFG1 offset:0x124
对应位:10:8
配置模式:output:001
数据寄存器:PI_DAT offset:0x130




第二步:IO驱动模块
驱动代码(放在lichee/linux3.3/drivers路径下):
/*************************************************************
***********************ctl_io_driver*************************
**************************************************************/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/slab.h>
#include<linux/device.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <asm/uaccess.h>
/*configs for spi1_cs0*/
static volatile unsigned long *pccfg2 = NULL;
static volatile unsigned long *pcdat0 = NULL;
/*configs for 485_rse*/
static volatile unsigned long *picfg1 = NULL;//PI14
static volatile unsigned long *pidat0 = NULL;
static volatile unsigned long *picfg2 = NULL;//PI10
static volatile unsigned long *pidat1 = NULL;
static volatile unsigned long *pgcfg0 = NULL;//PG04
static volatile unsigned long *pgdat0 = NULL;
/*configs for spi1_cs0*/
/*0x01C208+addr*/
#define vPC_CFG2 0x01C20850
#define vPC_DAT0 0x01c20858
#define vPI_CFG1 0x01C20924 //0x01C208+0x124
#define vPI_DAT0 0x01c20930 //0x01C208+0x130
#define vPG_CFG0 0x01C208d8
#define vPG_DAT0 0x01c208e8
struct ctl_io_driver
{
unsigned int dev_major;
struct class *ctl_io_class;
struct device *ctl_io_device;
struct cdev *ctl_io_cdev;
dev_t devno;
unsigned int rval;
};
static struct ctl_io_driver *a20_driver;
static int ctl_io_open(struct inode *inode, struct file *file)
{
// pr_info(KERN_INFO "ctl_io open ok\n");
/*spi_cs:output*/
*pccfg2 &= ~(0xf<<12);
*pccfg2 |= (0x1<<12);
/*485_rse:output*/
*picfg1 &= ~(0xf<<24); //PI14
*picfg1 |= (0x1<<24);
*picfg1 &= ~(0xf<<8); //PI10 对应寄存器bit的较低位 (10:8)
*picfg1 |= (0x1<<8); //设置为输出模式
*pgcfg0 &= ~(0xf<<16);//PG04
*pgcfg0 |= (0x1<<16);
return 0;
}
static ssize_t ctl_io_read(struct file * file, const char __user * buf, size_t size, loff_t * ppos)
{
// printk(KERN_INFO "ctl_io read ok\n");
return size;
}
static ssize_t ctl_io_write(struct file * file, const char __user * buf, size_t size, loff_t * ppos)
{
if(copy_from_user(&a20_driver->rval, buf, size))
{
printk(KERN_ERR "failed to copy from user!\n");
return -EINVAL;
}
switch(a20_driver->rval)
{
/*config for spi_cs*/
/*down*/
case 1:
{
*pcdat0 &= ~((0x1<<19));
break;
}
/*up*/
case 2:
{
*pcdat0 |= ((0x1<<19));
break;
}
/*config for 485_rse*/
/*down*/
case 3:
{
*pidat0 &= ~((0x1<<14));
break;
}
/*up*/
case 4:
{
*pidat0 |= ((0x1<<14));
break;
}
/*down*/
case 5:
{
*pgdat0 &= ~((0x1<<4));
break;
}
/*up*/
case 6:
{
*pgdat0 |= ((0x1<<4));
break;
}
/*down*/
case 7:
{
*pidat1 &= ~((0x1<<10)); //置为低电平
break;
}
/*up*/
case 8:
{
*pidat1 |= ((0x1<<10)); //置为高电平
break;
}
}
return size;
}
//函数名称的映射
struct file_operations open_file = {
.owner = THIS_MODULE,
.open = ctl_io_open,
.read = ctl_io_read,
.write= ctl_io_write,
};
/*int cdev struct*/
static void setup_cdev_init(void)
{
a20_driver->ctl_io_cdev = cdev_alloc();
cdev_init(a20_driver->ctl_io_cdev, &open_file);
cdev_add(a20_driver->ctl_io_cdev, a20_driver->devno, 1);
}
static int __init ctl_io_test_init(void)
{
int ret;
// printk(KERN_INFO "ctl_io_init ok!\n");
a20_driver = kmalloc(sizeof(struct ctl_io_driver), GFP_KERNEL);
if(NULL == a20_driver)
{
printk(KERN_ERR "faild to malloc memory\n");
return -ENOMEM;
}
a20_driver->dev_major = 0;
if(a20_driver->dev_major)
{
a20_driver->devno = MKDEV(100,0);
ret = register_chrdev_region(a20_driver->devno, 1, "ctl_io_module");
if(ret < 0)
{
printk(KERN_INFO "failed to register chrdev region\n");
goto register_err;
}
}
else
{
a20_driver->devno = MKDEV(a20_driver->dev_major,0);
ret = alloc_chrdev_region(&a20_driver->devno, 0,1, "ctl_io_module");
if(ret < 0)
{
printk(KERN_INFO "failed to register chrdev region\n");
goto register_err;
}
}
setup_cdev_init();
a20_driver->ctl_io_class = class_create(THIS_MODULE, "ctl_io_class");
if (IS_ERR(a20_driver->ctl_io_class))
{
ret = PTR_ERR(a20_driver->ctl_io_class);
goto class_err;
}
a20_driver->ctl_io_device = device_create(a20_driver->ctl_io_class, NULL, a20_driver->devno, NULL, "ctl_io");
if (IS_ERR(a20_driver->ctl_io_device))
{
ret = PTR_ERR在lichee/linux3.3/drivers/ctl_io(a20_driver->ctl_io_device);
goto device_err;
}
/*map the addr*/
pccfg2 = ioremap(vPC_CFG2, 0x10);
pcdat0 = ioremap(vPC_DAT0, 0x10);
picfg1 = ioremap(vPI_CFG1, 0x10);
pidat0 = ioremap(vPI_DAT0, 0x10);
picfg2 = ioremap(vPI_CFG1, 0x10);
pidat1 = ioremap(vPI_DAT0, 0x10);
pgcfg0 = ioremap(vPG_CFG0, 0x10);
pgdat0 = ioremap(vPG_DAT0, 0x10);
return 0;
device_err:
class_destroy(a20_driver->ctl_io_class);
class_err:
unregister_chrdev_region(a20_driver->devno, 1);
cdev_del(a20_driver->ctl_io_cdev);
register_err:
kfree(a20_driver);
return ret;
}
static void __exit ctl_io_test_exit(void)
{
printk(KERN_INFO "ctl_io_exit ok!\n");
unregister_chrdev_region(a20_driver->devno, 1);
cdev_del(a20_driver->ctl_io_cdev);
device_destroy(a20_driver->ctl_io_class, a20_driver->devno);
class_destroy(a20_driver->ctl_io_class);
iounmap(pccfg2);
iounmap(pcdat0);
iounmap(picfg1);
iounmap(pidat0);
iounmap(picfg2);
iounmap(pidat1);
iounmap(pgcfg0);
iounmap(pgdat0);
kfree(a20_driver);
}
module_init(ctl_io_test_init);
module_exit(ctl_io_test_exit);
MODULE_LICENSE("GPL");
Makefile代码:
lichee/linux3.3/drivers下的Makefile,在最后添加:在lichee/linux3.3/drivers/ctl_io
obj-y +=ctl_io.o
借鉴下面的方法,编译生成新的固件,并升级开发板。
http://blog.youkuaiyun.com/penglijiang/article/details/45457627
再次开机后,会在/dev下生成名为ctl_io的文件节点。
第三步:JNI层开发
1.新建eclipse项目



2.在Led.java中添加 public static native String led(int ctl);
3.在workspace的工程目录下面新建jni文件夹

4.在jni下面新建led.c文件

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
//#include <linux/spi/spidev.h>
#include <android/log.h>
#define LOG_TAG "LED" //android logcat
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__ )
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS_ _)
int pullup = 8;
int pulldown = 7;
jstring JNICALL Java_com_example_led_Led_led(JNIEnv *env, jclass thiz,jint ctl)
{
int fd ;
fd = open("/dev/ctl_io",O_RDWR);
if(fd < 0)
return (*env)->NewStringUTF(env, "Can't open /dev/ctl_io!\n");
jstring xstring;
jint ret;
if(ctl == 1)
{
xstring = "onnnnnnnnn";
ret = write(fd, &pullup, 4);
if(ret < 0)
{
close(fd);
return (*env)->NewStringUTF(env, "write pullup failed\n");
}
else
{
close(fd);
return (*env)->NewStringUTF(env, "write pullup ok\n");
}
}
else
{
xstring = "offffffffff";
ret = write(fd, &pulldown, 4);
if(ret < 0)
{
close(fd);
return (*env)->NewStringUTF(env, "write pulldown failed\n");
}
else
{
close(fd);
return (*env)->NewStringUTF(env, "write pulldown ok\n");
}
}
//close(fd);
//return (*env)->NewStringUTF(env, xstring);
}
5.在jni下面新建Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LIB_PATH := $(LOCAL_PATH)/../../libs/armeabi
LOCAL_MODULE := led
LOCAL_SRC_FILES := \
led.c
LOCAL_C_INCLUDES := $(MY_ANDROID_SOURCE)/frameworks/base/core/jni/android/graphics \
LOCAL_ARM_MODE := arm
include $(BUILD_SHARED_LIBRARY)
6.新建Application.mk文件
APP_PLATFORM := android-20
APP_ABI := armeabi-v7a
7.在工程目录下执行ndk-build生成.so文件


生成相应头文件#javah -classpath bin/classes -d jni com.example.led.Led
第四步 安卓App开发
java代码:
package com.example.led;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.util.Log;
import android.view.Menu;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
public class Led extends Activity {
private Button button1;
private TextView tvvv;
private boolean index = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.led);
button1 = (Button)findViewById(R.id.btn1);
button1.setText("off");
//led(0,0);
tvvv = (TextView)findViewById(R.id.textView1);
button1.setOnClickListener(new Button.OnClickListener()
{
private String tt;
public void onClick(View v)
{
if(index)
{
tt = led(1);
tvvv.setText(tt); //显示驱动返回的字符,调试用
button1.setText("on");
index = false;
}
else
{
tt = led(0);
tvvv.setText(tt);
button1.setText("off");
index = true;
}
}
} );
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.led, menu);
return true;
}
public static native String led(int ctl);
static
{
System.loadLibrary("led");
}
}
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btn1"
android:layout_width="140dip"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
<TextView
android:id="@+id/textView1"
android:layout_width="252dp"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
如果提示打开文件失败,则可能是没有操作/dev下ctl_io的权限,可通过adb shell进入dev路径执行:chmod 777 ctl_io
将apk安装至开发板,执行效果如下:
本文介绍了Android平台驱动开发,从HAL层、JNI层到APP层的详细步骤。首先强调了Linux系统下硬件驱动的重要性,接着通过实例展示了Linux设备驱动的配置和编写,包括SPI接口的IO参数设置。然后,阐述了JNI作为Android与Linux交互的桥梁,以及在JNI层的开发过程。最后,讲解了如何进行Android App的开发,包括布局文件的创建和权限设置,以实现对硬件设备的控制。
482

被折叠的 条评论
为什么被折叠?



