实现了收发短信,并且支持字符短信和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();                       //延时100msSIM900_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();                       //延时100msSIM900_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;          //起始偏移为0while(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);                                 //取消SLEEPSIM900_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最大999msTimeOut:等待超时时间,单位字节超时时间
* 返回                :   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);   //等待响应,超时1000msif(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,"",34if(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,2BpInfo->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;                                         //跳过短信长度字节,2Bif(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后占用2BpInfo->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-0x0Fpucode[cnt ++] = chTmp-0x0A+'A';       //! 'A'-'F'else                                 //! 0x00-0x09pucode[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",145SIM900_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,4SIM900_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,0SIM900_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,1SIM900_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,长度为每个数字半字节,不足补Ftemp = (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;//! PDUpPDU[OffsetCnt++] = '1';pPDU[OffsetCnt++] = '1';//! For MRpPDU[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 PIDpPDU[OffsetCnt++] = '0';pPDU[OffsetCnt++] = '0';//! For DCSif(EnableU2S == ENABLE)           //U2S{pPDU[OffsetCnt++] = '0';pPDU[OffsetCnt++] = '8';}else                           //7BIT{pPDU[OffsetCnt++] = '0';pPDU[OffsetCnt++] = '0';}//! For VPpPDU[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进制样式字符串,存储短信长度//endpPDU[OffsetCnt++] = 0x1A;pPDU[OffsetCnt++] = 0x00;SIM900_debug("\r\n%s\r\n",pPDU);//! 下面是发送过程//! ATif(SIM900_TestAT(10) == FALSE)   //串口同步失败{SIM900_WaitSleep(1000);    //等待上一个操作完成}//! ATE0SIM900_SendATcom("ATE0");if(AT_RETURN_OK != SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20)) //等待响应,超时200MS{return FALSE;}//! AT+CMGFSIM900_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 ContentSIM900_ClearRxCnt();                //清除接收缓冲区SIM900_SendString((char *)pPDU);   //发送字符串error = SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 50);if(error == AT_RETURN_ERROR)      //返回错误{  return FALSE;}else{temp = 30;                         //等待短信接收成功,最大等待30SSIM900_ClearRxCnt();              //清除接收缓冲区do{GSM_DelaySer(1);                //延时1Serror = 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 --); }//测试短信//0891683108200705F011000D91685172910098F40008AA086D4B8BD577ED4FE1return 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;
OKMO RINGMO CONNECTED+CDRIND: 0NO CARRIER
*/SIM900_SendATcom(buff);                                               //拨号error = SIM900_GetATResp(SIM900_Buff, &cnt, "OK", 10, 20);               //等待响应,超时200MSif(error == AT_RETURN_ERROR)        //返回错误{  return SIM900_CALL_ERROR;}else{do{GSM_DelaySer(1);                                         //延时1Serror = 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);                                               //延时1Serror = 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);                                               //延时1Serror = 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-7unsigned 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-6unsigned 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

/************************************************************************************************************** 文件名:     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;                   //短信有效内容长度,最大140Bu8             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模块型号,MG323CDMA_MC323  =  1,      //GSM_CDMA模块型号,MC323GSM_SIM900A =  2,      //GSM_CDMA模块SIM900AGSM_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,汉字偏移起点elsereturn 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,汉字偏移起点elsereturn 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"); 

SIM900/SIM900A 模块实现收发短信,打电话相关推荐

  1. micropython 串口写文件_MicroPython通过2G模块串口收发短信

    集成2G通信.定位模组,赋予物联网特性.本例使用M6220,它是一款基于eSIM技术的2G模组,支持GSM/GPRS,提供GPS+北斗双模定位功能,并具备一定的数据处理能力,其2G工作频段有GSM85 ...

  2. 使用计算机扬声器打电话,【IT之家干货店】教你用Win10连接iPhone,在电脑上接打电话、收发短信...

    最近小编在少数派发现了一篇文章,讲的是如何在Win10上与iPhone互联,最终实现在电脑上接打电话.收发短信,这个篇文章切实的解决了小编的使用痛点,IT之家小编亲自试验该方法可行,再此将方法分享出来 ...

  3. android接听ios电话,教程】教你用电脑连接 iPhone 、 Android ,在电脑上接打电话、收发短信。...

    原标题:教程]教你用电脑连接 iPhone . Android ,在电脑上接打电话.收发短信. 你有「手机依赖症」吗?相信大家对自己的手机都是机不离手,眼不离机吧~那么在办公或者学习的时间里,怎么不接 ...

  4. [转]【IT之家干货店】教你用Win10连接iPhone,在电脑上接打电话、收发短信

    最近小编在少数派发现了一篇文章,讲的是如何在Win10上与iPhone互联,最终实现在电脑上接打电话.收发短信,这个篇文章切实的解决了小编的使用痛点,IT之家小编亲自试验该方法可行,再此将方法分享出来 ...

  5. 用电脑发短信_重磅!一个软件实现电脑上接打手机电话、收发短信、传文件、屏幕镜像!...

    点击箭头处"蓝色字",关注我们哦!! Dell Mobile Connect 本身是一款专门为 Dell 指定设备提供的专门为计算机与 iOS 或者 Android 智能手机之间进 ...

  6. 用串口操作手机收发短信总结

    终于完成了用Java操作手机的模块,遇到了不少麻烦,在这里总结一下,也希望对大家有所帮助. 可能有很多人会问,现在的手机与计算机连接都是USB口,那研究串口手机通讯有什么意义?我开始也是这样考虑,所以 ...

  7. 怎样用电脑收发短信?

    急啊!!急啊!!我有一个西门子GSM模块,想用它与电脑串口连接起来,用VB实现与手机收发短信,该如何实现? 手机与手机之间的是用什么样的数据传输格式? 有关于这方面的书籍或网站吗?,请指教!! 急啊! ...

  8. 【物联网】AT指令|AT返回错误|AT 指令 收发短信和GPRS上网 SIM508/548

    目录 常用AT命令解释 AT 指令 收发短信和GPRS上网 SIM508/548 AT指令返回错误 解释 CME错误: CMS错误 CME错误(英文) CMS 错误(英文) 短信通信的几种情况和CMS ...

  9. python GSM模块-电脑接收短信

    文章目录 一.GSM模块: 1. 产品介绍 2.前言:最近做了一个需求,需要接收手机短信验证码.因此我这里考虑到了 GSM 模块 一.GSM模块: 1. 产品介绍 ① 一种具有独立的操作系统的功能模块 ...

  10. []AT 指令 收发短信和GPRS上网 SIM508/548

    原文地址:AT 指令 收发短信和GPRS上网 SIM508/548 作者:半岛鱼 可以使用电脑自带的超级终端发送AT指令,也可以下载串口大师等工具发送. 1.通信模块sim548/508  AT 指令 ...

最新文章

  1. (转)互斥对象锁和临界区锁性能比较 .
  2. 滴滴人脸识别申诉照片怎么拍_滴滴司机理发被停账号,平台规则到底如何遵守才能避免踩坑?...
  3. Git的撤销操作 git checkout -- <file>
  4. 13、Java Swing事件监听:事件处理模型和事件监听器
  5. JavaScript实现InsertionSort插入排序算法(附完整源码)
  6. Linux命令基础3
  7. Chap2-构造函数语意学
  8. 2018python做图形界面哪个库简单_2018年常见的python编程开发库都有哪些类型
  9. layui 日期格式不正确(date、datetime)区别
  10. FPGA学无止境(目录篇)
  11. 本田与索尼宣布将成立合资企业 计划2025年开始销售电动汽车
  12. 2019 fall CS224w:01-intro
  13. 从日志中截取某个时间段的日志分析
  14. 对WordCOM类工厂80070005和8000401a错误分析及解决办法(DCOM)的补充
  15. js版算24点小游戏
  16. 神经网络的BP算法推导详解
  17. 常微分二阶线性齐次微分方程的通解推导
  18. pcb外观维修_5种最常见的PCB维修
  19. python曲线拟合预测_python曲线拟合
  20. @async 注解使主线程不等待

热门文章

  1. python sorted怎么排序_python sorted怎么降序排序
  2. arduino uno连接超声波传感器测距
  3. android课程设计体重测量仪,数字身高体重测量仪毕业设计样本.docx
  4. 概念学习(学习笔记)
  5. SPSS 中介效应检验(图文+数据集)【SPSS 043期】
  6. CorelDRAW最好用免费稳定版本win,mac版本做图设计使用技巧教程
  7. 喜马拉雅FM下载的音频文件保存在哪_怎么导出来
  8. 百词斩不复习_有人用过百词斩和不背单词两款背单词app吗?良心推荐哪一个好一点?...
  9. 使用Python横向合并excel文件的实例
  10. RGB与CMYK以及加色与减色