传感器驱动系列之PAW3212DB鼠标光电传感器

news2024/11/26 16:46:10

目录

一、PAW3212DB鼠标光电传感器简介

1.1 主要特点

1.2 引脚定义

1.3 传感器组装

1.4 应用场景

1.5 传感器使用注意

1.5.1 供电选择

1.5.2 SPI读写设置

1.5.3 MOTION引脚

1.6 寄存器说明

1.6.1 Product_ID1寄存器

1.6.2 MOTION_Status寄存器

1.6.3 Delta_X寄存器

二、STM32驱动源码

三、驱动示例

四、个人广告

END


注:以下关于传感器的数据手册的引用和复制仅作科普和示例讲解,如有侵权,立马删除。

若有读者需要对应的传感器数据手册,详情参见下载链接:https://wwjh.lanzoup.com/b00jdr9vzg   密码:d6lr

一、PAW3212DB鼠标光电传感器简介

        PAW3212DB是一款高性能的光学运动传感器,通常用于各种需要运动检测和位置跟踪的应用中,如无线鼠标、游戏控制器和手势识别设备。其针对基于红色 LED 的无线鼠标应用进行了优化。它具有低功耗架构、高精度表面跟踪能力、自动电源管理模式、灵活的可编程分辨率、可配置的睡眠和唤醒时间,使其适用于对功率敏感的无线鼠标应用。PAW3212DB能够进行高达 30 英寸/秒的速度和 10g 加速度的高速运动检测。其实物图见下图1:

图1   PAW3212DB实物图

1.1 主要特点

  • 高灵敏度:PAW3212DB 具有较高的灵敏度,能够检测微小的运动,确保准确的位移跟踪。
  • 宽电压范围:低电压段:1.7V至 2.1V(VDD.VDDA 短路),高压段:2.1V至3.6V(VDDA应将电容器连接到 GND);
  • 可选择3线(默认)或2线 SPI串行接口;
  • 可选择 8位(默认)或 12 位运动数据长度,用于Delta X和Delta Y;
  • 可选分辨率高达 2400CPI(每英寸计数),步长为 38 cpi(基于 x1 镜头放大倍率),适合快速移动和高精度需求的应用;
  • 移动侦测中断输出;
  • 跟踪速度高达 30ips(英寸/秒)和 10g 加速度;
  • 内置低功耗定时器(LPT),用于Sleep1/Sleep2/Sleep3 模式;
  • 自适应帧率控制,可在以不同速度移动时节省更多电量;

1.2 引脚定义

        其传感器引脚对应说明如下图2所示:

图2   PAW3212DB传感器引脚说明

        其获取数据对应的传感器方向示意图如图3所示:

图3   PAW3212Db传感器数据输出方向说明

1.3 传感器组装

        由于该传感器的光学器件是集成在芯片内部的,但是需要透镜对红外LED灯进行光学反射和折射,所以需要使用一个光学透镜和红外LED灯(数据手册建议使用昌裕 LED 配光测角仪 V110 来测量 LED 视角。),其3D组装示意图见下图4说明:

图4   PAW3212DB 3D组装图

1.4 应用场景

  • 无线鼠标和键盘:用于提高操作的精准度和响应速度;
  • 游戏控制器:增强玩家体验,提供更流畅的操控感;
  • 移动设备:可以用于手势识别和运动控制,提升用户交互体验;

1.5 传感器使用注意

1.5.1 供电选择

        传感器的上电默认设置在 High Voltage 段。如果用户想使用Low Voltage段,则应在上电序列后设置传感器寄存器(地址0x4B)(值 0x40)。如果此寄存器设置不正确,则由于内部稳压器的电流泄漏,传感器将消耗额外的功率。其对应数据手册说明如下图5:

图5   设置供电选择代码设置

        其中供电的接法选择如下图6所示,左侧为High Voltage对应接法,右侧为Low Voltage接法。

图6     芯片供电硬件选择

1.5.2 SPI读写设置

        这里以3线SPI为例进行说明,两线SPI的读写设置只需要将CS引脚一直下拉到地,且设置寄存器读写说明即可。

        PAW3212DB与MCU的传输协议采用的是3线半双工的SPI协议,即同一时刻只有MISO接收数据或者MOSI发送数据。其中SDI0上的数据更改发生在 SCLK的时钟下降沿,时钟的上升沿用于数据保持。

        其寄存器的写入和读取两种操作模式都由两个字节组成。第一个字节包含7位数据(寄存器地址)+最高位MSB(读/写方向判定,0代表读操作,1代表写操作);第二个字节包含数据。其具体说明示意入下图7所示。

图7   寄存器读写操作示意

        写入操作时序示意图如下:

图8  寄存器写入操作时序

        读取操作时序示意图如下:

图9   寄存器读取操作时序

1.5.3 MOTION引脚

        每当传感器检测到运动的发生时,MOTION 引脚将从高变为低电平。MOTION 引脚可用于监控传感器运动数据是否已被清除。如果运动数据未被清除,MOTON 引脚将保持低电平,当主机控制器读出所有运动数据后(即 Motion bit.Delta X和Delta Y均为零),MOTION 引脚将变为高电平。

        当鼠标系统工作在空闲状态时,传感器处于 Sleep1/Sleep2/Sleep3 模式,主机控制器处于空闲模式,当传感器检测到运动发生时,MOTION 引脚将变为低电平。MOTION 引脚上的下降沿可用作唤醒主机控制器的中断事件。其状态说明见下图10。

图10   MOTION引脚状态转换说明

1.6 寄存器说明

        传感器的所有寄存器示意图见下图11所示:

图11   PAW3212DB寄存器说明

        这里只介绍几个比较常用的寄存器,具体说明如下:

1.6.1 Product_ID1寄存器

        该寄存器可以检查SPI的通信是否完成,如果SPI正常通信,则可以读取到该寄存器的数据值为0x30,具体数据手册说明见下图12:

图12   Prodect_ID1寄存器说明

1.6.2 MOTION_Status寄存器

        在运动检测例程中,主机控制器可以通过检査 寄存器的Motion 位来轮询传感器以获取有效的运动数据。如果Motion 位置位,则 Delta X和 Delta Y寄存器中的运动数据有效,可以读取。在读出 Delta X和 Delta Y寄存器之前请务必先读取 Motion 位。DXOVF 位和 DYOVF 位显示自上次读出以来运动报告缓冲区是否溢出。具体数据手册说明见下图13。

图13   MOTION_Status寄存器说明

1.6.3 Delta_X寄存器

        该寄存器可以读取X轴Motion Status运动。绝对值由 CPI X的分辨率设置决定。读取此寄存器将清除此寄存器的内容。数据范围:-128~+127。MSB 位表示符号位。具体寄存器说明见下图14。

图14   Delta_X寄存器说明

1.6.4 Delta_Y寄存器

        该寄存器可以读取Y轴Motion Status运动。绝对值由 CPI Y的分辨率设置决定。读取此寄存器将清除此寄存器的内容。数据范围:-128~+127。MSB 位表示符号位。具体寄存器说明见下图15。

图15   Delta_Y寄存器说明

二、STM32驱动源码

        paw3212db.c驱动实现如下:

#include "dev_paw3212db.h"
#include "bsp.h"


/* 为了优化文件不受到其他文件的干扰 使用宏定义重定义底层操作函数 */
#define     paw_cs_write        bsp_paw_spi_cs_ctrl
#define     paw_spi_write       bsp_spi_write
#define     paw_spi_read        bsp_spi_read

/**
 * @brief 传感器最大支持30英寸/s的速度检测和10g的加速度检测
 *        底层SPI时钟速度最大只能设置为10MHz 目前底层SPI时钟配置为71KHz
 *        1厘米(cm) = 0.3937英寸(in)  1英寸(in) = 2.54厘米(cm)
 */

/* PAW3212DB寄存器 */
enum {
    PAW_REG_PRODUCT_ID1   = 0x0,    /* 产品ID [11:4] 默认值0x30 */
    PAW_REG_PRODUCT_ID2   = 0x1,    /* 产品ID 高4位-PID 低4位-VID版本号 默认值0x2 */
    PAW_REG_MOTION_STATUS = 0x2,    /* 保存运动状态信息 */
    PAW_REG_DELTA_X       = 0x3,    /* X轴运动数据 2进制补码存放 使用int8_t读取 */
    PAW_REG_DELTA_Y       = 0x4,    /* Y轴运动数据 2进制补码存放 使用int8_t读取 */
    PAW_REG_OPORATION_MODE= 0x5,    /* 操作模式选择寄存器 默认值0xb8 */
    PAW_REG_CONFIGURATION = 0x6,    /* 配置寄存器 设置软件掉电和复位状态 默认值0x11 */
    PAW_REG_WRITE_PROTECT = 0x9,    /* 写保护寄存器 默认值0x00 */
    PAW_REG_SLEEP1        = 0xA,    /* 睡眠寄存器1 默认值0x77 */
    PAW_REG_SLEEP2        = 0xB,    /* 睡眠寄存器2 默认值0x10 */
    PAW_REG_SLEEP3        = 0xC,    /* 睡眠寄存器3 默认值0x70 */
    PAW_REG_CPI_X         = 0xD,    /* X轴CPI配置寄存器 默认值0x1B */
    PAW_REG_CPI_Y         = 0xE,    /* Y轴CPI配置寄存器 默认值0x1B */
    PAW_REG_DELTA_XY_HI   = 0x12,   /* 12位数据格式的delta_x和delta_y运动数据高4位 只读 */
    PAW_REG_IQC           = 0x13,   /* 图像质量补充 寄存器 只读 */
    PAW_REG_SHUTTER       = 0x14,   /* 快门时间指数 寄存器 只读 */
    PAW_REG_FRAME_AVG     = 0x17,   /* 帧平均亮度   寄存器 只读 */
    PAW_REG_MOUSE_OPTION  = 0x19,   /* 鼠标操作选项 寄存器 默认值0x00 */
    PAW_REG_SPI_MODE      = 0x26,   /* 3线/2线SPI模式选择寄存器 默认值0xB4 */
} paw_reg_e;

struct paw_data_t {
    bool is_motion;         /* 是否有运动  false-无运动 true-运动 */
    bool is_x_overflow;     /* X轴数据溢出 false-无溢出 true-溢出 */
    bool is_y_overflow;     /* Y轴数据溢出 false-无溢出 true-溢出 */
    bool is_8bit_mode;      /* 8位数据格式 false-12位数据格式 true-8位数据格式 */
    unsigned char id;       /* 设备ID 读取ID1默认值0x30 */
    unsigned char iqc;      /* 图像质量补充 寄存器 只读 */
    unsigned char shutter;  /* 快门时间指数 寄存器 只读 */
    unsigned char frame_avg;/* 帧平均亮度   寄存器 只读 */
    signed short delta_x;   /* X轴运动位移增量数据 */
    signed short delta_y;   /* Y轴运动位移增量数据 */
} ;

struct paw_data_t paw_data = {
    .is_motion     = false,
    .is_x_overflow = false,
    .is_y_overflow = false,
    .is_8bit_mode  = true,
    .delta_x = 0,
    .delta_y = 0,
};

/* private function ---------------------------------------------------- */
/**
 * @brief PAW3212DB 寄存器写
 * @param addr 寄存器地址
 * @param data 写入的数据
 * @note  数据手册中指出 写入数据时 寄存器地址最高位为1代表写
 *        且地址字节的第6-0位用于表示寄存器地址
 */
static void paw_reg_write(unsigned char addr, unsigned char data)
{
    paw_cs_write(0);
    addr = (addr | 0x80);
    paw_spi_write(addr);
    paw_spi_write(data);
    paw_cs_write(1);
}

/**
 * @brief  PAW3212DB 寄存器读
 * @param  addr 寄存器地址
 * @retval 读取到的数据
 * @note  数据手册中指出 读出数据时 寄存器地址最高位为0代表读
 *        且地址字节的第6-0位用于表示寄存器地址
 */
static unsigned char paw_reg_read(unsigned char addr)
{
    unsigned char buf;
    paw_cs_write(0);
    addr = (addr & 0x7F);
    paw_spi_write(addr);
    buf = paw_spi_read();
    paw_cs_write(1);
    return buf;
}

/**
 * @brief  PAW3212DB 寄存器位操作 置位
 * @param  reg 寄存器地址
 * @param  mask 操作位
 */
static void paw_set_bit_mask(unsigned char reg, unsigned char mask)
{
    unsigned char buf = 0;
    buf = paw_reg_read(reg);
    paw_reg_write(reg, buf | mask);
}

/**
 * @brief  PAW3212DB 寄存器位操作 复位
 * @param  reg 寄存器地址
 * @param  mask 操作位
 */
static void paw_clr_bit_mask(unsigned char reg, unsigned char mask)
{
    unsigned char buf = 0;
    buf = paw_reg_read(reg);
    paw_reg_write(reg, buf & (~mask));
}


/* public function ---------------------------------------------------- */
/**
 * @brief  PAW3212DB 初始化
 */
void paw_init(void)
{
    paw_product_id_read(&paw_data.id);
    
    paw_write_protect_ctrl(false);              /* 禁用写保护 */
    // paw_reg_write(PAW_REG_SPI_MODE, 0x34);   /* 设置切换到2线SPI模式禁用NCS时 开启注释 */
    // paw_reg_write(0x4B, 0x04);               /* 设置使用低电压时 开启注释 */
    paw_reg_write(0x5B, 0x63);                  /* 将LED设置为连续模式 */
    paw_set_8bit_mode(true);                   /* 数据格式配置 true-8位 false-12位 */
    paw_write_protect_ctrl(true);               /* 启用写保护 */
}

/**
 * @brief      PAW3212DB 产品ID读取
 * @param[out] id - 产品ID
 */
void paw_product_id_read(unsigned char *id)
{
    *id = paw_reg_read(PAW_REG_PRODUCT_ID1);
}

/**
 * @brief PAW2312DB 运动状态+XY轴数据状态检测
 * @param[out] is_motion - 是否有运动
 * @param[out] is_x_overflow - X轴数据溢出
 * @param[out] is_y_overflow - Y轴数据溢出
 */
void paw_motion_detect(bool *is_motion, bool *is_x_overflow, bool *is_y_overflow)
{
    unsigned char buf = 0;

    buf = paw_reg_read(PAW_REG_MOTION_STATUS);
    *is_motion = (buf & 0x80) ? true : false;
    *is_x_overflow = (buf & 0x10) ? true : false;
    *is_y_overflow = (buf & 0x08) ? true : false;
}

/**
 * @brief      PAW2312DB XY轴数据读取
 * @param[out] delta_x - x轴运动位移增量数据
 * @param[out] delta_y - y轴运动位移增量数据
 */
void paw_xy_data_read(signed short *delta_x, signed short *delta_y)
{
    unsigned char buf[3] = {0}; // 数据缓存
    buf[1] = paw_reg_read(PAW_REG_DELTA_X); // 读取 X 轴数据
    buf[2] = paw_reg_read(PAW_REG_DELTA_Y); // 读取 Y 轴数据
    
    if (paw_data.is_8bit_mode) { // 8位模式 直接读取
        *delta_x = (signed char)buf[1];
        *delta_y = (signed char)buf[2];
    } else { // 处理高位和低位数据
        buf[0] = paw_reg_read(PAW_REG_DELTA_XY_HI); // 读取高位数据
        *delta_x = (signed short)((((buf[0] & 0xF0) << 8) | (buf[1]))); // 计算 X 轴数据
        *delta_y = (signed short)((((buf[0] & 0x0F) << 8) | (buf[2]))); // 计算 Y 轴数据
    }
}

/**
 * @brief     PAW2312DB 睡眠模式设置
 * @param[in] mode - 睡眠模式 
 * @note      mode: 详细说明见数据手册第25页 Operation_Mode寄存器和Configuration寄存器
 *            0-禁用睡眠模式1和2
 *            1-启用睡眠模式1 禁用睡眠模式2
 *            2-启用睡眠模式1和2
 *            3-进入睡眠模式2
 *            4-进入睡眠模式1
 *            5-从睡眠模式唤醒到运行模式
 *            6-禁用睡眠模式3
 *            7-启用睡眠模式3
 */
void paw_set_sleep_mode(unsigned char mode)
{
    unsigned char buf, buf1;
    buf = paw_reg_read(PAW_REG_OPORATION_MODE);
    buf1 = paw_reg_read(PAW_REG_CONFIGURATION);
    switch (mode) {
        case 0: buf &= (~0x10);              break; /* 禁用睡眠模式1和2 */
        case 1: buf |= 0x10; buf &= (~0x08); break; /* 启用睡眠模式1 禁用睡眠模式2 */
        case 2: buf |= (0x18);               break; /* 启用睡眠模式1和2 */
        case 3: buf |= 0x1C; buf &= (~0x03); break; /* 进入睡眠模式2 */
        case 4: buf |= 0x12; buf &= (~0x05); break; /* 进入睡眠模式1 */
        case 5: buf |= 0x11; buf &= (~0x06); break; /* 从睡眠模式唤醒到运行模式 */
        case 6: buf &= (~0x20);              break; /* 禁用睡眠模式3 */
        case 7: buf1 |= 0x20;                break; /* 启用睡眠模式3 */
        default: break;
    }
}

/**
 * @brief PAW2312DB 复位
 */
void paw_reset(void)
{
    paw_set_bit_mask(PAW_REG_CONFIGURATION, 0x80);
}

/**
 * @brief     PAW2312DB 电源模式控制
 * @param[in] enable - true-正常模式 false-掉电模式
 */
void paw_power_mode_ctrl(bool enable)
{
    (enable) ? paw_clr_bit_mask(PAW_REG_CONFIGURATION, 0x08) : paw_set_bit_mask(PAW_REG_CONFIGURATION, 0x08);
}

/**
 * @brief     PAW3212DB 写保护控制
 * @param[in] enable - true-启用写保护 false-禁用写保护
 */
void paw_write_protect_ctrl(bool enable)
{
    (enable) ? paw_reg_write(PAW_REG_WRITE_PROTECT, 0x00) : paw_reg_write(PAW_REG_WRITE_PROTECT, 0x5A);
}

/**
 * @brief        PAW3212DB 设置运动轴CPI分辨率 默认值0x1B=27
 * @param[inout] x_cpi - x轴运动阈值 最大值63
 * @param[inout] y_cpi - y轴运动阈值 最大值63
 * @note         目标CPI分辨率=38*cpi  cpi建议取值范围16-63
 *               x_cpi和y_cpi为0xFF时代表读取寄存器值 否则写入寄存器
 */
void paw_xy_cpi_config(unsigned char *x_cpi, unsigned char *y_cpi)
{
    if(*x_cpi == 0xFF) {
        *x_cpi = paw_reg_read(PAW_REG_CPI_X);
    } else {
        if(*x_cpi > 63) *x_cpi = 63;
        paw_reg_write(PAW_REG_CPI_X, *x_cpi);
    }
    if(*y_cpi == 0xFF) {
        *y_cpi = paw_reg_read(PAW_REG_CPI_Y);
    } else {
        if(*y_cpi > 63) *y_cpi = 63;
        paw_reg_write(PAW_REG_CPI_Y, *y_cpi);
    }
}

/**
 * @brief      PAW3212DB IQC(图像质量评价)数据读取
 * @param[out] iqc - IQC数据 0-255
 * @note       查看传感器表面的图像质量/模糊度 其值越小传感器的图像跟踪越准确
 */
void paw_img_quallity_read(unsigned char *iqc)
{
    *iqc = paw_reg_read(PAW_REG_IQC);
}

/**
 * @brief      PAW3212DB LED快门时间数据读取
 * @param[out] shutter - LED快门时间数据 0-28
 * @note       LED快门时间 = (shutter+1)*7.1us
 */
void paw_shutter_read(unsigned char *shutter)
{
    *shutter = paw_reg_read(PAW_REG_SHUTTER);
}

/**
 * @brief      PAW3212DB 帧平均亮度数据读取
 * @param[out] brightness - 帧平均亮度数据 0-255
 * @note       一帧像素点 324
 */
void paw_frame_brightness_read(unsigned char *brightness)
{
    *brightness = paw_reg_read(PAW_REG_FRAME_AVG);
}

/**
 * @brief     PAW3212DB 设置旋转角度
 * @param[in] rotation - 旋转角度 0-3对应0-360度
 * @note      默认摆放为0度(即芯片1脚在左上角的位置) 具体位置说明参见数据手册28页Mouse_Option寄存器
 */
void paw_set_rotation(unsigned char rotation)
{
    unsigned char buf = 0;
    buf = paw_reg_read(PAW_REG_MOUSE_OPTION);
    switch (rotation) {
        case 0: buf &= (~0x1C);              break; /* bit[4:2] - 000 */
        case 1: buf |= 0x18; buf &= (~0x04); break; /* bit[4:2] - 110 */
        case 2: buf |= 0x14; buf &= (~0x08); break; /* bit[4:2] - 101 */
        case 3: buf |= 0x0C; buf &= (~0x10); break; /* bit[4:2] - 011 */
        default: break;
    }
    paw_reg_write(PAW_REG_MOUSE_OPTION, buf);
}

/**
 * @brief     PAW2312DB 设置8bit数据输出模式
 * @param[in] enable - true-8bit模式 false-12bit模式
 */
void paw_set_8bit_mode(bool enable)
{
    paw_data.is_8bit_mode = enable;
    (enable) ? paw_clr_bit_mask(PAW_REG_MOUSE_OPTION, 0x04) : paw_set_bit_mask(PAW_REG_MOUSE_OPTION, 0x04);
}

/**
 * @brief     PAW2312DB 设置SPI模式
 * @param[in] mode 
 */
void paw_set_spi_mode(unsigned char mode)
{

}

/**
 * @brief PAW3212DB 运动检测
 * @param priv - 私有数据
 */
void paw_handle(void *priv)
{
    paw_img_quallity_read(&paw_data.iqc);
    paw_shutter_read(&paw_data.shutter);
    paw_frame_brightness_read(&paw_data.frame_avg);
    paw_motion_detect(&paw_data.is_motion, &paw_data.is_x_overflow, &paw_data.is_y_overflow);
    paw_xy_data_read(&paw_data.delta_x, &paw_data.delta_y);
    printf("delta_x:%d, delta_y:%d\r\n", paw_data.delta_x, paw_data.delta_y);
}

        paw3212db.h驱动文件如下:

#ifndef __DEV_PAW3212DB_H
#define __DEV_PAW3212DB_H

#include <stdbool.h>

/**
 * @brief  PAW3212DB 初始化
 */
void paw_init(void);

/**
 * @brief      PAW3212DB 产品ID读取
 * @param[out] id - 产品ID
 */
void paw_product_id_read(unsigned char *id);

/**
 * @brief PAW2312DB 运动状态+XY轴数据状态检测
 * @param[out] is_motion - 是否有运动
 * @param[out] is_x_overflow - X轴数据溢出
 * @param[out] is_y_overflow - Y轴数据溢出
 */
void paw_motion_detect(bool *is_motion, bool *is_x_overflow, bool *is_y_overflow);

/**
 * @brief      PAW2312DB XY轴数据读取
 * @param[out] delta_x - x轴运动位移增量数据
 * @param[out] delta_y - y轴运动位移增量数据
 */
void paw_xy_data_read(signed short *delta_x, signed short *delta_y);

/**
 * @brief     PAW2312DB 睡眠模式设置
 * @param[in] mode - 睡眠模式 
 * @note      mode: 详细说明见数据手册第25页 Operation_Mode寄存器和Configuration寄存器
 *            0-禁用睡眠模式1和2
 *            1-启用睡眠模式1 禁用睡眠模式2
 *            2-启用睡眠模式1和2
 *            3-进入睡眠模式2
 *            4-进入睡眠模式1
 *            5-从睡眠模式唤醒到运行模式
 *            6-禁用睡眠模式3
 *            7-启用睡眠模式3
 */
void paw_set_sleep_mode(unsigned char mode);

/**
 * @brief PAW2312DB 复位
 */
void paw_reset(void);

/**
 * @brief     PAW2312DB 电源模式控制
 * @param[in] enable - true-正常模式 false-掉电模式
 */
void paw_power_mode_ctrl(bool enable);

/**
 * @brief     PAW3212DB 写保护控制
 * @param[in] enable - true-启用写保护 false-禁用写保护
 */
void paw_write_protect_ctrl(bool enable);

/**
 * @brief        PAW3212DB 设置运动轴CPI分辨率 默认值0x1B=27
 * @param[inout] x_cpi - x轴运动阈值 最大值63
 * @param[inout] y_cpi - y轴运动阈值 最大值63
 * @note         目标CPI分辨率=38*cpi  cpi建议取值范围16-63
 *               x_cpi和y_cpi为0xFF时代表读取寄存器值 否则写入寄存器
 */
void paw_xy_cpi_config(unsigned char *x_cpi, unsigned char *y_cpi);

/**
 * @brief      PAW3212DB IQC(Image Quality Compliment,图像质量评价) 数据读取
 * @param[out] iqc - IQC数据 0-255
 * @note       查看传感器表面的图像质量/模糊度 其值越小传感器的图像跟踪越准确
 */
void paw_img_quallity_read(unsigned char *iqc);

/**
 * @brief      PAW3212DB LED快门时间数据读取
 * @param[out] shutter - LED快门时间数据 0-28
 * @note       LED快门时间 = (shutter+1)*7.1us
 */
void paw_shutter_read(unsigned char *shutter);

/**
 * @brief      PAW3212DB 帧平均亮度数据读取
 * @param[out] brightness - 帧平均亮度数据 0-255
 * @note       一帧像素点 324
 */
void paw_frame_brightness_read(unsigned char *brightness);

/**
 * @brief     PAW3212DB 设置旋转角度
 * @param[in] rotation - 旋转角度 0-3对应0-360度
 * @note      默认摆放为0度(即芯片1脚在左上角的位置) 具体位置说明参见数据手册28页Mouse_Option寄存器
 */
void paw_set_rotation(unsigned char rotation);

/**
 * @brief     PAW2312DB 设置8bit数据输出模式
 * @param[in] enable - true-8bit模式 false-12bit模式
 */
void paw_set_8bit_mode(bool enable);

/**
 * @brief      PAW2312DB XY轴数据读取
 * @param[out] x - x轴运动数据
 * @param[out] y - y轴运动数据
 */
void paw_xy_data_read(signed short *x, signed short *y);

/**
 * @brief     PAW3212DB 运动检测
 * @param[in] priv - 私有数据
 */
void paw_handle(void *priv);

#endif

        由于驱动文件选择了一种低耦合的写法,底层的SPI驱动设计在“bsp.h”文件中完成的,需要注意的是,根据传感器的引脚说明和时序图分析,SPI的通信这里通过的软件SPI进行模拟,且SCK引脚初始时应为高电平,SPI数据在SCK的下降沿发送,上升沿锁定并读取数据,其主要代码实现如下:

#include "bsp.h"

/* public functions -------------------------------------------------------------- */
/**
  * @brief  BSP initialization.
  * @retval None
  */
void bsp_init(void)
{
    bsp_pin_config();
}

/* SPI控制 ------------------------------------------------------------------- */
/**
 * @brief PAW_SPI_CS控制
 * @param state - false 关闭; true 打开
 */
void bsp_paw_spi_cs_ctrl(bool state)
{
    GPIO_WRITE(PAW_SPI_CS_PORT, PAW_SPI_CS_PIN, (BitAction)state);
}

/**
 * @brief PAW_SPI_SCK控制
 * @param state - false 关闭; true 打开
 */
void bsp_paw_spi_sck_ctrl(bool state)
{
    GPIO_WRITE(PAW_SPI_SCK_PORT, PAW_SPI_SCK_PIN, (BitAction)state);
}

/**
 * @brief PAW_SPI_MOSI控制
 * @param state - false 关闭; true 打开
 */
void bsp_paw_spi_mosi_ctrl(bool state)
{
    #if (USED_DUAL_SPI == 1)
        pin_config(PAW_SPI_MOSI_PORT, PAW_SPI_MOSI_PIN, GPIO_Mode_Out_PP, GPIO_Speed_50MHz);
    #endif
    GPIO_WRITE(PAW_SPI_MOSI_PORT, PAW_SPI_MOSI_PIN, (BitAction)state);
}

/**
 * @brief  PAW_SPI_MISO控制
 * @retval true 高电平; false 低电平
 */
bool bsp_paw_spi_miso_read(void)
{
    #if (USED_DUAL_SPI == 1)
        pin_config(PAW_SPI_MOSI_PORT, PAW_SPI_MOSI_PIN, GPIO_Mode_IPD, GPIO_Speed_50MHz);
    #endif
    return (GPIO_READ_INPUT(PAW_SPI_MISO_PORT, PAW_SPI_MISO_PIN) ? (true) : (false));
}

/**
 * @brief  SPI 发送数据
 * @param  val - 要发送的数据
 * @retval 无
 * @note   使用软件模拟SPI时 时钟频率在71KHz左右
 */
void bsp_spi_write(unsigned char val)
{
    /* 硬件SPI */
    #if (USED_HARD_SPI == 1) 
        while (!(USED_SPIx->SR & SPI_SR_TXE));  /* 等待发送寄存器空 */
        USED_SPIx->DR = val;                    /* 发送数据 */
        while (!(USED_SPIx->SR & SPI_SR_RXNE)); /* 等待接收寄存器非空 */
    #else /* 软件SPI */
        unsigned char i = 0;
        bsp_paw_spi_sck_ctrl(1);    /* 置高电平 */
        for(i=0; i<8; i++) {        /* 先发高位 */
            bsp_paw_spi_mosi_ctrl( (val & (1<<(7-i))) ? (true) : (false));  /* 发送数据 */
            soft_delay_us(3);
            bsp_paw_spi_sck_ctrl(false);
            soft_delay_us(10);
            bsp_paw_spi_sck_ctrl(true);
        }
    #endif
}

/**
 * @brief  SPI 接收数据
 * @retval unsigned char - 接收到的数据
 * @note   使用软件模拟SPI时 时钟频率在71KHz左右
 */
unsigned char bsp_spi_read(void)
{
    /* 硬件SPI */
    #if (USED_HARD_SPI == 1) 
        while (!(USED_SPIx->SR & SPI_SR_TXE));  /* 等待发送寄存器空 */
        USED_SPIx->DR = val;                    /* 发送数据 */
        while (!(USED_SPIx->SR & SPI_SR_RXNE)); /* 等待接收寄存器非空 */
        return (USED_SPIx->DR);                 /* 返回接收到的数据 */
    #else /* 软件SPI */
        unsigned char i, rx_buf;
        bsp_paw_spi_sck_ctrl(true);
        for(i=0; i<8; i++) { // 准备数据
            bsp_paw_spi_mosi_ctrl(true); // 写入0xFF
            soft_delay_us(10);
            bsp_paw_spi_sck_ctrl(false);
            soft_delay_us(3);
            if(bsp_paw_spi_miso_read() == true) { /* 数据手册提示 高电平区间接收数据 */
                rx_buf |= (1 << (7 - i));
            }
            bsp_paw_spi_sck_ctrl(true);
        }
        return rx_buf;
    #endif
}

/* private functions -------------------------------------------------------- */
/**
 * @brief 硬件层GPIO初始化
 * @param GPIOx - GPIOx 端口地址
 * @param Pin   - 引脚编号
 * @param mode  - 模式
 * @param speed - 速度
 * @param pull  - 上拉/下拉
 * @param alternate - 复用
 */
static void pin_config(void *GPIOx, uint16_t pin, uint32_t mode, uint8_t speed)
{
    assert_param(IS_GPIO_ALL_PERIPH((GPIO_InitTypeDef *)GPIOx));
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Speed = (GPIOSpeed_TypeDef)speed;
    GPIO_InitStructure.GPIO_Mode  = (GPIOMode_TypeDef)mode;
    GPIO_InitStructure.GPIO_Pin   = pin;
    GPIO_Init((GPIO_TypeDef *)GPIOx, &GPIO_InitStructure);
}

/**
 * @brief 硬件层GPIO初始化
 */
static void bsp_pin_config(void)
{
    /* 时钟配置 -------------------------------------------------------- */
    GPIOA_CLK_ENABLE();
    GPIOB_CLK_ENABLE();
    GPIO_AF_CLK_ENABLE();

    /* PAW3212DB PIN初始化 */
    pin_config(PAW_SPI_CS_PORT, PAW_SPI_CS_PIN, GPIO_Mode_Out_PP, GPIO_Speed_2MHz);
    #if (USED_HARD_SPI == 1) 
        pin_config(PAW_SPI_SCK_PORT, PAW_SPI_SCK_PIN, GPIO_Mode_AF_PP, GPIO_Speed_50MHz);
        pin_config(PAW_SPI_MISO_PORT, PAW_SPI_MISO_PIN, GPIO_Mode_IPD, GPIO_Speed_50MHz);
        pin_config(PAW_SPI_MOSI_PORT, PAW_SPI_MOSI_PIN, GPIO_Mode_AF_PP, GPIO_Speed_50MHz);
    #else
        pin_config(PAW_SPI_SCK_PORT, PAW_SPI_SCK_PIN, GPIO_Mode_Out_PP, GPIO_Speed_50MHz);
        pin_config(PAW_SPI_MISO_PORT, PAW_SPI_MISO_PIN, GPIO_Mode_IPD, GPIO_Speed_50MHz);
        pin_config(PAW_SPI_MOSI_PORT, PAW_SPI_MOSI_PIN, GPIO_Mode_Out_PP, GPIO_Speed_50MHz);
    #endif

    bsp_paw_spi_cs_ctrl(false);         /* SPI 片选置初值 */
    bsp_paw_spi_sck_ctrl(true);         /* SPI 时钟置初值 */
}

        bsp.h文件配置如下:

#ifndef __BSP_H
#define __BSP_H

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#include "misc.h"
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"


/* enum definition --------------------------------------------------------- */


/* macro definition --------------------------------------------------------- */


/* pins macro definition ---------------------------------------------------- */
/* GPIO时钟使能 */
#define     GPIOA_CLK_ENABLE()          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE)
#define     GPIOB_CLK_ENABLE()          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE)
#define     GPIOC_CLK_ENABLE()          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE)
#define     GPIOD_CLK_ENABLE()          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE)
/* GPIO时钟失能 */
#define     GPIOA_CLK_DISABLE()         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, DISABLE)
#define     GPIOB_CLK_DISABLE()         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, DISABLE)
#define     GPIOC_CLK_DISABLE()         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, DISABLE)
#define     GPIOD_CLK_DISABLE()         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, DISABLE)
/* GPIO操作 */
#define     GPIO_WRITE(port, pin, val)  ((val) ? (port->BSRR = pin) : (port->BRR = pin))
#define     GPIO_READ_OUTPUT(port, pin) (port->ODR & pin)
#define     GPIO_READ_INPUT(port, pin)  (port->IDR & pin)
/* 复用时钟使能/失能 */
#define     GPIO_AF_CLK_ENABLE()        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE)
#define     GPIO_AF_CLK_DISABLE()       RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, DISABLE)

/* SPI配置 ----------------------------------------------------- */
#define     USED_HARD_SPI               (0)
#define     USED_DUAL_SPI               (1) /* SPI从机设备IO是双向IO */
#if (USED_HARD_SPI == 1)
    #define     USED_SPIx                   SPI1
    #define     SPI_DMA_ENABLE              (0u)
    #if (SPI_DMA_ENABLE==1)
    #define     USED_DMAx                   DMA1
    #define     USED_DMAx_CHANNEL           DMA1_Channel3   /* SPI1 TX */
    #define     USED_DMAx_FLAG              DMA1_FLAG_TC3
    #endif
#endif
/* PAW3212DB SCK */
#define     PAW_SPI_SCK_PORT            GPIOB
#define     PAW_SPI_SCK_PIN             GPIO_Pin_3
/* PAW3212DB MISO */
#define     PAW_SPI_MISO_PORT           GPIOB
#define     PAW_SPI_MISO_PIN            GPIO_Pin_4
/* PAW3212DB MOSI */
#define     PAW_SPI_MOSI_PORT           GPIOA
#define     PAW_SPI_MOSI_PIN            GPIO_Pin_12
/* PAW3212DB CS */
#define     PAW_SPI_CS_PORT             GPIOA
#define     PAW_SPI_CS_PIN              GPIO_Pin_15

/* SPI控制 ------------------------------------------------------------------- */
/**
 * @brief PAW_SPI_CS控制
 * @param state - false 关闭; true 打开
 */
void bsp_paw_spi_cs_ctrl(bool state);

/**
 * @brief PAW_SPI_SCK控制
 * @param state - false 关闭; true 打开
 */
void bsp_paw_spi_sck_ctrl(bool state);

/**
 * @brief PAW_SPI_MOSI控制
 * @param state - false 关闭; true 打开
 */
void bsp_paw_spi_mosi_ctrl(bool state);

/**
 * @brief  PAW_SPI_MISO控制
 * @retval true 高电平; false 低电平
 */
bool bsp_paw_spi_miso_read(void);

/**
 * @brief  SPI 发送数据
 * @param  val - 要发送的数据
 * @retval 无
 * @note   使用软件模拟SPI时 时钟频率在71KHz左右
 */
void bsp_spi_write(unsigned char val);

/**
 * @brief  SPI 接收数据
 * @retval unsigned char - 接收到的数据
 * @note   使用软件模拟SPI时 时钟频率在71KHz左右
 */
unsigned char bsp_spi_read(void);

#endif

三、驱动示例

         代码一般驱动和使用流程如下:

1. 在bsp.h文件中修改传感器的接口引脚宏定义,使之适配自己的端口引脚;

2. 调用bsp_init()函数和paw_init()函数用于初始化传感器;

3. 周期性的调用paw_handle()函数用于获取传感器的数据,这里使用的是结构体数据做输出,若想要单个数据可以创建接口函数去获取并添加打印函数实现;

        具体驱动实现示例如下:

#include "bsp.h"
#include "app.h"
#include "delay.h"
#include "dev_task_timer.h"

static void app_paw_entry_task(void *priv);

int main(void)
{
    delay_init();
    bsp_init();
    app_init();
    
    dev_task_timer_init();
    
    dev_task_timer_create(TASK_ID_PAW , app_paw_entry_task , nullptr, 10 , TASK_STATE_START);

    while(1) {
        delay_ms(1000);
    }
}

/**
 * @brief paw运动控制处理任务
 * @param priv - 私有数据
 */
void app_paw_entry_task(void *priv)
{
    paw_handle(nullptr);
}

        这里实现的只是通过Keil MDK自带的Debug窗口去获取传感器的结构体去进行数据调试获取,并没有添加外部接口实现,所以需要的可以自行添加接口获取对应数据。

四、个人广告

        如有嵌入式课程设计和软件外包设计的需要,可联系本人,保证价格童叟无欺,且提供售后支持和服务,也算是凭借知识赚钱。当然有遇到部分疑问也可联系作者帮助解决。

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文笔记】X-Former: Unifying Contrastive and Reconstruction Learning for MLLMs

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: X-Former: Unifying Contr…

为您的 WordPress 网站打造完美广告布局 A5广告单元格插件

一个为 WordPress 网站量身定制的强大工具,它将彻底改变您展示广告的方式 灵活多变的布局设计 A5 广告单元格插件的核心优势在于其无与伦比的灵活性。无论您是想要创建整齐的网格布局,还是希望打造独特的不规则设计,这款插件都能满足您的需求。 自定义网格数量&#xff1a;从 2…

C# 条形码、二维码标签打印程序

1、条码标答打印主界面 2、打印设置 3、生成QR代码 private void GetBarcode_T(string lr) { QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();//创建一个对象 qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE; //设置编码测量…

Mamba学习笔记(2)—序列数据处理基础

文章目录 (1) RNN&#xff08;Recurrent Neural Networks&#xff09;基本原理代码定义 (2) SLTM (Long Short-Term Memory)基本原理代码定义 (3) GRU (Gated Recurrent Unit)基本原理代码定义 (4) Transformer&#xff08;☆☆☆Attention Is All You Need☆☆☆&#xff09;0…

量子门电路开销——T门、clifford门、toffoli门、fredkin门

在量子计算中&#xff0c;T门的成本比Clifford门高出很多倍的原因与量子计算中纠错的实现、物理门操作的复杂性以及容错量子计算架构中的成本评估有关。以下是几个关键原因&#xff0c;解释了为什么 T 门的成本在量子计算中远远高于 Clifford 门&#xff1a; 1. T 门和 Cliffo…

递归、搜索与回溯(二)——递归练习与快速幂

文章目录 递归、搜索与回溯——递归两两交换链表中的节点Pow(x, n) 递归、搜索与回溯——递归 该文仍然是解决递归问题&#xff0c;值得注意的是快速幂算法。接下来会系统学习二叉树深搜题目&#xff0c;慢慢走向搜索与回溯。 两两交换链表中的节点 原题链接&#xff1a;24. 两…

AI识谱——将乐曲转化为五线谱

导言&#xff1a; 会乐曲的小伙伴在听到一首好听的乐曲的时候&#xff0c;肯定想过将这首歌曲转换为谱子给弹出来。除了上网找乐谱、请大神帮忙扒谱或者自己扒谱外&#xff0c;小伙伴也可以尝试一下本文介绍的AI识谱流程&#xff0c;让我们开始吧&#xff01; 注意了&#xf…

2024 Python3.10 系统入门+进阶(十七):面向对象基础

目录 一、面向对象概述1.1 面向对象简介1.2 对象和类1.3 定义属性和行为1.3.1 用数据描述对象的状态1.3.2 行为就是动作 1.4 隐藏细节并创建公共接口1.5 组合1.6 继承1.6.1 继承提供抽象1.6.2 多重继承 二、封装2.1 Python类定义2.2 创建类的成员2.2.1 创建实例方法并访问2.2.2…

PythonExcel批量pingIP地址

问题&#xff1a; 作为一个电气工程师&#xff08;PLC&#xff09;&#xff0c;当设备掉线的时候&#xff0c;需要用ping工具来检查网线物理层是否可靠连接&#xff0c;当项目体量过大时&#xff0c;就不能一个手动输入命令了。 解决方案一&#xff1a; 使用CMD命令 for /L %…

机器学习在聚合物及其复合材料中的应用与实践

在当前的工业和科研领域&#xff0c;聚合物及其复合材料因其卓越的物理和化学性能而受到广泛关注。这些材料在航空航天、汽车制造、能源开发和生物医学等多个行业中发挥着至关重要的作用。随着材料科学的发展&#xff0c;传统的实验和理论分析方法已逐渐无法满足新材料研发的需…

【力扣打卡系列】滑动窗口与双指针(无重复字符的最长子串)

坚持按题型打卡&刷&梳理力扣算法题系列&#xff0c;语言为go&#xff0c;Day7 无重复字符的最长子串 题目描述解题思路 不含重复字符——》考虑使用哈希表来存储记录为了提高效率也可以用数组&#xff0c;hash : [128]bool{} &#xff08;因为存的是字符的ASCLL码&…

【Unity踩坑】无法关闭Unity(Application.Shutdown.CleanupEngine)

安装了Unity 6正式版&#xff0c;在关闭Unity 项目时&#xff0c;会出现下面的提示&#xff0c;一直无法关闭。 一直显示 Application.Shutdown.CleanupEngine。 查了一下。这是一个历史性问题了&#xff0c;看来依然没有解决。 参考&#xff1a;Application.Shutdown.Cleanu…

web API基础

作用和分类 作用: 就是使用 JS 去操作 html 和浏览器 分类&#xff1a; DOM (文档对象模型)、 BOM &#xff08;浏览器对象模型&#xff09; 什么是DOM DOM (Document Object Model) 译为文档对象模型&#xff0c;是 HTML 和 XML 文档的编程接口。 HTML DOM 定义了访问和操作 …

权限(补充)

在上一篇Linux权限&#xff08;想了解的可以点击看看哦&#xff09;中已经见识了一部分权限&#xff0c;但是少了很重要的一部分&#xff1a; 那就是用户之间的转换&#xff0c;文件读写的关系&#xff0c;这里就简单的介绍一些&#xff1b; 我们在Linux权限知道了目录权限的关…

推荐一款多显示器管理工具:DisplayMagician

DisplayMagician是一款开源工具&#xff0c;专为Windows用户设计&#xff0c;能够通过一个快捷方式轻松自动配置屏幕和声音。它特别适合游戏玩家和应用程序用户&#xff0c;可以实现屏幕配置、声音设备切换以及启动额外程序等功能&#xff0c;最后在游戏或应用程序关闭时&#…

Qml-Item的Id生效范围

Qml-Item的Id生效范围 前置声明 本实例在Qt6.5版本中做的验证同一个qml文件中&#xff0c;id是唯一的&#xff0c;即不同有两个相同id 的Item;当前qml文件中声明的id在当前文件中有效&#xff08;即如果其它组件中传入的id&#xff0c;与当前qml文件中id 相同&#xff0c;当前…

基于curl和wget命令编写的多文件或大文件批量上传下载

最近需要在windows和服务器linux系统之间传递大量的文件&#xff0c;部分文件非常大&#xff08;TB以上&#xff09;&#xff0c;并且文件夹中包含文件或文件夹&#xff0c;需要先进行上传再进行组织&#xff0c;因此就想办法结合curl和wget命令编写了命令或脚本工具&#xff0…

三菱PLC伺服-停止位置不正确故障排查

停止位置不正确时&#xff0c;请确认以下项目。 1)请确认伺服放大器(驱动单元)的电子齿轮的设定是否正确。 2&#xff09;请确认原点位置是否偏移。 1、设计近点信号(DOG)时&#xff0c;请考虑有足够为0N的时间能充分减速到爬行速度。该指令在DOG的前端开始减速到爬行速度&…

【云从】六、云存储

文章目录 1、应用架构2、存储设备3、存储方案3.1 直连式存储DAS3.2 网络连接存储NAS3.3 存储区域网络SAN3.4 分布式存储ServerSAN3.5 软件定义存储SDS 4、云存储4.1 云硬盘CBS4.2 文件存储CFS4.3 对象存储COS 1、应用架构 2、存储设备 硬盘性能对比&#xff1a; 硬盘接口对比&…

C语言(函数)—函数栈帧的创建和销毁

目录 前言 补充知识 一、函数线帧是什么&#xff1f; 二、函数线帧的实现&#xff08;举例说明&#xff09; 两数之和代码 ​编辑两数之和 汇编代码分析 执行第一条语句 执行第二条语句 执行第三条语句 执行第四、五、六条语句 执行第七条语句 执行第八、九、十条语句 执行第十…