#include "ec20module.h" #include "stm32f10x_gpio.h" #include "sysport.h" #include "syslib.h" #include "io.h" /******************************************************************************** * @file ec20module.c * @author 晏诚科技 Mr.Wang * @version V1.0.0 * @date 11-Dec-2018 * @brief 提供Quectel模块EC20模块级驱动程序(控制IO、电源开关、回码、握手、CSQ、ICCID等) ****************************************************************************** * @attention * 外部调用EC20_Module_Init()对模块进行初始化,此函数如果返回失败则说明MCU无法与EC20通过串口通信 可能是EC20供电、或者模块没有安装就位导致的。 * 网络通信前模块需要先查询SIM是否就绪,接着注册到CS Service和PS service, * 最后才能配置相关通信协议实现通讯 *******************************************************************************/ /***************************************** *ec20 module相关的常变量 ****************************************/ ModuleParam_s sEc20Param ; //定义存放EC20模块参数的结构体 volatile bool httpDataMode = false ; //串口模式:接收http数据模式 volatile bool ftpDataMode = false ; //串口模式:接收ftp数据模式 char ec20AtBuf[EC20_ATBUF_LEN] ; //EC20 AT命令回码存放缓冲区 char ec20HttpBuf[EC20_HTTPBUF_LEN] ; //EC20 HTTP返回数据存放缓冲区 char ec20FtpBuf[EC20_FTPBUF_LEN] ; //EC20 FTP返回数据存放缓冲区 volatile int ec20FtpBufIndex = 0 ; //ec20FtpBuf缓冲区写指针 FrameQueue_s sTcpQueue ; //环形帧缓冲区,存储模块返回的tcp相关数据 FrameQueue_s sUrcQueue ; //环形帧缓冲区,存储模块返回的URC数据 /******************************************************** ec20模块Module指令相关处理 *********************************************************/ enum eModuleCmdNum { TURNOFF = 0, HANDSHAKE = 1, QUERYCONFIG = 2, CLOSEECO =3, SAVECONFIG =4, QUERYRELESE =5, QUERYVOLTAGE=6 } ; //枚举ec20模块Module相关指令 volatile EC20_CMD_DATA_s sModuleCmd[7]= { // cmdNum cmdStr, timeout trueStr trueOffset falseStr revResult rtyNum {TURNOFF, "AT+QPOWD\r\n", (65*10), "POWERED DOWN", -1, "ERROR", TIMEOUT, 2 }, //关机命令 {HANDSHAKE, "AT\r\n", 10, "OK" , -1, "ERROR", TIMEOUT, 3 }, //握手指令 {QUERYCONFIG, "AT&V\r\n", 3, "E: 0" , -1, "E: 1", TIMEOUT, 1 }, //查询EC20配置命令AT&V {CLOSEECO, "ATE0\r\n", 3, "OK" , -1, "ERROR", TIMEOUT, 2 }, //关闭命令回显ATE0命令 {SAVECONFIG, "AT&W\r\n", 3, "OK" , -1, "ERROR", TIMEOUT, 2 }, //保存配置命令 {QUERYRELESE, "AT+GMR\r\n", 3, "OK" , -1, "ERROR", TIMEOUT, 2 }, //查询EC20软件版本号命令 {QUERYVOLTAGE,"AT+CBC\r\n", 10, "+CBC" , -1, "ERROR", TIMEOUT, 2 } //查询EC20电压命令 } ; //EC20模块module相关指令的EC20_CMD_DATA_s结构体类型参数 /************************************************************************************************** * 名 称: static const char *ModuleCmdNumToString(enum eModuleCmdNum result) * 功能说明: 输出枚举成员名的字符串指针。 * 入口参数: eModuleCmdNum类型的枚举 * 出口参数: 为枚举的成员名字符串指针 **************************************************************************************************/ static const char *ModuleCmdNumToString(enum eModuleCmdNum result) { switch (result) { ENUM_CHIP_TYPE_CASE(TURNOFF) ENUM_CHIP_TYPE_CASE(HANDSHAKE) ENUM_CHIP_TYPE_CASE(QUERYCONFIG) ENUM_CHIP_TYPE_CASE(CLOSEECO) ENUM_CHIP_TYPE_CASE(SAVECONFIG) ENUM_CHIP_TYPE_CASE(QUERYRELESE) ENUM_CHIP_TYPE_CASE(QUERYVOLTAGE) } return "无此命令"; } /************************************************************************************************** * 名 称: void Ec20AtBufReset(void) * 功能说明: ec20AtBuf缓冲区初始化 **************************************************************************************************/ void Ec20AtBufReset(void) { memset(ec20AtBuf, 0, EC20_ATBUF_LEN) ; } /************************************************************************************************** * 名 称: void Ec20HttpBufReset(void) * 功能说明: ec20HttpBuf缓冲区初始化 **************************************************************************************************/ void Ec20HttpBufReset(void) { memset(ec20HttpBuf, 0, EC20_HTTPBUF_LEN) ; } /************************************************************************************************** * 名 称: void Ec20FtpBufReset(void) * 功能说明: ec20FtpBuf缓冲区初始化 **************************************************************************************************/ void Ec20FtpBufReset(void) { ec20FtpBufIndex = 0 ; memset(ec20FtpBuf, 0, EC20_FTPBUF_LEN) ; } /************************************************************************************************** * 名 称: void Ec20ReceiveFrameCallback(void) * 功能说明: EC20模块——>MCU串口3接收空闲中断回调函数 * 将EC20模块返回的数据分为三大类:1、http相关的数据;2、tcp相关的数据;3、模块URC相关的数据。 **************************************************************************************************/ void Ec20ReceiveFrameCallback(char *recvBuf, uint16_t recvLen) { int pos = 0; //printf("\r\nEC20:%s", buf) ; // if( httpDataMode == true ) /*串口接收模式为:接收HTTP数据*/ // { // strncat(ec20HttpBuf, recvBuf, recvLen) ; //// SysLog("COM3 RxLen:%d; RxBuf:%s", len, uart3_Dma_Rx_Buf); // } if( ftpDataMode == true ) /*串口接收模式为:接收HTTP数据*/ { memcpy((ec20FtpBuf+ec20FtpBufIndex), recvBuf, recvLen) ; ec20FtpBufIndex += recvLen ; } else if ( kmp(recvBuf, "+QIURC:")>=0 ) /*判断是否为TCP下行数据或者URC*/ { pos = kmp(recvBuf, "+QIURC: \"recv\"") ; if( pos>=0) /*判断是否为接收到TCP下行数据*/ { if( RW_OK != InsertQueueMemData(&sTcpQueue, recvBuf, recvLen) ) { ErrorLogPrintf("%s,%d:sTcpQueue spillover!",__FILE__, __LINE__) ; } } else /*判断是模块返回的URC*/ { if( RW_OK != InsertQueueMemData(&sUrcQueue, recvBuf, recvLen) ) { ErrorLogPrintf("%s,%d:sUrcQueue spillover!",__FILE__, __LINE__) ; } } } else //EC20模块AT命令回码 { if( recvLen < (EC20_ATBUF_LEN-strlen((const char*)ec20AtBuf))) /*ec20AtBuf剩余长度足以存贮recvBuf*/ { strcat((char*)ec20AtBuf, recvBuf) ; //将接收到的AT命名回码数据存储到ec20AtBuf中去 } else /*ec20AtBuf剩余长度不足以存贮recvBuf*/ { ErrorLogPrintf("%s,%d:ec20AtBuf spillover. ",__FILE__, __LINE__) ; //报错 Ec20AtBufReset() ; //先清空 ec20AtBuf strncpy((char*)ec20AtBuf, recvBuf, EC20_ATBUF_LEN) ; //再将recvBuf中的数据拷贝到ec20AtBuf中去 } } } /************************************************************************************************** * 名 称: RunResult EC20_Module_Init( void ) * 功能说明: EC20模块初始化(IO初始化、供电、关闭回显、握手、查询SIM卡号、注册CS\PS网络、查询CSQ) * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Module_Init( void ) { RunResult runResult = TIMEOUT ; EC20_Uart_Init() ; //EC20_UART串口初始化,初始化接收缓冲区、注册串口回调函数 EC20_GPIO_Init() ; //MCU与EC20相关IO初始化、电源使能引脚、复位引脚 EC20_START() ; //EC20开机流程 EC20_Handshake() ; //EC20串口握手 runResult = EC20_CloseEcho() ; //EC20关闭命令回显 EC20_Query_SoftRelese(sEc20Param.ec20SoftVer) ; //查询EC20软件版本号并通过debug串口输出 AppLogPrintf("EC20 软件版本:%.*s", EC20_VER_LEN, sEc20Param.ec20SoftVer) ; EC20_Query_Voltage(sEc20Param.ec20Voltage) ; //查询EC20供电电压并输出(实测在EC20刚刚启动阶段查询不到电压值) AppLogPrintf("EC20 供电电压:%s mV", sEc20Param.ec20Voltage) ;// EC20_CLOSE() ; //AppLogPrintf("EC20 供电电压:%.*smV", EC20_VOL_LEN, sEc20Param.ec20Voltage) ;// EC20_CLOSE() ; return(runResult) ; } /************************************************************************************************** * 名 称: RunResult EC20_SendModuleCmd( uint8_t cmdNum, char *format,... ) * 功能说明: MCU串口向EC20发送Module相关命令 * 入口参数: * @param1 cmdNum EC20_CMD_DATA_s中cmdNum成员命令编号 * @param2 char *format,... 可变参变量 * 出口参数: * @param status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_SendModuleCmd( uint8_t cmdNum, char *format,... ) { uint8_t revTimes = 0 ; RunResult status = TIMEOUT ; uint8_t retryTimes = sModuleCmd[cmdNum].rtyNum ; char *cmdPack = NULL ; format = sModuleCmd[cmdNum].cmdStr ; cmdPack = portMalloc(MODULE_CMDPACK_LEN*sizeof(uint8_t)) ; va_list ap; va_start (ap, format); int outLen = vsnprintf(cmdPack, MODULE_CMDPACK_LEN, (const char*)format, ap); //vsprintf (temp, cmd, ap); //到此为止,所有的参数情况已经汇总到temp了 if((outLen<=0)||( outLen > MODULE_CMDPACK_LEN)) { ErrorLogPrintf("%s,%d:cmdPack spillover!",__FILE__, __LINE__) ; va_end (ap); portFree(cmdPack) ; return RUNERR ; } while(retryTimes--) //命令失败重试 { Ec20AtBufReset() ; //ec20AtBuf缓冲区清空 revTimes = 0 ; //轮询计数器清零 UARTx_SendData(EC20_UART, cmdPack, outLen) ; //EC20_UART发送AT命令 while( revTimes++ < sModuleCmd[cmdNum].timeout ) /*在命令超时时间内一直在ec20AtBuf缓冲区内循环查找trueStr*/ { Wait_For_Nms(10) ; //轮询查找时间间隔 sModuleCmd[cmdNum].trueOffset = kmp(ec20AtBuf, sModuleCmd[cmdNum].trueStr) ; //获取sModuleCmd[cmdNum].trueStr在ec20AtBuf中的偏移值 if( sModuleCmd[cmdNum].trueOffset >= 0) /*在ec20AtBuf中获取到sModuleCmd[cmdNum].trueStr字串*/ { status = RUNOK ; goto OUT ; } else if( kmp(ec20AtBuf, sModuleCmd[cmdNum].falseStr) >= 0)/*在ec20AtBuf中没有获取到sModuleCmd[cmdNum].trueStr字串*/ { status = RUNERR ; goto OUT ; } } Wait_For_Nms( 200+( sModuleCmd[cmdNum].rtyNum-retryTimes)*100 ) ; //失败后延时一段时间再次发起请求命令 } OUT: portFree(cmdPack) ; //释放内存空间 va_end (ap) ; //释放内存空间 DebugLogPrintf("%s %s", ModuleCmdNumToString((enum eModuleCmdNum)cmdNum), RunResultToString(status) ) ; //输出命令执行结果 return (status) ; //返回函数执行结果 } /************************************************************************************************** * 名 称: void EC20_Uart_Init(void) * 功能说明: 初始化EC20_UART,初始化环形缓冲区、注册EC20_UART串口帧中断回调函数 **************************************************************************************************/ void EC20_Uart_Init(void) { UARTx_Init(EC20_UART, 115200, USART_Mode_Rx | USART_Mode_Tx, INT_RANK_1) ; //MCU与EC20通讯的串口初始化,设置为收发模式,中断优先级INT_RANK_1 Ec20AtBufReset() ; //ec20AtBuf缓冲区初始化 Ec20HttpBufReset() ; //ec20HttpBuf缓冲区初始化 InitQueueMem(&sTcpQueue) ; //初始化环形帧缓冲区,存储模块返回的tcp相关数据 InitQueueMem(&sUrcQueue) ; //初始化环形帧缓冲区,存储模块返回的URC数据 Uart_RegHookCallback(EC20_UART, Ec20ReceiveFrameCallback) ; //注册EC20接收帧中断回调函数 } /************************************************************************************************** * 名 称: void EC20_GPIO_Init( void ) * 功能说明: EC20模块相关IO初始化 **************************************************************************************************/ void EC20_GPIO_Init( void ) { Gpio_Init(EC20_POW_PORT, EC20_POW_PIN, GPIO_Mode_Out_PP) ; EC20_POWOFF() ; //初始化时EC20初始为断电状态 Gpio_Init(EC20_RST_PORT, EC20_RST_PIN, GPIO_Mode_Out_PP) ; //PERST上拉 = PE15拉低 EC20_RST = 0 ; } /**************************************************************************** * 名 称:void EC20_POWON(void) * 功 能:打开EC20模块供电电源 ****************************************************************************/ void EC20_POWON(void) { // Wait_For_Nms(3000) ; //开机前保持3S断电状态 EC20_POW = 1 ; } /**************************************************************************** * 名 称:void EC20_POWOFF(void) * 功 能:关闭EC20模块供电电源 ****************************************************************************/ void EC20_POWOFF(void) { EC20_POW = 0 ; Wait_For_Nms(100) ; //断电后保持5S断电状态,因为模块内核可能由于电容放电没有完全断电清除网络参数 } /**************************************************************************** * 名 称:RunResult EC20_START(void) * 功 能:EC20开机启动 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 * 说 明:EC20上电大概15S内串口会输出“RDY”字符串 ****************************************************************************/ RunResult EC20_START(void) { uint8_t findCount = 0 ; RunResult runStatus = TIMEOUT ; EC20_POWON() ; //开启EC20电源 Ec20AtBufReset() ; //ec20AtBuf缓冲区初始化 while( findCount < 20) //循环等待EC20开机标志 { findCount++ ; Wait_For_Nms(10) ; if( kmp(ec20AtBuf, "RDY") >= 0) /*收到EC20返回的”RDY“字符串,EC20开机完成*/ { DebugLogPrintf("EC20 Start Waited:%d S", findCount ) ; runStatus = RUNOK ; break ; } } return(runStatus) ; } /**************************************************************************** * 名 称:RunResult EC20_CLOSE(void) * 功 能:EC20关机流程 软关机-->等待1S-->断电 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 ****************************************************************************/ RunResult EC20_CLOSE(void) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendModuleCmd(TURNOFF, NULL ) ; if( RUNOK != runStatus ) { ErrorLogPrintf("EC20 软关机失败,即将断电关机!") ; } Wait_For_Nms(10) ; //《AT Command》手册中建议延时1s后再断电 EC20_POWOFF() ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_Handshake( void ) * 功能说明: MCU串口发送AT指令,验证串口通信是否就绪 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Handshake( void ) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendModuleCmd(HANDSHAKE, NULL ) ; return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_CloseEcho(void) * 功能说明: 关闭EC20指令回码 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_CloseEcho(void) { RunResult runStatus = TIMEOUT ; runStatus = EC20_SendModuleCmd( QUERYCONFIG, NULL ) ; if( runStatus != RUNOK ) { EC20_SendModuleCmd( CLOSEECO, NULL ) ; //关闭串口AT命令ECHO runStatus = EC20_SendModuleCmd( QUERYCONFIG, NULL ) ; // EC20_SendModuleCmd( SAVECONFIG, sModuleCmd[SAVECONFIG].cmdStr ) ; //为了兼容前面版本,不保存 } return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_Query_SoftRelese(char *version) * 功能说明: 查询EC20的固件版本 * 入口参数: * @param1 *version 存放version的内存地址 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Query_SoftRelese(char *version) { RunResult runStatus = TIMEOUT ; char *start ; memset(version, 0, EC20_VER_LEN+1); runStatus = EC20_SendModuleCmd(QUERYRELESE, NULL ) ; if( RUNOK == runStatus ) { start = strchr( (const char*)ec20AtBuf, 0x0D ) ; CopyValues((uint8_t*)version, (uint8_t*)(start+2), 0x0D, EC20_VER_LEN) ; } return (runStatus) ; } /************************************************************************************************** * 名 称: RunResult EC20_Query_Voltage(char *voltage) * 功能说明: 查询EC20的供电电压 * 入口参数: * @param1 *voltage 存放voltage的内存地址 * 出口参数: * @param1 status RunResult枚举类型变量,返回函数运行结果 **************************************************************************************************/ RunResult EC20_Query_Voltage(char *voltage) { RunResult runStatus = TIMEOUT ; char *start ; memset(voltage, 0, EC20_VOL_LEN+1); runStatus = EC20_SendModuleCmd(QUERYVOLTAGE, NULL ) ; if( RUNOK == runStatus ) { start = strrchr( (const char*)ec20AtBuf, ',' ) ; CopyValues((uint8_t*)voltage, (uint8_t*)start+1, 0x0D, EC20_VOL_LEN) ; } return (runStatus) ; }