嵌入式模板库 (Embedded Template Library)


sidebar_position: 3


ETL (嵌入式模板库)

Embedded Template Library,STL 库在嵌入式领域的一个补充,纯头文件库,并且尽量避免了继承来减少虚函数开销,官网

ETL 大概需要 20-50KB,STM32 空间参考:

  1. STM32 F103: RAM 20KB, FLASH 64KB
  2. STM32 L4R5: RAM (192+64+384)KB, FLASH 2MB

建议版本:C++11

容器

按规则存放元素的东西,支持常见容器,如: vector, map, bitset, …

虽然很多都支持动态内存,如 vector_ext。但在大多数情况下,不建议使用

vector 动态数组

最常见的容器,大小: (SIZE * sizeof(T)) + (2 * sizeof(size_t)) + sizeof(T*)

#include 
void vectorTest () {
    etl::vector<int, 10> base; // 初始化时需要声明大小, 所以push不会导致迭代器实现
    int *ptr = base.begin(); // 迭代器就是指针
    etl::vector<int, 10>::reverse_iterator rPtr = base.rbegin(); // 反向迭代器是真迭代器
    base.push_back(27); // STL方法
    bool isFull = base.full(); // 额外方法
    int *newInt = new int [5]{67, 89, 36};
    memcpy(ptr + 1, newInt, 5 * sizeof(int));
    base.repair(); // 通过底层函数移动元素后,必须使用该方法
}

pool 对象池

对象池可以减少从头创建每个对象的性能开销

#include 
void poolTest () {
    class Obj {
        public:
            Obj () { std::cout << "Obj ctor" << std::endl; }
            ~Obj () { std::cout << "Obj dtor" << std::endl; }
    };
    etl::pool<Obj, 10> objPool;
    Obj *newObj1 = objPool.create(); // 分配一个初始化好的对象
    Obj *newObj2 = objPool.allocate(); // 未初始化的裸指针
    objPool.destroy(newObj1); // 析构对象后放回池子
    objPool.release(newObj2); // 放回池子
}

bitset 比特数组

用于管理一系列 bit 位

#include 
void bitsetTest () {
    // 全特化模板: <8, uint8_t>, <16, uint16_t>, <32, uint32_t>, <8, uint64_t>
    etl::bitset<8, uint8_t> base{"0101"}; // <数量, 基本存储单位>
    bool result = base.any(0x3f); // 掩码, 仅使用低六位
    auto spanize = base.span(); // 返回span对象
}

optional 可选值

表示一个可能存在的值,比使用特殊值更为安全

#include 
etl::optional<int> optionalTest () {
    etl::optional<int> returnValue{42};
    returnValue = {}; // 不同于 stl, 这里将调用默认构造
    return returnValue;
}

auto result = optionalTest();
if (result.has_value())
    std::cout << result.value() << std::endl;
else
    std::cout << "INOP" << std::endl;

string 字符串

既有基本的 string 类型

#include 
void stringTest () {
    etl::string<10> str;
    str.repair(); // 同 vector.repair()
    size_t location = str.find(""); // STL方法
    bool isStart = str.starts_with(""); // 额外方法
}

也有很好用的其他类型转 string,当然也可以使用 string_stream

#include 
#include 
void toStringTest () {
    etl::format_spec format;
    format.hex().width(8).fill('0'); // 格式要求: 16进制, 宽度8, 左填充0
    etl::string<10> str;
    etl::to_string(401, str, format);
}

或者 string 转数值

#include 
void fromStringTest () {
    char cStr[]{"101"};
    etl::string<10> cppStr{"101"};
    int result = etl::to_arithmetic<int>(cStr, 3, etl::bin); // 以二进制形式转换
    result = etl::to_arithmetic<int>(cppStr, etl::radix::binary); // 以二进制形式转换
}

还有一些好用的小功能

void otherStringTest () {
    etl::string<32> str{"a,b:c"};
    etl::trim_whitespace_left(str); // 删除左侧空白 (' ', '\t', '\n', '\r', '\f', '\v')
    etl::reverse(str); // 翻转字符串
    etl::vector<etl::string<32>, 10> base;
    etl::optional<etl::string_view> token;
    while ((token = etl::get_token(str, ".,:", token, true)))
        base.emplace_back(token.value()); // 实现类似 python的split()
}

设计模式

聪明人总结出的方案

ETL 实现了全部或部分细节:观察者、责任链、单例、访问者

Observer 观察者模式

一个对象发生变化时,通知其他对象。注意虚函数带来的性能开销

#include 
struct Position {
    int x, y;
};
struct Velocity {
    double x;
};
typedef etl::observer<const Position&, Velocity> Observer; // 对象: 两个结构体

class Handler : public Observer { // 订阅消息, 纯虚函数, 需要实现全部方法
    public:
        void notification (const Position &pos) override {
            std::cout << pos.x << " " << pos.y << std::endl;
        }
        void notification (const Velocity v) override {
            std::cout << v.x << std::endl;
        }
};

class Driver : public etl::observable<Observer, 1> { // 发布消息
    public:
        void event () {
            Position pos{1, 2};
            notify_observers(pos);
        }
};

void observeTest () { // 发布消息后, 订阅者查收
    Driver driver;
    Handler handler;
    driver.add_observer(handler);
    driver.event();
}

Overload 重载

不能算设计模式吧,但这里的重载比一般情况要灵活一些

#include 
int result_int;
float result_float;
auto overloaded = etl::make_overload([](int i) { result_int = i; },
                                     [](float i) { result_float = i; });
template <typename T, typename TOverload>
void Function (T value, TOverload &&ol) {
    ol(value);
}
void overloadTest () {
    Function(1, overloaded);
}

数学

一系列算数和图像处理算法

Pseudo Moving Average 伪移动平均

一种计算移动平均的简化方法

#include "etl/pseudo_moving_average.h"
#include "etl/vector.h"
void pmaTest () {
    etl::vector<int, 10> data{1, 2, 3, 5};
    etl::pseudo_moving_average<int, 5, 2> base(0); // 样本数, 缩放因子, (初始值)
    std::copy(data.begin(), data.end(), base.input()); // 通过 copy() 添加一串
    base.add(27); // 通过 add() 方法添加一个值
    std::cout << base.value();
}

Hash & CRC 哈希及循环冗余校验

hash 支持嵌入式中常用算法:fnv、murmur3、jenkins

crc 支持非常全面,实现了绝大部分能想到的 crc 类型

#include 
void hashTest () {
    etl::murmur3<uint32_t> hasher;
    std::string sentense{"abcdefg"};
    hasher.add(sentense.begin(), sentense.end()); // 只支持 add() 方法
    uint32_t result = hasher.value();
}

#include 
void crcTest () {
    std::string sentense{"abcdef"};
    etl::crc32_bzip2 crc;
    crc.add(sentense.begin(), sentense.end()); // 既可以通过 add() 方法
    std::copy(sentense.begin(), sentense.end(), crc.input()); // 也能通过 copy() 实现
    uint32_t result = crc.value();
}

框架

比较大的设计,例如:委托、状态机、多任务

Finite State Machine 有限状态机

有限个状态间转换

看了会代码,感觉用不上,就这样吧

#include 

const etl::message_router_id_t CONTROL = 0;

// 事件
struct EventId {
    enum { START, STOP };
};
class Start : public etl::message<EventId::START> {};
class Stop : public etl::message<EventId::STOP> {};

// 状态
struct StateId {
    enum { IDLE, RUNNING, NUMBER_OF_STATES };
};
class Control : public etl::fsm {
    public:
        Control (): fsm(CONTROL) {}
};

// Idle状态, 接受启动事件
class Idle : public etl::fsm_state<Control, Idle, StateId::IDLE, Start> {
    public:
        etl::fsm_state_id_t on_event (const Start &event) {
            return StateId::RUNNING;
        }
        etl::fsm_state_id_t on_event_unknown (const etl::imessage &img) {
            return STATE_ID;
        }
};
// Running状态, 接受停止事件
class Running : public etl::fsm_state<Control, Running, StateId::RUNNING, Stop> {
    public:
        etl::fsm_state_id_t on_event (const Stop &event) {
            return StateId::IDLE;
        }
        etl::fsm_state_id_t on_event_unknown (const etl::imessage &img) {
            return STATE_ID;
        }
};

Idle idle;
Running running;
etl::ifsm_state *stateList[StateId::NUMBER_OF_STATES] = {&idle, &running};

void fsmTest () {
    Control control;
    control.set_states(stateList, etl::size(stateList));
    control.receive(Start()); // 接受一个启动事件, 转换为Running状态
}

你可能感兴趣的:(STM32入门HAL库,嵌入式硬件,stm32,单片机,c++)