STM32MP157驱动开发——Linux I2C驱动

article/2025/10/25 2:34:34

相关文章:正点原子教程第四十章——Linux I2C驱动实验

0.前言

  为了简化笔记的编写以及降低工作量,本节开始相关的基础知识部分通过引入原子哥的教材链接来完成,有兴趣的可以进入学习。
  上一节学完 RGB LCD 本来想直接学习 RGB 转 HDMI 实验,但是转换芯片需要一个 I2C 引脚来控制芯片功能,所以还是回来先学习 I2C。

一、原理简述

在这里插入图片描述
SCL:串行时钟线
SDA:串行数据线
标准速度:100Kb/秒
快速模式:400Kb/秒
需要上拉电阻(通常4.7k),空闲时均处于高电平。

1.起始位

SCL 为高电平时,SDA 出现下降沿

2.停止位

SCL 为高电平时,SDA 出现上升沿

3.数据传输

SDA 上的数据变化只能在 SCL 低电平期间发生,这样是为了保证 SCL 高电平时 SDA的数据稳定。
在这里插入图片描述

4.应答信号

由从机发出,但时钟由主机提供。从机通过将 SDA 拉低来表示发出应答信号。

5.I2C写时序

起始信号–>发送 I2C 设备地址–>从机发送应答信号–>重新发送起始信号–>发送写入数据的寄存器地址–>从机应答–>发送写入数据–>从机应答–>停止信号
其中 “发送 I2C 设备地址” 数据时,高7位为设备地址,最后一位为 1 :读操作;0:写操作。

6.I2C读时序

起始信号–>发送 I2C 设备地址(最后一位置1)–>从机发送应答信号–>重新发送起始信号–>发送读取数据的寄存器地址–>从机应答
前半部分仍为先写入数据。后半部分有所不同:
–>重新发送起始信号–>重新发送 I2C 从设备地址(最后一位置0)–>从机应答–>在从机的返回数据里读取–>主机发送 NO ACK,完成读取(从机不应答)–>停止信号

二、设备驱动开发

1.不使用设备树

通过 i2c_board_info 结构体来描述设备:

406 struct i2c_board_info {
407 	char type[I2C_NAME_SIZE];
408 	unsigned short flags;
409 	unsigned short addr;
410 	const char *dev_name;
411 	void *platform_data;
412 	struct device_node *of_node;
413 	struct fwnode_handle *fwnode;
414 	const struct property_entry *properties;
415 	const struct resource *resources;
416 	unsigned int num_resources;
417 	int irq;
418 };

其中 type 和 addr 这两个成员变量必须要设置,一个是 I2C 设备的名字,一个是 I2C 设备的器件地址。
例:arch/arm/mach-imx/mach-armadillo5x0.c 文件中有关 I2C 设备 s35390a 的信息:

246 static struct i2c_board_info armadillo5x0_i2c_rtc = {
247 	I2C_BOARD_INFO("s35390a", 0x30),
248 };#define I2C_BOARD_INFO(dev_type, dev_addr) \.type = dev_type, .addr = (dev_addr)

使用 I2C_BOARD_INFO 来完成 armadillo5x0_i2c_rtc 的初始化,I2C_BOARD_INFO 宏设置 i2c_board_info 的 type 和 addr 这两个成员变量。

2.使用设备树

通过在设备树中创建 I2C 节点来实现。在 STM32MP157 开发板上有一个 I2C 器件 AP3216C,以此为例。
原理图:
在这里插入图片描述
AP3216C 使用了 I2C5,I2C5_SCL 使用的是 PA11,I2C_SDA 使用的是 PA12。 AP3216C 还有个中断引脚,这里没有用到。

1.修改设备树

在 stm32mp15-pinctrl.dtsi 中,将 PA11、PA12 复用为 I2C:(ST官方已写好)
在这里插入图片描述

2.追加子节点

在 stm32mp157d-atk.dts 文件中,添加一个 i2c5 节点,并在节点中添加“ap3216c@1e”子节点:

&i2c5 {pinctrl-names = "default", "sleep";pinctrl-0 = <&i2c5_pins_a>;pinctrl-1 = <&i2c5_pins_sleep_a>;status = "okay";ap3216c@1e {compatible = "alientek,ap3216c";reg = <0x1e>;};
};

其中 @ 后面的“1e”是 ap3216c 的器件地址,reg 属性也是设置 ap3216c 器件地址的,因此 reg 设置为 0x1e。
设备树修改完成以后使用“make dtbs”重新编译,然后使用新的设备树启动 Linux 内核,如果设备树修改正确,会在 /sys/bus/i2c/devices 目录下看到一个名为“0-001e”的子目录,进入目录就可以看到名为"name"的文件,name 文件保存着此设备名字。
注意:在开发完 RGB LCD 驱动之后,每次编译设备树需要使用 make uImage dtbs LOADADDR=0xC2000040 -j8命令,不然有可能会报一些奇奇怪怪的Warning。暂时还没发现这些warning会产生什么影响,后续发现了来填上。
在这里插入图片描述

3.驱动编写

I2C 总线使用的是 I2C 子系统,与 platform 平台驱动的开发流程相似,都是将某一类总线驱动统一为某一个系统的驱动框架,减少冗余代码和冗余开发流程。
新建"21_iic"文件夹,在里面创建 ap3216c.c 和 ap3216creg.h 两个文件,ap3216c.c 为 AP3216C 的驱动代码,ap3216creg.h 是 AP3216C 寄存器头文件。先在 ap3216creg.h 中定义好 AP3216C 的寄存器,输入如下内容:

#ifndef AP3216C_H
#define AP3216C_H#define AP3216C_ADDR 0X1E /* AP3216C 器件地址 *//* AP3316C 寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR 数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR 数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS 数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS 数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS 数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS 数据高字节 */#endif

ap3216c.c:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "ap3216creg.h"#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"struct ap3216c_dev {struct i2c_client *client; /* i2c 设备 */dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */struct device_node *nd; /* 设备节点 */unsigned short ir, als, ps; /* 三个光传感器数据 */
};/*读取多个寄存器*/
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = (struct i2c_client *)dev->client;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr; /* ap3216c 地址 */msg[0].flags = 0; /* 标记为发送数据 */msg[0].buf = &reg; /* 读取的首地址 */msg[0].len = 1; /* reg 长度 *//* msg[1]读取数据 */msg[1].addr = client->addr; /* ap3216c 地址 */msg[1].flags = I2C_M_RD; /* 标记为读取数据 */msg[1].buf = val; /* 读取数据缓冲区 */msg[1].len = len; /* 要读取的数据长度 */ret = i2c_transfer(client->adapter, msg, 2);if(ret == 2) {ret = 0;} else {printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}/*向多个寄存器写入数据*/
static s32 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->client;b[0] = reg; /* 寄存器首地址 */memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组 b 里面 */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 的数据,包括 ALS,PS 和 IR, 注意!如果同时
* 打开 ALS,IR+PS 两次数据读取的时间间隔要大于 112.5ms
*/
void ap3216c_readdata(struct ap3216c_dev *dev)
{unsigned char i =0;unsigned char buf[6];/* 循环读取所有传感器数据 */for(i = 0; i < 6; i++) {buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);}if(buf[0] & 0X80) /* IR_OF 位为 1,则数据无效 */dev->ir = 0;else /* 读取 IR 传感器的数据 */dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);dev->als = ((unsigned short)buf[3] << 8) | buf[2];if(buf[4] & 0x40) /* IR_OF 位为 1,则数据无效 */dev->ps = 0;else /* 读取 PS 传感器的数据 */dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}/*打开设备*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{
/* 从 file 结构体获取 cdev 指针, 再根据 cdev 获取 ap3216c_dev 首地址 */struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;struct ap3216c_dev *ap3216cdev = container_of(cdev, struct ap3216c_dev, cdev);/* 初始化 AP3216C */ap3216c_write_reg(ap3216cdev, AP3216C_SYSTEMCONG, 0x04);mdelay(50);ap3216c_write_reg(ap3216cdev, AP3216C_SYSTEMCONG, 0X03);return 0;
}/*从设备读取数据*/
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{short data[3];long err = 0;struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;struct ap3216c_dev *dev = container_of(cdev, struct ap3216c_dev, cdev);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 int ap3216c_release(struct inode *inode, struct file *filp)
{return 0;
}/* AP3216C 操作函数 */
static const struct file_operations ap3216c_ops = {.owner = THIS_MODULE,.open = ap3216c_open,.read = ap3216c_read,.release = ap3216c_release,
};/*probe函数*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret;struct ap3216c_dev *ap3216cdev;ap3216cdev = devm_kzalloc(&client->dev, sizeof(*ap3216cdev), GFP_KERNEL);if(!ap3216cdev)return -ENOMEM;/* 注册字符设备驱动 *//* 1、创建设备号 */ret = alloc_chrdev_region(&ap3216cdev->devid, 0, AP3216C_CNT, AP3216C_NAME);if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", AP3216C_NAME, ret);return -ENOMEM;}/* 2、初始化 cdev */ap3216cdev->cdev.owner = THIS_MODULE;cdev_init(&ap3216cdev->cdev, &ap3216c_ops);/* 3、添加一个 cdev */ret = cdev_add(&ap3216cdev->cdev, ap3216cdev->devid, AP3216C_CNT);if(ret < 0) {goto del_unregister;}/* 4、创建类 */ap3216cdev->class = class_create(THIS_MODULE, AP3216C_NAME);if (IS_ERR(ap3216cdev->class)) {goto del_cdev;}/* 5、创建设备 */ap3216cdev->device = device_create(ap3216cdev->class, NULL, ap3216cdev->devid, NULL, AP3216C_NAME);if (IS_ERR(ap3216cdev->device)) {goto destroy_class;}ap3216cdev->client = client;/* 保存 ap3216cdev 结构体 */i2c_set_clientdata(client,ap3216cdev);return 0;
destroy_class:device_destroy(ap3216cdev->class, ap3216cdev->devid);
del_cdev:cdev_del(&ap3216cdev->cdev);
del_unregister:unregister_chrdev_region(ap3216cdev->devid, AP3216C_CNT);return -EIO;
}/*i2c 驱动的 remove 函数*/
static int ap3216c_remove(struct i2c_client *client)
{struct ap3216c_dev *ap3216cdev = i2c_get_clientdata(client);/* 注销字符设备驱动 *//* 1、删除 cdev */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 const struct i2c_device_id ap3216c_id[] = {{"alientek,ap3216c", 0},{}
};/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "alientek,ap3216c" },{ /* Sentinel */ }
};/* i2c 驱动结构体 */
static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.owner = THIS_MODULE,.name = "ap3216c",.of_match_table = ap3216c_of_match,},.id_table = ap3216c_id,
};/*驱动入口函数*/
static int __init ap3216c_init(void)
{int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}/*驱动出口函数*/
static void __exit ap3216c_exit(void)
{i2c_del_driver(&ap3216c_driver);
}/* module_i2c_driver(ap3216c_driver) */
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("amonter");
MODULE_INFO(intree, "Y");

①自定义一个 ap3216c_dev 结构体,其中的client 成员变量用来存储从设备树提供的 i2c_client 结构体,ir、als 和 ps 分别存储 AP3216C 的 IR、ALS 和 PS数据
②ap3216c_read_regs 函数实现多字节读取,但是 AP3216C 好像不支持连续多字节读取,此函数在测试其他 I2C 设备的时候可以实现多给字节连续读取,但是在 AP3216C上不能连续读取多个字节, 不过读取一个字节没有问题的
③ap3216c_write_regs 函数实现连续多字节写操作
④ap3216c_read_reg 函数用于读取 AP3216C 的指定寄存器数据,用于一个寄存器的数据读取
⑤ap3216c_write_reg 函数用于向 AP3216C 的指定寄存器写入数据,用于一个寄存器的数据写操作
⑥ap3216c_readdata 读取 AP3216C 的 PS、 ALS 和 IR 等传感器原始数据值
⑦open、read、release、probe等就是标准的 iic 设备驱动框架,是基于字符驱动的一层封装。
ap3216c_dev 结构体里有一个 cdev 的变量成员,open函数中使用filp->f_path.dentry->d_inode->i_cdev获取这个成员变量的地址,再用 container_of 获取ap3216c_dev的首地址。
probe函数中i2c_set_clientdata 函数将 ap3216cdev 变量的地址绑定到 client,进行绑定之后,可以通过 i2c_get_clientdata 来获取 ap3216cdev 变量指针。
remove函数调用 i2c_get_clientdata 函数来得到 ap3216cdev 变量的地址,后面执行的一系列卸载、注销操作。

测试App:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
/** @description : main 主程序* @param - argc : argv 数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;char *filename;unsigned short databuf[3];unsigned short ir, als, ps;int ret = 0;if (argc != 2){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0){printf("can't open file %s\r\n", filename);return -1;}while (1){ret = read(fd, databuf, sizeof(databuf));if (ret == 0){                     /* 数据读取成功 */ir = databuf[0];  /* ir 传感器数据 */als = databuf[1]; /* als 传感器数据 */ps = databuf[2];  /* ps 传感器数据 */printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);}usleep(200000); /*100ms */}close(fd); /* 关闭文件 */return 0;
}

在 while 循环中不断的读取 AP3216C 的设备文件,从而得到 ir、 als 和 ps 这三个数据值,并输出到终端。


http://chatgpt.dhexx.cn/article/TSpL1V6l.shtml

相关文章

SPI转can芯片mcp2515

开发环境 CPU&#xff1a;RK3399 ARCH: aarch64 KERNEL&#xff1a;Linux4.4 OS&#xff1a;ubuntu18.04 mcp2515芯片相关信息 CAN、SPI接口控制电路图 修改设备树文件 文件路径&#xff1a;kernel/arm64/boot/dts/rockchip/rk3399.dts 增加spi节点&#xff0c;具体增加那…

MSP432 TFTLCD ILI9481 软件SPI

下面是我用的LCD屏的图片 CS : PF1 RS : PF2 RST: PF3 MOSI: PL4 SCK: PL5 代码&#xff1a; SPI.h #ifndef _SPI_…

MPC5744P-SPI

1.结构 5744的SPI模块支持全双工三线同步传输&#xff0c;可运行在主机或从机模式&#xff0c;分别含有深度为5的FIFO发送和接收缓存区。其结构如下图。SPI配置允许模块发送和接收串行数据&#xff0c;同时也支持带FIFO缓存区的的进行扩展队列操作的数据传输。模块接收和发送的…

SPI协议、MCP2515裸机驱动详解----主流SPI总线接口原理

最近看到一个介绍SPI接口原理的帖子&#xff0c;看完觉得甚好。特来分析给大家一起学习。 SPI概述 Serial Peripheral interface 通用串行外围设备接口 是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM&#xff0c;FLASH&#xff0c;实时时钟&#…

MP2451 使能脚电阻判断

MP2451使能脚(EN)内部接一个稳压管&#xff0c;防止EN所接电压过大。 EN输入的电压范围&#xff1a;从PDF文档中找到EN的开启电压和最高电压。 Enable up Threshold 1.4-1.7 因为计算时需要考虑其极端现象&#xff0c;所以开启电压的临界电压选择最高&#xff0c;1.7v。最高…

CSM3416SF兼容MP2451,MCP16301HT-E,LT1933ES6,AOZ1282CI

CSM3416SF是一颗高耐压DC-DC降压芯片&#xff0c;宽电压范围输入&#xff0c;完美兼容MP2451,MCP16301HT-E,LT1933ES6,AOZ1282CI&#xff0c;助力智能电表市场&#xff0c;赋能车库门驱动系统。

S32K144开发笔记5 - SPI驱动MCP2515

1、接线图 PTB13 — INT 接收数据中断引脚 PTB14 — CLK 时钟 PTB15 — MISO 接收 PTB16 — MOSI 发送 PTB17 — CSN 片选 2、软件SPI 2.1、GPIO口配置 鼠标放在如下位置右击选择Pin Functional Properities&#xff0c;进入引脚属性配置 PTB13引脚配置如下&#xff1a; PT…

nRF52笔记(26)QSPI接口液晶显示屏

1 平台条件 硬件&#xff1a;nrf52840 软件&#xff1a;sdk17.0 2 QSPI概述 QSPI 外设支持使用 SPI 与外部闪存设备进行通信 此处列出了 QSPI 外设的主要特性&#xff1a; • 单/双/四通道 SPI 输入/输出 • 2–32 MHz 可配置时钟频率 • 从/到外部闪存的单字读/写访问 • …

MPC5744-LINFlexD

目录 一、基本介绍1.功能2.时钟源3.外设控制器4.中断向量5.基地址 二、寄存器介绍1.LIN控制寄存器1 LINFlexD_LINCR12.LIN中断使能寄存器LINFlexD_LINIER3.LIN状态寄存器LINFlexD_LINSR4.LIN错误状态寄存器LINFlexD_LINESR5.UART控制寄存器LINFlexD_UARTCR6.UART状态寄存器LINF…

MP2456的芯片的学习

本章将讲述MP2456的特征、性能、参数、应用电路、以及使用时的注意事项。小白总结&#xff0c;如有错误&#xff0c;请大神指教。 目录 一、MP2456的特征 二、MP2456的性能和参数 四、MP2456使用时的注意事项 五、名词解释 一、MP2456的特征 &#xff08;1&#xff09;MP…

硬件电路-MP2451组成的电压反转/极性反转电路设计

板上要集成一个18V供电的模拟信号处理电路&#xff0c;包括线圈驱动、小信号拾取、滤波、二级放大等部分。因此&#xff0c;需要板上提供18V电源。正负电压需要分开控制&#xff0c;因为正电压需要兼作485传感器供电&#xff0c;此时关闭负电压部分节省耗电以及保护模拟端。 b…

MP2451的应用电路

电阻R32和电阻R23是怎么实现的电压&#xff0c;FB口的输出是0.8V。 正确的计算应该是&#xff1a; 0.8/10*(1027)这样就是输出的电压。 SS14是大电流二极管&#xff0c;可以用5819完全替代。还有续流二极管。 SS14是40V的耐压&#xff0c;SS12是20V的耐压。 8050可以替代BC8…

MP2451问题记录

数据手册 https://pan.baidu.com/s/1ggJs0y3 MP2451应用电路如下图 在我自己的应用电路中R1120K&#xff0c;R224k 问题一、 电源输出0.42V V(FB)手册中应等于0.8V&#xff0c;但在测试中发现该引脚电压仅为0.07V 0.07 * (12024) / 24 0.42V 判断是芯片损坏 问题二、FB引脚输…

MP2451的BUCK电路

5V的稳压模块经常会用得到&#xff0c;普通的AMS1117-5.0虽然电压输出非常稳定&#xff0c;但是只适合低压降&#xff0c;小电流的场合。笔者曾经用AMS1117-5.0把8.4V稳压到5V,电流160mA&#xff08;8.4V端&#xff09;&#xff0c;芯片一会儿就非常烫手了&#xff0c;即使并联…

MP2451使用注意事项

MP2451使用注意事项 MP2451是一个高速2M的高效的降压芯片&#xff0c;提供0.6A的电流输出&#xff0c;输入电压范围3.3V-36V可调&#xff0c;有关闭芯片引脚&#xff0c;关闭电流3uA&#xff0c;用起来还不错&#xff0c;分享一下经验&#xff0c;如下原理图已经制成了板子&am…

类间关系和内部类和数组

Final关键词 定义Pepole类&#xff0c;运用了final修饰方法eat()&#xff0c;该方法不能被改写&#xff0c;但可以随类进行继承。 用final修饰的类&#xff0c;不能有子类。 内部成员类定义方式 外部类.成员类 对象名 new 外部类&#xff08;&#xff09;.new 内部类。 局部…

UML六种关系图示(过目不忘版)

图中&#xff0c;从左到右分别是依赖、关联、聚合、组合、实现、继承六种关系在UML图中的画法&#xff0c;有箭头&#xff0c;有菱形&#xff0c;有三角&#xff0c;他们为什么这么画呢&#xff0c;之间有什么联系呢&#xff0c;这要从全局来看。 首先&#xff0c;依赖关联关系…

类图有哪些关系?

此刻是最佳时间&#xff0c;抓住当下&#xff01;最近在学习设计模式的时候&#xff0c;时常要用到类图及它们的关联&#xff0c;所以学习了UML类图及类图之间的关系&#xff0c;转载记录一下。 首先我们需要知道什么是类图。 类图&#xff08;ClassDiagram&#xff09;是UML图…

UML中的六种关系

UML关系 1、依赖关系 是一种使用关系&#xff0c;它是对象之间耦合度最弱的一种关联方式&#xff0c;是临时性的关联。 在代码中&#xff0c;某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类&#xff08;被依赖类&#xff09;中的某些方法来完成一…

UML的六大关系

关联关系 单向关联 双向关联 自关联 聚合关系 聚合关系是关联关系的一种&#xff0c;是强关联关系&#xff0c;是整体和部分之间的关系 聚合关系通过成员对象来实现的&#xff0c;其中成员对象是整体对象的一部分&#xff0c;但是成员对象可以脱离整体对象而独立存在。例如&…