浅谈GSM/GPRS模块软硬件设计(基于有方M660+模块和单片机)

GSM/GPRS芯片是手机中负责收发短信、拨打电话以及访问GPRS网络的核心器件。有方M660+为深圳有方公司生产的一款超小封装的GSM/GPRS工业无线模块,可以提供高品质的语音、短信、数据业务等功能,在各种工业和民用领域得到广泛的应用。

有方M660+ GPRS模块的硬件设计

硬件设计参考附件《M660+ 硬件设计指南》。

需要注意的几点:

  1. 模块工作电压为3.5V~4.3V(推荐值3.9V),不是5V。

  2. 模块平时工作电流较小,但是在模块注册网络或者其他一些特殊情况下,电流可能瞬间达到1.8A,如果电源供电能力不足,电压瞬间下降严重,将可能导致模块工作异常,没有信号,无法收发短信等情况。建议采用大电流的开关电源芯片供电,如LM2596 DC-DC转换模块;对于移动式设备,采用一块3.7V锂离子电池则刚好可以满足要求;另外应在GPRS模块供电端附近增加大电容滤波,滤波电容大小见硬件设计指南。

    • 瞬间电流Max 1.8A
    • 平均工作电流 <300mA
    • 待机电流1.5mA typ

  3. 该模块使用3.3V 电平串口,即高电平为3.3V,不是5V,对于5V电平的单片机或TTL串口,为保证工作可靠,不至于烧坏芯片,最好按照硬件设计指南中电路图搭建电平转换电路。

单片机系统的硬件设计

通常使用较多的单片机有51、AVR、MSP430、STM32等。需要根据系统功能,考虑选择合适的单片机。

51单片机使用5V电平,且多数型号RAM、ROM较小,运行速度较慢,适合做一些较简单的应用,优点是简便易学,且焊接容易(很容易买到使用DIP(直插式)封装的51单片机)。

430单片机使用3.3V电平,与GPRS模块能很好的兼容,且配置相比51更好,最大的特点是低功耗,且性价比较高,适合做不需要处理大量数据(如多媒体信息处理,复杂的人机交互)的多数应用。缺点是大部分430采用贴片封装,不易于焊接,且使用不当容易烧坏芯片。

STM32单片机基于ARM7内核,功能强大,功耗较低,性价比很高,但对于只接触过51一类单片机的人来说,不一定能很好的掌握,因为正是由于功能强大,导致其程序庞大,一些比较简单的应用不需要使用STM32。STM32适合开发一些需要处理一些图片、音频,以及用户可视化交互的系统。

总体来说,推荐优先选用MSP430;如果应用比较简单,可使用51;如果需要一些较为复杂的数据处理,尤其是多媒体信息,可考虑使用STM32、ARM9及以上处理器,或者结合FPFA、DSP来实现。

有方M660+ GPRS模块的软件设计

从硬件层面来说,有方GPRS模块仅仅涉及了串口通信,硬件层的编程比较容易。初次接触GPRS模块编程时,比较难的是面向AT指令集协议编程(尤其是字符串处理很麻烦),以及如何编写高效,而又可靠健壮的程序。

AT指令集是一种可用于GSM/GPRS模块的通信协议。何谓协议?举个简单的例子,我们说话过程中使用的语言,作为大家共同接受的标准,可以在我们之间传递信息,这就叫做协议。而串口则是一种接口,接口又是什么呢?接口就是收发信息的实现,我们的嘴巴和耳朵就相当于接口。

**接口和协议又有什么关系呢?**串口作为接口,只能一个字节一个字节的传输字符,类似的,我们的嘴巴和耳朵原本也只能收发不同的声音。接口是具体实现,而协议则规定了接口传输的信息代表什么含义。如果没有接口,协议肯定没法实现,人没有嘴巴和耳朵是没法用声音交流的。另一方面,如果没有协议,或者协议不兼容,接口的实现也并不能代表什么含义。没有语言的话,两个人只是乱叫完全没法交流;如果两个人使用不同的语言,而且听不懂彼此的语言(相当于协议不兼容),也是同样不能交流的。

在GPRS模块软件的开发中,接口的实现远比协议实现要简单。接口的实现在这里就不做介绍,这里主要探讨如何编写高效可靠的程序来实现GPRS模块的协议即AT指令集。

完整的AT指令集请参考附件《AT命令手册》,这里只介绍一些基本用法。

AT指令集的学习

要想编写程序实现AT指令集的功能,就应该先学习AT指令集。如何学习AT指令集呢?一些人会直接给单片机写程序去控制GPRS模块的方式学习。事实上,这样很难学好AT指令集,往往会出现各种错误。当你对AT指令集不了解的情况下,盲目的去写程序,而又并不能保证程序没有错误按设想运行(如果你能保证这一点,那么你就是编程经验非常丰富的高手了,反正我是做不到这一点),这样的情况下,实验失败时,你根本没法确定是程序的问题还是AT指令集的问题,甚至有可能是硬件问题等等。

于是,一般我们将GPRS模块连接到计算机上来学习AT指令集。对于大多数没有串口的计算机,你需要有个USB转TTL串口的转接线,即使有RS232串口,也需要通过一些转接芯片进行电平转换,转换成TTL串口。注意GPRS模块TTL串口的电平为3.3V,尽量使用电平转换电路以免电平不兼容烧坏GPRS模块。同时,你需要设计好GPRS模块的供电电路,给其提供一个稳定可靠的电压。然后插上一张可用的手机SIM卡,连接好电源和串口,GPRS模块上电后会和手机开机一样,需要数分钟的时间搜索信号,然后就可以正常工作。

电脑端我们使用串口超级终端软件来调试GPRS模块。

当我们从超级终端给GPRS模块发送“AT”,并按回车时,如果一切正常,屏幕上会显示返回的“OK”字符,这就是AT指令集最基本的操作:确认AT指令集工作正常。

通过AT指令集初始化GPRS模块一般包括以下步骤(前面加“–”符号的表示正常工作返回的字符串,不同模块不完全一样,仅供参考,//表示注释):

  1. AT // 确认AT指令集工作正常
  2. --OK
  3. ATE0 // 关闭回显(即禁止GPRS模块发送接收到每一个的字符,在电脑上调试时不需此语句)
  4. --OK
  5. AT+CPIN? // SIM卡准备好?
  6. --READY
  7. AT+CMGF=1 // 设置短消息格式为Text模式,需在模块上电一段时间后才能正常返回OK,在此之前会返回ERROR
  8. --OK
  9. AT+CSMP=17,167,0,0 // 设置Text模式参数,用于短信发送
  10. --OK
  11. AT+CSCS="GSM" // 设置GSM字符集,用于短信发送
  12. --OK
  13. AT+CREG? // 网络注册完成?应该一直循环发送此句直到网络注册完成才能进行短信和电话操作,如果没有信号,也会返回错误信息
  14. --+CREG: 0, 1, "341B", "2DBF"

如果需要使用GPRS网络,也可以在此处初始化,或者在需要使用时才初始化。

写程序时发送这些字符串要注意,在每一句结尾处应有一个"\r"结束标志(回车符,ASCII码中的13,常缩写为)。对于大部分指令,GPRS模块接收到此结束符才会执行指令。另外有些引号不可省略,在写程序时应写为转义字符。

网络注册正常后,发送ATD10086,就会给10086打电话,返回OK说明正在尝试拨打,拨号成功或没有信号会返回相应字符串。拨号成功,如果连接了话筒和耳机,就能正常通话了。

发送短信有两种方式,一种是TEXT模式,另一种是PDU模式,需要先设置好。其中TEXT模式只支持英文(不排除可能有些模块或者某些特别的方法能使其支持中文),PDU模式支持中文,但编码方式较为复杂。

PDU可参考:http://yuanyu5237.iteye.com/blog/1126185

注:文中提到一个字符ASCII码中的26(0x1A),在超级终端中可通过Ctrl+Z输入

AT指令集协议的编程实现

要编写一个高效而又可靠的程序,我们既要向GPRS模块发送数据,又需要接收返回的数据并判断返回值是否正确。

发送数据可以调用串口发送函数直接发送,而接收数据时,需要接收一段完整的字符串。由于单片机可能正在处理其他任务,并不一定能马上响应接收的指令。编程时我们可以使用环形队列作为串口接收缓冲区,并在接收到"\r"结束符时置位一个接收标志,然后等待CPU处理。对于一些特殊情形,返回值并不是以"\r"结束,比如发送短信提示输入短信内容时是以">"结束,此时可以通过延时等待并不断查询的方式判断是否接收到正常的返回值。

还有一种通过超时判断是否接收完一条指令的方法,效果相对比较好。当接收到第一个字符时,启动定时器计时,如果一定时间内接收到下一个字符,清零定时器,再次重新计时……当接收到某个字符之后,计时到超过一定时间仍未收到下一个字符,表明接收完一条指令。因为返回的每条指令都是连续发送出来的,间隔时间固定,所以这种方式能准确判断指令是否接收完成,缺点是会占用更多硬件资源(需要多用一个定时器)。

为了充分实现代码复用,加快程序开发,可以考虑实现这样几个函数:

  1. // 字符串复制(可使用string.h中的标准函数)
  2. void StrCopy(char*, char*);
  3. // 判断一个字符串是否包含另一个字符串
  4. unsigned char StrContain(char*, char*);
  5. // 发送指令并等待返回指定字符串。注意如果超时仍未返回指定字符串,则应退出函数,以免死机
  6. unsigned char SendAndGet(char*, char*);
  7. // 发送指令并等待返回指定字符串,尝试n次
  8. unsigned char SendAndGetTimes(char*, char*, unsigned char);
  9. // 初始化AT指令集设备,返回初始化是否成功结果
  10. unsigned char AT_Init(void);
  11. // 发送短信,并返回发送是否成功标志
  12. unsigned char SendMsg(char*, char*);
  13. // 检测模块当前是否有信号
  14. unsigned char CheckSignal(void);

一些更复杂的问题

当先后间隔很短时间发送多条指令,或者是前一条指令需要GPRS模块使用较长时间去响应时,情况就变得比较复杂了。比如说,我们给出指令发送一条短信,一般可能在10秒左右时间发完,然后返回OK。而在返回OK之前,如果又需要发送另外一条指令,或者很不幸有人打电话来,处理起来就很麻烦了。再比如在间隔很短时间内接收到多条消息,前一条正在处理,后一条消息又被接收到,这个时候怎么保证不遗漏消息呢?我们可以考虑使用队列来解决这个问题。

类似这样的问题很多,尤其对于速度低存储空间小的单片机来说也不太好解决,而对于一些简单应用也不一定要考虑这么多情况,本文不做详细探讨。

附件

串口超级终端 http://download.csdn.net/detail/jzj1993/5744509
有方M660+硬件设计指南 http://download.csdn.net/detail/jzj1993/5507753
AT命令手册 http://download.csdn.net/detail/jzj1993/5507747