Open Jimmy39 opened 1 year ago
砖已经抛出去了,成功砸得群友眼前一黑,看来填坑还得靠我自己,那我就慢慢填吧。
wikipedia 是这样说的, IEEE 754 是数值和符号的一组表示形式,并且也可以包括编码格式。也就是说 IEEE 754 定义了一组映射,定义域是部分实数的集合,值域是特定长度的全部二进制数,将一个实数依照规则映射到一个特定长度2进制数。
以 binary32(float) 为例,其在 IEEE 754 中的数字将会表示成 $(-1)^s 1.c 2^q$ 的形式,其中
Sign(1位):用来表示浮点数是正数还是负数,即数字s,0表示正数,1表示负数。
Exponent(8位):指数部分。即数字q,但是这里不是直接存储q,为了同时表示正负指数以及他们的大小顺序,这里实际存储的是q+127。
Mantissa(23位):尾数部分。即数字c。
根据规则123.456对应的编码为0x42f6e979
我们希望构建一个函数来描述 IEEE 754 规范的这种映射关系,但是函数是一种数对数的映射。那么这里我们就可以把编码后的2进制数据按照2进制整数的权读出,这样就能得到一个自然数。比如上文中的 0x42f6e979 的十进制即为 1123477881。
这样我们有了一个从一个数到另一个数的映射。除0之外,一个定义域里的元素只对应一个值域里的元素,所以这就是一个函数 $y = IEEE754binary32(x) (x \neq 0)$。
这一过程,用c语言实现的话那就是:
y = *(uint32 *)&(float)x;
我们可以注意到由于数字量是离散的,所以这个函数的值域也是离散的。为了便于下一步处理,我们可以限定定义域为:除0以外能用 IEEE 754 精确表示的实数,对于 binary32(float) 即为 $x \in \lbrace (-1)^s ( 1 + c 2^{-23} ) * 2^q | s \in \lbrace 0,1 \rbrace, c \in \lbrace x | x \in z, 0 \leq x \leq 0x7fffff \rbrace, q \in \lbrace x | x \in z, -127 \leq x \leq 128 \rbrace \rbrace$,这样 $y = IEEE754binary32(x)$ 就成了一个离散函数。
现在我们定义:将离散的 IEEE 754 函数各点相连,组成一个包含这个原离散函数的各点的这个连续的函数为 IEEE 754 模拟变换的函数。传输函数为这一函数的模拟电路称为 IEEE 754 模拟变换。
说到这里有的朋友已经想到这东西的巧妙之处了,有的朋友可能还一头雾水,这里我以 binary32(float) 为例示范一下这东西是怎么用的。
假设我们有一个理想的完全线性的无噪声的32位ADC,我们设定其基准为 $2^{32}$ uV,这样其最小分辨电压就是1uV。
如果我们输入123.456uV,那将从ADC中读出0x7B(整形的123)。
我们的目标是实现一个浮点ADC,也就是输入123.456uV,读出0x42f6e979(float格式的123.456)。
这个 IEEE 754 模拟变换电路就能实现这一功能。123.456uV输入模拟计算电路,根据IEEE 754 模拟变换传函的定义,输出为1123477881uV,而1123477881uV电压输入ADC,将可以读出0x42f6e979(以整形来看是1123477881,但是以float来看的话就是123.456)。
这样由于 IEEE 754 模拟变换电路压缩了输入信号的动态范围,ADC系统的测量范围就从 uint32_t 的 $0$ ~ $2^{32}$ 扩展到了 binary32(float) 的 $-3.4028235 10^{38}$ ~ $3.4028235 10^{38}$ 。
当然,更实用一点的做法是把16位ADC的测量范围从 uint16_t 扩展到 binary16(Harf) 的范围,或者按照 IEEE 754 的规范自定义一种合适字长的二进制浮点数。
隐约记得这个世界上好像有一种叫做浮点ADC的东西 https://ieeexplore.ieee.org/document/814428 但是好像简单看了看不太一样? 顺便一提貌似ElectroBOOM的硕士论文也是浮点ADC
我在想,咱能不能先弄个4bit的原理样机
我们设 $y = IEEE754binary()$,根据 IEEE 754 规范,以2为基底的时候编码可以分为三部分,1位符号位s,m位指数q,n位尾数c,表示的数 $x = -1^s ( 1 + c 2^{-n} ) 2^{q - 2^{m -1} + 1}$,以二进制整数的权来读出的数 $y = s 2^{ m + n } + q * 2^n + c$ 。PS: IEEE 754 规范并没限定以2位基底,以3为基底的3进制浮点或者以10为基底的10进制浮点(我是说科学计数法)也是可以放入 IEEE 754 规范的。
首先只考虑正数,令 $s = 0$。将 $x = ( 1 + c 2^{-n} ) 2^{q - 2^{m - 1} + 1}$ 变换可得:
$$c = (x 2^{2^{m - 1} - 1 - q} - 1) 2^{n}$$
带入 $y = q * 2^n + c$ ,得:
$$y = q 2^n + (x 2^{2^{m - 1} - 1 - q} - 1) 2^n = (x 2^{2^{m - 1} - 1 - q} + q - 1) * 2^n$$
其中, $q \in z$ 且 $2^{q - 2^{m - 1} + 1} \leq x \lt 2^{q - 2^{m - 1} + 2}$。
Chennn老师建议我用4位浮点更易理解。float确实好长好长,但是4位有点短,之后我都用9位浮点来举例吧。这里我们定义 binary9:包括1位符号,4位指数,4位尾数,即 $m = n = 4$。那么就有:
$$x = -1^s ( 1 + c 2^{-4} ) * 2^{q - 7}$$
//正一或负一1.c2的q-8次方
$$y = s 2^8 + q 2^4 + c$$
//简单的数字拼接:第9位、高4位、低4位
我们先手算一个 $x = 3.25$ ,转换为2进制小数是 $11.01B$,那么指数是 $1 + 7 = 8$,尾数是 $1010B = 0xA$,编码结果为 $y = 0x08A = 138$
使用之前我们推导的公式,
$2^{8 - 7} \leq 3.25 \lt 2^{8 - 6}$ ,有 $q = 8$
$$y = 8 16 + (3.25 0.5 - 1) * 16 = 138$$
同样,我们把123.456带入 binary32(float) 的公式:
$$2^{133 - 127} \leq 123.456 \lt 2^{133 - 126}$$
$$y = 133 2^{23} + (123.456 2^{127 - 133} - 1) * 2^{23} = 1123477880.832 ~= 1123477881$$
与上文C语言运算结果相同。
隐约记得这个世界上好像有一种叫做浮点ADC的东西 https://ieeexplore.ieee.org/document/814428 但是好像简单看了看不太一样? 顺便一提貌似ElectroBOOM的硕士论文也是浮点ADC
我感觉他这个更倾向于通过adc的设计提升adc的动态范围? 我是想用一个模拟电路压缩信号的动态范围同时恰好满足或基本满足 IEEE 754 以方便处理器处理
我们可以直接看 binary9,这里放着一个表格:
转成10进制的输出 y | 浮点编码内容 | 二进制小数 | 十进制小数 x |
---|---|---|---|
1 | 0x001 | 1.0001B*2^(-7) | 0.008300781 |
2 | 0x002 | 1.0010B*2^(-7) | 0.008789063 |
3 | 0x003 | 1.0011B*2^(-7) | 0.009277344 |
… | … | … | … |
13 | 0x00d | 1.1101B*2^(-7) | 0.014160156 |
14 | 0x00e | 1.1110B*2^(-7) | 0.014648438 |
15 | 0x00f | 1.1111B*2^(-7) | 0.015136719 |
16 | 0x010 | 1.0000B*2^(-6) | 0.015625 |
17 | 0x011 | 1.0001B*2^(-6) | 0.016601562 |
18 | 0x012 | 1.0010B*2^(-6) | 0.017578125 |
19 | 0x013 | 1.0011B*2^(-6) | 0.018554687 |
… | … | … | … |
29 | 0x01d | 1.1101B*2^(-6) | 0.028320312 |
30 | 0x01e | 1.1110B*2^(-6) | 0.029296875 |
31 | 0x01f | 1.1111B*2^(-6) | 0.030273437 |
… | … | … | … |
简单观察即可发现,从 $x = 0.008300781 , y = 1$ 到 $x = 0.015625 , y = 16$, $x$ 每增加 $2^{-11}$ $y$ 增加 $1$。换一种模拟电路的说法,此时的交流增益是 $2^{11}$。
同样观察从从 $x = 0.015625 , y = 16$ 到 $x = 0.030273437 , y = 31$ , $x$ 每增加 $2^{-10}$ $y$ 增加 $1$,这时的交流增益是 $2^{10}$。
这样我们就能看出来这是一个随着输入增加增益逐级下降的电路,使用逐级饱和电路就可以完成。调节限幅放大器的限幅电压就能校正指数,调节每级增益就可以使校正尾数。
我们看一个带有衰减器的由 $k$ 级限幅放大器组成的逐级饱和放大器,也就是这样:
input->ATT->amp->amp->amp->amp->amp->amp->amp-
输出是各级放大器输出之和。
我们令第 $n$ 级的放大器的增益为 $A_n$,限幅的电压为 $m$。这时我们可以得到:
当输入小电压时,第 $n$ 级的输出电压为 $vn = \displaystyle \prod{i = 1}^{n}{Ai} * v{in} * A_{ATT}$。
同时有当 $\displaystyle \prod_{i = 1}^{n}{Ai} * v{in} A{ATT} \lt m \leq \displaystyle \prod{i = 1}^{n + 1}{A_i} v{in} * A{ATT}$ ,第 $n$ 级未饱和而第 $n + 1$ 级饱和。整理一下可得:
$$\displaystyle \prod_{i = 1}^{n + 1}{\frac {1}{Ai}} * \frac {m}{A{ATT}} \leq v{in} \lt \displaystyle \prod{i = 1}^{n}{\frac {1}{Ai}} * \frac {m}{A{ATT}}$$
这时候的输出电压为:
$$vout = \displaystyle \sum{i=1}^{k}{Vi} = \displaystyle \sum{i=1}^{n}{\displaystyle \prod_{j = 1}^{i}{Aj} * v{in} A_{ATT}} + (k - n) m$$
一些动态范围巨大的信号时常给我们造成麻烦,为了处理里这些麻烦的信号,我们有多种选择:
使用非线性电路压缩动态范围最简单的方式是直接使用对数放大器,但是这样存在几个显而易见的问题。首先精密、带宽足够、正或负信号的响应具有对称性、价格实惠的的对数放大器也难以选型。其次对数后的信号经过ADC量化后既不是整型定点也不是IEEE754浮点,为后续数字信号处理增加了困难。
考虑到不给数字信号处理增加困难,我们可以从IEEE754入手。稍加思考我们就可以发现,IEEE754实际上也是一种类对数的压缩,其值与直接二进制读出的值的对应关系是使用一系列折线组成对数的趋势,与逐级饱和放大器所有输出之和是一样的曲线。所以我们可以使用如下电路结构来实现模拟计算转换出IEEE754。
处理后的信号动态范围得到有效压缩,可以不需要PGA直接送入ADC处理,且ADC量化后的二进制符合IEEE754(对于正压,负压需要稍作处理),方便进行后续数字信号处理。
关于符号位,可以通过两种方式解决。一是模拟方法,绝对值电路和过零比较,符号位和其他位分别处理。二是数字信号处理方法,使用正负双电源的ADC或电平抬升,量化后符号位不变(或取反)其他位取绝对值即可。
这只是一个框架性质的抛砖引玉,实际实现中应当具体情况具体分析。32位长度的float对于大部分情况下显然过于充足了,32位的ADC一共没有几款。8位长度指数部分意味着256级限幅放大,小数部分23位的长度实际中也也没必要(也做不到)。这时候我们可以对IEEE754进行适当裁剪,根据实际需求确定指数部分和小数部分长度后计算逐级饱和放大器的参数。如果是并口ADC的话可以直接对数据线操作将数据补零补全成32位,串口ADC也只需要简单的移位拼接就可以处理成IEEE754。