你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
映射
映射函数的主要作用是将一个数值从一个范围转换到另一个范围。在您的摇杆项目中,这个函数非常重要,因为它能够将摇杆的原始模拟输入(这些输入可能因设备而异且具有不同的输入范围)标准化到一个统一的数字输出范围内。
映射函数的数学原理:
函数通常表达为:
其中:
- x 是原始的输入值。
- in_min 和 in_max分别是输入值的最小和最大可能值。
- out_min 和 out_max 分别是输出值的最小和最大范围。
这个公式的作用是首先计算输入值 𝑥 在其原始范围内的相对位置,然后将这个相对位置转换到目标范围。
边界检查:
为了防止异常值和数据溢出,映射函数中加入了边界检查:
- 如果x小于in_min,则将x设置为in_min
- 如果x大于in_max,则将x设置为in_max
// 映射函数
uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
if (x < in_min) {
x = in_min;
}
if (x > in_max) {
x = in_max;
}
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
读取中心点
// 自动读取摇杆中心点
void CalibrateJoystickCenter(void) {
uint32_t sumX = 0, sumY = 0;
for (int i = 0; i < SAMPLE_COUNT; i++) {
sumX += HW504_R_X();
sumY += HW504_R_Y();
Delay_ms(10); // 适当延时以获取更稳定的读数
}
X_CENTER = sumX / SAMPLE_COUNT;
Y_CENTER = sumY / SAMPLE_COUNT;
}
滑动平均滤波处理
滑动平均滤波是一种常用的数字滤波技术,用于减少数据中的随机变异(噪声),从而获得更加平滑的数据输出。它的工作原理是连续取样本的平均值,每次包括最新的样本并丢弃最早的样本。
滑动平均的计算步骤:
1、将新的输入值加入到一个固定长度的缓冲区(数组)中。
2、缓冲区满后,最旧的数据将被新数据替代。
3、计算缓冲区内所有数据的平均值。
4、返回这个平均值作为滤波后的结果。
代码实现:
在代码中,MovingAverageFilter函数实现了上述逻辑。通过一个循环数组来存储最近的 FILTER_LEN个数据点。索引 filter_index 用于追踪最新数据应该存放的位置。每次调用该函数时,都会计算当前缓冲区内所有值的平均数,并将其作为滤波后的结果返回。
// 滑动平均滤波处理
uint16_t MovingAverageFilter(uint16_t *buffer, int len, uint16_t newValue) {
buffer[filter_index] = newValue;
filter_index = (filter_index + 1) % len;
uint32_t sum = 0;
for (int i = 0; i < len; i++) {
sum += buffer[i];
}
return sum / len;
}
代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "HW504.h"
#define DEAD_ZONE 50
#define SAMPLE_COUNT 100 // 用于中心点校准的样本数
#define FILTER_LEN 10 // 滑动平均滤波器长度
uint16_t X_CENTER, Y_CENTER;
uint16_t X, Y;
uint8_t SW;
uint16_t filter_buffer_X[FILTER_LEN] = {0};
uint16_t filter_buffer_Y[FILTER_LEN] = {0};
int filter_index = 0;
// 映射函数
uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
if (x < in_min) {
x = in_min;
}
if (x > in_max) {
x = in_max;
}
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// 自动读取摇杆中心点
void CalibrateJoystickCenter(void) {
uint32_t sumX = 0, sumY = 0;
for (int i = 0; i < SAMPLE_COUNT; i++) {
sumX += HW504_R_X();
sumY += HW504_R_Y();
Delay_ms(10); // 适当延时以获取更稳定的读数
}
X_CENTER = sumX / SAMPLE_COUNT;
Y_CENTER = sumY / SAMPLE_COUNT;
}
// 滑动平均滤波处理
uint16_t MovingAverageFilter(uint16_t *buffer, int len, uint16_t newValue) {
buffer[filter_index] = newValue;
filter_index = (filter_index + 1) % len;
uint32_t sum = 0;
for (int i = 0; i < len; i++) {
sum += buffer[i];
}
return sum / len;
}
// 读取并处理摇杆值
void ReadJoystick(void) {
int rawX = HW504_R_X();
int rawY = HW504_R_Y();
SW = HW504_R_SW();
// 对X轴应用死区和映射
if (abs(rawX - X_CENTER) < DEAD_ZONE) {
X = 2048; // 死区内默认值,设置为新范围的中点
} else {
X = map(MovingAverageFilter(filter_buffer_X, FILTER_LEN, rawX), 8, 4080, 0, 4095);
}
// 对Y轴应用死区和映射
if (abs(rawY - Y_CENTER) < DEAD_ZONE) {
Y = 2048; // 死区内默认值,设置为新范围的中点
} else {
Y = map(MovingAverageFilter(filter_buffer_Y, FILTER_LEN, rawY), 8, 4080, 0, 4095);
}
}
int main(void) {
OLED_Init();
HW504_Init();
CalibrateJoystickCenter(); // 在初始化时自动校准摇杆中心点
OLED_ShowString(1, 1, "X:");
OLED_ShowString(2, 1, "Y:");
OLED_ShowString(3, 1, "SW:");
while (1) {
ReadJoystick();
OLED_ShowNum(1, 3, X, 5);
OLED_ShowNum(2, 3, Y, 5);
OLED_ShowNum(3, 5, SW, 1);
Delay_ms(100);
}
}