CSK.Blog--个人原创Weblog

« 基于MK802的应用开发和相关的工具在MK802上结合OpenCV库进行计算机视觉处理 »

MK802与外部硬件设备的通讯

这是一个文章系列的一部分,介绍基于MK802这类MiniPC的扩展开发,并展示他在计算机视觉、机器人控制方面的潜能

欢迎转载,但请保留原始作者信息(Shikai Chen, http://www.csksoft.net),以及指向本文原始出处的链接!

访问目录:基于MK802 MiniPC的扩展开发应用-简介篇(http://www.csksoft.net/blog/post/mk802_dev_intro.html)

 

revision: 2

通过了前几篇文章[1][2][3]的介绍,相信大家已经为MK802准备好了适合的自制系统也了解了应用开发的基本过程。从这篇文章开始我们将正式介绍本系列文章的最终目的:使用MK802与Arduino等外部硬件通讯,制作各类应用。
这篇文章将介绍其中的一个重要步骤:如何实现MK802与外部硬件设备的通讯。这是后续进一步开发的必要环节。

1. 几种可行的通讯方式

从MK802的硬件配置来看,我们可以使用如下的通讯接口与外部的硬件设备通讯

 

  1. USB
  2. 内置的调试串口
  3. WIFI

其中内置的调试串口可以通过前文[2]的介绍引出来。但是正如前文所说,这个串口信号主要用途是查看uboot和内核系统的log信息,以及作为控制终端使用。如果同时用作与外部设备通讯的驱动,势必会产生数据之间的干扰。因此我不推荐利用它与外部设备通讯。如果坚持使用,需要重新配置编译linux kernel以及uboot,将该串口的日志输出和终端关闭。

对于MK802来说使用USB总线与外设通讯是最理想的方式。MK802本身有2个usb口可以使用,其中一个mini usb otg口可以用买来附送的转换接口连接外设,如果需要连接更多外部设备,则可以外接一个usb hub。下图就展示了这样的情况。

图:利用MK802的2个usb口连接usb外设

 

不过直接基于USB总线进行通讯对于大部分爱好者具有一定难度,一方面需要使用支持USB总线的单片机(如STM32),同时还要在MK802上开发usb驱动程序。而另一方面,像Arduino这类开发版,虽然使用usb与外部连接,但他本质上使用了usb转串口芯片,将串口信号通过usb总线传输。而在单片机芯片(arduino上是AVR)和PC/MK802的系统看来,他们之间是直接通过串口相连的。
因此这里我们也列出另一种MK802与外设的通讯方式:

通过串口

串口通讯是Arduino上相对容易的方式,在MK802这类arm-linux系统的设备上编程也相对usb容易,并且对于大部分的应用他的速度也是足够的。因此下文我将重点围绕串口通信方式介绍。同时我也给出了我编写的可以运行在Linux设备(PC以及MK802这类arm设备)的串口库,方便大家直接使用。

对于WIFI方式的通讯,其本质是基于TCP/IP的网络通讯。如果与外部设备通讯,在MK802上只需要基于标准的socket接口开发网络通讯程序即可。在这个系列中,我们将跳过这个方式,对其感兴趣的朋友可以自行尝试。

2. Linux下串口通讯程序的开发

这里我们假设大家清楚如何在外部设备那端使用串口通讯,比如Arduino就有方便的Serial库可以使用。这里介绍对于爱好者来说相对困难的在MK802或者PC这一段的串口开发问题。
这里我们将主要关注MK802使用的linux系统的串口编程。由于linux的广泛使用,这里的代码可以完全不做修改,在如下设备上直接编译使用:
 其他使用arm-linux的设备,如:树梅派
 其他使用linux的嵌入式设备,如采用openwrt的路由器
 使用linux的PC
 MacOS

另外这里也给出我编写的串口库代码,可以在如下地址获取:


在后续介绍中将会看到如何使用这个串口库。

2.1. 串口在Linux下的设备名(路径):


与Windows上不同,Linux不使用COM1、COM2这类名称对串口设备命名。而是采用如下方式对绝大多数串口设备的设备路径命名:

/dev/tty*

比如MK802上的内置串口,他的设备路径是:

/dev/ttyS0

如果将一个usb转串口设备接入了MK802,或者PC上的linux系统,他的设备路径名就是如下格式:

/dev/ttyUSB0

如果有2个usb转串口同时接入了MK802,那么他们的路径名可以分别是:

/dev/ttyUSB0
/dev/ttyUSB1

其实这套命名规则是所有类UNIX系统通用的,比如MacOS也使用了类似的命名规则。

2.2. 如何确定串口路径名

那么如何确定需要通讯的串口设备的路径名呢?
对于硬件自身集成的设备,一般的命名是/dev/ttyS0, /dev/ttyS1这样的形式,像前文提到MK802的调试串口,就是/dev/ttyS0。如果系统自身自带了多个串口,比如MK802的Allwinner A10  CPU其实自带有8个串口(只有一组信号可以引出),在/dev目录下就能找到如下的8个设备文件:


图:查看MK802内置的串口设备


对于内置的串口名称确定,一般没有特别好的办法,最直接的方式是通过dmesg命令阅读kernel启动过程的日志甚至阅读kernel的驱动代码来了解。不过对于我们来说更加关注的是如何确定USB转串口设备的串口路径。
前面提到这类串口设备路径名一般格式是/dev/ttyUSBn的形式,这里可以简单的用命令

dmegs | tail –f


在usb串口插入到系统不久,执行上述命令。如果该串口设备的驱动程序存在,则可以看到类似的文字输出:


图:通过dmesg查看刚接入系统的usb转串口设备路径

上图展示了将Arduino通过usb线缆与MK802相连后,dmesg的输出。最后一行就可以看到当前接入的设备是ttyUSB0。他的路径名就是/dev/ttyUSB0。

2.3. 如何操作串口

在Linux下,所有的设备都映射成了文件对象,可以使用传统的open()、write()、read()这类系统调用来像普通文件那样操作(与传统文件存在一些区别,这里不做深入)
下面展示了一个打开MK802上的usb串口,并且发送字符串“hello”的代码最简单例子:

int serialport_handle = open(“/dev/ttyUSB0”, O_RDWR | O_NOCTTY | O_NDELAY);
if (!serialport_handle) {
    printf(“Cannot open the serial port.\n”);
    return -1;
}
const char * msg = “hello”;
write(serialport_handle, msg, strlen(msg));
close(serialport_handle);

上述代码将通过在设备名为/dev/ttyUSB0的usb串口上发送“hello”这个字符串数据。不过在实际使用中还需要加入额外的配置:

设置串口设备波特率和传输格式


上述代码并没有设置串口设备的波特率以及传输格式,这在实际使用中是不允许的。
Linux系统为此提供了如下的函数和接口用于串口设备的相关配置:

struct termios;
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
int cfsetspeed(struct termios *termios_p, speed_t speed);

这些函数和结构体定义在头文件termios.h以及unistd.h当中。这里我就略过他们的具体使用了,大家可以参考上文给出的我写的串口操作库查看详情。

如果想系统的了解Linux这类POSIX系统下的串口编程,这里有篇不错的参考文献[4]。

2.4. 使用我编写的串口库

上文给出的我的串口库已经上上述的串口操作包装成了一个C++类对象:

class raw_serial

其中可以使用如下接口完成串口操作:
以特定的波特率打开串口:

bool bind(const char * portname, uint32_t baudrate, uint32_t flags = NULL);
bool open();

发送数据

senddata(const unsigned char * data, _word_size_t size);


读取数据:

recvdata(unsigned char * data, _word_size_t size);

等待数据到来

waitfordata(_word_size_t data_count,_u32 timeout = -1, _word_size_t * returned_size = NULL)

在后续的例子中将使用这个库作为基础为大家介绍。

2.5. 使用Minicom工具进行串口调试

对于基于串口通讯程序开发过程中可能会需要对通讯过程的数据进行分析,这时候就需要串口调试器来帮忙。这里推荐一个linux下广泛使用的命令行串口调试工具:minicom。
该工具可以在MK802上使用apt-get命令安装:

sudo apt-get install minicom

minicom本身非常强大,可以作为串口终端通过串口信号登陆一台主机的shell,也可以登陆老式的串口调制解调器(modem)发送AT指令。这里我们仅介绍他的最简单使用:以特定波特率打开一个串口设备,并进行纯文本级别的数据查看和发送。

如果有按照前文[2]的介绍引出了MK802的调试串口,这里不妨将他使用USB转串口设备连接后,重新接在MK802的USB口。这样将实现一个有趣的功能:可以在MK802自身观查MK802的串口日志数据并且登陆其中的shell。连接如下图所示:


图:将MK802的自身调试串口信号通过usb转串口重新接回MK802

 

一般使用usb串口连接MK802后,其串口路径就是/dev/ttyUSB0,我们使用如下命令格式,打开MK802自身的调试串口终端:

sudo minicom -b 115200 -o -D /dev/ttyUSB0


执行上述命令后,将可以看到打开的MK802调试串口的输出内容了:


图:通过minicom查看MK802自身调试串口的日志

在基于串口的开发中,不妨使用minicom查看开发过程中设备通过串口输出的各种信息。

3. MK802通过串口与Arduino的通讯

3.1. 设备相连

Arduino开发版自身集成了USB转串口芯片,可以直接将他与MK802的USB口相连。如果使用我提供的系统镜像,则linux kernel中已经包含了对Arduino自带的usb转串口芯片的驱动了。


图:直接将MK802和Arduino相连

 

不过需要注意的是MK802的USB口的供电输出很小,虽然可以给Arduino开发版自身提供足够的电流,但如果需要Arduino外接其他设备,则需要单独给Arduino或者其他外设供电。不然会影响MK802自身的工作稳定,甚至破坏MK802的电路。

3.2. 简单的数据传递例子

这里我们将实现一个很简单的数据交互的例子:
 在MK802上由用户输入一段文字,在按下回车后,由我们接下来编写的程序通过串口发送给Arduino
 Arduino在收到一行文字后,将它重新发送回MK802
 MK802收到来自Arduino的消息后,重新显示在命令行上

Arduino的固件代码:

这里我假设大家已经熟悉Arduino的串口程序开发了,直接给出Arduino上的代码,供大家直接测试(在Arduino IDE 022版本测试通过):

char recvbuffer[100];
unsigned char pos = 0;

void setup() {
  Serial.begin(115200);
 
}

void loop() {
  int currentchar = Serial.read();
  if (currentchar == -1) return;
 
  if (currentchar == '\n' && pos) {
     Serial.write((uint8_t *)recvbuffer, pos);
     pos = 0;
     return;
  }
 
  recvbuffer[pos++] = currentchar;
  if (pos == sizeof(recvbuffer)) pos= sizeof(recvbuffer)-1;
}

上述代码将以115200bps开启Arduino串口。并不断地接受收到的串口数据并加以缓存。一旦接受到一个换行字符(“\n”)后,就把之前保存的所有内容一次性发送。

在MK802上运行的程序代码:

这里将结合之前提到的我编写的串口库,编写运行在MK802上的程序,其核心逻辑代码如下:
 

    char msg[1024];
    char recvBuf[1024];
    while(1) {
        printf("Message to Arduino:\n");
        fgets(msg, sizeof(msg), stdin);
        Serial.senddata((unsigned char *)msg,strlen(msg));
      
        // wait for data
        while (Serial.rxqueue_count()<strlen(msg)-1);

        Serial.recvdata((unsigned char *)recvBuf,strlen(msg)-1);
        recvBuf[strlen(msg)-1] = 0;


        printf("Got Message from Arduino:\n");
        printf("%s\n\n", recvBuf);
    }

该代码将从命令行接受使用者输入的一段数据,随后将它通过串口发送,并且等待串口另一头的Arduino发送回同样长度的数据。随后将接受到的数据加以显示。

完整的代码可以使用如下链接访问:

下文将介绍如何使用这段代码并编译。

编译并执行MK802上的代码
本例子的所有代码位于本系列文章所专门开设的github项目中,可以用如下链接访问:

需要使用git 客户端将代码下载到本地(MK802或者PC上),使用如下命令:

该代码树中还包括了本系列文章将会介绍的其他应用的代码,这里我们暂时略过他们。在下载完代码后,使用configure命令首先配置编译设置。这里我们分别介绍交叉编译和本机编译两种方式。这里我假设读者已经具有相关知识或者阅读过我之前的文章[3]。

交叉编译

在PC上,进入上述代码目录:

cd mk802_demo

使用如下的配置命令:

./configure --host=arm-linux-gnueabihf

如果这个过程没有出现错误,则之后该项目将按照交叉编译模式编译。

 

图:将实例程序代码配置成交叉编译模式

 

随后分别输入如下命令进行编译
(注:可以直接输入make命令对所有项目进行编译,但目前尚未介绍OpenCV的交叉编译问题,因此会出现错误)

编译我编写的串口库和其他依赖项目:

(cd vfd_comm/ && make)
(cd serialdrv && make)

编译这里介绍的与Arduino通讯的程序:

cd arduino_test
make
cd ..

如果上述过程没有遇到错误,则可以在.output目录中找到编译结果:

cd .output/armv7l
ls -l


此时可以看到我们编译得到的程序:


图:位于.output目录下的编译而得的程序

 

接下来将这些程序通过WIFI传输到MK802,在代码根目录下使用scp命令:


上述命令将所有.output/armv7l文件传输到了MK802的目录/home/linaro/下边(假设MK802已经通过WIFI联网,且ip地址是192.168.1.103)
随后我们通过ssh登陆MK802,按照前文描述连接Arduino。进入/home/linaro目录,就可以运行本实例的程序了:

cd /home/linaro
sudo LD_LIBRARY_PATH=`pwd`   ./arduino_test /dev/ttyUSB0

其执行效果如下图所示:


图:与Arduino交互的例子程序执行效果

 

在MK802上编译

编译和执行的过程其实与交叉编译很接近,首先需要下载代码:

sudo apt-get install git   #(如果没有git命令,请用这句安装)
git clone
https://github.com/cheaven/mk802_demo.git

随后开始配置编译模式为本机编译:

cd mk802_demo
./configure

完成后,可以直接按照交叉编译部分使用的命令编译出我们的程序:

(cd vfd_comm/ && make)
(cd serialdrv && make)
cd arduino_test
make
cd ..

此时,编译结果将位于.output目录下,可以直接进入执行:

cd .output/armv7l
sudo LD_LIBRARY_PATH=`pwd`  ./arduino_test /dev/ttyUSB0

程序执行结果与交叉编译的一样,这就不再重复了。


4. 例子:MK802通过串口控制由Arduino驱动的舵机云台

这里介绍一个在后面将会一直使用到的应用:让MK802通过串口控制由Arduino驱动的舵机云台。该舵机云台上可以安装摄像头,用于后续例子中摄像头视野的控制。

4.1. 硬件设计

这里我搭建了一个如下图所示的舵机云台,云台和舵机零件都可以在taobao上购买得到:

图:自制的舵机云台

图:在自制舵机云台上安装的USB摄像头(将在后续文章介绍)

该舵机云台使用了2个9g舵机分别控制X轴与Y轴的旋转,因此我们可以另上面的摄像头对准前方任意一点。
将这些舵机直接与Arduino的Digital IO口相连,并使用外部5V电源供电。硬件部分就搭建完毕了。
在Arduino中,可以使用IDE自带的Servo库实现对舵机的驱动。

4.2. 串口通讯协议

这里介绍MK802程序与Arduino通过串口交互的通讯协议。目前的设计中,MK802将发送如下格式的数据包到Arduino:

数据包内字节偏移

内容

描述

0

0xA5 0xA5

数据包起始标志

2

X轴偏移量(-100-+100)

控制X轴舵机相对于当前位置的旋转量。正值为顺时针

3

Y轴偏移量(-100-+100)

控制Y轴舵机相对于当前位置的旋转量。正值为向下

 从上面协议可以看出,MK802程序将永远只能控制舵机的相对上一次的旋转量,而不能控制舵机定位到一个特定角度。这样做的目的是为了为后续做视觉定位跟踪提供便利。

这里解释下为什么要有开头2个字节的“数据包起始标识”。由于串口通讯本身并没有机制能够表示何时是当前传输数据的开头。比如假设程序要通过串口发送100个字节的数据。但是Arduino在程序发送到第30个字节时,才启动完毕并开始接收。由于串口本身并不会告诉Arduino当前接受的是第30个字节这个信息,Arduino将认为它接受到的第一个字节就是MK802程序要发送给它的第一个字节…如此一来,后续的所有的通讯就会错位。因此一般串口通讯都会引入一个起始标志的数据段,用于实现通讯的同步。

4.3. 例子程序

这里仅给出MK802用于发送串口数据包的例子代码。具体代码大家可以参考如下链接:

为了实现MK802对Arduino发送数据包并控制舵机云台,这里同样可以使用我编写的串口库,下面的代码片段展示了如何构造一个控制包并从串口发送出去:

//发送包起始标志
static const _u8 magicleading[] = {0xa5, 0xa5};
ptz_serialchn.senddata(magicleading, sizeof(magicleading));
ptz_serialchn.waitforsent();

//发送X-Y轴的偏移控制信号
_s8 ctrlSignal[2];
ctrlSignal[0] = <X轴偏移量>
ctrlSignal[1] = <Y轴偏移量>
ptz_serialchn.senddata((_u8 *)ctrlSignal, sizeof(ctrlSignal));

这里就不专门给出舵机控制的效果描述了,大家可以通过下面的视频了解:

 

 

5. MK802使用USB通讯的思路

这里简单介绍下如何直接通过USB总线传输数据的方案。虽然相比串口繁琐,但是USB通讯的速度快很多,比如使用STM32单片机可以与MK802进行高达100kb/s的数据传输。

5.1. 如何让Arduino直接通过usb总线通讯

Arduino上面使用的是USB转串口芯片与外部通讯。实际上Arduino使用的AVR芯片也可以直接不需要其他芯片在USB上通讯。这主要归功于社区提供的v-usb库[5]。

关于他的具体使用情况,可以参考我们RoboPeak团队做的一个开源项目:

RoboPeak USB Connector – 免驱动USB的AVR/51 ISP编程器
http://www.robopeak.net/blog/?p=133

使用v-usb可以实现最快1.5Mbps的通讯速度,虽然属于Low speed usb范围,但是相比串口来说也快了不少。

5.2. 使用libusb库

在MK802或者pc上对USB进行开发,除了传统的编写usb内核驱动外,目前可以使用一个用户态的开源库libusb[6]。

这个库有诸多好处,首先它不需要开发人员编写内核驱动,也不用担心代码bug将系统破坏。同时提供的API接口非常友好。更重要的是这个库的跨平台做的很好,可以在linux、Windows、macOS下工作,也支持ARM、MIPS的硬件平台。

6. 参考资料

[1] MK802的系统自制
http://www.csksoft.net/blog/post/mk802_dev_sysbuild.html

 

[2] MK802的软/硬件修改和扩展
http://www.csksoft.net/blog/post/mk802_dev_hacks.html

 

[3] 基于MK802的应用开发和相关的工具
http://www.csksoft.net/blog/post/mk802_dev_devtools.html

 

[4] Serial Programming Guide for POSIX Operating Systems
http://www.easysw.com/~mike/serial/serial.html

 

[5] V-USB
http://www.obdev.at/products/vusb/index.html

 

[6] libusb
http://www.libusb.org/

  • 相关文章:

发表评论:

注意:为了有效防止SPAM,任何含有http://字样的消息会被阻止发布同时,本站仅供技术交流,请不要讨论任何政治敏感话题或者低级趣味问题。

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

日历

最新评论及回复

最近发表

Copyright Shikai Chen 2000-2012. Powered By Z-Blog(CSK Modified)