嵌入式数据库,ADC,C语言以及无源滤波网络文章分享

轻如羽翼,超轻量的嵌入式数据库!

FlashDB简介

FlashDB 是一款超轻量级的嵌入式数据库,专注于提供嵌入式产品的数据存储方案。与传统的基于文件系统的数据库不同,FlashDB 结合了 Flash 的特性,具有较强的性能及可靠性。并在保证极低的资源占用前提下,尽可能延长 Flash 使用寿命。

FlashDB 提供两种数据库模式:

  • 键值数据库 :是一种非关系数据库,它将数据存储为键值(Key-Value)对集合,其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。
  • 时序数据库 :时间序列数据库 (Time Series Database , 简称 TSDB),它将数据按照 时间顺序存储 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。

https://gitee.com/armink/FlashDB

应用场景

如今,物联网产品种类越来越多,运行时产生的数据种类及总量及也在不断变大。FlashDB 提供了多样化的数据存储方案,不仅资源占用小,并且存储容量大,非常适合用于物联网产品。下面是主要应用场景:

键值数据库

  • 产品参数存储
  • 用户配置信息存储
  • 小文件管理

时序数据库

  • 存储动态产生的结构化数据:如 温湿度传感器采集的环境监测信息,智能手环实时记录的人体健康信息等
  • 记录运行日志:存储产品历史的运行日志,异常告警的记录等

主要特性

  • 资源占用极低,内存占用几乎为 0 ;
  • 支持 多分区,多实例 。数据量大时,可细化分区,降低检索时间;
  • 支持 磨损平衡 ,延长 Flash 寿命;
  • 支持 掉电保护 功能,可靠性高;
  • 支持 字符串及 blob 两种 KV 类型,方便用户操作;
  • 支持 KV 增量升级 ,产品固件升级后, KVDB 内容也支持自动升级;
  • 支持 修改每条 TSDB 记录的状态,方便用户进行管理;

例子

使用键值数据库存储UUID

#include <stdio.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <flashdb.h>

#define FDB_LOG_TAG "[main]"

static pthread_mutex_t kv_locker;
static uint32_t init_data = 0;
static struct fdb_kvdb kvdb = { 0 };

static struct fdb_default_kv_node default_kv_table[] = 
{
    {"init_data", &init_data, sizeof(init_data)}, 
};

static void lock(fdb_db_t db)
{
    pthread_mutex_lock((pthread_mutex_t *)db->user_data);
}

static void unlock(fdb_db_t db)
{
    pthread_mutex_unlock((pthread_mutex_t *)db->user_data);
}

int main(void)
{
    fdb_err_t result;
    bool file_mode = true;
    uint32_t sec_size = 4096, db_size = sec_size * 4;
    struct fdb_default_kv default_kv;
    struct fdb_blob blob;

    // 默认 KV 集合
    default_kv.kvs = default_kv_table;
    default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);

    // 设置加解锁函数
    pthread_mutex_init(&kv_locker, NULL);
    fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, (void *)lock);
    fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, (void *)unlock);

    // 设置扇区
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);

    // 设置数据库最大大小
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
    
    // 设置文件模式
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
    
    // 设置数据库文件夹
    mkdir("fdb_kvdb1", 0777);

    // 初始化KV数据库
    result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, &kv_locker);
    if (result != FDB_NO_ERR) 
    {
        return -1;
    }

    // 写入UUID
    char uuid_str[64] = "3F2504E0-4F89-11D3-9A0C-0305E82C3301";
    fdb_kv_set(&kvdb, "uuid", uuid_str);
    FDB_INFO("create the 'uuid' blob KV, value is: %s\n", uuid_str);

    // 读取UUID
    char *return_value = NULL;
    char dst_uuid_str[64] = {0};
    return_value = fdb_kv_get(&kvdb, "uuid");
    if (return_value != NULL) 
    {
        strncpy(dst_uuid_str, return_value, sizeof(dst_uuid_str));
        FDB_INFO("get the 'uuid' value is: %s\n", dst_uuid_str);
    }

    return 0;
}

fdb_kvdb_init为初始化kv数据库的接口,需要传参:

在初始化kv数据库之前,可根据实际需要调用fdb_kvdb_control接口对数据库进行一些控制设置操作。支持的命令控制字如下:

#define FDB_KVDB_CTRL_SET_SEC_SIZE     0x00             /**< 设置扇区大小,需要在数据库初始化前配置 */
#define FDB_KVDB_CTRL_GET_SEC_SIZE     0x01             /**< 获取扇区大小 */
#define FDB_KVDB_CTRL_SET_LOCK         0x02             /**< 设置加锁函数 */
#define FDB_KVDB_CTRL_SET_UNLOCK       0x03             /**< 设置解锁函数 */
#define FDB_KVDB_CTRL_SET_FILE_MODE    0x09             /**< 设置文件模式,需要在数据库初始化前配置 */
#define FDB_KVDB_CTRL_SET_MAX_SIZE     0x0A             /**< 在文件模式下,设置数据库最大大小,需要在数据库初始化前配置 */
#define FDB_KVDB_CTRL_SET_NOT_FORMAT   0x0B             /**< 设置初始化时不进行格式化,需要在数据库初始化前配置 */

这个demo基于Linux系统运行,需要设置成文件模式,存储到文件中进行测试。

初始化 KVDB 前通常需要通过 control 函数设置 加锁回调 与 解锁回调 。对于裸机平台,加锁与解锁回调通常设置为关中断与开中断函数。而 RTOS 平台一般使用 mutex 互斥锁或 二值信号量 的 take 及 release 动作作为加锁与解锁的方式。

查看原文:https://www.dianyuan.com/eestar/article-8527.html


经常混淆的ADC输入类型!

这篇文章我们来聊聊ADC的输入类型。ADC的输入类型根据ADI的官网,分为了3种类型,单端,差分,伪差分。如下图快速选型的界面所示。

同时,TI的官网对ADC的输入类型划分也是同样的3种类型。

可见,两个器件厂家对ADC的输入类型都是这样定义的。为了减少ADC输入类型的种类,伪差分也可以归为差分类型。也就是ADC的输入类型整体上可以分为单端和差分两种。1、单端输入单极性信号是相对于地而言,是单极性的ADC。

单端输入和差分输入是针对ADC的输入。对ADC的输入端而言,存在两种类型,一种是只有一个输入APM,电压信号相对地而言;另外一个是存在两个输入APM和APN,但是APN接地,这两种都属于单端类型的输入。如下图的ADC所示,ADC081C021的输入端端口只有一个输入引脚,

这种ADC的输入引脚只有VIN,输入信号和ADC供电电源的GND是统一的GND。另外一种也是单端类型的ADC,但是输入端口有两个,APM和APN,如下图单端型的ADC所示:

这种ADC的输入引脚有两个端口,但是其中AINN接地,这样的ADC也是属于单端类型的ADC。2、差分输入

很明显是有两个端口,并且AINN引脚不接地,,如下面差分输入型ADS9120的ADC所示:

AINP和AINM都可以和输入信号链接。

引脚上也有对应的信号输入管脚。

3、单极性输入和双极性输入

介绍完单端和差分输入后,还有一个容易混淆的概念是单极性输入和双极性输入。单极性和双极性是对电平信号的约束。单极性意味着电平信号为正。双极性意味着电平信既有正电压也有负电压。总结一下就是:信号和地之间是否有小于0的部分,如果没有就是单极性输入,如果有就是双极性输入。

单极性和双极性的信号输入,对于ADC来说,转换函数存在比较大的区别,如下图所示:

单极性的信号输入,ADC只需要转换0Vd电压以上的信号,转换函数在如左图所示。如果是双极性的信号输入ADC的需要转换0V以下的信号,在整个ADC的量程测量范围上具有较大的区别。信号有单极性和双极性之分,ADC的输入有单端和差分之分,两两组合存在四种可能性。

如果将上面四种类似的输入用在ADC的输入端表示,即为下图所示:

4、伪差分和真差分

ADC的差分输入从前面的ADI和TI的官方选型可以看出,输入类型存在伪差分这种类型。伪差分是相对真差分而言。


输入信号只要单个的信号称为单端信号,输入信号是差分时,被称为是差分信号。单端信号容易受到共模信号的干扰,不利于信号处理,而差分信号能有效避免共模信号的干扰,因此,为了尽可能降低对单端信号的干扰,引入了伪差分。

正常的差分信号是方向相反,大小相等的信号,这样的信号直接接在ADC的AINP和AINM上,而差分信号的AINM不是接差分信号,是接上1/2VREF,即固定电平。如下所示:

根据LTC2311器件的应用框架可知,全差分和伪差分的区别就在AINN引脚上的配置,当原始输入信号在时间轴上本身就是等大反向的差分信号,则可以直接采用差分输入,当输入的信号是单端的,但是为了降低共模干扰,可以采用伪差分。在实际电路设计时,如果使用伪差分电路,AINN端的直流电压大小一般设置在AINP端信号1/2VPP,从上面的图中可以看出,输入电压范围在0~VREF,AINN端的电压设定在1/2VREF。这样做的目的是为了充分利用ADC的输入能力。共模电压是固定的,共模电压对称。共模电压必须为满量程的一半。真差分共模电压不是固定的,允许任意的共模电压范围。

查看原文:https://www.dianyuan.com/eestar/article-8508.html


绝了,还能这么转化十六进制字符串-C语言骚操作

前段时间移植了 tinyUSB 开源库,使 STM32F013C8T6 成功模拟了 7 个串口 独立的 CDC 设备(参考了知乎上一篇文章,不过那个实现还有一些问题,并且不能单独设置每个串口的波特率)。

后面又移植了 lwip ,成功实现了虚拟网络适配器:

于是准备看看源码实现,突然发现一条看不懂的代码:

_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];

乍看一下,字符串不像字符串,数组也不像,直到看到说明,才大概明白,运行之后,颠覆三观了。。。

完整代码如下:

static uint16_t _desc_str[32 + 1];
unsigned int chr_count = 0;
uint8_t tud_network_mac_address[6] = {0x02,0x02,0x84,0x6A,0x96,0x00};
// Convert MAC address into UTF-16
for (unsigned i=0; i<sizeof(tud_network_mac_address); i++) {
    _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
    _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}

功能就是将 mac 地址转化为 16 进制,并且还是 utf-16 格式(16 bit 一个字符)

最终转化效果如下(16bit 和字符串显示):

并且虽然 "0123456789ABCDEF" 写了两次,但内存中只有一份拷贝,因此并不会占用更多空间,并且因为使用索引形式,效率也是杠杠滴!

嗯,以后十六进制转化就用这个代码了。

查看原文:https://www.dianyuan.com/eestar/article-8511.html


为什么有的无源滤波网络要采用共模驱动的结构?

在设计仪表放大器或差分放大器应用电路的时候,为了避免噪声和干扰影响输出信号质量,通常都会在其前端利用阻容等无源元件搭建滤波电路,尽可能滤除输入信号中的噪声和干扰。前端滤波电路除了常见的结构外,还会出现共模驱动的结构,为什么会采用这种结构?这种结构有什么特点呢?下面就来简单分析一下。

差分结构的一个显著特点就是抗干扰能力强,因为差分结构传输的信号是两根信号线做差获得的,在理想状态下,即使两根信号线上会出现干扰信号,干扰信号也是一模一样的,即共模干扰,其在输入端做差之后会变为零,不会对后级电路产生影响。

如果差分电路前端滤波的阻容参数完全一致,正负输入端的共模干扰在经过滤波之后信号仍一模一样,依旧不会对后级电路产生影响。但是,基本不存在完全一致的元件,电阻电容总有误差,这就导致正负输入端的共模干扰滤波之后的信号存在差异,输入做差之后信号不为零,这一差异会被放大电路放大,影响输出信号质量,即共模干扰转化成了差模干扰

但是,如果滤波网络采用共模驱动,情况就不一样了,对于共模干扰,虽然滤波网络中阻容参数存在差异,但是网络中没有电流,即滤波网络对共模干扰不起作用,共模干扰直接向后级传输,在后级做差后变为零,不会对后级输出产生影响。

查看原文:https://www.dianyuan.com/eestar/article-8532.html


更多精彩内容,尽在电子星球 APP(https://www.eestar.com/)

六篇技术文章,让你秒懂电容的脾气秉性

七篇DIY技术文章献给你,让你脑洞全开

五篇文章帮你开启DSP的学习思路

汇总篇:关于PID知识,重点在此

原文链接:,转发请注明来源!