当前位置: 首页 > news >正文

传感器驱动系列之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);#endifGPIO_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);#endifreturn (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); // 写入0xFFsoft_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);#elsepin_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);#endifbsp_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.mrgr.cn/news/54835.html

相关文章:

  • ruoyi域名跳转缓存冲突问题(解决办法修改:session名修改session的JSESSIONID名称)
  • C++ 异步执行任务async()(补充)
  • 曲线与平面曲线 | 正则曲线、弧长参数、切线方程曲率
  • 数据结构 -- 排序算法
  • vulnhub靶场之JOY
  • Spring Boot与JavaWeb协同:在线考试系统的实现“
  • AMBA-CHI协议详解(十)
  • Pencils Protocol 用户特权?持有 DAPP 将获 Scroll 生态空投!
  • 模型的部署:服务端与客户端建立连接(Flask)
  • GO语言编程之旅
  • 【27续】c++项目练习
  • 软件游戏缺失d3dx9_42.dll如何修复,马上教你6种靠谱的方法
  • 【设计模式-迪米特法则】
  • 网页从输入网址到页面渲染完成都经历了哪些过程?
  • 区块链可投会议CCF B--SenSys 2025 截止11.07 附2023录用率
  • 水题四道。
  • RAG流程的实现与改进
  • Codeforces Round 979 (Div. 2) B. Minimise Oneness
  • spdlog学习记录
  • Redis高阶篇之Redis单线程与多线程
  • 【深度学习】(12)--模型部署 <连接客户端与服务端>
  • 【Java SE 】封装 的特性 和 static 详解
  • 【C++】13.string类的底层
  • 机器学习与神经网络:科技的星辰大海
  • 关于WPF项目降低.Net版本
  • java分页遍历