更新。点击上方"蓝字"关注我们
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;}
总结
>>>
所有内容会在今年更新。
故我在
点击下方卡片 关注我
↓↓