【单片机笔记】上海移远公司NB-IOT模组 BC26 使用STM32 AT命令实现连接阿里云数据上传和下载
前言
在调试之前看这个数据手册一脸懵,特别是MQTT部分还是独立的,这个和前接触到的上海合宙的模块多少有点出处。另外就是那个AT命令的传入参数也是一脸懵,后来发现BC26的模块好像把MQTT部分单独的做成了支持阿里云服务器的功能。接触过阿里云的设备对接相比都知道,阿里云要求的是一机一密或者一型一密,这个在对于简单的成本低廉的MCU来说无疑是一个很大的考验。而BC26这块还是做的非常友好的,在MQTT部分只需要传入产品对应设备下的三元组即可,无需经过哈希算法计算密钥。
先上图:
连接及上传部分:
数据下载部分:
基本上通信也是非常稳定的,不过我这个地方信号贼差,以模块满格31来算,这里测试才有9,10的样子。
整体的应用层思路是这样的:
1、单片机控制模块自检
2、单片机控制模块连接阿里云
3、单片机控制模块定时发送数据并接收下发数据
看上面三个步骤虽然简洁明了,单是要做好单片机的应用底层也不是那么简单,为此我专门谢了一个对应BC26传输机制的地层代码,可以检测BC26模块的状态,连接状态,断线重连、超时复位等机制。详细请看下文代码:
BC26底层驱动部分:
C文件:
#include "fy_bc26.h"
static void ResetModule(void);
static void CheckModule(void);
static void SetCFUN(void);
static void CheckCIMI(void);
static void ActivateNetwork(void);
static void CheckNetwork(void);
static void CheckCSQ(void);
static void QMTCFG(char* PK, char* DN, char* DS);
static void QMTOPEN(void);
static void QMTCONN( char* DN );
static void QMTSUB( char* TOPIC );
static void CheckPubTopic(void);
_typdef_bc26 _bc26;static void ClearFIFO(void) {memset(_bc26.rxbuf,0,256);*(_bc26.rxlen) = 0;
}
void BC26_Configuration(u8 *prx,u16 *rxlen)
{_bc26.rxbuf = prx;*(_bc26.rxlen) = *rxlen;_bc26.timeOver=0;_bc26.csq = 0;_bc26.sta = 0;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( BC26_RST_RCC,ENABLE);GPIO_InitStructure.GPIO_Pin = BC26_RST_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(BC26_RST_PORT, &GPIO_InitStructure);GPIO_ResetBits(BC26_RST_PORT,BC26_RST_PIN);}
static void CheckPubTopic(void) {if(strstr((char *)_bc26.rxbuf,"+QMTPUB: 0,1,0") != NULL) {_bc26.sta = 11;}
}//每300ms执行一次
void BC26_IRQHandler(void) {switch(_bc26.sta) {case 0:ResetModule();break; //复位模块case 1:CheckModule();break; //检查模块case 2:SetCFUN();break; //设置全功能case 3:CheckCIMI();break; //检查卡case 4:ActivateNetwork();break; //激活网络case 5:CheckNetwork();break; //检查网络case 6:CheckCSQ();break; //检查信号强度case 7:QMTCFG(ProductKey,DeviceName,DeviceSecret);break; //配置MQTT参数case 8:QMTOPEN();break; //打开TCP连接case 9:QMTCONN(DeviceName);break; //登录MQTT服务器case 10:QMTSUB(SubTopic);break; //订阅主题case 11:break;case 12:CheckPubTopic(); //发布消息状态检测default:break; //正常运行状态}
}/*** 功能:复位BC26模组* 参数:None* 返回值:* 0 :正常* 其他: 异常*/
static void ResetModule(void) {if(_bc26.timeOver==0) {_bc26.timeOver = 66; //20sGPIO_SetBits(BC26_RST_PORT,BC26_RST_PIN);}else {--_bc26.timeOver;if(_bc26.timeOver == 63){GPIO_ResetBits(BC26_RST_PORT,BC26_RST_PIN);}else if(_bc26.timeOver == 60){BC26_SendString("AT+QRST=1\r\n");LOG("AT+QRST=1\r\n");}else if(_bc26.timeOver == 4) {BC26_SendString("ATE0\r\n"); //关闭回显LOG("ATE0\r\n"); //关闭回显}else if(_bc26.timeOver==0) {_bc26.timeOver = 0; _bc26.sta = 1;ClearFIFO();}}
}/*** 功能:检查BC26模组是否正常* 参数:None* 返回值:* 0 :正常* 其他: 异常*/
static void CheckModule(void) {if(strstr((char*)_bc26.rxbuf,"OK") == NULL) {if(_bc26.timeOver==0) {ClearFIFO();BC26_SendString("AT\r\n");LOG("AT\r\n");_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=2;ClearFIFO();}
}/*** 功能:设置BC26的CFUN功能(默认打开全功能)* 参数:None* 返回值:* 0 :正常* 其他: 异常*/
static void SetCFUN(void) {if(strstr((char*)_bc26.rxbuf,"OK") == NULL) {if(_bc26.timeOver==0) {ClearFIFO();BC26_SendString("AT+CFUN=1\r\n");LOG("AT+CFUN=1\r\n");_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=3;ClearFIFO();}
}/*** 功能:检查BC26的SIM卡是否正常* 参数:None* 返回值:* 0 :正常* 其他: 异常*/
static void CheckCIMI(void) {if(strstr((char*)_bc26.rxbuf,"460") == NULL) {if(_bc26.timeOver==0) {ClearFIFO();BC26_SendString("AT+CIMI\r\n");LOG("AT+CIMI\r\n");_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=4;ClearFIFO();}
}/*** 功能:设置(激活)网络* 参数:None* 返回值:* 0 :正常* 其他: 异常*/
static void ActivateNetwork(void) {if(strstr((char*)_bc26.rxbuf,"OK") == NULL) {if(_bc26.timeOver==0) {ClearFIFO();BC26_SendString("AT+CGATT=1\r\n");LOG("AT+CGATT=1\r\n");_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=5;ClearFIFO();}
}/*** 功能:检查网络激活状态* 参数:None* 返回值:* 0 :正常* 其他: 异常*/
static void CheckNetwork(void) {if(strstr((char*)_bc26.rxbuf,"+CGATT: 1") == NULL) {if(_bc26.timeOver==0) {ClearFIFO();BC26_SendString("AT+CGATT?\r\n");LOG("AT+CGATT?\r\n");_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=6;ClearFIFO();}
}/*** 功能:查看获取CSQ值* 参数:None* 返回值:RSSI信号强度*/
static void CheckCSQ(void) {_bc26.csq = _bc26.rxbuf[6]-'0';_bc26.csq *= 10;_bc26.csq += _bc26.rxbuf[7]-'0';if(_bc26.csq == 0 || _bc26.csq == 99) {ClearFIFO();if(_bc26.timeOver==0) {BC26_SendString("AT+CSQ\r\n");LOG("AT+CSQ\r\n");_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=7;ClearFIFO();}
}static void QMTCFG(char* PK, char* DN, char* DS) {if(strstr((char*)_bc26.rxbuf,"OK") == NULL) {if(_bc26.timeOver==0) {char temp[200];memset( temp, 0, sizeof( temp ) ); //清空 temp,避免隐藏错误strcat( temp, "AT+QMTCFG=\"aliauth\",0,\"" ); //AT+MQTCFG="aliauth",0,"strcat( temp, PK ); //AT+MQTCFG="aliauth",0,"PKstrcat( temp, "\",\"" ); //AT+MQTCFG="aliauth",0,"PK","strcat( temp, DN ); //AT+MQTCFG="aliauth",0,"PK","DNstrcat( temp, "\",\"" ); //AT+MQTCFG="aliauth",0,"PK","DN","strcat( temp, DS ); //AT+MQTCFG="aliauth",0,"PK","DN","DSstrcat( temp, "\"\r\n" ); //AT+MQTCFG="aliauth",0,"PK","DN","DS"\r\nClearFIFO();BC26_SendString(temp);LOG("%s",temp);_bc26.timeOver = 3;}else {--_bc26.timeOver;if(_bc26.timeOver==0) {_bc26.sta = 0;ClearFIFO();}}}else {_bc26.timeOver = 0; _bc26.sta=8;ClearFIFO();}
}
//TCP连接阿里云 注意,连接过程时间是比较长的,在连接过程重复发送连接命令会造成错误
static void QMTOPEN(void) {if(strstr((char*)_bc26.rxbuf,"+QMTOPEN: 0,0") == NULL) {ClearFIFO();if(_bc26.timeOver == 0) {BC26_SendString("AT+QMTOPEN=0,\"iot-as-mqtt.cn-shanghai.aliyuncs.com\",1883\r\n");LOG("AT+QMTOPEN=0,\"iot-as-mqtt.cn-shanghai.aliyuncs.com\",1883\r\n");_bc26.timeOver = 66; //TCP连接加入检测机制 20s时间}else {_bc26.timeOver--;if(_bc26.timeOver == 0) {_bc26.sta=0;}}}else {_bc26.timeOver = 0;_bc26.sta=9;ClearFIFO();}
}//登录阿里云MQTT 注意,连接过程时间是比较长的,在连接过程重复发送连接命令会造成错误
static void QMTCONN( char* DN )
{if(strstr((char*)_bc26.rxbuf,"+QMTCONN: 0,0,0") == NULL) {if(_bc26.timeOver == 0) {char temp[200];memset( temp, 0, sizeof( temp ) ); //清空 temp,避免隐藏错误strcat( temp, "AT+QMTCONN=0,\"" ); //AT+QMTCONN=0,"strcat( temp, DN ); //AT+QMTCONN=0,"DNstrcat( temp, "\"\r\n" ); //AT+QMTCONN=0,"DN"\r\nClearFIFO();BC26_SendString( temp );LOG( "%s",temp );_bc26.timeOver = 66; //MQTT登录连接加入检测机制 20s时间}else {_bc26.timeOver--;if(_bc26.timeOver == 0) {_bc26.sta=0;}}}else {_bc26.sta=10;_bc26.timeOver = 0;ClearFIFO();}
}void QMTSUB( char* TOPIC )
{if(strstr((char*)_bc26.rxbuf,"+QMTSUB: 0,1,0,1") == NULL) {if(_bc26.timeOver == 0) {char temp[200];memset( temp, 0, sizeof( temp ) ); //清空 temp,避免隐藏错误strcat( temp, "AT+QMTSUB=0,1,\"" ); //AT+QMTSUB=0,1,"strcat( temp, TOPIC ); //AT+QMTSUB=0,1,"TOPICstrcat( temp, "\",0\r\n" ); //AT+QMTSUB=0,1,"TOPIC",0\r\nClearFIFO();BC26_SendString( temp );LOG( "%s",temp );_bc26.timeOver = 66; //20s}else {_bc26.timeOver--;if(_bc26.timeOver == 0) {_bc26.sta=8;}}}else {_bc26.timeOver = 0; _bc26.sta=11;ClearFIFO();}
}/* 此处的KeyWord 是阿里云“功能定义”->“添加功能”->所选功能的标识符号*/
void BC26_PubTopic( char* Topic,char *KeyWord,char* value )
{if(_bc26.sta == 11) { //空闲状态可以发布消息char temp[200];memset( temp, 0, sizeof( temp ) ); //清空 temp,避免隐藏错误strcat( temp, "AT+QMTPUB=0,1,1,0,\"" ); //AT+QMTPUB=0,0,0,0,"strcat( temp, Topic ); //AT+QMTPUB=0,0,0,0,"Topicstrcat( temp, "\",\"{params:{" ); //AT+QMTPUB=0,0,0,0,"Topic","{params:{strcat( temp, KeyWord ); //AT+QMTPUB=0,0,0,0,"Topic","{params:{KeyWordstrcat( temp, ":" ); //AT+QMTPUB=0,0,0,0,"Topic","{params:{KeyWord:strcat( temp, value ); //AT+QMTPUB=0,0,0,0,"Topic","{params:{KeyWord:valuestrcat( temp, "}}\"" ); //AT+QMTPUB=0,0,0,0,"Topic","{params:{KeyWord:value}}"strcat( temp, "\r\n" ); //AT+QMTPUB=0,0,0,0,"Topic","{params:{KeyWord:value}}"ClearFIFO();BC26_SendString( temp );LOG( "%s",temp );_bc26.timeOver = 33; //10s}else if(_bc26.sta == 12) {if(_bc26.timeOver) {--_bc26.timeOver;if(_bc26.timeOver == 0) {printf("Topic Error!\r\n");_bc26.sta = 11;}}}
}/*********************************************END OF FILE********************************************/
H文件:
#ifndef __FY_BC26_H
#define __FY_BC26_H#include "fy_includes.h"#define BC26_RST_RCC RCC_APB2Periph_GPIOB
#define BC26_RST_PORT GPIOB
#define BC26_RST_PIN GPIO_Pin_3#define BC26_SendString Usart2_SendString
#define BC26_SendBuf Usart2_SendBuf/*阿里云服务器地址(华东2):*.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883 *ProductKeyhmacsha1加密在线计算网站:http://encode.chahuo.com/客户端ID: *|securemode=3,signmethod=hmacsha1| *设备名称用户名 : *&# *设备名称 #ProductKey密码 : 用DeviceSecret作为密钥对clientId*deviceName*productKey#进行hmacsha1加密后的结果 *设备名称 #ProductKey*/
域名(华东2)
//#define DomainName "%s.iot-as-mqtt.cn-shanghai.aliyuncs.com" //ProductKey
端口
//#define Port 1883//设备三元组 根据自己的填写就好
#define ProductKey "xxx"
#define DeviceName "xxx"
#define DeviceSecret "xxx"//MQTT 发布主题
#define PubTopic "/sys/xxx/xxx/thing/event/property/post" //ProductKey DeviceName
//MQTT 订阅主题
#define SubTopic "/sys/xxx/xxx/thing/service/property/set" //ProductKey DeviceName#define CurrentTemperature "CurrentTemperature"
#define CurrentHumidity "CurrentHumidity"
#define TPSet "TPSet"typedef struct {u8 *rxbuf;u16 *rxlen;u16 timeOver;u8 csq;u8 sta;
} _typdef_bc26;extern _typdef_bc26 _bc26;void BC26_Configuration(u8 *rxbuf,u16 *rxlen);
void BC26_IRQHandler(void);
void BC26_PubTopic( char* Topic,char *KeyWord,char* value );#endif/*********************************************END OF FILE********************************************/
上述代码主要的功能实现在于BC26的IRQHandler函数,这个函数在主程序中是每300ms执行一次,为什么是300ms?这个数据手册给出了定义:
可以看到几乎每条AT指令的响应时间最大都是300ms,所以这里索性就300ms。
然后再来看看主程序部分:
#include "fy_includes.h"
#include "hmac_sha1.h"//实验17
//BC26-MQTT-ALIYUN
//技术交流群:733945348
//作者:Urien @MARS
//日期:2020/1/3u8 u2_rxbuf[256];
u16 u2_rxlen=0;
u8 u2_rxok=0;
u16 cnt300ms=0;
u16 cnt5s=0;
int main(void)
{float tempvalue;char tempstr[20];NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4 SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO时钟GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //禁止JTAG保留SWDSystick_Configuration();Led_Configuration();Usart1_Configuration(115200);Usart2_Configuration(115200);printf("this is aliyun test!\n\n");BC26_Configuration(u2_rxbuf,&u2_rxlen);while(1){if(cnt300ms>=300 || u2_rxok){Led_Tog();if(u2_rxok){Usart1_SendBuf(u2_rxbuf,u2_rxlen);}cnt300ms = 0;u2_rxok = 0;BC26_IRQHandler();u2_rxlen=0;}Led_Tog();if(cnt5s == 5000){srand(SysTick->VAL);tempvalue = 0.1*(rand()%1200);sprintf(tempstr,"%.1f",tempvalue);BC26_PubTopic(PubTopic,CurrentTemperature,tempstr);}else if(cnt5s == 10000){srand(SysTick->VAL);tempvalue = 0.1*(rand()%1000);sprintf(tempstr,"%.1f",tempvalue);BC26_PubTopic(PubTopic,CurrentHumidity,tempstr);}else if(cnt5s == 15000){cnt5s=0;srand(SysTick->VAL);tempvalue = (rand()%200);sprintf(tempstr,"%d",(u8)tempvalue);BC26_PubTopic(PubTopic,TPSet,tempstr);}}
}u8 rx_buf[256];
u8 rx_len=0;
//USART1串口中断函数
void USART1_IRQHandler(void)
{if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){rx_buf[rx_len++] = USART1->DR;}if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){u8 clear = USART1->DR;//读DR和SR有效清除中断clear = USART1->SR;u2_rxok=1;}
}//USART1串口中断函数
void USART2_IRQHandler(void)
{if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){u2_rxbuf[u2_rxlen++] = USART2->DR;u2_rxlen%=256;}if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET){u8 clear = USART2->DR;//读DR和SR有效清除中断clear = USART2->SR;u2_rxok=1;}
}
主程序比较简单,使用系统滴答的1ms时基定时中断用来简单计时,然后就是每300ms执行BC26的IRQHandler函数,每500ms上传一次数据。注意这里因为简单测试,上传我只是把单一的数据点一个个分时上传,为了提高效率,完全可以上传多个或者所有的数据点。
再有就是串口部分,串口部分用到了2个,串口1用来打印调试信息,串口2才是对接BC26模组的,两个串口都用到了串口的接收中断和空闲中断。空闲中断不宜耗时太长,所以我这里只是立了一个flag变量,放在while里面去处理。
最后要说明一下,关于BC26的底层部分,如果是参考上述代码的话建议多啃啃,难就难在传输机制上面,举一个简单的例子,TCP连接上如果AT发送了TCP连接后,模组并不是马上或者就很快能连上服务器的,各种原因都有,网络延迟啦,信号不好啦等等,那么这个时候再次发送连接命令是会报错的,即使连接成功也一样。MQTT的登录也一样。再有就是BC26的复位,建议是软件复位和硬件复位都接上。
By Urien 2020年1月8日 18:44:04
【单片机笔记】上海移远公司NB-IOT模组 BC26 使用STM32 AT命令实现连接阿里云数据上传和下载相关推荐
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之02【EC20模组硬件供电和开关机复位操作】
QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之02[EC20模组硬件供电和开关机复位操作] 一.模组硬件知识 1.背景知识:网络制式 2.模组的供电 3.模组的开机 4 ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之03【EC20模组基础串口指令说明】
QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之03[EC20模组基础串口指令说明] 一.准备工作 二.硬件环境的搭建 三.基础AT指令的说明和测试 STM32F103学 ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之04【EC20模组SIM卡和驻网模组指令说明】
QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之04[EC20模组SIM卡和驻网模组指令说明] 一.准备工作 二.硬件环境的搭建 三.基础AT指令的说明和测试 关于SIM ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之05【EC20模组TCP/IP模块AT指令说明】
QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之05[EC20模组TCP/IP模块AT指令说明] Socket长连接 一.准备工作 二.针对TCP/IP协议 关于TCP/ ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之01物联网模组简介
本系列博文将系统性讲解物联网模组系列的实际使用和调试指南,以移远4G模组EC20为例(其他厂家模组的调试方法大同小异),加快嵌入式软硬件工程师对物联网模组调试和使用的上手速度,让你们的项目进度尽可能快 ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之02EC20模组硬件供电和开关机复位操作
本系列博文将系统性讲解物联网模组系列的实际使用和调试指南,以移远4G模组EC20为例(其他厂家模组的调试方法大同小异),加快嵌入式软.硬件工程师对物联网模组调试和使用的上手速度. 一.模组硬件知识 1 ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之03EC20模组基础串口指令说明
本系列博文将系统性讲解物联网模组系列的实际使用和调试指南,以移远4G模组EC20为例(其他厂家模组的调试方法大同小异),加快嵌入式软.硬件工程师对物联网模组调试和使用的上手速度. 一.准备工作 1.软 ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之【05EC20模组TCP/IP模块AT指令说明】
本系列博文将系统性讲解物联网模组系列的实际使用和调试指南,以移远4G模组EC20为例(其他厂家模组的调试方法大同小异),加快嵌入式软.硬件工程师对物联网模组调试和使用的上手速度. 一.准备工作 1.软 ...
- QUECTEL上海移远4G通讯CAT4模组EC20CEFAG模块串口调试指南之04EC20模组SIM卡和驻网模组指令说明
本系列博文将系统性讲解物联网模组系列的实际使用和调试指南,以移远4G模组EC20为例(其他厂家模组的调试方法大同小异),加快嵌入式软.硬件工程师对物联网模组调试和使用的上手速度. 一.准备工作 1.软 ...
最新文章
- The following module was built either with optimizations enabled or without debug information - winz
- 鲲鹏服务器的作用,眼见为实,华为鲲鹏架构服务器生态大揭秘
- eclipse 下编写java code 比较好的设置和快捷键
- API文档和代码片段管理器:​​​​Dash
- 通过tomcat配置solr 4.10.3
- MThings:ModbusTCP通讯调试调测工具助手
- 游程编码用matlab实现代码_二值图像游程编码算法的Matlab实现 -
- 计算机cpu后面字母代表什么意思,英特尔CPU型号中最后的字母什么意思?如有不懂欢迎驻足停留...
- android 验证手机、邮箱格式
- 谁说大象不能跳舞读后感
- 图像处理与计算机视觉:基础,经典以及最近发展(转)
- gtk之G_LIKELY(expr)和G_UNLIKELY(expr)
- kettle 9.x 版本连接资源库,资源库灰色
- 将Opera默认搜索引擎改为百度
- 简单易懂的P2P通信原理
- 【论文摘要】一种基于NSPD-DCT域变参数混沌映射的零水印新方案
- HTML罕见的冷门标签
- Laravel Debugbar
- 那些职场高手,都是怎么解决问题的?
- 00软考 中级 信息安全工程师考试介绍