Linux SPI 驱动示例

2023-05-16

一 Linux 下 SPI 驱动框架

SPI 驱动框架分为主机控制器驱动和设备驱动,主机控制器也就是 SOC 的 SPI 控制器接口。

1.1 SPI 主机驱动

SPI 主机驱动就是 SOC SPI 控制器驱动,Linux 内核使用 spi_master结构体表示 SPI 主机驱动。

1.2 SPI 设备驱动

Linux 内核使用 spi_driver 结构体来表示 spi 设备 驱动,我们在编写 SPI 设备驱动的时候需要实现 spi_driver
spi_driver 注册和注销函数原型如下
int spi_register_driver(struct spi_driver *sdrv)
void spi_unregister_driver(struct spi_driver *sdrv)
 
spi_driver 注册示例程序
 
1 /* probe 函数 */
2 static int xxx_probe(struct spi_device *spi) 
3 { 
4 /* 具体函数内容 */
5 return 0; 
6 } 
7 
8 /* remove 函数 */
9 static int xxx_remove(struct spi_device *spi)
10 {
11 /* 具体函数内容 */
12 return 0;
13 }
14 /* 传统匹配方式 ID 列表 */
15 static const struct spi_device_id xxx_id[] = {
16 {"xxx", 0}, 
17 {}
18 };
19
20 /* 设备树匹配列表 */
21 static const struct of_device_id xxx_of_match[] = {
22 { .compatible = "xxx" },
23 { /* Sentinel */ }
24 };
25
26 /* SPI 驱动结构体 */
27 static struct spi_driver xxx_driver = {
28 .probe = xxx_probe,
29 .remove = xxx_remove,
30 .driver = {
31 .owner = THIS_MODULE,
32 .name = "xxx",
33 .of_match_table = xxx_of_match,
34 },
35 .id_table = xxx_id,
36 };
37 
38 /* 驱动入口函数 */
39 static int __init xxx_init(void)
40 {
41 return spi_register_driver(&xxx_driver);
42 }
43
44 /* 驱动出口函数 */
45 static void __exit xxx_exit(void)
46 {
47 spi_unregister_driver(&xxx_driver);
48 }
49
50 module_init(xxx_init);
51 module_exit(xxx_exit);

1.3 SPI 设备和驱动匹配过程
SPI
设备和驱动的匹配过程是由 SPI 总线来完成的,和 platformI2C 等驱动一样。
SPI
设备和驱动的匹配函数为 spi_match_device,定义在 drivers/spi/spi.c 文件中。

二、I.MX6U SPI 主机驱动

SPI 主机驱动一般都由 SOC 厂商编写好了。I.MX6U ECSPI 主机驱动文件为 drivers/spi/spi-imx.c。

三、SPI 设备驱动编写流程

3.1SPI 设备信息描述

1、在设备树中根据所使用的 IO 来创建或修改 pinctrl 子节点

        pinctrl_ecspi3: ecspi3grp{
            fsl,pins = <
                MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20         0x10b0 //片选信号
                MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK    0x10b1 //CLK信号
                MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI            0x10b1 //MOSI信号
                MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO            0x10b1 //MISO信号
            >;
        };

2、SPI 设备节点的创建与修改

&ecspi3{
	fsl,spi-num-chipselects = <1>;					/* 一个片选 */
	cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;	/* 片选引脚,软件片选 */
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi3>;//设置 IO 要使用的 pinctrl 子节点
	status = "okay";

/* 对应的icm20608子节点 */
	spidev0: icm20608@0 {/* @后面的0表示icm20608连接到 ECSPI3 的第 0 个通道上*/
		reg = <0>;//表示icm20608连接到 ECSPI3 的第 0 个通道上
		compatible = "alientek,icm20608";//兼容属性
		spi-max-frequency = <8000000>;/* SPI时钟频率8MHZ */
	};
};



3.2 SPI 设备数据收发处理流程
SPI
设备驱动的核心是 spi_driver,当我们向 Linux 内 核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。

SPI 数据传输步骤如下:
①、申请并初始化 spi_transfer结构体 ,设置 spi_transfer tx_buf 成员变量, tx_buf 为要发送的数据。然后设置 rx_buf 成员变量, rx_buf 保存着接收到的数据。最后设置 len 成员变量,也就是 要进行数据通信的长度。
②、使用 spi_message_init 函数初始化 spi_message
③、使用 spi_message_add_tail 函数将前面设置好的 spi_transfer 添加到 spi_message 队列中。
④、使用 spi_sync 函数完成 SPI 数据同步传输。
 
SPI 数据读写操作示例代码
 

/* SPI 多字节发送 */
static int spi_send(struct spi_device *spi, u8 *buf, int len) {
 int ret;
 struct spi_message m;
 
 struct spi_transfer t = {
 .tx_buf = buf,
 .len = len,
 };
 spi_message_init(&m); /* 初始化 spi_message */
 spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
 ret = spi_sync(spi, &m); /* 同步传输 */
 return ret; 
}

/* SPI 多字节接收 */
static int spi_receive(struct spi_device *spi, u8 *buf, int len) {
 int ret;
 struct spi_message m;
 
 struct spi_transfer t = {
 .rx_buf = buf,
 .len = len,
 };
 spi_message_init(&m); /* 初始化 spi_message */
 spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
 ret = spi_sync(spi, &m); /* 同步传输 */
 return ret; 
}

四、驱动示例代码
4.1 设备树修改

iomuxc 节点中添加一个新的pinctrl子节点来描述 ICM20608 所使用的 SPI 引脚。(同3.1)
在 ecspi3
节点追加 icm20608 子节点。(同3.1)
 

4.2示例代码
icm20608 spi接口六轴传感器驱动代码

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "icm20608reg.h"

#define ICM20608_CNT 1
#define ICM20608_NAME "icm20608"

/* 设备结构体 */
struct icm20608_dev
{
    int major;
    int minor;
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    void *private_data;
    int cs_gpio;
    struct device_node *nd;
    signed int gyro_x_adc;  /* 陀螺仪X轴原始值 	 */
    signed int gyro_y_adc;  /* 陀螺仪Y轴原始值		*/
    signed int gyro_z_adc;  /* 陀螺仪Z轴原始值 		*/
    signed int accel_x_adc; /* 加速度计X轴原始值 	*/
    signed int accel_y_adc; /* 加速度计Y轴原始值	*/
    signed int accel_z_adc; /* 加速度计Z轴原始值 	*/
    signed int temp_adc;    /* 温度原始值 			*/
};

static struct icm20608_dev icm20608dev;



/***************************SPI设备数据收发处理****************************/

/* SPI读寄存器 */
static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)
{
    u8 data = 0;
    struct spi_device *spi = (struct spi_device *)dev->private_data;
    //gpio_set_value(dev->cs_gpio, 0); /* 片选拉低 */

    data = reg | 0x80;
    spi_write_then_read(spi, &data, 1, buf, len);

    // spi_write(spi, &data, 1); /* 发送要读取的寄存器地址 */
    // spi_read(spi, buf, len);  /*读取数据*/

    //gpio_set_value(dev->cs_gpio, 1); /* 拉高片选 */
    return 0;
}

/* SPI写寄存器 */
static int icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, int len)
{
    u8 data = 0;
    u8 *txdata;
    struct spi_device *spi = (struct spi_device *)dev->private_data;

    txdata = kzalloc(len + 1, GFP_KERNEL);

    //gpio_set_value(dev->cs_gpio, 0); /* 片选拉低 */

    txdata[0] = reg & ~0x80;         /* 要写的寄存器地址 */
    memcpy(&txdata[1], buf, len);    /* 要发送的数据拷贝到txdata里面 */
    spi_write(spi, txdata, len + 1); /* 发送要写的寄存器地址 */

    // spi_write(spi, &data, 1); /* 发送要写的寄存器地址 */
    // spi_write(spi, buf, len); /* 发送要写的寄存器地址 */

    kfree(txdata);
    //gpio_set_value(dev->cs_gpio, 1); /* 拉高片选 */
    return 0;
}

/*ICM20608读取单个寄存器 */
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{
    u8 data = 0;
    icm20608_read_regs(dev, reg, &data, 1);
    return data;
}

/*ICM20608写一个寄存器 */
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{
    u8 buf = value;
    icm20608_write_regs(dev, reg, &buf, 1);
}


/***************************传感器数据的读取****************************/
/*
 * @description	: 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
 * 				: 三轴加速度计和内部温度。
 * @param - dev	: ICM20608设备
 * @return 		: 无。
 */
void icm20608_readdata(struct icm20608_dev *dev)
{
    unsigned char data[14];
    icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);

    dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);
    dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);
    dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);
    dev->temp_adc = (signed short)((data[6] << 8) | data[7]);
    dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
    dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
    dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}

/***************************spi设备的初始化****************************/
/* ICM20608初始化 */
void icm20608_reginit(struct icm20608_dev *dev)
{
    u8 value = 0;
    icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x80); /* 复位,复位后为0x40,睡眠模式 */
    mdelay(50);
    icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x01); /* 关闭睡眠,自动选择时钟 */
    mdelay(50);

    value = icm20608_read_onereg(dev, ICM20_WHO_AM_I);
    printk("ICM20608 ID=%#X\r\n", value);

    value = icm20608_read_onereg(dev, ICM20_PWR_MGMT_1);
    printk("ICM20_PWR_MGMT_1=%#X\r\n", value);

    icm20608_write_onereg(&icm20608dev, ICM20_SMPLRT_DIV, 0x00);    /* 输出速率是内部采样率					*/
    icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18);   /* 陀螺仪±2000dps量程 				*/
    icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18);  /* 加速度计±16G量程 					*/
    icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04);        /* 陀螺仪低通滤波BW=20Hz 				*/
    icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/
    icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00);    /* 打开加速度计和陀螺仪所有轴 				*/
    icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00);   /* 关闭低功耗 						*/
    icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00);       /* 关闭FIFO	 */
}







/*************************************文件操作集***********************************/

static int icm20608_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &icm20608dev; /* 设置私有数据 */
    return 0;
}

ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    signed int data[7];
    long err = 0;
    struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;

    icm20608_readdata(dev);
    data[0] = dev->gyro_x_adc;
    data[1] = dev->gyro_y_adc;
    data[2] = dev->gyro_z_adc;
    data[3] = dev->accel_x_adc;
    data[4] = dev->accel_y_adc;
    data[5] = dev->accel_z_adc;
    data[6] = dev->temp_adc;
    err = copy_to_user(buf, data, sizeof(data));
    return 0;
}

static int icm20608_release(struct inode *inode, struct file *filp)
{

    return 0;
}

static const struct file_operations icm20608_fops = {
    .owner = THIS_MODULE,
    .open = icm20608_open,
    .read = icm20608_read,
    .release = icm20608_release,
};







/***************************SPI 设备驱动注册注销流程****************************/

/*
* @description : spi 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
* @param - spi : spi 设备
*/
static int icm20608_probe(struct spi_device *spi)
{
    int ret = 0;
    printk("icm20608_probe\r\n");

    /* 搭建字符设备驱动框架,在/dev/下 */
    /* 2,注册字符设备 */
    icm20608dev.major = 0; /* 由系统分配主设备号 */

    if (icm20608dev.major)
    { /* 给定主设备号 */
        icm20608dev.devid = MKDEV(icm20608dev.major, 0);
        ret = register_chrdev_region(icm20608dev.devid, ICM20608_CNT, ICM20608_NAME);
    }
    else
    { /* 没有给定主设备号 */
        ret = alloc_chrdev_region(&icm20608dev.devid, 0, ICM20608_CNT, ICM20608_NAME);
        icm20608dev.major = MAJOR(icm20608dev.devid);
        icm20608dev.minor = MINOR(icm20608dev.devid);
    }
    if (ret < 0)
    {
        printk("icm20608 chrdev_region err!\r\n");
        goto fail_devid;
    }
    printk("icm20608 major=%d, minor=%d\r\n", icm20608dev.major, icm20608dev.minor);

    /* 3,注册字符设备 */
    icm20608dev.cdev.owner = THIS_MODULE;
    cdev_init(&icm20608dev.cdev, &icm20608_fops);
    ret = cdev_add(&icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);
    if (ret < 0)
    {
        goto fail_cdev;
    }

    /* 4,自动创建设备节点 */
    icm20608dev.class = class_create(THIS_MODULE, ICM20608_NAME);
    if (IS_ERR(icm20608dev.class))
    {
        ret = PTR_ERR(icm20608dev.class);
        goto fail_class;
    }

    icm20608dev.device = device_create(icm20608dev.class, NULL,
                                       icm20608dev.devid, NULL, ICM20608_NAME);
    if (IS_ERR(icm20608dev.device))
    {
        ret = PTR_ERR(icm20608dev.device);
        goto fail_device;
    }


    /* 初始化spi_device */
    spi->mode = SPI_MODE_0;
    spi_setup(spi);

    /* 设置icm20608dev的私有数据为spi */
    icm20608dev.private_data = spi;

    /* 初始化icm20608 寄存器 */
    icm20608_reginit(&icm20608dev);

    return 0;
fail_gpio:

fail_device:
    class_destroy(icm20608dev.class);
fail_class:
    cdev_del(&icm20608dev.cdev);
fail_cdev:
    unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);
fail_devid:
    return ret;
}

/*
* @description : i2c 驱动的 remove 函数,移除 i2c 驱动的时候此函数会执行
* @param - spi : spi 设备
*/
static int icm20608_remove(struct spi_device *spi)
{
    /* 1,删除字符设备 */
    cdev_del(&icm20608dev.cdev);

    /* 2,注销设备号 */
    unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);

    /* 3,摧毁设备 */
    device_destroy(icm20608dev.class, icm20608dev.devid);

    /* 4,摧毁类 */
    class_destroy(icm20608dev.class);

    /*5.释放片选IO */
    gpio_free(icm20608dev.cs_gpio);

    return 0;
}

/* 传统匹配方式 ID 列表 */
struct spi_device_id icm20608_id[] = {
    {"alientek,icm20608", 0},
    {}};

/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {
    {
        .compatible = "alientek,icm20608",
    },
    {}};

/* SPI 驱动结构体 */
struct spi_driver icm20608_driver = {

    .probe = icm20608_probe,
    .remove = icm20608_remove,

    .driver = {
        .name = "icm20608",
        .owner = THIS_MODULE,
        .of_match_table = icm20608_of_match,
    },

    .id_table = icm20608_id,

};

/*驱动入口函数*/
static int __init icm20608_init(void)
{
    int ret = 0;
    ret = spi_register_driver(&icm20608_driver);

    return ret;
}

/*驱动出口函数*/
static void __exit icm20608_exit(void)
{
    spi_unregister_driver(&icm20608_driver);
}

module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");


驱动代码中主要包含四个部分

1)SPI设备数据收发处理
2)传感器数据的读取,spi设备的初始化
3)  文件操作集
4)  SPI 设备驱动注册注销流程

示例代码中spi数据收发处理使用的函数为spi_write_then_read函数和spi_write函数

int spi_write_then_read(struct spi_device *spi,
		const void *txbuf, unsigned n_tx,
		void *rxbuf, unsigned n_rx)
{
	static DEFINE_MUTEX(lock);

	int			status;
	struct spi_message	message;
	struct spi_transfer	x[2];
	u8			*local_buf;

	/* Use preallocated DMA-safe buffer if we can.  We can't avoid
	 * copying here, (as a pure convenience thing), but we can
	 * keep heap costs out of the hot path unless someone else is
	 * using the pre-allocated buffer or the transfer is too large.
	 */
	if ((n_tx + n_rx) > SPI_BUFSIZ || !mutex_trylock(&lock)) {
		local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx),
				    GFP_KERNEL | GFP_DMA);
		if (!local_buf)
			return -ENOMEM;
	} else {
		local_buf = buf;
	}

	spi_message_init(&message);
	memset(x, 0, sizeof(x));
	if (n_tx) {
		x[0].len = n_tx;
		spi_message_add_tail(&x[0], &message);
	}
	if (n_rx) {
		x[1].len = n_rx;
		spi_message_add_tail(&x[1], &message);
	}

	memcpy(local_buf, txbuf, n_tx);
	x[0].tx_buf = local_buf;
	x[1].rx_buf = local_buf + n_tx;

	/* do the i/o */
	status = spi_sync(spi, &message);
	if (status == 0)
		memcpy(rxbuf, x[1].rx_buf, n_rx);

	if (x[0].tx_buf == buf)
		mutex_unlock(&lock);
	else
		kfree(local_buf);

	return status;
}
EXPORT_SYMBOL_GPL(spi_write_then_read);




static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
	struct spi_transfer	t = {
			.tx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spi_sync(spi, &m);
}

从上面两个函数看出,这两个函数也是通过调用以下三个函数完成数据的传输

spi_message_init(&m);
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux SPI 驱动示例 的相关文章

  • 标准头文件中的 C 编译器错误 - 未定义的 C++ 定义

    我正在尝试编译 C 程序 但收到许多错误 这些错误是在标准 C 头文件 inttypes h stdio h stat h 等 中遇到的 错误的来源是以下未定义的常量 BEGIN DECLS END DECLS BEGIN NAMESPAC
  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • Linux shell 脚本:十六进制数字到二进制字符串

    我正在 shell 脚本中寻找一些简单的方法来将十六进制数字转换为 0 和 1 字符的序列 Example 5F gt 01011111 是否有任何命令或简单的方法来完成它 或者我应该为其编写一些开关 echo ibase 16 obase
  • Bash 方法的返回值总是模 256

    我有一个 bash 脚本方法 它返回输入值 然而 返回值始终是模 256 的值 我用 google 搜索了一段时间 发现this http www tldp org LDP abs html exitcodes html文章说它总是以 25
  • sleep 0 有特殊含义吗?

    我看到很多用法sleep 0在我的一个客户项目中 代码看起来像这样 while true sleep 0 end 阅读一些像这样的答案this https stackoverflow com questions 3727420 signif
  • 适用于 KDE 和 Gnome 的 Gui [重复]

    这个问题在这里已经有答案了 我想为一个现在是 CLI 的应用程序编写一个 gui 它需要在 KDE 和 Gnome DE 中 看起来不错 充分利用用户的外观设置 如果我选择 Qt 或 GTK 我能够做到这一点吗 它们与两个 DE 集成良好吗
  • 如何在特定 systemd 服务重新启动时触发自定义脚本运行

    我想知道如何安排自定义脚本在重新启动服务时运行 我的用例是 每当重新启动 Tomcat 服务时 我都必须运行多个命令 我想知道是否有一种方法可以编写脚本并安排它在重新启动 Tomcat 服务时运行 我已将 tomcat 脚本设置为 syst
  • 如何查找连接到 AF_INET 套接字的客户端的 UID?

    有什么方法或类似的东西ucred for AF UNIX如果是AF INET插座 TCP在我的例子中 找出连接到我的套接字的客户端的UID 还有 proc net tcp但它显示了UID of the creator插座的而不是连接的cli
  • Linux 桌面快捷方式和安装图标

    我需要添加什么到我的 spec文件来创建桌面快捷方式并在安装过程中为快捷方式分配一个图标 rpm 如果需要脚本 一个示例将非常有帮助 您在 Linux 下使用 desktop 文件作为图标 图标放置的位置取决于您使用的发行版和桌面环境 由于
  • 在主目录中安装库

    在 Linux Ubuntu 中 我尝试运行一个工具 但它显示错误 库丢失 我无权在系统中安装任何内容 或者根本无法从我的用户帐户执行 sudo 是否可以在我的主目录 没有 sudo 中安装缺少的库 在我的例子中为 libstdc so 6
  • GCC 和 ld 找不到导出的符号...但它们在那里

    我有一个 C 库和一个 C 应用程序 尝试使用从该库导出的函数和类 该库构建良好 应用程序可以编译 但无法链接 我得到的错误遵循以下形式 app source file cpp text 0x2fdb 对 lib namespace Get
  • PHP 致命错误:未找到“MongoClient”类

    我有一个使用 Apache 的网站 代码如下 当我尝试访问它时 我在 error log 中收到错误 PHP Fatal Error Class MongoClient not found 以下是可能错误的设置 但我认为没有错误 php i
  • 如何制作和应用SVN补丁?

    我想制作一个SVN类型的补丁文件httpd conf这样我就可以轻松地将其应用到其他主机上 If I do cd root diff Naur etc httpd conf httpd conf original etc httpd con
  • 如何模拟ARM处理器运行环境并加载Linux内核模块?

    我尝试加载我的vmlinux into gdb并使用 ARM 内核模拟器 但我不明白为什么我会得到Undefined target command sim 这是外壳输出 arm eabi gdb vmlinux GNU gdb GDB 7
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 在Linux上编译C# + WPF以便在Windows上运行

    我有一个 C 应用程序 其中某些部分是使用 WPF 编写的 Mono 不支持 可以在 Linux 上编译这个应用程序吗 最终 该应用程序将在 Windows 上运行 但它是更大框架的一部分 并且我们的整个构建过程在 Linux 上运行 因此
  • 在 Mac OS X 上构建 Linux 内核

    我正在做一个修改Linux内核的项目 我有一台桌面 Linux 机器 在上面构建内核没有问题 不过 我要去旅行 我想在途中工作 我只有一台 MacBook 当我尝试构建 Linux 内核时 它抱怨说elf h was not found 我
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • 创建 jar 文件 - 保留文件权限

    我想知道如何创建一个保留其内容的文件权限的 jar 文件 我将源代码和可执行文件打包在一个 jar 文件中 该文件将在使用前提取 人们应该能够通过运行批处理 shell 脚本文件立即运行示例和演示 然后他们应该能够修改源代码并重新编译所有内
  • 如何查明CONFIG_FANOTIFY_ACCESS_PERMISSIONS是否启用?

    我想利用fanotify 7 http man7 org linux man pages man7 fanotify 7 html我遇到的问题是在某些内核上CONFIG FANOTIFY ACCESS PERMISSIONS不起作用 虽然C

随机推荐

  • Spring Security身份认证之UserDetailsService

    zhiqian我们采用了配置文件的方式从数据库中读取用户进行登录 虽然该方式的灵活性相较于静态账号密码的方式灵活了许多 xff0c 但是将数据库的结构暴露在明显的位置上 xff0c 绝对不是一个明智的做法 本文通过Java代码实现UserD
  • 基于Apache OLTU的OAuth2.0授权解决方案

    Apache OLTU实现了OAuth 2 0的规范 xff0c 是一种可靠的Java授权解决方案 但是 xff0c 官方文档实在是太惨不忍睹了 本文参考了开涛的 OAuth 2 0集成Shiro文章 模拟了OAuth2 0的认证流程 技术
  • Couch的MapReduce查询

    1 MapReduce介绍 传统的关系型数据库中 xff0c 只要你的数据是结构化的 xff0c 你可以进行任何类型的查询 Apache Couch与此相反 xff0c 它使用MapReduce xff08 预定义的map和的reduce方
  • Java遍历读取文件目录结构

    Java读取计算机目录 xff0c 并打印 public class ReadDirectory 文件所在的层数 private int fileLevel 生成输出格式 64 param name 输出的文件名或目录名 64 param
  • Java实现数字水印

    数字水印有可见不可见之分 xff0c 可见的比如课件上印有学校校徽 xff0c 微博发图片会水印上上传者的信息及微博logo等 用java实现可见的数字水印 xff0c 草人主要是用到了java awt包中的AlphaComposite类
  • 程序员应该如何去设计需求

    刚出道的程序员 xff0c 在做需求分析的时候 xff0c 总是经常挨批 xff0c 客户说他们不能按照客户的要求去设计原型 xff0c 领导说他们不用心去与客户沟通交流 程序员总是感到自己很冤枉 xff0c 明明客户没有给出一点建设性建议
  • 小小程序员的一周日报

    工作依旧在有条不紊的进行着 xff0c 一周的时间很快就会过去 xff0c 正如今天李哥所说的 xff0c 这一周还没有感觉怎么过呢 xff0c 就结束了 是啊 xff0c 这就是我们的工作 xff0c 程序员的工作 xff0c 软件设计师
  • 项目空间都有啥

    项目空间是什么 xff0c Workplace 答案是 xff1a No 项目空间是由项目负责人提出的实施某项目方案的一种流程 项目空间是XX海油ERP管理系统下的一个业务 xff0c 项目负责人通过创建项目名称 项目负责人 使用资源 所属
  • 你不要瞧不起Ctrl+C

    曾经 xff0c 在我未参加工作之前 xff0c 我认为靠 Ctrl 43 C 来完成工作的人 xff0c 肯定是懒惰的程序员 xff0c 但是现在我发现我错了 xff0c 而且是彻底的错了 能够通过 Ctrl 43 C 来完成工作的人 x
  • 文档交接说明书(模板)

    因为同事的离职 xff0c 我的入职 xff0c 要从同事手中交接过来一些项目 公司里只有一些开发文档相关的模板 xff0c 并没有文档交接相关的模板 xff0c 所以交接文档的模板也就由我们自己来定 我结合自己在工作中的经验 xff0c
  • UDS网络层/TP层(ISO 15765-2)的解读

    本文是对 ISO 15765 2 2011 协议的一些解读 需要指出该协议的最新版为2016版 TP层存在意义 UDS网络层 xff0c 又称为TP层 xff08 Transport Protocol Layer xff09 其存在的目的是
  • std vector传递指针使用说明

    今天用WM COPYDATA传递一个Vector的指针 xff0c 传递过来始终失败 后面找到一篇文章 xff0c 说只要传递第一个元素的地址就行 xff0c 因为vector在内存是连续的 static std vector lt UIm
  • Leetcode之运算库函数自定义

    一 Leetcode50 pow 注意点 1 n的值可以为正 xff0c 负 xff0c 0 2 O n 会TLE xff0c 使用递归时 xff0c 一定要将中间步保存 3 有博文中提到 xff0c 若n lt 0 xff0c 可以令n
  • 树莓派无键盘安装步骤

    树莓派无键盘安装 下载系统烧录系统配置无线网络开机并连接树莓派更新源和系统安装xrdp xff08 远程访问 xff09 Windows连接远程桌面 下载系统 应该只有官方的Raspbian系统支持无键盘安装 xff0c 官网下载系统 xf
  • iic实现采集温湿度传感器值

    iic h ifndef IIC H define IIC H include 34 stm32mp1xx gpio h 34 include 34 stm32mp1xx rcc h 34 通过程序模拟实现I2C总线的时序和协议 GPIOF
  • Matlab在线运行网站

    桌面版的Matlab不仅安装包很大 xff0c 而且也很吃性能 xff0c 不如就用网页版 xff0c 来玩啊 xff01 https www tutorialspoint com execute matlab online php 点击c
  • An Introduction on Deep Learning for the Physical Layer

    An Introduction on Deep Learning for the Physical Layer 代码实现 xff1a https github com shengjian3476077 DLforPhy 一 文章的主要工作
  • motion planning 一起学习

    shenlan 学院 motion planning 一起学习 打算买深蓝的motion planning for Mobile robots xff0c 主要是讲规划算法的 xff0c 有无一起学习的小伙伴 xff1f 一起学习 xff0
  • 【java面试之Linux】Linux启动过程、

    一 Linux启动过程 启动第一步 xff0d xff0d 加载BIOS 启动第二步 xff0d xff0d 读取MBR 主引导记录 启动第三步 xff0d xff0d Boot Loader 启动第四步 xff0d xff0d 加载内核
  • Linux SPI 驱动示例

    一 Linux 下 SPI 驱动框架 SPI 驱动框架分为主机控制器驱动和设备驱动 xff0c 主机控制器也就是 SOC 的 SPI 控制器接口 1 1 SPI 主机驱动 SPI 主机驱动就是 SOC 的 SPI 控制器驱动 xff0c L