脑电波模块以串口UART相连,提供了有线直连和无线蓝牙两种物理连接方式最初的情况是脑电波模块只有Windows下的调试程序和源代码,而我们使用的是树莓派B+,它的硬件框架是ARMv6,因此实际上不但不能支持Windows,就连大部分Linux发行版诸如Ubuntn都不支持
官方推荐的是Raspbian,这是一个由Debian经过为树莓派优化改进后的版本(%3的软件包经过重新编译)到官网下载系统安装包(实际上是img镜像 )通过Win32DiskImager写入SD卡(在官网上会有一张它所支持的或不支持的SD卡品牌型号,并且考虑到201502版的Raspbian展开有14个G,所有SD卡大小最后不小于32G)
SSH 树莓派默认开启了ssh连接,通过一根网线直连或通过路由器 或UART串口和电脑相连,windows下用PuTTy等远程连接软件连接,并且树莓派安装了tightvnc后,可以用tightvnc 远程开启树莓派图形界面
默认密码root:pi
password: raspberry
首先是sudo raspi-config
进入配置界面,将整个空间拓展到sd卡全部,修改键盘布局及地区,设置字库spacs
选中
/boot/cmdline.txt
将cOnsole=ttyAMA0 kgdboc=ttyAMA0两个选项删除,/dev/ttyAMA0 就是UART端口文件/etc/inittab
将 最后一行 #T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
注释掉 以下是各种比较挫折的经历,实际上折腾这个串口的编程持续到了现在,它的关键实际上就是:
1. 从串口获取数据并成功解析出来
2. 实时获取串口数据,在此基础是实现非堵塞更好
为了实现以上两个目的,作为菜鸟的我从2014年折腾到2015年,冬去春来花落花开一转眼夏天都要到来了。。。
脑电波串口发送的是16进制数据,数据格式听起来很清楚,但仔细一想md,16进制还是10进制还是2进制和我有什么关系,我要的是编码方式!也许这个16进制是某种默认的编码方式但我对那很陌生,一开使尝试 用网线直连树莓派和笔记本ssh远程连接,用线直连脑电波模块串口 并且用 minicom(linux)调试串口,嗯,没什么意义,而且一直收不到目标数据
后来用 TTL串口转usb在Windows下调试,还是收不到数据,也试过TTL串口转usb连树莓派能正常从树莓派向笔记本发送数据,但无法接受数据,这里没有深入研究,之后上网搜了一下串口编程的源码因为我只是对c不谦虚的说比较熟练因此代码只局限于c,结果找出了一大坨的代码,
满屏跑宏,看看就是微醺。在大二下学期的剩余时间里研究c代码,又尝试让树莓派显示中文并输入中文,但尝试很长时间无果,也没有深究。至今疑惑(通过SSH当然可以输入中文)
其实大部分时间还是学习(毕竟是高中一般的西北某高校)和各种强制性的无聊的官方活动
实际上没有做什么有实际效果的事情,到了没有强制性活动
的寒假,花了几天看完了三本Windows注册表的入门级书,又买了本python 学习手册啃啃,啃完了基本数据类型,呵,啃不动了,再加上装电脑什么的就又耽搁了(好吧这其实是借口,前二十天)后二十天又学习了若干部精品Galgame,看了几本小说最后一个周,想起某位操蛋的脸,和我心中残存的良知,我开始重新拾起c的串口源代码,嗯嗯,
其实最麻烦的就是关于串口的控制,各种宏的名字真是抽象,好在本人擅长黑箱编程 总算还是把c串口操作的代码弄好了(期间又浅浅了解了vi/vim,emacs,gcc)整天看着raw字符界面感觉很有逼格233,接着就是
开学。(哎,假期总是太短了。。。)
开学以后,在折腾c,但是自发自收没问题,却无法接收脑电波正确的数据,我看到了穷举式解决问题的思路,颇以为然,然后就开始寻找其他串口通信的方法,听说python是生成快速原型的有力的工具,就找了一下python串口编程(其实就是打开串口,写和读)我擦,居然如此简洁,于是我就抱着试一试的态度分分钟用python重新写了通信的程序,甚至包括windows下的(就那么几行)可是依旧是脑电波模块以外能正常收发数据,而这时候实际上依旧不能通过TTL串口转USB 来调试脑电波模块,通过蓝牙串口的却可以在windows下用官方给定的程序下正确的结束数据,这时候我没有意识到我其实离成功就差一根线
那是后来我的小伙伴小唯 过来关心teammate,我说明了情况,他说向要向 上天祈祷答案,本来没有在意,结果大神给的建议里的一条真的彻底扭转了被动局面(快要中期答辩(同时评定项目等级)了,却还没有东西做出来)建议就是把共地线连上(啥,你说为什么之前连共地线都没有连,哈哈不懂就是这么任性,现在想想如此缺乏电路常识,真是有损我西北某高校的名声)
和脑电波共地以后,接收到的数据也正常了,精神也高涨了,解析个包然后稍微分析一下也是分分钟的事了。但有一点,数据是正确的收到了,但经过分析,全是噪声,根据前次经验,继续向上天祷告,这次大神同样没有令人失望,不但告诉我们用线直连串口对脑电波模块就是干扰大的事实(之前的初步验证是这样的)而且说明了不能用TTL串口转USB的原因
脑电波模块波特率太高(38400应该支持的。。。)不过感谢模电告诉我们的常识,这个理由起码是可以接受的
接下来小伙伴小唯 提出用蓝牙串口代替线连,开始到某宝买了一个,试了几天,发现是坏的。。。,退货,重新买了一个,型号是**CZ-HC-05**CZ-HC-0X系列貌似差不多,嗯,写了这么长时间,是时候贴出源码提升一下逼格了,以下是将蓝牙串口设置为从机模式,而且连接绑定
脑电波模块的蓝牙的过程。
#-*-coding:utf-8-*-
#python 2.7
import serial
import time
ser=serial.Serial('/dev/ttyAMA0','38400')
status=ser.isOpen()
print status
def Transaction(requst,response,ser):
while True:
ser.write(requst)
print 'send:'+requst
n=ser.inWaiting()
response_2=ser.read(n)
print response_2
print len(response_2)
raw_input('press any key to continue')
if response_2==response:
break
#Testing
while True:
ser.write('AT\r\n')
print 'send requst...'
n=ser.inWaiting()
respOnse=ser.read(n)
print response
print len(response)
raw_input('press any key to continue')
if respOnse=='OK\r\n':
break
#set the mode of the Device
while True:
print 'set at the mode of master...'
ser.write('AT+ROLE=1\r\n')
respOnse=ser.read(4)
print response
raw_input('press any key to continue')
if respOnse=='OK\r\n':
break
#Set the mode of query #0--bind mode 1--any 2--loop
print 'Set the mode of query'
Transaction('AT+CMODE=0\r\n','OK\r\n',ser)
#Bind the Addr
print 'Bind the Addr'
Addr='80BA,86,6CE14B'
Transaction('AT+BIND='+Addr+'\r\n','OK\r\n',ser)
#Set the pw
print 'Set the pw'
pw='0000'
Transaction('AT+PSWD='+pw+'\r\n','OK\r\n',ser)
#Set the parameter of the UART # baud,stopbits,cntl #stopbits--0:11:2cntl--0:none 1:odd 2:even
print 'Set the parameter of the UART'
UART='57600,0,0'
Transaction('AT+UART='+UART+'\r\n','OK\r\n',ser)
ser.close()
注释:
1. 就像前文说的树莓派的中文显示问题没有搞定,也没时间了,所以 注释全是英文。
2. serial不是2.7的标准库,好在树莓派创建的初因是一台python学习机 ,python装的很全,其实不全也没关系,下载也很方便,利用pip。
3. /dev/ttyAMA0
是树莓派下的串口地址。
4. 其余情况对照 CZ-HC-0X 系列AT 语法易理解
之前忘说了,使用前还要激活,不说了,key 是激活接口,其余网上教程很详细,只是还要注意转为AT模式有两种,二者的波特率不同。
接上蓝牙串口后,噪声神奇的没有了!然后开始寻找眨眼的动作,之前和期间又投入了大量时间解决树莓派的上网问题,最初是可以利用网线直连笔记本,共享笔记本网络的,但后来一次树莓派突然断电(西北某高校晚上断电传统,但那次提前断电了。。。)之后各种折腾包括树莓派重刷系统都无法
连到树莓派,ip找不到,树莓派也上不去网,换了一个笔记本试也是这样,
真是诡异。b+没有无线网卡,我先后买了两个linux下有驱动的无线网卡一个某宝上的,声称有usb接口,结果是有四个孔,kao,自己焊呐,期间犹豫要不要焊的时候又买了某东上的号称专为树莓派准备的免驱动的EUDP无线网卡,但是买回来一看,Linux下免驱都是骗人的(实际我之前傻乎乎的修改了若干遍无线网配置文件,阿西吧果然还是too young吗),EUDP官网上的驱动Linux内核版本,太老了是3.x而201502版的raspbian 的内核版本是3.18 呵呵哒,估计起码这1415两年买的EUDP树莓派要用都得重新编译驱动!
再说说之前那个需要焊USB的无线网卡,虽然我西北某高校盛产焊工,但我委实没有这项手艺和工具,好在拿到某某实验室,好心学长帮焊,又拿502粘了又买了usb延长线,结果发现那个网上适用范围模糊的驱动果然不能用。。。
我也曾(ˇˍˇ) 想~一怒之下学习Linux下的编译。但看了一下教程,嗯
(@ - @)根据以往的经验,再考虑到西北某高校以及新来的某人瞎折腾给我们带来的时间上的压力,学习达到一定效果可能以月记(毕竟完全是小白),而答辩时间上不允许,最后还是滞后了这个问题,毕竟根据会根据源码重新编译很重要,虽然嵌入式方向不太是我的菜。
没办法,还是暂时通过路由器上网吧。
后来小伙伴小唯 去某宝上买了一个1080p树莓派推荐摄像头,号称免驱嗯,还是假的,而且更新了驱动也不能用,坏的,再退货。。。
好了继续谈我的串口编程问题,换上蓝牙串口后一切正常,唯一不正常的是我的程序,太慢了,数据处理的速度,居然赶不上1s的串口数据等待时间。虽然是python但这也太奇葩了吧,这时候无意关注到之前发布的树莓派2
看完就跪了,ARMv7 架构 四核900MHz 1G RAM性能保守估计是B+(单核700MHz、500MRAM)的六倍!而且升级到了ARMv7,这意味着不仅可以支持恐怕是全部的Linux发行版,甚至可以刷WIN10(事实已经有很多人那么干了)而且价格一样几百块搞定233瞬间想起了王菲的那句歌词 为谁辛苦 为谁甜 想起了Aldnoah Zero 那令人无语的结局,想起了某老师毕设是安卓反编译的那年安卓开源了想起了。。。
不过推倒回去显然是不现实的,做下去也挺好,于是乎继续,改进数据处理的时间。很自然的未经测试的我想到这么慢是硬件限制下用python不可避免的结果,然后就想到用C来改写我自以为的拖慢运行时间的部分,实际上我的确是测试了各部分的运行时间,但后来证明我的测试不够准确,因为运行时间和代码量不成正相关,python不同的语句运行时间相差可能不止一两个数量级。接下来我开始了两周的南辕北辙行动。安照
开始考虑多线程,_Thread (python 3.x),Thread(python 2.x),通过
全局锁进行同步,做完了才知道没有用。B+是单核的,而且python的线程无法不是真正的并行。
**后来
Python Programming 书中推荐的,我写好了c源程序和头文件,然后使用swig 封装产生c的封装文件,最后编写makefile 文件,编译产生动态库和对应的python模块的脚本。
sudo apt-get install python-dev
。.i
文件方法可查swig -python -I xxx(c头文件所在目录) xxx.i(之前编写的.i文件)
模块名_wrap.c
(模块名在.i文件开头设置好) 胶水源文件和 模块名.py
gcc -shared 模块名_wrap.c 源文件.c -L xxx(python 库目录) -l xxx(要连接到的库名比如python2.7) -o xxx(Linux .so windows .dll)
_模块名.so
或_模块名.dll
用swig在C/C++中拓展python 优点:
1. 不需要手工在C/C++中写大量的粘合部分。
2. 生成对应的模块可以向python模块一样用,使用拓展时是透明的。非指针的值传递一点问题都没有。
缺点:
1. 很难通过指针传递的参数 因此不实用
花了大约一个多周的努力,我认识到直接用swig封装是不靠谱的,因为我需要将传入的参数传回去。
此时我可以选择
1. 手动编写胶水部分的c代码,然后自己修改可以传递指针。
2. 利用python ctypes
库直接调用c api。
我选择了第二项,于是将之前写的.c 源文件直接编译成 .so 动态库
gcc -shared -fPIC -L xxx(头文件目录) -fpermissive xxx.c -o xxx.so` 。
看了一晚上的ctypes英文文档
然后改写竟然分分钟搞定了。然而运行时我发现,调用c优化后的python程序居然比之前还慢!
又尝试将转码部分也用C实现,发现遇到了问题,传过的c_char_p直接转成"%02X"
的16进制格式,格式化成unsigned char*
类型不行
又尝试用c_wchar_p
传 字符串,在
下 用swprintf(wchar_t *s,size_t n,const wchar_t * format,...)
%ls 16进制字符串。但依旧不行。
用python很容易实现的转码,c比较麻烦,看到网上有用c++做的,不理解原理,具体的编码还要学习。算了,不管
进一步进行运行时间的测量,发现最耗时的不是我之前所想的那样,是大段的全包搜索模式匹配,而是转码。具体来说就是eval(str)
这条实际上无关紧要的写了只为图方便的语句。。。改掉以后运行时间直接减为原来的2/5
然后文章开头提到的串口编程的两个核心问题解决了。
ctypes
调用 C API 或自己编写的C/C++库