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

目录

一、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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GRU神经网络理解

全文参考以下B站视频及《神经网络与深度学习》邱锡鹏&#xff0c;侧重对GPU模型的理解&#xff0c;初学者入门自用记录&#xff0c;有问题请指正【重温经典】GRU循环神经网络 —— LSTM的轻量级版本&#xff0c;大白话讲解_哔哩哔哩_bilibili 更新门、重置门、学习与输出 注&a…

Go通过gorm连接sqlserver报错TLS Handshake failed

Go通过gorm连接sqlserver报错TLS Handshake failed [error] failed to initialize database, got error TLS Handshake failed: tls: server selected unsupported protocol version 301 panic: TLS Handshake failed: tls: server selected unsupported protocol version 301 …

综合小程序的设计

熟悉python可视化的设计 完成综合小程序的设计。 登录系统设计 from tkinter import * import tkinter.messagebox def onClick(): namebname.get() pwdbpwd.getO() if (namezhou and pwd123): tkinter.messagebox.showinfo(title提示,message登陆成功&a…

Linux 中 .bash_history、.bash_logout 等用户配置文件

目录 前言.bash_history.bash_logout.bash_profile.bashrc.cshrc.tcshrc.viminfo 总结 前言 在 Linux 中我们经常会看见用户家目录下存在 .bash_history、.bash_logout、.bash_profile、.bashrc、.cshrc、.tcshrc、.viminfo 这写文件&#xff0c;那它们区别是什么呢&#xff1…

2024软考网络工程师笔记 - 第8章.网络安全

文章目录 网络安全基础1️⃣网络安全威胁类型2️⃣网络攻击类型3️⃣安全目标与技术 &#x1f551;现代加密技术1️⃣私钥密码/对称密码体制2️⃣对称加密算法总结3️⃣公钥密码/非对称密码4️⃣混合密码5️⃣国产加密算法 - SM 系列6️⃣认证7️⃣基于公钥的认证 &#x1f552…

Unity CRP学习笔记(一)

Unity CRP学习笔记&#xff08;一&#xff09; 主要参考&#xff1a; https://catlikecoding.com/unity/tutorials/custom-srp/ https://docs.unity.cn/cn/2022.3/ScriptReference/index.html 中文教程部分参考&#xff08;可选&#xff09;&#xff1a; https://tuncle.blog/c…

算力的定义、单位、影响因素、提升方法、分类、应用等。附超算排名

文章目录 算力的定义算力的单位FLOPS&#xff08;Floating Point Operations Per Second&#xff0c;浮点运算次数/秒&#xff09;IPS&#xff08;Instructions Per Second&#xff0c;指令/秒&#xff09;TOPS&#xff08;Trillion Operations Per Second&#xff0c;万亿次/秒…

Win10系统安装docker操作步骤

Docker下载 docker下载地址&#xff1a;Docker: Accelerated Container Application Development 打开网页后&#xff0c;点击图下所示&#xff0c;下载windows版本的docker 启用Hyper-V 和容器特性 右键左下角windows图标&#xff0c;选择应用和功能 然后在下面的界面中&am…

【Nuvoton干货分享】开发应用篇 4 -- 8bit MCU Flash 操作

我们在进行实际开发设计中&#xff0c;难免需要进行数据存储&#xff0c;早期很多都是外接EEPROM来进行设计&#xff0c;但是需要增加成本。其实芯片内部的Flash也是可以当成数据存储空间的。本章节主要介绍新唐的8位机如何进行常量数据的存储操作。 一、存储空间划分 我这边…

w~自动驾驶合集6

我自己的原文哦~ https://blog.51cto.com/whaosoft/12286744 #自动驾驶的技术发展路线 端到端自动驾驶 Recent Advancements in End-to-End Autonomous Driving using Deep Learning: A SurveyEnd-to-end Autonomous Driving: Challenges and Frontiers 在线高精地图 HDMa…

小程序无法获取头像昵称以及手机号码

用户在使用小程序的时候&#xff0c;登录弹出获取昵称头像或者个人中心点击默认头像弹窗获取头像昵称的时候&#xff0c;点击弹窗中的头像昵称均无反应&#xff0c; 这个是因为你的小程序隐私政策没有更新&#xff0c;或者老版本没有弹窗让用户同意导致的 解决办法&#xff1…

利用彩色相机给激光点云染色

文章目录 概述核心代码效果概述 在激光SLAM(Simultaneous Localization and Mapping)中,使用彩色相机为激光点云染色是一个常见的做法。这种技术结合了激光雷达的高精度距离测量和相机的丰富色彩信息,使得生成的点云不仅包含空间位置信息,还包含颜色信息,从而更直观和细…

【OpenAI】第六节(语音生成与语音识别技术)从 ChatGPT 到 Whisper 的全方位指南

前言 在人工智能的浪潮中&#xff0c;语音识别技术正逐渐成为我们日常生活中不可或缺的一部分。随着 OpenAI 的 Whisper 模型的推出&#xff0c;语音转文本的过程变得前所未有的简单和高效。无论是从 YouTube 视频中提取信息&#xff0c;还是将播客内容转化为文本&#xff0c;…

[实时计算flink]数据摄入YAML作业快速入门

实时计算Flink版基于Flink CDC&#xff0c;通过开发YAML作业的方式有效地实现了将数据从源端同步到目标端的数据摄入工作。本文介绍如何快速构建一个YAML作业将MySQL库中的所有数据同步到StarRocks中。 前提条件 已创建Flink工作空间&#xff0c;详情请参见开通实时计算Flink版…

Jenkins配置CI/CD开发环境(理论到实践的完整流程)

目录 一、对于CI/CD的理解1.1、什么是CI&#xff08;持续集成&#xff09;1.2、CI 的主要特点&#xff1a;1.3、CI 的优势&#xff1a;**实际开发中的场景举例:** 1.4、什么是 CD&#xff08;持续交付和持续部署&#xff09;1.5、持续交付&#xff08;Continuous Delivery&…

鸿蒙HarmonyOS NEXT 5.0开发(2)—— ArkUI布局组件

文章目录 布局Column&#xff1a;从上往下的布局Row&#xff1a;从左往右的布局Stack&#xff1a;堆叠布局Flex&#xff1a;自动换行或列 组件Swiper各种选择组件 华为官方教程B站视频教程 布局 主轴和交叉轴的概念&#xff1a; 对于Column布局而言&#xff0c;主轴是垂直方…

如何区分真假Facebook三不限海外户?

对于需要推广到海外的企业来说&#xff0c;Facebook是一个重要的渠道。由于Facebook对国内普通企业户的风控极为严格&#xff0c;让不少出海广告主都很头疼&#xff0c;一到要出量的时刻就限额、挂户各种问题&#xff0c;根本没有办法跑起来量&#xff0c;白白错过好时机。对于…

R语言统计分析——置换检验3

参考资料&#xff1a;R语言实战【第2版】 列联表的独立性 通过chisq_test()或cmh_test()函数&#xff0c;我们可以用置换检验判断两类别型变量的独立性。当数据可根据第三个类别型变量进行分层时&#xff0c;需要使用后一个函数。若变量都是有序型&#xff0c;可使用lbl_test(…

047_python基于Hadoop的租房数据分析系统的设计与实现

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

基于 Konva 实现Web PPT 编辑器(三)

完善公式 上一节我们简单讲述了公式的使用&#xff0c;并没有给出完整的样例&#xff0c;下面还是完善下相关步骤&#xff0c;我们是默认支持公式的编辑功能的哈&#xff0c;因此&#xff0c;我们只需要提供必要的符号即可&#xff1a; 符号所表达的含义是 mathlive 的command命…