目录
一、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.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所示:
其获取数据对应的传感器方向示意图如图3所示:
1.3 传感器组装
由于该传感器的光学器件是集成在芯片内部的,但是需要透镜对红外LED灯进行光学反射和折射,所以需要使用一个光学透镜和红外LED灯(数据手册建议使用昌裕 LED 配光测角仪 V110 来测量 LED 视角。),其3D组装示意图见下图4说明:
1.4 应用场景
- 无线鼠标和键盘:用于提高操作的精准度和响应速度;
- 游戏控制器:增强玩家体验,提供更流畅的操控感;
- 移动设备:可以用于手势识别和运动控制,提升用户交互体验;
1.5 传感器使用注意
1.5.1 供电选择
传感器的上电默认设置在 High Voltage 段。如果用户想使用Low Voltage段,则应在上电序列后设置传感器寄存器(地址0x4B)(值 0x40)。如果此寄存器设置不正确,则由于内部稳压器的电流泄漏,传感器将消耗额外的功率。其对应数据手册说明如下图5:
其中供电的接法选择如下图6所示,左侧为High Voltage对应接法,右侧为Low Voltage接法。
1.5.2 SPI读写设置
这里以3线SPI为例进行说明,两线SPI的读写设置只需要将CS引脚一直下拉到地,且设置寄存器读写说明即可。
PAW3212DB与MCU的传输协议采用的是3线半双工的SPI协议,即同一时刻只有MISO接收数据或者MOSI发送数据。其中SDI0上的数据更改发生在 SCLK的时钟下降沿,时钟的上升沿用于数据保持。
其寄存器的写入和读取两种操作模式都由两个字节组成。第一个字节包含7位数据(寄存器地址)+最高位MSB(读/写方向判定,0代表读操作,1代表写操作);第二个字节包含数据。其具体说明示意入下图7所示。
写入操作时序示意图如下:
读取操作时序示意图如下:
1.5.3 MOTION引脚
每当传感器检测到运动的发生时,MOTION 引脚将从高变为低电平。MOTION 引脚可用于监控传感器运动数据是否已被清除。如果运动数据未被清除,MOTON 引脚将保持低电平,当主机控制器读出所有运动数据后(即 Motion bit.Delta X和Delta Y均为零),MOTION 引脚将变为高电平。
当鼠标系统工作在空闲状态时,传感器处于 Sleep1/Sleep2/Sleep3 模式,主机控制器处于空闲模式,当传感器检测到运动发生时,MOTION 引脚将变为低电平。MOTION 引脚上的下降沿可用作唤醒主机控制器的中断事件。其状态说明见下图10。
1.6 寄存器说明
传感器的所有寄存器示意图见下图11所示:
这里只介绍几个比较常用的寄存器,具体说明如下:
1.6.1 Product_ID1寄存器
该寄存器可以检查SPI的通信是否完成,如果SPI正常通信,则可以读取到该寄存器的数据值为0x30,具体数据手册说明见下图12:
1.6.2 MOTION_Status寄存器
在运动检测例程中,主机控制器可以通过检査 寄存器的Motion 位来轮询传感器以获取有效的运动数据。如果Motion 位置位,则 Delta X和 Delta Y寄存器中的运动数据有效,可以读取。在读出 Delta X和 Delta Y寄存器之前请务必先读取 Motion 位。DXOVF 位和 DYOVF 位显示自上次读出以来运动报告缓冲区是否溢出。具体数据手册说明见下图13。
1.6.3 Delta_X寄存器
该寄存器可以读取X轴Motion Status运动。绝对值由 CPI X的分辨率设置决定。读取此寄存器将清除此寄存器的内容。数据范围:-128~+127。MSB 位表示符号位。具体寄存器说明见下图14。
1.6.4 Delta_Y寄存器
该寄存器可以读取Y轴Motion Status运动。绝对值由 CPI Y的分辨率设置决定。读取此寄存器将清除此寄存器的内容。数据范围:-128~+127。MSB 位表示符号位。具体寄存器说明见下图15。
二、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窗口去获取传感器的结构体去进行数据调试获取,并没有添加外部接口实现,所以需要的可以自行添加接口获取对应数据。
四、个人广告
如有嵌入式课程设计和软件外包设计的需要,可联系本人,保证价格童叟无欺,且提供售后支持和服务,也算是凭借知识赚钱。当然有遇到部分疑问也可联系作者帮助解决。