
Современные компьютерные мыши, тачпады, сенсорные панели и мобильная робототехника, обязаны своей точностью и отзывчивостью миниатюрным системам движения. Одним из таких является PAT9125 — это высокоточный двухосевой оптический датчик, способный с невероятной точностью отслеживать перемещение по различным поверхностям.
PAT9125 представляет собой интеллектуальный датчик, в основе которого — микроскопическая камера и инфракрасная подсветка. Он реализует текстуру поверхности под собой, фиксируя мельчайшие смещения и на основе полученных данных изображений рассчитывает вектор движения. Благодаря высокой кадровой частоте, датчик способен точно отслеживать даже быстрое перемещение, сохраняя стабильность и минимальную задержку.
Основные характеристики:
Количество осей: 2 (X и Y);
Разрешение: до 1200 CPI (Counts Per Inch);
Интерфейсы связи: I2C и SPI;
Скорость кадров: до 2300 FPS (кадров в секунду);
Рабочее расстояние: около 1.2мм ± 0.2мм от поверхности.
Применение и обработка данных
Считывание данных с датчика по I2C и SPI — интерфейсам, дает доступ к приращениям по осям X и Y. Эти значения можно использовать для расчета абсолютного перемещения, а при необходимости — перевести в сантиметры или метры, учитывая установленное разрешение (CPI), Таким образом, PAT9125 может эффективно выполнять роль оптического энкодера, позволяя измерять расстояние, путь и даже направление движения.
Виды отслеживания оптического датчика

Пример схемы электрической принципиальной, подключение к микроконтроллеру STM32F103 по интерфейсу I2C.

Список регистров | |||||
|
|
| Значение по умолчанию | Описание | |
|
|
|
|
| |
| Product_ID2 | RO | 0x91 | Верхние 4 бита: идентификатор продукта (PID [3:0]) | |
| Motion_Status | RO | - | Информация о статусе движения | |
| Delta_X_Lo | RO | - | Смещение по оси X, 8-битное число в формате дополнительного кода | |
| Delta_Y_Lo | RO | - | Смещение по оси Y, 8-битное число в формате дополнительного кода | |
| Operation_Mode | R/W | 0xA0 | Режим работы | |
| Configuration | R/W | 0x17 | Программное отключение питания и сброс | |
| Write_Protect | R/W | 0x00 | Защита от записи для предотвращения случайной перезаписи регистров | |
| Sleep1 | R/W | 0x77 | Конфигурация режима сна 1 | |
| Sleep2 | R/W | 0x10 | Конфигурация режима сна 2 | |
| RES_X | R/W | 0x14 | Настройка разрешения (CPI) по оси X | |
| RES_Y | R/W | 0x14 | Настройка разрешения (CPI) по оси Y | |
| Delta_XY_Hi | RO | - | Старшие 4 бита данных смещения по X и Y для 12-битного формата | |
| Shutter | RO | - | Индекс времени срабатывания затвора (LASER shutter) | |
| Frame_Avg | RO | 0 | Средняя яркость кадра | |
| Orientation | R/W | 0x04 | Настройка ориентации чипа |
Реализация программного кода (Настройка, прием данных, конвертация) PAT9125 и микроконтроллера STM32F103.
Настройка в CubeIDE
Выберем I2C и выставим параметры:
I2C Speed Mode: Standard Mode;
I2C Speed Frequency(KHz): 100.

Создание переменных и определение макросов
#define PAT9125_I2C_ADDR (0x79<<1) // Адрес датчика (если ID_SEL = NC) (0x79<<1) (0x73 << 1)
#define REG_PRODUCT_ID 0x00 // Регистр идентификатора продукта
#define REG_MOTION_BURST 0x02 // Регистр для чтения движения Режим BURST чтения
#define REG_DELTA_X_LO 0x03 // 8-битный формат
#define REG_DELTA_Y_LO 0x04 // 8-битный формат
#define REG_DELTA_XY_HI 0x12 // 12-битный формат
#define REG_X_CPI 0x0D // регистр CPI по X
#define REG_Y_CPI 0x0E // регистр CPI по Y
#define NOISE_THRESHOLD_CM 0.01f // Фильтр шума (1 мм)
#define CPI 700.0f // Разрешение датчика (Counts Per Inch) 720
#define INCH_TO_MM 25.4f // 1 дюйм = 25.4 мм
#define INCH_TO_CM 2.54f // 1 дюйм = 2.54 см
#define USE_12BIT_FORMAT // 12-битный формат
#define I2C_RX_BUFFER_SIZE 16//Основной буфер для данных Нужно 5 байт: [MOTION, Delta_X_LO, Delta_Y_LO, SQUAL, Delta_XY_HI]
uint8_t i2c_rx_buffer[I2C_RX_BUFFER_SIZE]={0,};
int32_t total_x = 0; // отсчеты по х
int32_t total_y = 0; // отсчеты по y
int32_t motion_flag =0;
static uint16_t delta_x_lo =0;
static uint16_t delta_y_lo =0;
static uint16_t delta_xy_hi =0;
float delta_x_cm = 0.0f; //Данные по x в см
float delta_y_cm = 0.0f; //Данные по y в см
float flow_vel_x_cop_ab = 0.0f;//Данные по x в мм
float flow_vel_y_cop_ab = 0.0f;//Данные по y в мм
float beta_rad = 0.0f; //Угловая скорость в радианах
Метод PAT9125_Init
В данном методе производится инициализация датчика:
Считывание ID и проверка доступности устройства;
Установление разрешения CPI, через метод PAT9125_SetCpi().
void PAT9125_Init() {
uint8_t product_id = PAT9125_ReadReg(REG_PRODUCT_ID);
if (product_id != 0x31) { // Ожидаемый ID датчика
printf("%s", message_test_1);
} else {
/*
* Значение 0x7F (127) ≈ 500 CPI (базовое значение).
Значение 0xBC (188) ≈ 1000 CPI.
Значение 0xDE (222) ≈ 1500 CPI.
Значение 0xFF (255) ≈ 2000 CPI (максимальное).*/
PAT9125_SetCPI(0xBC);// Установлено 1000 CPI
printf("%s", message_test_2);
}
}
Метод PAT9125_SetCPI
Данный метод, устанавливает заданный CPI в регистры X[0x0D] и Y[0x0E], считывает один байт из регистра датчика PAT9125 по I2C, (выставлено 1000 CPI, что эквивалентно 0.0254 мм/отсчет).
void PAT9125_SetCPI(uint16_t cpi_value){
uint8_t data[2];
// Устанавливаем X-CPI
data[0] = REG_X_CPI;
data[1] = cpi_value;
HAL_I2C_Master_Transmit(&hi2c1, PAT9125_I2C_ADDR, data, 2, HAL_MAX_DELAY);
// Устанавливаем Y-CPI
data[0] = REG_Y_CPI;
HAL_I2C_Master_Transmit(&hi2c1, PAT9125_I2C_ADDR, data, 2, HAL_MAX_DELAY);
}
Метод PAT9125_ReadReg
Считывает один байт из регистра датчика PAT9125 по I2C (используется для чтения текущих значений (идентификатора устройства, данных движения и т.д.)).
uint8_t PAT9125_ReadReg(uint8_t reg) {
uint8_t data = 0;// от функции PAT9125_ReadReg
HAL_I2C_Master_Transmit(&hi2c1, PAT9125_I2C_ADDR, ®, 1, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(&hi2c1, PAT9125_I2C_ADDR, &data, 1, HAL_MAX_DELAY);
return data;
}
Метод PAT9125_WriteReg
Данный метод, записывает значение value в регистр reg датчика PAT9125, используется для настройки параметров датчика, например (Установка CPI-разрешения).
void PAT9125_WriteReg(uint8_t reg, uint8_t value) {
uint8_t data[2] = {reg, value};
HAL_I2C_Master_Transmit(&hi2c1, PAT9125_I2C_ADDR, data, 2, HAL_MAX_DELAY);
}
Метод PAT9125_ReadMotion
Данный метод, считывает информацию о перемещении по осям X и Y из датчика (режим Burst Read), так же обновляет общее смещение total_x, total_y, и вызывает функцию обновления общего пройденного пути, простыми словами (используется каждый раз, когда нужно получить новые данные движения с датчика).
Поддерживает форматы:
8-битный формат и 12-битный (определяется макросом USE_12BIT_FORMAT), для работы в 8-битном формате, просто закоментируйте макрос(#define USE_12BIT_FORMAT), в режиме 12-бит, данные собираются из старших и младших байтов и расширяются до знакового значения.
void PAT9125_ReadMotion(int16_t *delta_x, int16_t *delta_y) {
uint8_t reg = REG_MOTION_BURST;
// Отправляем команду BURST READ
if (HAL_I2C_Master_Transmit(&hi2c1, PAT9125_I2C_ADDR, ®, 1, HAL_MAX_DELAY) != HAL_OK) {
printf("Ошибка передачи I2C!\n");
return;
}
// Читаем сразу 5 байт данных (MOTION, Delta_X_LO, Delta_Y_LO, SQUAL, Delta_XY_HI)
if (HAL_I2C_Master_Receive(&hi2c1, PAT9125_I2C_ADDR, i2c_rx_buffer, I2C_RX_BUFFER_SIZE, HAL_MAX_DELAY) != HAL_OK) {
printf("Ошибка приёма I2C!\n");
return;
}
delta_x_lo = i2c_rx_buffer[1]; // Delta_X (младший байт)
delta_y_lo = i2c_rx_buffer[2]; // Delta_Y (младший байт)
delta_xy_hi = i2c_rx_buffer[4]; // Delta_XY_Hi (старшие 4 бита X и Y)
#ifdef USE_12BIT_FORMAT // Если используем 12-битный формат
delta_xy_hi = PAT9125_ReadReg(REG_DELTA_XY_HI);
// Формируем 12-битные значения
*delta_x = (int16_t)(((delta_xy_hi & 0xF0) << 4) | delta_x_lo);
*delta_y = (int16_t)(((delta_xy_hi & 0x0F) << 8) | delta_y_lo);
// Расширяем знаковый бит для 12-битного числа (если отрицательное)
if (*delta_x & 0x0800) *delta_x |= 0xF000;
if (*delta_y & 0x0800) *delta_y |= 0xF000;
#else
// 8-битный формат (если нужно)
*delta_x = (int8_t)delta_x_lo;
*delta_y = (int8_t)delta_y_lo;
#endif
total_x += *delta_x;
total_y += *delta_y;
UpdateTotalDistance(total_x,total_y);
}
Метод ProcessMotionData
Данный метод является основным обработчиком движения, вызывает метод PAT9125_ReadMotion, обрабатывает данные и формирует строки для вывода по UART.
void ProcessMotionData(void) {
HAL_Delay(1);//чтобы HAL_GetTick() не выдавал ноль
uint32_t ms = HAL_GetTick();
PAT9125_ReadMotion(&dx, &dy);
/** Данные после выполнения метода PAT9125_ReadMotion
* total_x - Отсчеты по x
* total_y - Отсчеты по y
* delta_x_cm - данные по x в см
* delta_x_cm - данные по y в см
* total_distance_cm - общий пройденный путь в см
*/
//-------------В данном куске кода работа предназначена для CartScan---------------
float time_sec = ms/1000.0f;//перевод в секунды
flow_vel_x_cop_ab = delta_x_cm * 10.0f;
flow_vel_y_cop_ab = delta_y_cm * 10.0f;
position_x_m_cop_long = (long)roundf(flow_vel_x_cop_ab);
position_y_m_cop_long = (long)roundf(flow_vel_y_cop_ab);
total_path_m_cop = total_distance_cm*10.0f;
total_path_m_cop_long = (long)roundf(total_path_m_cop);
beta_rad = calculateBetaRadians(delta_x_cm, delta_y_cm)*10.0f;//получение угла в радианах
}
Метод UpdateTotalDistance
Данный метод вычисляет прирост пройденного пути на основе новых данных смещения и обновляет общий путь total_distance_cm
void UpdateTotalDistance(int32_t delta_x, int32_t delta_y) {
// Переводим X и Y в см
delta_x_cm = convert_to_cm(delta_x, CPI);
delta_y_cm = convert_to_cm(delta_y, CPI);
// Фильтруем шум (игнорируем слишком маленькие изменения)
if (fabsf(delta_x_cm) < NOISE_THRESHOLD_CM) delta_x_cm = 0;
if (fabsf(delta_y_cm) < NOISE_THRESHOLD_CM) delta_y_cm = 0;
// Проверяем, изменились ли данные
bool is_moving = (delta_x != last_delta_x || delta_y != last_delta_y);
// Вычисляем пройденный путь (Только если датчик двигается)
if (is_moving) {
float delta_distance = sqrtf(delta_x_cm * delta_x_cm + delta_y_cm * delta_y_cm);
total_distance_cm += delta_distance/720.0f;
}
last_delta_x = delta_x;
last_delta_y = delta_y;
}
Метод convert_to_cm
Преобразует значение смещения из отсчетов в сантиметры, используя установленное разрешение CPI
float convert_to_cm(int32_t delta, float cpi) {
return (float)delta * INCH_TO_CM / cpi;
}
Метод calculateBetaRadians
Данный метод вычисляет угол направления движения по данным X и Y
float calculateBetaRadians(float flow_vel_x, float flow_vel_y)
{
return atan2(flow_vel_y, flow_vel_x); // Угол в радианах
}
Главный метод
Запускается в главном цикле while.
void proj_main()
{
HAL_Delay(1);//чтобы HAL_GetTick() не выдавал ноль
PAT9125_Init();
while (1){
ProcessMotionData();
}//while (1)
}
Итоговый вывод
Это полноценный драйвер + обработчик данных с датчика PAT9125, включающий:
Считывание и обработку данных о движении;
Перевод в физические единицы (см, мм, радианы);
Калибровка чувствительности
В целом, PAT9125 — это пример того, как миниатюрные технологии могут обеспечить высочайшую точность в системах, где важна каждая микронная деталь, благодаря своей компактности, энергоэффективности и простоте интеграции, он становится идеальным выбором для современных устройств ввода и мобильных систем позиционирования.
Если статья показалась Вам интересной, буду рад выпустить для Вас еще множество статей исследований по всевозможным видам устройств, так что, если не хотите их пропустить – буду благодарен за подписку на мой ТГ-канал: https://t.me/ChipCraft.