实现了收发短信,并且支持字符短信和PDU格式短信,支持电话拨打与接听
长期工作稳定
//SIM900.C
/************************************************************************************************************* * 文件名: SIM900.c * 功能: STM32 SIM900底层驱动函数 * 作者: [email protected] * 创建时间: 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:短信中心号码长度 * 返回 : 无 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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编码,一般为汉字编码 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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:字符偏移 * 返回 : 字符串大小 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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:超时 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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:短信发送失败 *依赖 : 底层 *作者 : [email protected] *时间 : 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相关的硬件 * 参数 : 无 * 返回 : 无 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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硬件开机 * 参数 : 无 * 返回 : 无 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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硬件关机 * 参数 : 无 * 返回 : 无 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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硬件复位 * 参数 : 无 * 返回 : 无 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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:初始化成功 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:通信成功 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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 * 依赖 : 无 * 作者 : [email protected] * 时间 : 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:错误,其它:短信数量 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:失败; * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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); *作者 : [email protected] *时间 : 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:状态 *依赖 : 短信读取与解析 *作者 : [email protected] *时间 : 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:失败 *依赖 : 无 *作者 : [email protected] *时间 : 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:电话号码长度 *返回 : 字符长度 *依赖 : 底层宏定义 *作者 : [email protected] *时间 : 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:字符号码长度 *返回 : 电话长度 *依赖 : 底层宏定义 *作者 : [email protected] *时间 : 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) *作者 : [email protected] *时间 : 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); *作者 : [email protected] *时间 : 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进制数字位数 *返回 : 转换后的数字 *依赖 : 无 *作者 : [email protected] *时间 : 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进制数字位数 *返回 : 无 *依赖 : 无 *作者 : [email protected] *时间 : 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进制数字位数 *返回 : 转换后的数字 *依赖 : 无 *作者 : [email protected] *时间 : 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:通信成功 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:通信成功 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:通信成功 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:通信成功 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:信号强度; * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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 * 依赖 : 底层 * 作者 : [email protected] * 时间 : 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:短信发送失败 *依赖 : 底层 *作者 : [email protected] *时间 : 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:拨打电话状态 *依赖 : 底层 *作者 : [email protected] *时间 : 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:电话接通状态 *依赖 : 底层 *作者 : [email protected] *时间 : 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:电话接通状态 *依赖 : 底层 *作者 : [email protected] *时间 : 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:失败 *依赖 : 底层 *作者 : [email protected] *时间 : 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底层驱动函数 * 作者: [email protected] * 创建时间: 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 * 功能: 汉字编码转换 * 作者: [email protected] * 创建时间: 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:初始化成功;其它:初始化失败 * 依赖 : 底层读写函数 * 作者 : [email protected] * 时间 : 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 * 依赖 : 底层读写函数 * 作者 : [email protected] * 时间 : 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 * 依赖 : 底层读写函数 * 作者 : [email protected] * 时间 : 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:初始化成功;其它:初始化失败 * 依赖 : 底层读写函数 * 作者 : [email protected] * 时间 : 2013-04-18 * 最后修改时间 : 2013-04-18 * 说明 : 无 *************************************************************************************************************************/ u8 GBK_UNICODE_Init(void) { return 0; } /************************************************************************************************************************* * 函数 : u16 OneGBKtoUNICODE(u16 GBKCode) * 功能 : 将GBK编码转换为unicode编码 * 参数 : GBK * 返回 : unicode * 依赖 : 底层读写函数 * 作者 : [email protected] * 时间 : 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 * 依赖 : 底层读写函数 * 作者 : [email protected] * 时间 : 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 * 作者 : [email protected] * 时间 : 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 * 作者 : [email protected] * 时间 : 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 * 功能: 汉字编码转换 * 作者: [email protected] * 创建时间: 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");