SIM900B是一款新型无线模块,属于B2B类型的四频GSM/GPRS模块,采用非常强大的AMR926EJ-S单芯片处理器,可完全兼容于SIM300/340 。其性能稳定,外观小巧,性价比高,能满足您的多种需求。SIM900B采用工业标准接口,工作频率为GSM/GPRS 850/900/1800/1900MHz,可以低功耗实现语音、SMS、数据和传真信息的传输。
2、AT指令集
要与GPRS进行通信,首先要了解AT指令,AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter, TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。通过TA,TE发送AT指令来控制移动台(Mobile Station,MS)的功能,与GSM 网络业务进行交互。用户可以通过AT指令进行呼叫、短信、电话本、数据业务、传真等方面的控制。
2.1 、常用AT指令
这里我就列出我在编程当中所用到的一些AT命令,更多的命令详见http://wenku.baidu.com/view/f8168c50f01dc281e53af0d4.html
1、检测SIM卡是否注册上
AT+CPIN?返回值:READY
2、检测SIM卡的信号强度
AT+CSQ返回值:+CSQ 29,99
3、查询模块版本
AT+CGMR返回值:R4A021 CXC1122528 (版本信息)
4、拨打电话
ATD+号码+;
5、发英文短信
AT+CMGF=1/0(PS:1是文本模式;0是PDU模式)
AT+CMGS="号码"
>短信内容(只能是英文或者数字,要发中文,要进行PDU编码,比较麻烦,想学的可以直接百度)
Ctrl+Z(发送)
3、串口
因为我们的开发板是通过串口向GPRS模块发送命令的,所以这里我们先来了解一下串口。在Linux中,它给我们提供了一个termios结构体,这使得我们更方便的在程序中对串口进行初始化。
(更详细的信息请参考:http://baike.sogou.com/v53994134.htm?fromTitle=Termios)
让我们先来看一下termios结构体内部信息:
struct termios {
unsigned short c_iflag; /* 输入模式标志*/
unsigned short c_oflag; /* 输出模式标志*/
unsigned short c_cflag; /* 控制模式标志*/
unsigned short c_lflag; /*区域模式标志或本地模式标志或局部模式*/
unsigned char c_line; /*行控制line discipline */
unsigned char c_cc[NCC]; /* 控制字符特性*/
};
常用校验位和停止位的设置:
无校验位 8位 Option.c_cflag &= ~PARENB;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= ~CSIZE;
Option.c_cflag |= ~CS8;
奇校验位 7位 Option.c_cflag |= ~PARENB;
Option.c_cflag &= ~PARODD;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= ~CSIZE;
Option.c_cflag |= ~CS7;
偶校验位 7位 Option.c_cflag &= ~PARENB;
Option.c_cflag |= ~PARODD;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= ~CSIZE;
Option.c_cflag |= ~CS7;
Space校验 7位 Option.c_cflag &= ~PARENB;
Option.c_cflag &= ~CSTOPB;
Option.c_cflag &= &~CSIZE;
Option.c_cflag |= CS8;
Linux下串口的操作一般分为四个步骤:
1、打开需要使用的设备
在 Linux 下串口文件是位于 /dev 下的
串口一 为 /dev/ttyS0(/dev/ttyUSB0)
串口二 为 /dev/ttyS1(/dev/ttyUSB1)
fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);
2、获取设备的属性
struct termios options;
tcgetattr(fd, &options);
3、设置串口
options.c_cflag |= ( CLOCAL | CREAD );
options.c_cflag &= ~CSIZE;
options.c_cflag &= ~CRTSCTS;
options.c_cflag |= CS8;
options.c_cflag &= ~CSTOPB;
options.c_iflag |= IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
cfsetispeed(&options, B115200);//设置发送波特率
cfsetospeed(&options, B115200); //设置接收波特率
4、将所设置的参数生效
tcsetattr(fd,TCSANOW,&options);
TCSANOW: 不等数据传输完毕就立即改变属性
TCSADRAIN: 等待所有数据传输结束才改变属性
TCSAFLUSH: 清空输入输出缓冲区才改变属性
4、线程
1、简介
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,那就是程序本身。线程能独立执行,能充分利用和发挥处理机与外围设备并行工作的能力。
2、实例代码
这是一个简单的创建线程的示例代码,能让我们更快的了解如何创建一个线程,这个例子中运用到了互斥锁,互斥锁的作用是用来保证程序在一个时间内只有一个线程运行,这样保证了线程执行的先后顺序,只有当一个线程释放锁之后,另一个上锁的线程才能运行,接下来就是代码:
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex ;
void *print_msg1(void *agrs ){
pthread_mutex_lock(&mutex);
char led;
led=*(char*)agrs;
if(led=='1')
{
printf("this is print_msg1,led=%c\n",led);
}
usleep(100);
pthread_mutex_unlock(&mutex);
}
void *print_msg2(void *arg){
pthread_mutex_lock(&mutex);
printf("this is print_msg2\n");
usleep(100);
pthread_mutex_unlock(&mutex);
}
int main(int argc,char** argv){
pthread_t id1;
pthread_t id2;
pthread_mutex_init(&mutex,NULL);
pthread_create(&id2,NULL,(void *)print_msg2,NULL);
pthread_create(&id1,NULL,(void *)print_msg1,NULL);
sleep(1);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
return 1;
}
5、SIM900B GPRS模块 打电话、发短信、接电话
假如说,你能了解上面我所介绍的内容,那么看懂这个代码并不是上面难事,而且,还能再我代码的基础上进行改善
/*引进编译所需要的头文件
#include <termios.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
pthread_mutex_t mutex ; //定义一个互斥锁
int choice=0; //定义全局变量
//存储短信信息的结构体
struct message_info
{
char phnum[16];
char message[128];
};
//用来接电话的线程
void* thread_get_RING(void* arg)
{
char reply[100];
int fd;
fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);
if (-1 == fd)
{
perror("Can't Open Serial Port");
}
//一直检测串口是否有RING 这个字符串 PS:如果向GPRS模块打电话,串口回返回RING 字符串
while(1)
{
memset(reply,0,sizeof(reply));
read(fd,reply,sizeof(reply));//读取串口信息
if(strstr(reply,"RING"))
{
printf("=====================================================\n");
printf(" you have a call in,please input :\n");
printf(" 4 to connect\n");
printf(" 5 to hang up\n");
printf("=====================================================\n");
pthread_mutex_lock(&mutex);//上锁
switch(choice)
{
case 4: get_up_phone(fd) ;break;
case 5: hang_up_phone(fd) ; break;
}
pthread_mutex_unlock(&mutex);//解锁
}
}
}
//初始化串口函数
void serial_init(int fd)
{
struct termios options;
tcgetattr(fd, &options);
options.c_cflag |= ( CLOCAL | CREAD );
options.c_cflag &= ~CSIZE;
options.c_cflag &= ~CRTSCTS;
options.c_cflag |= CS8;
options.c_cflag &= ~CSTOPB;
options.c_iflag |= IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
tcsetattr(fd,TCSANOW,&options);
}
//挂断电话的函数
int hang_up_phone(int fd)
{
getchar(); //PS:吃掉缓冲区中的回车符
char buff[10];
char reply[20];
int nwrite;
int nread;
memset(buff,0,sizeof(buff));
memset(reply,0,sizeof(reply));
strcat(buff,"ath\r");
nwrite=write(fd,buff,sizeof(buff)); //向串口发送AT命令
printf("nwrite=%d,%s,\n",nwrite,buff);
sleep(1);
nread=read(fd,reply,sizeof(reply)); //读取返回值
printf("nread=%d,%s\n",nread,reply); //打印返回值的信息
}
//接电话的函数
int get_up_phone(int fd)
{
getchar();
char buff[10];
char reply[20];
int nwrite;
int nread;
memset(buff,0,sizeof(buff));
memset(reply,0,sizeof(reply));
strcat(buff,"ata\r");
nwrite=write(fd,buff,sizeof(buff));
printf("nwrite=%d,%s,\n",nwrite,buff);
sleep(1);
nread=read(fd,reply,sizeof(reply));
printf("nread=%d,%s\n",nread,reply);
}
//获取用户所拨打的手机号函数
int get_phonenumber(int fd,char *phnum)
{
getchar();
int counter=0;
char phonenum[20]={'\0'};
printf("please enter the phone number who do you want to call:\n");
gets(phnum);
while(strlen(phnum) != 12)
{
if(counter >= 3)
{
printf("conter out !\n");
return -1;
}
printf("number shuld be --11-- bits ! enter agin :\n");
gets(phnum);
counter ++;
}
strcat(phonenum,"atd");
strcat(phonenum,phnum);
strcat(phonenum,";\r");
called(fd,phonenum);
}
//拨打电话的函数
int called(int fd,char *atd)
{
int nread,nwrite;
char buff[100];
char reply[100];
char hang_up;
memset(buff,0,sizeof(buff));
memset(reply,0,sizeof(reply));
strcpy(buff,"at\r");
nwrite=write(fd,buff,strlen(buff));
printf("nwrite=%d,%s\n",nwrite,buff);
sleep(1);
nread=read(fd,reply,strlen(reply));
printf("reply=%d,%s\n",nread,reply);
memset(buff,0,sizeof(buff));
memset(reply,0,sizeof(reply));
strcat(buff,atd);
nwrite=write(fd,buff,sizeof(buff));
printf("nwrite=%d,%s\n",nwrite,buff);
sleep(1);
nread=read(fd,reply,sizeof(reply));
printf("nread=%d,%s\n",nread,reply);
printf("==================================\n");
printf("if you want to hang up this dialog,\n please enter 2\n");
printf("==================================\n");
hang_up=getchar();
if(hang_up=='2')
{
hang_up_phone(fd);
}
}
//发送信息的函数
int send(int fd,char *cmgf,char *cmgs,char *message)
{
int nread,nwrite;
char buff[128];
char reply[128];
memset(buff,0,sizeof(buff));
strcpy(buff,"at\r");
nwrite = write(fd,buff,strlen(buff));
printf("nwrite=%d,%s\n",nwrite,buff);
memset(reply,0,sizeof(reply));
sleep(1);
nread = read(fd,reply,sizeof(reply));
printf("nread=%d,%s\n",nread,reply);
memset(buff,0,sizeof(buff));
strcpy(buff,"AT+CMGF=");
strcat(buff,cmgf);
strcat(buff,"\r");
nwrite = write(fd,buff,strlen(buff));
printf("nwrite=%d,%s\n",nwrite,buff);
memset(reply,0,sizeof(reply));
sleep(1);
nread = read(fd,reply,sizeof(reply));
printf("nread=%d,%s\n",nread,reply);
memset(buff,0,sizeof(buff));
strcpy(buff,"AT+CMGS=");
strcat(buff,cmgs);
strcat(buff,"\r");
nwrite = write(fd,buff,strlen(buff));
printf("nwrite=%d,%s\n",nwrite,buff);
memset(reply,0,sizeof(reply));
sleep(1);
nread = read(fd,reply,sizeof(reply));
printf("nread=%d,%s\n",nread,reply);
memset(buff,0,sizeof(buff));
strcpy(buff,message);
nwrite = write(fd,buff,strlen(buff));
printf("nwrite=%d,%s\n",nwrite,buff);
memset(reply,0,sizeof(reply));
sleep(1);
nread = read(fd,reply,sizeof(reply));
printf("nread=%d,%s\n",nread,reply);
}
//获取用户所输入的短信收件人
int send_en_message(int fd,struct message_info info)
{
getchar();
char cmgf[] = "1";
int conter = 0;
char cmgs[16] = {'\0'};
printf("enter recever phnumber :\n");
fgets(info.phnum,15,stdin);
while(strlen(info.phnum) != 12)
{
if(conter >= 3)
{
printf("conter out !\n");
return -1;
}
printf("number shuld be --11-- bits ! enter agin :\n");
fgets(info.phnum,11,stdin);
conter ++;
}
printf("enter you message !\n");
fgets(info.message,128,stdin);
strcat(info.message,"\x1a");
strcat(cmgs,"\"");
strcat(cmgs,info.phnum);
strcat(cmgs,"\"");
send(fd,cmgf,cmgs,info.message);
}
int main()
{
int ret;
int fd;
char phnum[15];
struct message_info info;
pthread_t id;
pthread_mutex_init(&mutex,NULL); //初始化互斥锁
fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY); //打开串口设备
if (-1 == fd)
{
perror("Can't Open Serial Port");
}
serial_init(fd); //初始化串口
ret=pthread_create(&id,NULL,(void *)thread_get_RING,NULL);//创建线程
usleep(100);
if(ret!=0)
{
printf("create thread failed!!\n");
exit (1);
}
printf("\n============================================\n");
printf("\tthis is a gprs test program !\n");
printf("\tcopyright fj@farsight 2016\n");
printf("============================================\n");
printf("enter your selete :\n");
printf("1.send english message.\n");
printf("2.call to someone. \n");
printf("3.exit.\n");
while(1)
{
pthread_mutex_lock(&mutex); //上锁
scanf("%d",&choice);
switch(choice)
{
case 1: send_en_message(fd,info); break;
case 2: get_phonenumber(fd,phnum); break;
case 3: printf("Quite!!\n"); break;
default : printf("Quite!!\n"); break;
}
pthread_mutex_unlock(&mutex);//解锁
sleep(1);
printf("\n============================================\n");
printf("\tthis is a gprs test program !\n");
printf("\tcopyright for huangan 2016\n");
printf("============================================\n");
printf("enter your selete :\n");
printf("1.send english message.\n");
printf("2.call to someone. \n");
printf("3.exit.\n");
}
pthread_join(id,NULL); //等待线程的结束
pthread_mutex_destroy(&mutex); //毁掉互斥锁
close(fd); //关闭设备
return 0;
}
6、总结
这个模块我玩了十天,在这个过程中我学到了很多东西,但是遇到的问题也是很多的,比如刚开始都不知道线程的工作原理,也不知道如何创建线程,这是最难的,但是经过各种百度,请教同学,终于是把这个模块完成,后面还有PPP拨号上网,这个将在后面介绍