Power's Wiki -

ADC 与 DAC 基础知识

在现实世界中,常见的信号大都是模拟量,像温度、声音、气压等,但在信号的处理与传输中,为了减少噪声的干扰,较多使用的是数字量。因此我们经常会将现实中的模拟信号,通过 ADC 转换为数字信号进行运算、传输、储存,再通过 DAC 转换为模拟信号,呈现出来。 但要注意的是,现实中的模拟量连续的,意味着它有无限的分辨率,但转换为数字量之后,将会丢失一定的精度,在时间和幅度上都会变成离散的值。 ADC 基本原理 ADC(Analog-to-Digital Converter)指模拟 / 数字转换器,可将真实世界的模拟信号,例如温度、压力、声音或者图像等,转换成更容易储存、处理和发射的数字形式。 采样 因为输入的模拟信号是连续的,而将要输出的数字信号是离散的,所以只能进行瞬时采样,再将采样值转换为输出的数字量,再重新开始下一轮的采样。 为了能准确无误用信号 $v_s$ 表示出模拟输入信号 $v_1$,至少需要满足采样定理,即采样频率 $f_s$ 在模拟输入信号最高频率分量 $f_{i(max)}$ 的 2 倍以上(通常会取 3~5 倍,但太高的频率需要更快的工作速度,需要综合成本考虑): $$ f_s≥2\cdot f_{i(max)} $$ 只要满足了采样定理,即可用低通滤波器,将 $v_s$ 还原为 $v_1$。滤波器电压传输系数应在低于 $f_{i(max)}$ 时保持不变,在 $f_s-f_{i(max)}$ 前迅速下降为 0。 保持 保持电路能够采样结束后,让信号保持一段时间,使 ADC 有充分时间进行转换。一般采样脉冲频率越高、采样越密,采样值就越多,采样保持电路的输出信号就越接近输入信号的波形。采样 - 保持电路的基本形式如下: 采样 - 保持的基本步骤: 当采样控制信号 $v_L$ 为高电平时,使 MOS 管 $T$ 导通,$v_1$ 经过电阻 $R_1$ 和 MOS 管 $T$,给电容 $C_H$ 充电。 若取 $R_1=R_F$,则充电结束后 $v_0=v_c=-v_1$。 当采样控制信号 $v_L$ 跌落回电平时,MOS 管 $T$ 截止,电容 $C_H$ 上的电压不会突变,所以 $v_0$ 也能保持一段时间,采样结果得以被记录下来。 量化 采样得到的数字量,必须为某个规定的最小数值单位的整数倍,这个转换过程称为量化,所取的最小数量单位称为量化单位 $\Delta$。数字信号最低有效位 LSB 的 1 所代表的数量大小就等于 $\Delta$。 因为模拟电压是连续的,不一定能被 $\Delta$ 整除,因此会出现量化误差。 量化级越细,量化误差就越小,所用二进制代码的位数就越多,电路也越复杂。 编码 将量化的结果用二进制(或其他进制)表示出来,称为编码。 ADC 常见类型 并联比较型(Flash) 并联比较型 ADC 又称 Flash ADC,属于直接 ADC,能将输入的模拟电压直接转换为输出的数字量,不需要经过中间变量转换。它由一系列电压比较器组成,每个比较器将输入信号与唯一的分压后的参考电压进行比较。比较器的输出连接编码器电路的输入,产生二进制的输出。 不仅在操作理论方面是最简单的,而且在速度方面也是最有效的 ADC 技术,仅受比较器和栅极传播延迟的限制。不幸的是,对于任何给定数量的输出位,它是最密集的组件 并联比较型 ADC 的转换速度是最快的,但缺点是需要使用很多电压比较器和大规模的代码转换电路(常见的并联比较型输出大都在 8 位以下)。 逐次逼近型 逐次逼近型(Successive Approximation)ADC 采用的是一种反馈比较型电路结构。由比较器、DAC、寄存器、时钟脉冲源和控制逻辑等组成: 其原理是,设定一个数字量,通过 DAC 得到一个对应的输出模拟电压。将这个模拟电压和输入的模拟电压信号从最高位开始顺序地相比较,如果两者不相等,则调整所取的数字量,直到两个模拟电压相等为止,最后所取的这个数字量就是所求的转换结果。其过程像用天平去称量位置重量的物体,先加大砝码,再逐次添加或换用小砝码。 逐次逼近型 ADC 的优点是速度高,功耗低,在低分辨率(12 位)下具有性价比优势;缺点是转换速率一般,电路规模中等。 双积分型(V-T) 双积分型 ADC 是一种间接 ADC,它首先将输入的模拟电压信号转换成与之成正比的时间宽度信号,随后在此时间宽度内,对固定频率的时钟进行脉冲计数,计数的值就是正比于模拟输入电压的数字信号。因此,也将这种 ADC 称为电压 - 时间变换型(V-T)ADC。 双积分型 ADC 由积分器、比较器、计数器、控制逻辑和时钟信号源组成,如图: 双积分型 ADC 的优点是工作性能稳定(两次积分,排除 RC 参数差异)、抗干扰能力强(积分受噪声影响不大);缺点是转换速率低(转换精度依赖于积分时间)。 Σ-Δ 型 Σ-Δ 调制型 ADC 的原理与上文的并联型与逐次逼近型 ADC 不同,它不是将采样信号的绝对值进行量化编码,而是将两次相邻采样值之差(增量)进行量化与编码的。其基本结构如下: 它由线性电压积分器、1 位输出量化器、1 位输入 DAC 和一个求和电路组成。经过量化器处理输出的数字信号 $V_0$,经过 DAC 转换为模拟信号 $V_F$,并负反馈至输入端的求和电路,与输入信号 $v_1$ 相减,得到差值 $v_D$。积分器对 $v_D$ 作线性积分,输出电压 $v_{INT}$ 至量化器,由量化器量化为 1 位的数字量输出。由于采用 1 为输出的量化器,所以在连续工作的状态下,输出信号 $V_0$ 是由 0 和 1 组成的数据流。 Σ-Δ 调制型 ADC 的优点是可以容易地做到高分辨率测量;缺点是转换速率低、电路规模大。 电压 - 频率变换型(V-F) 电压 - 频率变换型(V-F)ADC 是一种间接 ADC。主要由 V-F 变换器(也称为压控振荡器 Voltage Controlled Oscillator,简称 VCO)、计数器及其时钟信号控制闸门、寄存器、单稳态触发器等几部分构成: 其原理是: 将输入的模拟电压信号转换为对应的频率信号。 在固定的时间内对频信号率计数。 计数结果正比于输入电压的幅值。 ADC 主要参数 分辨率:输出数字量变化一个相邻数值所需输入模拟电压的变化量,一般用二进制的位数表示,分辨率为 n 表示是满刻度 Fs 的 2 的 n 次方分之一。 量化误差:ADC 的有限位数对模拟量进行量化而引起的误差。要准确表示模拟量,ADC 的位数需要很大甚至无穷大,所以 ADC 器件都有量化误差。一个分辨率有限的 ADC 的阶梯状转换特性曲线与具有无限分辨率的 ADC 转化特性曲线之间的最大偏差就是量化误差。 转换速率:每秒进行转换的次数。 转换量程:ADC 所能测量的最大电压,一般等于参考电压,超过此电压有可能损毁 ADC。当信号较小时可以考虑降低参考电压来提高分辨率,改变参考电压后,对应的转换值也会改变,计算实际电压时需要将参考电压考虑进去,所以说一般参考电压都要做到很稳定且不带有高次谐波。 偏移误差:ADC 输入信号为 0 时,但 ADC 转换输出信号不为 0 的值。 满刻度误差:ADC 满刻度输出时对应的输入信号与理想输入信号值之差。 线性度:实际 ADC 的转移函数和理想直线的最大偏移。 DAC 基本原理 DAC(Digital-to-Analog Canverter),指数字 / 模拟转换器。可将数字量转换为成比例的模拟电压或电流。举个例子,计算机可能产生范围从 00000000 到 11111111 的数字输出,DAC 将其转换为范围从 0 到 10V 的电压。DAC 从基本原理上可以分两类:电流求和型、分压器型。 DAC 常见类型 开关树型 开关树型 DAC 是最简单粗暴的 DAC,由电阻分压器和树状的开关网络组成: 这些开关分别受 3 位输入 $d_0,d_1,d_2$ 控制,由此可得: $$ v_0=\frac{V_{REF}}{2^1} d_2+\frac{V_{REF}}{2^2} d_1+\frac{V_{REF}}{2^3} d_0 $$ $$ v_0=\frac{V_{REF}}{2^3} (d_2 2^2+d_1 2^1+d_0 2^0) $$ 进一步看,对于 n 位二进制输入的开关树型 DAC,输出为: $$ v_0=\frac{V_{REF}}{2^n} (d_{n-1} 2^{n-1}+d_{n-2} 2^{n-2}+...+d_1 2^1+d_0 2^0) $$ 开关树型 DAC 特点是电阻种类单一,且在输出端基本不取电流的情况下,对开关导通电阻要求不高;但缺点是用的开关太多。 权电阻网络 权指的是一个多位二进制数中,每一位 1 所代表的数值。例如,一个 n 位二进制数 $D_n=d_{n-1}d_{n-2}...d_1 d_0$ 从最高位(Most Significant Bit, MSB)到最低位(LSB)的权依次为 $2^{n-1},2^{n-2}...2^1,2^0$。 权电阻网络型 DAC(属于电压输出型)的原理如下图所示(4 位),它由权电阻网络,4 个模拟开关和 1 个求和放大器组成: 其中,$S_0,S_1,S_2,S_3$ 是 4 个电子开关,受 $d_0,d_1,d_2,d_3$ 4 个信号的控制,输入为 1 时开关拨到 $V_{REF}$,输入为 0 时开关接地。所以,当 $d_i=1$ 时有之路电流 $I_i$ 流向求和放大器,$d_i=0$ 时之路电流为零。求和放大器是一个负反馈放大器,当反相输入端 $V_-$ 的电位低于同相输入端的电位 $V_+$ 时,输出端对地电压 $v_0$ 为正;当 $V_->V_+$ 时,$v_0$ 为负。且当 $V_-$ 稍高于 $V_+$ 时,即可在 $v_0$ 产生大幅度的负输出电压。$v_0$ 经 $R_F$ 反馈回 $V_-$,使得 $V_-$ 降低回 $V_+$(0V)。 假设运算放大器为理想器件(输入电流为零),则可得到: $$ v_O=-R_F i_{\sum}=-R_F (I_3+I_2+I_1+I_0) $$ 又因为 $V_-\approx 0$,因此各支路电流分别为: $$ I_3=\frac{V_{REF}}{2^0 R} d_3 $$ $$ I_2=\frac{V_{REF}}{2^1 R} d_2 $$ $$ I_1=\frac{V_{REF}}{2^2 R} d_1 $$ $$ I_0=\frac{V_{REF}}{2^3 R} d_0 $$ 其中,$d_n$ 可取 0 或 1。代入上式,并假设反馈电阻 $R_F=\frac{R}{2}$ 时,可得到输出电压: $$ v_O=-\frac{V_{REF}}{2^4}(d_3 2^3+d_2 2^2+d_1 2^1+d_0 2^0) $$ 进一步看,对于 n 位权电阻网络 DAC,当反馈电阻 $R_F=\frac{R}{2}$ 时,输出电压计算公式是: $$ v_O=-\frac{V_{REF}}{2^n}(d_{n-1} 2^{n-1}+d_{n-2} 2^{n-1}+...+d_{1} 2^{1}+d_{0} 2^{0}) $$ $$ v_O=-\frac{V_{REF}}{2^n}D_n $$ 所以,输出的模拟电压正比于输入的数字量 $D_n$,其变化范围是 0 至 $-\frac{2^n-1}{2^n}V_{REF}$。另外一方面,如果需要得到正输出电压,则应该提供负的 $V_{REF}$。 权电阻网络型 DAC 的优点是结构简单,但缺点是个电阻阻值相差较大,在现实中有可能造成比较大的精度差。为了改善,可以采用双极权电阻网络,此处不展开说明,但仍无法从根本上解决。 倒 T 形电阻网络 为了改善权电阻网络 DAC 阻值相差太大的问题,可以采用倒 T 形电阻网络 DAC,它只用了 R 和 2R 两种阻值的电阻(所以也称为 R2R DAC),对于控制精度有很大的帮助: 当求和放大器反馈电阻阻值为 R 时,输出电压: $$ v_O=-Ri_{\sum}=-\frac{V_{REF}}{2^n}D_n $$ 可见,倒 T 形电阻网络与权电阻网络 DAC 的计算公式是相同的。 权电流型 在分析权电阻网络与倒 T 形电阻网络时,会将模拟开关当理想器件看待,但实际中它们存在一定的导通电阻和压降,开关之间的一致性又有差别,所以会产生转换误差而影响精度。解决方法是采用权电流型 DAC,它有一组恒流源,每个恒流源电流大小依次为前一个的一半,与输入二进制对应位的权成正比。采用恒流源使得每个支路电流大小不再受开关导通电阻和压降的影响。 当输入数字量的某位为 1 时,对应的开关将恒流源接至运算放大器的输入端;当输入代码为 0 时,对应的开 关接地,故输出电压为: $$ v_O=\frac{R_F V_{REF}}{2^n R_R}D_n $$ DAC 主要参数 分辨率:最小输出电压(也就是输入数字量为 1 时的电压)与最大输出电压(也就是输入数字量为最大,每一位都是 1 时的电压)之比。一般通过输入数字量的位数来表示。 转换量程:DAC 能输出的最大电压,一般的关于参考电压或其倍数。 建立时间:从输入数字量到输出模拟量之间的延时时间。 转换精度:与 ADC 的转换精度类似。 参考与致谢 《ADC/DAC 应用设计宝典》 数模转换与模数转换 ADC and DAC (Analog to Digital And Digital to Analog Converters) 漫谈 DAC 原理 《Analog Engineer’s Pocket Reference》 《数字电子技术(第六版)_阎石》 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 ADC(模拟/数字转换器)将模拟信号转换为数字信号,常见类型有并联比较型、逐次逼近型、双积分型和Σ-Δ型。ADC的主要参数包括分辨率、量化误差、转换速率、转换量程、偏移误差和线性度。DAC(数字/模拟转换器)将数字信号转换为模拟信号,常见类型有开关树型、权电阻网络型和倒T形电阻网络型。DAC的主要参数包括分辨率、转换量程、建立时间和转换精度。

相关推荐 去reddit讨论

Power's Wiki -

HAL 库开发笔记 - I2C 通信(MPU6050)

本篇基于自研 RobotCtrl 开发套件,单片机内核为 STM32F407ZET6,使用 MPU6050 模组讲解 HAL 库 I2C 通信的方式,开发套件原理图及详细介绍请见 RobotCtrl - STM32 通用开发套件。 基本原理 I2C 通信 I2C 通信的基本原理可跳转文章 通信协议 - I2C MPU6050 模组 模组的引脚定义: VCC:3.3V~5V GND:地 SCL:I2C 时钟 / SPI 时钟 SDA:I2C 数据 / SPI 数据输入 XDA:给 I2C 设备提供主时钟 AD0:I2C 器件地址选择位 / SPI 数据输出 INT:中断引脚 带卡尔曼滤波的 MPU6050 库 这里我们调用带卡尔曼滤波的 MPU6050 库:leech001/MPU6050,将下载的 mpu6050.c 和 mpu6050.h 拷贝至项目文件夹下,并在 STM32CubeIDE/Keil 内将其添加到项目中: ```c title="mpu6050.h" ifndef INC_GY521_H_ define INC_GY521_H_ endif / INC_GY521_H_ / include include "i2c.h" // MPU6050 structure typedef struct { int16_t Accel_X_RAW; int16_t Accel_Y_RAW; int16_t Accel_Z_RAW; double Ax; double Ay; double Az; int16_t Gyro_X_RAW; int16_t Gyro_Y_RAW; int16_t Gyro_Z_RAW; double Gx; double Gy; double Gz; float Temperature; double KalmanAngleX; double KalmanAngleY; } MPU6050_t; // Kalman structure typedef struct { double Q_angle; double Q_bias; double R_measure; double angle; double bias; double P[2][2]; } Kalman_t; uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx); void MPU6050_Read_Accel(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct); void MPU6050_Read_Gyro(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct); void MPU6050_Read_Temp(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct); void MPU6050_Read_All(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct); double Kalman_getAngle(Kalman_t *Kalman, double newAngle, double newRate, double dt); ``` ```c title="mpu6050.c" include include "mpu6050.h" define RAD_TO_DEG 57.295779513082320876798154814105 define WHO_AM_I_REG 0x75 define PWR_MGMT_1_REG 0x6B define SMPLRT_DIV_REG 0x19 define ACCEL_CONFIG_REG 0x1C define ACCEL_XOUT_H_REG 0x3B define TEMP_OUT_H_REG 0x41 define GYRO_CONFIG_REG 0x1B define GYRO_XOUT_H_REG 0x43 // Setup MPU6050 define MPU6050_ADDR 0xD0 const uint16_t i2c_timeout = 100; const double Accel_Z_corrector = 14418.0; uint32_t timer; Kalman_t KalmanX = { .Q_angle = 0.001f, .Q_bias = 0.003f, .R_measure = 0.03f}; Kalman_t KalmanY = { .Q_angle = 0.001f, .Q_bias = 0.003f, .R_measure = 0.03f, }; uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx) { uint8_t check; uint8_t Data; // check device ID WHO_AM_I HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, WHO_AM_I_REG, 1, &check, 1, i2c_timeout); if (check == 104) // 0x68 will be returned by the sensor if everything goes well { // power management register 0X6B we should write all 0's to wake the sensor up Data = 0; HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, PWR_MGMT_1_REG, 1, &Data, 1, i2c_timeout); // Set DATA RATE of 1KHz by writing SMPLRT_DIV register Data = 0x07; HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, SMPLRT_DIV_REG, 1, &Data, 1, i2c_timeout); // Set accelerometer configuration in ACCEL_CONFIG Register // XA_ST=0,YA_ST=0,ZA_ST=0, FS_SEL=0 -> � 2g Data = 0x00; HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, ACCEL_CONFIG_REG, 1, &Data, 1, i2c_timeout); // Set Gyroscopic configuration in GYRO_CONFIG Register // XG_ST=0,YG_ST=0,ZG_ST=0, FS_SEL=0 -> � 250 �/s Data = 0x00; HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, GYRO_CONFIG_REG, 1, &Data, 1, i2c_timeout); return 0; } return 1; } void MPU6050_Read_Accel(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct) { uint8_t Rec_Data[6]; // Read 6 BYTES of data starting from ACCEL_XOUT_H register HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, ACCEL_XOUT_H_REG, 1, Rec_Data, 6, i2c_timeout); DataStruct->Accel_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]); DataStruct->Accel_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]); DataStruct->Accel_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]); /*** convert the RAW values into acceleration in 'g' we have to divide according to the Full scale value set in FS_SEL I have configured FS_SEL = 0. So I am dividing by 16384.0 for more details check ACCEL_CONFIG Register ****/ DataStruct->Ax = DataStruct->Accel_X_RAW / 16384.0; DataStruct->Ay = DataStruct->Accel_Y_RAW / 16384.0; DataStruct->Az = DataStruct->Accel_Z_RAW / Accel_Z_corrector; } void MPU6050_Read_Gyro(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct) { uint8_t Rec_Data[6]; // Read 6 BYTES of data starting from GYRO_XOUT_H register HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, GYRO_XOUT_H_REG, 1, Rec_Data, 6, i2c_timeout); DataStruct->Gyro_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]); DataStruct->Gyro_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]); DataStruct->Gyro_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]); /*** convert the RAW values into dps (�/s) we have to divide according to the Full scale value set in FS_SEL I have configured FS_SEL = 0. So I am dividing by 131.0 for more details check GYRO_CONFIG Register ****/ DataStruct->Gx = DataStruct->Gyro_X_RAW / 131.0; DataStruct->Gy = DataStruct->Gyro_Y_RAW / 131.0; DataStruct->Gz = DataStruct->Gyro_Z_RAW / 131.0; } void MPU6050_Read_Temp(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct) { uint8_t Rec_Data[2]; int16_t temp; // Read 2 BYTES of data starting from TEMP_OUT_H_REG register HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, TEMP_OUT_H_REG, 1, Rec_Data, 2, i2c_timeout); temp = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]); DataStruct->Temperature = (float)((int16_t)temp / (float)340.0 + (float)36.53); } void MPU6050_Read_All(I2C_HandleTypeDef I2Cx, MPU6050_t DataStruct) { uint8_t Rec_Data[14]; int16_t temp; // Read 14 BYTES of data starting from ACCEL_XOUT_H register HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, ACCEL_XOUT_H_REG, 1, Rec_Data, 14, i2c_timeout); DataStruct->Accel_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]); DataStruct->Accel_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]); DataStruct->Accel_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]); temp = (int16_t)(Rec_Data[6] << 8 | Rec_Data[7]); DataStruct->Gyro_X_RAW = (int16_t)(Rec_Data[8] << 8 | Rec_Data[9]); DataStruct->Gyro_Y_RAW = (int16_t)(Rec_Data[10] << 8 | Rec_Data[11]); DataStruct->Gyro_Z_RAW = (int16_t)(Rec_Data[12] << 8 | Rec_Data[13]); DataStruct->Ax = DataStruct->Accel_X_RAW / 16384.0; DataStruct->Ay = DataStruct->Accel_Y_RAW / 16384.0; DataStruct->Az = DataStruct->Accel_Z_RAW / Accel_Z_corrector; DataStruct->Temperature = (float)((int16_t)temp / (float)340.0 + (float)36.53); DataStruct->Gx = DataStruct->Gyro_X_RAW / 131.0; DataStruct->Gy = DataStruct->Gyro_Y_RAW / 131.0; DataStruct->Gz = DataStruct->Gyro_Z_RAW / 131.0; // Kalman angle solve double dt = (double)(HAL_GetTick() - timer) / 1000; timer = HAL_GetTick(); double roll; double roll_sqrt = sqrt( DataStruct->Accel_X_RAW * DataStruct->Accel_X_RAW + DataStruct->Accel_Z_RAW * DataStruct->Accel_Z_RAW); if (roll_sqrt != 0.0) { roll = atan(DataStruct->Accel_Y_RAW / roll_sqrt) * RAD_TO_DEG; } else { roll = 0.0; } double pitch = atan2(-DataStruct->Accel_X_RAW, DataStruct->Accel_Z_RAW) * RAD_TO_DEG; if ((pitch < -90 && DataStruct->KalmanAngleY > 90) || (pitch > 90 && DataStruct->KalmanAngleY < -90)) { KalmanY.angle = pitch; DataStruct->KalmanAngleY = pitch; } else { DataStruct->KalmanAngleY = Kalman_getAngle(&KalmanY, pitch, DataStruct->Gy, dt); } if (fabs(DataStruct->KalmanAngleY) > 90) DataStruct->Gx = -DataStruct->Gx; DataStruct->KalmanAngleX = Kalman_getAngle(&KalmanX, roll, DataStruct->Gx, dt); } double Kalman_getAngle(Kalman_t *Kalman, double newAngle, double newRate, double dt) { double rate = newRate - Kalman->bias; Kalman->angle += dt * rate; Kalman->P[0][0] += dt * (dt * Kalman->P[1][1] - Kalman->P[0][1] - Kalman->P[1][0] + Kalman->Q_angle); Kalman->P[0][1] -= dt * Kalman->P[1][1]; Kalman->P[1][0] -= dt * Kalman->P[1][1]; Kalman->P[1][1] += Kalman->Q_bias * dt; double S = Kalman->P[0][0] + Kalman->R_measure; double K[2]; K[0] = Kalman->P[0][0] / S; K[1] = Kalman->P[1][0] / S; double y = newAngle - Kalman->angle; Kalman->angle += K[0] * y; Kalman->bias += K[1] * y; double P00_temp = Kalman->P[0][0]; double P01_temp = Kalman->P[0][1]; Kalman->P[0][0] -= K[0] * P00_temp; Kalman->P[0][1] -= K[0] * P01_temp; Kalman->P[1][0] -= K[1] * P00_temp; Kalman->P[1][1] -= K[1] * P01_temp; return Kalman->angle; }; ``` 可以看到,在设置了 I2C 的地址后,在 MPU6050_Init 函数内初始化,并在其余的函数中操作读取各个数值。 使用 I2C 读取 MPU6050 返回的信息 在 CubeMX 内配置 I2C 总线 在 CubeMX 左侧功能分类栏选择 通信 - I2Cx,将 I2C 的选项设置从 disable 更改为 I2C,并在弹出的配置界面配置参数(默认即可): 在代码内配置 I2C 读取 MPU6050 返回的信息 首先,在 main.c 中调用 MPU6050 的库: ```c title="main.c" / USER CODE BEGIN Includes / include "mpu6050.h" / USER CODE END Includes / ``` 接着,实例化对象: ```c title="main.c" / USER CODE BEGIN PV / MPU6050_t MPU6050; / USER CODE END PV / ``` 在主函数里面初始化,完毕时才继续执行程序: ```c title="main.c" / USER CODE BEGIN 2 / while (MPU6050*Init(&hi2c1) == 1); / USER CODE END 2 / ``` 在 while 循环内读取库计算出来的变量,并给一定的延时缓冲: ```c title="main.c" / USER CODE BEGIN 3 / MPU6050_Read_All(&hi2c1, &MPU6050); HAL_Delay(100); } / USER CODE END 3 / ``` 执行了这条语句,就能读出 MPU6050 结构体内的变量,比如MPU6050.KalmanAngleX(X 轴滤波后角度)。MPU6050 结构体的元素及类型如下: ```c typedef struct { int16_t Accel_X_RAW; int16_t Accel_Y_RAW; int16_t Accel_Z_RAW; double Ax; double Ay; double Az; int16_t Gyro_X_RAW; int16_t Gyro_Y_RAW; int16_t Gyro_Z_RAW; double Gx; double Gy; double Gz; float Temperature; double KalmanAngleX; double KalmanAngleY; } MPU6050_t; ``` 可以在配置串口后,通过以下语句输出变量: c printf("XAngle: %.2f°\t", MPU6050.KalmanAngleX); 参考与致谢 leech001/MPU6050 通信协议 - I2C 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 本文介绍了使用HAL库进行I2C通信的方法,以MPU6050模块为例。文章详细介绍了MPU6050模块的引脚定义和使用带卡尔曼滤波的MPU6050库。通过调用MPU6050库中的函数,可以读取MPU6050返回的加速度、陀螺仪和温度等信息。在CubeMX中配置I2C总线,并在代码中初始化MPU6050并读取其返回的信息。

相关推荐 去reddit讨论

Power's Wiki -

Homelab - 自动更新 Docker 容器的工具 Watchtower

Watchtower 是一个自动化更新全部或选定 Docker 容器的工具。 部署(Docker Compose) 首先创建 compose.yaml 文件,并粘贴以下内容: yaml title="compose.yaml" version: "3" services: watchtower: container_name: ${STACK_NAME}_app image: containrrr/watchtower:${APP_VERSION} volumes: - /var/run/docker.sock:/var/run/docker.sock restart: always (可选)推荐在 compose.yaml 同级目录下创建 .env 文件,并自定义你的环境变量。如果不想使用环境变量的方式,也可以直接在 compose.yaml 内自定义你的参数(比如把 ${STACK_NAME} 替换为 watchtower)。 ```dotenv title=".env" STACK_NAME=watchtower watchtower APP_VERSION=latest ``` 最后,在 compose.yaml 同级目录下执行 docker compose up -d 命令即可启动编排的容器。 参考与致谢 官网 / 文档 GitHub repo Docker Hub 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 Watchtower是一个自动更新Docker容器的工具。通过创建compose.yaml文件并设置相关参数,可以使用Watchtower自动更新选定的Docker容器。可以通过在compose.yaml同级目录下创建.env文件来自定义环境变量。最后,通过执行docker compose up -d命令启动编排的容器。

相关推荐 去reddit讨论

Power's Wiki -

Homelab - 电子书管理服务器 calibre-web

calibre-web 是一个一站式电子书解决方案,它基于 Calibre,可在网页上阅读电子书,集成了 calibre-server 服务,也带电子书格式转换。 部署(Docker Compose) 首先创建 compose.yaml 文件,并粘贴以下内容: yaml title="compose.yaml" version: "3" services: calibre-web: container_name: ${STACK_NAME}_app image: johngong/calibre-web:${APP_VERSION} ports: - ${APP_PORT_WEB}:8083 - ${APP_PORT_SERVER}:8080 volumes: - ${STACK_DIR}:/config - ${DATA_DIR}:/library - ${DATA_DIR}/autoaddbooks:/autoaddbooks restart: unless-stopped (可选)推荐在 compose.yaml 同级目录下创建 .env 文件,并自定义你的环境变量。如果不想使用环境变量的方式,也可以直接在 compose.yaml 内自定义你的参数(比如把 ${STACK_NAME} 替换为 audiobookshelf)。 ```dotenv title=".env" STACK_NAME=calibre-web STACK_DIR=xxx # 自定义项目储存路径,例如 ./calibre-web DATA_DIR=xxx # 自定义播客储存路径,例如 ./book calibre-web APP_VERSION=latest APP_PORT_WEB=xxxx # 自定义 Web UI 的访问端口,选择不被占用的即可 APP_PORT_SERVER=xxxx # 自定义 calibre-server 的访问端口,选择不被占用的即可 ``` 如果你有个 NAS,也可以通过 NFS 协议挂载 NAS 上的储存空间,把音乐储存在 NAS 上以节省服务器空间,详情请参考 Linux 下挂载群晖 NAS 硬盘拓展空间(NFS)。 最后,在 compose.yaml 同级目录下执行 docker compose up -d 命令即可启动编排的容器。 配置说明 默认的账号是 admin,密码是 admin123。 书籍上传功能 系统默认是没有书籍上传功能的,需要依次点击右上角 管理权限 - 编辑基本配置 — 启用上传,这样才能启用书籍上传功能。 移动端使用 Android 上可使用 Librera,通过 OPDS 协议连接 calibre-web。添加书库的 url 是在原 url 最后加上/opds,例如calibre.xxx.com/opds。 忘记密码 如果忘记密码,可以将 calibre-web 中的 app.db 数据库下载下来,使用 SQLite 查看软件(或在线工具如 Sqlite 查看器 | 修改器),分别执行以下语句: sql SELECT * FROM 'user' LIMIT 0,30 --也可也手动切换到名为 user 的表 sql UPDATE user SET password='pbkdf2:sha256:150000$ODedbYPS$4d1bd12adb1eb63f78e49873cbfc731e35af178cb9eb6b8b62c09dcf8db76670' WHERE name='xxx'; -- 需要修改xxx为你当前的用户名 把修改的 app.db 替换掉原来的,随后使用新的密码 hello 登录即可。 参考与致谢 GitHub repo Docker Hub 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 calibre-web is an all-in-one ebook solution based on Calibre. It allows users to read ebooks on a web page and includes ebook format conversion. The article provides instructions on how to deploy calibre-web using Docker Compose. It also explains how to configure the application, enable book uploading, and use it on mobile devices. In case of forgetting the password, the article suggests downloading the app.db database and using SQLite to update the password.

相关推荐 去reddit讨论

Power's Wiki -

Homelab - 支持多种协议的堡垒机 Next Terminal

Next Terminal 是一个简单好用的跳板机(堡垒机),集成了 Apache Guacamole 无客户端的远程桌面网关的堡垒机方案,支持 RDP、SSH、VNC、TELNET、Kubernetes 多协议,能直接通过 web 访问内网资源,跨平台兼容性佳。它支持 MFA 多因子认证登录,也有审计录像功能和其他记录。 部署(Docker Compose) 首先创建 compose.yaml 文件,并粘贴以下内容: yaml title="compose.yaml" version: "3.3" services: guacd: container_name: ${STACK_NAME}_guacd image: dushixiang/guacd:${GUACD_VERSION} volumes: - ${STACK_DIR}/data:/usr/local/next-terminal/data restart: always next-terminal: container_name: ${STACK_NAME}_app image: dushixiang/next-terminal:${APP_VERSION} environment: DB: sqlite GUACD_HOSTNAME: ${APP_GUACD_HOSTNAME} GUACD_PORT: ${APP_GUACD_PORT} ports: - ${APP_PORT}:8088 volumes: - /etc/localtime:/etc/localtime - ${STACK_DIR}/data:/usr/local/next-terminal/data restart: always (可选)推荐在 compose.yaml 同级目录下创建 .env 文件,并自定义你的环境变量。如果不想使用环境变量的方式,也可以直接在 compose.yaml 内自定义你的参数(比如把 ${STACK_NAME} 替换为 next-terminal)。 ```dotenv title=".env" STACK_NAME=next-terminal STACK_DIR=xxx # 自定义项目储存路径,例如 ./next-terminal next-terminal APP_VERSION=latest APP_PORT=xxxx # 自定义访问端口,选择不被占用的即可 APP_GUACD_HOSTNAME=guacd # 默认 APP_GUACD_PORT=4822 # 默认 guacd GUACD_VERSION=latest ``` 最后,在 compose.yaml 同级目录下执行 docker compose up -d 命令即可启动编排的容器。 配置说明 初始账户 / 密码:admin。 参考与致谢 官网 文档 GitHub repo Docker Hub Demo site(账号:test,密码:test) Next Terminal | 开源 轻量 简单的堡垒机 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 Next Terminal是一个集成了Apache Guacamole的堡垒机方案,支持多种协议,包括RDP、SSH、VNC、TELNET和Kubernetes。它具有MFA多因素认证登录、审计录像功能和其他记录。部署时需要创建compose.yaml文件,并设置环境变量。最后,执行docker compose up -d命令启动容器。初始账户/密码为admin。

相关推荐 去reddit讨论

Power's Wiki -

BeagleBone 系列 - 无线连接

各版本 BeagleBone 的区别 | BeagleBone® Black | Seeed Studio BeagleBone® Green | Seeed Studio BeagleBone® Green Wireless | Seeed Studio BeagleBone® Green Gateway | | ----------------- | ------------------------------ | --------------------------------------------- | ---------------------------------------------------------------------- | | $ 60.00 USD | $ 44.00 USD | $ 52.90 USD | $ 78.90 USD | | 1 x USB Host | 1 x USB Host | 4 x USB2.0 Host | 2 x USB2.0 Host | | Ethernet | Ethernet 10/100M | Wi-Fi 802.11b/g/n 2.4GHz and Bluetooth 4.1 LE | Ethernet 10/100M Bit and Wi-Fi 802.11b/g/n 2.4GHz and Bluetooth 4.1 LE | | HDMI Port | 2 x Grove Connectors | 2 x Grove Connectors | 2 x Grove Connectors | BeagleBone Green Gateway 连接 Wi-Fi shell debian@beaglebone:~$ connmanctl connmanctl> scan wifi Scan completed for wifi connmanctl> services se.101 wifi_1862e41aec0d_73652e313031_managed_psk STU-EE wifi_1862e41aec0d_5354552d4545_managed_psk connmanctl> agent on Agent registered connmanctl> connect wifi_1862e41aec0d_5354552d4545_managed_psk Agent RequestInput wifi_1862e41aec0d_5354552d4545_managed_psk Passphrase = [ Type=psk, Requirement=mandatory, Alternates=[ WPS ] ] WPS = [ Type=wpspin, Requirement=alternate ] Passphrase? 输入密码 Connected wifi_1862e41aec0d_5354552d4545_managed_psk connmanctl> quit 连接蓝牙 shell sudo apt install bluez 如果有错误,就先更新一下: shell sudo apt update 连接附近的蓝牙: shell bb-wl18xx-bluetooth bluetoothctl scan on 配对连接设备(后面一串是要配对设备的 MAC 地址): shell pair A4:xx:xx:xx:xx:30 trust A4:xx:xx:xx:xx:30 connect A4:xx:xx:xx:xx:30 可使用 quit 推出蓝牙命令行。 参考与致谢 Seeed Studio BeagleBone® Green Gateway 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 BeagleBone系列有多个版本,包括BeagleBone Black、Seeed Studio BeagleBone Green、Seeed Studio BeagleBone Green Wireless和Seeed Studio BeagleBone Green Gateway。它们之间的区别在于价格、USB端口数量、以太网和无线连接等。本文介绍了如何连接Wi-Fi和蓝牙,并提供了参考链接。

相关推荐 去reddit讨论

Power's Wiki -

Basics of VBT Syntax

Data Objects TheHdw and TheExec There are two global handles in VBT interface, to operate the hardware of the tester: TheHdw (The Hardware): Support to access and control the instruments, and include more general functions of the hardware, such as alarms. TheExec (The Executive): To control the overall test program-related functions, such as executing the test, handling the test results, and recording datalog. Below are examples of their usage: vbscript ' Set the current range of pin p0 TheHdw.DCVI.Pins("p0").CurrentRange = 0.002 vbscript ' Get the path of the current output STDF file CurrStdfFile = TheExec.Datalog.Setup.STDFOutputFile Other Data Objects More global handles are included in the VBT interface, such as PinListData, DSPWave, RtaDataObj (Run-Time Adjust Data Object) and so on. We will continue to explore them in future articles. Access By-instrument or By-pin The VBT syntax supports access tester hardware by-instrument or by-pin, they are equivalent in the result. Below are examples of their usage: vbscript ' By-instrument Access, applies a single instrument to different pins With TheHdw.instrument .Pins("Vcc").CurrentLimit = 0.75 .Pins("Vee").ForceValue = 3.2 End With vbscript ' By-pin Access, defines a pin list and then using different instruments With TheHdw.Pins("Vcc,Vdd,Vee") .instrument1.Disconnect .instrument2.CurrentLimit = 0.75 End With Structure of the VBT code A VBT code file must be named as VBT_xxx, and the name must be unique. The return value of a VBT function is expected to be 0 by default, or may cause unexpected results. For the parameters about timing and levels, you may add them in the Instance Editor or Test Instant sheet, don't need to include in the VBT function. And you can control whether to enable them in the VBT function by following usage: vbscript TheHdw.Digital.ApplyLevelsTiming For the test limits, you can use the following code: vbscript TheExec.Flow.TestLimit to compare a result value against low/high limits, and send the test result(TL_SUCCESS/TL_ERROR) and other information to the datalog. To see more clearly the basic structure of a VBT test function, here is a sample: ```vbscript Public Function VBTLeakTest(Pins As PinList, ForceVoltage As Double, PrePattern As PatternSet) As Long On Error GoTo errHandler Dim measure_results As New PinListData ' Set up timing and levels for Preconditioning Pattern TheHdw.Digital.ApplyLevelsTiming ConnectAllPins:=True, loadLevels:=True, loadTiming:=True, relaymode:=tlPowered ' Run Preconditioning Pattern and test for Pass/Fail TheHdw.Patterns(PrePattern).test pfAlways, 0 ' Force V, Measure I With TheHdw.DCVI.Pins(Pins) .Mode = tlDCVIModeVoltage ... ' Addition code measure_results = .Meter.Read End With ' Test using limits in flow and write datalog Call TheExec.Flow.TestLimit(resultval:=measure_results, unit:=unitAmp, forceval:=ForceVoltage, forceunit:=unitVolt, ForceResults:=tlForceFlow) ' Reset the variable measure_results = Nothing Exit Function errHandler: If AbortTest Then Exit Function Else Resume Next End Function ``` Multi-site 🚧 PinList Operation 🚧 Tips in VBA Avoid saving code in VBA, because this will create internal hard links in the workbook. Saving in DataTool interface instead. If you meet the error "Procedure Too Large", you may against the Excel restriction of 64K limit per vb file. But actually, it is possible that you forgot to switch the version from 32bit to 64bit of the Windows system.

AI生成摘要 VBT语法基础,数据对象,TheHdw和TheExec是VBT接口中的两个全局句柄,用于操作测试仪器的硬件。还有其他全局句柄如PinListData、DSPWave、RtaDataObj等。VBT代码文件必须以VBT_xxx命名,返回值默认为0。可以在Instance Editor或Test Instant sheet中添加时间和电平参数,不需要在VBT函数中包含。可以使用TheExec.Flow.TestLimit来比较结果值与上下限,并将测试结果和其他信息发送到数据日志。示例代码展示了VBT测试函数的基本结构。

相关推荐 去reddit讨论

Power's Wiki -

Homelab - 多功能 PDF 工具箱 Stirling-PDF

Stirling-PDF 是一个自托管 PDF 工具包,功能包括 PDF 的分割、合并、旋转、提取页面、图像互转、重新排序、添加 / 提取图像、添加删除密码、设置权限、添加水印、将其他文件转换为 PDF、OCR 文字识别、元数据编辑,支持暗黑模式。 部署(Docker Compose) 首先创建 compose.yaml 文件,并粘贴以下内容: yaml title="compose.yaml" version: "3.3" services: s-pdf: container_name: ${STACK_NAME}_app image: frooodle/s-pdf:${APP_VERSION} ports: - ${APP_PORT}:8080 restart: always (可选)推荐在 compose.yaml 同级目录下创建 .env 文件,并自定义你的环境变量。如果不想使用环境变量的方式,也可以直接在 compose.yaml 内自定义你的参数(比如把 ${STACK_NAME} 替换为 s-pdf)。 ```dotenv title=".env" STACK_NAME=s-pdf STACK_DIR=xxx # 自定义项目储存路径,例如 ./s-pdf s-pdf APP_VERSION=latest APP_PORT=xxxx # 自定义访问端口,选择不被占用的即可 ``` 最后,在 compose.yaml 同级目录下执行 docker compose up -d 命令即可启动编排的容器。 参考与致谢 文档 / GitHub repo Docker Hub 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 Stirling-PDF是一个自托管的多功能PDF工具包,可以进行PDF的分割、合并、旋转、提取页面、图像互转、重新排序、添加/提取图像、添加删除密码、设置权限、添加水印、将其他文件转换为PDF、OCR文字识别、元数据编辑等操作。可以通过Docker Compose进行部署,创建compose.yaml文件并粘贴相应内容,然后执行docker compose up -d命令启动容器。

相关推荐 去reddit讨论

Power's Wiki -

Altium Designer 安装库文件

将库文件全部 拷贝 至软件对应的 Shared\Library 文件夹下; 打开 Altium Designer ,在右侧面板点击 Components 页面,点击右上角 三条杠 标志,点击 File-based Library Preferences 选项,点击 已安装 页面,点击 安装 按钮,安装对应的库文件; 几种特殊情况: 嘉立创集成库的路径位于 JLCSMT_LIB\Project Outputs for Miscellaneous Devices LC 文件夹内; 若第三方库文件非 集成库(.IntLib),而是 原理图库(SchLib) 或 封装库(PcbLib) 的形式,则需 同时安装 以上两个文件。此时需要在安装库文件时弹出的路径选择窗口右侧点击下拉框切换 All Files(*.*) 通配符,否则只能看到 .Intlib 格式的文件。 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 Altium Designer安装库文件的步骤:将库文件拷贝至软件的Shared\Library文件夹下,打开Altium Designer,在Components页面点击三条杠标志,选择File-based Library Preferences,进入已安装页面,点击安装按钮安装对应的库文件。特殊情况下,嘉立创集成库的路径在JLCSMT_LIB\Project Outputs for Miscellaneous Devices LC文件夹内。若第三方库文件是原理图库或封装库的形式,则需同时安装以上两个文件。

相关推荐 去reddit讨论

Power's Wiki -

Homelab - 播客与有声书服务器 Audiobookshelf

Audiobookshelf 是一款自托管的播客与有声书服务器,可以方便地搜索播客、自动检测更新并下载播客、自动归档整理。 部署(Docker Compose) 首先创建 compose.yaml 文件,并粘贴以下内容: yaml title="compose.yaml" version: "3.7" services: audiobookshelf: container_name: ${STACK_NAME}_app image: ghcr.io/advplyr/audiobookshelf:${APP_VERSION} ports: - ${APP_PORT}:80 volumes: - ${STACK_DIR}/audiobooks:/audiobooks - ${STACK_DIR}/config:/config - ${STACK_DIR}/metadata:/metadata - ${DATA_DIR}:/podcasts restart: unless-stopped (可选)推荐在 compose.yaml 同级目录下创建 .env 文件,并自定义你的环境变量。如果不想使用环境变量的方式,也可以直接在 compose.yaml 内自定义你的参数(比如把 ${STACK_NAME} 替换为 audiobookshelf)。 ```dotenv title=".env" STACK_NAME=audiobookshelf STACK_DIR=xxx # 自定义项目储存路径,例如 ./audiobookshelf DATA_DIR=xxx # 自定义播客储存路径,例如 ./podcast audiobookshelf APP_VERSION=latest APP_PORT=xxxx # 自定义访问端口,选择不被占用的即可 ``` 如果你有个 NAS,也可以通过 NFS 协议挂载 NAS 上的储存空间,把播客储存在 NAS 上以节省服务器空间,详情请参考 Linux 下挂载群晖 NAS 硬盘拓展空间(NFS)。 最后,在 compose.yaml 同级目录下执行 docker compose up -d 命令即可启动编排的容器。 配置说明 移动端 App:在 iOS 与 Android 端都有官方的 App,可直接使用。 参考与致谢 官网 文档 GitHub repo Docker Hub 原文地址:https://wiki-power.com/ 本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。

AI生成摘要 Audiobookshelf is a self-hosted podcast and audiobook server that allows for easy searching, automatic updates and downloads, and automatic archiving and organizing. It can be deployed using Docker Compose. The configuration file, compose.yaml, can be customized with environment variables or directly within the file. It is recommended to create a .env file for customizing environment variables. The server can also be mounted on a NAS using the NFS protocol. To start the container, execute the command "docker compose up -d" in the directory where compose.yaml is located. The mobile app is available for both iOS and Android.

相关推荐 去reddit讨论