Linux·i2c驱动示例

2023-11-10

I2C 是很常用的一个串行通信接口,常用于连接各种外设、传感器等器件。

一、Linux I2C 驱动框架

Linux 内核将 I2C 驱动分为两部分:
①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
②、 I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。
一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,设备驱动开发者只要专注于 I2C 设备驱动即可。 
 
I2C 设备驱动重点关注两个数据结构: i2c_client 和 i2c_driver,i2c_client 就是描述设备信息的,         i2c_driver 描述驱动内容,类似于 platform_driver。
1)一个设备对应一个 i2c_client ,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个 i2c_client。
2) i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容。


对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver,构建完成以后需要向Linux 内核注册这个 i2c_driver。
注册i2c设备驱动 常使用 int i2c_register_driver,i2c_add_driver函数。
注销i2c 设备驱动使用i2c_del_driver 函数。 
 
i2c_driver 注册流程如下 
 

1 /* i2c 驱动的 probe 函数 */
2 static int xxx_probe(struct i2c_client *client,
const struct i2c_device_id *id) 
3 { 
4 /* 函数具体程序 */
5 return 0; 
6 } 
7 
8 /* i2c 驱动的 remove 函数 */
9 static int xxx_remove(struct i2c_client *client)
10 {
11 /* 函数具体程序 */
12 return 0;
13 }
14
15 /* 传统匹配方式 ID 列表 */
16 static const struct i2c_device_id xxx_id[] = {
17 {"xxx", 0}, 
18 {}
19 };
20
21 /* 设备树匹配列表 */
22 static const struct of_device_id xxx_of_match[] = {
23 { .compatible = "xxx" },
24 { /* Sentinel */ }
25 };
26
27 /* i2c 驱动结构体 */
28 static struct i2c_driver xxx_driver = {
29 .probe = xxx_probe,
30 .remove = xxx_remove,
31 .driver = {
32 .owner = THIS_MODULE,
33 .name = "xxx",
34 .of_match_table = xxx_of_match,
35 },
36 .id_table = xxx_id,
37 };
38 
39 /* 驱动入口函数 */
40 static int __init xxx_init(void)
41 {
42 int ret = 0;
43
44 ret = i2c_add_driver(&xxx_driver);
45 return ret;
46 }
47
48 /* 驱动出口函数 */
49 static void __exit xxx_exit(void)
50 {
51 i2c_del_driver(&xxx_driver);
52 }
53
54 module_init(xxx_init);
55 module_exit(xxx_exit);

设备和驱动的匹配过程是由 I2C 总线完成的,具体参考drivers/i2c/i2c-core.c 下 i2c_device_match函数。 

二、I2C 设备驱动编写流程

2.1 I2C 设备信息描述

1)未使用设备树的时候,使用 i2c_board_info 结构体来描述一个具体的 I2C 设备。type 和 addr 这两个成员变量是必须要设置的,一个是 I2C 设备的名字,一个是 I2C 设备的器件地址。

例如:OV2640 的 I2C 设备信息

 #define I2C_BOARD_INFO(dev_type, dev_addr) \
 .type = dev_type, .addr = (dev_addr)
 
 static struct i2c_board_info mx27_3ds_i2c_camera = {
 I2C_BOARD_INFO("ov2640", 0x30),
 };


2)使用设备树的时候,通过在设备树中创建相应的节点就行了。
例如:mag3110磁力计子节点

1 &i2c1 {
2 clock-frequency = <100000>;
3 pinctrl-names = "default"; 
4 pinctrl-0 = <&pinctrl_i2c1>;
5 status = "okay"; 
6 
7 mag3110@0e { 
8 compatible = "fsl,mag3110"; 
9 reg = <0x0e>;
10 position = <2>;
11 };
......
20 };

现在基本上都使用设备树


2.2 I2C 设备数据收发处理流程

通过 i2c_transfer 函数对I2C 设备寄存器进行读写操作。使用 i2c_transfer 函数发送数据之前要先构建好 i2c_msg。

示例代码: I2C 设备多寄存器数据读写 
 

1 /* 设备结构体 */
2 struct xxx_dev { 
3 ......
4 void *private_data; /* 私有数据,一般会设置为 i2c_client */
5 };
6 
7 /*
8 * @description : 读取 I2C 设备多个寄存器数据
9 * @param – dev : I2C 设备
10 * @param – reg : 要读取的寄存器首地址
11 * @param – val : 读取到的数据
12 * @param – len : 要读取的数据长度
13 * @return : 操作结果
14 */
15 static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val,int len)
16 {
17 int ret;
18 struct i2c_msg msg[2];
19 struct i2c_client *client = (struct i2c_client *)dev->private_data;
20
21 /* msg[0],第一条写消息,发送要读取的寄存器首地址 */
22 msg[0].addr = client->addr; /* I2C 器件地址 */
23 msg[0].flags = 0; /* 标记为发送数据 */
24 msg[0].buf = &reg; /* 读取的首地址 */
25 msg[0].len = 1; /* reg 长度 */
26
27 /* msg[1],第二条读消息,读取寄存器数据 */
28 msg[1].addr = client->addr; /* I2C 器件地址 */
29 msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
30 msg[1].buf = val; /* 读取数据缓冲区 */
31 msg[1].len = len; /* 要读取的数据长度 */
32
33 ret = i2c_transfer(client->adapter, msg, 2);
34 if(ret == 2) {
35 ret = 0;
36 } else {
37 ret = -EREMOTEIO;
38 }
39 return ret;
40 }
41
42 /*
43 * @description : 向 I2C 设备多个寄存器写入数据
44 * @param – dev : 要写入的设备结构体
45 * @param – reg : 要写入的寄存器首地址
46 * @param – buf : 要写入的数据缓冲区
47 * @param – len : 要写入的数据长度
48 * @return : 操作结果
49 */
50 static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf,u8 len)
51 {
52 u8 b[256];
53 struct i2c_msg msg;
54 struct i2c_client *client = (struct i2c_client *)dev->private_data;
55 
56 b[0] = reg; /* 寄存器首地址 */
57 memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组 b 里面 */
58 
59 msg.addr = client->addr; /* I2C 器件地址 */
60 msg.flags = 0; /* 标记为写数据 */
61
62 msg.buf = b; /* 要发送的数据缓冲区 */
63 msg.len = len + 1; /* 要发送的数据长度 */
64
65 return i2c_transfer(client->adapter, &msg, 1);
66 }


 另外还有两个 API 函数分别用于 I2C 数据的收发操作,这两个函数最终都会调用i2c_transfer。
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)// I2C 数据发送函数
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)// I2C 数据接收函数


三、AP3216C三合一环境传感器驱动示例代码 

3.1设备树

 pinctrl_i2c1: i2c1grp {
            fsl,pins = <
                MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
                MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
            >;
        };
pinctrl_i2c1 就是 I2C1 的 IO 节点,这里将 UART4_TXD 和 UART4_RXD 这两个 IO 分别复用为 I2C1_SCL 和 I2C1_SDA ,电气属性都设置为 0x4001b8b0 。
&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";
 
        ap3216c@1e {
        compatible = "alientek,ap3216c";
        reg = <0x1e>;
    };
};


AP3216C 连接在 I2C1 上的,因此需要在 i2c1 节点下添加 ap3216c 的设备子节点。


3.2示例代码

#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/i2c.h>
#include <linux/delay.h>
#include "ap3216creg.h"
 
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
 
struct ap3216c_dev
{
    int major;
    int minor;
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    void *private_data;
    unsigned short ir, als, ps;
};
 
static struct ap3216c_dev ap3216cdev;
 

 
/******************************I2C 设备数据收发处理***********************************/
 
/* 读取AP3216C的N个寄存器值 */
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
    struct i2c_msg msg[2];
 
    struct i2c_client *client = (struct i2c_client *)dev->private_data;
 
    /* msg[0]发送要读取的寄存器首地址 */
    msg[0].addr = client->addr; /* 从机地址,也就是AP3216C地址*/
    msg[0].flags = 0;           /* 表示为要发送的数据*/
    msg[0].buf = &reg;          /* 表示为要发送的数据,也就是寄存器地址*/
    msg[0].len = 1;             /* 要发送的寄存器地址长度为1*/
 
    /* msg[1]读取数据 */
    msg[1].addr = client->addr; /* 从机地址,也就是AP3216C地址*/
    msg[1].flags = I2C_M_RD;    /* 表示读数据*/
    msg[1].buf = val;           /* 接收到的从机发送的数据*/
    msg[1].len = len;           /* 要读取的寄存器长度*/
 
    return i2c_transfer(client->adapter, msg, 2);
}
 
/* 向AP3216C写N个寄存器的数据 */
static int ap3216c_write_regs(struct ap3216c_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[0]发送要读取的寄存器首地址 */
    msg.addr = client->addr; /* 从机地址,也就是AP3216C地址*/
    msg.flags = 0;           /* 表示为要发送的数据*/
    msg.buf = b;             /* 表示为要发送的数据,也就是寄存器地址*/
    msg.len = len + 1;       /* 要发送的数据长度:寄存器地址长度+实际的数据长度*/
 
    return i2c_transfer(client->adapter, &msg, 1);
}
 
/*读取AP3216C 一个寄存器*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
    u8 data = 0;
 
    ap3216c_read_regs(dev, reg, &data, 1);
 
    return data;
}
 
/*向AP3216C 一个寄存器写数据*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
    u8 buf = 0;
    buf = data;
    ap3216c_write_regs(dev, reg, &buf, 1);
}
 
/*ap3216c数据读取*/
void ap3216c_readdata(struct ap3216c_dev *dev)
{
    unsigned char buf[6] = {0};
    unsigned char i = 0;
 
    /*循环的读取数据*/
    for (i = 0; i < 6; i++)
    {
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
    }
 
    if (buf[0] & 0x80) /*为真表示IR 和PS数据无效*/
    {
        dev->ir = 0;
        dev->ps = 0;
    }
    else
    {
        dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03);
        dev->ps = (((unsigned short)buf[5] & 0x3F) << 4) | ((unsigned short)buf[4] & 0x0F);
    }
 
    dev->als = ((unsigned short)buf[3] << 8) | buf[2];
}
 
 
 
/*************************************文件操作集****************************************/
 
static int ap3216c_open(struct inode *inode, struct file *filp)
{
    unsigned char value = 0;
 
    filp->private_data = &ap3216cdev;
 
    /*初始化AP3216C*/
    ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x4); /* 复位 */
    mdelay(50);
    ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x3); /* 复位 */
 
    value = ap3216c_read_reg(&ap3216cdev, AP3216C_SYSTEMCONG);
    printk("AP3216C_SYSTEMCONG=%#x\r\n", value);
 
    return 0;
}
 
static int ap3216c_release(struct inode *inode, struct file *filp)
{
    //struct ap3216c_dev *dev = (struct ap3216c_dev*)filp->private_data;
    printk("ap3216c_release\r\n");
    return 0;
}
 
ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    long err = 0;
    short data[3];
 
    struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
 
    /*向应用返回AP3216C的原始数据*/
    ap3216c_readdata(dev);
    data[0] = dev->ir;
    data[1] = dev->als;
    data[2] = dev->ps;
 
    err = copy_to_user(buf, data, sizeof(data));
    return 0;
}
 
static const struct file_operations ap3216c_fops = {
    .owner = THIS_MODULE,
    .open = ap3216c_open,
    .read = ap3216c_read,
    .release = ap3216c_release,
};
 
 
/*************************************i2c设备驱动注册与注销*****************************/
 
/*
 * @description : i2c 驱动的 probe 函数,当驱动与
 * 设备匹配以后此函数就会执行
 * @param - client : i2c 设备
 * @param - id : i2c 设备 ID
 * @return : 0,成功;其他负值,失败
 */
static int ap3216c_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
{
    int ret = 0;
    printk("ap3216c_probe!\r\n");
 
    /* 搭建字符设备驱动框架,在/dev/下 */
    /* 1、构建设备号 */
    ap3216cdev.major = 0; /* 由系统分配主设备号 */
 
    if (ap3216cdev.major)
    { /* 给定主设备号 */
        ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
        ret = register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
    }
    else
    { /* 没有给定主设备号 */
        ret = alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
        ap3216cdev.major = MAJOR(ap3216cdev.devid);
        ap3216cdev.minor = MINOR(ap3216cdev.devid);
    }
    if (ret < 0)
    {
        printk("ap3216c chrdev_region err!\r\n");
        goto fail_devid;
    }
    printk("ap3216c major=%d, minor=%d\r\n", ap3216cdev.major, ap3216cdev.minor);
 
    /* 2、注册设备 */
    ap3216cdev.cdev.owner = THIS_MODULE;
    cdev_init(&ap3216cdev.cdev, &ap3216c_fops);
    ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
    if (ret < 0)
    {
        goto fail_cdev;
    }
 
    /******** 自动创建设备节点 *******/
 
    /* 3、创建类 */
    ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
    if (IS_ERR(ap3216cdev.class))
    {
        ret = PTR_ERR(ap3216cdev.class);
        goto fail_class;
    }
 
    /* 4、创建设备 */
    ap3216cdev.device = device_create(ap3216cdev.class, NULL,
                                      ap3216cdev.devid, NULL, AP3216C_NAME);
    if (IS_ERR(ap3216cdev.device))
    {
        ret = PTR_ERR(ap3216cdev.device);
        goto fail_device;
    }
 
    ap3216cdev.private_data = client;
 
    return 0;
 
fail_device:
    class_destroy(ap3216cdev.class);
fail_class:
    cdev_del(&ap3216cdev.cdev);
fail_cdev:
    unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
fail_devid:
    return ret;
}
 
/*
* @description : i2c 驱动的 remove 函数,移除 i2c 驱动此函数会执行
* @param – client : i2c 设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
    /* 1,删除字符设备 */
    cdev_del(&ap3216cdev.cdev);
 
    /* 2,注销设备号 */
    unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
 
    /* 3,摧毁设备 */
    device_destroy(ap3216cdev.class, ap3216cdev.devid);
 
    /* 4,摧毁类 */
    class_destroy(ap3216cdev.class);
 
    return 0;
}
 
/* 传统匹配方式 ID 列表 */
static struct i2c_device_id ap3216c_id[] = {
    {"alientek,ap3216c", 0},
    {}
 
};
 
/* 设备树匹配表 */
static const struct of_device_id ap3216c_of_match[] = {
    {
        .compatible = "alientek,ap3216c",
    },
    {}
 
};
 
/* i2c_driver */
static struct i2c_driver ap3216c_driver = {
    .probe = ap3216c_probe,
    .remove = ap3216c_remove,
    .driver = {
        .name = "ap3216c",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(ap3216c_of_match),
    },
    .id_table = ap3216c_id,
};
 
/*驱动入口函数*/
static int __init ap3216c_init(void)
{
    int ret;
    ret = i2c_add_driver(&ap3216c_driver);
    if (ret != 0)
    {
        pr_err("AP3216C I2C registration failed %d\n", ret);
        return ret;
    }
    return 0;
}
 
/*驱动出口函数*/
static void __exit ap3216c_exit(void)
{
    i2c_del_driver(&ap3216c_driver);
}
 
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");


示例代码主要分三大块

  1. i2c设备驱动注册与注销
  2. 文件操作函数集
  3. I2C 设备数据收发处理


原文链接:https://blog.csdn.net/shengnan89/article/details/124270291

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

Linux·i2c驱动示例 的相关文章

  • 00 SD卡知识简介

    具体可见如下文章 源地址 SD卡介绍
  • 第七篇 硬件内存资源的获取,解析

    硬件资源的获取 解析 1 生成WDF的KMDFhelloWorld程序 2 改写INF文件中硬件ID 3 编译安装 以上三点不重复介绍 下面直接关注更新 增加的代码 在DeviceADD例程中添加 首先是增加即插即用管理 应该对应于WDM的
  • Linux驱动系列-PWM驱动

    转自 嵌入式系统研发 1 概述 本文主要讲述了Linux的PWM驱动框架 实现方法 驱动添加方法和调试方法 示例Linux内核版本 6 2 8 2 原理 PWM是Pulse Width Modulation的简称 中文译作脉冲宽度调制 作为
  • FPGA学习记录:第28章 VGA显示器驱动设计与验证

    硬件平台 Cyclone IV E EP4CE10F17C8 开发平台 Quartus II 64 Bit Version 13 0 1 Build 232 06 12 2013 SP 1 SJ Full Version 开发板 野火征途p
  • EDK II Module Writers Guide上

    一 EDK2简介 1 EDK2工作流 二 EDK2 Packages 1 Packages介绍 EDK2 Packages是一个容器 其中包含一组模块及模块的相关定义 每个Package是一个EDK2单元 整个Project的源代码可以被分
  • linux-arm电源管理

    一文搞懂ARM SoC功耗控制架构
  • 编译内核的相关知识

    1 在PC端搭建环境 ubantu 2 树莓派等芯片带操作系统的启动过程 C51 STM32 裸机 用C直接操控底层寄存器实现相关业务 业务流程型的裸机代码 3 带有操作系统的 X86 intel windows 启动过程 电源 gt bi
  • 伺服电机堵转检测

    一 电流数据的分析 电机工作时的电流如下图 电机正常工作时 电机电流具有两个状态 正常旋转和堵转 正常旋转时 电流在控制算法的作用下 一开始会有很快的上升 过程中电流受到控制算法的作用 没有平稳阶段 堵转时 电机结束了控制算法 所以堵转时电
  • Linux驱动:应用程序open如何调用到驱动程序的open函数

    字符设备文件的打开流程 相关结构体 流程涉及相关结构体如下 struct inode dev t i rdev const struct file operations i fop former gt i op gt default fil
  • ARM(IMX6U)裸机模仿STM32驱动开发实验(定义外设结构体)

    参考 Linux之ARM IMX6U 裸机模仿STM32驱动开发格式 作者 一只青木呀 发布时间 2020 08 15 12 11 56 网址 https blog csdn net weixin 45309916 article deta
  • 正点原子STM32 H743完成RT Thread下的LAN8720 网卡驱动 LWIP跑起来

    目前RT官网对H743的支持力度还不理想 本想按照F407的搞定网卡的套路来搞定H743的网卡 因为phy也是LAN 8720 以为会很轻松 没想到却是一条遍布荆棘的路 好在已经有不少大佬做了不少工作 终于在巨人肩膀人完成了网卡的驱动 能p
  • 异步通知实验(信号)

    目录 异步通知 异步通知简介 驱动中的信号处理 应用程序对异步通知的处理 硬件原理图分析 实验程序编写 修改设备树文件 程序编写 编写测试APP 运行测试 编译驱动程序和测试APP 运行测试 在前面使用阻塞或者非阻塞的方式来读取驱动中按键值
  • 杂项设备(misc device)和字符设备(char device)区别

    杂项设备 misc device 杂项设备也是在嵌入式系统中用得比较多的一种设备驱动 在 Linux 内核的include linux目录下有Miscdevice h文件 要把自己定义的misc device从设备定义在这里 其实是因为这些
  • Windows驱动开发第11课(R3与R0通信交换数据第二节)

    在上一节课我们证实了在用户层调用CreateFile函数时 相应的在驱动层会响应一个IRP MJ CREATE的事件 这节课我们来看看用户层和驱动层是怎么交换数据的 首先来介绍一下控制码 由CTL CODE宏创建 是一个唯一的32位系统I
  • 磁盘数据线接触不良的故障排查

    手头有个小型主机 运行centos 发现工作很不稳定 经常启动不起来 就算启动起来也会在几分钟内出现各种IO错误 可能出现以下几种报错 1 只读文件系统 Read only file system 尝试对磁盘写入的时候可能出现这个错误 2
  • Linux·进程权限控制

    Linux系统的安全性得益于其进程权限和文件权限的控制机制 今天抽空梳理下Linux下的进程权限控制相关的文件权限涉及一点 首先明确四个名词 真实用户ID real ID 有效用户ID effective ID 保存用户ID Saved I
  • Linux字符设备驱动的register_chrdev()与unregister_chrdev()

    Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合 通过这些函数使得Windows的设备操作犹如文件一般 在应用程序看来 硬件设备只是一个设备文件 应用程序可以象操作普通文件一样对硬件设备进行操作 如open close rea
  • <Linux开发>驱动开发 -之- Linux LCD 驱动

    Linux开发 驱动开发 之 Linux LCD 驱动 交叉编译环境搭建 Linux开发 linux开发工具 之 交叉编译环境搭建 uboot移植可参考以下 Linux开发 之 系统移植 uboot移植过程详细记录 第一部分 Linux开发
  • linux应用程序直接return与exit的区别

    在Linux应用程序中 可以使用 return 语句直接从 main 函数返回 这将导致程序终止并返回给操作系统 然而 有时候使用 exit 函数比直接使用 return 语句更有优势 以下是一些原因 清理资源 exit 函数可以确保在程序
  • OpenHarmony基于HDF简单驱动开发实例

    背景 OpenHarmony 3 0 LTS qemu small system demo liteos a qemu 添加配置 device qemu arm virt liteos a hdf config device info de

随机推荐

  • eclipse导入项目会有红叉叉

    eclipse导入项目会有红叉叉 我这边以导入的是SpringMVC项目为例 下面有很多图片 eclipse导入项目后 jsp页面都是红叉叉 注意 有一些情况有红叉叉的情况 是因为字符集 要把eclipse默认字符集设为UTF 8 第一步
  • 网络安全攻防对抗之白加黑技术

    目录 一 什么是白加黑技术 二 怎么防范白加黑技术 一 什么是白加黑技术 白加黑 手法是一种利用DLL劫持技术的攻击方式 它通过在应用程序的导出目录中创建一个DLL文件 并通过LoadLibrary函数 或者找一个已有的DLL注入恶意代码
  • 多元时间序列

    多元时间序列 BiLSTM双向长短期记忆神经网络多变量时间序列预测 Matlab完整程序 目录 多元时间序列 BiLSTM双向长短期记忆神经网络多变量时间序列预测 Matlab完整程序 预测结果 评价指标 基本介绍 程序设计 参考资料 预测
  • [YAPI]导出API文档

    1 进入某个分组主页 切换到 项目列表 菜单下 点击进入其中一个项目 2 切换到 数据管理 菜单下 执行导出功能
  • 肾模?你还不会sqlalchemy!【SQLite】

    Part1什么是 sqlalchemy sqlalchemy 是 Python 的一个优秀的开源 ORM 框架 为开发者提供了方便快捷的 API 能够提高开发效率 让开发者专心于业务代码开发 而非浪费时间在数据库的维护上 今天我们就来一起了
  • 基于java的医院住院管理系统

    为了更好的满足医护人员用户的需求 本医院住院管理系统包括如下功能模块 出入院管理 病人管理 病房管理 系统用户管理模块 每个模块都有其独自的功能 以达到更好的服务于用户 系统采用BS结构 用当前最流行的java技术开发 系统架构采用MVC模
  • vue2+elementui表单手机号码、邮箱、经纬度、百分比验证

    1 手机号码验证 验证方式写在一个公共js文件中 在对应组件中引入即可 方法一 需传参 n为是否必填参数 export const isPhone n gt return required n message 不能为空 trigger bl
  • 封装、继承、多态

    目录 访问限定符 封装 继承 super关键字 super和this的比较 继承在内存中的情况 重写override 重写和重载的比较 多态 JAVA中的动态绑定机制 JAVA面向对象程序三大特性 封装 继承 多态 在类和对象阶段 主要研究
  • iTerm2配置(rz/sz命令)

    1 安装iTerm2 到iTerm2官网下载安装 2 安装HomeBrew 参考博客 https brew idayer com guide start 作者写的非常细 但我照着执行下来有2个注意事项 1 别急忙执行安装命令 先得去镜像助手
  • UNC博士计算机申请,科学网-申请美国博士研究生的自我陈述(Personal Statement)该怎么写?-周耀旗的博文...

    申请美国大学的博士研究生除了考试成绩 简历之外 常常还要写一篇长短要求不一的 自我陈述 Personal Statement 或者 目的陈述 Statement of purpose 我在美国大学做老师的时候 曾经多次参与招生委员会的工作
  • 方差,协方差、标准差,与其意义

    有国才有家 支持国产 生活中点滴做起 买手机就买华为 这是我们国家IT界的脊梁 协方差的意义和计算公式 协方差的意义和计算公式 学过概率统计的孩子都知道 统计里最基本的概念就是样本的均值 方差 或者再加个标准差 首先我们给你一个含有n个样本
  • 关于手机Camera的硬件电路知识

    前阶段 小白教同事测了些Camere的基本功耗 正愁不知道写什么的小白 突然想到了素材 于是乎便趁着周末雷雨天宅家之际 写一篇关于手机Camere的硬件文章 手机Camera 一 工作原理 关于Camera 景物通过镜头生成光学图像投射到图
  • MNIST手写体识别训练过程数据流图

    1 数据流图 2 对应的 Python 代码 import torch import torchvision import torchvision transforms as transforms import torch nn as nn
  • 嵌入式数据库知识概括

    嵌入式数据库知识概况 嵌入式数据库 Derby SQLite H2 总结 嵌入式数据库 嵌入式数据库 Embedded Database 简介 从软件角度来说 数据库分类为两种 第一种 数据库服务器 Database Server 第二种
  • Unity控制在面板上显示变量

    unity之定制Inspector Ngui 刚接触Unity时知道脚本的共有变量会在Inspector中显示出来 但是有一些确不行 前些日子添加按钮的音效 我用的是Ngui按钮也基本是UIButton 但是给button一个个挂UIPla
  • Maven配置环境变量

    在上文java在Windows配置Path环境变量 中我们找到了环境变量所在位置我们直接打开环境变量 第一步 在环境变量页面点击新建 第二步 配置MAVEN HOME 在变量名中输入 MAVEN HOME 在变量值中输入jdk安装位置 D
  • C语言qsort函数详解

    目录 一 qsort函数的使用 二 qsort函数的模拟 一 qsort函数的使用 快排函数qsort是C的库函数 它可以对输入的任何类型的数组排序 通过该函数的函数声明我们可以看出它的使用方法 举个栗子 include
  • 还不会部署高可用的kubernetes集群?企业DevOps实践之使用kubeadm方式安装高可用k8s集群v1.23.7...

    关注 WeiyiGeek 公众号 设为 特别关注 每天带你玩转网络安全运维 应用开发 物联网IOT学习 原文地址 还不会部署高可用的kubernetes集群 企业DevOps实践之使用kubeadm方式安装高可用k8s集群v1 23 7 本
  • Qt学习笔记1:创建一个QT的空项目

    初始QT 在创建QT项目时系统提供了几个不同的模板 点选模板 系统会自动为用户创建好一个基础框架方便开发 这里 我们试着不适用系统提供的基础框架 自己创建一个空的QT项目 创建工程 1 进入QT界面 选择新建工程 在跳出的选项中选择其他项目
  • Linux·i2c驱动示例

    I2C 是很常用的一个串行通信接口 常用于连接各种外设 传感器等器件 一 Linux I2C 驱动框架 Linux 内核将 I2C 驱动分为两部分 I2C 总线驱动 I2C 总线驱动就是 SOC 的 I2C 控制器驱动 也叫做 I2C 适配