s5p4418 android 驱动 hal 应用之led 串口 rs485 can总线应用(app 有源码)

本篇是继上篇的jni层后的app应用层。

由于app相对比较大,说起来也比较麻烦,这里就用一个串口来说明即可,实际上也就串口最具代表性,这个明白了,其他都不是问题。

串口涉及的java文件如下图红色圈所示:

技术分享

具体的代码下面一一贴出来,捡重要的说明:

MainActivity.java:

package com.example.realarmapp;

import realarm.can.RealarmCanActivity;
import realarm.serial.RealarmUartActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {
	private Button btnLED, btnUART, btnRS485, btnCAN;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btnLED = (Button) findViewById(R.id.btnLED);
        btnUART = (Button) findViewById(R.id.btnUART);
        btnRS485 = (Button) findViewById(R.id.btnRS485);
        btnCAN = (Button) findViewById(R.id.btnCAN);
        
        btnLED.setOnClickListener(this);
        btnUART.setOnClickListener(this);
        btnRS485.setOnClickListener(this);
        btnCAN.setOnClickListener(this);
    }
	@Override
	public void onClick(View v) {
		if(v.equals(btnLED)){
			Intent i = new Intent(MainActivity.this, RealarmLedActivity.class);
			startActivity(i);
		}
		else if(v.equals(btnUART)) {
			Intent i = new Intent(MainActivity.this, RealarmUartActivity.class);
			startActivity(i);
		}
		else if(v.equals(btnRS485)) {
			Intent i = new Intent(MainActivity.this, RealarmUartActivity.class);
			startActivity(i);
		}
		else if(v.equals(btnCAN)) {
			Intent i = new Intent(MainActivity.this, RealarmCanActivity.class);
			startActivity(i);
		}
	}
    
}
这个文件是主activity文件,没什么好说的,就是四个按钮执行不同的功能。

HardwareControl.java:

package realarm.hardware;

package realarm.hardware;

import java.io.FileDescriptor;

public class HardwareControl {
	
	public native int LedSetState(int ledNum,int ledState);
	public native static FileDescriptor OpenSerialPort(String path, int baudrate,
			int flags);
	public native static void CloseSerialPort();
	
	public native static  void InitCan(int baudrate);
	public native static  int OpenCan();
	public native static  int CanWrite(int canId,String data);
	public native static  CanFrame CanRead(CanFrame mcanFrame, int time);
	public native static   void CloseCan();
	static {
		System.loadLibrary("RealarmHardwareJni");
	}

}

该文件是声明native接口,并加载libRealarmHardwareJNI.so库。

MyApplication.java:

package realarm.serial;

import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;

import realarm.serial.utils.SerialPort;
import realarm.serial.utils.SerialPortFinder;

import android.app.Application;
import android.content.SharedPreferences;
import realarm.hardware.HardwareControl;
public class MyApplication extends Application {
	public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
    private SerialPort mSerialPort = null;

    public SerialPort getSerialPort() throws SecurityException, IOException, InvalidParameterException {
            if (mSerialPort == null) {
                    /* Read serial port parameters */
                    SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE);
                    String path = sp.getString("DEVICE", "/dev/ttyAMA4");
                    int baudrate = Integer.decode(sp.getString("BAUDRATE", "115200"));

                    /* Check parameters */
                    if ( (path.length() == 0) || (baudrate == -1)) {
                            throw new InvalidParameterException();
                    }

                    /* Open the serial port */
                    mSerialPort = new SerialPort(new File(path), baudrate, 0);
            }
            return mSerialPort;
    }
    public SerialPort getSerialPort(String path, String baudrate, int flag) throws NumberFormatException, SecurityException, IOException{
    	if (mSerialPort == null) {
    		/* Open the serial port */
            mSerialPort = new SerialPort(new File(path), Integer.decode(baudrate), flag);
    	}    	
		return mSerialPort;
    	
    }
    public void closeSerialPort() {
            if (mSerialPort != null) {
            		HardwareControl.CloseSerialPort();
                    mSerialPort = null;
            }
    }
}
该文件实现串口的创建和关闭方法。
SerialPort.java:

package realarm.serial.utils;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;
import realarm.hardware.HardwareControl;
public class SerialPort {
	private static final String TAG = "SerialPort";

	/*
	 * Do not remove or rename the field mFd: it is used by native method
	 * close();
	 */
	private FileDescriptor mFd;
	private FileInputStream mFileInputStream;
	private FileOutputStream mFileOutputStream;

	public SerialPort(File device, int baudrate, int flags)
			throws SecurityException, IOException {

		/* Check access permission */
		if (!device.canRead() || !device.canWrite()) {
			try {
				/* Missing read/write permission, trying to chmod the file */
				Process su;
				su = Runtime.getRuntime().exec("/system/xbin/su");
				String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
						+ "exit\n";
				su.getOutputStream().write(cmd.getBytes());
				if ((su.waitFor() != 0) || !device.canRead()
						|| !device.canWrite()) {
					throw new SecurityException();
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new SecurityException();
			}
		}

		mFd = HardwareControl.OpenSerialPort(device.getAbsolutePath(), baudrate, flags);
		if (mFd == null) {
			Log.e(TAG, "native open returns null");
			throw new IOException();
		}
		mFileInputStream = new FileInputStream(mFd);
		mFileOutputStream = new FileOutputStream(mFd);
	}

	// Getters and setters
	public InputStream getInputStream() {
		return mFileInputStream;
	}

	public OutputStream getOutputStream() {
		return mFileOutputStream;
	}
}
该文件实现串口的打开、数据输入、数据输出的方法。

SerialPortFinder.java:

package realarm.serial.utils;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;

import android.util.Log;

public class SerialPortFinder {

	private static final String TAG = "SerialPort";

	private Vector<Driver> mDrivers = null;

	public class Driver {
		public Driver(String name, String root) {
			mDriverName = name;
			mDeviceRoot = root;
		}

		private String mDriverName;
		private String mDeviceRoot;
		Vector<File> mDevices = null;

		public Vector<File> getDevices() {
			if (mDevices == null) {
				mDevices = new Vector<File>();
				File dev = new File("/dev");
				File[] files = dev.listFiles();
				int i;
				for (i = 0; i < files.length; i++) {
					if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
						Log.d(TAG, "Found new device: " + files[i]);
						mDevices.add(files[i]);
					}
				}
			}
			return mDevices;
		}

		public String getName() {
			return mDriverName;
		}
	}

	Vector<Driver> getDrivers() throws IOException {
		if (mDrivers == null) {
			mDrivers = new Vector<Driver>();
			LineNumberReader r = new LineNumberReader(new FileReader(
					"/proc/tty/drivers"));
			String l;
			while ((l = r.readLine()) != null) {
				// Issue 3:
				// Since driver name may contain spaces, we do not extract
				// driver name with split()
				String drivername = l.substring(0, 0x15).trim();
				String[] w = l.split(" +");
				if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
					Log.d(TAG, "Found new driver " + drivername + " on "
							+ w[w.length - 4]);
					mDrivers.add(new Driver(drivername, w[w.length - 4]));
				}
			}
			r.close();
		}
		return mDrivers;
	}

	public String[] getAllDevices() {
		Vector<String> devices = new Vector<String>();
		// Parse each driver
		Iterator<Driver> itdriv;
		try {
			itdriv = getDrivers().iterator();
			while (itdriv.hasNext()) {
				Driver driver = itdriv.next();
				Iterator<File> itdev = driver.getDevices().iterator();
				while (itdev.hasNext()) {
					String device = itdev.next().getName();
					String value = String.format("%s (%s)", device,
							driver.getName());
					devices.add(value);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return devices.toArray(new String[devices.size()]);
	}

	public String[] getAllDevicesPath() {
		Vector<String> devices = new Vector<String>();
		// Parse each driver
		Iterator<Driver> itdriv;
		try {
			itdriv = getDrivers().iterator();
			while (itdriv.hasNext()) {
				Driver driver = itdriv.next();
				Iterator<File> itdev = driver.getDevices().iterator();
				while (itdev.hasNext()) {
					String device = itdev.next().getAbsolutePath();
					devices.add(device);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return devices.toArray(new String[devices.size()]);
	}
}
该文件实现应用程序在/dev目录下检索tty*节点的方法。


RealarmUartActivity.java:

package realarm.serial;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import com.example.realarmapp.R;

import realarm.serial.utils.SerialPort;
import realarm.serial.utils.SerialPortFinder;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.ToggleButton;

public class RealarmUartActivity extends Activity {
	protected MyApplication mApplication;
	protected SerialPort mSerialPort;
	protected OutputStream mOutputStream;
	private InputStream mInputStream;
	private ReadThread mReadThread;

	EditText EditTextEmission, EditTextReception;
	SerialPortFinder mSerialPortFinder;// 串口设备搜索
	Spinner spinnerCOM;
	Spinner spinnerBaudRateCOM;
	CheckBox checkBoxAutoCOM;
	ToggleButton toggleButtonCOM;
	Button buttonSendCOM, buttonClean;

	private class ReadThread extends Thread {

		@Override
		public void run() {
			super.run();
			while (!isInterrupted()) {
				int size;
				try {
					byte[] buffer = new byte[64];
					if (mInputStream == null)
						return;

					/**
					 * 这里的read要尤其注意,它会一直等待数据,等到天荒地老,海枯石烂。如果要判断是否接受完成,只有设置结束标识,
					 * 或作其他特殊的处理。
					 */
					size = mInputStream.read(buffer);
					if (size > 0) {
						onDataReceived(buffer, size);
					}
				} catch (IOException e) {
					e.printStackTrace();
					return;
				}
			}
		}
	}
	protected void onDataReceived(final byte[] buffer, final int size) {
            runOnUiThread(new Runnable() {
                    public void run() {
                            if (EditTextReception != null) {
                            	EditTextReception.append(new String(buffer, 0, size));
                            }
                    }
            });
    }
	private void DisplayError(int resourceId) {
		AlertDialog.Builder b = new AlertDialog.Builder(this);
		b.setTitle("Error");
		b.setMessage(resourceId);
		b.setPositiveButton("OK", new OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				RealarmUartActivity.this.finish();
			}
		});
		b.show();
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.uart);

		<span style="color:#ff0000;">mApplication = (MyApplication) getApplication();</span>
		
		mSerialPortFinder = new SerialPortFinder();
		EditTextReception = (EditText) findViewById(R.id.EditTextReception);
		EditTextEmission = (EditText) findViewById(R.id.EditTextEmission);

		buttonClean = (Button) findViewById(R.id.buttonClean);
		buttonSendCOM = (Button) findViewById(R.id.buttonSendCOM);
		spinnerCOM = (Spinner) findViewById(R.id.spinnerCOM);
		spinnerBaudRateCOM = (Spinner) findViewById(R.id.spinnerBaudRateCOM);
		checkBoxAutoCOM = (CheckBox) findViewById(R.id.checkBoxAutoCOM);
		toggleButtonCOM = (ToggleButton) findViewById(R.id.toggleButtonCOM);

		ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
				this, R.array.baudrates_value,
				android.R.layout.simple_spinner_item);
		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		spinnerBaudRateCOM.setAdapter(adapter);
		spinnerBaudRateCOM.setSelection(16);

		String[] entryValues = mSerialPortFinder.getAllDevicesPath();
		List<String> allDevices = new ArrayList<String>();
		for (int i = 0; i < entryValues.length; i++) {
			allDevices.add(entryValues[i]);
		}
		ArrayAdapter<String> aspnDevices = new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item, allDevices);
		aspnDevices
				.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		spinnerCOM.setAdapter(aspnDevices);
		if (allDevices.size() > 0) {
			spinnerCOM.setSelection(4);
		}

		buttonClean.setOnClickListener(new ButtonClickEvent());
		buttonSendCOM.setOnClickListener(new ButtonClickEvent());
		toggleButtonCOM.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent());	
		spinnerCOM.setOnItemSelectedListener(new ItemSelectedEvent());
		spinnerBaudRateCOM.setOnItemSelectedListener(new ItemSelectedEvent());
		
		if (!toggleButtonCOM.isChecked()) {/*如果没有打开串口,那么发送按钮灰色不允许操作*/
			buttonSendCOM.setEnabled(false);
		}
//		try {
//			mSerialPort = mApplication.getSerialPort();
//			mOutputStream = mSerialPort.getOutputStream();
//			mInputStream = mSerialPort.getInputStream();
//
//			/* Create a receiving thread */
//			mReadThread = new ReadThread();
//			mReadThread.start();
//		} catch (SecurityException e) {
//			DisplayError(R.string.error_security);
//		} catch (IOException e) {
//			DisplayError(R.string.error_unknown);
//		} catch (InvalidParameterException e) {
//			DisplayError(R.string.error_configuration);
//		}
	}

//	protected abstract void onDataReceived(final byte[] buffer, final int size);

	@Override
	protected void onDestroy() {
		if (mReadThread != null)
			mReadThread.interrupt();
		mApplication.closeSerialPort();
		mSerialPort = null;
		super.onDestroy();
	}

	// ----------------------------------------------------串口号或波特率变化时,关闭打开的串口
	class ItemSelectedEvent implements Spinner.OnItemSelectedListener {
		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,
				long arg3) {
			if ((arg0 == spinnerCOM) || (arg0 == spinnerBaudRateCOM)) {
				mApplication.closeSerialPort();
				checkBoxAutoCOM.setChecked(false);
				toggleButtonCOM.setChecked(false);
			}
		}

		public void onNothingSelected(AdapterView<?> arg0) {
		}

	}

	// ----------------------------------------------------清除按钮、发送按钮
	class ButtonClickEvent implements View.OnClickListener {
		public void onClick(View v) {
			if (v == buttonClean) {
				EditTextReception.setText("");
			}else if (v == buttonSendCOM) {
				if (toggleButtonCOM.isChecked()) {
					try {
						mOutputStream.write(EditTextEmission.getText().toString().getBytes());
						mOutputStream.write('\n');
						System.out.println("串口数据发送成功");
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				else{
					System.out.println("串口未打开");
				}
			}
		}
	}
	//----------------------------------------------------打开关闭串口
    class ToggleButtonCheckedChangeEvent implements ToggleButton.OnCheckedChangeListener{
		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
		{
			if (buttonView == toggleButtonCOM){
				if (isChecked){
					
					try {
//						mSerialPort = new SerialPort(new File(spinnerCOM.getSelectedItem().toString()), 
//													Integer.decode(spinnerBaudRateCOM.getSelectedItem().toString()), 0);
						mSerialPort = mApplication.getSerialPort(spinnerCOM.getSelectedItem().toString(), 
																spinnerBaudRateCOM.getSelectedItem().toString(), 0);
						mOutputStream = mSerialPort.getOutputStream();
						mInputStream = mSerialPort.getInputStream();
						
						/* Create a receiving thread */
						mReadThread = new ReadThread();
						mReadThread.start();
						
						buttonSendCOM.setEnabled(true);/*使能发送按钮,这时可以发送数据了*/
						System.out.println("打开串口"+spinnerCOM.getSelectedItem().toString()+"成功!");

					} catch (NumberFormatException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (SecurityException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
				}else {
					mApplication.closeSerialPort();
					checkBoxAutoCOM.setChecked(false);
					buttonSendCOM.setEnabled(false);/*关闭发送按钮使能*/
					System.out.println("关闭串口"+spinnerCOM.getSelectedItem().toString()+"成功!");
				}
			}
		}
    }
}
该文件就是串口操作的主activity了,加载布局文件等。在该文件中,这里重要说明MyApplication这个类的调用。就是上面的红色代码。

也许有朋友在http://blog.csdn.net/tangcheng_ok/article/details/7021470这篇文章时,就遇到了这个问题,虽然编译通过,可是运行就会蹦掉。当然了,我也看了,而且遇到了。

那么这是怎么回事呢,通过报错提示“找不到资源”,可以知道当执行到红色部分时,应用程序无法找到资源。可是我们明明定义了MyApplication这个类,且继承与Application,为什么会找不到呢。代码定义当然没问题,实际上问题出在没有告诉app我定义了这个MyApplication。只需要在AndroidManifest.xml文件中添加下面红色的代码即可解决问题:

技术分享

这句代码就是告诉app要使用MyApplication这个继承与Application的类。

下面贴出运行的界面,O(∩_∩)O。

主界面:

技术分享

led操作界面:

技术分享技术分享

串口操作界面:

技术分享

技术分享

485与串口区别就是打开的串口号不同:

技术分享

can操作界面:

技术分享

再来个竖屏的,在布局时考虑到了适应问题,所以没有采用绝对布局,可以适应各分辨率大小的屏幕,只要分辨率不要太小,完全显示都是没问题的。

技术分享



源码下载地址:http://download.csdn.net/detail/u010406724/8539209


转载请注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44676937

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。