
Современные компьютерные мыши, тачпады, сенсорные панели и мобильная робототехника, обязаны своей точностью и отзывчивостью миниатюрным системам движения. Одним из таких является 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.
Комментарии (8)
Am0k-HABR
07.08.2025 13:33У вас на схеме шина SCL подтянута к отрицательному напряжению питания. Думаю, чтобы таких казусов не происходило, стоит подтяжки напряжения питания рисовать кверху, а подтяжки к земле - к низу, как и принято.
DM_ChipCraft Автор
07.08.2025 13:33Спасибо большое что отметили некорректное составление схемы, обязательно исправлю, там конечно же нет отрицательного напряжения питания, в редакторе к сожалению не заметил :)
leshabirukov
Картинку с камеры вытащить можно?
ЗЫ сейчас мои коллеги наблюдали странное, - я прижимал мышь к лицу пытаясь поймать движения глазного яблока.
DM_ChipCraft Автор
К сожалению картинку никаким образом не вытащить... внутри сенсора встроена 2D пиксельная матрица, кадры 30x30 пикселей (примерно) получаются, на основе этих данных уже алгоритмы вычисляют вектор смещения X и Y, между кадрами :)
leshabirukov
А не попадались ли кому-нибудь сенсоры где можно, хотя бы на уровне jtag? Конечно, по-настоящему интересно реальное время, понятно, что 30х30, но зато сколько fps...
DM_ChipCraft Автор
У компании P X I есть много датчиков, и покопавшись в закромах сайта я нашел сенсор который работает с изображениями, ссылка[https://www.pixart.com/products-detail/61/PAS6329LT ] , характеристики: Оптический формат 1/7 дюймаРазмер (пиксель) 3.2*3.2Разрешение (пиксель) 640*480Макс. частота кадров VGA: 30 кадров в секундуФормат вывода RAW10, YUV422, RGB565/555/444
DM_ChipCraft Автор
К сожалению мне удалось поработать только с PAT9125, а так бы хотелось прикоснуться к кому либо еще сенсору . . . . :)