Linux内核之I2C协议
I2C协议标准文档
THE I2C-BUS SPECIFICATION VERSION 2.1 JANUARY 2000: https://www.csd.uoc.gr/~hy428/reading/i2c_spec.pdf
I2C全称Inter-IC,又写作IIC,有些又归类为TWI(Two-Wire Interface).
电路原理
IIC仅由SDA数据线、SCL时钟线构成。并且两根线都需要接上拉电阻,原因是采用了OD门。
OD门(Open Drain漏极开路)的作用:
适用于 输出\输入
单独使用时,可独立输出或输入低电平和高阻态(可理解为开路)
- 我们只需要给一个上拉电阻产生高电平,在高阻态/开路的作用下,线路电平就会等同于上拉电阻的高电平;
- 在低电平的作用下,线路电平仍然是低电平;
所以接上拉电阻,可以让OD门有 输入\输出 高低电平 的功能,可以使用半双工,这就是通信的物理电路基础。
高电平一般有1.8V 3.3V 5V 三种。
上拉电阻阻值(3.3K~10K)与速度和容性负载相关,可以决定稳定性。
而且标准文档还提供了不同电平之间的电路兼容方案
连接方式
所有从机SDA都并联到主机上,所有从机SCK线都并联到主机SCL上。
时序图
关键点:
空闲状态:SCL/SDA都是高电平。
工作状态:SCL脉冲在高电平状态下指示SDA有效。
经典的开始和结束信号
SDA下降沿表示开始,上升沿表示结束
多机冲突与通讯
有低电平的话,同一线上的都会被拉低。
接到同一SCL线的都会被同步
对端SDA的可判断电平不一致而打断操作。
如果需要FPGA实现,还需要留意标准文档里的Table5和Fig.31的时间间隔要求。
主要传输形式
速度: 0100KHz400KHz~3.4MHz
不同速度模式的设备混合接入总线系统,速度如表:
速度是需要“协商”的,如启用 High Speed Mode
保留地址作为管理码(master code),详见标准手册 10.1 Definition of bits in the first byte
开始条件S 8位管理码(00001xxx) 一位NACK
普通速率下传输数据(7地址模式)
其数据可抽象简化为
DEV_ADDR
BYTE_DATA
[..BYTE_DATA]
发送设备地址(7位+R/W标识位) 另外也有10位地址的,就是两个地址字而已,用得少可以看标准文档。
发送数据(8位一次)
可选继续发送数据(每次也都是8位)
每8位都需要从机回复一个A/A应答位。
也就说,它是基于字节传输的超短距简单低速协议。
一般来说,I2C最适合用于读写寄存器,其数据形态与可以现有计算
机/微机体系匹配,符合8/16/32/64位等以字节整倍数的寄存器。
一个写入寄存器的例子(16位寄存器地址,32位寄存器数据): DEV_ADDR
REG_ADDR0
REG_ADDR1
REG_DATA0
REG_DATA1
REG_DATA2
REG_DATA3
Tips: 基于字节传输的协议都可以替代串口协议的部分应用场景。
测试工具
Linux上一般使用i2c-tools这个包的工具
i2cdetect 用于扫描总线上挂接的从机设备地址。注意:非标设备不回复,则扫描不到。i2cdetect -y -r 0
代表确认并以Read扫描一次IIC总线0下的所有非保留地址。把-r换成-q则表示以QUICK写模式扫描。
i2cdump 用于读取某个i2c从机设备的所有寄存器数据。
小技巧:当i2cdetect扫描不到的时候,可以用循环i2cdump抓出所有的从机设备数据,只要有数据的,说明该地址就有设备。
关于i2cdetect不回复的问题排查,可以查看i2ctools源码: https://github.com/mozilla-b2g/i2c-tools/blob/master/tools/i2cdetect.c#L50
其实就是从机要支持主机发送的 SMBUG_READ 指令
有个案例:https://bbs.aw-ol.com/topic/2304/分析笔记-linux-i2c-tools-使用踩坑笔记/2
变种
I2C的变种有 SMBUS MDIO I3C MIPI系等等协议。
本质上都是1时钟线+1数据线,OD门实现双向高低电平。可并联。
Linux内核I2C&SMBUS子系统 API文档
see: https://www.kernel.org/doc/html/v5.14/driver-api/i2c.html
文档路径 » The Linux driver implementer’s API guide » I2C and SMBus Subsystem
SMBUS是I2C的兄弟协议,大部分SMBUS也是I2C,并且电气规定上比I2C更严格。
(SoC)I2C控制器收发的内核API
// 普通版本
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
// 带DMA控制器的版本
int i2c_master_send_dmasafe(const struct i2c_client *client, const char *buf, int count)
int i2c_master_recv_dmasafe(const struct i2c_client *client, char *buf, int count)
一些重要的结构体
架构分层
贴张经典老图,出处找不到了,侵权请联系
APP
/dev/i2c-x (device文件节点)
设备驱动driver
i2c-Core
I2C_Adapter (控制器)
控制器一般是这样的:一般由I2C控制器的IP设计服务商给到SoC厂商,SoC厂商在处理器微电路设计适配后,仅放出寄存器给用户。所以我们用户只需要根据SoC数据手册对SoC寄存器操作即可。SoC会根据寄存器内容对控制器进行微电路操作的。
SOC厂家的对接代码
硬件上对应:
n个SOC片上控制器 - Adapter
n个从机硬件设备 - 一个Driver+多个同系列型号Device
Adapter结构体里还包含了传输算法algo、独占操作lock_ops、适配器对应的device及其name、下挂的userspace_clients,总线恢复操作bus_rec、特性quirks、中断irq_domain等等。
传输算法algo里面其实就是i2c和smbus的传输函数指针而已。
//设备识别
struct i2c_device_identity {
u16 manufacturer_id; //0 - 4095, database maintained by NXP
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS 0;
//...
#define I2C_DEVICE_ID_ATMEL 13;
#define I2C_DEVICE_ID_NONE 0xffff;
u16 part_id;
u8 die_revision;
};
//板载设备信息模板 template for device creation
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
const char *dev_name;
void *platform_data;
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct software_node *swnode;
const struct resource *resources;
unsigned int num_resources;
int irq;
};
//时序控制
struct i2c_timings {
u32 bus_freq_hz;
u32 scl_rise_ns;
u32 scl_fall_ns;
u32 scl_int_delay_ns;
u32 sda_fall_ns;
u32 sda_hold_ns;
u32 digital_filter_width_ns;
u32 analog_filter_cutoff_freq_hz;
};
总线API
//锁,实现独占操作
void i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
int i2c_trylock_bus(struct i2c_adapter *adapter, unsigned int flags)
void i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
电源操作
void i2c_mark_adapter_suspended(struct i2c_adapter *adap)
void i2c_mark_adapter_resumed(struct i2c_adapter *adap)
特性检测
bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
初始化/卸载操作
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
struct i2c_client * i2c_verify_client(struct device *dev)
struct i2c_client * i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
void i2c_unregister_device(struct i2c_client *client)
//dummy是虚拟设备
struct i2c_client * i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address)
struct i2c_client * devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adapter, u16 address)
struct i2c_client * i2c_new_ancillary_device(struct i2c_client *client, const char *name, u16 default_addr)
struct i2c_adapter * i2c_verify_adapter(struct device *dev)
int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
int i2c_add_adapter(struct i2c_adapter *adapter)
void i2c_del_adapter(struct i2c_adapter *adap)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter)
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
void i2c_del_driver(struct i2c_driver *driver)
向从机读写数据
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, int count, u16 flags)
int i2c_get_device_id(const struct i2c_client *client, struct i2c_device_identity *id)
u8* i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold)
void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred)
u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
s32 i2c_smbus_read_byte(const struct i2c_client *client)
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value)
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values)
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values)
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data)
s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, u8 command, u8 length, u8 *values)
struct i2c_client * i2c_new_smbus_alert_device(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup)
Linux内核I2C&SMBUS子系统架构
从机设备驱动部分
设备驱动编写:https://docs.kernel.org/i2c/writing-clients.html (较简单)
I2C SysFs的:https://docs.kernel.org/i2c/i2c-sysfs.html
用法:https://docs.kernel.org/i2c/instantiating-devices.html
有以下几种为Linux内核创建I2C设备的方法(任选其一即可):
结构信息
- 设备树
- ACPI(可以理解为X86的设备树)
代码调用
- 填充 struct i2c_board_info 结构体并使用i2c_new_client_device()创建
- 调用i2c_new_scanned_device() 等API函数
- 填充 struct i2c_board_info 结构体并使用i2c_new_client_device()创建
驱动里写
- probe(比较常见) 见:lm90_driver and lm90_detect() in drivers/hwmon/lm90.c
- probe(比较常见) 见:lm90_driver and lm90_detect() in drivers/hwmon/lm90.c
用户空间使用sysfs接口
- 例子
echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
或者delete_device
- 例子
核心 I2C-Core
总线部分 BUS主控/片上外设驱动 Adapter
这部分一般是原厂在弄
//i2c.h里的适配器结构体
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED 0
#define I2C_ALF_SUSPEND_REPORTED 1
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
struct irq_domain *host_notify_domain;
struct regulator *bus_regulator;
struct dentry *debugfs;
};
//设备器成员函数-收发接口
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*master_xfer_atomic)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
u32 (*functionality)(struct i2c_adapter *adap);
#if IS_ENABLED(CONFIG_I2C_SLAVE);
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif;
};
//适配器成员函数-锁定解锁操作
struct i2c_lock_operations {
void (*lock_bus)(struct i2c_adapter *adapter, unsigned int flags);
int (*trylock_bus)(struct i2c_adapter *adapter, unsigned int flags);
void (*unlock_bus)(struct i2c_adapter *adapter, unsigned int flags);
};
//适配器成员-特性结构体
struct i2c_adapter_quirks {
u64 flags;
int max_num_msgs;
u16 max_write_len;
u16 max_read_len;
u16 max_comb_1st_msg_len;
u16 max_comb_2nd_msg_len;
};
//适配器成员函数-操作两根线
struct i2c_bus_recovery_info {
int (*recover_bus)(struct i2c_adapter *adap);
int (*get_scl)(struct i2c_adapter *adap);
void (*set_scl)(struct i2c_adapter *adap, int val);
int (*get_sda)(struct i2c_adapter *adap);
void (*set_sda)(struct i2c_adapter *adap, int val);
int (*get_bus_free)(struct i2c_adapter *adap);
void (*prepare_recovery)(struct i2c_adapter *adap);
void (*unprepare_recovery)(struct i2c_adapter *adap);
struct gpio_desc *scl_gpiod;
struct gpio_desc *sda_gpiod;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_gpio;
};
从机设备节点
//从机设备结构体
struct i2c_client {
unsigned short flags;
#define I2C_CLIENT_PEC 0x04 ;
#define I2C_CLIENT_TEN 0x10 ;
#define I2C_CLIENT_SLAVE 0x20 ;
#define I2C_CLIENT_HOST_NOTIFY 0x40 ;
#define I2C_CLIENT_WAKE 0x80 ;
#define I2C_CLIENT_SCCB 0x9000 ;
unsigned short addr;
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter;
struct device dev;
int init_irq;
int irq;
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE);
i2c_slave_cb_t slave_cb;
#endif;
void *devres_group_id;
};
从机设备驱动/板上外设驱动
拿到新的IIC从机设备,首先就是根据其数据手册适配该外设的驱动
//从机设备驱动接口
struct i2c_driver {
unsigned int class;
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
int (*probe_new)(struct i2c_client *client);
void (*shutdown)(struct i2c_client *client);
void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
const unsigned short *address_list;
struct list_head clients;
};
我们要实现i2c_algorithm的transfer接口,才能完成对I2C主控/Soc的驱动适配。
drivers/i2c
除了一些 include/linux/i2c.h之类相关的头文件,剩下的实现都在 drivers/i2c
i2c-core.h
头文件主要是一个i2c_devinfo
结构体和一些static inline静态内联函数定义
struct i2c_devinfo {
struct list_head list;
int busnum;
struct i2c_board_info board_info;
};
i2c-core-base.c
core层的实现,支撑了i2c-core-xxx.c的功能。
i2c-core-of.c
是设备树的实现,主要是match、设备树sysfs导出、设备树属性reg
host-notify
wakeup-source
等支持,注册/反注册设备。
i2c-core-acpi.c
是X86特有的ACPI表(ACPI比ARM设备树更高级强大)
i2c-core-slave.c
是从机设备的核心支持,主要是从机模式选择、事件、注册/反注册的支持。
i2c-core-smbus.c
是SMBus支持。其读写最终会调用__i2c_smbus_xfer
到adapter->algo->smbus_xfer()
xxx有acpi表、base、of设备树、从机slave、SMBUS等类型的注册/反注册。
i2c-atr.c
是I2C Address Translator地址翻译器的缩写,
i2c-boardinfo.c
用于静态声明板载从机I2C设备。这个源码很少,仅有一个函数(加锁,然后把填充i2c_devinfo
并加到i2c_board链表)。
i2c-dev.c
主要是SoC片上i2c总线控制器的驱动实现,即I2C主机设备字符驱动,代表了一个 i2c_adapter
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
struct device dev;
struct cdev cdev;
};
i2c-mux.c
是I2C多路总线驱动(Multiplexed I2C bus driver),用于支持控制器的多路通道设计。
i2c-smbus.c
是其SMBus驱动实现,几乎所有的I2C主控都是同时支持SMBus的。
i2c-stub.c
是I2C/SMBus芯片模拟器
剩下的两个i2c-slave-eeprom.c
和i2c-slave-testuint.c
都是I2C从机模拟器
目录 drivers/i2c/algos
下面是一些通用控制器的algo实现,比较古老了一般用不到,现在都是高度集成到SoC里了。目前实现了移位寄存器类型/PCF8584/PCA9564这几类拓展的适配器。放张PCF8584的框图欣赏一下,FPGA要实现也可以参考这种。
目录drivers/i2c/busses
则较为庞大,是各厂家提供的总线驱动,遵守内核的i2c_adapter模型。SoC厂家可能会使用不同的IP定制,使得自家SoC能采用不同的I2C控制器,但是他们一般对片上外设的控制器是一致的,这个叫做总线控制。例如博通的[drivers/i2c/busses/i2c-bcm2835.c](struct bcm2835_i2c_dev {)
和赛灵思的 drivers/i2c/busses/i2c-xiic.c ,虽然看起来复杂,但里面也基本都是ARM核通过寄存器配置片上外设(I2C控制器),比单片机裸机寄存器编程难,但也不会太难。
这部分只有需要定制自己的SoC时才需要,一般能做SoC的都是原厂。
目录drivers/i2c/muxes
是一些板上外设的分线器/拓展器驱动,例如你SoC线不够用了,可以通过在板子上加个外设如PCA9544
来拓展I2C的,让你1路变4路,多爽啊。
总结
- i2c-core是内核对I2c的核心支持,最主要的就是提供了适配器模型,
- 厂家提供的总线驱动
drivers/i2c/busses
都需要遵循这个适配器模型,而里面的操作基本都是写处理器的寄存器,由处理器最后去操作对应的控制器IP - 板载外设IIC设备的驱动编写则使用内核提供的Client API,见
https://docs.kernel.org/i2c/writing-clients.html
- 板载外设IIC拓展器使用的是
drivers/i2c/muxes
- SMBUS有自己的读写概念,所以额外实现。
I2C例子:
原厂写总线驱动
SoC数据手册:https://docs.amd.com/r/en-US/ug1085-zynq-ultrascale-trm/I2C-Controllers
代码:drivers/i2c/busses/i2c-xiic.c
编写外设设备驱动
基于I2C通信的DAC外设,数据手册:https://www.ti.com/lit/ds/symlink/dac7571.pdf?ts=1714157964385
drivers/iio/dac/ti-dac5571.c
Linux内核之I2C协议的更多相关文章
- Linux内核调用I2C驱动_驱动嵌套驱动方法
禁止转载!!!! Linux内核调用I2C驱动_以MPU6050为例 0. 导语 最近一段时间都在恶补数据结构和C++,加上导师的事情比较多,Linux内核驱动的学习进程总是被阻碍.不过,十一假期终于 ...
- linux内核中i2c驱动中slave模式接口的调用
1. 关注unreg_slave接口 1.1 这个接口在哪里被调用呢? 在drivers/i2c/i2c-core-slave.c中 int i2c_slave_unregister(struct i ...
- linux设备驱动程序-i2c(0)-i2c设备驱动源码实现
(基于4.14内核版本) 为了梳理清楚linux内核中的i2c实现框架,从本文开始,博主将分几个章节分别解析i2c总线在linux内核中的形成过程.匹配过程.以及设备驱动程序源码实现. 在介绍linu ...
- Linux驱动之I2C总线设备以及驱动
[ 导读] 本文通过阅读内核代码,来梳理一下I2C子系统的整体视图.在开发I2C设备驱动程序时,往往缺乏对于系统整体的认识,导致没有一个清晰的思路.所以从高层级来分析一下I2C系统的设计思路,将有助于 ...
- Linux内核的特征
Linux内核的特征 Linux是个人计算机和工作站上的Unix类操作系统.但是,它绝不是简化的Unix.相反,Linux是强有力和具有创新意义的Unix类操作系统.它不仅继承了Unix的特征,而且在 ...
- linux内核I2C子系统学习(三)
写设备驱动: 四部曲: 构建i2c_driver 注册i2c_driver 构建i2c_client ( 第一种方法:注册字符设备驱动.第二种方法:通过板文件的i2c_board_info填充,然后注 ...
- 嵌入式Linux内核I2C子系统详解
1.1 I2C总线知识 1.1.1 I2C总线物理拓扑结构 I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高 ...
- Linux内核中影响tcp三次握手的一些协议配置
在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ...
- Linux内核中SPI/I2c子系统剖析
Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...
- 浅析linux内核中的idr机制
idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制.这个机制最早是在2003年2月加入内核的,当时是作为POSIX定时器的一个补丁.现在, ...
随机推荐
- ChatGPT写作提示词指令大全
1 .用ChatGPT写影评 指令:你是一个自媒体人,同时也是一个专业的影评人.最近熬夜看完了韩剧黑暗荣耀第一季和第二季,忍不住想在公众号分享给粉丝们,请写一篇1000字左右的自媒体文章,并且加上一个 ...
- https://codeforces.com/gym/496137
AB略. C:想复杂了. 只要判断最大的那个能不能继续吃即可. D:我的做法是建完全图然后跑生成树. 实际上可以这么考虑:和a[1]不同的直接连,相同的就和上一轮和a[1]不同的店去连可以O(n). ...
- CSS设置图片根据div的大小等比例缩放
1 .img{ 2 position: absolute; 3 background:url("../images/success.png") no-repeat; 4 width ...
- CTFshow pwn53 wp
PWN53 那么先看保护 虽然没有开canary但是本题在ida打开看见他是模拟了canary的效果,不同的是他的是固定的canary,但是一样要爆破 而且发现还有后门函数 在ctfshow函数我们发 ...
- #分治,决策单调性dp#CF868F Yet Another Minimization Problem
题目 给定一个序列 \(a\),要把它分成 \(k\) 个子段.(\(n\leq 10^5,k\leq 20\)) 每个子段的费用是其中相同元素的对数.求所有子段的费用之和的最小值. 分析 有一个很明 ...
- 开发人员使用HANA交付业务的学习路径
本文于2019年7月22日完成,发布在个人博客网站上. 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来. 入门 编程规范. 开发环境使用方法. 基本语法,与其它同类软件的 ...
- C语言 02 安装
C 语言的编译器有很多,其中最常用的是 GCC,这里以安装 GCC 为例. Windows 这里以 Windows 11 为例 官方下载地址:https://www.mingw-w64.org/ 选择 ...
- Git 12 IDEA上传本地项目到远程
这里以上传 Spring 开源项目到 Gitee 为例: 1.点击 Create Git Repository 2.选择项目目录 3.添加到缓存库 4.提交到本地库 5.复制远程库地址 6.推送到远程 ...
- CabloyJS 4.22重磅推出弹出式页面交互风格
升级说明 我们知道CabloyJS提供了pc=mobile+pad自适应布局机制,可以通过一套代码同时适配mobile端和pc端.基本思路就是优先适配mobile端,然后再把mobile端的交互体验带 ...
- maven 设置阿里镜像[二]
前言 因为我们在国内,网速很慢,所以最好设置一下阿里镜像. 1.在maven中的conf下的setting 修改为: <mirror> <id>alimaven</id& ...