安卓手机和单片机音频通信
2019-04-13 10:58发布
生成海报
这里是这几天做的实验,在安卓上的软件程序主要参考《疯狂的安卓讲义》,电路中的一些东西参考的是HiJack项目中的电路。在这里,我真的突然感慨学好模电真的很重要。在没有参考HIJACK之前,我对这些音频信号手足无措。在这个小项目学习完后,我决定再重新学习模电,感觉这一次重温应该能让我这模电知识再上一个台阶。
下面我从几个方面记录一下自己的学习过程,给自己留个记录以后翻阅,也可以给大家一个参考;
1).耳机线的接口;
2).话筒和耳机的声音原理;
3).安卓中的程序编写
1.耳机接口问题
一般MP3中的耳机就真的是耳机,其中没有话筒,分为三段左声道、右声道、地。而我们的手机中原装的耳机是带有话筒的,这种耳机分为四段分别为左声道、右声道、地、MIC。就是这手机带的四段带话筒的耳机还不同,像国内的一些手机厂商和国外的厂商,这种耳机的地和MIC可能是对换了的。有条件的可以折几个测测就知道。
2.其实话筒接收的是振动信号,以些振动信号产生变化的电流,通过耳机接口传到手机里面的处理电路,最后被采样。所以,这里用以下电路,参考HIJACK中的,单片机用PWM来输出。
用的是1KHZ占空比为50%的PWM波,最后在安卓中保存话筒采集到的数据,用的是16bit单通道格式保存为PCM,显示的波形图如下:振动幅值都是最大的-32768,32767.
在手机播放500HZ的音频时,我在耳机中测到的输入波形如下图:C1即为在左声道测得的波形,Z1、Z2为放大后的。
从手机通过耳机到单片机的电路如下:
3.安卓中的程序主要是涉及两个模块,一个是音频的录放,另一个就是文件的存储;
程序如下:APK编写的时候是支持android2.3.1以上的,大家可以在资源里下载http://download.csdn.net/detail/raoqin/5884203
界面文件main.xml
主程序如下:
package packname.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Toast;
public class acti extends Activity
{
/** Called when the activity is first created. */
//----
final String FILE_NAME = "/myraoqin.bin";
File sdCardDir = null;
File targetFile = null;
RandomAccessFile raf = null;
//----
Button btnRecord, btnStop, btnExit;
SeekBar skbVolume;//调节音量
boolean isRecording = false;//是否录放的标记
static final int frequency = 44100;
static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;//AudioFormat.ENCODING_PCM_16BIT;
int recBufSize,playBufSize;
AudioRecord audioRecord;
AudioTrack audioTrack;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setTitle("Lhyraoqin");
//----------------------------SD卡文件
//获取SD卡的目录
try
{
sdCardDir = Environment.getExternalStorageDirectory();
targetFile = new File(sdCardDir.getCanonicalPath() + FILE_NAME );
//以指定文件创建 randomaccessfile对象
raf = new RandomAccessFile( targetFile, "rw" );
}
catch(Exception e)
{
e.printStackTrace();
}
//获取两个按钮
Button read = (Button) findViewById(R.id.read);
Button write = (Button) findViewById(R.id.write);
//获取两个文本框
final EditText edit1 = (EditText) findViewById(R.id.edit1);
final EditText edit2 = (EditText) findViewById(R.id.edit2);
//为write按钮绑定事件监听器
write.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View source)
{
//将edit1中的内容写入文件中
Mywrite(edit1.getText().toString());
edit1.setText("");
}
});
read.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//读取指定文件中的内容并显示出来
edit2.setText(Myread());
}
});
//-------------------------SD文件
//-----
recBufSize = AudioRecord.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
playBufSize=AudioTrack.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
// -----------------------------------------
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency,
channelConfiguration, audioEncoding, recBufSize);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency,
channelConfiguration, audioEncoding,
playBufSize, AudioTrack.MODE_STREAM);
//------------------------------------------
btnRecord = (Button) this.findViewById(R.id.btnRecord);
btnRecord.setOnClickListener(new ClickEvent());
btnStop = (Button) this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(new ClickEvent());
btnExit = (Button) this.findViewById(R.id.btnExit);
btnExit.setOnClickListener(new ClickEvent());
skbVolume=(SeekBar)this.findViewById(R.id.skbVolume);
skbVolume.setMax(100);//音量调节的极限
skbVolume.setProgress(70);//设置seekbar的位置值
audioTrack.setStereoVolume(0.7f, 0.7f);//设置当前音量大小
skbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
float vol=(float)(seekBar.getProgress())/(float)(seekBar.getMax());
audioTrack.setStereoVolume(vol, vol);//设置音量
}
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
// TODO Auto-generated method stub
}
});
}
//------------
//读写文件用的
//
//
//----------------
private String Myread()
{
try
{
//如果手机插入了SD卡,而且应用程序具有访问SD的权限
if(Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED) )
{
//获取SD卡的目录
File sdCardDir = Environment.getExternalStorageDirectory();
//获取指定文件对应的输入流
FileInputStream fis = new FileInputStream(sdCardDir.getCanonicalPath() + FILE_NAME);
//将指定输入流包装成Bufferreader
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
StringBuilder sb = new StringBuilder("");
String line = null;
while((line = br.readLine()) != null)
{
sb.append(line);
}
return sb.toString();
}
/*
//打开文件输入流
FileInputStream fis = openFileInput("myFILE_NAME");
byte[] buff = new byte[1024];
int hasRead = 0;
StringBuilder sb = new StringBuilder("");
while( (hasRead = fis.read(buff)) > 0 )
{
sb.append(new String(buff, 0, hasRead));
}
return sb.toString();
*/
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
//-------------------------
//读写文件用的
//
//
//---------------------------------
private void Mywrite(String content)
{
try
{
//如果手机插入了SD卡,而且应用程序具有访问SD的权限
if(Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED) )
{
/*
//获取SD卡的目录
File sdCardDir = Environment.getExternalStorageDirectory();
File targetFile = new File(sdCardDir.getCanonicalPath() + FILE_NAME );
//以指定文件创建 randomaccessfile对象
RandomAccessFile raf = new RandomAccessFile( targetFile, "rw" );
*/
//输出文件内容
raf.write(content.getBytes());
byte[] mybytes = new byte[3];
//raf.close();
}
/*
//以追加模式打开文件输出流
FileOutputStream fos = openFileOutput("myFILE_NAME", MODE_APPEND);
//将fileoutputstream包装成printstream
PrintStream ps = new PrintStream(fos);
//输出文件内容
ps.println(content);
ps.close();
*/
}
catch(Exception e)
{
e.printStackTrace();
}
}
//-------------------------
//读写文件用的
//
//
//---------------------------------
private void Mywrite(byte[] mybytes)
{
try
{
//如果手机插入了SD卡,而且应用程序具有访问SD的权限
if(Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED) )
{
//输出文件内容
raf.write(mybytes);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
//-------------------------
//
//
//
//---------------------------------
@Override
protected void onDestroy()
{
try
{
raf.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
class ClickEvent implements View.OnClickListener
{
@Override
public void onClick(View v)
{
if (v == btnRecord)
{
isRecording = true;
new RecordPlayThread().start();// 开一条线程边录边放
}
else if (v == btnStop)
{
isRecording = false;
}
else if (v == btnExit)
{
isRecording = false;
acti.this.finish();
}
}
}
class RecordPlayThread extends Thread
{
public void run()
{
try
{
byte[] buffer = new byte[recBufSize];
audioRecord.startRecording();//开始录制
audioTrack.play();//开始播放
while (isRecording)
{
//从MIC保存数据到缓冲区
int bufferReadResult = audioRecord.read(buffer, 0,
recBufSize);
byte[] tmpBuf = new byte[bufferReadResult];
System.arraycopy(buffer, 0, tmpBuf, 0, bufferReadResult);
//for(int i=0; i
最后记得在AndroidManifest.xml文件中加入权限
程序将把录到的PCM文件保存到SD上的myraoqin.bin文件中,这个文件就是16bit单通道文件,可以用GoldWave软件打开进行播放测试。
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮