在《龙芯2k0300 - 久久派开发环境搭建及内核升级》中我们介绍了如何将久久派默认带有的PMON固件以及内核linux 4.19升级为uboot及linux 6.12版本。当我们的板子执行了升级操作后,我们的系统环境已经搭建完成了,接下来的任务就是移植驱动程序和编写应用程序。其中涉及的模块有:
- VL53测距模块;
- 1.8寸SPI TFT屏幕;
- MPU6050陀螺仪。
这里我们参考的资料主要来源以下两个仓库:
- 《龙芯2K300_301软件开源库》;
- 《WwuSama 21届智能车走马观碑开源仓库》。
一、开源驱动
1.1 创建项目目录
在/opt/2k0300目录下创建子目录loongson_2k300_lib;- zhengyang@ubuntu:/opt/2k0300$ mkdir loongson_2k300_lib
- zhengyang@ubuntu:/opt/2k0300$ cd loongson_2k300_lib
复制代码 创建驱动子目录:- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib$ mkdir driver
复制代码 创建测试用例子目录:- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib$ mkdir example
复制代码 创建走马观碑项目子目录:- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib$ mkdir car_project
复制代码 1.2 拷贝驱动
这里我们将《WwuSama/21届智能车走马观碑开源仓库》开源的linux 6.9源码下载下来,需要注意的是仓库中已经将linux内核源码移除了,但是其QQ交流群依然提供了内核源码,这里我下载了linux-6.9-WuwuSama-99pi.tar.gz,其驱动位于drivers/wuwu_drivers/:- zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.9-WuwuSama-99pi$ ll drivers/wuwu_drivers/
- -rw-rw-r-- 1 zhengyang zhengyang 7109 12月 17 16:42 imc20602.h
- -rw-rw-r-- 1 zhengyang zhengyang 4144 12月 17 16:42 wuwu_brushless.c
- -rw-rw-r-- 1 zhengyang zhengyang 3601 12月 17 16:42 wuwu_buzzer.c
- -rw-rw-r-- 1 zhengyang zhengyang 4134 12月 17 16:42 wuwu_fans.c
- -rw-rw-r-- 1 zhengyang zhengyang 9198 12月 17 16:42 wuwu_icm42688.c
- -rw-rw-r-- 1 zhengyang zhengyang 5306 12月 17 16:42 wuwu_icm42688.h
- -rw-rw-r-- 1 zhengyang zhengyang 11666 12月 17 16:42 wuwu_imc20602.c
- -rw-rw-r-- 1 zhengyang zhengyang 13770 12月 17 16:42 wuwu_motor.c
- -rw-rw-r-- 1 zhengyang zhengyang 1488 12月 17 16:42 wuwu_motor.h
- -rw-rw-r-- 1 zhengyang zhengyang 4416 12月 17 16:42 wuwu_servo.c
- -rw-rw-r-- 1 zhengyang zhengyang 30204 12月 17 16:42 wuwu_vl53l0x.c
- -rw-rw-r-- 1 zhengyang zhengyang 877 12月 17 16:42 wuwu_vl53l0x.h
复制代码 我们将这些驱动拷贝到loongson_2k300_lib/driver目录,供我们参考使用;- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/driver$ cp /opt/2k0300/build-2k0300/workspace/linux-6.9-WuwuSama-99pi/drivers/wuwu_drivers/* ./
复制代码 1.3 准备工作
在下文,我们将会进行各个模块的驱动编译,在编译之前,先在内核源码目录执行如下命令,生成 Module.symvers:- zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ source ../set_env.sh && make loongson_2k300_defconfig V=1
- # 生成Module.symvers的命令
- zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ source ../set_env.sh && make modules_prepare -j$(nproc)
- # 如果没有Module.symvers才需要执行下面这条指令
- zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ cp vmlinux.symvers Module.symvers
复制代码 这一步会生成完整的 Module.symvers,其中记录了内核导出的所有符号信息。
二、vl53l0x设备驱动
如果对I2C驱动源码移植感兴趣,可参考:
- 《通信协议-I2C》;
- 《linux驱动移植-I2C总线设备驱动》;
- 《linux驱动移植-I2C适配器驱动移植》;
- 《linux驱动移植-I2C驱动移植(OLED SSD1306)》。
2.1 项目目录
接下来我们创建子目录i2c_vl53l0x_driver :- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/driver$ mkdir i2c_vl53l0x_driver
复制代码 拷贝源码:- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/driver/i2c_vl53l0x_driver$ cp ../wuwu_vl53l0x.* ./
- zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/driver/i2c_vl53l0x_driver$ ll
- -rw-rw-r-- 1 zhengyang zhengyang 30204 3月 23 21:25 wuwu_vl53l0x.c
- -rw-rw-r-- 1 zhengyang zhengyang 877 3月 23 21:25 wuwu_vl53l0x.h
复制代码 wuwu_vl53l0x.c代码内容较多,这里我们对其进行拆分,并进行适当调整,最终目录结构如下:- i2c_vl53l0x_driver/
- ├── inc/
- │ └── vl53l0x.h # 公共头文件(宏、结构体定义、ioctl 命令等)
- ├── src/
- │ ├── vl53l0x_core.c # 核心层:传感器初始化、测距算法
- │ ├── vl53l0x_dev.c # 设备层:字符设备接口
- │ └── vl53l0x_hw.c # 硬件层:I2C 读写操作
- ├── vl53l0x_driver.c # 驱动层:I2C 驱动注册、probe/remove
- └── Makefile
复制代码 2.2 vl53l0x.h
inc为头文件目录,在inc创建vl53l0x.h文件;- /*
- * VL53L0X Linux I2C Driver
- * Copyright (C) 2026 zhengyang
- *
- * Based on Seekfree TC264 Library: https://gitee.com/seekfree/TC264_Library
- * SPDX-License-Identifier: GPL-2.0-only
- */
- #ifndef _VL53L0X_H_
- #define _VL53L0X_H_
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/uaccess.h>
- #include <linux/io.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/i2c.h>
- #include <linux/delay.h>
- #include <linux/ioctl.h>
- /* ========================= 设备信息 ========================= */
- #define VL53L0X_COUNT 1
- #define VL53L0X_NAME "vl53l0x_tof"
- /* ========================= ioctl 命令 ========================= */
- #define VL53L0X_IOCTL_MAGIC 't'
- #define VL53L0X_GET_DATA _IOR(VL53L0X_IOCTL_MAGIC, 1, uint16_t)
- /* ========================= 硬件常量 ========================= */
- #define VL53L0X_TIMEOUT_COUNT 10
- /* ========================= 辅助宏 ========================= */
- #define decode_vcsel_period(reg_val) (((reg_val) + 1) << 1)
- #define calc_macro_period(vcsel_period_pclks) ((((uint32_t)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000)
- /* ========================= 枚举定义 ========================= */
- typedef enum {
- VL53L0X_VCSEL_PERIOD_PER_RANGE,
- VL53L0X_VCSEL_PERIOD_FINAL_RANGE,
- } vl53l0x_vcsel_period_type_enum;
- /* ========================= 结构体定义 ========================= */
- typedef struct {
- uint8_t tcc;
- uint8_t msrc;
- uint8_t dss;
- uint8_t pre_range;
- uint8_t final_range;
- } vl53l0x_sequence_enables_step_struct;
- typedef struct {
- uint16_t pre_range_vcsel_period_pclks;
- uint16_t final_range_vcsel_period_pclks;
- uint16_t msrc_dss_tcc_mclks;
- uint16_t pre_range_mclks;
- uint16_t final_range_mclks;
- uint32_t msrc_dss_tcc_us;
- uint32_t pre_range_us;
- uint32_t final_range_us;
- } vl53l0x_sequence_timeout_step_struct;
- /* 内部设备结构(所有模块共用) */
- struct vl53l0x_dev {
- dev_t devid;
- struct cdev cdev;
- struct class *class;
- struct device *device;
- void *private_data; /* 指向 i2c_client */
- struct mutex lock;
- uint16_t vl53l0x_distance_mm;
- };
- /* ========================= 全局变量声明 ========================= */
- extern struct vl53l0x_dev vl53l0x;
- /* ========================= 硬件层函数声明 ========================= */
- int vl53l0x_read_regs(struct vl53l0x_dev *dev, u8 reg, void *val, int len);
- int vl53l0x_write_regs(struct vl53l0x_dev *dev, u8 reg, u8 *buf, u8 len);
- void vl53l0x_writeByte(u8 reg, u8 Byte);
- void vl53l0x_writeBuf(u8 reg, u8 *buf, u8 len);
- void vl53l0x_readByte(u8 reg, u8 *byte);
- void vl53l0x_readBuf(u8 reg, u8 *buf, u8 len);
- /* ========================= 核心层函数声明 ========================= */
- void vl53l0x_set_signal_rate_limit(uint16_t limit_mcps);
- uint8_t vl53l0x_get_spad_info(uint8_t *index, uint8_t *type_is_aperture);
- void vl53l0x_get_sequence_step_enables(vl53l0x_sequence_enables_step_struct *enables);
- uint8_t vl53l0x_get_vcsel_pulse_period(vl53l0x_vcsel_period_type_enum type);
- uint32_t vl53l0x_timeout_mclks_to_microseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks);
- uint16_t vl53l0x_decode_timeout(uint16_t reg_val);
- void vl53l0x_get_sequence_step_timeouts(const vl53l0x_sequence_enables_step_struct *enables,
- vl53l0x_sequence_timeout_step_struct *timeouts);
- uint32_t vl53l0x_get_measurement_timing_budget(void);
- uint16_t vl53l0x_encode_timeout(uint16_t timeout_mclks);
- uint32_t vl53l0x_timeout_microseconds_to_mclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks);
- uint8_t vl53l0x_set_measurement_timing_budget(uint32_t budget_us);
- uint8_t vl53l0x_perform_single_ref_calibration(uint8_t vhv_init_byte);
- int vl53l0x_init(void);
- void vl53l0x_get_distance(void);
- /* ========================= 设备层函数声明 ========================= */
- int vl53l0x_cdev_init(struct i2c_client *client);
- void vl53l0x_cdev_destroy(void);
- #endif /* _VL53L0X_H_ */
复制代码 2.3.2 vl53l0x_core.c
vl53l0x_core.c: 核心层,实现传感器初始化、距离读取等算法,调用硬件层;
点击查看详情- #include "vl53l0x.h"
- /* I2C 读写函数(硬件层) */
- int vl53l0x_read_regs(struct vl53l0x_dev *dev, u8 reg, void *val, int len)
- {
- struct i2c_client *client = (struct i2c_client *)dev->private_data;
- struct i2c_msg msg[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = ®,
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val,
- },
- };
- return i2c_transfer(client->adapter, msg, 2);
- }
- int vl53l0x_write_regs(struct vl53l0x_dev *dev, u8 reg, u8 *buf, u8 len)
- {
- u8 b[256];
- struct i2c_msg msg;
- struct i2c_client *client = (struct i2c_client *)dev->private_data;
- b[0] = reg;
- memcpy(&b[1], buf, len);
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = len + 1;
- msg.buf = b;
- return i2c_transfer(client->adapter, &msg, 1);
- }
- /* 单字节/多字节封装 */
- void vl53l0x_writeByte(u8 reg, u8 Byte)
- {
- vl53l0x_write_regs(&vl53l0x, reg, &Byte, 1);
- }
- void vl53l0x_writeBuf(u8 reg, u8 *buf, u8 len)
- {
- vl53l0x_write_regs(&vl53l0x, reg, buf, len);
- }
- void vl53l0x_readByte(u8 reg, u8 *byte)
- {
- vl53l0x_read_regs(&vl53l0x, reg, byte, 1);
- }
- void vl53l0x_readBuf(u8 reg, u8 *buf, u8 len)
- {
- vl53l0x_read_regs(&vl53l0x, reg, buf, len);
- }
复制代码 2.4 vl53l0x_driver.c
vl53l0x_driver.c: I2C驱动层,实现probe/remove,创建设备节点;- #include "vl53l0x.h"
- /* 全局设备结构实例(所有模块共享) */
- struct vl53l0x_dev vl53l0x;
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 设置返回信号速率限制 该值单位为 MCPS (百万次每秒)
- // 参数说明 limit_mcps 设置的最小速率
- // 返回参数 void
- // 使用示例 vl53l0x_set_signal_rate_limit(32);
- // 备注信息 这个速率表示从目标反射并被设备检测到的信号的振幅
- // 设置此限制可以确定传感器报告有效读数所需的最小测量值
- // 设置一个较低的限制可以增加传感器的测量范围
- // 但似乎也增加了 <由于来自目标以外的物体的不需要的反射导致> 得到不准确读数的可能性
- // 默认为 32 MCPS 可预设范围为 0 - 65534
- //-------------------------------------------------------------------------------------------------------------------
- void vl53l0x_set_signal_rate_limit(uint16_t limit_mcps_fixed)
- {
- uint8_t data_buffer[2];
- data_buffer[0] = (limit_mcps_fixed >> 8) & 0xFF;
- data_buffer[1] = limit_mcps_fixed & 0xFF;
- vl53l0x_write_regs(&vl53l0x, 0x44, data_buffer, 2);
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 获取设备 SPAD 信息
- // 参数说明 index 索引
- // 参数说明 type 类型值
- // 返回参数 uint8_t 是否成功 0-成功 1-失败
- // 使用示例 vl53l0x_get_spad_info(index, type_is_aperture);
- // 备注信息
- //-------------------------------------------------------------------------------------------------------------------
- uint8_t vl53l0x_get_spad_info(uint8_t *index, uint8_t *type_is_aperture)
- {
- uint8_t tmp = 0;
- uint8_t return_state = 0;
- volatile uint16_t loop_count = 0;
- do {
- vl53l0x_writeByte(0x80, 0x01);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x00, 0x00);
- vl53l0x_writeByte(0xFF, 0x06);
- vl53l0x_readByte(0x83, &tmp);
- vl53l0x_writeByte(0x83, tmp | 0x04);
- vl53l0x_writeByte(0xFF, 0x07);
- vl53l0x_writeByte(0x81, 0x01);
- vl53l0x_writeByte(0x80, 0x01);
- vl53l0x_writeByte(0x94, 0x6b);
- vl53l0x_writeByte(0x83, 0x00);
- tmp = 0x00;
- while (0x00 == tmp || 0xFF == tmp) {
- msleep(1);
- vl53l0x_readByte(0x83, &tmp);
- if (VL53L0X_TIMEOUT_COUNT < loop_count++) {
- return_state = 1;
- break;
- }
- }
- if (return_state)
- break;
- vl53l0x_writeByte(0x83, 0x01);
- vl53l0x_readByte(0x92, &tmp);
- *index = tmp & 0x7f;
- *type_is_aperture = (tmp >> 7) & 0x01;
- vl53l0x_writeByte(0x81, 0x00);
- vl53l0x_writeByte(0xFF, 0x06);
- vl53l0x_readByte(0x83, &tmp);
- vl53l0x_writeByte(0x83, tmp);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x00, 0x01);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x80, 0x00);
- } while (0);
- return return_state;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 获取序列步骤使能设置
- // 参数说明 enables 序列使能步骤结构体
- // 返回参数 void
- // 使用示例 vl53l0x_get_sequence_step_enables(enables);
- // 备注信息
- //-------------------------------------------------------------------------------------------------------------------
- void vl53l0x_get_sequence_step_enables(vl53l0x_sequence_enables_step_struct *enables)
- {
- uint8_t sequence_config = 0;
- vl53l0x_readByte(0x01, &sequence_config);
- enables->tcc = (sequence_config >> 4) & 0x1;
- enables->dss = (sequence_config >> 3) & 0x1;
- enables->msrc = (sequence_config >> 2) & 0x1;
- enables->pre_range = (sequence_config >> 6) & 0x1;
- enables->final_range = (sequence_config >> 7) & 0x1;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 获取脉冲周期
- // 参数说明 type 预量程类型
- // 返回参数 uint8_t 返回的周期值
- // 使用示例 vl53l0x_get_vcsel_pulse_period(VL53L0X_VCSEL_PERIOD_PER_RANGE);
- // 备注信息 在 PCLKs 中获取给定周期类型的 VCSEL 脉冲周期
- //-------------------------------------------------------------------------------------------------------------------
- uint8_t vl53l0x_get_vcsel_pulse_period(vl53l0x_vcsel_period_type_enum type)
- {
- uint8_t data_buffer = 0;
- if (VL53L0X_VCSEL_PERIOD_PER_RANGE == type) {
- vl53l0x_readByte(0x50, &data_buffer);
- data_buffer = decode_vcsel_period(data_buffer);
- } else if (VL53L0X_VCSEL_PERIOD_FINAL_RANGE == type) {
- vl53l0x_readByte(0x70, &data_buffer);
- data_buffer = decode_vcsel_period(data_buffer);
- } else {
- data_buffer = 255;
- }
- return data_buffer;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 将超时数值从 MCLKs 转换到对应的 ms
- // 参数说明 timeout_period_mclks 超时周期 MCLKs
- // 参数说明 vcsel_period_pclks PCLK 值
- // 返回参数 uint32_t 返回超时数值
- // 使用示例 vl53l0x_timeout_mclks_to_microseconds(timeout_period_mclks, vcsel_period_pclks);
- // 备注信息 将序列步骤超时从具有给定 VCSEL 周期的 MCLK (以 PCLK 为单位)转换为微秒
- //-------------------------------------------------------------------------------------------------------------------
- uint32_t vl53l0x_timeout_mclks_to_microseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks)
- {
- uint32_t macro_period_ns = calc_macro_period(vcsel_period_pclks);
- return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 对超时数值进行解码
- // 参数说明 reg_val 超时时长 寄存器值
- // 返回参数 uint16_t 返回超时数值
- // 使用示例 vl53l0x_decode_timeout(reg_val);
- // 备注信息 从寄存器值解码 MCLK 中的序列步骤超时
- //-------------------------------------------------------------------------------------------------------------------
- uint16_t vl53l0x_decode_timeout(uint16_t reg_val)
- {
- return (uint16_t)((reg_val & 0x00FF) << ((reg_val & 0xFF00) >> 8)) + 1;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 获取序列步骤超时设置
- // 参数说明 enables 序列使能步骤结构体
- // 参数说明 timeouts 序列超时步骤结构体
- // 返回参数 void
- // 使用示例 vl53l0x_get_sequence_step_timeouts(enables, timeouts);
- // 备注信息 获取所有超时而不仅仅是请求的超时 并且还存储中间值
- //-------------------------------------------------------------------------------------------------------------------
- void vl53l0x_get_sequence_step_timeouts(const vl53l0x_sequence_enables_step_struct *enables,
- vl53l0x_sequence_timeout_step_struct *timeouts)
- {
- uint8_t reg_buffer[2];
- uint16_t reg16_buffer = 0;
- timeouts->pre_range_vcsel_period_pclks = vl53l0x_get_vcsel_pulse_period(VL53L0X_VCSEL_PERIOD_PER_RANGE);
- vl53l0x_readByte(0x46, reg_buffer);
- timeouts->msrc_dss_tcc_mclks = reg_buffer[0] + 1;
- timeouts->msrc_dss_tcc_us = vl53l0x_timeout_mclks_to_microseconds(timeouts->msrc_dss_tcc_mclks,
- (uint8_t)timeouts->pre_range_vcsel_period_pclks);
- vl53l0x_readBuf(0x51, reg_buffer, 2);
- reg16_buffer = ((uint16_t)reg_buffer[0] << 8) | reg_buffer[1];
- timeouts->pre_range_mclks = vl53l0x_decode_timeout(reg16_buffer);
- timeouts->pre_range_us = vl53l0x_timeout_mclks_to_microseconds(timeouts->pre_range_mclks,
- (uint8_t)timeouts->pre_range_vcsel_period_pclks);
- timeouts->final_range_vcsel_period_pclks = vl53l0x_get_vcsel_pulse_period(VL53L0X_VCSEL_PERIOD_FINAL_RANGE);
- vl53l0x_readBuf(0x71, reg_buffer, 2);
- reg16_buffer = ((uint16_t)reg_buffer[0] << 8) | reg_buffer[1];
- timeouts->final_range_mclks = vl53l0x_decode_timeout(reg16_buffer);
- if (enables->pre_range) {
- timeouts->final_range_mclks -= timeouts->pre_range_mclks;
- }
- timeouts->final_range_us = vl53l0x_timeout_mclks_to_microseconds(timeouts->final_range_mclks,
- (uint8_t)timeouts->final_range_vcsel_period_pclks);
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 获取测量定时预算 (以微秒为单位)
- // 参数说明 void
- // 返回参数 uint32_t 已设定的测量允许的时间
- // 使用示例 vl53l0x_get_measurement_timing_budget();
- // 备注信息
- //-------------------------------------------------------------------------------------------------------------------
- uint32_t vl53l0x_get_measurement_timing_budget(void)
- {
- vl53l0x_sequence_enables_step_struct enables;
- vl53l0x_sequence_timeout_step_struct timeouts;
- uint32_t budget_us = 1910 + 960;
- vl53l0x_get_sequence_step_enables(&enables);
- vl53l0x_get_sequence_step_timeouts(&enables, &timeouts);
- if (enables.tcc)
- budget_us += (timeouts.msrc_dss_tcc_us + 590);
- if (enables.dss)
- budget_us += 2 * (timeouts.msrc_dss_tcc_us + 690);
- else if (enables.msrc)
- budget_us += (timeouts.msrc_dss_tcc_us + 660);
- if (enables.pre_range)
- budget_us += (timeouts.pre_range_us + 660);
- if (enables.final_range)
- budget_us += (timeouts.final_range_us + 550);
- return budget_us;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 对超时数值进行编码
- // 参数说明 timeout_mclks 超时时长 -MCLKs 值
- // 返回参数 uint16_t 返回编码值
- // 使用示例 vl53l0x_encode_timeout(timeout_mclks);
- // 备注信息 在 MCLK 中对超时的序列步骤超时寄存器值进行编码
- //-------------------------------------------------------------------------------------------------------------------
- uint16_t vl53l0x_encode_timeout (uint16_t timeout_mclks)
- {
- uint32_t ls_byte = 0;
- uint16_t ms_byte = 0;
- uint16_t return_data = 0;
- if(0 < timeout_mclks)
- {
- // 格式: (LSByte * 2 ^ MSByte) + 1
- ls_byte = timeout_mclks - 1;
- while(0 < (ls_byte & 0xFFFFFF00))
- {
- ls_byte >>= 1;
- ms_byte++;
- }
- return_data = (ms_byte << 8) | ((uint16_t)ls_byte & 0xFF);
- }
- return return_data;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 将超时数值从 ms 转换到对应的 MCLKs
- // 参数说明 timeout_period_us 超时周期 微秒单位
- // 参数说明 vcsel_period_pclks PCLK 值
- // 返回参数 uint32_t 返回超时数值
- // 使用示例 vl53l0x_timeout_microseconds_to_mclks(timeout_period_us, vcsel_period_pclks);
- // 备注信息 将序列步骤超时从微秒转换为具有给定 VCSEL 周期的 MCLK (以 PCLK 为单位)
- //-------------------------------------------------------------------------------------------------------------------
- uint32_t vl53l0x_timeout_microseconds_to_mclks (uint32_t timeout_period_us, uint8_t vcsel_period_pclks)
- {
- uint32_t macro_period_ns = calc_macro_period(vcsel_period_pclks);
- return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns);
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 设置测量定时预算 (以微秒为单位)
- // 参数说明 budget_us 设定的测量允许的时间
- // 返回参数 uint8_t 操作结果 0-成功 1-失败
- // 使用示例 vl53l0x_set_measurement_timing_budget(measurement_timing_budget_us);
- // 备注信息 这是一次测量允许的时间
- // 即在测距序列的子步骤之间分配时间预算
- // 更长的时间预算允许更精确的测量
- // 增加一个N倍的预算可以减少一个sqrt(N)倍的范围测量标准偏差
- // 默认为33毫秒 最小值为20 ms
- //-------------------------------------------------------------------------------------------------------------------
- uint8_t vl53l0x_set_measurement_timing_budget (uint32_t budget_us)
- {
- uint8_t return_state = 0;
- uint8_t data_buffer[3];
- uint16_t data = 0;
- vl53l0x_sequence_enables_step_struct enables;
- vl53l0x_sequence_timeout_step_struct timeouts;
- do
- {
- if(20000 > budget_us)
- {
- return_state = 1;
- break;
- }
- uint32_t used_budget_us = 1320 + 960;
- vl53l0x_get_sequence_step_enables(&enables);
- vl53l0x_get_sequence_step_timeouts(&enables, &timeouts);
- if (enables.tcc)
- {
- used_budget_us += (timeouts.msrc_dss_tcc_us + 590);
- }
- if (enables.dss)
- {
- used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + 690);
- }
- else if (enables.msrc)
- {
- used_budget_us += (timeouts.msrc_dss_tcc_us + 660);
- }
- if (enables.pre_range)
- {
- used_budget_us += (timeouts.pre_range_us + 660);
- }
- if (enables.final_range)
- {
- // 请注意 最终范围超时由计时预算和序列中所有其他超时的总和决定
- // 如果没有空间用于最终范围超时 则将设置错误
- // 否则 剩余时间将应用于最终范围
- used_budget_us += 550;
- if (used_budget_us > budget_us)
- {
- // 请求的超时太大
- return_state = 1;
- break;
- }
- // 对于最终超时范围 必须添加预量程范围超时
- // 为此 最终超时和预量程超时必须以宏周期 MClks 表示
- // 因为它们具有不同的 VCSEL 周期
- uint32_t final_range_timeout_us = budget_us - used_budget_us;
- uint16_t final_range_timeout_mclks =
- (uint16_t)vl53l0x_timeout_microseconds_to_mclks(final_range_timeout_us,
- (uint8_t)timeouts.final_range_vcsel_period_pclks);
- if (enables.pre_range)
- {
- final_range_timeout_mclks += timeouts.pre_range_mclks;
- }
- data = vl53l0x_encode_timeout(final_range_timeout_mclks);
- data_buffer[0] = 0x71;
- data_buffer[1] = ((data >> 8) & 0xFF);
- data_buffer[2] = (data & 0xFF);
- vl53l0x_writeBuf(data_buffer[0], &data_buffer[1], 2);
- }
- }while(0);
- return return_state;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 执行单次参考校准
- // 参数说明 vhv_init_byte 预设校准值
- // 返回参数 uint8_t 操作是否成功 0-成功 1-失败
- // 使用示例 vl53l0x_get_vcsel_pulse_period(VL53L0X_VCSEL_PERIOD_PER_RANGE);
- // 备注信息 在 PCLKs 中获取给定周期类型的 VCSEL 脉冲周期
- //-------------------------------------------------------------------------------------------------------------------
- uint8_t vl53l0x_perform_single_ref_calibration (uint8_t vhv_init_byte)
- {
- uint8_t return_state = 0;
- uint8_t data_buffer = 0;
- volatile uint16_t loop_count = 0;
- do
- {
- vl53l0x_writeByte(0x00, 0x01 | vhv_init_byte);
- vl53l0x_readByte(0x46, &data_buffer);
- while(0 == (data_buffer & 0x07))
- {
- msleep(1);
- vl53l0x_readByte(0x46, &data_buffer);
- if(VL53L0X_TIMEOUT_COUNT < loop_count ++)
- {
- return_state = 1;
- break;
- }
- }
- if(return_state)
- {
- break;
- }
- vl53l0x_writeByte(0x0B, 0x01);
- vl53l0x_writeByte(0x00, 0x00);
- }while(0);
- return return_state;
- }
- int vl53l0x_init(void)
- {
- uint8_t stop_variable = 0;
- uint8_t reg_data_buffer = 0;
- uint8_t return_state = 0;
- uint32_t measurement_timing_budget_us;
- uint8_t data_buffer[7] = {0};
- uint8_t ref_spad_map[6];
- int i = 0;
- do
- {
- vl53l0x_readByte(0x89, ®_data_buffer);
- vl53l0x_writeByte(0x89, reg_data_buffer | 0x01);
- vl53l0x_writeByte(0x88, 0x00);
- vl53l0x_writeByte(0x80, 0x01);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x00, 0x00);
- vl53l0x_readByte(0x91, &stop_variable);
- vl53l0x_writeByte(0x00, 0x01);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x80, 0x00);
- vl53l0x_readByte(0x60, ®_data_buffer);
- vl53l0x_writeByte(0x60, reg_data_buffer | 0x12);
- vl53l0x_set_signal_rate_limit(32);
- vl53l0x_writeByte(0x01, 0xFF);
- if(vl53l0x_get_spad_info(&data_buffer[0], &data_buffer[1]))
- {
- return_state = 1;
- printk("init error\r\n");
- break;
- }
- vl53l0x_read_regs(&vl53l0x, 0xB0, ref_spad_map, 6);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x4F, 0x00);
- vl53l0x_writeByte(0x4E, 0x2C);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x86, 0xB4);
- data_buffer[2] = data_buffer[1] ? 12 : 0;
- for(i = 0; 48 > i; i ++)
- {
- if(i < data_buffer[2] || data_buffer[3] == data_buffer[0])
- {
- // 此位低于应启用的第一个位
- // 或者 (eference_spad_count) 位已启用
- // 因此此位为零
- ref_spad_map[i / 8] &= ~(1 << (i % 8));
- }
- else if((ref_spad_map[i / 8] >> (i % 8)) & 0x1)
- {
- data_buffer[3] ++;
- }
- }
- data_buffer[0] = 0xB0;
- for(i = 1; 7 > i; i ++)
- {
- data_buffer[1] = ref_spad_map[i - 1];
- }
- vl53l0x_write_regs(&vl53l0x, 0xB0, &data_buffer[1], 6);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x00, 0x00);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x09, 0x00);
- vl53l0x_writeByte(0x10, 0x00);
- vl53l0x_writeByte(0x11, 0x00);
- vl53l0x_writeByte(0x24, 0x01);
- vl53l0x_writeByte(0x25, 0xFF);
- vl53l0x_writeByte(0x75, 0x00);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x4E, 0x2C);
- vl53l0x_writeByte(0x48, 0x00);
- vl53l0x_writeByte(0x30, 0x20);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x30, 0x09);
- vl53l0x_writeByte(0x54, 0x00);
- vl53l0x_writeByte(0x31, 0x04);
- vl53l0x_writeByte(0x32, 0x03);
- vl53l0x_writeByte(0x40, 0x83);
- vl53l0x_writeByte(0x46, 0x25);
- vl53l0x_writeByte(0x60, 0x00);
- vl53l0x_writeByte(0x27, 0x00);
- vl53l0x_writeByte(0x50, 0x06);
- vl53l0x_writeByte(0x51, 0x00);
- vl53l0x_writeByte(0x52, 0x96);
- vl53l0x_writeByte(0x56, 0x08);
- vl53l0x_writeByte(0x57, 0x30);
- vl53l0x_writeByte(0x61, 0x00);
- vl53l0x_writeByte(0x62, 0x00);
- vl53l0x_writeByte(0x64, 0x00);
- vl53l0x_writeByte(0x65, 0x00);
- vl53l0x_writeByte(0x66, 0xA0);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x22, 0x32);
- vl53l0x_writeByte(0x47, 0x14);
- vl53l0x_writeByte(0x49, 0xFF);
- vl53l0x_writeByte(0x4A, 0x00);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x7A, 0x0A);
- vl53l0x_writeByte(0x7B, 0x00);
- vl53l0x_writeByte(0x78, 0x21);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x23, 0x34);
- vl53l0x_writeByte(0x42, 0x00);
- vl53l0x_writeByte(0x44, 0xFF);
- vl53l0x_writeByte(0x45, 0x26);
- vl53l0x_writeByte(0x46, 0x05);
- vl53l0x_writeByte(0x40, 0x40);
- vl53l0x_writeByte(0x0E, 0x06);
- vl53l0x_writeByte(0x20, 0x1A);
- vl53l0x_writeByte(0x43, 0x40);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x34, 0x03);
- vl53l0x_writeByte(0x35, 0x44);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x31, 0x04);
- vl53l0x_writeByte(0x4B, 0x09);
- vl53l0x_writeByte(0x4C, 0x05);
- vl53l0x_writeByte(0x4D, 0x04);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x44, 0x00);
- vl53l0x_writeByte(0x45, 0x20);
- vl53l0x_writeByte(0x47, 0x08);
- vl53l0x_writeByte(0x48, 0x28);
- vl53l0x_writeByte(0x67, 0x00);
- vl53l0x_writeByte(0x70, 0x04);
- vl53l0x_writeByte(0x71, 0x01);
- vl53l0x_writeByte(0x72, 0xFE);
- vl53l0x_writeByte(0x76, 0x00);
- vl53l0x_writeByte(0x77, 0x00);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x0D, 0x01);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x80, 0x01);
- vl53l0x_writeByte(0x01, 0xF8);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x8E, 0x01);
- vl53l0x_writeByte(0x00, 0x01);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x80, 0x00);
- vl53l0x_writeByte(0x0A, 0x04);
- vl53l0x_readByte(0x84, ®_data_buffer);
- vl53l0x_writeByte(0x84, reg_data_buffer & ~0x10);
- vl53l0x_writeByte(0x0B, 0x01);
- measurement_timing_budget_us = vl53l0x_get_measurement_timing_budget();
- vl53l0x_writeByte(0x01, 0xE8);
- vl53l0x_set_measurement_timing_budget(measurement_timing_budget_us); // 重新计算时序预算
- vl53l0x_writeByte(0x01, 0x01);
- if(vl53l0x_perform_single_ref_calibration(0x40))
- {
- return_state = 1;
- break;
- }
- vl53l0x_writeByte(0x01, 0x02);
- if(vl53l0x_perform_single_ref_calibration(0x00))
- {
- return_state = 1;
- break;
- }
- vl53l0x_writeByte(0x01, 0xE8);
- msleep(100);
- vl53l0x_writeByte(0x80, 0x01);
- vl53l0x_writeByte(0xFF, 0x01);
- vl53l0x_writeByte(0x00, 0x00);
- vl53l0x_writeByte(0x91, stop_variable);
- vl53l0x_writeByte(0x00, 0x01);
- vl53l0x_writeByte(0xFF, 0x00);
- vl53l0x_writeByte(0x80, 0x00);
- vl53l0x_writeByte(0x00, 0x02);
- }while(0);
- return return_state;
- }
- //-------------------------------------------------------------------------------------------------------------------
- // 函数简介 返回以毫米为单位的范围读数
- // 参数说明 void
- // 返回参数 void
- // 使用示例 vl53l0x_get_distance();
- // 备注信息 在开始单次射程测量后也调用此函数
- //-------------------------------------------------------------------------------------------------------------------
- void vl53l0x_get_distance (void)
- {
- uint8_t reg_databuffer[3];
- vl53l0x_readByte(0x13, reg_databuffer);
- if((reg_databuffer[0] & 0x07) != 0)
- {
- // 假设线性度校正增益为默认值 1000 且未启用分数范围
- vl53l0x_readBuf(0x14 + 10, reg_databuffer, 2);
- vl53l0x.vl53l0x_distance_mm = ((uint16_t)reg_databuffer[0] << 8);
- vl53l0x.vl53l0x_distance_mm |= reg_databuffer[1];
- vl53l0x_writeByte(0x0B, 0x01);
- }
- if(reg_databuffer[0] & 0x10)
- {
- vl53l0x_readBuf(0x14 + 10, reg_databuffer, 2);
- vl53l0x_writeByte(0x0B, 0x01);
- }
- }
复制代码 通过 of_match_table 与设备树中 compatible = "icar,vl53l0x" 的节点匹配,实现驱动与硬件自动绑定。
2.5 Makefile
创建Makefile文件:
[code]# 内核源码路径KERNELDIR ?= /opt/2k0300/build-2k0300/workspace/linux-6.12# 当前驱动目录PWD := $(shell pwd)# 交叉编译工具链CROSS_COMPILE := loongarch64-linux-gnu-# 架构ARCH := loongarch# 所需文件夹BUILD_DIR := buildINCLUDE:=incKO_DIR:=koSRC_DIR:=srcEXTRA_CFLAGS += -I$(src)/$(INCLUDE)# 编译目标obj-m := vl53l0x.ovl53l0x-y := vl53l0x_driver.o \ src/vl53l0x_hw.o \ src/vl53l0x_core.o \ src/vl53l0x_dev.o# 编译规则ll: prepare compile move_files# 提前创建目录prepare: @mkdir -p $(BUILD_DIR) $(KO_DIR) @echo "
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |