实现了收发短信,并且支持字符短信和PDU格式短信,支持电话拨打与接听
长期工作稳定
//SIM900.C
/*************************************************************************************************************
* 文件名: SIM900.c
* 功能: STM32 SIM900底层驱动函数
* 作者: cp1300@139.com
* 创建时间: 2013-10-16
* 最后修改时间: 2013-10-16
* 详细: GSM_CDMA发送短信等
2014-04-22:添加字节超时与总超时
*************************************************************************************************************/
#include "system.h"
#include "usart.h"
#include "SIM900.h"
#include "delay.h"
#include "string.h"
#include "ucos_ii.h"
#include "unicode_gbk.h"
#include "main.h"
//SIM900通信缓冲区
u8 SIM900_Buff[SIM900_BUFF_SIZE]; //缓冲区
//调试开关
#define SIM900_DBUG 0
#if SIM900_DBUG
#include "system.h"
#define SIM900_debug(format,...) uart_printf(format,##__VA_ARGS__)
#else
#define SIM900_debug(format,...) /\
/
#endif //SIM900_DBUG
//所有短信接收缓冲区
//#define PDU_BUFF_SIZE 1024*20 //20KB 可以一次读取50条未读短信
u8 SmsPduBuff[PDU_BUFF_SIZE]; //PDU数据缓冲区
static u8 PhoneNumtoPDUChar(u8 *pNum, char *pChar,u8 NumLen); //将电话号码字符转换为PDU要求的字符
static u8 ChartoPhoneNum(char *pChar, char *pNum, u8 CharLen); //将字符转换为电话号码
static u32 StringToUnicodeStr(char *pStr, char *pucode,u32 SrtLen); //将字符串转换为unicode,并存储为16进制样式的字符串
static u32 UnicodeStrToString(u8 *pucode,char *pStr,u32 SrtLen); //将字符unicode转换为字符串
static u32 GSM_StringToHex(char *pStr, u8 NumDigits); //将16进制样式字符串转换为16进制整型数(必须保证字符串字母都是大写)
static void GSM_HexToString(u32 HexNum,char *pStr, u8 NumDigits); //将整型数字转换为16进制样式字符串(字母为大写,不带结束符)
static int gsmDecode7bit(const u8* pSrc, char* pDst, int nSrcLength);//7bit编码解码
static int gsmEncode7bit(const char* pSrc,u8* pDst);
static u16 GSM_GetU2SCharOffset(char *pBuff,u16 CharNum);
static PHONE_NUMBER SMSServeNumber; //全局短信中心号码
/*************************************************************************************************************************
* 函数 : void SIM900_SetSMSServeNumber(char *pSMSServeNumber,u8 NumLen)
* 功能 : 设置全局短信中心号码
* 参数 : pSMSServeNumber:短信中心号码,NumLen:短信中心号码长度
* 返回 : 无
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-25
* 最后修改时间 : 2013-10-25
* 说明 : 用于发送短信的时候进行调用
*************************************************************************************************************************/
void SIM900_SetSMSServeNumber(char *pSMSServeNumber,u8 NumLen)
{
u8 i;
if(NumLen > PHONE_NUMBER_MAX_LEN) NumLen = PHONE_NUMBER_MAX_LEN; //限制电话号码长度
for(i = 0;i < NumLen;i ++)
{
SMSServeNumber.PhoneNumBuff[i] = pSMSServeNumber[i];
}
SMSServeNumber.PhoneNumLen = NumLen;
SMSServeNumber.PhoneNumBuff[SMSServeNumber.PhoneNumLen] = '\0'; //添加结束符
SIM900_debug("设置短信中心号码为:%s\r\n",SMSServeNumber.PhoneNumBuff);
}
/*************************************************************************************************************************
* 函数 : bool GSM_CheckNotASCII(char *pBuff,u16 Len)
* 功能 : 检查字符串中是否含有非ASCII编码
* 参数 : pBuff:字符串缓冲区;Len:长度
* 返回 : FALSE:字符串全部为ASCII编码;TRUE:字符串含有非ASCII编码,一般为汉字编码
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-25
* 最后修改时间 : 2013-10-25
* 说明 : 用于选择发送短信的模式,选择U2S或者7BIT编码
*************************************************************************************************************************/
bool GSM_CheckNotASCII(char *pBuff,u16 Len)
{
u16 i;
for(i = 0;i < Len;i ++)
{
if(pBuff[i] >= 0x80)
return TRUE;
}
return FALSE;
}
/*************************************************************************************************************************
* 函数 : static u16 GSM_GetU2SCharOffset(char *pBuff,u16 CharNum)
* 功能 : 计算指定字符的偏移位置
* 参数 : pBuff:字符串缓冲区;
CharNum:字符偏移
* 返回 : 字符串大小
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-25
* 最后修改时间 : 2013-10-25
* 说明 : 计算指定数量的字符(不分中英文)的大小,比如PDU,U2S模式下,短信只能有70个字符,但是不分中英文
此时英文只占用一个字节,但是中文占用2个字节
*************************************************************************************************************************/
static u16 GSM_GetU2SCharOffset(char *pBuff,u16 CharNum)
{
u16 i;
u16 cnt = 0;
for(i = 0;i < CharNum;)
{
if(pBuff[i] >= 0x80) //中文
{
cnt +=2;
i +=2;
}
else if(pBuff[i] == 0) //字符串结束
{
break;
}
else //ASCII
{
cnt += 1;
i ++;
}
}
return cnt;
}
/*************************************************************************************************************************
* 函数 : bool SIM900_WaitSleep(void)
* 功能 : 等待GSM模块空闲,并重新唤醒
* 参数 : TimeOut:等待超时,时间单位ms
* 返回 : TRUE:成功;FALSE:超时
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-25
* 最后修改时间 : 2013-10-25
* 说明 : 用于等待操作完成,防止快速操作造成模块不响应
*************************************************************************************************************************/
bool SIM900_WaitSleep(u32 TimeOut)
{
u32 i;
u32 cnt;
TimeOut /= 100;
TimeOut +=1;
SIM900_SetDTR(1); //等待模块空闲后进入SLEEP模式
for(i = 0;i < TimeOut;i ++)
{
GSM_Delay100MS(); //延时100ms
SIM900_SendATcom("AT"); //发送"AT",同步波特率,并且等待应答
if(AT_RETURN_TIME_OUT == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 15)) //等待响应,超时150ms
{
break;
}
}
SIM900_SetDTR(0); //唤醒
if(i == TimeOut)
{
SIM900_debug("模块进入空闲模式失败!\r\n");
return FALSE;
}
GSM_Delay100MS(); //延时100ms
SIM900_debug("模块进入空闲模式成功!\r\n");
SIM900_TestAT(10);
return TRUE;
}
/*************************************************************************************************************************
*函数 : bool GSM_SendSMS(char *pSMS, char *pPhoneNumber)
*功能 : 发送一条短信
*参数 : pSMS:短信内容缓冲区指针,内容为文本文档,并且字符串需要结束符
pPhoneNumber:目标电话号码
*返回 : TRUE:短信发送成功;FALSE:短信发送失败
*依赖 : 底层
*作者 : cp1300@139.com
*时间 : 2013-10-25
*最后修改时间 : 2013-10-25
*说明 : 需要先调用SIM900_SetSMSServeNumber()设置短信中心号码
需要使用全局的PDU数据缓冲区
一定要添加结束符
当短信长度超过单条短信长度限制后会发送多条短信
*************************************************************************************************************************/
#define SMS_MAX_LEN 2048 //短信最大长度
bool GSM_SendSMS(char *pSMS, char *pPhoneNumber)
{
char SMSBuff[160+1]; //短信最大160B,加上一个结束符
u8 PDUBuff[512]; //短信PDU数据缓冲区
u16 SMSLen; //短信长度
u16 SMSOffset; //短信发送偏移位置,用于发送多条短信
u16 i,j;
SMSLen = strlen(pSMS); //获取要发送的短信长度
if(SMSLen > SMS_MAX_LEN) SMSLen = SMS_MAX_LEN; //限制短信最大长度,防止无限发送
if(strlen(SMSServeNumber.PhoneNumBuff) == 0)
{
SIM900_debug("由于短信中心号码设置失败,导致短信无法发送!\r\n");
return FALSE;
}
SMSOffset = 0; //起始偏移为0
while(1)
{
if((SMSLen-SMSOffset) > 160)
j = 160;
else
j = SMSLen-SMSOffset;
for(i = 0;i < j;i ++)
{
SMSBuff[i] = pSMS[SMSOffset + i]; //复制短信到发送缓冲区
}
SMSBuff[j] = 0; //添加结束符
if(GSM_CheckNotASCII(SMSBuff,j) == TRUE) //分割的短信中含有非ASCII编码,那么只能使用U2S编码,只能发送70个字符(包括中英文)
{
SMSOffset += GSM_GetU2SCharOffset(SMSBuff,70); //第一条短信限制70个字符,返回下一条分割的起始位置
SMSBuff[SMSOffset] = 0;
}
else
{
SMSOffset += j; //下一条分割的起始位置
SMSBuff[SMSOffset] = 0;
}
//SIM900_WaitSleep(1000); //等待上一个操作完成
if(GSM_SendOneSMS(SMSBuff, PDUBuff, SMSServeNumber.PhoneNumBuff, pPhoneNumber) == TRUE)
{
SIM900_debug("发送短信成功!\r\n");
}
else
{
SIM900_debug("发送短信失败!\r\n");
return FALSE;
}
if(SMSOffset >= SMSLen) break; //短信发送完成,退出
}
return TRUE;
}
/*************************************************************************************************************************
* 函数 : void SIM900_HardwareInit(void)
* 功能 : 初始化SIM900相关的硬件
* 参数 : 无
* 返回 : 无
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-16
* 最后修改时间 : 2013-10-16
* 说明 : 主要初始化与SIM900相关的STM32 IO 以及 UART
*************************************************************************************************************************/
void SIM900_HardwareInit(void)
{
SIM900_UartInit(); //初始化串口
SIM900_SetRxBuff(SIM900_Buff, SIM900_BUFF_SIZE); //设置通信缓冲区
//初始化RI,用于指示新短信或者电话
DeviceClockEnable(DEV_GPIOB,ENABLE); //使能GPIOB时钟
GPIOx_Init(GPIOB,BIT14, IN_IPU, IN_IN); //上拉输入
GPIOx_Init(GPIOB,BIT12|BIT13|BIT15, OUT_PP, SPEED_10M); //推挽输出
SIM900_SetDTR(0); //取消SLEEP
SIM900_SetRESET(1); //复位无效
SIM900_SetPWR(1); //上电无效
}
/*************************************************************************************************************************
* 函数 : void SIM900_HardwarePowerUP(void)
* 功能 : SIM900硬件开机
* 参数 : 无
* 返回 : 无
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-29
* 最后修改时间 : 2013-10-29
* 说明 : 用于SIM900模块开机,拉低PWR
*************************************************************************************************************************/
void SIM900_HardwarePowerUP(void)
{
SIM900_SetPWR(1); //恢复高电平
GSM_DelayMS(200);
SIM900_SetPWR(0); //拉低750ms开机
GSM_DelayMS(750);
GSM_Delay100MS();
SIM900_SetPWR(1); //恢复高电平
GSM_DelaySer(3); //延时3S等待开机完毕
}
/*************************************************************************************************************************
* 函数 : void SIM900_HardwarePowerDOWN(void)
* 功能 : SIM900硬件关机
* 参数 : 无
* 返回 : 无
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-29
* 最后修改时间 : 2013-10-29
* 说明 : 用于SIM900模块关机机,拉低PWR大于1S小于5S
*************************************************************************************************************************/
void SIM900_HardwarePowerDOWN(void)
{
SIM900_SetPWR(1); //恢复高电平
GSM_DelayMS(200);
SIM900_SetPWR(0); //拉低1500ms关机
GSM_DelaySer(1);
GSM_DelayMS(500);
SIM900_SetPWR(1); //恢复高电平
GSM_DelaySer(2); //延时2S等待注销网络
}
/*************************************************************************************************************************
* 函数 : void SIM900_HardwareReset(void)
* 功能 : SIM900硬件复位
* 参数 : 无
* 返回 : 无
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-29
* 最后修改时间 : 2013-10-29
* 说明 : 用于SIM900模块硬件复位
*************************************************************************************************************************/
void SIM900_HardwareReset(void)
{
SIM900_SetRESET(1); //恢复高电平
GSM_Delay100MS();
SIM900_SetRESET(0); //拉低100mS复位
GSM_Delay100MS();
SIM900_SetRESET(1); //恢复高电平
GSM_DelaySer(2); //延时2S
}
/*************************************************************************************************************************
* 函数 : bool SIM900_ModuleInit(void)
* 功能 : 初始化SIM900模块
* 参数 : 无
* 返回 : FALSE:初始化失败;TRUE:初始化成功
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-16
* 最后修改时间 : 2013-10-16
* 说明 : 主要初始化与SIM900配置,以及初始化网络
*************************************************************************************************************************/
bool SIM900_ModuleInit(void)
{
u32 cnt;
u8 retry = 5; //重试次数
//检测模块存在
retry = 5; //重试次数
do
{
SIM900_SendATcom("AT"); //发送"AT",同步波特率,并且等待应答
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 10)) //等待响应,超时100ms
{
break;
}
retry --;
}while(retry);
if(retry == 0) return FALSE;
//设置关闭回显
retry = SIM900_RETRY; //重试次数
do
{
SIM900_SendATcom("ATE 0"); //发送"ATE",关闭回显模式
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 10)) //等待响应,超时100ms
{
SIM900_debug("\r\n关闭AT回显模式成功!\r\n");
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0)
{
SIM900_debug("\r\n关闭AT回显模式失败!\r\n");
return FALSE;
}
//设置短消息格式为PDU格式
retry = SIM900_RETRY; //重试次数
do
{
SIM900_SendATcom("AT+CMGF=0"); //发送"AT+CMGF",设置短消息格式为PDU格式
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 10)) //等待响应,超时100ms
{
SIM900_debug("\r\n设置短消息格式为PDU格式成功!\r\n");
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0)
{
SIM900_debug("\r\n设置短消息格式为PDU格式失败!\r\n");
return FALSE;
}
//使能RI引脚提示
retry = SIM900_RETRY; //重试次数
do
{
SIM900_SendATcom("AT+CFGRI=1"); //发送"AT+CFGRI",启动RI引脚提示
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 10)) //等待响应,超时100ms
{
SIM900_debug("\r\n启动RI引脚提示成功!\r\n");
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0)
{
SIM900_debug("\r\n启动RI引脚提示失败!\r\n");
return FALSE;
}
//设置模块sleep模式使能
retry = SIM900_RETRY; //重试次数
do
{
SIM900_SendATcom("AT+CSCLK=1"); //发送"AT+CSCLK",启动SLEEP模式
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 10)) //等待响应,超时100ms
{
SIM900_debug("\r\n设置SLEEP成功!\r\n");
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0)
{
SIM900_debug("\r\n设置SLEEP失败!\r\n");
return FALSE;
}
SIM900_SetDTR(1); //使能SLEEP模式
return TRUE;
}
/*************************************************************************************************************************
* 函数 : bool SIM900_TestAT(u32 retry)
* 功能 : SIM900 AT 命令通信测试
* 参数 : retry:重试次数
* 返回 : FALSE:通信失败;TRUE:通信成功
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-20
* 最后修改时间 : 2013-10-20
* 说明 : 每隔20ms向SIM900发送一个"AT",等待响应返回
*************************************************************************************************************************/
bool SIM900_TestAT(u32 retry)
{
u32 cnt;
//检测模块存在
do
{
SIM900_SendATcom("AT"); //发送"AT",同步波特率,并且等待应答
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 15)) //等待响应,超时150ms
{
return TRUE;
}
retry --;
}while(retry);
return FALSE;
}
/*************************************************************************************************************************
* 函数 : SIM900_ERROR SIM900_GetATResp(u8 *pRxBuff, u32 *pLen, const char *pKeyword, u32 ByteTime, u32 TimeOut)
* 功能 : 获取SIM900的AT指令响应
* 参数 : pRxBuff:接收缓冲区指针(输入);pLen:接收到的数据大小(输出),
pKeyword:关键字,为字符串,比如"OK",如果在接收到的字符串中有OK字符,就返回成功,否则失败(输入)
ByteTime:字节超时时间,单位ms最大999ms
TimeOut:等待超时时间,单位字节超时时间
* 返回 : SIM900_ERROR
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2013-10-16
* 最后修改时间 : 2014-04-22
* 说明 : 本函数会在接收缓冲区字符串结束添加'\0'
2014-04-22:添加字节超时与总超时
*************************************************************************************************************************/
SIM900_ERROR SIM900_GetATResp(u8 *pRxBuff, u32 *pLen, const char *pKeyword, u32 ByteTime, u32 TimeOut)
{
u32 cnt1, cnt2=0; //接收数据计数器
u32 timeCnt = TimeOut;
if(ByteTime > 999)ByteTime = 999;
do
{
cnt1 = cnt2;
GSM_DelayMS(ByteTime); //延时字节超时
cnt2 = SIM900_GetRxCnt(); //获取接收数据计数器
if(cnt1 == cnt2) //完成接收数据了,退出等待
{
timeCnt --;
if((cnt1 > 0)&&(timeCnt!=0)) timeCnt=1; //数据接收完毕,退出
}
else
{
timeCnt = TimeOut;
}
}while(timeCnt);
//等待超时
if(cnt2 == 0)
{
SIM900_debug("\r\nAT指令返回超时\r\n");
return AT_RETURN_TIME_OUT; //返回超时错误
}
//数据接收完毕
*pLen = cnt2; //返回接收数据长度
pRxBuff[cnt2] = '\0'; //将数据结尾添加结束字符串
SIM900_debug("%s\r\n",pRxBuff); //打印返回信息
if(strstr((const char*)pRxBuff, pKeyword) != NULL) //搜索关键字
{
SIM900_debug("%s 返回成功!\r\n",pKeyword);
return AT_RETURN_OK;
}
else if(strstr((const char*)pRxBuff, "ERROR") != NULL)
{
SIM900_debug("%s 返回错误!\r\n",pKeyword);
return AT_RETURN_ERROR;
}
else
{
SIM900_debug("%s 返回未知!\r\n",pKeyword);
return AT_RETURN_UNKNOWN;
}
}
/*************************************************************************************************************************
* 函数 : int SIM900_GetSmsNum(void)
* 功能 : 获取SIM卡存储的短信数量
* 参数 : 无
* 返回 : <0:错误,其它:短信数量
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-20
* 最后修改时间 : 2013-10-20
* 说明 : 无
*************************************************************************************************************************/
int SIM900_GetSmsNum(void)
{
u8 n;
u32 cnt;
char *p;
u8 retry = SIM900_RETRY; //重试次数
do
{
SIM900_SendATcom("AT+CPMS?"); //发送"AT+CPMS",获取短信数量
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0) return -1; //超时
p = strstr((const char*)SIM900_Buff, "\"SM\""); //搜索字符""SM""
if(p != NULL)
{
if(p[6] != ',') n = 2; //短信数量有可能是1位数,也有可能是2位数,通过判断后面是否为','
else n = 1;
return GSM_StringToDec(p + 5, n); //跳过前面的5字节,""SM",",并获取存储的短信数量
}
return -1; //错误
}
/*************************************************************************************************************************
* 函数 : bool SIM900_DelMultiSMS(SIM900_DEL DelStatus)
* 功能 : SIM900批量删除短信
* 参数 : SIM900_DEL
* 返回 : TRUE:成功;FALSE:失败;
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-17
* 最后修改时间 : 2013-10-20
* 说明 : 批量删除的时候可能会很慢
*************************************************************************************************************************/
bool SIM900_DelMultiSMS(SIM900_DEL DelStatus)
{
u32 cnt;
u8 retry = SIM900_RETRY; //重试次数
do
{
switch(DelStatus)
{
case DEL_READ_SMS: //删除所有已读短信
SIM900_SendATcom("AT+CMGDA=1"); break;
case DEL_UNREAD_SMS: //删除所有未读短信
SIM900_SendATcom("AT+CMGDA=2"); break;
case DEL_SENT_SMS: //删除所有已经发送的短信
SIM900_SendATcom("AT+CMGDA=3"); break;
case DEL_UNSENT_SMS: //删除所有未发送短信
SIM900_SendATcom("AT+CMGDA=4"); break;
case DEL_INBOX_SMS: //删除所有接收短信
SIM900_SendATcom("AT+CMGDA=5"); break;
case DEL_ALL_SMS: //删除所有短信
SIM900_SendATcom("AT+CMGDA=6"); break;
default: return FALSE;
}
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 200))//等待响应,超时2S
{
return TRUE;
}
SIM900_Ready(); //等待就绪
}while(retry --);
return FALSE;
}
/*************************************************************************************************************************
* 函数 : SIM900_ERROR SIM900_GetUnreadSMS(u8 *pUnreadSMSBuff, u32 BuffSize, u32 *pPDUCnt)
* 功能 : 读取SIM900所有的未读短信
* 参数 : pUnreadSMSBuff:未读短信PDU数据缓冲区指针,BuffSize:缓冲区大小,pPDUCnt:PDU数据大小
* 返回 : SIM900_ERROR
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-17
* 最后修改时间 : 2013-10-17
* 说明 : 短信最大存储数量为50条
缓冲区必须足够大,做好最坏打算,有50条未读短信,如果缓冲区不够大,会发送溢出
溢出后虽然不会造成系统错误,但是会覆盖前面的未读短信数据.
*************************************************************************************************************************/
SIM900_ERROR SIM900_GetUnreadSMS(u8 *pUnreadSMSBuff, u32 BuffSize, u32 *pPDUCnt)
{
SIM900_ERROR error;
u8 retry = SIM900_RETRY; //重试次数
SIM900_SetRxBuff(pUnreadSMSBuff, BuffSize); //重新设置接收缓冲区
do
{
SIM900_SendATcom("AT+CMGL=0"); //发送"AT+CMGL",读取所有的未读短息
error = SIM900_GetATResp(pUnreadSMSBuff, pPDUCnt, "OK", 250, 4); //等待响应,超时1000ms
if(error == AT_RETURN_OK)
break;
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
SIM900_SetRxBuff(SIM900_Buff, SIM900_BUFF_SIZE); //恢复默认缓冲区
return error;
}
/*************************************************************************************************************************
*函数 : bool GSM_ParsePDUSMS(char *pPDU,char *pSMS,u32 len,SMS_INFO *pInfo)
*功能 : 解析一条PDU格式短信
*参数 : pPDU:短信PDU数据缓冲区指针
* pSMS:解析后的短信缓冲区指针
* pInfo:短信信息指针
*返回 : TRUE:成功;FALSE:失败
*依赖 : void UnicodeToGBK(u16 *pUnicode, u16 *pGBK, u32 cnt);
*作者 : cp1300@139.com
*时间 : 2013-04-04
*最后修改时间 : 2013-05-01
*说明 : 无
*************************************************************************************************************************/
bool GSM_ParsePDUSMS(char *pPDU, char *pSMS, u32 PDUSize, SMS_INFO *pInfo)
{
u16 cnt = 0;
u16 temp;
char *p;
u16 SMS_Size;
p = strstr((const char*)pPDU, "+CMGR:");
if(p == NULL)
{
SIM900_debug("短信中没有搜索到\"+CMGR:\"\r\n");
p = strstr((const char*)pPDU, "+CMGL:");
if(p == NULL)
{
SIM900_debug("短信中没有搜索到\"+CMGL:\"\r\n");
return FALSE;
}
}
//提取短信的编号 //+CMGR: 1,"",34
if(p[8] != ',') pInfo->IndexNum = GSM_StringToDec(p + 7, 2); //短信索引可能是1位数,也有可能是2位数,通过判断后面是否为','
else pInfo->IndexNum = GSM_StringToDec(p + 7, 1);;
p = strstr((const char*)p, "\r\n"); //寻找短信PDU开始位置
cnt = ((u32)p - (u32)pPDU) + 2; //找到短信PDU开始的位置了
if(p == NULL || cnt >= PDUSize)
{
pInfo->SMS_Size = 0;
SIM900_debug("短信解析错误!\r\n");
return FALSE;
}
//获取短信中心号码长度
temp = GSM_StringToHex(&pPDU[cnt], 2); //将16进制样式字符串转换为整型数
cnt += 2; //跳过前面的短信中心号码长度字节
cnt += temp*2; //跳过前面的短信中心信息
//解析PDU数据 RT UDHI SRI - - MMS MTI MTI //UDHI为1,代表用户数据有头部信息,用于标识短信拆分信息
pInfo->PDU = GSM_StringToHex(&pPDU[cnt], 2); //将16进制样式字符串转换为整型数//PDU数据
cnt += 2; //跳过PDU头数据字节
//计算发送短信的号码的长度
temp = GSM_StringToHex(&pPDU[cnt], 2); //将16进制样式字符串转换为整型数
cnt += 2; //跳过电话号码长度字节
cnt += 2; //跳过地址类型,常为"91",一字节
pInfo->NumLen = ChartoPhoneNum((char *)&pPDU[cnt], (char *)&(pInfo->NumBuff[0]), (temp > SMS_NUM_LEN_MAX - 2) ? (SMS_NUM_LEN_MAX - 2) : temp); //转换发送号码
pInfo->NumBuff[pInfo->NumLen] = 0; //结束符
//lcd_printf("pInfo->NumLen=%d\r\n",pInfo->NumLen);
//uart_printf("%s\r\n",pInfo->NumBuff);
cnt += (temp%2) ? (temp+1) : temp; //跳过发送号码长度的字节数
cnt+=2; //跳过PID,2B
pInfo->DSC = GSM_StringToHex(&pPDU[cnt], 2); //获取DSC信息
cnt+=2; //跳过DSC,2B
//cnt+=2; //跳过VP,2B //没有这个标志
//cnt+=2; //跳过UDL,2B//没有这个标志
pInfo->Timer.Year = (pPDU[cnt + 1] - '0') * 10 + (pPDU[cnt] - '0'); cnt += 2; //年
pInfo->Timer.Month = (pPDU[cnt + 1] - '0') * 10 + (pPDU[cnt] - '0'); cnt += 2; //年
pInfo->Timer.Day = (pPDU[cnt + 1] - '0') * 10 + (pPDU[cnt] - '0'); cnt += 2; //年
pInfo->Timer.Hour = (pPDU[cnt + 1] - '0') * 10 + (pPDU[cnt] - '0'); cnt += 2; //年
pInfo->Timer.Minute = (pPDU[cnt + 1] - '0') * 10 + (pPDU[cnt] - '0'); cnt += 2; //年
pInfo->Timer.Second = (pPDU[cnt + 1] - '0') * 10 + (pPDU[cnt] - '0'); cnt += 2; //年
cnt += 2; //跳过时差2字节
SMS_Size = GSM_StringToHex(&pPDU[cnt], 2); //计算短信字符数量,不管英文,中文都算一个字符
SIM900_debug("SMS_Size = GSM_StringToHex(&pPDU[cnt], 2) = %d\r\n",SMS_Size);
cnt += 2; //跳过短信长度字节,2B
if(pInfo->PDU & 0x40) //用户数据有头部信息,标识短信已经被分割为几条
{
cnt += 8; //跳过前面8个数据,只要后面的4个,标识
SMS_Size -= 12; //短信长度减去偏移
pInfo->AllNum = GSM_StringToHex(&pPDU[cnt], 2);//计算总分割数
cnt += 2; //跳过2B的总数
pInfo->PreNum = GSM_StringToHex(&pPDU[cnt], 2);//计算当前位置
cnt += 2; //跳过2B的当前位置
SIM900_debug("短信分割:%d/%d\r\n",pInfo->AllNum, pInfo->PreNum);
}
else
{
pInfo->AllNum = pInfo->PreNum = 0; //短信没有被分割
}
//DCS 00:7BIT编码;08:UCS2编码;04:8bit编码
switch((pInfo->DSC) & 0x0f)
{
case 0x00: //7bit编码
{
SIM900_debug("短信为7bit编码(TEXT格式)\r\n");
pInfo->SMS_Size = (SMS_Size > 160) ? 160 : SMS_Size; //短信大小
pInfo->TEXT_MODE = 1;
SMS_Size = (SMS_Size * 7 / 8) + (((SMS_Size * 7) % 8) ? 1 : 0);//计算短信占用空间大小
pPDU += cnt;
for(temp = 0;temp < SMS_Size;temp ++) //将PDU数据转换为16进制数据
{
pPDU[temp] = GSM_StringToHex(&pPDU[temp << 1], 2); //1B数据转换为PDU格式后占用2B
}
gsmDecode7bit((u8 *)pPDU, (char *)pSMS, SMS_Size); //7bit->8bit,数据长度会发生变化
//SIM900_debug("SMS:%s\r\n",pSMS);
}break;
case 0x04: //8bit编码
{
SIM900_debug("短信为8bit编码(不支持)\r\n");
return FALSE;
}
case 0x08: //UCS2编码
{
SIM900_debug("短信为UCS2编码(PDU格式)\r\n");
pInfo->TEXT_MODE = 0;
SMS_Size = (SMS_Size > 140) ? 140 : SMS_Size; //短信字符限制为140B
//UNICODE PDU转换为字符串 --> GBK,返回短信大小,每个短信字符占用2字节,每个字节转换为PDU后占用2B
pInfo->SMS_Size = UnicodeStrToString((u8 *)pPDU+cnt,(char *)pSMS,SMS_Size<<1);
}break;
default:SIM900_debug("未知短信编码格式!\r\n");return FALSE;
}
pSMS[pInfo->SMS_Size] = '\0'; //添加结束符
return TRUE;
}
/*************************************************************************************************************************
*函数 : SIM900_ERROR SIM900_ReadTextSMS(char *pSMS, SMS_INFO *pInfo, u16 IndexNum)
*功能 : 读取一条TEXT格式短信
*参数 : pSMS:解析后的短信存放位置指针,注意存放的最大大小由_MAX_SMS_SIZE决定
* pInfo:短信信息指针
* IndexNum:短信索引号
*返回 : GSM_ERROR:状态
*依赖 : 短信读取与解析
*作者 : cp1300@139.com
*时间 : 20130408
*最后修改时间 : 20130408
*说明 :
*************************************************************************************************************************/
SIM900_ERROR SIM900_ReadTextSMS(char *pSMS, SMS_INFO *pInfo, u16 IndexNum)
{
SIM900_ERROR error;
u32 cnt;
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
//配置短信为TEXT格式
SIM900_SendATcom("AT+CMGF=1");
if(SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20) == AT_RETURN_OK)
{
sprintf((char *)SIM900_Buff, "AT+CMGR=%d", IndexNum); //写入索引号
SIM900_SendATcom((char *)SIM900_Buff); //发送读取短信命令
error = SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20); //等待返回
if(error == AT_RETURN_OK)
{
GSM_ParseTextSMS((char *)SIM900_Buff, pSMS, cnt, pInfo); //解析TEXT格式短信
}
}
SIM900_SendATcom("AT+CMGF=0"); //配置短信为PDU格式
error = SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20);
return error;
}
/*************************************************************************************************************************
*函数 : u32 GSM_ParseTextSMS(char *pText, char *pSMS, u32 TextSize, SMS_INFO *pInfo)
*功能 : 解析一条TEXT格式短信
*参数 : pText:短信TEXT数据缓冲区指针
* pSMS:解析后的短信缓冲区指针
TextSize:数据大小
* pInfo:短信信息指针
*返回 : TRUE:成功;FALSE:失败
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 2013-04-30
*最后修改时间 : 2013-04-30
*说明 : 需要先切换到TEXT格式,用于解析TEXT格式短信,之后会切换回PDU格式
需要先解析为PDU后才知道是否为TEXT格式短信
*************************************************************************************************************************/
bool GSM_ParseTextSMS(char *pText, char *pSMS, u32 TextSize, SMS_INFO *pInfo)
{
u16 cnt = 0;
// u16 temp;
char *p;
// u16 SMS_Size;
pText[TextSize] = '\0'; //添加结束符
p = strstr((const char*)pText, "+CMGR:");
p = strstr((const char*)p, "\r\n"); //寻找短信TEXT开始位置
cnt = ((u32)p - (u32)pText) + 2; //找到短信TEXT开始的位置了
if(p == NULL || cnt >= TextSize)
{
SIM900_debug("TEXT短信解析错误!\r\n");
return FALSE;
}
p +=2; //跳到短信开始位置
for(cnt = 0;cnt < pInfo->SMS_Size;cnt ++) //复制短信
{
pSMS[cnt] = p[cnt];
}
pSMS[pInfo->SMS_Size] = 0; //添加结束符
return TRUE;
}
/*************************************************************************************************************************
*函数 : static u8 PhoneNumtoPDUChar(u8 *pNum, char *pCHAR,u8 NumLen)
*功能 : 将电话号码字符转换为PDU要求的字符
*参数 : pNum:电话号码指针
* pChar:字符缓冲区指针
* NumLen:电话号码长度
*返回 : 字符长度
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 2013-04-04
*最后修改时间 : 2013-10-17
*说明 : 主要用于电话号码,短信中心号码转换
* 如果长度为奇数,则将补成偶数,并在最后一个字符的高位保留位0xf;
* 本函数不添加结束符
*************************************************************************************************************************/
static u8 PhoneNumtoPDUChar(u8 *pNum, char *pChar,u8 NumLen)
{
u8 i;
u8 temp;
for(i = 0;i < NumLen;i ++)
{
temp = (pNum[i]+'0') & 0x0f;
if(i % 2) //位数为奇数
pChar[i-1] = (temp > 9) ? ('a' + temp - 10) :( temp + '0');
else //位数为偶数
pChar[i+1] = (temp > 9) ? ('a' + temp - 10) : (temp + '0');
}
if(i % 2)
{
pChar[NumLen-1] = 'F';
return (NumLen + 1);
}
return NumLen;
}
/*************************************************************************************************************************
*函数 : static u8 ChartoPhoneNum(char *pChar, char *pNum, u8 CharLen)
*功能 : 将字符转换为电话号码
*参数 : pCHAR:字符缓冲区指针
* pNum:电话号码指针
* charLen:字符号码长度
*返回 : 电话长度
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 2013-04-04
*最后修改时间 : 2013-10-17
*说明 : 主要用于电话号码,短信中心号码转换
* 如果电话长度为奇数,则将补成偶数,并在最后一个字符的高位保留位0xf;
* 转换后为字符
*************************************************************************************************************************/
static u8 ChartoPhoneNum(char *pChar, char *pNum, u8 CharLen)
{
u32 i;
u8 temp;
for(i = 0;i < CharLen;i ++)
{
temp = pChar[i];
if(temp == 'F') //还有一位就结束了
{
pNum[i] = pChar[i+1];
return i + 1;
}
else if(temp > '9') //非数字
{
return 0; //电话号码格式错误
}
else if(i % 2) //位数为奇数
pNum[i-1] = temp;
else //位数为偶数
pNum[i+1] = temp;
}
return i;
}
/*************************************************************************************************************************
*函数 : static u32 StringToUnicodeStr(char *pStr, char *pucode,u32 SrtLen)
*功能 : 将字符串转换为unicode,并存储为16进制样式的字符串
*参数 : pStr:字符缓冲区指针
* pucode:转换结果缓冲区
* SrtLen:字符串字节长度
*返回 : 转换成为字符后的长度
*依赖 : u16 OneGBKtoUNICODE(u16 GBKCode)
*作者 : cp1300@139.com
*时间 : 2013-04-04
*最后修改时间 : 2013-10-17
*说明 : 用于将短信内容转换为PUD格式,本函数不添加字符串结束符
* 如"a,b,c"--->"0,0,6,1,0,0,6,2,0,0,6,3"
* 输出缓冲区至少为输入的4倍
*************************************************************************************************************************/
static u32 StringToUnicodeStr(char *pStr, char *pucode,u32 SrtLen)
{
u32 i;
u16 temp;
u8 m;
u8 chTmp= 0;
u32 cnt = 0;
for(i = 0;i < SrtLen;i ++)
{
if(pStr[i] < 0x80) //ASCII
{
temp = pStr[i];
}
else //GBK
{
temp = pStr[i ++]<< 8;
temp |= pStr[i];
temp = OneGBKtoUNICODE(temp);
}
for(m = 0; m <= 12; m+=4)
{
chTmp = (temp >> (12-m)) & 0x0F; //先取高位
if(chTmp > 0x09) //! 0x0A-0x0F
pucode[cnt ++] = chTmp-0x0A+'A'; //! 'A'-'F'
else //! 0x00-0x09
pucode[cnt ++] = chTmp-0x00+'0'; //! '0'-'9'
}
}
return cnt;
}
/*************************************************************************************************************************
*函数 : u32 UnicodeStrToString(u8 *pucode,char *pStr,u32 SrtLen)
*功能 : 将字符unicode转换为字符串
*参数 : pucode:转换结果缓冲区
* pStr:字符缓冲区指针
* SrtLen:字符串字节长度
*返回 : 转换成为字符后的长度
*依赖 : u16 OneUNICODEtoGBK(u16 unicode);
*作者 : cp1300@139.com
*时间 : 2013-04-04
*最后修改时间 : 2013-10-26
*说明 : 用于将PUD格式短信解析,本函数不添加字符串结束符
2013-10-26:解决短信中句号无法解析
*************************************************************************************************************************/
static u32 UnicodeStrToString(u8 *pucode,char *pStr,u32 SrtLen)
{
u32 i;
u16 temp;
u32 cnt = 0;
u8 H,L;
for(i = 0;i < SrtLen;i+=4)
{
if(pucode[i] == '0') //0
{
H = pucode[i+2];
L = pucode[i+3];
H = (H > '9') ? H - 'A' + 10 : H - '0';
L = (L > '9') ? L - 'A' + 10 : L - '0';
pStr[cnt++] = (H << 4) + L;
}
else
{
H = pucode[i];
L = pucode[i+1];
H = (H > '9') ? H - 'A' + 10 : H - '0';
L = (L > '9') ? L - 'A' + 10 : L - '0';
temp = (H << 4) + L;
temp <<= 8;
H = pucode[i+2];
L = pucode[i+3];
H = (H > '9') ? H - 'A' + 10 : H - '0';
L = (L > '9') ? L - 'A' + 10 : L - '0';
temp |= (H << 4) + L;
//lcd_printf("temp1 = 0x%04X\r\n",temp);
switch(temp)
{
case 0x3002: //句号无法显示,转换为GBK编码句号
temp = 0xA1A3;break;//'。'; break;
default :
temp = OneUNICODEtoGBK(temp);break; //编码转换
}
//lcd_printf("temp2 = 0x%04X\r\n",temp);
pStr[cnt++] = temp >> 8 ;
pStr[cnt++] = temp & 0xff;
}
}
return cnt;
}
/*************************************************************************************************************************
*函数 : u32 GSM_StringToHex(char *pStr, u8 NumDigits)
*功能 : 将16进制样式字符串转换为16进制整型数(必须保证字符串字母都是大写)
*参数 : pStr:字符串起始指针
* NumDigits:数字位数,16进制数字位数
*返回 : 转换后的数字
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 2013-04-30
*最后修改时间 : 2013-10-17
*说明 : 比如字符串"A865"转换后为0xA865,位数为4位
必须保证字符串字母都是大写
*************************************************************************************************************************/
static u32 GSM_StringToHex(char *pStr, u8 NumDigits)
{
u8 temp;
u32 HEX = 0;
u8 i;
NumDigits = (NumDigits > 8) ? 8 : NumDigits; //最大支持8位16进制数
for(i = 0;i < NumDigits;i ++)
{
HEX <<= 4;
temp = pStr[i];
temp = (temp > '9') ? temp - 'A' + 10 : temp - '0';
HEX |= temp;
}
return HEX;
}
/*************************************************************************************************************************
*函数 : void GSM_HexToString(u32 HexNum,c har *pStr, u8 NumDigits)
*功能 : 将整型数字转换为16进制样式字符串(字母为大写,不带结束符)
*参数 : HexNum:16进制数字
pStr:字符缓冲区指针
* NumDigits:数字位数,16进制数字位数
*返回 : 无
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 2013-04-30
*最后修改时间 : 2013-04-30
*说明 : 比如字符串0xA865转换后为"A865",位数为4位
*************************************************************************************************************************/
static void GSM_HexToString(u32 HexNum,char *pStr, u8 NumDigits)
{
u8 temp;
u8 i;
NumDigits = (NumDigits > 8) ? 8 : NumDigits; //最大支持8位16进制数
for(i = 0;i < NumDigits;i ++)
{
temp = 0x0f & (HexNum >> (4 * (NumDigits - 1 - i)));
temp = (temp > 0x09) ? (temp - 0x0A + 'A') : (temp + '0');
pStr[i] = temp;
}
}
/*************************************************************************************************************************
*函数 : u32 GSM_StringToDec(char *pStr, u8 NumDigits)
*功能 : 将10进制样式字符串转换为整型数(必须保证完全为数字字符)
*参数 : pStr:字符串起始指针
* NumDigits:数字位数,10进制数字位数
*返回 : 转换后的数字
*依赖 : 无
*作者 : cp1300@139.com
*时间 : 2013-04-30
*最后修改时间 : 2013-04-30
*说明 : 比如字符串"1865"转换后为1865,位数为4位
必须保证完全为数字字符
*************************************************************************************************************************/
u32 GSM_StringToDec(char *pStr, u8 NumDigits)
{
u32 temp;
u32 DEC = 0;
u8 i;
u8 j;
NumDigits = (NumDigits > 10) ? 10 : NumDigits; //最大支持10位10进制数
for(i = 0;i < NumDigits;i ++)
{
temp = pStr[i] - '0';
if(temp > 9) //只能是数字范围
return 0;
for(j = 1;j < (NumDigits - i);j ++)
{
temp *= 10;
}
DEC += temp;
}
return DEC;
}
/*************************************************************************************************************************
* 函数 : SIM900_CALLS SIM900_TestCallStatus(void)
* 功能 : SIM900 通话状态检测
* 参数 : retry:重试次数
* 返回 : FALSE:通信失败;TRUE:通信成功
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-20
* 最后修改时间 : 2013-10-20
* 说明 : 每隔20ms向SIM900发送一个"AT",等待响应返回
*************************************************************************************************************************/
SIM900_CALLS SIM900_TestCallStatus(void)
{
u32 cnt;
u8 retry = SIM900_RETRY;
char *p;
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
SIM900_SendATcom("AT+CPAS");
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200ms
{
p = strstr((const char*)SIM900_Buff, "+CPAS:"); //搜索字符"+CPAS"
if(p != NULL)
{
cnt = GSM_StringToDec(p + 7, 1); //获取状态编码
if(cnt > SIM900_CALL_CENTER)
return SIM900_CALL_ERROR;
else return (SIM900_CALLS)cnt;
}
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
return SIM900_CALL_ERROR;
}
/*************************************************************************************************************************
* 函数 : bool SIM900_HangUp(void)
* 功能 : SIM900 挂掉电话
* 参数 : 无
* 返回 : FALSE:通信失败;TRUE:通信成功
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-20
* 最后修改时间 : 2013-10-20
* 说明 :
*************************************************************************************************************************/
bool SIM900_HangUp(void)
{
u32 cnt;
u8 retry = SIM900_RETRY;
//检测模块存在
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
SIM900_SendATcom("ATH"); //挂机
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200ms
{
return TRUE;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
return FALSE;
}
/*************************************************************************************************************************
* 函数 : bool SIM900_GetServeNumber(PHONE_NUMBER *pServeNumber)
* 功能 : 获取短信服务中心号码
* 参数 : pServeNumber:电话号码存储缓冲区指针
* 返回 : FALSE:通信失败;TRUE:通信成功
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-20
* 最后修改时间 : 2013-10-20
* 说明 : 获取SIM卡内部的短信服务中心号码,一般在办理SIM卡的时候已经进行了设置.
如果没有预置短信中心号码需要使用手机进行设置
*************************************************************************************************************************/
bool SIM900_GetServeNumber(PHONE_NUMBER *pServeNumber)
{
u8 n;
u32 cnt;
char *p,*p1;
u8 retry = SIM900_RETRY; //重试次数
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
//+CSCA: "+8613800270500",145
SIM900_SendATcom("AT+CSCA?"); //发送"AT+CSCA",获取短信服务中心号码
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
p = strstr((const char*)SIM900_Buff, "+CSCA:"); //搜索字符"+CSCA:"
if(p != NULL) //搜索成功
{
p = strstr(p+1, "+"); //搜索"+"
if(p != NULL)
{
p1 = strstr(p+1, "\""); //搜索"\""
if(p1 != NULL)
{
n = p1 - (p+1); //计算电话号码长度
pServeNumber->PhoneNumLen = (n > PHONE_NUMBER_MAX_LEN) ? PHONE_NUMBER_MAX_LEN : n; //存储短信服务中心号码长度
p ++; //跳过前面的"+"
for(n = 0;n < pServeNumber->PhoneNumLen;n ++)
{
pServeNumber->PhoneNumBuff[n] = p[n]; //复制电话号码
}
pServeNumber->PhoneNumBuff[n] = '\0'; //添加结束符
SIM900_debug("短信中心号码:%s(长度:%d)\r\n",pServeNumber->PhoneNumBuff,pServeNumber->PhoneNumLen);
return TRUE;
}
}
}
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
return FALSE;
}
/*************************************************************************************************************************
* 函数 : bool SIM900_GetPhoneNumber(PHONE_NUMBER *pPhoneNumber)
* 功能 : 获取本机号码
* 参数 : CenterPhone:电话号码存储缓冲区指针
* 返回 : FALSE:通信失败;TRUE:通信成功
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-20
* 最后修改时间 : 2013-10-20
* 说明 : 通常会预存本机号码到SIM卡,也可能没有
*************************************************************************************************************************/
bool SIM900_GetPhoneNumber(PHONE_NUMBER *pPhoneNumber)
{
u8 n;
u32 cnt;
char *p,*p1;
u8 retry = SIM900_RETRY; //重试次数
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
//+CNUM: "","15871750634",129,7,4
SIM900_SendATcom("AT+CNUM"); //发送"AT++CNUM",获取号码
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
p = strstr((const char*)SIM900_Buff, "+CNUM:"); //搜索字符"+CNUM:"
if(p != NULL) //搜索成功
{
p = strstr(p+1, "\",\""); //搜索"",""
if(p != NULL)
{
p1 = strstr(p+3, "\","); //搜索"","
if(p1 != NULL)
{
n = p1 - (p+3); //计算电话号码长度
pPhoneNumber->PhoneNumLen = (n > PHONE_NUMBER_MAX_LEN) ? PHONE_NUMBER_MAX_LEN : n; //存储号码长度
p +=3; //跳过前面的"\",\""
for(n = 0;n < pPhoneNumber->PhoneNumLen;n ++)
{
pPhoneNumber->PhoneNumBuff[n] = p[n]; //复制电话号码
}
pPhoneNumber->PhoneNumBuff[n] = '\0'; //添加结束符
SIM900_debug("本机号码:%s(长度:%d)\r\n",pPhoneNumber->PhoneNumBuff,pPhoneNumber->PhoneNumLen);
return TRUE;
}
}
}
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
return FALSE;
}
/*************************************************************************************************************************
* 函数 : int SIM900_GetSignal(void)
* 功能 : 获取信号强度
* 参数 : 无
* 返回 : <0:获取失败;0-31:信号强度;
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-21
* 最后修改时间 : 2013-10-21
* 说明 : 无
*************************************************************************************************************************/
int SIM900_GetSignal(void)
{
u8 temp;
u32 cnt;
char *p;
u8 retry = SIM900_RETRY; //重试次数
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
//+CSQ: 27,0
//+CSQ: 5,0
SIM900_SendATcom("AT+CSQ"); //发送"AT++CSQ",获取号码
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
p = strstr((const char*)SIM900_Buff, "+CSQ:"); //搜索字符"+CSQ:"
if(p != NULL) //搜索成功
{
if(p[7] == ',') //信号强度为1位数
{
temp = GSM_StringToDec(&p[6], 1);
}
else
{
temp = GSM_StringToDec(&p[6], 2);
}
return temp;
}
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
return -1;
}
/*************************************************************************************************************************
* 函数 : SIM900_NETSTATUS SIM900_GetNetworkStatus(void)
* 功能 : 获取网络注册状态
* 参数 : 无
* 返回 : SIM900_NETSTATUS
* 依赖 : 底层
* 作者 : cp1300@139.com
* 时间 : 2013-10-29
* 最后修改时间 : 2013-10-29
* 说明 : 无
*************************************************************************************************************************/
SIM900_NETSTATUS SIM900_GetNetworkStatus(void)
{
u32 cnt;
char *p;
u8 retry = SIM900_RETRY; //重试次数
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
//+CREG: 0,1
SIM900_SendATcom("AT+CREG?"); //发送"AT+CREG?",获取网络注册状态
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
p = strstr((const char*)SIM900_Buff, "+CREG:"); //搜索字符"+CSQ:"
if(p != NULL) //搜索成功
{
return (SIM900_NETSTATUS)GSM_StringToDec(&p[9], 1);
}
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
return SIM900_NET_ERROR;
}
//PDU模式短信限制长度,最大70个字符,不分中英文
//返回:限制之后的字符个数
u32 GSM_PDUStrRes(char *pStr)
{
u32 n = 0;
while(*pStr != 0)
{
n ++;
if(n == 71)
{
SIM900_debug("PDU模式短信长度超出70B,强制为70B!\r\n");
*pStr = 0; //强制添加结束符
return n;
}
if((u8)*pStr < 0x80) //ASCII
{
pStr ++;
}
else if((u8)*pStr > 0x80) //中文
{
pStr += 2;
}
}
return n;
}
//发送一条短信
//短信电话号码长度按照字节算但是短信中心号码按照半字节算
//0891683108100005F0 1100 0D 91685156525310F30008AA0C 9E3F9E4475355B5079D16280
/*************************************************************************************************************************
*函数 : bool GSM_SendOneSMS(char *pSMS, u8 *pPDU, char *pServeNumber, char *pPhoneNumber)
*功能 : 发送一条普通短信,正常长度
*参数 : pSMS:短信内容缓冲区指针,内容为文本文档,并且字符串需要结束符
* pPDU:PDU数据缓冲区指针
pServeNumber:短信中心号码
pPhoneNumber:目标手机号码结构指针
*返回 : TRUE:短信发送成功;FALSE:短信发送失败
*依赖 : 底层
*作者 : cp1300@139.com
*时间 : 2013-04-04
*最后修改时间 : 201310-23
*说明 : 短信文本需要添加结束符
电话号码必须以86等国际区号开头
PDU可以发送中文,但是text只能发送英文
*************************************************************************************************************************/
bool GSM_SendOneSMS(char *pSMS, u8 *pPDU, char *pServeNumber, char *pPhoneNumber)
{
SIM900_ERROR error;
u16 OffsetCnt = 0; //缓冲区偏移计数器
u32 cnt;
u16 temp;
char ComBuff[16];
u16 nSMSCenterLen= 0, nSMSPduLen = 0;
u16 SMSLen = 0; //短信字符长度
FunctionalState EnableU2S = DISABLE; //使能U2S编码模式,默认为7BIT编码模式
u8 *p = (u8 *)pSMS;
while(*p != 0)
{
if(*p >= 0x80) //有汉字
{
EnableU2S = ENABLE; //使能U2S编码模式
SIM900_debug("需要发送的短信为PDU格式\r\n");
break;
}
p ++;
}
if(EnableU2S == ENABLE) //使能了U2S编码模式
{
SMSLen = GSM_PDUStrRes(pSMS); //限制PDU短信长度,计算短信长度
}
else //TEXT模式短信
{
SMSLen = strlen(pSMS); //计算短信长度
if(SMSLen > 160) //短信长度大于160个字符
{
pSMS[160] = 0; //添加结束符,限制长度
SMSLen = 160;
}
}
//计算短信中心号码长度,+91,+86,短信中心号码必须由86开头,并且要加上91,长度为每个数字半字节,不足补F
temp = (strlen(pServeNumber) + 2 + 1) / 2;
GSM_HexToString(temp, (char *)(pPDU+OffsetCnt), 2); //短信中心号码长度转换为16进制样式字符串
OffsetCnt += 2; //跳过短信中心号码长度字节
pPDU[OffsetCnt++] = '9'; //服务中心类型
pPDU[OffsetCnt++] = '1';
OffsetCnt += PhoneNumtoPDUChar((u8 *)pServeNumber,(char *)(pPDU+OffsetCnt),strlen(pServeNumber)); //短信中心号码
nSMSCenterLen = OffsetCnt / 2;
//! PDU
pPDU[OffsetCnt++] = '1';
pPDU[OffsetCnt++] = '1';
//! For MR
pPDU[OffsetCnt++] = '0';
pPDU[OffsetCnt++] = '0';
//! For DA
//计算电话号码长度,+86,发送短信的电话号码由86开头,电话号码长度为字符个数
GSM_HexToString(strlen(pPhoneNumber), (char *)(pPDU+OffsetCnt), 2); //手机号码长度转换为16进制样式字符串
OffsetCnt += 2; //跳过手机号码长度字节
pPDU[OffsetCnt++] = '9'; //服务中心类型
pPDU[OffsetCnt++] = '1';
OffsetCnt += PhoneNumtoPDUChar((u8 *)pPhoneNumber,(char *)(pPDU+OffsetCnt),strlen(pPhoneNumber)); //短信发送号码
//! For PID
pPDU[OffsetCnt++] = '0';
pPDU[OffsetCnt++] = '0';
//! For DCS
if(EnableU2S == ENABLE) //U2S
{
pPDU[OffsetCnt++] = '0';
pPDU[OffsetCnt++] = '8';
}
else //7BIT
{
pPDU[OffsetCnt++] = '0';
pPDU[OffsetCnt++] = '0';
}
//! For VP
pPDU[OffsetCnt++] = 'A';
pPDU[OffsetCnt++] = 'A';
//! For UDL AND UD
//! 注意,此处先将用户数据长度设置为00,并
//! 记录此时的缓冲区位置,然后等编码完成,
//! 确定了用户数据长度后再修改为实际长度
cnt = OffsetCnt;
pPDU[OffsetCnt++] = '0';
pPDU[OffsetCnt++] = '0';
//短信内容
if(EnableU2S == ENABLE) //U2S
{
temp = StringToUnicodeStr(pSMS,(char *)&pPDU[OffsetCnt], strlen(pSMS));//将短信数据转换为字符数据
OffsetCnt += temp;
GSM_HexToString(temp/2, (char *)&pPDU[cnt], 2);
//! PDU串的长度,后面AT+CMGS要用到此长度
nSMSPduLen = OffsetCnt / 2 -nSMSCenterLen;
}
else //7bit
{
u8 buff[140]; //TEXT短信缓冲区
temp = gsmEncode7bit(pSMS, buff); //将ASCII转换为7bit编码
GSM_HexToString(SMSLen, (char *)&pPDU[cnt], 2);
for(cnt = 0;cnt < temp;cnt ++)
{
GSM_HexToString(buff[cnt], (char *)&pPDU[OffsetCnt+cnt*2], 2); //7bit编码转换为16进制格式字符串
}
OffsetCnt += (temp << 1);
//! PDU串的长度,后面AT+CMGS要用到此长度
nSMSPduLen = OffsetCnt / 2 -nSMSCenterLen;
}
//短信内容长度转换为16进制样式字符串,存储短信长度
//end
pPDU[OffsetCnt++] = 0x1A;
pPDU[OffsetCnt++] = 0x00;
SIM900_debug("\r\n%s\r\n",pPDU);
//! 下面是发送过程
//! AT
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
//! ATE0
SIM900_SendATcom("ATE0");
if(AT_RETURN_OK != SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
return FALSE;
}
//! AT+CMGF
SIM900_SendATcom("AT+CMGF=0");
if(AT_RETURN_OK != SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
return FALSE;
}
//! AT+CMGS//
sprintf(ComBuff, "AT+CMGS=%d", nSMSPduLen);
SIM900_debug("AT+CMGS=%d\r\n", nSMSPduLen);
SIM900_SendATcom(ComBuff);
if(AT_RETURN_ERROR == SIM900_GetATResp(SIM900_Buff, &cnt, ">", 10, 20)) //等待响应,超时200MS
{
return FALSE;
}
//PDU Content
SIM900_ClearRxCnt(); //清除接收缓冲区
SIM900_SendString((char *)pPDU); //发送字符串
error = SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 50);
if(error == AT_RETURN_ERROR) //返回错误
{
return FALSE;
}
else
{
temp = 30; //等待短信接收成功,最大等待30S
SIM900_ClearRxCnt(); //清除接收缓冲区
do
{
GSM_DelaySer(1); //延时1S
error = SIM900_GetATResp(SIM900_Buff, &cnt, "+CMGS", 10, 20); //查询发送成功提示
SIM900_ClearRxCnt(); //清除接收缓冲区
if(error == AT_RETURN_OK)
{
SIM900_debug("短信已发送成功,对方已经接收!\r\n");
break;
}
else if(error == AT_RETURN_ERROR)
{
SIM900_debug("短信发送失败!很有可能是欠费了!\r\n");
return FALSE;
}
if(temp == 0)
{
SIM900_debug("短信发送超时!\r\n");
return FALSE;
}
}while(temp --);
}
//测试短信
//0891683108200705F011000D91685172910098F40008AA086D4B8BD577ED4FE1
return TRUE;
}
/*************************************************************************************************************************
*函数 : SIM900_CALLS SIM900_MakingCall(const char *pPhoneNumber, u8 TimeOut)
*功能 : 拨打指定电话号码
*参数 : pPhoneNumber:电话号码字符串指针
TimeOut:接听超时,1-255S
*返回 : SIM900_CALLS:拨打电话状态
*依赖 : 底层
*作者 : cp1300@139.com
*时间 : 2013-10-24
*最后修改时间 : 2013-10-24
*说明 : 拨打电话
*************************************************************************************************************************/
SIM900_CALLS SIM900_MakingCall(const char *pPhoneNumber, u8 TimeOut)
{
u32 cnt;
u8 retry = SIM900_RETRY; //重试次数
char buff[32];
SIM900_ERROR error;
if(strlen(pPhoneNumber) > 26) return SIM900_CALL_ERROR; //电话号码太长了
sprintf(buff, "ATD%s;", pPhoneNumber);
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
do
{
SIM900_SendATcom("AT+MORING=1"); //发送"AT+MORING=1",设置拨号提示
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
break;
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0) return SIM900_CALL_ERROR;
//拨打电话
/*
AT+MORING=1
OK
ATD15271900894;
OK
MO RING
MO CONNECTED
+CDRIND: 0
NO CARRIER
*/
SIM900_SendATcom(buff); //拨号
error = SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20); //等待响应,超时200MS
if(error == AT_RETURN_ERROR) //返回错误
{
return SIM900_CALL_ERROR;
}
else
{
do
{
GSM_DelaySer(1); //延时1S
error = SIM900_GetATResp(SIM900_Buff, &cnt, "MO RING", 10, 1); //查询拨打成功提示
if(error == AT_RETURN_OK)
{
SIM900_debug("呼叫成功,对方振铃中...\r\n");
return SIM900_CALL_RING;
}
error = SIM900_GetATResp(SIM900_Buff, &cnt, "MO CONNECTED", 10, 1); //查询接通标志
if(error == AT_RETURN_OK)
{
SIM900_debug("对方已经接听电话!\r\n");
return SIM900_CALL_PUT;
}
error = SIM900_GetATResp(SIM900_Buff, &cnt, "BUSY", 10, 1); //查询接通标志
if(error == SIM900_CALL_BUSY)
{
SIM900_debug("对方占线!\r\n");
return SIM900_CALL_PUT;
}
SIM900_ClearRxCnt(); //清除接收缓冲区
TimeOut --;
}while(TimeOut);
if(TimeOut == 0)
{
SIM900_debug("拨打电话超时!\r\n");
return SIM900_CALL_TIMEOUT;
}
}
return SIM900_CALL_ERROR;
}
/*************************************************************************************************************************
*函数 : SIM900_CALLS SIM900_WaitGetThrough(u8 TimeOut)
*功能 : 电话拨打成功后等待对方接听
*参数 : TimeOut:接听超时,1-255S
*返回 : SIM900_CALLS:电话接通状态
*依赖 : 底层
*作者 : cp1300@139.com
*时间 : 2013-10-26
*最后修改时间 : 2013-10-26
*说明 : 拨打电话成功后,等待对方接通,或不接
*************************************************************************************************************************/
SIM900_CALLS SIM900_WaitGetThrough(u8 TimeOut)
{
u32 cnt;
SIM900_ERROR error;
while(TimeOut --)
{
SIM900_ClearRxCnt(); //清除接收缓冲区
GSM_DelaySer(1); //延时1S
error = SIM900_GetATResp(SIM900_Buff, &cnt, "MO CONNECTED", 10, 1); //查询接通标志
if(error == AT_RETURN_OK)
{
SIM900_debug("对方已经接听电话!\r\n");
return SIM900_CALL_PUT;
}
error = SIM900_GetATResp(SIM900_Buff, &cnt, "NO ANSWER", 10, 1); //查询一直未接听标志
if(error == AT_RETURN_OK)
{
SIM900_debug("对方无人接听!\r\n");
return SIM900_CALL_NO_ANSWER;
}
error = SIM900_GetATResp(SIM900_Buff, &cnt, "NO CARRIER", 10, 1); //电话已经挂断
if(error == AT_RETURN_OK)
{
SIM900_debug("对方拒接电话,对方已经挂断!\r\n");
return SIM900_CALL_NO_CARRIER;
}
}
SIM900_debug("对方接听电话超时!\r\n");
return SIM900_CALL_TIMEOUT;
}
/*************************************************************************************************************************
*函数 : SIM900_CALLS SIM900_WaitHangUP(u8 TimeOut)
*功能 : 电话接通后等待对方挂电话
*参数 : TimeOut:接听超时,1-255S
*返回 : SIM900_CALLS:电话接通状态
*依赖 : 底层
*作者 : cp1300@139.com
*时间 : 2013-10-26
*最后修改时间 : 2013-10-26
*说明 : 等待对方挂电话
*************************************************************************************************************************/
SIM900_CALLS SIM900_WaitHangUP(u8 TimeOut)
{
u32 cnt;
SIM900_ERROR error;
while(TimeOut --)
{
SIM900_ClearRxCnt(); //清除接收缓冲区
GSM_DelaySer(1); //延时1S
error = SIM900_GetATResp(SIM900_Buff, &cnt, "NO CARRIER", 10, 1); //电话已经挂断
if(error == AT_RETURN_OK)
{
SIM900_debug("对方电话已经挂断!\r\n");
return SIM900_CALL_NO_CARRIER;
}
}
SIM900_debug("对方挂电话超时!\r\n");
return SIM900_CALL_TIMEOUT;
}
/*************************************************************************************************************************
*函数 : bool SIM900_TestCall(void)
*功能 : 查询模块是否可以拨打电话
*参数 : 无
*返回 : TRUE:成功;FALSE:失败
*依赖 : 底层
*作者 : cp1300@139.com
*时间 : 2013-10-24
*最后修改时间 : 2013-10-24
*说明 : 用于检查模块是否准备好拨打电话
*************************************************************************************************************************/
bool SIM900_TestCall(void)
{
u32 cnt;
u8 retry = SIM900_RETRY; //重试次数
do
{
if(SIM900_TestAT(10) == FALSE) //串口同步失败
{
SIM900_WaitSleep(1000); //等待上一个操作完成
}
SIM900_SendATcom("AT+CCALR?"); //发送"AT+CCALR?",查询模块是否准备好
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS
{
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "+CCALR: 1", 10, 1))
{
return TRUE;
}
if(AT_RETURN_OK == SIM900_GetATResp(SIM900_Buff, &cnt, "+CCALR: 0", 10, 1))
{
return FALSE;
}
}
SIM900_Ready(); //等待就绪
retry --;
}while(retry);
if(retry == 0) return FALSE;
return FALSE;
}
// 7-bit编码
// pSrc: 源字符串指针
// pDst: 目标编码串指针
// nSrcLength: 源字符串长度
// 返回: 目标编码串长度
static int gsmEncode7bit(const char* pSrc,u8* pDst)
{
int nSrc; // 源字符串的计数值
int nDst; // 目标编码串的计数值
int nChar; // 当前正在处理的组内字符字节的序号,范围是0-7
unsigned char nLeft=0; // 上一字节残余的数据
int nSrcLength = strlen(pSrc);
// 计数值初始化
nSrc = 0;
nDst = 0;
// 将源串每8个字节分为一组,压缩成7个字节
// 循环该处理过程,直至源串被处理完
// 如果分组不到8字节,也能正确处理
while(nSrc<nSrcLength)
{
// 取源字符串的计数值的最低3位
nChar = nSrc & 7;
// 处理源串的每个字节
if(nChar == 0)
{
// 组内第一个字节,只是保存起来,待处理下一个字节时使用
nLeft = *pSrc;
}
else
{
// 组内其它字节,将其右边部分与残余数据相加,得到一个目标编码字节
*pDst = (*pSrc << (8-nChar)) + nLeft;
// 将该字节剩下的左边部分,作为残余数据保存起来
nLeft = *pSrc >> nChar;
// 修改目标串的指针和计数值 pDst++;
//SIM900_debug("%c",*pDst);
pDst++; nDst++;
}
// 修改源串的指针和计数值
pSrc++; nSrc++;
}
//Nleft还有剩余,需要一个自己保留。
nChar = nSrc & 7;
if(nChar != 0)
{
*pDst=nLeft;
nDst++;
pDst++;
}
//*pDst='\0';
// 返回目标串长度
return nDst;
}
// 7-bit解码
// pSrc: 源编码串指针,7bit编码
// pDst: 目标字符串指针
// nSrcLength: 源编码串长度
// 返回: 目标字符串长度
static int gsmDecode7bit(const u8 *pSrc, char *pDst, int nSrcLength)
{
int nSrc; // 源字符串的计数值
int nDst; // 目标解码串的计数值
int nByte; // 当前正在处理的组内字节的序号,范围是0-6
unsigned char nLeft; // 上一字节残余的数据
// 计数值初始化
nSrc = 0;
nDst = 0;
// 组内字节序号和残余数据初始化
nByte = 0;
nLeft = 0;
// 将源数据每7个字节分为一组,解压缩成8个字节
// 循环该处理过程,直至源数据被处理完
// 如果分组不到7字节,也能正确处理
while(nSrc<nSrcLength)
{
// 将源字节右边部分与残余数据相加,去掉最高位,得到一个目标解码字节
*pDst = (((*pSrc) << nByte) | nLeft) & 0x7f;
// 将该字节剩下的左边部分,作为残余数据保存起来
nLeft = (*pSrc) >> (7-nByte);
// 修改目标串的指针和计数值
pDst++;
nDst++;
// 修改字节计数值
nByte++;
// 到了一组的最后一个字节
if(nByte == 7)
{
// 额外得到一个目标解码字节
*pDst = nLeft;
// 修改目标串的指针和计数值
pDst++;
nDst++;
// 组内字节序号和残余数据初始化
nByte = 0;
nLeft = 0;
}
// 修改源串的指针和计数值
pSrc++;
nSrc++;
}
*pDst = 0; //添加结束符
// 返回目标串长度
return nDst;
}
/*************************************************************************************************************
* 文件名: SIM900.h
* 功能: STM32 SIM900底层驱动函数
* 作者: cp1300@139.com
* 创建时间: 2013-04-03
* 最后修改时间: 2013-10-16
* 详细: GSM_CDMA发送短信等
*************************************************************************************************************/
#ifndef SIM900_H_
#define SIM900_H_
#include "system.h"
//GSM模块相关定义
/*#define SIM900_RESET //PBout(6) //PB6
#define SIM900_PORON PBout(5) //PB5
#define GSM_STATUS_IN PDin(2) //PD2
#define GSM_BUZZ PCout(12) //PC12*/
#define SIM900_UART_CH UART_CH3 //串口3
//GSM底层操作宏
/*#define GSM_RESET_H() (GSM_RESET=1)
#define GSM_RESET_L() (GSM_RESET=0)
#define GSM_PORON_H() (GSM_PORON=1)
#define GSM_PORON_L() (GSM_PORON=0)
#define GSM_STATUS() (GSM_STATUS_IN)
#define GSM_BUZZ_H() (GSM_BUZZ=1)
#define GSM_BUZZ_L() (GSM_BUZZ=0)
#define GSM_Delay_MS(x) Delay_MS(x) //CDMA GSM操作延时,单位MS*/
//GSM/CDMA模块UART相关接口
#define SIM900_SendATcom(x) UARTx_ClearRxCnt(SIM900_UART_CH);UARTx_SendString(SIM900_UART_CH,x);UARTx_SendString(SIM900_UART_CH,"\r\n") //调用串口发送一个AT命令,并且先清除接收计数器
#define SIM900_SendData(data,len) UARTx_SendData(SIM900_UART_CH, data, len); //发送指定长度数据
#define SIM900_SendString(x) UARTx_SendString(SIM900_UART_CH, x) //发送字符串
#define SIM900_GetRxCnt() UARTx_GetRxCnt(SIM900_UART_CH); //获取新数据计数器
#define SIM900_ClearRxCnt() UARTx_ClearRxCnt(SIM900_UART_CH); //清除新数据计数器
#define SIM900_UartInit() UARTx_Init(SIM900_UART_CH, 115200, ENABLE) //初始化串口,波特率115200,开启接收中断
#define SIM900_SetRxBuff(pBuff, size) UARTx_SetRxBuff(SIM900_UART_CH, pBuff,size) //设置串口接收缓冲区
#define GSM_DelayMS(x) OSTimeDlyHMSM(0,0,0,x) //延时ms,最大延时999ms
#define GSM_Delay10MS() OSTimeDlyHMSM(0,0,0,10) //延时10ms
#define GSM_Delay100MS() OSTimeDlyHMSM(0,0,0,100) //延时100ms
#define GSM_DelaySer(x) OSTimeDlyHMSM(0,0,x,0) //S延时,最大59S
//SIM900返回错误
typedef enum
{
AT_RETURN_OK = 0, //返回成功
AT_RETURN_ERROR = 1, //返回错误
AT_RETURN_UNKNOWN = 2, //返回结果未知
AT_RETURN_TIME_OUT = 0xf, //等待返回超时
}SIM900_ERROR;
//短信发送日期,使用字符格式
typedef __packed struct
{
u8 Year; //年20xx年
u8 Month; //月
u8 Day; //日
u8 Hour; //小时
u8 Minute; //分钟
u8 Second; //秒
u8 Reserve1; //保留
u8 Reserve2; //保留
}SMS_TIMER ;
//短信解析相关//注意要保证数据对齐
#define SMS_NUM_LEN_MAX 16 //电话号码最大长度16位
typedef __packed struct
{
SMS_TIMER Timer; //短信发送的时间
char NumBuff[SMS_NUM_LEN_MAX]; //电话号码缓冲区,使用的是字符模式
u8 NumLen; //电话号码长度
u8 SMS_Size; //短信有效内容长度,最大140B
u8 TEXT_MODE; //1:短信为TEXT模式;0:短信为PDU模式
u8 PDU; //PDU数据,用于区分是否有短信头部信息
u8 DSC; //DSC数据,用于区分是否为字符模式(0),PDU模式(0X08)
u8 AllNum; //当前短信总分割数
u8 PreNum; //当前位置
u8 IndexNum; //当前索引位置1-50
}SMS_INFO ;
//GSM/CDMA模块型号
typedef enum
{
GSM_MG323 = 0, //GSM_CDMA模块型号,MG323
CDMA_MC323 = 1, //GSM_CDMA模块型号,MC323
GSM_SIM900A = 2, //GSM_CDMA模块SIM900A
GSM_UNKNOWN = 0xff //未知模块
}GSM_TYPE;
//SIM900删除短信选择
typedef enum
{
DEL_READ_SMS = 1, //删除所有已读短信
DEL_UNREAD_SMS = 2, //删除所有未读短信
DEL_SENT_SMS = 3, //删除所有已经发送的短信
DEL_UNSENT_SMS = 4, //删除所有未发送短信
DEL_INBOX_SMS = 5, //删除所有接收短信
DEL_ALL_SMS = 6, //删除所有短信
}SIM900_DEL;
//通话状态
typedef enum
{
SIM900_CALL_READY = 0, //准备就绪,当前空闲
SIM900_CALL_UNKNOWN = 1, //未知响应指令
SIM900_CALL_RING = 2, //振铃,准备好可以接通
SIM900_CALL_CENTER = 3, //呼叫进行中
SIM900_CALL_TIMEOUT = 4, //拨打电话超时
SIM900_CALL_PUT = 5, //拨打的电话对方已经接通
SIM900_CALL_NO_ANSWER= 6, //对方无人接听
SIM900_CALL_NO_CARRIER= 7, //对方已经挂断
SIM900_CALL_BUSY = 8, //占线
SIM900_CALL_ERROR = 0xff//其他错误
}SIM900_CALLS;
//网络注册状态
typedef enum
{
SIM900_NET_NOT = 0, //未注册
SIM900_NET_YES = 1, //已经注册
SIM900_NET_SEA = 2, //未注册,正在搜索
SIM900_NET_TUR = 3, //注册被拒绝
SIM900_NET_UNK = 4, //未知
SIM900_NET_ROA = 5, //已经注册,但是漫游
SIM900_NET_ERROR=0XFF//错误
}SIM900_NETSTATUS;
//SIM900通信缓冲区
#define SIM900_BUFF_SIZE 2048 //2KB
extern u8 SIM900_Buff[SIM900_BUFF_SIZE]; //缓冲区
#define PDU_BUFF_SIZE 1024*10 //20KB 可以一次读取50条未读短信
extern u8 SmsPduBuff[PDU_BUFF_SIZE]; //PDU数据缓冲区
//相关控制引脚
#define SIM900_GetRI() PBin(14) //RI PB14
#define SIM900_SetDTR(x) (PBout(13)=x) //DTR PB13
#define SIM900_SetRESET(x) (PBout(15)=x) //RESET PB15
#define SIM900_SetPWR(x) (PBout(12)=x) //PWR PB12
//电话号码结构
#define PHONE_NUMBER_MAX_LEN 24-2 //电话号码最大长度
typedef __packed struct
{
u8 PhoneNumLen; //电话号码长度
char PhoneNumBuff[PHONE_NUMBER_MAX_LEN + 1];//电话号码缓冲区,电话号码前面的2位为地区编号,中国为86,打电话需要跳过前面的2位数字
}PHONE_NUMBER;
//最大重试次数,防止AT指令操作失败
#define SIM900_RETRY 2
//API
#define SIM900_Ready() if(SIM900_TestAT(10) == FALSE){SIM900_WaitSleep(1000);} //让SIM900就绪,防止卡住//串口同步失败,等待上一个操作完成
void SIM900_HardwarePowerUP(void);//SIM900硬件开机
void SIM900_HardwarePowerDOWN(void);//SIM900硬件关机
void SIM900_HardwareReset(void);//SIM900硬件复位
bool GSM_SendSMS(char *pSMS, char *pPhoneNumber);//发送短信
void SIM900_HardwareInit(void); //初始化SIM900相关的硬件
bool SIM900_ModuleInit(void); //初始化SIM900模块
bool SIM900_TestAT(u32 retry); //检测模块响应
bool SIM900_HangUp(void); //挂掉电话
SIM900_CALLS SIM900_TestCallStatus(void);//检测电话通话状态
SIM900_ERROR SIM900_GetATResp(u8 *pRxBuff, u32 *pLen, const char *pKeyword, u32 ByteTime, u32 TimeOut);//获取SIM900的AT指令响应
int SIM900_GetSmsNum(void); //获取SIM卡存储的短信数量
bool GSM_ParsePDUSMS(char *pPDU, char *pSMS, u32 PDUSize, SMS_INFO *pInfo);//解析一条PDU格式短信
SIM900_ERROR SIM900_GetUnreadSMS(u8 *pUnreadSMSBuff, u32 BuffSize, u32 *pPDUCnt);//读取SIM900所有的未读短信
SIM900_ERROR SIM900_ReadTextSMS(char *pSMS, SMS_INFO *pInfo, u16 IndexNum);//用text格式读取短信
bool GSM_ParseTextSMS(char *pText, char *pSMS, u32 TextSize, SMS_INFO *pInfo);//解析一条TEXT格式短信
bool SIM900_DelMultiSMS(SIM900_DEL DelStatus); //批量删除SIM900短信
bool SIM900_GetServeNumber(PHONE_NUMBER *pServeNumber); //获取短信服务中心号码
bool SIM900_GetPhoneNumber(PHONE_NUMBER *pPhoneNumber); //获取本机号码
bool GSM_SendOneSMS(char *pSMS, u8 *pPDU, char *pServeNumber, char *pPhoneNumber); //发送一条短信
int SIM900_GetSignal(void); //获取信号强度
SIM900_CALLS SIM900_MakingCall(const char *pPhoneNumber, u8 TimeOut);//拨打电话
bool SIM900_TestCall(void);//检查是否可以拨打电话
void SIM900_SetSMSServeNumber(char *pSMSServeNumber,u8 NumLen);//设置短信中心号码
bool SIM900_WaitSleep(u32 TimeOut); //等待模块空闲
SIM900_CALLS SIM900_WaitGetThrough(u8 TimeOut);//等待对方接听电话
SIM900_CALLS SIM900_WaitHangUP(u8 TimeOut); //等待对方挂电话
SIM900_NETSTATUS SIM900_GetNetworkStatus(void); //获取网络注册状态
u32 GSM_StringToDec(char *pStr, u8 NumDigits); //将10进制样式字符串转换为整型数(必须保证完全为数字字符
#endif /*SIM900A_H_*/
unicode_gbk.c
/*************************************************************************************************************
* 文件名: unicode_gbk.c
* 功能: 汉字编码转换
* 作者: cp1300@139.com
* 创建时间: 2013-04-03
* 最后修改时间:2013-04-03
* 详细: 需要码表支持
*************************************************************************************************************/
#include "system.h"
#include "unicode_gbk.h"
#define GBK_UNICODE_IS_SDCARD 0 //GBK,UNICODE编码表在SD卡或其它存储器中
//码表在SD卡中
#if GBK_UNICODE_IS_SDCARD
#include "ff.h"
#define GtoU "0:/GtoU.sys" //GBK 转 UCICODE 编码表位置
#define UtoG "0:/UtoG.sys" //UCICODE 转 GBK 编码表位置
static FIL GtoU_File; //GtoU 文件工作区
static FIL UtoG_File; //UtoG 文件工作区
/*************************************************************************************************************************
* 函数 : u8 GBK_UNICODE_Init(void)
* 功能 : 初始化GBK,UNICODE编码表
* 参数 : 无
* 返回 : 0:初始化成功;其它:初始化失败
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2013-04-18
* 最后修改时间 : 2013-04-18
* 说明 : 无
*************************************************************************************************************************/
u8 GBK_UNICODE_Init(void)
{
FRESULT status;
status = f_open(&UtoG_File, UtoG, FA_OPEN_EXISTING | FA_READ); //以只读方式打开UNICODEtoGBK码表,打开失败返回错误
if(status != FR_OK) //打开失败
{
lcd_printf("open %s error (%d)!\r\n",UtoG, status);
return 1;
}
status = f_open(&GtoU_File, GtoU, FA_OPEN_EXISTING | FA_READ); //以只读方式打开GBKtoUNICODE码表,打开失败返回错误
if(status != FR_OK) //打开失败
{
lcd_printf("open %s error (%d)!\r\n",GtoU, status);
return 1;
}
return 0;
}
/*************************************************************************************************************************
* 函数 : u16 OneGBKtoUNICODE(u16 GBKCode)
* 功能 : 将GBK编码转换为unicode编码
* 参数 : GBK
* 返回 : unicode
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20120602
* 最后修改时间 : 20120602
* 说明 : 需要flash中的码表支持
GBK码范围,高8位:0x81~0xfe;低8位:0x40~0xfe
*************************************************************************************************************************/
u16 OneGBKtoUNICODE(u16 GBKCode)
{
u8 ch,cl;
UINT bw;
u16 data;
ch = GBKCode >> 8;
cl = GBKCode & 0x00ff;
ch -= 0x81;
cl -= 0x40;
f_lseek(&GtoU_File, (ch*0xbf+cl)*2); //文件指针调到偏移位置
if(f_read(&GtoU_File, (u8 *)&data, 2, &bw) != FR_OK) //读取2字节
{
return 0x1fff;
}
return (ch<=0x7d && cl<=0xbe) ? data : 0x1fff;
/* ch = GBKCode >> 8;
cl = GBKCode & 0x00ff;
ch -= 0x81;
cl -= 0x40;
return (ch<=0x7d && cl<=0xbe) ? wUnicodes[ch*0xbf+cl] : 0x1fff; */
}
/*************************************************************************************************************************
* 函数 : u16 OneUNICODEtoGBK(u16 unicode)
* 功能 : 将unicode编码转换为GBK编码
* 参数 : unicode
* 返回 : GBK
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20120602
* 最后修改时间 : 20120602
* 说明 : 需要flash中的码表支持
GBK码范围,高8位:0x81~0xfe;低8位:0x40~0xfe
*************************************************************************************************************************/
u16 OneUNICODEtoGBK(u16 unicode) //用二分查找算法
{
u32 offset;
u16 temp;
UINT bw;
u8 buff[2];
if(unicode<=0X9FA5)
{
if(unicode>=0X4E00)
offset=unicode-0X4E00;//0x1b87 //0X4E00,汉字偏移起点
else
return 0x2020; //不能显示的字符就给两个空格填充,否则乱码
}
else if(unicode>0X9FA5)//是标点符号
{
if(unicode<0XFF01||unicode>0XFF61)
return 0x2020;//没有对应编码 //不能显示的字符就给两个空格填充,否则乱码
offset=unicode-0XFF01+0X9FA6-0X4E00;
}
offset *= 2;
f_lseek(&UtoG_File, offset); //文件指针调到偏移位置
if(f_read(&UtoG_File, buff, 2, &bw) != FR_OK) //读取2字节
{
return 0x2020;
}
temp = buff[0];
temp <<= 8;
temp += buff[1];
return temp; //返回找到的编码
}
#else //码表直接在代码中
#include "unicode_gbk_code.h"
/*************************************************************************************************************************
* 函数 : u8 GBK_UNICODE_Init(void)
* 功能 : 初始化GBK,UNICODE编码表
* 参数 : 无
* 返回 : 0:初始化成功;其它:初始化失败
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2013-04-18
* 最后修改时间 : 2013-04-18
* 说明 : 无
*************************************************************************************************************************/
u8 GBK_UNICODE_Init(void)
{
return 0;
}
/*************************************************************************************************************************
* 函数 : u16 OneGBKtoUNICODE(u16 GBKCode)
* 功能 : 将GBK编码转换为unicode编码
* 参数 : GBK
* 返回 : unicode
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20120602
* 最后修改时间 : 20120602
* 说明 : 需要flash中的码表支持
GBK码范围,高8位:0x81~0xfe;低8位:0x40~0xfe
*************************************************************************************************************************/
u16 OneGBKtoUNICODE(u16 GBKCode)
{
u8 ch,cl;
ch = GBKCode >> 8;
cl = GBKCode & 0x00ff;
ch -= 0x81;
cl -= 0x40;
return (ch<=0x7d && cl<=0xbe) ? wUnicodes[ch*0xbf+cl] : 0x1fff;
}
/*************************************************************************************************************************
* 函数 : u16 OneUNICODEtoGBK(u16 unicode)
* 功能 : 将unicode编码转换为GBK编码
* 参数 : unicode
* 返回 : GBK
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20120602
* 最后修改时间 : 20120602
* 说明 : 需要flash中的码表支持
GBK码范围,高8位:0x81~0xfe;低8位:0x40~0xfe
*************************************************************************************************************************/
u16 OneUNICODEtoGBK(u16 unicode) //用二分查找算法
{
u32 offset;
u16 temp;
if(unicode<=0X9FA5)
{
if(unicode>=0X4E00)
offset=unicode-0X4E00;//0x1b87 //0X4E00,汉字偏移起点
else
return 0x2020; //不能显示的字符就给两个空格填充,否则乱码
}
else if(unicode>0X9FA5)//是标点符号
{
if(unicode<0XFF01||unicode>0XFF61)
return 0x2020;//没有对应编码 //不能显示的字符就给两个空格填充,否则乱码
offset=unicode-0XFF01+0X9FA6-0X4E00;
}
offset *= 2;
temp = wGBKs[offset];
temp <<= 8;
temp += wGBKs[offset+1];
return temp; //返回找到的编码
}
#endif //GBK_UNICODE_IS_SDCARD
/*************************************************************************************************************************
* 函数 : void GBKToUnicode(u16 *pGBK, u16 *pUnicode, u32 cnt)
* 功能 : 将多个GBK编码转换为UNICODE
* 参数 : pGBK:GBK编码缓冲区
* pUnicode:UNCODE编码缓冲区
* cnt:转换编码个数
* 返回 : 无
* 依赖 : OneGBKtoUNICODE
* 作者 : cp1300@139.com
* 时间 : 20130403
* 最后修改时间 : 20130403
* 说明 : 需要flash中的码表支持
GBK码范围,高8位:0x81~0xfe;低8位:0x40~0xfe
*************************************************************************************************************************/
void GBKToUnicode(u16 *pGBK, u16 *pUnicode, u32 cnt)
{
while(cnt --)
{
*pUnicode = OneGBKtoUNICODE(*pGBK ++);
pUnicode ++;
}
}
/*************************************************************************************************************************
* 函数 : void UnicodeToGBK(u16 *pUnicode, u16 *pGBK, u32 cnt)
* 功能 : 将多个UNICODE编码转换为GBK
* 参数 : pUnicode:UNCODE编码缓冲区
* pGBK:GBK编码缓冲区
* cnt:转换编码个数
* 返回 : 无
* 依赖 : OneUNICODEtoGBK
* 作者 : cp1300@139.com
* 时间 : 20130403
* 最后修改时间 : 20130403
* 说明 : 需要flash中的码表支持
GBK码范围,高8位:0x81~0xfe;低8位:0x40~0xfe
*************************************************************************************************************************/
void UnicodeToGBK(u16 *pUnicode, u16 *pGBK, u32 cnt)
{
while(cnt --)
{
*pGBK = OneUNICODEtoGBK(*pUnicode ++);
pGBK ++;
}
}
unicode_gbk.h
/*************************************************************************************************************
* 文件名: unicode_gbk.h
* 功能: 汉字编码转换
* 作者: cp1300@139.com
* 创建时间: 2013-04-03
* 最后修改时间:2013-04-03
* 详细: 需要码表支持
*************************************************************************************************************/
#ifndef UNICODE_GBK_H_
#define UNICODE_GBK_H_
#include "system.h"
u8 GBK_UNICODE_Init(void);
u16 OneGBKtoUNICODE(u16 GBKCode);
u16 OneUNICODEtoGBK(u16 unicode);
void GBKToUnicode(u16 *pGBK, u16 *pUnicode, u32 cnt); //将多个GBK编码转换为UNICODE
void UnicodeToGBK(u16 *pUnicode, u16 *pGBK, u32 cnt); //将多个UNICODE编码转换为GBK
#endif /*UNICODE_GBK_H_*/
//短信接收
if(SIM900_GetSmsNum() > 0) //有消息
{
uart_printf("有短信需要读取!\r\n");
SIM900_TestAT(100);
error = SIM900_GetUnreadSMS(SmsPduBuff, PDU_BUFF_SIZE, &cnt); //读取SIM900所有的未读短信
if(error == AT_RETURN_OK)
{
p = (char *)SmsPduBuff;
while(1) //循环解析所有短信
{
p = (char *)strstr(p, "+CMGL:");
if(p == NULL) break;
else
{
if(GSM_ParsePDUSMS(p, SMS_Buff,cnt-((u32)p - (u32)SmsPduBuff) , &SMS_Info) ==TRUE)
{
uart_printf("\r\n***************************************************\r\n");
uart_printf("短信索引:%d\r\n",SMS_Info.IndexNum); //打印短信索引
uart_printf("电话号码:%s\r\n",SMS_Info.NumBuff); //打印电话号码
uart_printf("发送时间:20%d-%d-%d %d:%d:%d\r\n", SMS_Info.Timer.Year, SMS_Info.Timer.Month, SMS_Info.Timer.Day, SMS_Info.Timer.Hour, SMS_Info.Timer.Minute, SMS_Info.Timer.Second); //打印发送时间
uart_printf("短信长度:%d\r\n",SMS_Info.SMS_Size); //打印发送时间
uart_printf("短信内容:%s\r\n",SMS_Buff); //短信内容
uart_printf("***************************************************\r\n\r\n");