123.rk3399 uboot(2017.09) 源码分析3(2024-09-12)

news2024/11/24 20:02:06

啃了几天initf_dm,发现啃不动啊,但是后面的函数比这个简单,要不先把简单的做了吧。

本文接上一篇https://blog.csdn.net/zhaozhi0810/article/details/142050827

一、c的入口 board_init_f

1.3.12 arch_cpu_init_dm(common/board_f.c)

 空函数返回

1.3.13 board_early_init_f

CONFIG_BOARD_EARLY_INIT_F 配置未被选中。不执行。

1.3.14 get_clocks

配置没有选中,不执行

CONFIG_PPC  对应power pc 架构,rk3399是armv8 架构

CONFIG_M68K 对应M68K架构,不符合

1.3.15 timer_init

配置没有选中,不执行

1.3.16 board_postclk_init 

配置没有选中,不执行

1.3.17 env_init  (env/env.c)

环境变量的初始化,主要是gd->env_addr和gd->env_valid 被分别赋值。

uboot.map中,实际没有设置环境变量存储位置,CONFIG_ENV_IS_NOWHERE是被定义的。

使用的是nowhere驱动。对应的是nowhere.c文件。 

int env_init(void)
{
	struct env_driver *drv = env_driver_lookup_default();  //实际返回空
	int ret = -ENOENT;

	if (!drv)
		return -ENODEV;
	if (drv->init)
		ret = drv->init();
	if (ret == -ENOENT) {
		gd->env_addr = (ulong)&default_environment[0];  //使用默认环境变量
		gd->env_valid = ENV_VALID;

		return 0;
	} else if (ret) {
		debug("%s: Environment failed to init (err=%d)\n", __func__,
		      ret);
		return ret;
	}

	return 0;
}

nowhere.c

它使用的就是default_environment 默认环境变量

调用init函数之后

gd->env_addr和gd->env_valid 被分别赋值

DECLARE_GLOBAL_DATA_PTR;

/*
 * Because we only ever have the default environment available we must mark
 * it as invalid.
 */
static int env_nowhere_init(void)
{
	gd->env_addr	= (ulong)&default_environment[0];
	gd->env_valid	= ENV_INVALID;

	return 0;
}

U_BOOT_ENV_LOCATION(nowhere) = {
	.location	= ENVL_NOWHERE,
	.init		= env_nowhere_init,
	ENV_NAME("nowhere")
};

这里可以看到,默认环境变量都是通过配置设置的,(env_default.h)

CONFIG开头的都是配置部分。

const uchar default_environment[] = {

#ifdef    CONFIG_ENV_CALLBACK_LIST_DEFAULT
    ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif
#ifdef    CONFIG_ENV_FLAGS_LIST_DEFAULT
    ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
#endif
#ifdef    CONFIG_USE_BOOTARGS
    "bootargs="    CONFIG_BOOTARGS            "\0"
#endif
#ifdef    CONFIG_BOOTCOMMAND
    "bootcmd="    CONFIG_BOOTCOMMAND        "\0"
#endif
#ifdef    CONFIG_RAMBOOTCOMMAND
    "ramboot="    CONFIG_RAMBOOTCOMMAND        "\0"
#endif
#ifdef    CONFIG_NFSBOOTCOMMAND
    "nfsboot="    CONFIG_NFSBOOTCOMMAND        "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    "bootdelay="    __stringify(CONFIG_BOOTDELAY)    "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
    "baudrate="    __stringify(CONFIG_BAUDRATE)    "\0"
#endif
#ifdef    CONFIG_LOADS_ECHO
    "loads_echo="    __stringify(CONFIG_LOADS_ECHO)    "\0"
#endif
#ifdef    CONFIG_ETHPRIME
    "ethprime="    CONFIG_ETHPRIME            "\0"
#endif
#ifdef    CONFIG_IPADDR
    "ipaddr="    __stringify(CONFIG_IPADDR)    "\0"
#endif
#ifdef    CONFIG_SERVERIP
    "serverip="    __stringify(CONFIG_SERVERIP)    "\0"
#endif
#ifdef    CONFIG_SYS_AUTOLOAD
    "autoload="    CONFIG_SYS_AUTOLOAD        "\0"
#endif
#ifdef    CONFIG_PREBOOT
    "preboot="    CONFIG_PREBOOT            "\0"
#endif
#ifdef    CONFIG_ROOTPATH
    "rootpath="    CONFIG_ROOTPATH            "\0"
#endif
#ifdef    CONFIG_GATEWAYIP
    "gatewayip="    __stringify(CONFIG_GATEWAYIP)    "\0"
#endif
#ifdef    CONFIG_NETMASK
    "netmask="    __stringify(CONFIG_NETMASK)    "\0"
#endif
#ifdef    CONFIG_HOSTNAME
    "hostname="    __stringify(CONFIG_HOSTNAME)    "\0"
#endif
#ifdef    CONFIG_BOOTFILE
    "bootfile="    CONFIG_BOOTFILE            "\0"
#endif
#ifdef    CONFIG_LOADADDR
    "loadaddr="    __stringify(CONFIG_LOADADDR)    "\0"
#endif
#ifdef    CONFIG_CLOCKS_IN_MHZ
    "clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
    "pcidelay="    __stringify(CONFIG_PCI_BOOTDELAY)"\0"
#endif
#ifdef    CONFIG_ENV_VARS_UBOOT_CONFIG
    "arch="        CONFIG_SYS_ARCH            "\0"
#ifdef CONFIG_SYS_CPU
    "cpu="        CONFIG_SYS_CPU            "\0"
#endif
#ifdef CONFIG_SYS_BOARD
    "board="    CONFIG_SYS_BOARD        "\0"
    "board_name="    CONFIG_SYS_BOARD        "\0"
#endif
#ifdef CONFIG_SYS_VENDOR
    "vendor="    CONFIG_SYS_VENDOR        "\0"
#endif
#ifdef CONFIG_SYS_SOC
    "soc="        CONFIG_SYS_SOC            "\0"
#endif
#ifdef CONFIG_SILENT_CONSOLE
    "silent=enable\0"
#endif
#endif
#ifdef    CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
#endif
    "\0"
#ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
    }
#endif
};
 

rk3399一般都是使用emmc作为存储介质,它也按照一定的方式进行了分区操作。

如果要保存到emmc的话,估计还要对分区进行调整。

所以现在看到的都是环境变量可以通过配置uboot来完成,而实际使用过程中就不能修改了。

除非重新配置并编译uboot。

1.3.18 init_baud_rate(common/board_f.c)

gd->baudrate 赋值波特率。

static int init_baud_rate(void)
{
	if (gd && gd->serial.using_pre_serial)
		gd->baudrate = env_get_ulong("baudrate", 10, gd->serial.baudrate);
	else //波特率可以在配置uboot中设置,应该是走这一条路。
		gd->baudrate = env_get_ulong("baudrate", 10, CONFIG_BAUDRATE);

	return 0;
}

1.3.19 serial_init (drivers/serial/serial-uclass.c)

a. 主要通过serial_find_console_or_panic(void) 完成设备树中串口的查找,引脚复用的初始化

如果未找到,则初始化卡死panic_str 导致程序打印串口异常并卡死。

(先略过这个部分,后面有机会再把程序的分析补齐)

b.gd->flags 表示console串口可用了。

/* Called prior to relocation */
int serial_init(void)
{
	serial_find_console_or_panic();
	gd->flags |= GD_FLG_SERIAL_READY;

	return 0;
}

1.3.20 console_init_f 

实际只有这句生效了:gd->have_console = 1;

/* Called before relocation - use serial functions */
int console_init_f(void)
{
	gd->have_console = 1;

	console_update_silent();   //宏CONFIG_SILENT_CONSOLE未定义,空函数

	print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);  //PRE_CONSOLE_BUFFER未定义,空函数

	return 0;
}

可以在make menuconfig 中直接搜索PRE_CONSOLE_BUFFER,提示N表示没有配置。

1.3.21 display_options

这个应该就是uboot的第一条串口打印信息。

int display_options(void)
{
	char buf[DISPLAY_OPTIONS_BANNER_LENGTH]; //200字节

	display_options_get_banner(true, buf, sizeof(buf));  //完成字符串的组合
	printf("%s", buf);  //串口打印这个字符串

	return 0;
}


char *display_options_get_banner(bool newlines, char *buf, int size)
{
    //newlines 为ture,BUILD_TAG为NULL,buf即200字节的空间,size是200
	return display_options_get_banner_priv(newlines, BUILD_TAG, buf, size);  //
}


//version_string[]  --》 U_BOOT_VERSION " (" U_BOOT_DATE " - " \
//	U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING
//其中这些大写的宏,全部在一个自动生成的h文件中,version_autogenerated.h(include/genarated/)


char *display_options_get_banner_priv(bool newlines, const char *build_tag,
				      char *buf, int size)
{
	int len;

	len = snprintf(buf, size, "%s%s", newlines ? "\n\n" : "",
		       version_string);
	if (build_tag && len < size)  //build_tag == NULL, 不执行
		len += snprintf(buf + len, size - len, ", Build: %s",
				build_tag);
	if (len > size - 3)  //长度最大为197,超过了后面的字节就丢失了
		len = size - 3;
	strcpy(buf + len, "\n\n");  //末尾加两个换行

	return buf;
}

从代码中可以看到:

实际uboot启动时的打印:

因为没有更新到最新编译的,所以就还是用之前的时间吧,格式是一致的。

1.3.22 display_text_info

这个是一段调试信息,因为debug没有开启,实际并未打印

主要的打印信息,代码段的起始地址,bss段的起始地址和结束地址。

static int display_text_info(void)
{
#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_EFI_APP)
	ulong bss_start, bss_end, text_base;

	bss_start = (ulong)&__bss_start;
	bss_end = (ulong)&__bss_end;

#ifdef CONFIG_SYS_TEXT_BASE
	text_base = CONFIG_SYS_TEXT_BASE;
#else
	text_base = CONFIG_SYS_MONITOR_BASE;
#endif

	debug("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",
		text_base, bss_start, bss_end);
#endif

	return 0;
}

 1.3.23 checkcpu 

宏未定义,不执行

1.3.24 print_cpuinfo

宏未定义,不执行

1.3.25 embedded_dtb_select

宏未定义,不执行

1.3.26 show_board_info

打印板级信息

/*
 * If the root node of the DTB has a "model" property, show it.
 * Then call checkboard().
 */
int __weak show_board_info(void)
{
#ifdef CONFIG_OF_CONTROL   //这个宏定义了
	DECLARE_GLOBAL_DATA_PTR;
	const char *model;

	model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);  //从设备树中找到model节点

	if (model)
		printf("Model: %s\n", model); //打印该信息
#endif

#ifdef CONFIG_ARM64_BOOT_AARCH32   //未定义
	if (!(gd->flags & GD_FLG_RELOC))
		printf("CPU: AArch32\n");
#endif

	return checkboard();  //空函数
}

1.3.27 INIT_FUNC_WATCHDOG_INIT

CONFIG_WATCHDOG 和CONFIG_HW_WATCHDOG 均未定义

//两个宏均未定义,所以就是空的
#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG) 
#define INIT_FUNC_WATCHDOG_INIT	init_func_watchdog_init,
#define INIT_FUNC_WATCHDOG_RESET	init_func_watchdog_reset,
#else
#define INIT_FUNC_WATCHDOG_INIT
#define INIT_FUNC_WATCHDOG_RESET
#endif

1.3.28  misc_init_f

宏未定义,不执行该函数

1.3.29 INIT_FUNC_WATCHDOG_RESET

空,不需要执行

1.3.30 init_func_i2c

宏未定义,不执行

1.3.31 init_func_spi

宏未定义,不执行

1.3.32 announce_pre_serial

就是一个打印信息,看样子是串口的编号

rk3399调试串口是串口2(从0开始计数)。

static int announce_pre_serial(void)
{
	if (gd && gd->serial.using_pre_serial)  //gd->serial.using_pre_serial 不为0
		printf("PreSerial: %d\n", gd->serial.id);  //打印 2

	return 0;
}

1.3.33 announce_dram_init

空函数

static int announce_dram_init(void)
{
#ifndef CONFIG_SUPPORT_USBPLUG  //未定义
	puts("DRAM:  ");
#endif
	return 0;
}

1.3.34 dram_init

设置gd->ram_size 内存大小

int dram_init(void)
{
	/* Store complete RAM size and return */
	gd->ram_size = get_ram_size(PHYS_SDRAM_1, PHYS_SDRAM_1_MAXSIZE);

	return 0;
}

1.3.35 post_init_f

宏未定义,不执行

1.3.36 INIT_FUNC_WATCHDOG_RESET

1.3.37 testdram

宏未定义,不执行

1.3.38 INIT_FUNC_WATCHDOG_RESET

1.3.39 init_post

宏未定义,不执行 (见1.3.35)

1.3.40 INIT_FUNC_WATCHDOG_RESET

从这个函数开始,就是uboot的内存规划处理部分。

这里是一环扣一环,我还没有来得及仔细分析。这个坑还有点大!!!(2024-09-12).

干不动了又想休息了。

1.3.41 setup_dest_addr

这个函数应该还比较重要,但是目前几个宏定义不知道是哪个。

先跳过,回来再填坑

static int setup_dest_addr(void)
{
	debug("Monitor len: %08lX\n", gd->mon_len);
	/*
	 * Ram is setup, size stored in gd !!
	 */
	debug("Ram size: %08lX\n", (ulong)gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
	/*
	 * Subtract specified amount of memory to hide so that it won't
	 * get "touched" at all by U-Boot. By fixing up gd->ram_size
	 * the Linux kernel should now get passed the now "corrected"
	 * memory size and won't touch it either. This should work
	 * for arch/ppc and arch/powerpc. Only Linux board ports in
	 * arch/powerpc with bootwrapper support, that recalculate the
	 * memory size from the SDRAM controller setup will have to
	 * get fixed.
	 */
	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
#ifdef CONFIG_SYS_SDRAM_BASE
	gd->ram_top = CONFIG_SYS_SDRAM_BASE;
#endif
	gd->ram_top += get_effective_memsize();
	gd->ram_top = board_get_usable_ram_top(gd->mon_len);
	gd->relocaddr = gd->ram_top;
	debug("Ram top: %08lX\n", (ulong)gd->ram_top);
#if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500))
	/*
	 * We need to make sure the location we intend to put secondary core
	 * boot code is reserved and not used by any part of u-boot
	 */
	if (gd->relocaddr > determine_mp_bootpg(NULL)) {
		gd->relocaddr = determine_mp_bootpg(NULL);
		debug("Reserving MP boot page to %08lx\n", gd->relocaddr);
	}
#endif
	return 0;
}

1.3.42 reserve_pram

宏未定义,不执行

1.3.43 reserve_round_4k

重定位的地址,4k对齐,最低12位清零。

/* Round memory pointer down to next 4 kB limit */
static int reserve_round_4k(void)
{
	gd->relocaddr &= ~(4096 - 1);
	return 0;
}

1.3.44 reserve_mmu

CONFIG_SYS_ICACHE_OFF 未定义

CONFIG_SYS_DCACHE_OFF 也是未定义

实际这个代码被编译了。(在代码里面加几个字符,编译会报错)。

#ifdef CONFIG_ARM
__weak int reserve_mmu(void)
{
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
	/* reserve TLB table */
	gd->arch.tlb_size = PGTABLE_SIZE;
	gd->relocaddr -= gd->arch.tlb_size;

	/* round down to next 64 kB limit */
	gd->relocaddr &= ~(0x10000 - 1);

	gd->arch.tlb_addr = gd->relocaddr;
	debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr,
	      gd->arch.tlb_addr + gd->arch.tlb_size);

#ifdef CONFIG_SYS_MEM_RESERVE_SECURE
	/*
	 * Record allocated tlb_addr in case gd->tlb_addr to be overwritten
	 * with location within secure ram.
	 */
	gd->arch.tlb_allocated = gd->arch.tlb_addr;
#endif
#endif

	return 0;
}
#endif

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

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

相关文章

AWS账号申请指南:必须绑定银行卡吗?

小伙伴们&#xff0c;大家好&#xff01;今天九河云来和大家聊一聊一个常见的问题&#xff1a;申请AWS账号时&#xff0c;是否必须要绑定银行卡呢&#xff1f;相信很多小伙伴在注册AWS账号时都会遇到这个问题。为了帮助大家更好地了解这个过程&#xff0c;小编特意整理了一些信…

SprinBoot+Vue旅游景点管理系统设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

模 板

导引&#xff1a; 模板是为了解决函数类型不同所重载&#xff0c;带来的麻烦简化。利用一个模板&#xff08;示列&#xff09;代码&#xff0c;让编译器编写出不同类型的代码&#xff0c;满足所需。 int swap(int &p1,int &p2) {int pp1;p1p2;p2p; } char swap(char …

优化安防视频监控的关键体验:视频质量诊断技术如何应用在监控系统中?

随着科技的不断进步&#xff0c;视频监控平台在公安、司法、教育、基础设施等众多领域得到了广泛应用。然而&#xff0c;视频图像的质量直接关系到监控系统的应用效果&#xff0c;是反映监控系统运维效果的重要指标之一。因此&#xff0c;视频监控平台需要配备一系列先进的视频…

基于剂型改良的复杂注射剂分析!

改良型新药在医药领域的重要性日益凸显&#xff0c;其中脂质体注射剂作为一类重要的改良型新药&#xff0c;因其独特的临床优势和技术创新&#xff0c;正受到行业的高度关注。本文基于药融咨询团队的深度分析报告&#xff0c;探讨脂质体注射剂的技术创新、市场前景以及在中国的…

动手学深度学习(三)深度学习计算

一、模型构造 1、继承Module类来构造模型来构造模型 class MLP(nn.Module):# 声明带有模型参数的层&#xff0c;这里声明了两个全连接层def __init__(self, **kwargs):# 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数# 参数&#xff0c…

利用CubeMX复现正点原子TFTLCD驱动例程

来源&#xff1a;正点原子 FMC的工作原理暂时先欠着&#xff0c;先记录一下CRUD的过程。 第一步准备一个us级别延时函数&#xff0c;不会的参考拙作&#xff1a;STM32的定时器简介-CSDN博客 第二部开启FMC外设&#xff1a; ①进入 Pinout->FMC 配置栏&#xff0c;配置 …

【隐私计算】Paillier半同态加密算法

一、何为同态加密&#xff08;HE&#xff09;&#xff1f; HE是一种特殊的加密方法&#xff0c;它允许直接对加密数据执行计算&#xff0c;如加法和乘法&#xff0c;而计算过程不会泄露原文的任何信息。计算的结果仍然是加密的&#xff0c;拥有密钥的用户对处理过的密文数据进…

树莓派5开发板-安装Raspberry Pi系统-学习记录1

树莓派5开发板介绍 树莓派5&#xff08;Raspberry Pi 5&#xff09;是树莓派系列最新的开发板&#xff0c;相较于前几代产品&#xff0c;它在性能、连接性和功能方面都有了显著提升。以下是树莓派5的一些主要特点&#xff1a; 处理器&#xff1a;树莓派5搭载了Broadcom BCM27…

如何基于gpt模型抢先打造成功的产品

来自&#xff1a;Python大数据分析 费弗里 ChatGPT、gpt3.5以及gpt4&#xff0c;已然成为当下现代社会中几乎人尽皆知的话题&#xff0c;而当此种现象级产品引爆全网&#xff0c;极大程度上吸引大众注意力的同时&#xff0c;有一些嗅觉灵敏的人及时抓住了机会&#xff0c;通过快…

【FreeRL】我的深度学习库构建思想

文章目录 前言参考python环境效果已复现结果 综述DQN.py&#xff08;主要&#xff09;算法实现参数修改细节实现显示训练&#xff0c;保存训练 Buffer.pyevaluate.pylearning_curves 前言 代码实现在:https://github.com/wild-firefox/FreeRL 欢迎star 参考 动手学强化学习e…

Coggle数据科学 | 小白学 RAG:Milvus 介绍与使用教程

本文来源公众号“Coggle数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;小白学 RAG&#xff1a;Milvus 介绍与使用教程 什么是Milvus&#xff1f; Milvus 是一款高性能、高扩展性的开源向量数据库&#xff0c;专为处理…

【阿一网络安全】如何让你的密码更安全?(三) - 散列函数

散列函数 散列函数&#xff08;Hash Function&#xff0c;又称散列算法、哈希函数&#xff09;&#xff0c;是一种从任何一种数据中创建小的数字指纹的方法。 散列值 散列函数&#xff0c;把任意长的消息明文&#xff0c;压缩成摘要&#xff0c;使得数据量变小&#xff0c;将…

[数据集][目标检测]脊椎检测数据集VOC+YOLO格式1137张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1137 标注数量(xml文件个数)&#xff1a;1137 标注数量(txt文件个数)&#xff1a;1137 标注…

SpringBoot2:web开发常用功能实现及原理解析-上传与下载

文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端从Java接口下载文件2、Java接口调用Java接口下载文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和多个文件&#xff0c;并保存在本地…

基于小程序的教学辅助微信小程序设计+ssm(lw+演示+源码+运行)

教学辅助微信小程序 摘 要 随着移动应用技术的发展&#xff0c;越来越多的学生借助于移动手机、电脑完成生活中的事务&#xff0c;许多的传统行业也更加重视与互联网的结合&#xff0c;由于学生学习的压力越来越大&#xff0c;教学辅助是一个非常不错的教育平台&#xff0c;对…

人工智能(AI)领域各方向顶会和顶刊

在人工智能&#xff08;AI&#xff09;这个快速发展的领域&#xff0c;研究人员和从业者需要紧跟最新的研究动态和技术进展。顶级的会议和期刊是获取最新科研成果和交流思想的重要平台。以下是人工智能领域内不同方向的顶级会议和期刊概览。 顶级会议 人工智能基础与综合 A…

客厅无主灯设计:灯位布局与灯光灯具的和谐搭配

在现代家居设计中&#xff0c;客厅作为家庭活动的中心区域&#xff0c;其照明设计的重要性不言而喻。无主灯设计以其灵活多变、氛围营造独特的优势&#xff0c;逐渐成为客厅照明的热门选择。然而&#xff0c;如何合理规划灯位布局&#xff0c;并科学搭配灯光与灯具&#xff0c;…

20240913 每日AI必读资讯

AMD死战CUDA&#xff1a;我是一家软件公司 - AMD重大改变&#xff1a;重心将从硬件开发转向强调软件开发、API 和 AI 体验。 - 软件工程团队规模扩大了三倍&#xff0c;并且全力以赴投入软件开发 - AMD将自家已有5年历史的图形架构RDNA、计算架构CDNA重新整合在一起&#xf…

计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…