PX4二次开发快速入门(三):自定义串口驱动

news2024/11/19 10:47:18

文章目录

  • 前言

前言

软件:PX4 1.14.0稳定版
硬件:纳雷NRA12,pixhawk4
仿照原生固件tfmini的驱动进行编写
源码地址:
https://gitee.com/Mbot_admin/px4-1.14.0-csdn

修改
src/drivers/distance_sensor/CMakeLists.txt
添加

add_subdirectory(nra12)

修改
src/drivers/distance_sensor/Kconfig
添加

select DRIVERS_DISTANCE_SENSOR_NRA12

在src/drivers/distance_sensor/目录下添加一个nra12文件夹
在nra12文件夹下添加Kconfig,NRA12.cpp,NRA12.hpp,nra12_main.cpp,module.yaml,nra12_parser.cpp,nra12_parser.h。

Kconfig如下:

menuconfig DRIVERS_DISTANCE_SENSOR_NRA12
	bool "nra12"
	default n
	---help---
		Enable support for nra12

NRA12.cpp如下:

#include "NRA12.hpp"

#include <lib/drivers/device/Device.hpp>
#include <fcntl.h>

NRA12::NRA12(const char *port, uint8_t rotation) :
	ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(port)),
	_px4_rangefinder(0, rotation)
{
	// store port name
	strncpy(_port, port, sizeof(_port) - 1);

	// enforce null termination
	_port[sizeof(_port) - 1] = '\0';

	device::Device::DeviceId device_id;
	device_id.devid_s.devtype = DRV_DIST_DEVTYPE_NRA12;
	device_id.devid_s.bus_type = device::Device::DeviceBusType_SERIAL;

	uint8_t bus_num = atoi(&_port[strlen(_port) - 1]); // Assuming '/dev/ttySx'

	if (bus_num < 10) {
		device_id.devid_s.bus = bus_num;
	}

	_px4_rangefinder.set_device_id(device_id.devid);
	_px4_rangefinder.set_rangefinder_type(distance_sensor_s::MAV_DISTANCE_SENSOR_LASER);
}

NRA12::~NRA12()
{
	// make sure we are truly inactive
	stop();

	perf_free(_sample_perf);
	perf_free(_comms_errors);
}

int
NRA12::init()
{
	int32_t hw_model = 1; // only one model so far...

	switch (hw_model) {
	case 1: // NRA12 (12m, 100 Hz)
		// Note:
		// Sensor specification shows 0.3m as minimum, but in practice
		// 0.3 is too close to minimum so chattering of invalid sensor decision
		// is happening sometimes. this cause EKF to believe inconsistent range readings.
		// So we set 0.4 as valid minimum.
		_px4_rangefinder.set_min_distance(0.1f);
		_px4_rangefinder.set_max_distance(30.0f);
		_px4_rangefinder.set_fov(math::radians(1.15f));

		break;

	default:
		PX4_ERR("invalid HW model %" PRId32 ".", hw_model);
		return -1;
	}

	// status
	int ret = 0;

	do { // create a scope to handle exit conditions using break

		// open fd
		_fd = ::open(_port, O_RDWR | O_NOCTTY);

		if (_fd < 0) {
			PX4_ERR("Error opening fd");
			return -1;
		}

		// baudrate 115200, 8 bits, no parity, 1 stop bit
		unsigned speed = B115200;
		termios uart_config{};
		int termios_state{};

		tcgetattr(_fd, &uart_config);

		// clear ONLCR flag (which appends a CR for every LF)
		uart_config.c_oflag &= ~ONLCR;

		// set baud rate
		if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) {
			PX4_ERR("CFG: %d ISPD", termios_state);
			ret = -1;
			break;
		}

		if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
			PX4_ERR("CFG: %d OSPD\n", termios_state);
			ret = -1;
			break;
		}

		if ((termios_state = tcsetattr(_fd, TCSANOW, &uart_config)) < 0) {
			PX4_ERR("baud %d ATTR", termios_state);
			ret = -1;
			break;
		}

		uart_config.c_cflag |= (CLOCAL | CREAD);	// ignore modem controls
		uart_config.c_cflag &= ~CSIZE;
		uart_config.c_cflag |= CS8;			// 8-bit characters
		uart_config.c_cflag &= ~PARENB;			// no parity bit
		uart_config.c_cflag &= ~CSTOPB;			// only need 1 stop bit
		uart_config.c_cflag &= ~CRTSCTS;		// no hardware flowcontrol

		// setup for non-canonical mode
		uart_config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
		uart_config.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
		uart_config.c_oflag &= ~OPOST;

		// fetch bytes as they become available
		uart_config.c_cc[VMIN] = 1;
		uart_config.c_cc[VTIME] = 1;

		if (_fd < 0) {
			PX4_ERR("FAIL: laser fd");
			ret = -1;
			break;
		}
	} while (0);

	// close the fd
	::close(_fd);
	_fd = -1;

	if (ret == PX4_OK) {
		start();
	}

	return ret;
}

int
NRA12::collect()
{
	perf_begin(_sample_perf);

	// clear buffer if last read was too long ago
	int64_t read_elapsed = hrt_elapsed_time(&_last_read);

	// the buffer for read chars is buflen minus null termination
	char readbuf[sizeof(_linebuf)] {};
	unsigned readlen = sizeof(readbuf) - 1;

	int ret = 0;
	float distance_m = -1.0f;

	// Check the number of bytes available in the buffer
	int bytes_available = 0;
	::ioctl(_fd, FIONREAD, (unsigned long)&bytes_available);
	if (!bytes_available) {
		perf_end(_sample_perf);
		return 0;
	}

	// parse entire buffer
	const hrt_abstime timestamp_sample = hrt_absolute_time();

	do {
		// read from the sensor (uart buffer)
		ret = ::read(_fd, &readbuf[0], readlen);
// ret = ::write(_fd,&readbuf,10);
// if (ret < 0) {
//     PX4_ERR("write err: %d", ret);
// }
		if (ret < 0) {
			PX4_ERR("read err: %d", ret);
			perf_count(_comms_errors);
			perf_end(_sample_perf);

			// only throw an error if we time out
			if (read_elapsed > (kCONVERSIONINTERVAL * 2)) {
				/* flush anything in RX buffer */
				tcflush(_fd, TCIFLUSH);
				return ret;

			} else {
				return -EAGAIN;
			}
		}

		_last_read = hrt_absolute_time();

		// parse buffer
		for (int i = 0; i < ret; i++) {
			// PX4_INFO("readbuf=%x\n",readbuf[i]);
			// PX4_INFO("ret=%d\n",ret);
			// PX4_INFO("bytes_available=%d\n",bytes_available);
			nra12_parse(readbuf[i], _linebuf, &_linebuf_index, &_parse_state, &distance_m);
		}

		// bytes left to parse
		bytes_available -= ret;

	} while (bytes_available > 0);

	// no valid measurement after parsing buffer
	if (distance_m < 0.0f)
	{
		perf_end(_sample_perf);
		return -EAGAIN;
	}

	// publish most recent valid measurement from buffer
	_px4_rangefinder.update(timestamp_sample, distance_m);

	perf_end(_sample_perf);

	return PX4_OK;
}

void
NRA12::start()
{
	// schedule a cycle to start things (the sensor sends at 100Hz, but we run a bit faster to avoid missing data)
	ScheduleOnInterval(7_ms);
}

void
NRA12::stop()
{
	ScheduleClear();
}

void
NRA12::Run()
{
	// fds initialized?
	if (_fd < 0) {
		// open fd
		_fd = ::open(_port, O_RDWR | O_NOCTTY);
	}

	// perform collection
	if (collect() == -EAGAIN) {
		// reschedule to grab the missing bits, time to transmit 9 bytes @ 115200 bps
		ScheduleClear();
		ScheduleOnInterval(7_ms, 87 * 9);
		return;
	}
}

void
NRA12::print_info()
{
	printf("Using port '%s'\n", _port);
	perf_print_counter(_sample_perf);
	perf_print_counter(_comms_errors);
}

NRA12.hpp如下:

#pragma once

#include <termios.h>

#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <lib/drivers/rangefinder/PX4Rangefinder.hpp>
#include <uORB/topics/distance_sensor.h>

#include "nra12_parser.h"

#define NRA12_DEFAULT_PORT	"/dev/ttyS3"

using namespace time_literals;

class NRA12 : public px4::ScheduledWorkItem
{
public:
	NRA12(const char *port, uint8_t rotation = distance_sensor_s::ROTATION_DOWNWARD_FACING);
	virtual ~NRA12();

	int init();

	void print_info();

private:

	int collect();

	void Run() override;

	void start();
	void stop();

	PX4Rangefinder	_px4_rangefinder;

	NRA12_PARSE_STATE _parse_state {NRA12_PARSE_STATE::STATE0_UNSYNC};

	char _linebuf[24] {};
	char _port[20] {};

	static constexpr int kCONVERSIONINTERVAL{9_ms};

	int _fd{-1};

	unsigned int _linebuf_index{0};

	hrt_abstime _last_read{0};

	perf_counter_t _comms_errors{perf_alloc(PC_COUNT, MODULE_NAME": com_err")};
	perf_counter_t _sample_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": read")};

};

module.yaml 如下:

module_name: nanoradar nra12 lidar
serial_config:
    - command: nra12 start -d ${SERIAL_DEV}
      port_config_param:
        name: SENS_NRA12_CFG
        group: Sensors

nra12_main.cpp如下:

#include "NRA12.hpp"

#include <px4_platform_common/getopt.h>

/**
 * Local functions in support of the shell command.
 */
namespace nra12
{

NRA12	*g_dev{nullptr};

int start(const char *port, uint8_t rotation);
int status();
int stop();
int usage();

int
start(const char *port, uint8_t rotation)
{
	if (g_dev != nullptr) {
		PX4_ERR("already started");
		return PX4_OK;
	}

	// Instantiate the driver.
	g_dev = new NRA12(port, rotation);

	if (g_dev == nullptr) {
		PX4_ERR("driver start failed");
		return PX4_ERROR;
	}

	if (OK != g_dev->init()) {
		PX4_ERR("driver start failed");
		delete g_dev;
		g_dev = nullptr;
		return PX4_ERROR;
	}

	return PX4_OK;
}

int
status()
{
	if (g_dev == nullptr) {
		PX4_ERR("driver not running");
		return 1;
	}

	printf("state @ %p\n", g_dev);
	g_dev->print_info();

	return 0;
}

int stop()
{
	if (g_dev != nullptr) {
		PX4_INFO("stopping driver");
		delete g_dev;
		g_dev = nullptr;
		PX4_INFO("driver stopped");

	} else {
		PX4_ERR("driver not running");
		return 1;
	}

	return PX4_OK;
}

int
usage()
{
	PRINT_MODULE_DESCRIPTION(
		R"DESCR_STR(
### Description

Serial bus driver for the Benewake NRA12 LiDAR.

Most boards are configured to enable/start the driver on a specified UART using the SENS_NRA12_CFG parameter.

Setup/usage information: https://docs.px4.io/master/en/sensor/sonar.html

### Examples

Attempt to start driver on a specified serial device.
$ sonar start -d /dev/ttyS1
Stop driver
$ sonar stop
)DESCR_STR");

	PRINT_MODULE_USAGE_NAME("nra12", "driver");
	PRINT_MODULE_USAGE_SUBCATEGORY("distance_sensor");
	PRINT_MODULE_USAGE_COMMAND_DESCR("start","Start driver");
	PRINT_MODULE_USAGE_PARAM_STRING('d', nullptr, nullptr, "Serial device", false);
	PRINT_MODULE_USAGE_PARAM_INT('R', 25, 0, 25, "Sensor rotation - downward facing by default", true);
	PRINT_MODULE_USAGE_COMMAND_DESCR("status","Driver status");
	PRINT_MODULE_USAGE_COMMAND_DESCR("stop","Stop driver");
	PRINT_MODULE_USAGE_COMMAND_DESCR("test","Test driver (basic functional tests)");
	PRINT_MODULE_USAGE_COMMAND_DESCR("status","Print driver status");
	return PX4_OK;
}

} // namespace

extern "C" __EXPORT int nra12_main(int argc, char *argv[])
{
	int ch = 0;
    uint8_t rotation = distance_sensor_s::ROTATION_DOWNWARD_FACING;
	const char *device_path = NRA12_DEFAULT_PORT;
	int myoptind = 1;
	const char *myoptarg = nullptr;

	while ((ch = px4_getopt(argc, argv, "R:d:", &myoptind, &myoptarg)) != EOF) {
		switch (ch) {
		case 'R':
			rotation = (uint8_t)atoi(myoptarg);
			break;

		case 'd':
			device_path = myoptarg;
			break;

		default:
			PX4_WARN("Unknown option!");
			return PX4_ERROR;
		}
	}

	if (myoptind >= argc) {
		PX4_ERR("unrecognized command");
		return nra12::usage();
	}

	if (!strcmp(argv[myoptind], "start")) {
		if (strcmp(device_path, "") != 0) {
			return nra12::start(device_path, rotation);

		} else {
			PX4_WARN("Please specify device path!");
			return nra12::usage();
		}

	} else if (!strcmp(argv[myoptind], "stop")) {
		return nra12::stop();

	} else if (!strcmp(argv[myoptind], "status")) {
		return nra12::status();
	}

	return nra12::usage();
}

nra12_parser.cpp如下:

#include "nra12_parser.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>

#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/module.h>
 //#define NRA12_DEBUG

#ifdef NRA12_DEBUG
#include <stdio.h>

const char *parser_state[] = {
	"0_UNSYNC",
	"3_GOT_DIST_L",
	"4_GOT_DIST_H",
	"8_GOT_QUALITY"
	"9_GOT_CHECKSUM"
};
#endif

int nra12_parse(char c, char *parserbuf, unsigned *parserbuf_index, NRA12_PARSE_STATE *state, float *dist)
{
	int ret = -1;
	//char *end;
// PX4_INFO("parse");
	switch (*state) {
	case NRA12_PARSE_STATE::STATE12_GOT_END2:
	// PX4_INFO("STATE12_GOT_END2");
	// PX4_INFO("c=%x",c);
		if (c == 0xaa) {
			*state = NRA12_PARSE_STATE::STATE1_GOT_START1;
		} else {
			*state = NRA12_PARSE_STATE::STATE0_UNSYNC;
		}
		break;

	case NRA12_PARSE_STATE::STATE0_UNSYNC:
	// PX4_INFO("STATE0_UNSYNC");
	// PX4_INFO("c=%x",c);
		if (c == 0xaa) {
			*state = NRA12_PARSE_STATE::STATE1_GOT_START1;
		}
		break;

	case NRA12_PARSE_STATE::STATE1_GOT_START1:
	// PX4_INFO("STATE1_GOT_START1");
	// PX4_INFO("c=%x",c);
		if (c == 0xaa) {
			*state = NRA12_PARSE_STATE::STATE2_GOT_START2;

		}
		else
		{
			*state = NRA12_PARSE_STATE::STATE0_UNSYNC;
		}

		break;
	case NRA12_PARSE_STATE::STATE2_GOT_START2:
	// PX4_INFO("STATE2_GOT_START2");
	// PX4_INFO("c=%x",c);
	if(c == 0x0c){
		*state = NRA12_PARSE_STATE::STATE2_GOT_0C;
	}
	else{
	        *state = NRA12_PARSE_STATE::STATE0_UNSYNC;
	}
		break;
	case NRA12_PARSE_STATE::STATE2_GOT_0C:
	// PX4_INFO("STATE2_GOT_0C");
	// PX4_INFO("c=%x",c);
	if(c == 0x07){
		*state = NRA12_PARSE_STATE::STATE2_GOT_07;
	}
	else{
	        *state = NRA12_PARSE_STATE::STATE0_UNSYNC;
	}
		break;
	case NRA12_PARSE_STATE::STATE2_GOT_07:
	// PX4_INFO("STATE2_GOT_07");
	// PX4_INFO("c=%x",c);
	        *state = NRA12_PARSE_STATE::STATE3_GOT_Index;
		break;
	case NRA12_PARSE_STATE::STATE3_GOT_Index:
	// PX4_INFO("STATE3_GOT_Index");
	// PX4_INFO("c=%x",c);
	        *state = NRA12_PARSE_STATE::STATE4_GOT_Res;
		break;
	case NRA12_PARSE_STATE::STATE4_GOT_Res:
	// PX4_INFO("STATE4_GOT_Res");
	// PX4_INFO("c=%x",c);
		parserbuf[1]=c;
	        *state = NRA12_PARSE_STATE::STATE5_GOT_DIST_H;
		break;

	case NRA12_PARSE_STATE::STATE5_GOT_DIST_H:
	// PX4_INFO("STATE5_GOT_DIST_H");
	// PX4_INFO("c=%x",c);
		*state = NRA12_PARSE_STATE::STATE6_GOT_DIST_L;
		parserbuf[2]=c;
		break;

	case NRA12_PARSE_STATE::STATE6_GOT_DIST_L:
	// PX4_INFO("STATE6_GOT_DIST_L");
	// PX4_INFO("c=%x",c);
		*state = NRA12_PARSE_STATE::STATE7_GOT_UNUSE1;
		break;

	case NRA12_PARSE_STATE::STATE7_GOT_UNUSE1:
	// PX4_INFO("STATE7_GOT_UNUSE1");
	// PX4_INFO("c=%x",c);
		*state = NRA12_PARSE_STATE::STATE8_GOT_UNUSE2;
		break;

	case NRA12_PARSE_STATE::STATE8_GOT_UNUSE2:
	//  PX4_INFO("STATE8_GOT_UNUSE2");
	// PX4_INFO("c=%x",c);
		*state = NRA12_PARSE_STATE::STATE9_GOT_UNUSE3;
		break;

	case NRA12_PARSE_STATE::STATE9_GOT_UNUSE3:
	// PX4_INFO("STATE9_GOT_UNUSE3");
	// PX4_INFO("c=%x",c);
		*state = NRA12_PARSE_STATE::STATE10_GOT_UNUSE4;
		break;

	case NRA12_PARSE_STATE::STATE10_GOT_UNUSE4:
	//  PX4_INFO("STATE10_GOT_UNUSE4");
	// PX4_INFO("c=%x",c);
		if(c==0x55)
		{
		*state = NRA12_PARSE_STATE::STATE11_GOT_END1;
		}
		break;
	case NRA12_PARSE_STATE::STATE11_GOT_END1:
	//  PX4_INFO("STATE11_GOT_END1");
	// PX4_INFO("c=%x",c);
		if(c==0x55)
		{
		*state = NRA12_PARSE_STATE::STATE12_GOT_END2;
		unsigned int t1 = parserbuf[2];
			unsigned int t2 = parserbuf[1];
			t2 <<= 8;
			t2 += t1;

			if (t2 < 0xFFFFu) {
				*dist = ((float)t2) / 100;
			}
		//	 PX4_INFO("dist=%lf\n",(double)*dist);
		}
	        else {
			*state = NRA12_PARSE_STATE::STATE0_UNSYNC;
		}

		break;
	}

#ifdef NRA12_DEBUG
	printf("state: NRA12_PARSE_STATE%s\n", parser_state[*state]);
#endif

	return ret;
}

nra12_parser.h如下:

enum class NRA12_PARSE_STATE {
	STATE0_UNSYNC = 0,
	STATE1_GOT_START1,
	STATE2_GOT_START2,
	STATE2_GOT_0C,
	STATE2_GOT_07,
	STATE3_GOT_Index,
	STATE4_GOT_Res,
	STATE5_GOT_DIST_H,
	STATE6_GOT_DIST_L,
	STATE7_GOT_UNUSE1,
	STATE8_GOT_UNUSE2,
	STATE9_GOT_UNUSE3,
	STATE10_GOT_UNUSE4,
	STATE11_GOT_END1,
	STATE12_GOT_END2
};

int nra12_parse(char c, char *parserbuf, unsigned *parserbuf_index, NRA12_PARSE_STATE *state, float *dist);

修改src/drivers/drv_sensor.h
添加

#define DRV_DIST_DEVTYPE_NRA12      0xC2

修改boards/px4/fmu-v5/default.px4board
添加

CONFIG_COMMON_DISTANCE_SENSOR_NRA12=y

最后编译并将固件下载到飞控,将NRA12通过串口连接到UART&I2C B口,将参数SENS_NRA12_CFG设置成TELEM/SERIAL4,然后观察能观察到测距传感器的数据输出
在这里插入图片描述

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

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

相关文章

01-MySQL 基础篇笔记

一、MySQL 概述 1.1 数据库相关概念 数据库&#xff1a;&#xff08;DB&#xff1a;DataBase&#xff09; 存储数据的仓库&#xff0c;数据是有组织的进行存储 数据库管理系统&#xff1a;&#xff08;DBMS&#xff1a;DataBase Management System&#xff09; 操作和管理数…

IoTDB 入门教程 基础篇⑨——TsFile导入导出工具

文章目录 一、前文二、准备2.1 准备导出服务器2.2 准备导入服务器 三、导出3.1 导出命令3.2 执行命令3.3 tsfile文件 四、导入4.1 上传tsfile文件4.2 导入命令4.3 执行命令 五、查询六、参考 一、前文 IoTDB入门教程——导读 数据库备份与迁移是数据库运维中的核心任务&#xf…

最小费用流相位解包裹

% test_cunwrap.m % % Matlab script to test Costantinis unwrapping % Author: Bruno Luong <brunoluong@yahoo.com> % History: % Orginal: 27-Aug-2009clear all; close all; clc; I1=double(imread(E:\zhenlmailcom-E8E745\华为家庭存储\.public_files\博士阶段\小…

[stm32-1]LED闪烁LED流水灯蜂鸣器

1、LED闪烁&LED流水灯&蜂鸣器 1.使用RCC开启GPIO时钟 2.使用GPIO_Init函数初始化GPIO 3.使用输入或输出函数控制GPIO口 RCC常用的3个库函数&#xff1a; void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); void RCC_APB2PeriphClockC…

C++中的reverse_iterator迭代器结构设计

目录 reverse_iterator迭代器结构设计 reverse_iterator迭代器基本结构设计 operator*()函数 operator()函数 operator->()函数 operator!()函数 rbegin()函数 rend()函数 operator--()函数 operator()函数 测试代码 const_reverse_iterator迭代器设计 reverse…

【数据结构】第四讲:双向链表

目录 一、链表的分类 二、双向链表的结构及实现 1.带头双向链表的结构 2.创建节点 3.初始化 4.尾插 5.打印 6.头插 7.尾删 8.头删 9.在pos位置之后插入数据 10.删除pos节点 11.查找 12.销毁 个人主页&#xff1a;深情秋刀鱼-CSDN博客 数据结构专栏&#xff1a;数…

java基于云计算的SaaS医院his信息系统源码 HIS云平台源码

目录 云HIS功能模块 1、预约挂号&#xff1a; 2、药库管理&#xff1a; 3、门诊医生站&#xff1a; 4、门诊费用&#xff1a; 5、药房管理&#xff1a; 6、治疗室&#xff08;门诊护士工作站&#xff09;&#xff1a; 7、统计分析&#xff1a; 8、财务管理&#xff1a;…

vue 时间轴页面 自己的写法 欢迎交流指正

<div class"first-box"><!--贯穿线--><div class"vertical-line-wrap"><div class"vertical-line"></div><div class"vertical-line-arrow"></div></div><!--开始--><div c…

数据存储-SD卡存储

手机自带内存较小&#xff0c;不适合存储大量数据&#xff0c;一般&#xff0c;大量数据都会采用外部存储&#xff0c;如&#xff1a;服务器存储、SD卡存储、数据库存储等。 本文讲解在SD卡上存储数据&#xff0c;并实现对数据的读和写。 整体思路&#xff1a;静态权限声明、动…

第五篇:通信脉络:探索计算机外设与总线体系的精髓

通信脉络&#xff1a;探索计算机外设与总线体系的精髓 1 引言 在这个技术日新月异的时代&#xff0c;理解计算机系统的基本构成要素 —— 总线和外设 —— 对于每个从事技术工作的人来说都是至关重要的。这些组件不仅是计算机通信的基石&#xff0c;也直接影响着系统的性能、效…

非对称齿轮的跨棒距算的对不对

前面有一期咱们聊了非对称齿轮《》&#xff0c;非对称齿轮的齿厚测量一般都为跨棒距。最近研究了下计算方法&#xff0c;对计算结果的正确性做了下验证。 在MATLAB中编制了相关的计算程序&#xff1a; 齿轮的模数4&#xff0c;左侧分度圆压力角25&#xff0c;右侧分度圆压力角…

CARIS12如何设置kmall船型文件?

最近我们对船上多波束EM2040D的采集软件进行了升级&#xff0c;从SIS4.3升级至SIS5。当使用CARIS11/12处理kmall数据时&#xff0c;发现往返横切海底线性目标时有10-20m错位。 ​编辑​ 我们开始怀疑SIS5采集的设置有问题&#xff0c;似乎跟时间延迟有关系。但是改了如下图两…

JAVA系列 小白入门参考资料 接口

目录 接口 接口的概念 语法 接口使用 接口实现用例 接口特性 实现多个接口和实现用例 接口间的继承 接口 接口的概念 在现实生活中&#xff0c;接口的例子比比皆是&#xff0c;比如&#xff1a;笔记本上的 USB 口&#xff0c;电源插座等。 电脑的 USB 口上&am…

初学python记录:力扣1235. 规划兼职工作

题目&#xff1a; 你打算利用空闲时间来做兼职工作赚些零花钱。 这里有 n 份兼职工作&#xff0c;每份工作预计从 startTime[i] 开始到 endTime[i] 结束&#xff0c;报酬为 profit[i]。 给你一份兼职工作表&#xff0c;包含开始时间 startTime&#xff0c;结束时间 endTime …

水仙花数问题

问题描述&#xff1a; 求出0&#xff5e;100000之间的所有“水仙花数”并输出。 “水仙花数”是指一个n位数&#xff0c;其各位数字的n次方之和确好等于该数本身&#xff0c;如:153&#xff1d;1^3&#xff0b;5^3&#xff0b;3^3&#xff0c;则153是一个“水仙花数”。 #in…

对链表进行插入排序(详细解析)

对链表进行插入排序&#xff08;详解&#xff09; 题目&#xff1a; 对链表进行插入排序 给定单个链表的头 head &#xff0c;使用 插入排序 对链表进行排序&#xff0c;并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的&#xff0c;每次只移动一个元素&a…

CUDA和显卡驱动

1.安装显卡驱动 https://www.nvidia.com/download/index.aspx?langen-us 由于我的显卡是RTX4060&#xff0c;因此先选择RTX40系列&#xff0c;然后选择RTX4060&#xff0c;进行安装 2.查看显卡对应的CUDA CUDA安装地址&#xff1a;https://developer.nvidia.com/cuda-toolk…

数据库(MySQL)—— 事务

数据库&#xff08;MySQL&#xff09;—— 事务 什么是事务事务操作未控制事务测试异常情况 控制事务一查看/设置事务提交方式&#xff1a;提交事务回滚事务 控制事务二开启事务提交事务回滚事务 并发事务问题脏读&#xff08;Dirty Read&#xff09;不可重复读&#xff08;Non…

小红书流量机制解析

小红书流量机制解析 前言 大家好&#xff0c;我自2020年5月起&#xff0c;开始专注于小红书平台&#xff0c;帮助品牌商家在小红书上经营。在这段时间里&#xff0c;我深入研究了小红书的流量机制&#xff0c;并结合自己的实践经验&#xff0c;为大家带来了这份全网最硬核的小…

【面试经典 150 | 字典树】实现 Trie (前缀树)

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;前缀树 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容进行回…