Clion开发STM32之ESP8266系列(四)

news2024/11/24 3:39:09

前言

上一篇: Clion开发STM32之ESP8266系列(三)

本篇主要内容

  1. 实现esp8266需要实现的函数
  2. 串口3中断函数的自定义(这里没有使用HAL提供的)
  3. 封装esp8266服务端的代码和测试

正文

主要修改部分

核心配置头文件(添加一些宏定义)

  1. sys_core_conf.h文件中

在这里插入图片描述

在这里插入图片描述

实现esp8266需要实现的函数

在这里插入图片描述

源码

/*******************************************************************************
 * @author scl
 * @email shchlu188@163.com
 *      @brief 实现esp8266的驱动
 ******************************************************************************/
#include "module_esp8266.h"
#include "bsp.h"

extern data_frame_t esp_rx_frame;

static void sys_delay(uint32_t ms) {
    HAL_Delay(ms);
}

/**
 * @brief esp8266 驱动外设初始化
 */
void esp8266_driver_init() {
    /*设置系esp8266延时回调*/
    esp8266_set_delay_call(sys_delay);
    /*配置 CH_PD 引脚*/
    gpio_init(GPIOB, GPIO_PIN_8, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);
    /* 配置 RST 引脚*/
    gpio_init(GPIOB, GPIO_PIN_9, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);
    /*串口配置*/
    com3_init(115200);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
    /*开启串口中断*/
    UART_ENABLE_IT(USART3, UART_IT_RXNE);    /** 开启串口中断*/
    UART_ENABLE_IT(USART3, UART_IT_IDLE);   /** 开启串口空闲中断*/
}

/**
 * @brief esp8266 复位引脚
 * @param flag true: 置高; false: 置低
 */
void esp8266_rst_pin(bool flag) {
    if (flag) {
        pin_high(GPIOB, GPIO_PIN_9);
    } else {
        pin_low(GPIOB, GPIO_PIN_8);
    }
}

/**
 * @brief esp8266 使能引脚
 * @param flag true: 使能
 */
void esp8266_ch_enable(bool flag) {
    if (flag) {
        pin_high(GPIOB, GPIO_PIN_8);
    } else {
        pin_low(GPIOB, GPIO_PIN_8);
    }
}


/**
 * @brief esp8266 发送数据
 * @param data
 * @param len
 */
void esp8266_send(void *data, uint16_t len) {
    HAL_UART_Transmit(com_handle_get(com3), data, len, 1000);
}

/**
 * @brief 清空接收缓冲区
 */
void esp8266_clear_buf(void) {
    esp_rx_frame.InfAll = 0;
}

/**
 * @brief esp8266 接收数据
 * @return 接收数据
 */
char *esp8266_rec() {
    if (esp_rx_frame.InfBit.frame_finished_flag != 0) {
        /*添加结束符*/
        esp_rx_frame.p_rx_buf[esp_rx_frame.InfBit.frame_len] = '\0';
        esp_rx_frame.InfAll = 0;// 清除一次标志位
        return esp_rx_frame.p_rx_buf;
    }
    return NULL;
}

/**
 * @brief esp8266 构造指令的缓存区
 * @return
 */
char *esp8266_get_cmd_buf() {
    static char esp8266_buf[200] = {0};
    return esp8266_buf;
}

对应的串口3中断函数的编写

在这里插入图片描述

extern data_frame_t esp_rx_frame;

static inline void esp8266_tcp_server_IRQHandler() {
    uint8_t ucTemp;
    if (UART_GET_FLAG(USART3, UART_FLAG_RXNE)) {
        ucTemp = READ_REG(USART3->DR);// 读取数据
        if (esp_rx_frame.InfBit.frame_len < (esp_rx_frame.rx_buf_len - 1)) {
            esp_rx_frame.p_rx_buf[esp_rx_frame.InfBit.frame_len++] = ucTemp;
        }
    }
    //数据帧接收完毕
    if (UART_GET_FLAG(USART3, UART_FLAG_IDLE)) {
        esp_rx_frame.InfBit.frame_finished_flag = 1;
        // 添加结束符
        esp_rx_frame.p_rx_buf[esp_rx_frame.InfBit.frame_len] = '\0';
        //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)
        _UART_CLEAR_PEFLAG(USART3);
    }
}

void USART3_IRQHandler(void) {
    /* HAL库中断处理函数*/
    // HAL_UART_IRQHandler(&com3_handle);
    /* 自定义中断处理函数*/
    esp8266_tcp_server_IRQHandler();
}

封装esp8266服务端的代码(针对驱动层再进行一个封装)

头文件

/*******************************************************************************
 * @author scl
 * @email shchlu188@163.com
 ******************************************************************************/

#ifndef STM32F103VET6_ESP8266_ESP8266_SERVICE_H
#define STM32F103VET6_ESP8266_ESP8266_SERVICE_H

#include "service_include.h"
/*wifi 账号*/
#define WIFI_SSID "账号"
/*wifi 密码*/
#define WIFI_PWD "密码"
/*尝试执行次数*/
#define TRY_CNT 3
/*本地IP配置*/
#define LOCAL_SERVER_IP "192.168.199.12"
/*本地端口配置*/
#define LOCAL_SERVER_PORT 8000
/*宏定义调试日志*/
#define esp_ps(fmt, ...) os_ps(fmt,##__VA_ARGS__)
/*************************************ESP 服务端 STA模式**********************************************/
/**
 * @brief esp8266 在sta模式下初始化 sever
 * @return
 */
bool esp_sta_tcp_server_init(void);
/**
 * @brief esp8266 在sta模式下获取ip
 * @param dstIp 保存ip
 * @param len  dstIp的长度
 * @return
 */
bool esp_sta_tcp_server_get_ip(char *dstIp, uint8_t len);
/**
 * @brief esp8266 客户端请求处理
 * @param data 接收的数据
 * @param len  数据长度
 * @return
 */
bool esp_sta_tcp_server_request_handle(char *data, uint16_t len);

#endif //STM32F103VET6_ESP8266_ESP8266_SERVICE_H

源文件

/*******************************************************************************
 * @author scl
 * @email shchlu188@163.com
 ******************************************************************************/

#include "esp8266_service.h"
#include "module_esp8266.h"
#include "bsp.h"

/**
 * @brief 禁止接收数据(静态内联函数)
 */
sys_force_static_inline void esp_disable_rec() {
    UART_DISABLE_IT(USART3, UART_IT_RXNE);
}

sys_force_static_inline void esp_enable_rec() {
    UART_ENABLE_IT(USART3, UART_IT_RXNE);
}

bool esp_sta_tcp_server_init(void) {
    esp_ps("--esp_sta_tcp_server_init start---\r\n");
    /*使能信号*/
    esp8266_ch_enable(true);
    /*测试AT指令*/
    try_cnt_run (esp8266_base_at_cmd_test(10, 500), 1);
    esp_ps("esp8266_base_at_cmd_test ok\r\n");
    /*动态分配IP地址*/
    try_cnt_run(esp8266_dhcp_cur_set(sta_mode, true, -1), TRY_CNT);
    /* 设置为STA模式 */
    try_cnt_run(esp8266_wifi_mode_cur_set(sta_mode, -1), TRY_CNT);
    esp_ps("esp8266_mode_cur_set ok\r\n");
    /* 设备本地设备ip*/
    try_cnt_run(esp8266_ip_sta_cur_set(LOCAL_SERVER_IP, NULL, NULL, -1), TRY_CNT);
    esp_ps("esp8266_ip_sta_cur_set ok\r\n");
    /*连接WiFi*/
    try_cnt_run (esp8266_wifi_join_cur_set(WIFI_SSID, WIFI_PWD, NULL, -1), TRY_CNT);
    esp_ps("esp8266_wifi_join_cur_set ok\r\n");
    /*允许多连接*/
    try_cnt_run (esp8266_mul_connect_set(true, -1), TRY_CNT);
    esp_ps("esp8266_mul_connect_set ok\r\n");
    /*开启服务器模式*/
    try_cnt_run (esp8266_server_tcp_start(true, LOCAL_SERVER_PORT, -1), TRY_CNT);
    esp_ps("esp8266_server_tcp_start ok\r\n");
    /*设置服务器链接超时时间*/
    try_cnt_run (esp8266_server_tcp_timeout_set(20, -1), TRY_CNT);
    esp_ps("esp8266_server_tcp_timeout_set ok\r\n");

    esp_ps("--esp_sta_tcp_server_init end---\r\n");

    return true;
}

bool esp_sta_tcp_server_get_ip(char *dstIp, uint8_t len) {
    esp_ps("--esp_sta_tcp_server_get_ip start---\r\n");
    try_cnt_run (esp8266_ip_sta_inquire(dstIp, len, -1), TRY_CNT);
    esp_ps("--esp_sta_tcp_server_get_ip end---\r\n");
    return true;
}
/**
 * @brief 解析接收的数据
 * @param rec_data 接收的数据
 * @param no_id socket id
 * @param data_len 发送的数据长度
 * @return 实际数据的位置
 */
sys_force_static_inline char *parse_rec_data(char *rec_data, uint8_t *no_id, uint16_t *data_len) {
    static char *pre_str = "+IPD,";
    char *p_data = strstr(rec_data, pre_str);
    // 数据解析错误 +IPD,0,2:12
    if (!p_data) return false;
    /*跳过前缀*/
    p_data += strlen(pre_str);
    /*解析socket id 号*/
    *no_id = strtol(p_data, &p_data, 10);
    if (!*p_data) return false;
    /*解析数据长度 */
    *data_len = strtol(p_data + 1, &p_data, 10);
    if (!*p_data) return false;
    /*获取数据*/
    p_data += 1;
    return p_data;
}

bool esp_sta_tcp_server_request_handle(char *data, uint16_t len) {
    /* 禁止esp多个客户端同时发送数据 */
    uint16_t data_len = 0;/*实际客户端发送的数据长度*/
    uint8_t socket_id;/*实际客户端连接的端口号*/
    bool result_flag = true;
    /*禁止接收,保证同一时间只只处理一个请求 */
    esp_disable_rec();
    /*实际客户端发送的数据位置*/
    char *p_data = parse_rec_data(data, &socket_id, &data_len);
    if (p_data) {
        // todo 这里就是做业务逻辑控制的地方,目前只是回显
        esp_ps("parse_rec_data:%d %d %s\r\n", socket_id, data_len, p_data);
        if (esp8266_server_tcp_send_to(socket_id, p_data, data_len, -1)) {
            esp_ps("response ok\r\n");
        } else {
            esp_ps("response error\r\n");
        }
    } else {
        result_flag = false;
    }
    /*清除这次的请求数据,保证下次数据为最新的*/
    esp8266_clear_buf();
    /*开启请求,保证同一时间只只处理一个请求 */
    esp_enable_rec();
    return result_flag;
}

esp8266驱动配置文件的编写

在这里插入图片描述

/*******************************************************************************
 * @author scl
 * @email shchlu188@163.com
 ******************************************************************************/
#include "app_conf.h"
#include "module_esp8266.h"
#include "service.h"

/*创建数据接收帧*/
static char rx_buf[1024] = {0};
data_frame_t esp_rx_frame = {.p_rx_buf=rx_buf, .rx_buf_len=1024};

static void driver_init(void) {
    /*esp8266初始化*/
    esp8266_driver_init();
}




/*这句话就是将驱动导出到段中*/
DRIVER_EXPORT(esp8266_driver, driver_init);


/**
 * 测试阶段暂时使用此方法
 * */
void app_run() {
    if (esp_sta_tcp_server_init()) {
        os_ps("--esp_sta_tcp_server_init ok--\r\n");
        while (1) {
            if (esp_rx_frame.InfBit.frame_finished_flag) {
                esp_sta_tcp_server_request_handle(esp_rx_frame.p_rx_buf, esp_rx_frame.InfBit.frame_len);
            }
        }
    } else {
        os_ps("--esp_sta_tcp_server_init error--\r\n");
    }


}

代码测试

  1. 本次设置esp8266模块的IP为192.168.199.12,端口号为8000

主入口函数调整

在这里插入图片描述### 下载程序

观察日志记录

在这里插入图片描述

用客户端去连接192.168.199.12:8000

在这里插入图片描述

客户端

在这里插入图片描述

查看串口

  1. 接收数据正常

在这里插入图片描述
在这里插入图片描述

结尾

  1. ESP8266系列的章节也就更新完成,目前基础功能模板工程已经完。
  2. 目前可以根据此工程执行对应的指令控制的逻辑了。
  3. 后续在此基础之上设计一个指令集动态加载服务器接收处理和响应的框架。
  4. 完整的项目已上传到gitee,有需要自行查看开头结尾的git地址.

下个系列的目标

  1. 整合Freertos
  2. 整合Modbus协议和自定义协议框架

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

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

相关文章

【报错】检索 COM 类工厂中 CLSID 为 {28E68F9A-8D75-11D1-8DC3-3C302A000000} 的组件失败错误

【报错】检索 COM 类工厂中 CLSID 为 {28E68F9A-8D75-11D1-8DC3-3C302A000000} 的组件失败错误 情况描述解决方法修改目标平台CPU类型下载组件文件复制到指定路径运行指定命令行程序 情况描述 在使用C#进行工控软件开发&#xff0c;需要连接通过OPC连接DCS系统时&#xff0c;需…

STM32--ESP8266物联网WIFI模块(贝壳物联)--温湿度数据上传服务器显示

本文适用于STM32F103C8T6等MCU&#xff0c;其他MCU可以移植&#xff0c;完整资源见文末链接 一、简介 随着移动物联网的发展&#xff0c;各场景下对于物联控制、数据上传、远程控制的诉求也越来越多&#xff0c;基于此乐鑫科技推出了便宜好用性价比极高的wifi物联模块——ESP…

PowerShell系列(五):PowerShell通过脚本方式运行笔记

目录 一、四种执行方式介绍 1、当前文件夹运行命令 2、直接指定完整文件路径执行 3、通过cmd命令直接执行 4、通过Windows计划任务执行PowerShell脚本 二、通过脚本方式执行命令的优势 往期回顾 PowerShell系列&#xff08;一&#xff09;&#xff1a;PowerShell介绍和cm…

Java 异常处理和最佳实践(含案例分析)

概述 最近在代码 CR 的时候发现一些值得注意的问题&#xff0c;特别是在对 Java 异常处理的时候&#xff0c;比如有的同学对每个方法都进行 try-catch&#xff0c;在进行 IO 操作时忘记在 finally 块中关闭连接资源等等问题。回想自己对 java 的异常处理也不是特别清楚&#x…

第一章 软件工程概论

文章目录 第一章 软件工程概论1. 软件危机1.1.1 软件危机的介绍1.1.2 产生软件危机的原因与软件本身特点有关软件开发与维护的方法不正确有关 1.1.3 消除软件危机的途径例题 软件工程1.2.1 软件工程的介绍1.2.2 软件工程的基本原理1.2.3 软件工程方法学1. 传统方法学2. 面向对象…

集群间 ssh 互信免密码登录失败处理

一、问题描述 某次GreePlum集群免密配置过程中&#xff0c;需要使用普通用户实现ssh免密登录&#xff0c;前方反馈root用户已可完成免密登录&#xff0c;但普通用户同样配置&#xff0c;未生效&#xff0c;提示需输入密码才可以。 现场环境&#xff1a; 二、问题分析处理 …

安卓packageinfo的知识点

PackageInfo类包含AndroidManifest.xml文件的信息。 一些常用的属性如下&#xff1a; 获得PackageInfo //获取指定包名的packageInfo&#xff0c;并且包含所有的内容提供者 val pack context.packageManager.getPackageInfo(context.packageName,PackageManager.GET_PROVIDE…

GPT从入门到精通之 Tensorflow2.x 中如何使用 GPT 模型

Tensorflow2.x 中如何使用 GPT 模型 GPT 模型是自然语言处理&#xff08;NLP&#xff09;领域中一种重要的预训练模型。 TensorFlow2.x 是目前最流行的机器学习和深度学习框架之一&#xff0c;对 GPT 模型的支持度也非常高。在本篇文章中&#xff0c;我们将详细介绍如何使用 T…

前后端交互四、跨域与JSONP

零、文章目录 前后端交互四、跨域与JSONP 1、同源策略和跨域 &#xff08;1&#xff09;同源 如果两个页面URL的协议&#xff0c;域名和端口都相同&#xff0c;则两个页面具有相同的源。 例如&#xff0c;下表给出了相对于 http://www.test.com/index.html 页面的同源检测&…

【iOS_Swift_Alamofire实现网络请求】

文章目录 前言导入库原生网络请求单例封装一个原生网络请求Alamofire用法Alamofire的返回响应将返回的data转为字典/字符串 总结 前言 学习swift继续延续之前的思路&#xff0c;对照着OC学&#xff0c;从UI起手&#xff0c;学习到对应的自适应布局。 今天再次学习了swift的原生…

MySQL分库分表全攻略:从小白到大神的进阶指南!

大家好&#xff0c;我是小米&#xff0c;一个热爱技术的程序员。今天&#xff0c;我来和大家聊一下关于MySQL中的分库分表技术&#xff0c;相信对于开发者和DBA来说是一个非常重要的话题。 什么是分库分表 首先&#xff0c;我们先来了解一下什么是分库分表。分库分表是指将原本…

Apifox(1)比postman更优秀的接口自动化测试平台

Apifox介绍 Apifox 是 API 文档、API 调试、API Mock、API 自动化测试一体化协作平台&#xff0c;定位 Postman Swagger Mock JMeter。通过一套系统、一份数据&#xff0c;解决多个系统之间的数据同步问题。只要定义好 API 文档&#xff0c;API 调试、API 数据 Mock、API 自…

scratch躲避陨石 中国电子学会图形化编程 少儿编程 scratch编程等级考试三级真题和答案解析2023年5月

目录 scratch躲避陨石 一、题目要求 1、准备工作 2、功能实现 二、案例分析

性能测试计划,怎么写显得你能更专业?

性能测试计划 性能测试是一种非常重要的测试类型&#xff0c;用于确定系统或应用程序在特定负载下的性能指标。以下是一个性能测试计划的建议步骤&#xff1a; 目标 首先&#xff0c;定义性能测试的目标和范围。这包括测试的系统或应用程序以及所需测试的性能指标&#xff0…

基于蒙特卡洛模拟法的电动汽车充电负荷研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

开源项目的流程

开源的好处 增加知名度和使用量&#xff1a;开源项目可以让更多人知道和使用&#xff0c;进而增加项目的知名度和使用量。提高代码质量&#xff1a;开源项目需要接受有可能来自全球的开发者审核和参与&#xff0c;这样可以使得项目代码得到更多高质量的反复审查和改进。快速修…

计算机视觉 | 目标检测与MMDetection

目 录 目标检测的基本范式 滑窗使用卷积实现密集预测锚框多尺度检测与FPN 单阶段&无锚框检测器选讲 RPNYOLO、SSDFocal loss 与 RetinaNetFCOSYOLO系列选讲 一、目标检测的基本范式 1、什么是目标检测 给定一张图片 ——》用矩形框框出所感兴趣的物体同时预测物体类…

前后端交互一、服务器概念与初识Ajax

零、文章目录 前后端交互一、服务器概念与初识Ajax 1、上网的过程 网络相关知识请参考网络详解HTTP相关知识请参考HTTP详解 &#xff08;1&#xff09;客户端与服务器 **上网的目的&#xff1a;**通过互联网获取和消费资源 **服务器&#xff1a;**上网过程中&#xff0c;负…

基于STM32的智能粮仓系统设计

一、项目背景 随着粮食质量要求的提高和储存方式的改变&#xff0c;对于粮仓环境的监测和控制也愈发重要。在过去的传统管理中&#xff0c;通风、防潮等操作需要定期人工进行&#xff0c;精度和效率都较低。而利用嵌入式技术和智能控制算法进行监测和控制&#xff0c;不仅能够…

【Git】git push origin master时发生的各类错误汇总

文章目录 一、常见的git命令二、错误一三、错误二四、错误三五、问题解决 一、常见的git命令 使用 git 命令时&#xff0c;您可以执行一系列操作来管理代码仓库。下面是一些常用的 git 命令及其功能&#xff1a; git init: 在当前目录初始化一个新的 git 仓库。git clone <…