STM32 | STM32F4OTA_ESP8266_Bootloader为引导程序远程更新的代码(APP)

更新。点击上方"蓝字"关注我们

01、思路

>>>

STM32F4OTA_ESP8266_Bootloader为引导程序

远程更新的代码(APP):远程更新的APP

Ymoden_server:为运行在Linux的TCP服务器

备注:STM32 OTA远程更新需要连接热点

电脑与ubuntuIP同一个网段

热点名称:WSDSB

热点密码:TLYU.4936

第一步:

设置远程更新的代码(APP)中的ROM为第五扇区,编译生成的.bin文件放置在ymodem_server中。

IROM1  0x08020000  0x20000

             起始地址       大小

第二步:

设置FLASH为默认值,修STM32F4OTA_ESP8266_Bootloader中的WIFI热点名称与密码,修服务器IP地址。

IROM1  0x08000000  0x80000

             起始地址       大小

第三步:烧录STM32F4OTA_ESP8266_Bootloader,并重启,按下S3按键,启动服务器代码传输。

02、ymodem_server

>>>

需要源码的私聊,后续所有内容发布完,在统一整理资源分享。

// ymodem_server.c/*****************************************************************名    称:基于TCP服务器通过ymodem协议实现固件更新/文件传输*作    者:Qt历险记*创建日期:2021/05/28*知 识 点:  1.TCP服务器  2.ymodem协议  3.本地文件访问与传输*****************************************************************/#include <stdio.h>#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>//ymodem用到的信号#define SOH    0x01#define STX    0x02#define ACK    0x06#define NACK  0x15#define EOT    0x04#define CCC    0x43//状态机#define YMODEM_STA_IDLE    0#define YMODEM_STA_START  1#define YMODEM_STA_DATA    2#define YMODEM_STA_EOT1    3#define YMODEM_STA_EOT2    4#define YMODEM_STA_END    5uint16_t crc16(uint8_t *addr, int32_t num, uint16_t crc);unsigned long file_size_get(const char *pfile_path);int main (int argc,char **argv){  int fd_socket;  int fd_client;    int rt;  int len;  int crc;  int m=0,n=0;    char buf_rx[133];  char buf_tx[133];    unsigned char tx_seq=0;    char file_name[128]={0};  int  file_size=0;    int buf_tx_offset=0;      int ymodem_sta = YMODEM_STA_IDLE;    /* 检查输入的参数 */  if(argv[1] ==NULL)  {    printf("please input format:./ymodem filename\r\n");    return -1;      }    printf("This is ymodem server by Teacher.Wen\r\n");    /* 获取文件名 */  strcpy(file_name,argv[1]);    /* 创建套接字,协议为IPv4,类型为TCP */  fd_socket = socket(AF_INET,SOCK_STREAM,0);    if(fd_socket<0)  {    perror("create socket fail:");        return -1;  }    //30秒超时  struct timeval timeout={30,0};      //设置发送超时  rt = setsockopt(fd_socket,SOL_SOCKET,SO_SNDTIMEO,(const char *)&timeout,sizeof timeout);    if(rt < 0)  {    perror("setsockopt SO_RCVTIMEO:");    return -1;  }    //设置接收超时  rt = setsockopt(fd_socket,SOL_SOCKET,SO_RCVTIMEO,(const char *)&timeout,sizeof timeout);    if(rt < 0)  {    perror("setsockopt SO_RCVTIMEO:");    return -1;  }    int on =1;  /* 设置socket允许重复使用地址与端口 */  setsockopt(fd_socket,SOL_SOCKET,SO_REUSEADDR,&on,sizeof on);    struct sockaddr_in   local_addr;    local_addr.sin_family     = AF_INET;            //IPv4  local_addr.sin_port       = htons(8888);          //本机端口为8888  local_addr.sin_addr.s_addr  = htonl(INADDR_ANY);    rt=bind(fd_socket,(struct sockaddr *)&local_addr,sizeof local_addr);    if(rt < 0)  {    perror("bind");    return -1;  }    /*  设置连接数目为1个 */  rt = listen(fd_socket,5);    if(rt < 0)  {    perror("listen fail:");    return -1;      }    struct sockaddr_in   dest_addr;  socklen_t dest_addr_len =sizeof dest_addr;    printf("waiting tcp client...\r\n");    /* 等待客户端连接 */  fd_client = accept(fd_socket,(struct sockaddr *)&dest_addr, &dest_addr_len);    if(fd_client < 0)  {    perror("accept fail:");    return -1;    }    char *p = inet_ntoa(dest_addr.sin_addr);    printf("client ip:%s,port%d\n",p,ntohs(dest_addr.sin_port));    while(1)  {    switch(ymodem_sta)    {      case YMODEM_STA_IDLE:      {        printf("[YMODEM_STA_IDLE]\r\n");                bzero(buf_rx,sizeof buf_rx);                len = recv(fd_client, buf_rx, sizeof buf_rx, 0);                if(len <= 0)        {          perror("[YMODEM_STA_IDLE]recv");          printf("[YMODEM_STA_IDLE]please try again\r\n");          break;        }                  if(buf_rx[0]=='C')        {          ymodem_sta = YMODEM_STA_START;                }      }break;            case YMODEM_STA_START:      {        printf("[YMODEM_STA_START]\r\n");        memset(buf_tx,0,sizeof buf_tx);                tx_seq=0;        buf_tx_offset=0;        file_size=0;                        buf_tx[buf_tx_offset++]=(unsigned char )SOH;    //符号        buf_tx[buf_tx_offset++]=(unsigned char )tx_seq;    //序号        buf_tx[buf_tx_offset++]=(unsigned char )~tx_seq;  //序号反码                /* 文件名 */        memcpy(&buf_tx[buf_tx_offset],file_name,strlen(file_name));        buf_tx_offset+=strlen(file_name);        /* 文件名与文件大小要相隔一个空字符 */        buf_tx_offset++;                /* 文件大小 */                file_size = file_size_get(file_name);        buf_tx[buf_tx_offset++]=(unsigned char)((file_size>>24)&0xFF);        buf_tx[buf_tx_offset++]=(unsigned char)((file_size>>16)&0xFF);        buf_tx[buf_tx_offset++]=(unsigned char)((file_size>>8)&0xFF);        buf_tx[buf_tx_offset++]=(unsigned char)((file_size>>0)&0xFF);                /* 计算crc16 */        crc=0;        crc=crc16(&buf_tx[3],128,0);        buf_tx[131]=(unsigned char)((crc>>8)&0xFF);        buf_tx[132]=(unsigned char)((crc>>0)&0xFF);                /* 发送 */        len = send(fd_client,buf_tx,133,0);        if(len <= 0)        {          perror("[YMODEM_STA_START]send");          break;        }                  /* 等待应答 */        memset(buf_rx,0,sizeof buf_rx);        len = recv(fd_client, buf_rx, 1, 0);                if(len <= 0)        {          perror("[YMODEM_STA_START]recv ack");          break;        }                if(buf_rx[0] != ACK)        {          printf("[YMODEM_STA_START]please try again\r\n");                    ymodem_sta = YMODEM_STA_IDLE;          break;        }                /* 等待大写字母C */        memset(buf_rx,0,sizeof buf_rx);        len = recv(fd_client, buf_rx, 1, 0);                if(len <= 0)        {          perror("[YMODEM_STA_START]recv ack");          break;        }          if(buf_rx[0] != CCC)        {          printf("[YMODEM_STA_START]please try again\r\n");          ymodem_sta = YMODEM_STA_IDLE;          break;        }                ymodem_sta = YMODEM_STA_DATA;      }break;          case YMODEM_STA_DATA:      {          printf("YMODEM_STA_DATA\r\n");                int fd = open(file_name,O_RDONLY);              if(fd < 0)        {          perror("[YMODEM_STA_DATA]open");          break;        }                while(1)        {          memset(buf_tx,0,sizeof buf_tx);                    len = read(fd,&buf_tx[3],128);//读取128字节数据                    if(len == 0)          {            ymodem_sta = YMODEM_STA_EOT1;            break;          }                    buf_tx[0]=(unsigned char )SOH;  //符号                    tx_seq++;          buf_tx[1]=(unsigned char )tx_seq;  //序号          buf_tx[2]=(unsigned char )~tx_seq;//序号反码                              //printf("YMODEM_STA_DATA read() len is %d\r\n",len);                      /* 计算crc16 */          crc=0;          crc=crc16(&buf_tx[3],128,0);          buf_tx[131]=(unsigned char)((crc>>8)&0xFF);          buf_tx[132]=(unsigned char)((crc>>0)&0xFF);                    /* 发送 */          len = send(fd_client,buf_tx,133,0);          if(len <= 0)          {            perror("[YMODEM_STA_DATA]send");            break;          }            //printf("YMODEM_STA_DATA tx_seq=%d\r\n",tx_seq);          /* 等待应答 */          memset(buf_rx,0,sizeof buf_rx);          len = recv(fd_client, buf_rx, 1, 0);                    if(len <= 0)          {            perror("[YMODEM_STA_DATA]recv ack");            break;          }                    if(buf_rx[0] != ACK)          {            printf("[YMODEM_STA_DATA]please try again\r\n");                        ymodem_sta = YMODEM_STA_IDLE;            break;          }                    printf(".");        }        close(fd);                printf("\r\n");              }break;            case YMODEM_STA_EOT1:      {          printf("[YMODEM_STA_EOT1]\r\n");          buf_tx[0]=EOT;                    /* 发送 */          len = send(fd_client,buf_tx,1,0);          if(len <= 0)          {            perror("[YMODEM_STA_EOT1]send");            break;          }                      /* 等待应答 */          memset(buf_rx,0,sizeof buf_rx);          len = recv(fd_client, buf_rx, 1, 0);          if(len <= 0)          {            perror("[YMODEM_STA_EOT1]recv nak");            break;          }                    /* 等待NAK */          if(buf_rx[0] != NACK)          {            printf("[YMODEM_STA_EOT1]please try again\r\n");                        ymodem_sta = YMODEM_STA_IDLE;                        break;          }          ymodem_sta = YMODEM_STA_EOT2;                }break;            case YMODEM_STA_EOT2:      {        printf("[YMODEM_STA_EOT2]\r\n");        buf_tx[0]=EOT;                /* 发送 */        len = send(fd_client,buf_tx,1,0);        if(len <= 0)        {          perror("[YMODEM_STA_EOT2]send");          break;        }                  /* 等待应答 */        memset(buf_rx,0,sizeof buf_rx);        len = recv(fd_client, buf_rx, 1, 0);                if(len <= 0)        {          perror("[YMODEM_STA_EOT2]recv ack");          break;        }                if(buf_rx[0] != ACK)        {          printf("[YMODEM_STA_EOT2]please try again\r\n");                    ymodem_sta = YMODEM_STA_IDLE;          break;        }                /* 等待大写字母C */        memset(buf_rx,0,sizeof buf_rx);        len = recv(fd_client, buf_rx, 1, 0);                if(len <= 0)        {          perror("[YMODEM_STA_EOT2]recv ack");          break;        }          if(buf_rx[0] != CCC)        {          printf("[YMODEM_STA_EOT2]please try again\r\n");                    ymodem_sta = YMODEM_STA_IDLE;          break;        }                ymodem_sta = YMODEM_STA_END;      }break;              case YMODEM_STA_END:      {        printf("[YMODEM_STA_END]\r\n");        buf_tx[0]=SOH;            buf_tx[1]=0x00;        buf_tx[2]=0xFF;        memset(&buf_tx[3],0,128);                /* 计算crc16 */        crc=0;        crc=crc16(&buf_tx[3],128,0);        buf_tx[131]=(unsigned char)((crc>>8)&0xFF);        buf_tx[132]=(unsigned char)((crc>>0)&0xFF);                /* 发送 */        len = send(fd_client,buf_tx,133,0);        if(len <= 0)        {          perror("[YMODEM_STA_END]send");          break;        }                  /* 等待应答 */        memset(buf_rx,0,sizeof buf_rx);        len = recv(fd_client, buf_rx, 1, 0);                if(len <= 0)        {          perror("[YMODEM_STA_END]recv ack");          break;        }                if(buf_rx[0] != ACK)        {          printf("[YMODEM_STA_END]please try again\r\n");                    ymodem_sta = YMODEM_STA_IDLE;          break;        }                      printf("ymodem download success\r\n");        close(fd_socket);        exit(0);      }break;          default:break;    }    }    return 0;}#define POLY        0x1021  uint16_t crc16(uint8_t *addr, int32_t num, uint16_t crc)  {      int32_t i;      for (; num > 0; num--)          /* Step through bytes in memory */      {          crc = crc ^ (*addr++ << 8);      /* Fetch byte from memory, XOR into CRC top byte*/          for (i = 0; i < 8; i++)        /* Prepare to rotate 8 bits */          {            if (crc & 0x8000)        /* b15 is set... */                  crc = (crc << 1) ^ POLY;    /* rotate and XOR with polynomic */              else                            /* b15 is clear... */                  crc <<= 1;          /* just rotate */          }                  /* Loop for 8 bits */          crc &= 0xFFFF;            /* Ensure CRC remains 16-bit value */      }                    /* Loop until num=0 */      return(crc);              /* Return updated CRC */  }/**************************************************** *函数名称:file_size_get *输入参数:pfile_path  -文件路径 *返 回 值:-1    -失败       其他值  -文件大小 *说  明:获取文件大小 ****************************************************/unsigned long file_size_get(const char *pfile_path){  unsigned long filesize = -1;    struct stat statbuff;    if(stat(pfile_path, &statbuff) < 0)  {    return filesize;  }  else  {    filesize = statbuff.st_size;  }    return filesize;}

03、STM32F4OTA_ESP8266_Bootloader

>>>

知 识 点:

1.esp8266 wifi的AT指令的使用

2.ymodem协议

3.内部FLASH的读、写、擦除

4.系统复位、代码区跳转技巧

*说  明:

1.复位时检测到按键1持续按下,表示使用COM1接口,使用SecureCRT软件进行固件更新

2.复位时检测到按键3持续按下,表示使用COM3接口,使用ESP8266无线WiFi实现OTA固件更新

// mainc.c/*****************************************************************名    称:基于ymodem协议实现stm32f4的固件本地/OTA更新*作    者:Qt历险记*创建日期:2021/05/28*知 识 点:  1.esp8266 wifi的AT指令的使用  2.ymodem协议  3.内部FLASH的读、写、擦除  4.系统复位、代码区跳转技巧*说  明:      1.复位时检测到按键1持续按下,表示使用COM1接口,使用SecureCRT软件进行固件更新  2.复位时检测到按键3持续按下,表示使用COM3接口,使用ESP8266无线WiFi实现OTA固件更新*****************************************************************/#include "stm32f4xx.h"#include "sys.h"#include "usart.h"#include "delay.h"#include "led.h"#include "beep.h"#include "flash.h"#include "key.h"#include "esp8266.h"#include "tim.h"#include "ymodem.h"#include <stdio.h>#include <string.h>#include <stdlib.h>/** * @bieaf 进行BootLoader的启动 * * @param  none * @return none */void bootloader_start(void){    /*==========打印消息==========*/    switch(read_start_mode())                    //读取是否启动应用程序    {    case STARTUP_NORMAL:                    //正常启动    {      printf("> normal start......\r\n");      break;    }    case STARTUP_UPDATE:                    //升级再启动    {      printf("> start update......\r\n");      move_code(APPLICATION_1_ADDR,APPLICATION_2_ADDR,APPLICATION_2_SIZE);      printf("> update success......\r\n");      break;    }    case STARTUP_RESET:                      //恢复出厂设置 目前没使用    {      printf("> restore to factory program......\r\n");      break;    }    default:                          //启动失败    {      printf("> error:%X!!!......\r\n", read_start_mode());      return;    }    }    /* 跳转到应用程序 */    printf("> start up......\r\n\r\n");    iap_execute_app(APPLICATION_1_ADDR);}//主函数int main(void){  //led初始化  led_init();    //beep初始化  beep_init();      //按键检测  key_init();    //定时器3初始化  tim3_init();    //串口1初始化波特率为115200bps  usart1_init(115200);    //串口延迟一会,确保芯片内部完成全部初始化,printf无乱码输出  delay_ms(1000);    printf("This is bootloader test by teacher.chen\r\n");  //  sector_erase(0x08020000);//  delay_ms(200);//  sector_erase(0x08040000);//  delay_ms(200);    //启动后,没有任何按键按下,则调用bootloader_start函数  if(key_sta_get() == 0)    bootloader_start();    if(key_sta_get()&0x01)  {    g_ymodem_com=1;    printf("now start ymodem download from com1\r\n");  }        if(key_sta_get()&0x04)  {    printf("now start ymodem download from com3(esp8266)\r\n");        //esp8266初始化    esp8266_init();      g_ymodem_com=3;  }        while(1)    {    if(g_ymodem_com == 1)    {      ymodem_download_from_com1();    }    if(g_ymodem_com == 3)    {      ymodem_download_from_com3();    }    }    return 0;}

04、esp8266.h

#ifndef __ESP8266_H__#define __ESP8266_H__#define EN_DEBUG_ESP8266  0//添加WIFI热点宏定义,此处根据自己的wifi作调整#define WIFI_SSID       "WSDSB"#define WIFI_PASSWORD    "TLYU.4936"//#define WIFI_SSID     "zc-room-10"//#define WIFI_PASSWORD    "88888888"#define YMODEM_SERVER  "192.168.3.8"extern volatile uint8_t  g_esp8266_rx_buf[512];extern volatile uint32_t g_esp8266_rx_cnt;extern volatile uint32_t g_esp8266_rx_end;extern volatile uint32_t g_esp8266_transparent_transmission_sta;extern int32_t   esp8266_init(void);extern int32_t  esp8266_self_test(void);extern int32_t   esp8266_exit_transparent_transmission (void);extern int32_t   esp8266_entry_transparent_transmission(void);extern int32_t   esp8266_connect_ap(char* ssid,char* pswd);extern int32_t   esp8266_connect_server(char* mode,char* ip,uint16_t port);extern int32_t   esp8266_disconnect_server(void);extern void   esp8266_send_bytes(uint8_t *buf,uint32_t len);extern void   esp8266_send_str(char *buf);extern void   esp8266_send_at(char *str);extern int32_t  esp8266_enable_multiple_id(uint32_t b);extern int32_t   esp8266_create_server(uint16_t port);extern int32_t   esp8266_close_server(uint16_t port);extern int32_t   esp8266_enable_echo(uint32_t b);extern int32_t   esp8266_reset(void);#endif

05、esp8266.c

#include "stm32f4xx.h"#include "sys.h"#include "delay.h"#include "usart.h"#include "esp8266.h"#include <string.h>#include <stdlib.h>#include <stdio.h>volatile uint8_t  g_esp8266_rx_buf[512];volatile uint32_t g_esp8266_rx_cnt=0;volatile uint32_t g_esp8266_rx_end=0;volatile uint32_t g_esp8266_transparent_transmission_sta=0;int32_t esp8266_init(void){  int32_t rt;      usart3_init(115200);  //退出透传模式,才能输入AT指令  rt=esp8266_exit_transparent_transmission();    if(rt)  {    printf("esp8266_exit_transparent_transmission fail\r\n");    return -1;  }    printf("esp8266_exit_transparent_transmission success\r\n");  delay_ms(2000);    //复位模块  rt=esp8266_reset();  if(rt)  {    printf("esp8266_reset fail\r\n");    return -2;  }  printf("esp8266_reset success\r\n");  delay_ms(2000);      //关闭回显  rt=esp8266_enable_echo(0);  if(rt)  {    printf("esp8266_enable_echo(0) fail\r\n");    return -3;  }    printf("esp8266_enable_echo(0)success\r\n");  delay_ms(2000);        //连接热点  rt = esp8266_connect_ap(WIFI_SSID,WIFI_PASSWORD);  if(rt)  {    printf("esp8266_connect_ap fail\r\n");    return -4;  }    printf("esp8266_connect_ap success\r\n");  delay_ms(2000);    rt =esp8266_connect_server("TCP",YMODEM_SERVER,8888);  if(rt)  {    printf("esp8266_connect_server fail\r\n");    return -5;  }    printf("esp8266_connect_server success\r\n");  delay_ms(2000);      //进入透传模式  rt =esp8266_entry_transparent_transmission();  if(rt)  {    printf("esp8266_entry_transparent_transmission fail\r\n");    return -6;  }    printf("esp8266_entry_transparent_transmission success\r\n");  delay_ms(2000);    return 0;}void esp8266_send_at(char *str){  //清空接收缓冲区  memset((void *)g_esp8266_rx_buf,0, sizeof g_esp8266_rx_buf);    //清空接收计数值  g_esp8266_rx_cnt = 0;      //串口3发送数据  usart3_send_str(str);}void esp8266_send_bytes(uint8_t *buf,uint32_t len){  usart3_send_bytes(buf,len);}void esp8266_send_str(char *buf){  usart3_send_str(buf);}/* 查找接收数据包中的字符串 */int32_t esp8266_find_str_in_rx_packet(char *str,uint32_t timeout){  char *dest = str;  char *src  = (char *)&g_esp8266_rx_buf;    //等待串口接收完毕或超时退出  while((strstr(src,dest)==NULL) && timeout)  {        delay_ms(1);    timeout--;  }  if(timeout)     return 0;                           return -1; }/* 自检程序 */int32_t  esp8266_self_test(void){  esp8266_send_at("AT\r\n");    return esp8266_find_str_in_rx_packet("OK",1000);}/** * 功能:连接热点 * 参数: *         ssid:热点名 *         pwd:热点密码 * 返回值: *         连接结果,非0连接成功,0连接失败 * 说明: *         失败的原因有以下几种(UART通信和ESP8266正常情况下) *         1. WIFI名和密码不正确 *         2. 路由器连接设备太多,未能给ESP8266分配IP */int32_t esp8266_connect_ap(char* ssid,char* pswd){#if 0  //不建议使用以下sprintf,占用过多的栈  char buf[128]={0};    sprintf(buf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);#endif    //设置为STATION模式    esp8266_send_at("AT+CWMODE_CUR=1\r\n");     if(esp8266_find_str_in_rx_packet("OK",1000))    return -1;  //连接目标AP  //sprintf(buf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);  esp8266_send_at("AT+CWJAP_CUR=");   esp8266_send_at("\"");esp8266_send_at(ssid);esp8266_send_at("\"");    esp8266_send_at(",");    esp8266_send_at("\"");esp8266_send_at(pswd);esp8266_send_at("\"");    esp8266_send_at("\r\n");  if(esp8266_find_str_in_rx_packet("OK",5000))    if(esp8266_find_str_in_rx_packet("CONNECT",5000))      return -2;  return 0;}/* 退出透传模式 */int32_t esp8266_exit_transparent_transmission (void){  esp8266_send_at ("+++");    //退出透传模式,发送下一条AT指令要间隔1秒  delay_ms ( 1000 );     //记录当前esp8266工作在非透传模式  g_esp8266_transparent_transmission_sta = 0;  return 0;}/* 进入透传模式 */int32_t  esp8266_entry_transparent_transmission(void){  //进入透传模式  esp8266_send_at("AT+CIPMODE=1\r\n");    if(esp8266_find_str_in_rx_packet("OK",5000))    return -1;    delay_ms(2000);  //开启发送状态  esp8266_send_at("AT+CIPSEND\r\n");  if(esp8266_find_str_in_rx_packet("OK",5000))    return -2;  //记录当前esp8266工作在透传模式  g_esp8266_transparent_transmission_sta = 1;  return 0;}/** * 功能:使用指定协议(TCP/UDP)连接到服务器 * 参数: *         mode:协议类型 "TCP","UDP" *         ip:目标服务器IP *         port:目标是服务器端口号 * 返回值: *         连接结果,非0连接成功,0连接失败 * 说明: *         失败的原因有以下几种(UART通信和ESP8266正常情况下) *         1. 远程服务器IP和端口号有误 *         2. 未连接AP *         3. 服务器端禁止添加(一般不会发生) */int32_t esp8266_connect_server(char* mode,char* ip,uint16_t port){#if 0    //使用MQTT传递的ip地址过长,不建议使用以下方法,否则导致栈溢出  //AT+CIPSTART="TCP","a10tC4OAAPc.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,该字符串占用内存过多了    char buf[128]={0};    //连接服务器  sprintf((char*)buf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);    esp8266_send_at(buf);#else    char buf[16]={0};  esp8266_send_at("AT+CIPSTART=");  esp8266_send_at("\"");  esp8266_send_at(mode);  esp8266_send_at("\"");  esp8266_send_at(",");  esp8266_send_at("\"");  esp8266_send_at(ip);  esp8266_send_at("\"");    esp8266_send_at(",");  sprintf(buf,"%d",port);  esp8266_send_at(buf);    esp8266_send_at("\r\n");  #endif    if(esp8266_find_str_in_rx_packet("CONNECT",5000))    if(esp8266_find_str_in_rx_packet("OK",5000))      return -1;  return 0;}/* 断开服务器 */int32_t esp8266_disconnect_server(void){  esp8266_send_at("AT+CIPCLOSE\r\n");      if(esp8266_find_str_in_rx_packet("CLOSED",5000))    if(esp8266_find_str_in_rx_packet("OK",5000))      return -1;    return 0;  }/* 使能多链接 */int32_t esp8266_enable_multiple_id(uint32_t b){  char buf[32]={0};    sprintf(buf,"AT+CIPMUX=%d\r\n", b);  esp8266_send_at(buf);    if(esp8266_find_str_in_rx_packet("OK",5000))    return -1;    return 0;}/* 创建服务器 */int32_t esp8266_create_server(uint16_t port){  char buf[32]={0};    sprintf(buf,"AT+CIPSERVER=1,%d\r\n", port);  esp8266_send_at(buf);    if(esp8266_find_str_in_rx_packet("OK",5000))    return -1;    return 0;}/* 关闭服务器 */int32_t esp8266_close_server(uint16_t port){  char buf[32]={0};    sprintf(buf,"AT+CIPSERVER=0,%d\r\n", port);  esp8266_send_at(buf);    if(esp8266_find_str_in_rx_packet("OK",5000))    return -1;    return 0;}/* 回显打开或关闭 */int32_t esp8266_enable_echo(uint32_t b){  if(b)    esp8266_send_at("ATE1\r\n");   else    esp8266_send_at("ATE0\r\n");     if(esp8266_find_str_in_rx_packet("OK",5000))    return -1;  return 0;}/* 复位 */int32_t esp8266_reset(void){  esp8266_send_at("AT+RST\r\n");    if(esp8266_find_str_in_rx_packet("OK",10000))    return -1;  return 0;}

06、ymodem.h

#ifndef __YMODEM_H__#define __YMODEM_H__#define SOH    0x01#define STX    0x02#define ACK    0x06#define NACK  0x15#define EOT    0x04#define CCC    0x43/* 升级的步骤 */enum UPDATE_STATE{  TO_START = 0x01,  TO_RECEIVE_DATA = 0x02,  TO_RECEIVE_EOT1 = 0x03,  TO_RECEIVE_EOT2 = 0x04,  TO_RECEIVE_END = 0x05};extern uint32_t g_ymodem_com;extern void ymodem_download_from_com1(void);extern void ymodem_download_from_com3(void);#endif

07、ymodem.c

#include "stm32f4xx.h"#include "sys.h"#include "usart.h"#include "delay.h"#include "led.h"#include "beep.h"#include "flash.h"#include "esp8266.h"#include "key.h"#include "ymodem.h"#include <stdio.h>#include <string.h>#include <stdlib.h>uint32_t g_ymodem_com = 3;/** * @bieaf CRC-16 校验 * * @param addr 开始地址 * @param num   长度 * @param num   CRC * @return crc  返回CRC的值 */#define POLY        0x1021  uint16_t crc16(uint8_t *addr, int32_t num, uint16_t crc)  {      int32_t i;      for (; num > 0; num--)          /* Step through bytes in memory */      {          crc = crc ^ (*addr++ << 8);      /* Fetch byte from memory, XOR into CRC top byte*/          for (i = 0; i < 8; i++)        /* Prepare to rotate 8 bits */          {            if (crc & 0x8000)        /* b15 is set... */                  crc = (crc << 1) ^ POLY;    /* rotate and XOR with polynomic */              else                            /* b15 is clear... */                  crc <<= 1;          /* just rotate */          }                  /* Loop for 8 bits */          crc &= 0xFFFF;            /* Ensure CRC remains 16-bit value */      }                    /* Loop until num=0 */      return(crc);              /* Return updated CRC */  }/* 设置升级的步骤 */static enum UPDATE_STATE update_state = TO_START;void ymodem_set_state(enum UPDATE_STATE state){  update_state = state;}/* 查询升级的步骤 */uint8_t ymodem_get_state(void){  return update_state;}/* 发送指令 */void ymodem_send_cmd(uint8_t command){  if(g_ymodem_com ==1)  {    USART_SendData(USART1,command);    //等待数据发送成功    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);    USART_ClearFlag(USART1,USART_FLAG_TXE);    }  if(g_ymodem_com ==3)  {    USART_SendData(USART3,command);    //等待数据发送成功    while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);    USART_ClearFlag(USART3,USART_FLAG_TXE);    }    delay_ms(10);}/* 标记升级完成 */void update_set_down(void){  uint32_t update_flag = 0xAAAAAAAA;        // 对应bootloader的启动步骤    flash_program((APPLICATION_2_ADDR + APPLICATION_2_SIZE - 4), &update_flag,1 );}/** * @bieaf ymodem下载 * * @param none * @return none */void ymodem_download_from_com1(void){  uint16_t crc = 0;static   uint8_t data_state = 0;  if(ymodem_get_state()==TO_START)  {    ymodem_send_cmd(CCC);        delay_ms(1000);  }    /* 串口1接收完一个数据包 */  if(g_usart1_rx_end)        {    /* 清空接收完成标志位、接收计数值 */    g_usart1_rx_end=0;    g_usart1_rx_cnt=0;        switch(g_usart1_rx_buf[0])    {      case SOH://数据包开始      {          crc = 0;                  /* 计算crc16 */          crc = crc16((uint8_t *)&g_usart1_rx_buf[3], 128, crc);                      if(crc != (g_usart1_rx_buf[131]<<8|g_usart1_rx_buf[132]))            return;                    if((ymodem_get_state()==TO_START)&&(g_usart1_rx_buf[1] == 0x00)&&(g_usart1_rx_buf[2] == (uint8_t)(~g_usart1_rx_buf[1])))// 开始          {            ymodem_set_state(TO_RECEIVE_DATA);                        /* 若ymodem_send_cmd执行在sector_erase之前,则导致串口数据丢包,因为擦除会关闭所有中断 */            /* 擦除应用程序2的扇区 */            sector_erase(APPLICATION_2_SECTOR);                                    data_state = 0x01;                        ymodem_send_cmd(ACK);            ymodem_send_cmd(CCC);            }          else if((ymodem_get_state()==TO_RECEIVE_END)&&(g_usart1_rx_buf[1] == 0x00)&&(g_usart1_rx_buf[2] == (uint8_t)(~g_usart1_rx_buf[1])))// 结束          {            update_set_down();                        ymodem_set_state(TO_START);            ymodem_send_cmd(ACK);                        /* 嘀一声示,表示下载完成 */            PFout(9)=0;beep_on();            delay_ms(80);            PFout(9)=1;beep_off();                        /* 复位 */            NVIC_SystemReset();          }                    else if((ymodem_get_state()==TO_RECEIVE_DATA)&&(g_usart1_rx_buf[1] == data_state)&&(g_usart1_rx_buf[2] == (uint8_t)(~g_usart1_rx_buf[1])))// 接收数据          {            /* 烧录程序 */            flash_program((APPLICATION_2_ADDR + (data_state-1) * 128), (uint32_t *)(&g_usart1_rx_buf[3]), 32);            data_state++;                        ymodem_send_cmd(ACK);              }      }break;            case EOT://数据包传输结束      {        if(ymodem_get_state()==TO_RECEIVE_DATA)        {          ymodem_set_state(TO_RECEIVE_EOT2);                    ymodem_send_cmd(NACK);        }        else if(ymodem_get_state()==TO_RECEIVE_EOT2)        {                      ymodem_set_state(TO_RECEIVE_END);                    ymodem_send_cmd(ACK);          ymodem_send_cmd(CCC);        }        }break;              default:break;    }  }}void ymodem_download_from_com3(void){  uint16_t crc = 0;static   uint8_t data_state = 0;  if(ymodem_get_state()==TO_START)  {    ymodem_send_cmd(CCC);        delay_ms(1000);  }    /* 串口3接收完一个数据包 */  if(g_esp8266_rx_end)        {    /* 清空接收完成标志位、接收计数值 */    g_esp8266_rx_end=0;    g_esp8266_rx_cnt=0;        switch(g_esp8266_rx_buf[0])    {      case SOH://数据包开始      {          crc = 0;                  /* 计算crc16 */          crc = crc16((uint8_t *)&g_esp8266_rx_buf[3], 128, crc);                      if(crc != (g_esp8266_rx_buf[131]<<8|g_esp8266_rx_buf[132]))            return;                    if((ymodem_get_state()==TO_START)&&(g_esp8266_rx_buf[1] == 0x00)&&(g_esp8266_rx_buf[2] == (uint8_t)(~g_esp8266_rx_buf[1])))// 开始          {            ymodem_set_state(TO_RECEIVE_DATA);                        /* 若ymodem_send_cmd执行在sector_erase之前,则导致串口数据丢包,因为擦除会关闭所有中断 */            /* 擦除应用程序2的扇区 */            sector_erase(APPLICATION_2_SECTOR);                                    data_state = 0x01;                        ymodem_send_cmd(ACK);            ymodem_send_cmd(CCC);            }          else if((ymodem_get_state()==TO_RECEIVE_END)&&(g_esp8266_rx_buf[1] == 0x00)&&(g_esp8266_rx_buf[2] == (uint8_t)(~g_esp8266_rx_buf[1])))// 结束          {            update_set_down();                        ymodem_set_state(TO_START);            ymodem_send_cmd(ACK);                        /* 嘀一声示,表示下载完成 */            PFout(9)=0;beep_on();            delay_ms(80);            PFout(9)=1;beep_off();                        /* 复位 */            NVIC_SystemReset();          }                    else if((ymodem_get_state()==TO_RECEIVE_DATA)&&(g_esp8266_rx_buf[1] == data_state)&&(g_esp8266_rx_buf[2] == (uint8_t)(~g_esp8266_rx_buf[1])))// 接收数据          {            /* 烧录程序 */            flash_program((APPLICATION_2_ADDR + (data_state-1) * 128), (uint32_t *)(&g_esp8266_rx_buf[3]), 32);            data_state++;                        ymodem_send_cmd(ACK);              }      }break;            case EOT://数据包传输结束      {        if(ymodem_get_state()==TO_RECEIVE_DATA)        {          ymodem_set_state(TO_RECEIVE_EOT2);                    ymodem_send_cmd(NACK);        }        else if(ymodem_get_state()==TO_RECEIVE_EOT2)        {                      ymodem_set_state(TO_RECEIVE_END);                    ymodem_send_cmd(ACK);          ymodem_send_cmd(CCC);        }        }break;              default:break;    }  }}

08、flash.c

#include "stm32f4xx.h"#include "flash.h"#include <stdio.h>/** * @bieaf 扇区擦除 *   * @param sector_num    扇区号 * @return 1 */int32_t sector_erase(uint32_t sector_num){  int32_t rt=-1;    /* 解锁FLASH*/  FLASH_Unlock();    if(FLASH_COMPLETE==FLASH_EraseSector(sector_num,VoltageRange_3))    rt=0;  /* 锁定FLASH*/  FLASH_Lock();    return rt;}/** * @bieaf 写若干个数据 * * @param addr       写入的地址 * @param buf       写入数据的起始地址 * @param word_size  长度 * @return  */void flash_program(uint32_t addr, uint32_t * buf, uint32_t word_size){    uint32_t i;    /* 解锁FLASH*/  FLASH_Unlock();    for(i = 0; i < word_size; i++)    {    /* 对FLASH烧写*/    FLASH_ProgramWord( addr + 4 * i, buf[i]);    }  /* 锁定FLASH*/  FLASH_Lock();}/** * @bieaf 读若干个数据 * * @param addr      读数据的地址 * @param buf       读出数据的数组指针 * @param word_size 长度 * @return  */void flash_read(uint32_t addr, uint32_t * buf, uint32_t word_size){  uint32_t i=0;    for(i =0; i < word_size; i++)    buf[i] = *(__IO uint32_t*)(addr + 4 * i);}/* 读取启动模式 */uint32_t read_start_mode(void){  uint32_t mode = 0;    flash_read((APPLICATION_2_ADDR + APPLICATION_2_SIZE - 4), &mode, 1);    return mode;}/** * @bieaf 进行程序的覆盖 * @detail 1.擦除目的地址 *         2.源地址的代码拷贝到目的地址 *         3.擦除源地址 * * @param  搬运的源地址 * @param  搬运的目的地址 * @return 搬运的程序大小 */void move_code(uint32_t dest_addr, uint32_t src_addr,uint32_t word_size){  uint32_t temp[256];  uint32_t i;    /*1.擦除目的地址*/  printf("> start erase application 1 sector......\r\n");    //擦除  sector_erase(APPLICATION_1_SECTOR);    printf("> erase application 1 success......\r\n");    /*2.开始拷贝*/    printf("> start copy......\r\n");    for(i = 0; i <word_size/1024; i++)  {    flash_read((src_addr + i*1024), temp, 256);    flash_program((dest_addr + i*1024), temp, 256);  }    printf("> copy finish......\r\n");    /*3.擦除源地址*/    printf("> start erase application 2 sector......\r\n");    //擦除  sector_erase(APPLICATION_2_SECTOR);    printf("> erase application 2 success......\r\n");  }/* 采用汇编设置栈的值 */__asm void MSR_MSP (uint32_t ulAddr) {    MSR MSP, r0                          //set Main Stack value    BX r14}void iap_execute_app (uint32_t app_addr){  jump_func jump_to_app;     //printf("* ( __IO uint32_t * ) app_addr  =%08X ,app_addr=%08X\r\n",* ( __IO uint32_t * ) app_addr,app_addr );      //if ( ( ( * ( __IO uint32_t * ) app_addr ) & 0x2FFE0000 ) == 0x200006B0 )  //检查栈顶地址是否合法.  //{     //printf("stack is legal\r\n");        jump_to_app = (jump_func) * ( __IO uint32_t *)(app_addr + 4);      //用户代码区第二个字为程序开始地址(复位地址)            MSR_MSP( * ( __IO uint32_t * ) app_addr );                //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)        jump_to_app();                              //跳转到APP.  //}    //printf("stack is illegal\r\n");}

09、usart.c

#include "stm32f4xx.h"#include "sys.h"#include "usart.h"#include "esp8266.h"#include <stdio.h>#include <string.h>#include <stdlib.h>static USART_InitTypeDef       USART_InitStructure;static GPIO_InitTypeDef     GPIO_InitStructure;static NVIC_InitTypeDef       NVIC_InitStructure;volatile uint8_t  g_usart1_rx_buf[1280];volatile uint32_t g_usart1_rx_cnt=0;volatile uint32_t g_usart1_rx_end=0;#pragma import(__use_no_semihosting_swi)struct __FILE { int handle; /* Add whatever you need here */ };FILE __stdout;FILE __stdin;int fputc(int ch, FILE *f) {  USART_SendData(USART1,ch);      //等待数据发送成功  while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);  USART_ClearFlag(USART1,USART_FLAG_TXE);  return ch;}void _sys_exit(int return_code) {}void usart1_init(uint32_t baud){  //使能端口A硬件时钟  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);    //使能串口1硬件时钟  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);      //配置PA9、PA10为复用功能引脚  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9|GPIO_Pin_10;  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;  GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;    GPIO_Init(GPIOA,&GPIO_InitStructure);    //将PA9、PA10连接到USART1的硬件  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,  GPIO_AF_USART1);  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);      //配置USART1的相关参数:波特率、数据位、校验位  USART_InitStructure.USART_BaudRate = baud;//波特率  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位  USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//允许串口发送和接收数据  USART_Init(USART1, &USART_InitStructure);      //使能串口接收到数据触发中断  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure);    //使能串口1工作  USART_Cmd(USART1,ENABLE);}void usart3_init(uint32_t baud){  //使能端口B硬件时钟  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);    //使能串口3硬件时钟  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);    //配置PB10、PB11为复用功能引脚  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10|GPIO_Pin_11;  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;  GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;    GPIO_Init(GPIOB,&GPIO_InitStructure);    //将PB10、PB11连接到USART3的硬件  GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);  GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);      //配置USART1的相关参数:波特率、数据位、校验位  USART_InitStructure.USART_BaudRate = baud;//波特率  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位  USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//允许串口发送和接收数据  USART_Init(USART3, &USART_InitStructure);      //使能串口接收到数据触发中断  USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure);    //使能串口3工作  USART_Cmd(USART3,ENABLE);}void usart3_send_str(char *str){  char *p = str;    while(*p!='\0')  {    USART_SendData(USART3,*p);        p++;      //等待数据发送成功    while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);    USART_ClearFlag(USART3,USART_FLAG_TXE);  }}void usart1_send_bytes(uint8_t *buf,uint32_t len){  uint8_t *p = buf;    while(len--)  {    USART_SendData(USART1,*p);        p++;        //等待数据发送成功    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);    USART_ClearFlag(USART1,USART_FLAG_TXE);  }}void usart3_send_bytes(uint8_t *buf,uint32_t len){  uint8_t *p = buf;    while(len--)  {    USART_SendData(USART3,*p);        p++;        //等待数据发送成功    while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);    USART_ClearFlag(USART3,USART_FLAG_TXE);  }}void USART1_IRQHandler(void){  uint8_t d=0;    //检测是否接收到数据  if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)  {    d=USART_ReceiveData(USART1);        g_usart1_rx_buf[g_usart1_rx_cnt++]=d;      if(g_usart1_rx_cnt >= sizeof g_usart1_rx_buf)    {      g_usart1_rx_end=1;    }            //清空标志位,可以响应新的中断请求    USART_ClearITPendingBit(USART1, USART_IT_RXNE);  }}void USART3_IRQHandler(void){  uint8_t d=0;    //检测是否接收到数据  if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)  {    d=USART_ReceiveData(USART3);        g_esp8266_rx_buf[g_esp8266_rx_cnt++]=d;      if(g_esp8266_rx_cnt >= sizeof g_esp8266_rx_buf)    {      g_esp8266_rx_end=1;    }    //清空标志位,可以响应新的中断请求    USART_ClearITPendingBit(USART3, USART_IT_RXNE);  }}

10、远程更新的代码(APP)

>>>

main.c

#include "stm32f4xx.h"#include "led.h"#include "key.h"#include "exti.h"#include "delay.h"#include "tim.h"#include "pwm.h"#include "usart.h"#include "string.h"#include "sys.h"#include "dht11.h"u8 buffer[32] = {0};u8 rx_buffer[32] = {0};u8 count = 0, rx_i;u8 rx_flag = 0;  //rx_flag = 1说明接受到数据//USART中断接收void USART1_IRQHandler(void){     //若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   {      //判断为真后,为下次中断做准备,则需要对中断的标志清零    USART_ClearITPendingBit(USART1,USART_IT_RXNE);         /* DR读取接受到的数据*/    buffer[count++] = USART_ReceiveData(USART1);            if(buffer[count-1] == ':')     {            //过滤帧尾       for(rx_i=0; rx_i < (count-1); rx_i++)       {        rx_buffer[rx_i] = buffer[rx_i];       }              count = 0;       //清空数组       memset(buffer, 0, sizeof(buffer));             rx_flag = 1;//标志一帧数据接受完毕     }      }   }int main(void){  int ret;  u8 data[5] = {0};    //NVIC中断分组:第二组, 抢占优先级取值范围:0x00~0x03; 响应优先级取值范围:0x00~0x03;   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    Delay_Init();  Led_Init();  Usart1_Init();  Dht11_Init();  while(1)  {    ret = Dht11_Start();          if(ret == 0)    {      ret = Dht11_Read(data);      if(ret == 0)      {        printf("温度:%d.%d, 湿度:%d,%d\n",data[2], data[3], data[0], data[1]);      }          }            GPIO_ToggleBits(GPIOF, GPIO_Pin_9);        delay_s(2);      }  return 0;}

总结

>>>

所有内容会在今年更新。

故我在

点击下方卡片 关注我

↓↓

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【实战项目】——Boost搜索引擎(五万字)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、项目的相关背景 1.1、什么是Boost库&#xff1f; 1.2、什么是搜索引擎&#xff1f; 1.3、为什么要做Boost库搜索引擎&#xff1f; 二、搜索引擎的宏观原…

【优选算法篇】双指针的优雅舞步:C++ 算法世界的浪漫探索

文章目录 C 双指针详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;对撞指针1.1 移动零解题思路图解分析C代码实现易错点提示代码解读 1.2 复写零解题思路算法步骤C代码实现易错点提示代码复杂度 1.3 盛最多水的容器1. 题目链接2. 题目描述解法一&#xff08;暴力求解…

MySQL SELECT 查询(三):查询常用函数大全

MySQL SELECT 查询&#xff08;三&#xff09;&#xff1a;查询常用函数大全 1. 单行函数 单行函数是 SQL 中一类重要的函数&#xff0c;它们可以对单行数据进行处理&#xff0c;并返回单个结果。单行函数可以嵌套使用&#xff0c;并提供灵活的数据处理能力。 1.1 定义 只对单…

H7-TOOL的LUA小程序教程第14期:任意波形信号发生器,0-20mA输出和微型数控电源(2024-10-11,已更新)

LUA脚本的好处是用户可以根据自己注册的一批API&#xff08;当前TOOL已经提供了几百个函数供大家使用&#xff09;&#xff0c;实现各种小程序&#xff0c;不再限制Flash里面已经下载的程序&#xff0c;就跟手机安装APP差不多&#xff0c;所以在H7-TOOL里面被广泛使用&#xff…

矩阵系统源码搭建,oem贴牌,技术指导

一、技术选型与整合 多种技术的融合 矩阵系统通常需要整合多种技术&#xff0c;包括前端技术、后端技术、数据库技术、服务器技术等。选择合适的技术栈并确保它们能够良好地协同工作是一个挑战。例如&#xff0c;前端可能使用 React 或 Vue.js&#xff0c;后端可能使用 Java Sp…

解决新版Android studio不能连接手机的问题

我要说的是一个特例&#xff0c;装了22年的版本AS可以正常连接手机&#xff0c;装了23年以后新版本&#xff0c;AS不能正常连接手机了&#xff0c;但是在CMD控制台可以正常的执行adb命令&#xff0c;并且CMD和AS都是指向D:\android_sdk\platform-tools\adb.exe 一、 为什么会出…

消息队列面试题——第二篇

1. rocketmq、rabbitmq、kafka的区别 架构设计和消息模型 特性rocketmqrabbitmqkafka消息模型基于主题和消费组&#xff0c;支持发布/订阅和点对点两种模型基于队列模型&#xff0c;支持发布/订阅和点对点两种模型基于分区的主题模型&#xff0c;主要用于日志流式处理和高吞吐…

完成Sentinel-Dashboard控制台数据的持久化-同步到Nacos

本次案例采用的是Sentinel1.8.8版本 一、Sentinel源码环境搭建 1、下载Sentinel源码工程 git clone https://github.com/alibaba/Sentinel.git 2、导入到idea 这里可以先运行DashboardApplication.java试一下是否运行成功&#xff0c;若成功&#xff0c;源码环境搭建完毕&a…

IDEA Sping Boot 多配置文件application Maven动态切换

新建application-dev.yml与application-prod.yml pom.xml文件下添加profiles等 让idea识别出配置文件 <profiles><profile><id>dev</id><properties><!-- 环境标识&#xff0c;需要与配置文件的名称相对应 --><profiles.active>dev&…

基于京东:HotKey实现自动缓存热点Key!!!

一.引言 某些热点数据&#xff0c;我们提前如果能够预判到的话&#xff0c;可以提前人工给数据加缓存&#xff0c;也就是缓存预热&#xff0c;将其缓存在本地或者Redis中&#xff0c;提高访问性能同时&#xff0c;减低数据库压力&#xff0c;也减轻后端服务的压力。但是&#…

大顶堆优化dp,带限制的子序列

前言&#xff1a;看到这个题目的时候我们可以用大顶堆记录前面的最大值&#xff0c;这样我们转移的时候就少了很多繁琐的查询 题目地址 class Solution { public:int constrainedSubsetSum(vector<int>& nums, int k) {int n nums.size();vector<int> ans nu…

【计算机网络 - 基础问题】每日 3 题(三十八)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…

小米路由器刷机istoreOS,愉快上网

istoreOS与openwrt openwrt是一个开源的路由器系统&#xff0c;市场上所有小米路由器的内部系统都是基于openwrt进行二次开发形成的&#xff0c;做了硬件适配和功能上的阉割&#xff0c;不太好用。 istoreos是小宝团队基于openwrt制作的一个发行版&#xff0c;更适合中国宝宝体…

计算机网络:数据链路层 —— 网络适配器与 MAC 地址

文章目录 网络适配器使用网络适配器网络适配器类型 MAC 地址MAC 地址格式MAC 地址类型MAC 地址发送顺序数据接收MAC 地址泄露问题 网络适配器 要将计算机连接到以太网&#xff0c;需要使用相应的网络适配器&#xff08;Adapter)&#xff0c;网络适配器一般简称为“网卡”。在计…

AirServer v7.2.7 破解版 – iPhone屏幕镜像工具

AirServer v7.2.7 破解版 – iPhone屏幕镜像工具可以将你的Mac转变为一个通用的镜像接收器&#xff0c;除了无法接收Miracast外&#xff0c;你可以使用内置的AirPlay或Google Cast基于屏幕投影功能来镜像你的设备屏幕&#xff1b;一次一个或同时投影到AirServer。用户可以从任何…

TGRS 2024 面向雾天遥感图像的定向目标检测算法

TGRS 2024 | 面向雾天遥感图像的定向目标检测算法 论文信息 摘要 目前&#xff0c;大量工作集中在航空目标检测上&#xff0c;并取得了良好的结果。尽管这些方法在传统数据集上取得了有希望的结果&#xff0c;但在恶劣天气条件下捕获的低质量图像中定位对象仍然具有挑战性。目…

数据库课程 CMU15-445 2023 Fall Project-2 Extendible Hash Index

0 实验结果 tips:完成项目的前提不需要一定看视频 1 数据结构&#xff1a;扩展哈希 解释下这张图&#xff1a; 图中header的最大深度2&#xff0c;directory最大深度2&#xff0c;桶的容量2。 最开始的时候只有一个header。 插入第一个数据&#xff0c;假设这个数据对应的哈希…

[自然语言处理]RNN

1 传统RNN模型与LSTM import torch import torch.nn as nntorch.manual_seed(6)# todo:基础RNN模型 def dem01():参数1&#xff1a;input_size 每个词的词向量维度&#xff08;输入层神经元的个数&#xff09;参数2&#xff1a;hidden_size 隐藏层神经元的个数参数3&#xff1a…

【puppeteer】wvp-puppeteer制作 过程

目录 最后的结论 制作windows&ubuntu的docker 重启桌面上的docker 命令重启 通过 Docker Desktop 图形界面重启 制作centos docker 测试 参考文档 最后的结论 ubuntu && windows 使用 dualvenregistry:5000/wvp-puppeteer:1.0 centos7 使用&#xff1a;…

RabbitMQ事务模块

目录 消息分发​​​​​​​ 负载均衡 幂等性保障 顺序性保障 顺序性保障方案 二号策略:分区消费 三号策略:消息确认机制 四号策略: 消息积压 RabbitMQ集群 选举过程 RabbitMQ是基于AMQP协议实现的,该协议实现了事务机制&#xff0c;要么全部成功&#xff0c;要么全…