1.前言:
本文章写于 2024通院微控结束阶段。首先微控这个课吧,很耗费经历,对于很多组来说都是一道坎,尤其是头一次接触的上位机(安卓)部分。其次,这门课程的分工很重要,一定要找到适合自己的三个队友,你永远不知道会分到什么样的队友,希望你不要变成一个组里干所有活的苦力。
由于本人在微控期间涉及了除了MCU和L610的所有部分(因为这两个有大佬队友来),所以本人想以一个初学者的角度,写一篇速通微控指南,可能会涵盖很多方面(如果我不懒的话),接下来,我先写下上位机部分的指南。由于本篇只是提供一个学习路线与debug的经历,不足之处,往大家海涵。
对我而言,上位机部分更像是做裁缝,除了GUI别的部分真就东拼西凑,没整出来啥实际的东西,大多数时间都在写bug和debug(bushi,所以选择上位机分工,各位要有点心理准备,很多组最后都会因为上位机通宵,各位加油!
2.前期准备:
1.设备:
通院采用的设备为friendly arm公司Smart4418SDK,个人认为设备主要有以下几点重要须知:
- 注意启动方式:设备主要有两种启动方式,SD卡启动和内存启动,详情可以看老师在学在西电课程章节中上传的视频。很多时候不一定是设备坏了,可能是误触了相关开关。
- 注意关机方式:一定要先长按关机再拨开关,最后拔电源线。由于设备年久失修,这样是最好的保证设备不烂在手里的方式(bushi。具体演示也可以看老师在学在西电课程章节中上传的视频。
- 注意打开USB调试和检查数据线:首先是打开USB调试,这个闲言少叙,大家能上这个专业多少都了解,不会的也可以去看老师发的视频。最重要的是检查发的数据线是否可用,因为很多数据线都是学长自己配的,原装数据线十不存一(不要问我怎么知道的)。所以有时候无法连接并不是你的问题,或许换一根数据线就好了。
- 注意UART口的选择,这里建议选择UART3,最适合接线,其余的或多或少都有问题。
2.软件:
软件分以下几个部分来说:
- Java环境配置:这个在数电课程大作业里做运算单元时应该都配过了,没配过的可以参考这篇文章:
Java环境配置 - Android studio版本的选择:以下Android studio简称AS。这里我建议选择2019版本,因为更加稳定,而且本文章的所有代码也是在2019版本上编写的,且AS的工程移植性极差,故建议使用2019版本。
为了方便同学,我将如何下载和安装最新版AS的文章和2019版本的安装包都附录如下:
AS下载与安装
2019版AS下载
此外,AS的具体使用可以参考这篇文章:
AS基本使用 - 国内镜像源:在初次使用AS或者导入信你的工程文件时候,一定会面临gradle下载慢的问题,解决这个问题可以点击如下链接:
gradle镜像源替换 - 众所周知,AS是一款很难卸载的软件,每次需要更换版本都十分困难,这里我将引用一篇文章来为大家介绍AS的卸载:
彻底卸载AS
3.adb的安装与使用:
在初期GUI制作阶段,可能通过AS里自带的程序就可以将软件下载到上位机中。但是,我们在后期需要调用串口时,需要经过签名生成apk文件,然后将apk文件安装进上位机中,如此你所编写的软件才能顺利的打开串口进行与MCU的通信。
虽然可以通过VIVO手机助手进行链接和下载(蓝厂就是最diao的!!!),但我还是更推荐adb安装,更快,效率更高,同时还有一定的调试功能。
这里我也是引用一篇教程,通俗易懂。至于VIVO手机助手,大家可以查阅学长的视频教学。
3.GUI的界面制作:
1.GUI界面的设计:
在大约第五周的时候,老师会让你们交一份GUI的设计稿。这个以后是要跟着后续功能的,所以说建议慎重一点,跟组员讨论敲定后再设计。
在设计GUI时,有两种设计方式,一种类似于制作PPT,就用office和WPS就可以完成,因为这玩意本来也不高端,就是一层皮。还有一种方式就是使用Figma这款设计软件,他好就好在免费且有汉化社区,而且可以适配平板,还可以实现跳转,可以帮你理清前期逻辑,这玩意学习成本比较低,我就不在此赘述了,有兴趣的同学可以在B站上看up的课程,但是自学效率更快,具体链接如下:
2.GUI的制作
这个没什么可以说的,也是看b站的速成课,同时学在西电中老师也放了一些指南。纯前端边边的东西,学习成本不高,一周左右速通差不多。具体课程附录如下:
(ps:这个老师讲的挺不错的)
3.注意事项:
- 一定一定不要拖动组件!!! 这样很容易造成你的GUI下载下来一团乱麻。
- 一定要记住在Mainfest文件中定义页面优先级,不然很容易下载一堆进来。
4.通信部分:
最大的boss来啦!不过不怕,咱们庖丁解牛,懂得代码用处自然做起来得心应手。
1.硬件包的导入:
这部分时第一步的准备部分,需要引入对应的驱动,这里呢老师们在学在西电的文件里有详细的讲解,我就不过多赘述了,对应的文件也在学在西电的资料中,同学们可以自行查阅。
2.串口助手:
在第一次进行数据链联调时,我们往往都是先利用上位机的串口助手来进行的,这个串口助手的工程我会附录在文章末尾,具体使用不用我说大家应该都会,接下来我们将会对串口助手的java代码进行一个详解。
- 包和导入:这些导入语句引入了Android开发所需的各种类和接口,以及用于硬件控制的FriendlyARM库。
package com.example.sang.testserial; // 定义包名 import android.app.Activity; // 导入Activity类 import android.content.res.Configuration; // 导入Configuration类 import android.os.Bundle; // 导入Bundle类 import android.view.View; // 导入View类 import android.view.View.OnClickListener; // 导入OnClickListener接口 import android.widget.Button; // 导入Button类 import android.widget.EditText; // 导入EditText类 import android.widget.ScrollView; // 导入ScrollView类 import android.widget.TextView; // 导入TextView类 import android.util.Log; // 导入Log类 import android.text.Html; // 导入Html类 import android.widget.Toast; // 导入Toast类 import java.util.Timer; // 导入Timer类 import java.util.TimerTask; // 导入TimerTask类 import com.friendlyarm.FriendlyThings.HardwareControler; // 导入HardwareControler类 import com.friendlyarm.FriendlyThings.BoardType; // 导入BoardType类 import android.os.Handler; // 导入Handler类 import android.os.Message; // 导入Message类 import android.content.Context; // 导入Context类 import android.content.Intent; // 导入Intent类
- 主类定义与成员变量定义:这些变量用于存储UI组件、串口配置和数据缓冲区等信息。
public class MainActivity extends Activity implements OnClickListener { // 定义MainActivity类,继承Activity并实现OnClickListener接口 private static final String TAG = "SerialPort"; // 定义日志标签 private TextView fromTextView = null; // 定义TextView变量 private EditText toEditor = null; // 定义EditText变量 private final int MAXLINES = 200; // 定义最大行数 private StringBuilder remoteData = new StringBuilder(256 * MAXLINES); // 定义StringBuilder用于存储接收到的数据 // NanoPC-T4 UART4 private String devName = "/dev/ttyAMA3"; // 定义串口设备名 private int speed = 115200; // 定义串口波特率 private int dataBits = 8; // 定义数据位 private int stopBits = 1; // 定义停止位 private int devfd = -1; // 定义设备文件描述符
- onDestory:在活动销毁时,取消定时器并关闭串口。
@Override public void onDestroy() { // 重写onDestroy方法 timer.cancel(); // 取消定时器 if (devfd != -1) { // 如果设备文件描述符有效 HardwareControler.close(devfd); // 关闭串口 devfd = -1; // 重置设备文件描述符 } super.onDestroy(); // 调用父类的onDestroy方法 }
- onCreat:在活动创建时,根据设备的方向加载不同的布局文件,初始化UI组件,并尝试打开串口。如果串口打开成功,启动定时器定期检查数据。
@Override public void onCreate(Bundle savedInstanceState) { // 重写onCreate方法 super.onCreate(savedInstanceState); // 调用父类的onCreate方法 if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { // 如果设备处于横屏模式 setContentView(R.layout.serialport_dataprocessview_landscape); // 设置横屏布局 } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { // 如果设备处于竖屏模式 setContentView(R.layout.serialport_dataprocessview); // 设置竖屏布局 } String winTitle = devName + "," + speed + "," + dataBits + "," + stopBits; // 构建窗口标题 setTitle(winTitle); // 设置窗口标题 ((Button)findViewById(R.id.sendButton)).setOnClickListener(this); // 设置发送按钮的点击监听器 fromTextView = (TextView)findViewById(R.id.fromTextView); // 获取TextView组件 toEditor = (EditText)findViewById(R.id.toEditor); // 获取EditText组件 /* no focus when begin */ toEditor.clearFocus(); // 清除焦点 toEditor.setFocusable(false); // 设置不可获得焦点 toEditor.setFocusableInTouchMode(true); // 设置在触摸模式下可获得焦点 devfd = HardwareControler.openSerialPort(devName, speed, dataBits, stopBits); // 打开串口 if (devfd >= 0) { // 如果串口打开成功 timer.schedule(task, 0, 500); // 启动定时器,每500毫秒执行一次任务 } else { // 如果串口打开失败 devfd = -1; // 重置设备文件描述符 fromTextView.append("Fail to open " + devName + "!"); // 显示错误信息 } }
- 定时器和处理器(接收部分):定时器每500毫秒发送一次消息给处理器,处理器检查串口是否有数据可读,如果有则读取数据并更新UI。
private final int BUFSIZE = 512; // 定义缓冲区大小 private byte[] buf = new byte[BUFSIZE]; // 定义缓冲区 private Timer timer = new Timer(); // 定义定时器 private Handler handler = new Handler() { // 定义处理器 public void handleMessage(Message msg) { // 重写handleMessage方法 switch (msg.what) { // 根据消息类型进行处理 case 1: // 如果消息类型为1 if (HardwareControler.select(devfd, 0, 0) == 1) { // 检查串口是否有数据可读 int retSize = HardwareControler.read(devfd, buf, BUFSIZE); // 读取串口数据 if (retSize > 0) { // 如果读取到数据 String str = new String(buf, 0, retSize); // 将数据转换为字符串 remoteData.append(str); // 将数据追加到remoteData中 if (fromTextView.getLineCount() > MAXLINES) { // 如果TextView中的行数超过最大行数 int nLineCount = fromTextView.getLineCount(); // 获取当前行数 int i = 0; // 定义计数器 for (i = 0; i < remoteData.length(); i++) { // 遍历remoteData if (remoteData.charAt(i) == '\\n') { // 如果遇到换行符 nLineCount--; // 行数减一 if (nLineCount <= MAXLINES) { // 如果行数小于等于最大行数 break; // 退出循环 } } } remoteData.delete(0, i); // 删除多余的行 fromTextView.setText(remoteData.toString()); // 更新TextView内容 } else { // 如果行数未超过最大行数 fromTextView.append(str); // 追加数据到TextView } ((ScrollView)findViewById(R.id.scroolView)).fullScroll(View.FOCUS_DOWN); // 滚动到最底部 } } break; } super.handleMessage(msg); // 调用父类的handleMessage方法 } }; private TimerTask task = new TimerTask() { // 定义定时任务 public void run() { // 重写run方法 Message message = new Message(); // 创建消息对象 message.what = 1; // 设置消息类型 handler.sendMessage(message); // 发送消息 } };
- onClick:当发送按钮被点击时,获取
toEditor
中的文本并通过串口发送。如果发送成功,清空输入框并更新显示区域。public void onClick(View v) { // 重写onClick方法 switch (v.getId()) { // 根据点击的视图ID进行处理 case R.id.sendButton: // 如果点击的是发送按钮 String str = toEditor.getText().toString(); // 获取输入框中的文本 if (str.length() > 0) { // 如果文本不为空 if (str.charAt(str.length() - 1) != '\\n') { // 如果文本末尾不是换行符 str = str + "\\n"; // 添加换行符 } int ret = HardwareControler.write(devfd, str.getBytes()); // 发送数据到串口 if (ret > 0) { // 如果发送成功 toEditor.setText(""); // 清空输入框 str = ">>> " + str; // 添加前缀 if (remoteData.length() > 0) { // 如果remoteData不为空 if (remoteData.charAt(remoteData.length() - 1) != '\\n') { // 如果remoteData末尾不是换行符 remoteData.append('\\n'); // 添加换行符 fromTextView.append("\\n"); // 在TextView中添加换行符 } } remoteData.append(str); // 将数据追加到remoteData中 fromTextView.append(str); // 将数据追加到TextView中 ((ScrollView)findViewById(R.id.scroolView)).fullScroll(View.FOCUS_DOWN); // 滚动到最底部 } else { // 如果发送失败 Toast.makeText(this, "Fail to send!", Toast.LENGTH_SHORT).show(); // 显示错误信息 } } break; } } }
以上就是串口助手代码的所有详解。
3.代码的更改:
如果你需要发送固定字符,可以按照我下面的代码对于onClick的部分进行更改:
int ret;
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_login1_2:
String str4 = "*d0300";
String str5 = "*d3000";
if (str77 == 2) {
ret = HardwareControler.write(devfd, str4.getBytes());//这是发送数据的代码,ret是发送的字节数的返回值
}else{
ret = HardwareControler.write(devfd, str5.getBytes());
}
if (ret > 0) { //如果返回的ret大于0,说明发送成功了
Toast.makeText(this, "sucess!", Toast.LENGTH_SHORT).show();
Intent intent =null;
intent = new Intent(MainActivity2.this,MainActivity6.class);
startActivity(intent);
} else {
Toast.makeText(this, "Fail to send!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_login1_1:
Intent intent = null;
intent = new Intent(MainActivity2.this,MainActivity5.class);
startActivity(intent);
break;
//以上是加了按钮的代码,没有报错
}
}
}
接收部分可以添加如下代码对于数据帧进行替换:
if (str.contains("你的数据帧")) {
str77=2;
str = str.replaceAll("你的数据帧", "想要显示的字符");
str = str + "\\n";
final Toast toast = Toast.makeText(getApplicationContext(), "Green light ahead", 1000);
View toastView = toast.getView();
//toastView.setBackgroundResource(R.drawable.toast_bg);// 设置背景色为红色
TextView toastText = new TextView(getApplicationContext());
toastText.setText("想要显示的字符");
toastText.setTextColor(Color.GREEN); // 设置文本颜色为黑色
toastText.setTextSize(200); // 设置字体大小为100dp
toast.setGravity(Gravity.CENTER, 0, 0);
toast.setView(toastText); // 将TextView设置为Toast的视图
toast.show();
此外,对于只有按钮发送没有接受数据框的界面,可以查询代码详解删除对应的界面设置。
4.软件的签名与安装:
- cmd操作:我们对于软件的签名有两种方式,老师在学在西电的教程都有提及,这里我们主要将用命令行来签名:
1.打开你的工程中如下对应的路径:工程名称\\app\\build\\outputs\\apk\\debug
2.将Help1(文件打包与文末)中的 platform.pk8/platform.x509.pem/signapk.jar 粘贴进文件夹。
3.随后打开cmd窗口,首先输入X:(X为对应的盘)命令进行跳盘操作,随后使用 cd 文件路径 的指令转到对应的文件夹下。
4.最后输入如下命令即可完成签名。java -jar .\\signapk.jar .\\platform.x509.pem .\\platform.pk8 .\\app-debug.apk .\\Serial1-Signed.apk
- 签名后使用前面讲过的adb安装apk文件,应用就成功安装啦!
5.调试方法:
- 界面闪退问题与logcat调试:
如果碰到跳转后停止运行无法打开界面,多半时在组件定义中出现问题,比如button的定义,textview的定义等等,建议可以问问AI(喜)。这里我们介绍一种调试方法,是利用了AS中的logcat日志调试,可以比较清楚的解决组件定义问题。
具体教程如下链接,使用logcat在界面崩溃时可以定位到java文件的某一行,还是挺好用的。
logcat使用详解 - 按钮闪退:
如果成功进入通信页面但是点击按钮闪退,则可能是按钮对应onClick的问题,这里可以给每一步都加一个弹窗,看看到底是那一步出了问题无法发送数据,有点类似于C语言里的debug手段。
5.后记:
玛雅,没想到一时兴起真的能写完这篇文章,这篇文章也是我的第一篇博客,希望能为学弟学妹们提供力所能及的帮助。由于本文只是指南性质,多有不足请诸位包涵。有问题可以在评论区问我,看到就会回复!最后感谢你能够看到这里!
微控这门课说难不难,说简单不简单,一个组里最少要有三个能人,能覆盖所有技术面,小组才能顺利进行。希望大家都能和谐相处,顺利完成微控!
附录:
- 所有提到的文件:串口助手工程与Help1
- 调试好的一个java文件:调试(由于组员意见,设置了密码,需要用可以评论说明,讨论后会决定是否公开)
平台声明:以上文章转载于《CSDN》,文章全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,仅作参考。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/hooxi_shy/article/details/140238415