—————————————————————————————————————————————
主机操作系统:Centos 6.5
交叉编译器环境:arm-linux-gcc-4.5.4
开发板平台: FL2440
Linux内核版本: linux-3.0
开发模块: SIM900 GPRS
邮箱:[email protected]
—————————————————————————————————————————————
开发提醒:在开发gprs模块打电话发短信之前需满足开发板能正常加载linux内核及文件系统,并且开发板的串口已经使能,同时需准备一张开通gprs流量的sim卡。
一:GPRS介绍
GSM模块,是将GSM射频芯片、基带处理芯片、存储器、功放器件等集成在一块线路板上,具有独立的操作系统、GSM射频处理、基带处理并提供标准接口的功能模块。GSM模块根据其提供的数据传输速率又可以分为GPRS模块、EDGE模块和纯短信模块。短信模块只支持语音和短信服务。GPRS,可说是GSM的延续。它经常被描述成“2.5G”,也就是说这项技术位于第二代(2G)和第三代(3G)移动通讯技术之间。GPRS的传输速率从56K到114Kbps不等,理论速度最高达171k。相对于GSM的9.6kbps的访问速度而言,GPRS拥有更快的访问数据通信速度,GPRS技术还具有在任何时间、任何地点都能实现连接,永远在线、按流量计费等特点。EDGE技术进一步提升了数据传输的速率到384K-473K,被称为”2.75G”,数据传输速率更2倍于GPRS。目前,国内的GSM网络普遍具有GPRS通讯功能,移动和联通的网络都支持GPRS,EDGE在部分省市实现了网络覆盖。
GPRS模块,是具有GPRS数据传输功能的GSM模块。GPRS模块就是一个精简版的手机,集成GSM通信的主要功能于一块电路板上,具有发送短消息、通话、数据传输等功能。GPRS模块相当于手机的核心部分,如果增加键盘和屏幕就是一个完整的手机。普通电脑或者单片机可以通过RS232串口与GPRS模块相连,通过AT指令控制GPRS模块实现各种基于GSM的通信功能。
GPRS模块区别于传统的纯短信模块,两者都是GSM模块,但是短信模块只能收发短信和语音通讯,而GPRS模块还具有GPRS数据传输功能。”
二:硬件连接
由于GPRS,串口都是设备,难道就不需要再在linux内核中使能驱动吗?是这样的,在一开始内核中就已经对串口驱动进行了使能,而GPRS模块中有GPRS模块的驱动,这个模块通过自身的串口不断的发送数据开发板需要做的就是读取然后处理就够了。
串口线将 GPRS模块的串口和开发板的串口连接起来(我连接的是开发的第二个串口(ttys1),因为第一个串口连接PC了(ttys0)),然后将可以使用的手机SIM卡插入 GPRS模块的卡槽;然后启动开发板,接通GPRS模块的电源,然后按下GPRS模块的SW1,让SIM开始工作,搜索网络这时发光二极管D3会闪烁。硬件连接如下图:
在开发板上测试硬件连接:
~>microcom -s 115200 /dev/ttyS1
发送AT 是否能返回 Ok 检查模块和板子是不是连接好的模块能不能正常的工作(microcom是在制作根文件系统时busybox里自带命令)
开发步骤:
1.linux下c语言编程配置串口(串口参数的配置主要包括:波特率、数据位、校验位、停止位)
2.根据gprs的AT命令使用c语言编写具有打电话和发短信功能的代码
3.编写主函数调用打电话和发短信c代码,同时编写makefile文件
本次开发包含以下这几个文件:gprs_main.c call_number.c set_ttyS1.c send_message.c gprs.h makefile 主函数在gprs_main.c文件中,call_number.c是实现gprs打电话功能,send_message.c 实现gprs发短信功能,set_ttyS1.c 主要是设置gprs串口配置函数设计,gpd.h是头文件!
三:串口编程
至于串口编程的详细介绍,如何设置波特率,如何设置停止位等等,以下给出两个linux串口编程的博客链接,讲的很详细,我就在此不多说了:
http://www.cnblogs.com/wblyuyang/archive/2011/11/21/2257544.html
http://blog.csdn.net/mtv0312/article/details/6599162
其中串口设置其实就相当于串口通信的协议,
波特率:是为了两者信号流能同步,
数据位:是指又几位数据封装成一帧
结束位:是指以帧传输数据时,协定好结束位,便于提取有效数据
奇偶校验:检验数据的一种手段
四者的设置又通信双方协定。
串口设置由下面的结构体实现:
struct termios
{
tcflag_t c_iflag; //input flags
tcflag_t c_oflag; //output flags
tcflag_t c_cflag; //control flags
tcflag_t c_lflag; //local flags
cc_t c_cc[NCCS]; //control characters
};
该结构体中c_cflag最为重要,可设置波特率、数据位、校验位、停止位。在设置波特率时需要在数字前加上’B’,如B9600,B15200.
串口使用详解
打开串口 :
fd = open(“/dev/ttyS0”,O_RDWR | O_NOCTTY | O_NDELAY);
参数–
O_NOCTTY:通知linux系统,这个程序不会成为这个端口的控制终端.
O_NDELAY:通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止).
然后恢复串口的状态为阻塞状态,用于等待串口数据的读入,用fcntl函数: fcntl(fd,F_SETFL,0); //F_SETFL:设置文件flag为0,即默认,即阻塞状态
读写串口:
串口的读写与普通文件一样,使用read,write函数
read(fd,buff,8);
write(fd,buff,8);
配置代码如下:
/********************************************************************************* * Copyright: (C) 2016 SCUEC * All rights reserved. * * Filename: set_ttyS1.c * Description: This file * * Version: 1.0.0(2016年02月25日) * Author: hulu <[email protected]> * ChangeLog: 1, Release initial version on "2016年02月25日 21时40分23秒" * ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
/************************************************************************************** * Description: 串口参数配置 * Input Args: fd:open打开的文件描述符 nspeed:波特率 nBits:数据位数 nEvent:奇偶校验 nStop:停止位 * Output Argtingzhis: 串口参数设置失败返回-1 * Return Value: *************************************************************************************/
int set_opt(int fd,int nSpeed,int nBits,char nEvent,int nStop)
{
struct termios newttys1,oldttys1;
if(tcgetattr(fd,&oldttys1)!=0) //保存原先串口配置
{
perror("Setupserial 1");
return -1;
}
bzero(&newttys1,sizeof(newttys1)); //将一段内存区域的内容全清为零
newttys1.c_cflag|=(CLOCAL|CREAD ); //CREAD 开启串行数据接收,CLOCAL并打开本地连接模式
newttys1.c_cflag &=~CSIZE; //设置数据位数
switch(nBits) //选择数据位
{
case 7:
newttys1.c_cflag |=CS7;
break;
case 8:
newttys1.c_cflag |=CS8;
break;
}
switch( nEvent ) //设置校验位
{
case '0': //奇校验
newttys1.c_cflag |= PARENB; //开启奇偶校验
newttys1.c_iflag |= (INPCK | ISTRIP); //INPCK打开输入奇偶校验;ISTRIP去除字符的第八个比特
newttys1.c_cflag |= PARODD; //启用奇校验(默认为偶校验)
break;
case 'E' : //偶校验
newttys1.c_cflag |= PARENB; //开启奇偶校验
newttys1.c_iflag |= ( INPCK | ISTRIP); //打开输入奇偶校验并去除字符第八个比特
newttys1.c_cflag &= ~PARODD; //启用偶校验;
break;
case 'N': //关闭奇偶校验
newttys1.c_cflag &= ~PARENB;
break;
}
switch( nSpeed ) //设置波特率
{
case 2400:
cfsetispeed(&newttys1, B2400); //设置输入速度
cfsetospeed(&newttys1, B2400); //设置输出速度
break;
case 4800:
cfsetispeed(&newttys1, B4800);
cfsetospeed(&newttys1, B4800);
break;
case 9600:
cfsetispeed(&newttys1, B9600);
cfsetospeed(&newttys1, B9600);
break;
case 115200:
cfsetispeed(&newttys1, B115200);
cfsetospeed(&newttys1, B115200);
break;
default:
cfsetispeed(&newttys1, B9600);
cfsetospeed(&newttys1, B9600);
break;
}
if( nStop == 1) //设置停止位;若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB。
{
newttys1.c_cflag &= ~CSTOPB; //默认为送一位停止位;
}
else if( nStop == 2)
{
newttys1.c_cflag |= CSTOPB; //CSTOPB表示送两位停止位;
}
//设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时
newttys1.c_cc[VTIME] = 0; //非规范模式读取时的超时时间;
newttys1.c_cc[VMIN] = 0; //非规范模式读取时的最小字符数;
tcflush(fd ,TCIFLUSH); //tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来
// 在完成配置后,需要激活配置使其生效
if((tcsetattr( fd, TCSANOW,&newttys1))!=0) //TCSANOW不等数据传输完毕就立即改变属性
{
perror("com set error");
return -1;
}
return 0;
} /* ----- End of if() ----- */
三:编写打电话发短信代码
在编写代码之前先了解一下gprs常用的AT命令:
AT+CMGC Send an SMS command(发出一条短消息命令)
AT+CMGD Delete SMS message(删除 SIM 卡内存的短消息)
AT+CMGF Select SMS message formate (选择短消息信息收发格式: 0-PDU;1-文本)
AT+CMGL List SMS message from preferred store(列出 SIM 卡中的短消息
AT+CMGR Read SMS message(读短消息)
AT+CMGS Send SMS message(发送短消息)
AT+CMGW Write SMS message to memory(向 SIM 内存中写入待发的短消息)
AT+CMSS Send SMS message from storage(从 SIN |M 内存中发送短消息)
AT+CNMI New SMS message indications(显示新收到的短消息)
AT+CPMS Preferred SMS message storage(选择短消息内存)
AT+CSCA SMS service center address(短消息中心地址)
AT+CSCB Select cell broadcast messages(选择蜂窝广播消息)
AT+CSMP Set SMS text mode parameters(设置短消息文本模式参数)
AT+CSMS Select Message Service(选择短消息服务)
AT+CNMI=2,1,0,0,0 //设置收到新短信存于SIM卡中并发CMTI通知
+CMTI:”SM”,1 //收到了短信,自动弹出,其中1表示存在SIM中的序号
AT+CMGR=1 //读取短信,其中1要与上面序号对应
AT+CMGD=1 //删除短信,其中1为短信序号
编写打电话代码:
/********************************************************************************* * Copyright: (C) 2016 SCUEC * All rights reserved. * * Filename: call_number.c * Description: This file * * Version: 1.0.0(2016年02月26日) * Author: hulu <[email protected]> * ChangeLog: 1, Release initial version on "2016年02月26日 10时46分56秒" * ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include"gprs.h"
/************************************************************************************** * Description: gprs打电话函数 * Input Args: fd 串口设备文件描述符 * Output Args: * Return Value: *************************************************************************************/
int call_number (int fd)
{
getchar(); //将缓冲区回车吃掉
int count=0;
char call[20]="atd";
char number[20];
char reply[128];
printf("enter you call number\n");
if(NULL==fgets(number,20,stdin)) //输入电话号码,其中fgets在读入一个字符串后在字符串尾端默认加入\n字符
exit(0); //这个语句的功能可以用gets实现,区别在于 fgets 读入的含 "\n"(最后一个字符),gets 不含 "\n"
while(strlen(number)!=12)
{
printf("please again number\n");
if(NULL==fgets(number,20,stdin))
exit(0);
if(count==3)
exit(0);
count++;
}
number[strlen(number)-1]='\0'; //将刚才fgets读入的字符串尾端去除\n字符
strcat(call,number);
strcat(call,";\r"); // \r是换行字符
write(fd,call,strlen(call)); //向串口拨打号码
printf("write %s\n",call);
sleep(3);
memset(reply,0,sizeof(reply));
read(fd,reply,sizeof(reply));
printf("%s\n",reply);
printf("=================================================\n");
printf("number is calling,please press 'a' hang up \n");
printf("=================================================\n");
while('a'!=getchar())
printf("please again input 'a' to hung up \n");
memset(call,0,sizeof(call));
strcpy(call,"ATH\r");
write(fd,call,strlen(call));
sleep(1);
memset(reply,0,sizeof(reply));
read(fd,reply,sizeof(reply));
printf("%s\n",reply);
printf("has hung up\n");
return 0;
} /* ----- End of call_number() ----- */
编写发短信代码:
/********************************************************************************* * Copyright: (C) 2016 SCUEC * All rights reserved. * * Filename: send_message.c * Description: This file * * Version: 1.0.0(2016年02月26日) * Author: hulu <[email protected]> * ChangeLog: 1, Release initial version on "2016年02月26日 10时46分28秒" * ********************************************************************************/
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include"gprs.h"
/************************************************************************************** * Description: gprs发短信函数 * Input Args: fd 串口设备文件描述符 * Output Args: * Return Value: *************************************************************************************/
int send_message (int fd)
{
int count=0;
char cmgf[]="at+cmgf=1\r"; //设置短信发送模式为text (0-PDU;1-text)
char cmgs[128]="at+cmgs=\"";
char send_number[16];
char message[128];
char reply[128];
getchar(); //吃掉缓冲区回车
printf("enter send_message number :\n");
if(NULL==fgets(send_number,16,stdin))
exit(0);
while(12!=strlen(send_number))
{
getchar();
printf("please again input number\n");
if(NULL==fgets(send_number,16,stdin))
exit(0);
if(count==3)
exit(0);
count++;
}
send_number[strlen(send_number)-1]='\0'; //去除字符串末端读入的换行符\n;
strcat(cmgs,send_number);
strcat(cmgs,"\"\r");
printf("enter send_message :\n");
if(NULL==fgets(message,128,stdin))
exit(0);
message[strlen(message)-1]='\0';
strcat(message,"\x1a");
/* write cmgf */
write(fd,cmgf,strlen(cmgf));
printf("write %s\n",cmgf);
sleep(2);
memset(reply,0,sizeof(reply));
read(fd,reply,sizeof(reply));
printf("%s\n",reply);
/* write cmgs */
write(fd,cmgs,strlen(cmgs));
printf("writr %s\n",cmgs);
sleep(5);
//memset(reply,0,sizeof(reply));
//read(fd,reply,sizeof(reply));
//printf("%s\n",reply);
/*write message*/
write(fd,message,strlen(message));
printf("writr %s\n",message);
sleep(4);
memset(reply,0,sizeof(reply));
read(fd,reply,sizeof(reply));
printf("%s\n",reply);
return 0;
} /* ----- End of send_message() ----- */
四:编写main函数及头文件,makefile文件
工程头文件如下:
/********************************************************************************
* Copyright: (C) 2016 SCUEC
* All rights reserved.
*
* Filename: gprs.h
* Description: This head file
*
* Version: 1.0.0(2016年02月26日)
* Author: hulu <[email protected]>
* ChangeLog: 1, Release initial version on "2016年02月26日 10时55分23秒"
*
********************************************************************************/
#ifndef __GPRS_H__
#define __GPRS_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
extern int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop);
extern int send_message(int fd);
extern int call_number(int fd);
#endif
main函数:
/*********************************************************************************
* Copyright: (C) 2016 SCUEC
* All rights reserved.
*
* Filename: gprs_main.c
* Description: This file
*
* Version: 1.0.0(2016年02月26日)
* Author: hulu <[email protected]>
* ChangeLog: 1, Release initial version on "2016年02月26日 10时38分16秒"
*
********************************************************************************/
#include"gprs.h"
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop);
int send_message(int fd);
int call_number(int fd);
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
int fd;
char select;
fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);
if(fd<0){
perror("Can't Open Serial Port");
return -1;
}
//配置串口
set_opt( fd,115200,8,'N',1);
printf("==========================================\n");
printf("gprs call number and send message\n");
printf("==========================================\n");
printf("enter your select: 's' is send message, 'c' is call number, 'q' is exit \n");
select=getchar();
switch(select)
{
case 's':
send_message(fd);
break;
case 'c':
call_number(fd);
break;
case 'q':
exit(0);
break;
default:
break;
}
close(fd);
return 0;
} /* ----- End of main() ----- */
编写makefile文件
CC =/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
objs =set_ttyS1.o call_number.o send_message.o gprs_main.o
srcs =set_ttyS1.c call_number.c send_message.c gprs_main.c
gprs_bin: $(objs)
$(CC) -o gprs_bin $(objs)
@make clean
gprs_main.o: $(srcs) gprs.h
$(CC) -c $(srcs)
set_ttyS1.o: set_ttyS1.c gprs.h
$(CC) -c set_ttyS1.c
call_number.o: call_number.c gprs.h
$(CC) -c call_number.c
send_message.o: send_message.c gprs.h
$(CC) -c send_message.c
clear:
@rm gprs_bin
.PHONY: clean
clean:
@rm *.o
五:编译并烧录到开发板apps目录下执行
注意:将上图gprs_bin烧录到开发板后,记得更改可执行权限
~>chmod 777 gprs_bin
~>./gprs_bin