CAT1模块 EC800M HTTP 使用后续记录

news2025/4/21 8:08:54
记录一下 CAT1 模块EC800 HTTP 使用后续遇到的问题       by  矜辰所致

目录

  • 前言
  • 一、一些功能的完善
    • 1.1 新的交互指令添加
    • 1.2 连不上网络处理
  • 二、问题出现
  • 三、分析及解决
    • 3.1 定位问题
    • 3.2 问题分析与解决
      • 3.2.1 查看变量在内存中的位置
    • 3.3 数据类型说明
      • 3.3.1 常用格式化输出符号
      • 3.3.2 不同平台数据类型定义
  • 结语

前言

此前我写过一篇文章 CAT1模块 EC800M HTTP使用总结记录 详细讲述了,如何使用 EC800M HTTP协议 的应用。

在后续的过程中,根据客户的不同需求,应用有一定的改变,所以进行了一些定制的修改,同时也发现了以前留下的一个 bug , 觉得还是有必要来记录一下,这不是也太久没写文章了,都要生锈了。

所以本文就继续来说明一下CAT1模块 EC800M HTTP 应用的后续问题吧。

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • 一、一些功能的完善
    • 1.1 新的交互指令添加
    • 1.2 连不上网络处理
  • 二、问题出现
  • 三、分析及解决
    • 3.1 定位问题
    • 3.2 问题分析与解决
      • 3.2.1 查看变量在内存中的位置
    • 3.3 数据类型说明
      • 3.3.1 常用格式化输出符号
      • 3.3.2 不同平台数据类型定义
  • 结语

一、一些功能的完善

在前面的使用文章中,我们主要的目的在于介绍 EC800M HTTP 的使用,对于正式使用的产品来说,有一些问题还是需要考虑到的。

1.1 新的交互指令添加

使用 HTTP 协议,客户端想要和服务器交换数据,也只能等到 HTTP POST 以后通过服务器返回的响应来进行交互。这个其实在上一篇文章已经讲到过,所以呢,如果是后续有和服务器数据交互的需求,也只能在 HTTP POST 以后来实现,那对于我们来说,都是在读取 HTTP 响应之后,通过 strstr 函数找到我们需要的数据进行处理,比如下面一段代码:

...
else if(!strcmp(cmd,"AT+QHTTPREAD=2\r\n")){
            printf("%s\r\n", EC800_RX_BUF);   //打印出响应用来参考
            
            char* position = strstr((char*)EC800_RX_BUF, "\"expectGear\":");//先找到expectGear 的位置
            
            if (position != NULL) {
                // Use sscanf to extract the integer value after "expectGear"
                sscanf(position + strlen("\"expectGear\":"), "%hd", &Http_set_mode);
                
                if((Http_set_mode > 0)&&(Http_set_mode < 5)&&(Http_set_mode != Value_Mode)) need_change = TRUE;
                // printf("Http_set_mode is %d\r\n",Http_set_mode);
            } else {
                printf("not found expectGear!!\r\n");
            }

            position = strstr((char*)EC800_RX_BUF, "\"voice\":");//先找到 voice 的位置
            if (position != NULL) {
                // Use sscanf to extract the integer value after "expectGear"
                sscanf(position + strlen("\"voice\":"), "%hhu", &Http_set_voice);
                // printf("Http_set_voice is %d\r\n",Http_set_voice);  
                if(Http_set_voice)//这里要关闭声音
                {
                    Voice_close_state = TRUE;
                    Voice_close_count = 0;
                }
            } else {
                
            }

            CLEAR_EC800_Buffer(EC800_RX_Data);
            return 0;
        }
...

这个问题说明一下即可,没什么难点,可能需要注意的点就是 HTTP 的响应数据量会稍微有点多,达到几百个字节,需要注意自己的串口缓存区的大小,这个在上一篇文章也有说明。但是这一块的代码有个问题确实也是本文要说明的地方,上看的代码已经是修改过的版本。

1.2 连不上网络处理

对于有些网络产品,它具备本地功能,所以在实际工作的时候要根据需求来判断它如果连不上网络是不停联网还是保持本地功能完善。
如果是必须要连上网络才能运行,很简单,可以开启看门口,然后在配网的过程中如果失败重试的时间长一点,那么就能够不停的自动复位。
如果是需要保证没有网络也要使得本地功能正常,然后设备自动定时连接网络,那么就需要考虑好配网失败重试的时间,超时返回标志位,不能等到看门狗自动复位。再在程序中根据配网成功或者失败的标志位进行判断需不需要再次连接网络。比如本次我们就是要修改成这种状态。

所以我们需要在配网操作的时候做一个超时,同时还需要返回状态值,所以我们把配网联网的状态定义一个结构体:

typedef struct
{
	uint8		Ec800_init_state;	
	uint8		Ec800_pdp_prepare_state;	
	uint8		Http_set_url_state;	
} cat1_state_struct;

extern char IMEI[15];
extern cat1_state_struct My_4g_state;

上面的 3个状态分别对应 ec800_initec800_pdp_preparehttp_set_url 3个函数的状态,以前函数没有返回值,现在我们需要加上返回值:

uint8 ec800_init();
int Iot_SendCmd(const char* cmd, char* reply, int wait);
void Iot_SendNOCheck(char* cmd);

uint8 ec800_pdp_prepare();
uint8 http_set_url(char *url);
void http_post_message(const char *message); 

这里使用 ec800_init 作为示例看一下超时的实现:

uint8 ec800_init()
{
    u16 cat1_timeout = 0;

    while(Iot_SendCmd(AT,"OK", 200)){
        HAL_Delay(1);
        cat1_timeout ++;
        if(cat1_timeout >= 2000){
            printf("uart false\r\n");
            return false;   
        }
    }
    cat1_timeout = 0;
...

上面这个原始的逻辑是有点问题的, cat1_timeout 应该是一个重试的次数,每次尝试会等待 200ms ,然后再等待 1ms,再次尝试,一共要尝试 2000 次,那么不考虑程序运行指令的时间来算,都需要 201* 2000 ms 才会超时,除了时间太久了,失败后1ms 就重试也不是那么合理。所以我们简单修改一下:

  while(Iot_SendCmd(AT,"OK", 200)){
        HAL_Delay(100);
        cat1_timeout ++;
        if(cat1_timeout >= 30){
            printf("uart false\r\n");
            return false;   
        }
    }
    cat1_timeout = 0;
    printf("\r\nuart ok\r\n");
    while(Iot_SendCmd(CPIN,"READY", 200)){
        HAL_Delay(100);
        cat1_timeout ++;
        if(cat1_timeout >= 30){
            return false;   
        }
    }

具体的可以根据自己看门狗的设定时间来确定每次 AT 指令的重试次数。

然后我们在具体使用的时候,先定义一个结构体变量,保存我们的联网状态,然后在开机的时候进行配网联网操作,不管成功与否,我们可以得到对应的状态,示例如下:

cat1_state_struct My_4g_state;

...
//省略
...
int main(void)
{
//初始化省略
  MX_IWDG_Init();
  My_4g_state.Ec800_init_state = ec800_init();
  HAL_IWDG_Refresh(&hiwdg);
  printf("Ec800_init_state:%d\r\n",My_4g_state.Ec800_init_state);

  My_4g_state.Ec800_pdp_prepare_state = ec800_pdp_prepare();
  HAL_IWDG_Refresh(&hiwdg);
  printf("Ec800_pdp_prepare_state:%d\r\n",My_4g_state.Ec800_pdp_prepare_state);
 
  My_4g_state.Http_set_url_state = http_set_url(url);
  HAL_IWDG_Refresh(&hiwdg);
  printf("Http_set_url_state:%d\r\n",My_4g_state.Http_set_url_state);
	...
	//省略
	...
	//我这里使用了初始化状态确定是否需要 post
 if(My_4g_state.Ec800_init_state) http_post_message(message);
 while(1){
 	...
 	//定时发送,如果网络状态异常,就重新连接网络,再尝试发送
 	if(send_count >= 180){
      send_count = 0;
      snprintf(message, sizeof(message), pm_message, IMEI,PM10_Data,PM25_Data,PM1_Data,Value_Mode);
      if(My_4g_state.Ec800_init_state) {
        http_post_message(message);
      }
      else {  //重新连接
        My_4g_state.Ec800_init_state = ec800_init();      
        HAL_IWDG_Refresh(&hiwdg);
        My_4g_state.Ec800_pdp_prepare_state = ec800_pdp_prepare();
        HAL_IWDG_Refresh(&hiwdg);
        My_4g_state.Http_set_url_state = http_set_url(url); 
        HAL_IWDG_Refresh(&hiwdg);      
        if(My_4g_state.Ec800_init_state) http_post_message(message);
      }
    }
 	...
 }
}

上面是设备初始化上电后的联网配置示例,上电先配网,然后读取一次数据进行上传,在主函数的 while 循环中,根据自己的需要的周期,定时的进行数据上传,或者根据 My_4g_state.Ec800_init_state 状态,进行重新配置网络操作。

整体来说就是这么一个流程,不复杂,但是用起来却出现了一个小 bug 。

二、问题出现

如果我们 SIM 卡正常,网络信号正常,我们会经历正常的初始化了,得到的My_4g_state3个成员变量都为1,然后他可以正常的进行 POST 操作,但是我测试的时候发现,设备还是会定期联网,但是奇怪的是,它能够正常的进行 POST 操作。

确定的问题是只要运行过了http_post_message函数 ,My_4g_state.Ec800_init_state 就会变成 0 ,如下图:

在这里插入图片描述

简单一想,在实际代码中,http_post_message函数里根本没有改变这个变量的值, 那么出这种问题极大概率的就是数据溢出,最开始想的是不是串口缓存数据溢出,尝试过加大串口缓冲区,没有用。测试下来发现这个 bug 只会改变 1个字节,所以期间采用了一个办法,就是在结构体成员变量上加上一个预留位置,如下图:

typedef struct
{
	uint8       Reserved_state;
	uint8		Ec800_init_state;	
	uint8		Ec800_pdp_prepare_state;	
	uint8		Http_set_url_state;	
} cat1_state_struct;

倒是能够解决,程序逻辑正常的跑,但是这是治标不治本的方式,还是存在 bug 。

三、分析及解决

3.1 定位问题

还是得进一步的分析问题,于是进一步的修改了一下 http_post_message 函数,在每次操作后把My_4g_state.Ec800_init_state 值打印出来,如下 :

void http_post_message(const char *message) {
    int length = strlen(message);
    char at_post[32];
    u16 cat1_timeout = 0;
    snprintf(at_post, sizeof(at_post), "AT+QHTTPPOST=%d,%d,%d\r\n", length, 5, 10);
    printf("five:%d\r\n",My_4g_state.Ec800_init_state);
    while(Iot_SendCmd(at_post,"CONNECT", 500)){
        HAL_Delay(100);
        cat1_timeout ++;
        if(cat1_timeout >= 10){
            return;   
        }
    }
    cat1_timeout = 0; 
    printf("\r\nready to send post message!\r\n%s\r\n", message);
    printf("six:%d\r\n",My_4g_state.Ec800_init_state);
    while(Iot_SendCmd(message,"+QHTTPPOST:", 5000)){  // 
        HAL_Delay(100);
        cat1_timeout ++;
        if(cat1_timeout >= 3){
            printf("http post wrong\r\n");
            return;   
        }
    }
    cat1_timeout = 0;
    printf("\r\nhttp post OK\r\n");
    printf("seven:%d\r\n",My_4g_state.Ec800_init_state);
    HAL_IWDG_Refresh(&hiwdg);
    while(Iot_SendCmd("AT+QHTTPREAD=2\r\n","+QHTTPREAD:", 2000)){
        HAL_Delay(100);
        cat1_timeout ++;
        if(cat1_timeout >= 5){
            printf("read wrong\r\n");
            return;   
        }
    }
    cat1_timeout = 0;
    printf("\r\nHTTPREAD OK\r\n");
    printf("eight:%d\r\n",My_4g_state.Ec800_init_state); 
}

测试结果如下:

在这里插入图片描述

通过上面测试,已经可以直接定位到云运行过while(Iot_SendCmd("AT+QHTTPREAD=2\r\n","+QHTTPREAD:", 2000)) 后,My_4g_state.Ec800_init_state 的值就变成了 0 ,我们看看上面这条语句会做什么工作,我只把 HTTPREAD 相关的部分代码截取出来:

int Iot_SendCmd(const char* cmd, char* reply, int wait)
{
	u8 i=0;
    char* rss_str;
    int rssi,res;
    CLEAR_EC800_Buffer(EC800_RX_Data);

    Uart3_sendBuffer((u8*)cmd,strlen(cmd));
    
    /*
    此处串口回的不止是一帧数据,所以使用 IDLE 中断不合适
    */
    if ((!strcmp(reply,"+QHTTPREAD:"))||(!strcmp(reply,"+QHTTPPOST:"))){
        //读取和发送的处理,直接等一段时间
        HAL_Delay(1000);// 500 600 800 1000 一直加大   
    }
    /*
        另外的设置指令大多都是等待一个 OK 返回,属于一帧数据
        所以可以用 IDLE 中断
    */
    else{
			...
    }
    EC800ReceiveState = false;
    if (!strcmp(reply,"+CSQ"))
    {
       ...
    }
    
    else if (strstr((char*)EC800_RX_BUF, reply)){  
        if (!strcmp(cmd,"AT+CGSN\r\n")){
        }
        else if(!strcmp(cmd,"AT+QHTTPREAD=2\r\n")){
            printf("%s\r\n", EC800_RX_BUF);   //打印出响应用来参考
            
            char* position = strstr((char*)EC800_RX_BUF, "\"expectGear\":");//先找到expectGear 的位置
            
            if (position != NULL) {
                // Use sscanf to extract the integer value after "expectGear"
                sscanf(position + strlen("\"expectGear\":"), "%d", &Http_set_mode);
                
                if((Http_set_mode > 0)&&(Http_set_mode < 5)&&(Http_set_mode != Value_Mode)) need_change = TRUE;
            } else {
                printf("not found expectGear!!\r\n");
            }
            CLEAR_EC800_Buffer(EC800_RX_Data);
            return 0;
        }

        CLEAR_EC800_Buffer(EC800_RX_Data);
        return 0;
    }

    return 1;  
}

从上面可以看到,在这个操作中我们只会改变变量 Http_set_modeneed_change 的值,但是 need_change 并不是每次都改变,所以基本上可以判断是 Http_set_mode 的值变化使得My_4g_state.Ec800_init_state 变化了。

3.2 问题分析与解决

那我们前文也说过,此类问题极大概率的就是数据溢出问题,这两个数据在内存中存放的地址应该是靠在一起的才会出现这种问题,为了更加直观的说明这个问题,我们可以查看一下他们在内存中存放的位置。

3.2.1 查看变量在内存中的位置

如何查看变量的存放位置?那就是查看编译过后的 .map 文件!

我们打开 .map 文件 搜索一下我们的变量,如下图:

在这里插入图片描述

果然他们是紧靠着的,他们在内存中如下图分部:

在这里插入图片描述

那我们回去看一下问题,Http_set_mode 我们定义的为一个字节的数据,怎么影响到了后面的数据,我们在出问题的地方有一条语句,注意看:

sscanf(position + strlen("\"expectGear\":"), "%d", &Http_set_mode);

expectGear 字符位置后面的数值,放到变量Http_set_mode 的地址,也就是 0x2000046d 位置处,放的数据类型为 %d 类型 !!!

%d 类型是几个字节? 在 32 位系统中,%d 通常表示 4 字节(32 位)的有符号整数(int 类型)。

所以问题我们已经确定了,就是因为这个sscanf函数中的 %d导致的,比如我们收到的是一个 2 ,我们知道 STM32 为小端模式,那么操作过后我们的内存中的数据会变成如下这样:

在这里插入图片描述

到这里,问题已经很明了了,我们没有注意数据类型,导致我们改变了存放在地址 0x20000470 处的My_4g_state.Ec800_init_state 变量的值。

对于这个问题,我们只需要把 %d改成 %hh , 就解决了这个问题了!如下:

sscanf(position + strlen("\"expectGear\":"), "%hhu", &Http_set_mode);

3.3 数据类型说明

那既然遇到了这个问题,在解决的过程中,我也发现有一些值得说明的地方,所以接下来就顺带做做一个笔记说明把。

3.3.1 常用格式化输出符号

首先,先来看一看我们常用的格式化输出符号,这里只需要记住这张表格就好了:

格式符含义对应数据类型位数(典型情况)
%d有符号十进制整数int32位(4字节)
%u无符号十进制整数unsigned int32位(4字节)
%hd有符号短整数short16位(2字节)
%hu无符号短整数unsigned short16位(2字节)
%ld有符号长整数long32/64位(平台相关)
%lu无符号长整数unsigned long32/64位(平台相关)
%lld有符号长长整数long long64位(8字节)
%llu无符号长长整数unsigned long long64位(8字节)
%hhd有符号单字节整数char8位(1字节)
%hhu无符号单字节整数unsigned char8位(1字节)
%f十进制浮点数float32位(4字节)
%lf十进制双精度浮点数double64位(8字节)
%c单个字符char8位(1字节)
%s字符串char[](以\0结尾)动态长度
%p指针地址任意指针类型32/64位(平台相关)

3.3.2 不同平台数据类型定义

对于本文遇到的问题,除了上面的解决办法,我们其实还可以修改一下Http_set_mode 的数据类型,如下:

在这里插入图片描述

这样处理的话其实也能够解决问题。但是我这里要说的一个问题是,我在修改的过程中,有把 Http_set_mode 定义为 u16 的数据类型,但是呢他还是占用 4 个字节,实际上这里就让我发现了另外一个问题,按理来说u16 类型我本意是定义为 16位 的数据。

于是查看了一下u16 的定义:

在这里插入图片描述

我第一印象是,确实是 4 个字节的,为什么会这么定义? 想了一下,这个头文件是在上次 51 单片机项目中复制过来的……

在 8 位的单片机中:

unsigned int 通常是16 位(2字节)
unsigned long 为 32 位
unsigned char 8位

所以这里我忽略了平台的变换,直接使用 8 位单片机上的数据类型定义,如果要在 32 位单片机上定义 16位数据类型 ,建议使用uint16_t(无符号) 和 int16_t(有符号),因为这是在标准 C 语言库文件 <stdint.h> 中定义的,在所有平台上都明确表示16位,可以避免因编译器差异导致的问题。

其实,即便我们真的记不得某一个数据类型到底占多少个字节,我们可以直接通过 sizeof 来判断,示例如下:

printf("Size: %d bytes\n", (int)sizeof(uint16_t)); 

在这里插入图片描述

结语

本文我们讲到的问题,是数据类型处理不当的问题,对数据类型的应用不够熟练严谨导致的数据覆盖。

我们通过问题,复习了一遍数据类型的一些基础知识,也说明了如何通过 .map 文件检查数据溢出或者覆盖问题。希望对大家以后的产品开发有一定的帮助。

好了,本文就到这里,谢谢大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2322043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 标准库与数据结构

Python的标准库提供了丰富的内置数据结构和函数&#xff0c;使用这些工具能为我们提供一套强有力的工具。 需要注意的是&#xff0c;相比C与Java&#xff0c;Python的一些特点&#xff1a; Python不需要显式声明变量类型Python没有模板(Template)的概念&#xff0c;因为Pytho…

大疆上云api介绍

概述 目前对于 DJI 无人机接入第三方云平台,主要是基于 MSDK 开发定制 App,然后自己定义私有上云通信协议连接到云平台中。这样对于核心业务是开发云平台,无人机只是其中一个接入硬件设备的开发者来说,重新基于 MSDK 开发 App 工作量大、成本高,同时还需要花很多精力在无人…

2025-03-25 Unity 网络基础4——TCP同步通信

文章目录 1 Socket1.1 Socket 类型1.2 构造 Socket1.3 常用属性1.4 常用方法 2 TCP 通信2.1 服务端配置2.2 客户端配置2.3 进行通信2.4 多设备通信 3 区分消息 1 Socket ​ Socket 是 C# 提供的网络通信类&#xff08;其它语言也有对应的 Socket 类&#xff09;&#xff0c;是…

C++进阶(一)

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 前言 本篇博客是讲解函数的重载以及引用的知识点的。 文章目录 前言 1.函数重载 1.1何为函数重载 1.2函数重载的作用 1.3函数重载的实现 2.引用 2.1何为引用 2.2定义引用 2.3引用特性 2.4常引用 2…

深度解读DeepSeek:开源周(Open Source Week)技术解读

深度解读DeepSeek&#xff1a;开源周&#xff08;Open Source Week&#xff09;技术解读 深度解读DeepSeek&#xff1a;源码解读 DeepSeek-V3 深度解读DeepSeek&#xff1a;技术原理 深度解读DeepSeek&#xff1a;发展历程 文章目录 一、开源内容概览Day1&#xff1a;FlashMLAD…

AI Agent开发与应用

AI Agent开发与应用&#xff1a;本地化智能体实践——本地化智能体开发进展与主流框架分析 我要说的都在ppt里面了&#xff0c;相关复现工作请参考ai agent开发实例 OpenManus Dify Owl 第二个版本更新了对话的框架&#xff0c;通过gradio做了一个全新的界面 只测试了阿里云…

石斛基因组-文献精读122

A chromosome-level Dendrobium moniliforme genome assembly reveals the regulatory mechanisms of flavonoid and carotenoid biosynthesis pathways 《染色体水平的石斛基因组组装揭示了黄酮类和胡萝卜素生物合成途径的调控机制》 摘要 石斛&#xff08;Dendrobium monil…

javaSE.多维数组

1 final 引用类型 final int[] arr 继承Object 的引用类型&#xff0c;不能改变引用的对象 存的其实是引用 数组类型数组&#xff0c;其实存的是引用 int [][] arr new int[][] { {1,2,3}, {4,5,6} };int [] a arr[0]; int [] b arr[1];

Python条件处理,新手入门到精通

Python条件处理&#xff0c;新手入门到精通 对话实录 **小白**&#xff1a;&#xff08;崩溃&#xff09;我写了if x 1:&#xff0c;为什么Python会报错&#xff1f; **专家**&#xff1a;&#xff08;推眼镜&#xff09;**是赋值&#xff0c;才是比较**&#xff01;想判断相…

JPA实体类注解缺失异常全解:从报错到防御!!!

&#x1f6a8; JPA实体类注解缺失异常全解&#xff1a;从报错到防御 &#x1f6e1;️ 一、&#x1f4a5; 问题现象速览 // 经典报错示例 Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.entity.Product典型症状&#xff1a; &…

【C语言】多进程/多线程

【C语言】多进程/多线程 参考链接多进程/多线程服务器1. 多进程服务器2. 多线程服务器 结语参考链接 参考链接 c 中文网 菜鸟 c 多进程/多线程服务器 多进程和多线程是常用的并发编程技术。它们都允许程序同时执行多个任务&#xff0c;提高了系统的资源利用率和程序的运行效率…

模糊数学 | 模型 / 集合 / 关系 / 矩阵

注&#xff1a;本文为来自 “模糊数学 | 模型及其应用” 相关文章合辑。 略作重排。 如有内容异常&#xff0c;请看原文。 模糊数学模型&#xff1a;隶属函数、模糊集合的表示方法、模糊关系、模糊矩阵 wamg 潇潇 于 2019-05-06 22:35:21 发布 1.1 模糊数学简介 1965 年&a…

QinQ项展 VLAN 空间

随着以太网技术在网络中的大量部署&#xff0c;利用 VLAN 对用户进行隔离和标识受到很大限制。因为 IEEE802.1Q 中定义的 VLAN Tag 域只有 12 个比特&#xff0c;仅能表示 4096 个 VLAN&#xff0c;无法满足城域以太网中标识大量用户的需求&#xff0c;于是 QinQ 技术应运而生。…

数据结构—树(java实现)

目录 一、树的基本概念1.树的术语2.常见的树结构 二、节点的定义三、有关树结构的操作1.按照数组构造平衡 二叉搜索树2.层序遍历树3.前、中、后序遍历树(1).前序遍历树(2).中序遍历树(3).后序遍历树(4).各种遍历的情况的效果对比 4.元素添加5.元素删除1.删除叶子节点2.删除单一…

S32K144外设实验(七):FTM输出多路互补带死区PWM

文章目录 1. 概述1.1 时钟系统1.2 实验目的2. 代码的配置2.1 时钟配置2.2 FTM模块配置2.3 输出引脚配置2.4 API函数调用1. 概述 互补对的PWM输出是很重要的外设功能,尤其应用再无刷电机的控制。 1.1 时钟系统 笔者再墨迹一遍时钟的设置,因为很重要。 FTM的CPU接口时钟为SY…

[网鼎杯 2020 白虎组]PicDown1 [反弹shell] [敏感文件路径] [文件描述符]

常见读取路径 /etc/passwd一些用户和权限还有一些乱七八糟的 /proc/self/cmdline包含用于开始当前进程的命令 /proc/self/cwd/app.py当前工作目录的app.py /proc/self/environ包含了可用进程的环境变量 /proc/pid/exe 包含了正在进程中运行的程序链接&#xff1b; /proc/pid…

单纯形法之大M法

1. 问题背景与标准化 在求解某些线性规划问题时&#xff0c;往往难以直接找到初始的基本可行解。特别是当约束中存在等式或 “≥” 类型的不等式时&#xff0c;我们需要引入人工变量来构造一个初始可行解。 考虑如下标准形式问题&#xff08;假设为最大化问题&#xff09;&am…

各类神经网络学习:(四)RNN 循环神经网络(下集),pytorch 版的 RNN 代码编写

上一篇下一篇RNN&#xff08;中集&#xff09;待编写 代码详解 pytorch 官网主要有两个可调用的模块&#xff0c;分别是 nn.RNNCell 和 nn.RNN &#xff0c;下面会进行详细讲解。 RNN 的同步多对多、多对一、一对多等等结构都是由这两个模块实现的&#xff0c;只需要将对输入…

DeepSeek 发布DeepSeek-V3-0324 版本 前端与网页开发能力、推理与多任务能力提升

DeepSeek 发布 DeepSeek-V3-0324 版本 DeepSeek 发布 DeepSeek-V3-0324 版本&#xff0c;在其前代模型 DeepSeek-V3 的基础上进行了显著升级。 该模型专注于中文和多语言文本生成、推理、代码编写等综合能力的提升&#xff0c;支持 Function Calling&#xff08;函数调用&…

传输层安全协议 SSL/TLS 详细介绍

传输层安全性协议TLS及其前身安全套接层SSL是一种安全传输协议&#xff0c;目前TLS协议已成为互联网上保密通信的工业标准&#xff0c;在浏览器、邮箱、即时通信、VoIP等应用程序中得到广泛的应用。本文对SSL和TLS协议进行一个详细的介绍&#xff0c;以便于大家更直观的理解和认…