SoftwareSerial.h
文件解析
这个头文件定义了用于 ESP8266 和 ESP32 的软件串口实现的接口和一些功能。下面是关键部分的详细解释:
1. 文件头部注释
/*
SoftwareSerial.h - Implementation of the Arduino software serial for ESP8266/ESP32.
...
*/
这是文件的版权声明和许可证信息,表明这是为 ESP8266 和 ESP32 实现的 Arduino 软件串口库,并遵循 GNU Lesser General Public License。
2. 预处理器指令
#ifndef __SoftwareSerial_h
#define __SoftwareSerial_h
这部分代码是预处理器指令,用于防止头文件被多次包含,避免重复定义。
3. 包含其他头文件
#include "circular_queue/circular_queue.h"
#include <Stream.h>
这部分代码包含了其他必要的头文件,circular_queue.h
和 Stream.h
,分别用于循环队列实现和串流接口。
4. 命名空间和接口定义
namespace EspSoftwareSerial {...
}
所有的代码都被包含在 EspSoftwareSerial
命名空间中,以避免命名冲突。IGpioCapabilities
接口定义了 GPIO 引脚的功能检查,例如是否为有效引脚,是否有上拉电阻等。
5. GpioCapabilities
类
class GpioCapabilities : private IGpioCapabilities {
public:static constexpr bool isValidPin(int8_t pin) {...}...
};
这个类继承了 IGpioCapabilities
并实现了具体的方法来检查 GPIO 引脚的能力。不同的处理器(ESP8266 和 ESP32)有不同的实现。
6. BasicUART
类模板和 UART
类型
template <typename GpioType>
class BasicUART : public UARTBase {...
};using UART = BasicUART<GpioCapabilities>;
BasicUART
是一个模板类,使用 GpioCapabilities
作为模板参数来实现具体的 UART 功能。UART
是 BasicUART
的具体实例。
7. 导出接口
using SoftwareSerial = EspSoftwareSerial::UART;
using namespace EspSoftwareSerial;
这部分代码将 SoftwareSerial
定义为 EspSoftwareSerial::UART
,并使用 EspSoftwareSerial
命名空间,以便于使用。
8. 显式模板实例化
#if __GNUC__ < 12
...
#endif // __GNUC__ < 12
为了处理 GCC 编译器的特定问题,这部分代码显式实例化了一些模板函数。
9. 结束预处理器指令
#endif // __SoftwareSerial_h
结束预处理器指令,防止头文件被多次包含。
接下来,我将查看并解析 SoftwareSerial.cpp
文件。
SoftwareSerial.cpp
文件解析
这个文件实现了 SoftwareSerial.h
中声明的功能。以下是关键部分的详细解释:
1. 文件头部注释
/*
SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32.
...
*/
和头文件类似,这是版权声明和许可证信息。
2. 包含头文件
#include "SoftwareSerial.h"
#include <Arduino.h>
包含 SoftwareSerial.h
头文件和 Arduino 的核心头文件 Arduino.h
。
3. 使用命名空间
using namespace EspSoftwareSerial;
使用 EspSoftwareSerial
命名空间中的所有内容。
4. 条件编译变量定义
#ifndef ESP32
uint32_t UARTBase::m_savedPS = 0;
#else
portMUX_TYPE UARTBase::m_interruptsMux = portMUX_INITIALIZER_UNLOCKED;
#endif
根据是否是 ESP32 平台,定义不同的变量以处理中断。
5. 中断处理函数
ALWAYS_INLINE_ATTR inline void IRAM_ATTR UARTBase::disableInterrupts()
{
#ifndef ESP32m_savedPS = xt_rsil(15);
#elsetaskENTER_CRITICAL(&m_interruptsMux);
#endif
}ALWAYS_INLINE_ATTR inline void IRAM_ATTR UARTBase::restoreInterrupts()
{
#ifndef ESP32xt_wsr_ps(m_savedPS);
#elsetaskEXIT_CRITICAL(&m_interruptsMux);
#endif
}
这两个内联函数用于禁用和恢复中断,以确保在关键代码段内操作的原子性。
6. 常量定义
constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast<uint8_t>(0);
定义了一个常量 BYTE_ALL_BITS_SET
,它的值是 0xFF(所有位都设置为1)。
7. UARTBase
构造函数和析构函数
UARTBase::UARTBase() {
}UARTBase::UARTBase(int8_t rxPin, int8_t txPin, bool invert)
{m_rxPin = rxPin;m_txPin = txPin;m_invert = invert;
}UARTBase::~UARTBase() {end();
}
这些是 UARTBase
类的构造函数和析构函数。它们分别初始化和清理串口。
8. 设置 GPIO 引脚模式
void UARTBase::setRxGPIOPinMode() {if (m_rxValid) {pinMode(m_rxPin, m_rxGPIOHasPullUp && m_rxGPIOPullUpEnabled ? INPUT_PULLUP : INPUT);}
}void UARTBase::setTxGPIOPinMode() {if (m_txValid) {pinMode(m_txPin, OUTPUT);digitalWrite(m_txPin, !m_invert);}
}
这些函数设置接收和发送引脚的模式。根据引脚是否有效和是否需要上拉电阻,选择合适的模式。
9. 启动串口
void UARTBase::begin(uint32_t baud, Config config, int8_t rxPin, int8_t txPin, bool invert)
{// 配置波特率、引脚和其他参数
}
这个函数启动串口,配置波特率、引脚和其他参数。
10. 接收和发送处理函数
void UARTBase::handleRx()
{// 接收处理逻辑
}void UARTBase::handleTx()
{// 发送处理逻辑
}
这些函数处理接收和发送数据的逻辑。
11. 接收中断服务程序
void IRAM_ATTR UARTBase::rxISR(UARTBase* self)
{// 中断服务程序逻辑
}
这是接收中断服务程序,当接收到数据时触发,处理数据并调用相应的回调函数。
整体功能总结
SoftwareSerial.h
文件:定义了类和接口,用于管理软件串口的配置和功能,包括引脚有效性检查、串口配置等。SoftwareSerial.cpp
文件:实现了这些功能,提供了具体的串口初始化、中断处理、数据接收和发送的实现。
这两个文件共同实现了在 ESP8266 和 ESP32 上使用软件串口通信的功能,允许在硬件串口不够用的情况下通过软件模拟更多的串口。