使用单片机的ADC来解码正余弦编码器
代码如下,使用方法:在单片机定时器中断函数中调用Decode_SinCos() 中断频率取决于编码器的精度以及运动速度,我测试的编码器精度为20um/周期,速度尚未测量(以后补充),定时器间隔为50us,时间间隔太大容易丢正余弦周期,间隔太小频繁进中断增加CPU负荷,运行Decode_SinCos()这个函数大约需要3us(cpu频率550MHz,32位单片机)
用手移动音圈电机从顶部快速移动到底部,正余弦周期数始终是从0到281没有丢失或增加,相位没有实时采集而丢失不会影响最终位置的计算。

// 用于保存解码后的结果 float sin_voltage = 0.0f; // 正弦电压值,表示从ADC读取到的正弦信号的电压 float cos_voltage = 0.0f; // 余弦电压值,表示从ADC读取到的余弦信号的电压 float amplitude = 0.0f; // 幅值,表示正弦和余弦信号的强度 float phase = 0.0f; // 相位,表示正弦和余弦信号的相位角 // ADC 参数配置 const float v_ref = 3.3f; // ADC 参考电压,ADC采样的最大电压(通常是3.3V) const float adc_resolution = 65536.0f; // ADC 分辨率(16 位),表示 ADC 采样值的范围 // 差分信号中心电压和幅值的计算 const float v_center = (1.08f + 2.16f) / 2.0f; // 差分信号的中心电压 (1.62V),即正余弦信号的零点 const float v_amplitude = (2.16f - 1.08f) / 2.0f; // 差分信号的幅值 (0.54V),信号的幅度(从波谷到波峰的一半) // 位置和相位计数 int32_t cycle_count = 0; // 周期计数器,用于跟踪正弦余弦信号的周期数 float last_phase = 0.0f; // 上次的相位值,用于检测周期跳变 const float cycle_length = 20.0f; // 周期对应长度 (单位: μm),表示一个周期内的位置变化 float absolute_position = 0.0f; // 绝对位置 (单位: μm),表示电机的当前位置 /** * @brief 更新周期计数器,根据当前相位检测周期跳变。 * * @param current_phase 当前相位值(弧度制),表示正弦余弦信号的相位 * * @details 该函数用于检测正弦余弦信号的周期跳变,主要用于计算周期计数 `cycle_count`。当信号从正相位跳到负相位,或从负相位跳到正相位时,周期计数会增加或减少。 */ void Update_Cycle_Count(float current_phase) { // 检查相位变化是否跨越了一个周期(即从正半周跳到负半周,或从负半周跳到正半周) if (last_phase > M_PI_2 && current_phase < -M_PI_2) { cycle_count++; // 从正相位跳到负相位,周期增加 } else if (last_phase < -M_PI_2 && current_phase > M_PI_2) { cycle_count--; // 从负相位跳到正相位,周期减少 } last_phase = current_phase; // 更新上次的相位值,为下一次相位变化检测做准备 } /** * @brief 从ADC缓冲区解码正弦和余弦信号,并计算幅值和相位。 * * @details 该函数从ADC缓冲区中读取正弦和余弦信号的原始采样值,将其转换为电压,并进一步归一化处理。然后根据归一化后的值计算幅值和相位。最后,更新周期计数器并计算当前位置。 */ void Decode_SinCos(void) { // 从 ADC 缓冲区提取正弦和余弦的原始采样值 // adc_buffer[1] 存储正弦信号的差分输入数据 // adc_buffer[2] 存储余弦信号的差分输入数据 int32_t raw_sin = adc_buffer[1]; // 通道 4 数据(正弦差分输入) int32_t raw_cos = adc_buffer[2]; // 通道 5 数据(余弦差分输入) // 原始值转换为电压 // 将 ADC 原始采样值转换为电压值,基于 ADC 分辨率和参考电压 sin_voltage = ((float)raw_sin / adc_resolution) * v_ref; cos_voltage = ((float)raw_cos / adc_resolution) * v_ref; // 对信号进行归一化处理 // 将电压值转换为相对中心电压和幅值的标准化值 float sin_normalized = (sin_voltage - v_center) / v_amplitude; float cos_normalized = (cos_voltage - v_center) / v_amplitude; // 计算幅值(信号的强度) // 使用正弦和余弦信号的平方和计算幅值 amplitude = sqrtf(sin_normalized * sin_normalized + cos_normalized * cos_normalized); // 计算相位(单位:弧度) // 使用反正切函数计算正余弦信号的相位值 phase = atan2f(sin_normalized, cos_normalized); phase = fmodf(phase, 2.0f * M_PI); // 将相位值限制在 0 到 2π 之间 if (phase < 0) { phase += 2.0f * M_PI; // 如果相位为负,转换为正值 } // 更新周期计数器 // 将当前相位值传递给 Update_Cycle_Count 函数,用于检测周期跳变并更新周期计数 Update_Cycle_Count(phase); // 计算周期内的位置 (单位: μm) // 根据当前的相位计算周期内的位置,范围从 0 到 cycle_length float position_in_cycle = (phase / (2.0f * M_PI)) * cycle_length; if (position_in_cycle < 0) { position_in_cycle += cycle_length; // 如果计算得到的周期内位置为负数,转换为正值 } // 计算绝对位置 (单位: μm) // 通过周期计数器和当前位置计算电机的绝对位置,单位为微米(μm) absolute_position = cycle_count * cycle_length + position_in_cycle; }

浙公网安备 33010602011771号