RISCVLinuxkernel启动代码分析之九:initramfs实践

原创 嵌入式Lee 2024-11-24 16:51

一. 前言

内核启动后需要挂载根文件系统,找到并执行init程序。在调试适配阶段,我们甚至还没有实现各种存储设备的驱动,此时也没法从对应的存储设备挂载文件系统找到init执行。此时我们可以使用initramfs,直接将简单的根文件系统打包压缩嵌入到内核中,启动时解压解包文件到rootfs中,此时就可以找到对应的init执行,后续再加载存储设备驱动,挂载实际的根文件系统,并切换过去。

整个过程涉及如下几个主要过程

(1)挂载rootfs文件系统

(2)解压解包rootfs.cpio.gz中文件到rootfs

(3)找到rootfs中的/init执行

当然要使用initramfs还需要一些配置等,下面就分享下相关内容。

.rootfs.cpio.gz嵌入到内核

需要配置以下选项

2.1配置BLK_DEV_INITRD/INITRAMFS_SOURCE/INITRAMFS_COMPRESSION_GZIP(RD_GZIP)

BLK_DEV_INITRD 配置使能支持initramfs/initrd

INITRAMFS_SOURCE 配置rootfs.cpio.gz源文件的路径

INITRAMFS_COMPRESSION_GZIP 配置使用gzip压缩INITRAMFS,依赖RD_ZIP,当然也可以使用其他压缩方式这里以gzip为例,其他类似。

对应配置如下

init/Kconfig

config BLK_DEV_INITRD    bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support"    help      The initial RAM filesystem is a ramfs which is loaded by the      boot loader (loadlin or lilo) and that is mounted as root      before the normal boot procedure. It is typically used to      load modules needed to mount the "real" root file system,      etc. See <file:Documentation/admin-guide/initrd.rst> for details.
      If RAM disk support (BLK_DEV_RAM) is also included, this      also enables initial RAM disk (initrd) support and adds      15 Kbytes (more on some other architectures) to the kernel size.      If unsure say Y.

usr/KconfigINITRAMFS_SOURCE配置

config INITRAMFS_SOURCE    string "Initramfs source file(s)"    default ""    help      This can be either a single cpio archive with a .cpio suffix or a      space-separated list of directories and files for building the      initramfs image.  A cpio archive should contain a filesystem archive      to be used as an initramfs image.  Directories should contain a      filesystem layout to be included in the initramfs image.  Files      should contain entries according to the format described by the      "usr/gen_init_cpio" program in the kernel tree.
      When multiple directories and files are specified then the      initramfs image will be the aggregate of all of them.      See <file:Documentation/driver-api/early-userspace/early_userspace_support.rst> for more details.      If you are not sure, leave it blank.

usr/KconfigINITRAMFS_COMPRESSION_GZIP配置

config INITRAMFS_COMPRESSION_GZIP    bool "Gzip"    depends on RD_GZIP    help      Use the old and well tested gzip compression algorithm. Gzip provides      a good balance between compression ratio and decompression speed and      has a reasonable compression speed. It is also more likely to be      supported by your build system as the gzip tool is present by default      on most distros.
config RD_GZIP    bool "Support initial ramdisk/ramfs compressed using gzip"    default y    select DECOMPRESS_GZIP    help      Support loading of a gzip encoded initial ramdisk or cpio buffer.      If unsure, say Y.

menuconfig配置

General setup --->

[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

(images/rootfs.cpio.gz)Initramfs source file(s)

[*]   Support initial ramdisk/ramfs compressed using gzip

Built-in initramfs compression mode (Gzip)  ---> 

2.2rootfs.cpio.gz嵌入内核的过程

usr/Makefile

obj-$(CONFIG_BLK_DEV_INITRD) := initramfs_data.o$(obj)/initramfs_data.o: $(obj)/initramfs_inc_dataramfs-input := $(strip $(shell echo $(CONFIG_INITRAMFS_SOURCE)))

如果SOURCE指定的是压缩文件直接使用


# If CONFIG_INITRAMFS_SOURCE specifies a single file, and it is suffixed with# .cpio.*, use it directly as an initramfs, and avoid double compression.ifeq ($(words $(subst .cpio.,$(space),$(ramfs-input))),2)cpio-data := $(ramfs-input)compress-y := shippedendif

initramfs_inc_data依赖

$(obj)/initramfs_inc_data: $(cpio-data) FORCE    $(call if_changed,$(compress-y))
targets += initramfs_data.cpio initramfs_inc_data

output/usr/.initramfs_inc_data.cmd

rootfs.cpio.gz复制为initramfs_inc_data

cmd_usr/initramfs_inc_data := cat /home/qinyunti/sdk/buildroot/enhanced_5.10_glibc_br_defconfig/images/rootfs.cpio.gz > usr/initramfs_inc_data

usr/initramfs_data.S

usr/initramfs_inc_data链接进Vmlinux.

放在段.init.ramfs

__initramfs_size处放置大小,放在段.init.ramfs.info

.section .init.ramfs,"a"

__irf_start:

.incbin "usr/initramfs_inc_data"

__irf_end:

.section .init.ramfs.info,"a"

.globl __initramfs_size

__initramfs_size:

#ifdef CONFIG_64BIT

.quad __irf_end - __irf_start

#else

.long __irf_end - __irf_start

#endif

include/asm-generic/vmlinux.lds.h

#ifdef CONFIG_BLK_DEV_INITRD#define INIT_RAM_FS                         \    . = ALIGN(4);                           \    __initramfs_start = .;                      \    KEEP(*(.init.ramfs))                        \    . = ALIGN(8);                           \    KEEP(*(.init.ramfs.info))#else#define INIT_RAM_FS#endif

arch/um/include/asm/common.lds.S

放在了镜像最后

. = ALIGN(4096);

.init.ramfs : {

INIT_RAM_FS

}

可以通过__initramfs_start知道其位置,__initramfs_size知道其大小。

(gdb) p &__irf_end$7 = (no debug info> *) 0xffffffe0012a0174(gdb) p &__irf_start$8 = (no debug info> *) 0xffffffe000890788(gdb) p &__irf_end-&__irf_startwarning: Type size unknown, assuming 1. Try casting to a known type, or void *.$9 = 10549740(gdb)
(gdb) p &__initramfs_start$10 = (char (*)[]) 0xffffffe000890788(gdb)
(gdb) p &__initramfs_size$11 = (unsigned long *) 0xffffffe0012a0178(gdb)
(gdb) p __initramfs_size$2 = 10549740(gdb)

.相关过程

3.1rootfs挂载

Breakpoint 1, init_mount_tree () at ../fs/namespace.c:3791␦␦/home/qinyunti/sdk/linux/linux-custom/fs/namespace.c:3791:93171:beg:0xffffffe0000130d0(gdb) bt#0  init_mount_tree () at ../fs/namespace.c:3791#1  mnt_init () at ../fs/namespace.c:3846#2  0xffffffe000012da0 in vfs_caches_init () at ../fs/dcache.c:3234#3  0xffffffe000002bee in start_kernel () at ../init/main.c:1041#4  0xffffffe000002092 in _start_kernel () at ../arch/riscv/kernel/head.S:281Backtrace stopped: frame did not save the PC(gdb)

3.2rootfs.cpio.gz解压解包过程

3.2.1populate_rootf执行路径

linux/linux-custom/init/initramfs.c

populate_rootfs

(gdb) x /64xb 0xffffffe0008907880xffffffe000890788:     0x1f    0x8b    0x08    0x00    0x00    0x00    0x00    0x000xffffffe000890790:     0x02    0x03    0xbc    0x9a    0x79    0x9c    0xcd    0x650xffffffe000890798:     0xfb    0xc7    0xbf    0xe7    0xcc    0x99    0x31    0x260xffffffe0008907a0:     0x71    0xac    0xd9    0x1d    0x8c    0xa5    0x6c    0x670xffffffe0008907a8:     0x06    0x63    0x94    0x98    0x19    0x93    0x2d    0x240xffffffe0008907b0:     0x14    0x92    0xec    0x45    0x59    0xca    0x4e    0xdb0xffffffe0008907b8:     0x58    0xaa    0x21    0x4a    0x85    0x94    0x16    0xa10xffffffe0008907c0:     0x85    0x52    0x48    0x7b    0x09    0x6d    0xb4    0x29(gdb)
hexdump images/rootfs.cpio.gz -n 640000000 8b1f 0008 0000 0000 0302 9abc 9c79 65cd0000010 c7fb e7bf 99cc 2631 ac71 1dd9 a58c 676c0000020 6306 9894 9319 242d 9214 45ec ca59 db4e0000030 aa58 4a21 9485 a116 5285 7b48 6d09 29b40000040

执行路径

(gdb) hb populate_rootfsHardware assisted breakpoint 1 at 0xffffffe000004c58: file ../init/initramfs.c, line 606.(gdb) cContinuing.
Breakpoint 1, populate_rootfs () at ../init/initramfs.c:606␦␦/home/qinyunti/sdk/linux/linux-custom/init/initramfs.c:606:13203:beg:0xffffffe000004c58(gdb) bt#0 populate_rootfs () at ../init/initramfs.c:606#1 0xffffffe000200762 in do_one_initcall (fn=0xffffffe000004c58 ) at ../init/main.c:1217#2 0xffffffe000002e18 in do_initcall_level (command_line=0xffffffe001e05800 "earlycon", level=5) at ../init/main.c:1290#3 do_initcalls () at ../init/main.c:1306#4 do_basic_setup () at ../init/main.c:1326#5 kernel_init_freeable () at ../init/main.c:1526#6 0xffffffe0008743d6 in kernel_init (unused=) at ../init/main.c:1415#7 0xffffffe000201b5e in handle_exception () at ../arch/riscv/kernel/entry.S:243Backtrace stopped: frame did not save the PC(gdb)
(gdb) hb rest_initHardware assisted breakpoint 1 at 0xffffffe0008742d2: file ../init/main.c, line 681.(gdb) cContinuing.
Breakpoint 1, rest_init () at ../init/main.c:681␦␦/home/qinyunti/sdk/linux/linux-custom/init/main.c:681:16754:beg:0xffffffe0008742d2(gdb) bt#0 rest_init () at ../init/main.c:681#1 0xffffffe0000026e4 in arch_call_rest_init () at ../init/main.c:845#2 0xffffffe000002c12 in start_kernel () at ../init/main.c:1061#3 0xffffffe000002092 in _start_kernel () at ../arch/riscv/kernel/head.S:281Backtrace stopped: frame did not save the PC(gdb)

下面分析整个执行过程

populate_rootfs

include/linux/init.h

#define rootfs_initcall(fn)    __define_initcall(fn, rootfs)#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)#define ___define_initcall(fn, id, __sec) \    static initcall_t __initcall_##fn##id __used \        __attribute__((__section__(#__sec ".init"))) = fn;

rootfs_initcall(populate_rootfs);

展开为

    static initcall_t __initcall_populate_rootfsrootfs __used \        __attribute__((__section__(".initcallrootfs.init"))) = populate_rootfs;

include/linux/init.h

typedef int (*initcall_t)(void);

即定义了一个函数类型指针变量__initcall_populate_rootfsrootfs其值为populate_rootfs,

放在了段.initcallrootfs.init

GDB可以直接查看该变量

(gdb) p __initcall_populate_rootfsrootfs$1 = (initcall_t) 0xffffffe000004c58 (gdb)

init/main.c

static void __init do_initcalls(void){    int level;    size_t len = strlen(saved_command_line) + 1;    char *command_line;
    command_line = kzalloc(len, GFP_KERNEL);    if (!command_line)        panic("%s: Failed to allocate %zu bytes\n", __func__, len);    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {        /* Parser modifies command_line, restore it each time */        strcpy(command_line, saved_command_line);        do_initcall_level(level, command_line);    }    kfree(command_line);}

遍历initcall_levels

extern initcall_entry_t __initcall_start[];extern initcall_entry_t __initcall0_start[];extern initcall_entry_t __initcall1_start[];extern initcall_entry_t __initcall2_start[];extern initcall_entry_t __initcall3_start[];extern initcall_entry_t __initcall4_start[];extern initcall_entry_t __initcall5_start[];extern initcall_entry_t __initcall6_start[];extern initcall_entry_t __initcall7_start[];extern initcall_entry_t __initcall_end[];
static initcall_entry_t *initcall_levels[] __initdata = {    __initcall0_start,    __initcall1_start,    __initcall2_start,    __initcall3_start,    __initcall4_start,    __initcall5_start,    __initcall6_start,    __initcall7_start,    __initcall_end,};

调用do_initcall_level

static void __init do_initcall_level(int level, char *command_line){    initcall_entry_t *fn;
    parse_args(initcall_level_names[level],           command_line, __start___param,           __stop___param - __start___param,           level, level,           NULL, ignore_unknown_bootoption);
    trace_initcall_level(initcall_level_names[level]);    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)        do_one_initcall(initcall_from_entry(fn));}

遍历initcall_levels[level]initcall_levels[level+1]之间的函数指针,调用

initcall_levels[level+1]

int __init_or_module do_one_initcall(initcall_t fn){    int count = preempt_count();    char msgbuf[64];    int ret;
    if (initcall_blacklisted(fn))        return -EPERM;    do_trace_initcall_start(fn);    ret = fn();    do_trace_initcall_finish(fn, ret);    msgbuf[0] = 0;    if (preempt_count() != count) {        sprintf(msgbuf, "preemption imbalance ");        preempt_count_set(count);    }    if (irqs_disabled()) {        strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));        local_irq_enable();    }    WARN(msgbuf[0], "initcall %pS returned with %s\n", fn, msgbuf);    add_latent_entropy();    return ret;}

ret = fn();调用

initcall_levels[level]initcall_levels[level+1]之间的函数指针

linux/linux-custom/output/arch/riscv/kernel/vmlinux.lds

.init.data : AT(ADDR(.init.data) - 0xffffffe000000000) { KEEP(*(SORT(___kentry+*))) *(.init.data init.data.*) *(.meminit.data*) *(.init.rodata .init.rodata.*) . = ALIGN(8); __start_ftrace_events = .; KEEP(*(_ftrace_events)) __stop_ftrace_events = .; __start_ftrace_eval_maps = .; KEEP(*(_ftrace_eval_map)) __stop_ftrace_eval_maps = .; *(.meminit.rodata) . = ALIGN(8); __clk_of_table = .; KEEP(*(__clk_of_table)) KEEP(*(__clk_of_table_end)) . = ALIGN(8); __reservedmem_of_table = .; KEEP(*(__reservedmem_of_table)) KEEP(*(__reservedmem_of_table_end)) . = ALIGN(8); __timer_of_table = .; KEEP(*(__timer_of_table)) KEEP(*(__timer_of_table_end)) . = ALIGN(8); __cpu_method_of_table = .; KEEP(*(__cpu_method_of_table)) KEEP(*(__cpu_method_of_table_end)) . = ALIGN(32); __dtb_start = .; KEEP(*(.dtb.init.rodata)) __dtb_end = .; . = ALIGN(8); __irqchip_of_table = .; KEEP(*(__irqchip_of_table)) KEEP(*(__irqchip_of_table_end)) . = ALIGN(8); __earlycon_table = .; KEEP(*(__earlycon_table)) __earlycon_table_end = .; . = ALIGN(8); __kunit_suites_start = .; KEEP(*(.kunit_test_suites)) __kunit_suites_end = .; . = ALIGN(16); __setup_start = .; KEEP(*(.init.setup)) __setup_end = .; __initcall_start = .; KEEP(*(.initcallearly.init)) __initcall0_start = .; KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init)) __initcall1_start = .; KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init)) __initcall2_start = .; KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init)) __initcall3_start = .; KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init)) __initcall4_start = .; KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init)) __initcall5_start = .; KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init)) __initcallrootfs_start = .; KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init)) __initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) __initcall7_start = .; KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init)) __initcall_end = .; __con_initcall_start = .; KEEP(*(.con_initcall.init)) __con_initcall_end = .; . = ALIGN(4); __initramfs_start = .; KEEP(*(.init.ramfs)) . = ALIGN(8); KEEP(*(.init.ramfs.info)) }

对应linux/linux-custom/include/asm-generic/vmlinux.lds.h

#define INIT_DATA_SECTION(initsetup_align)              \    .init.data : AT(ADDR(.init.data) - LOAD_OFFSET) {       \        INIT_DATA                       \        INIT_SETUP(initsetup_align)             \        INIT_CALLS                      \        CON_INITCALL                        \        INIT_RAM_FS                     \    }
#define INIT_CALLS                          \        __initcall_start = .;                   \        KEEP(*(.initcallearly.init))                \        INIT_CALLS_LEVEL(0)                 \        INIT_CALLS_LEVEL(1)                 \        INIT_CALLS_LEVEL(2)                 \        INIT_CALLS_LEVEL(3)                 \        INIT_CALLS_LEVEL(4)                 \        INIT_CALLS_LEVEL(5)                 \        INIT_CALLS_LEVEL(rootfs)                \        INIT_CALLS_LEVEL(6)                 \        INIT_CALLS_LEVEL(7)                 \        __initcall_end = .;

arch/riscv/kernel/vmlinux.lds.S

INIT_DATA_SECTION(16)

.initcallrootfs.init放在了.initcall5.init.initcall6.init之间

__initcall5_start = .; KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init)) __initcallrootfs_start = .; KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init)) __initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init))

static initcall_entry_t *initcall_levels[] __initdata = {    __initcall0_start,    __initcall1_start,    __initcall2_start,    __initcall3_start,    __initcall4_start,    __initcall5_start,    __initcall6_start,    __initcall7_start,    __initcall_end,};

对应

  for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)        do_one_initcall(initcall_from_entry(fn));

level=5时遍历到initcall_levels[5]~initcall_levels[6]之间,即__initcall5_start~ __initcall6_start之间,包括了.initcall5.init段和.initcallrootfs.init段之间,即可执行到populate_rootfs

3.2.2populate_rootfs执行过程

调用unpack_to_rootfs(__initramfs_start, __initramfs_size);解压

参数就是前面cpio嵌入内核的过程分析的段的开始和长度。

(gdb) p buf$6 = 0xffffffe000890788 "\037\213\b"(gdb) p len$7 = 10549740(gdb) p buf$8 = 0xffffffe000890788 "\037\213\b"(gdb)

decompress = decompress_method(buf, len, &compress_name); 获取压缩方式

此时

(gdb) p compress_name$10 = 0xffffffe00158c1a8 "gzip"(gdb) p decompress$11 = (decompress_fn) 0xffffffe000018c7a (gdb)

然后解压

int res = decompress(buf, len, NULL, flush_buffer, NULL,

   &my_inptr, error);

3.3执行/init

(gdb) bt#0  run_init_process (init_filename=0xffffffe001543f90 "/init") at ../init/main.c:1342#1  0xffffffe000874438 in kernel_init (unused=) at ../init/main.c:1437#2  0xffffffe000201b5e in handle_exception () at ../arch/riscv/kernel/entry.S:243Backtrace stopped: frame did not save the PC(gdb)

kernel_init

 if (ramdisk_execute_command) {        ret = run_init_process(ramdisk_execute_command);        if (!ret)            return 0;        pr_err("Failed to execute %s (error %d)\n",               ramdisk_execute_command, ret);    }
    /*     * We try each of these until one succeeds.     *     * The Bourne shell can be used instead of init if we are     * trying to recover a really broken machine.     */    if (execute_command) {        ret = run_init_process(execute_command);        if (!ret)            return 0;        panic("Requested init %s failed (error %d).",              execute_command, ret);    }
    if (CONFIG_DEFAULT_INIT[0] != '\0') {        ret = run_init_process(CONFIG_DEFAULT_INIT);        if (ret)            pr_err("Default init %s failed (error %d)\n",                   CONFIG_DEFAULT_INIT, ret);        else            return 0;    }
    if (!try_to_run_init_process("/sbin/init") ||        !try_to_run_init_process("/etc/init") ||        !try_to_run_init_process("/bin/init") ||        !try_to_run_init_process("/bin/sh"))        return 0;
    panic("No working init found.  Try passing init= option to kernel. "          "See Linux Documentation/admin-guide/init.rst for guidance.");

.cpio操作实践

这里以替换原来roofs.cpio中的init为自己的init程序为例,来演示整个操作过程。

4.1解压

buildroot构建后images目录下有images/rootfs.cpiorootfs.cpio.gz

解压rootfs.cpio.gz得到rootfs.cpio,已经有了rootfs.cpio则无需再解压:

解压命令:gunzip rootfs.cpio.gz

解包,假设rootfs.cpiotmp文件夹下

解包命令:cpio -idmv < rootfs.cpio

解压后看到文件如下

 tree -L 1.├── bin├── dev├── etc├── init├── lib├── lib64 -> lib├── lib64v_xthead├── linuxrc -> bin/busybox├── media├── mnt├── opt├── proc├── root├── rootfs.cpio├── run├── sbin├── sys├── tmp├── usr└── var
17 directories, 3 files

4.2修改

这里以创建一个简单的init可执行程序,打印一条字符串进行测试。

Menuconfig,确认以下配置使能了elf支持

Executable file formats --->

[*] Kernel support for ELF binaries

buildroot/enhanced_5.10_glibc_br_defconfig/images/目录下

新建init.c文件内容如下

#include int main(int argc,char argv[]) {     printf("hello world, from initramfs.\n");     return 0; }

编译生成init可执行文件

~/sdk/buildroot/enhanced_5.10_glibc_br_defconfig/host/bin/riscv64-unknown-linux-gnu-gcc -g -o init -static init.c

可查看对应的汇编

~/sdk/buildroot/enhanced_5.10_glibc_br_defconfig/host/bin/riscv64-unknown-linux-gnu-objdump -l -S init > a.s

添加到文件夹中

cp init tmp/

先删除原来的rootfs.cpio

当前位于buildroot/enhanced_5.10_glibc_br_defconfig/images/tmp

添加如下节点

sudo mknod -m 600 dev/console c 5 1

否则会打印如下信息Warning: unable to open an initial console.init中标准输入输出无法使用

这个打印位于init/main.c

/* Open /dev/console, for stdin/stdout/stderr, this should never fail */void __init console_on_rootfs(void){    struct file *file = filp_open("/dev/console", O_RDWR, 0);
    if (IS_ERR(file)) {        pr_err("Warning: unable to open an initial console.\n");        return;    }    init_dup(file);    init_dup(file);    init_dup(file);    fput(file);}

4.3打包

添加修改新的文件后打包。

1. 产生cpio文件

usr/gen_initramfs.sh -o rootfs.cpio ../../images/tmp/

2.压缩

gzip rootfs.cpio

3.替换

cp rootfs.cpio.gz ../../images/

4.4测试

五. 总结

本文分享了initramfs相关的内容,包括rootfs.cpio.gz嵌入到内核的相关配置和流程,rootfs.cpio.gz解压解包过程流程。以及一一个实力分享了如何操作cpio文件。













评论 (0)
  • 六西格玛首先是作为一个量度质量水平的指标,它代表了近乎完美的质量的水平。如果你每天都吃一个苹果,有一间水果店的老板跟你说,他们所卖的苹果,质量达到六西格玛水平,换言之,他们每卖一百万个苹果,只会有3.4个是坏的。你算了一下,发现你如果要从这个店里买到一个坏苹果,需要805年。你会还会选择其他店吗?首先发明六西格玛这个词的人——比尔·史密斯(Bill Smith)他是摩托罗拉(Motorloa)的工程师,在追求这个近乎完美的质量水平的时候,发明了一套方法模型,开始时是MAIC,后来慢慢演变成DMA
    优思学院 2025-03-27 11:47 147浏览
  • 在当今竞争激烈的工业环境中,效率和响应速度已成为企业制胜的关键。为了满足这一需求,我们隆重推出宏集Panorama COOX,这是Panorama Suite中首款集成的制造执行系统(MES)产品。这一创新产品将Panorama平台升级为全面的工业4.0解决方案,融合了工业SCADA和MES技术的双重优势,帮助企业实现生产效率和运营能力的全面提升。深度融合SCADA与MES,开启工业新纪元宏集Panorama COOX的诞生,源于我们对创新和卓越运营的不懈追求。通过战略性收购法国知名MES领域专
    宏集科技 2025-03-27 13:22 182浏览
  • 案例概况在丹麦哥本哈根,西门子工程师们成功完成了一项高安全设施的数据集成项目。他们利用宏集Cogent DataHub软件,将高安全设施内的设备和仪器与远程监控位置连接起来,让技术人员能够在不违反安全规定、不引入未经授权人员的情况下,远程操作所需设备。突破OPC 服务器的远程连接难题该项目最初看似是一个常规的 OPC 应用:目标是将高安全性设施中的冷水机(chiller)设备及其 OPC DA 服务器,与远程监控站的两套 SCADA 系统(作为 OPC DA 客户端)连接起来。然而,在实际实施过
    宏集科技 2025-03-27 13:20 109浏览
  • 长期以来,智能家居对于大众家庭而言就像空中楼阁一般,华而不实,更有甚者,还将智能家居认定为资本家的营销游戏。商家们举着“智慧家居、智慧办公”的口号,将原本价格亲民、能用几十年的家电器具包装成为了高档商品,而消费者们最终得到的却是家居设备之间缺乏互操作性、不同品牌生态之间互不兼容的碎片化体验。这种早期的生态割裂现象致使消费者们对智能家居兴趣缺失,也造就了“智能家居无用论”的刻板印象。然而,自Matter协议发布之后,“命运的齿轮”开始转动,智能家居中的生态割裂现象与品牌生态之间的隔阂正被基于IP架
    华普微HOPERF 2025-03-27 09:46 109浏览
  • 在嵌入式语音系统的开发过程中,广州唯创电子推出的WT588系列语音芯片凭借其优异的音质表现和灵活的编程特性,广泛应用于智能终端、工业控制、消费电子等领域。作为该系列芯片的关键状态指示信号,BUSY引脚的设计处理直接影响着系统交互的可靠性和功能拓展性。本文将从电路原理、应用场景、设计策略三个维度,深入解析BUSY引脚的技术特性及其工程实践要点。一、BUSY引脚工作原理与信号特性1.1 电气参数电平标准:输出3.3V TTL电平(与VDD同源)驱动能力:典型值±8mA(可直接驱动LED)响应延迟:语
    广州唯创电子 2025-03-26 09:26 201浏览
  • 家电,在人们的日常生活中扮演着不可或缺的角色,也是提升人们幸福感的重要组成部分,那你了解家电的发展史吗?#70年代结婚流行“四大件”:手表、自行车、缝纫机,收音机,合成“三转一响”。#80年代随着改革开放的深化,中国经济开始飞速发展,黑白电视机、冰箱、洗衣机这“新三件”,成为了人们对生活的新诉求。#90年代彩电、冰箱、全自动洗衣机开始大量进入普通家庭,快速全面普及,90年代末,家电产品实现了从奢侈品到必需品的转变。#00年代至今00年代,随着人们追求高品质生活的愿望,常用的电视机、洗衣机等已经远
    启英AI平台 2025-03-25 14:12 89浏览
  •       知识产权保护对工程师的双向影响      正向的激励,保护了工程师的创新成果与权益,给企业带来了知识产权方面的收益,企业的创新和发明大都是工程师的劳动成果,他们的职务发明应当受到奖励和保护,是企业发展的重要源泉。专利同时也成了工程师职称评定的指标之一,专利体现了工程师的创新能力,在求职、竞聘技术岗位或参与重大项目时,专利证书能显著增强个人竞争力。专利将工程师的创意转化为受法律保护的“无形资产”,避免技术成果被他人抄袭或无偿使
    广州铁金刚 2025-03-25 11:48 181浏览
  • WT588F02B是广州唯创电子推出的一款高性能语音芯片,广泛应用于智能家电、安防设备、玩具等领域。然而,在实际开发中,用户可能会遇到烧录失败的问题,导致项目进度受阻。本文将从下载连线、文件容量、线路长度三大核心因素出发,深入分析烧录失败的原因并提供系统化的解决方案。一、检查下载器与芯片的物理连接问题表现烧录时提示"连接超时"或"设备未响应",或烧录进度条卡顿后报错。原因解析接口错位:WT588F02B采用SPI/UART双模通信,若下载器引脚定义与芯片引脚未严格对应(如TXD/RXD交叉错误)
    广州唯创电子 2025-03-26 09:05 146浏览
  • 汽车导航系统市场及应用环境参照调研机构GII的研究报告中的市场预测,全球汽车导航系统市场预计将于 2030年达到472亿美元的市场规模,而2024年至2030年的年复合成长率则为可观的6.7%。汽车导航系统无疑已成为智能汽车不可或缺的重要功能之一。随着人们在日常生活中对汽车导航功能的日渐依赖,一旦出现定位不准确或地图错误等问题,就可能导致车主开错路线,平白浪费更多行车时间,不仅造成行车不便,甚或可能引发交通事故的发生。有鉴于此,如果想要提供消费者完善的使用者体验,在车辆开发阶段便针对汽车导航功能
    百佳泰测试实验室 2025-03-27 14:51 187浏览
  • ​2025年3月27日​,贞光科技授权代理品牌紫光同芯正式发布新一代汽车安全芯片T97-415E。作为T97-315E的迭代升级产品,该芯片以大容量存储、全球化合规认证、双SPI接口协同为核心突破,直击智能网联汽车"多场景安全并行"与"出口合规"两大行业痛点,助力车企抢占智能驾驶与全球化市场双赛道。行业趋势锚定:三大升级回应智能化浪潮1. 大容量存储:破解车联网多任务瓶颈随着​车机功能泛在化​(数字钥匙、OTA、T-BOX等安全服务集成),传统安全芯片面临存储资源挤占难题。T97-415E创新性
    贞光科技 2025-03-27 13:50 148浏览
  • 在智能语音产品的开发过程中,麦克风阵列的选型直接决定了用户体验的优劣。广州唯创电子提供的单麦克风与双麦克风解决方案,为不同场景下的语音交互需求提供了灵活选择。本文将深入解析两种方案的性能差异、适用场景及工程实现要点,为开发者提供系统化的设计决策依据。一、基础参数对比分析维度单麦克风方案双麦克风方案BOM成本¥1.2-2.5元¥4.8-6.5元信噪比(1m)58-62dB65-68dB拾音角度全向360°波束成形±30°功耗8mW@3.3V15mW@3.3V典型响应延迟120ms80ms二、技术原
    广州唯创电子 2025-03-27 09:23 151浏览
  • 在电子设计中,电磁兼容性(EMC)是确保设备既能抵御外部电磁干扰(EMI),又不会对自身或周围环境产生过量电磁辐射的关键。电容器、电感和磁珠作为三大核心元件,通过不同的机制协同作用,有效抑制电磁干扰。以下是其原理和应用场景的详细解析:1. 电容器:高频噪声的“吸尘器”作用原理:电容器通过“通高频、阻低频”的特性,为高频噪声提供低阻抗路径到地,形成滤波效果。例如,在电源和地之间并联电容,可吸收电源中的高频纹波和瞬态干扰。关键应用场景:电源去耦:在IC电源引脚附近放置0.1μF陶瓷电容,滤除数字电路
    时源芯微 2025-03-27 11:19 152浏览
  • 文/陈昊编辑/cc孙聪颖‍2025 年,作为中国实施制造强国战略第一个十年计划的关键里程碑,被赋予了极为重大的意义。两会政府工作报告清晰且坚定地指出,要全力加速新质生产力的发展进程,推动传统产业全方位向高端化、智能化与绿色化转型。基于此,有代表敏锐提议,中国制造应从前沿技术的应用切入,逐步拓展至产业生态的构建,最终延伸到提升用户体验的维度,打出独树一帜、具有鲜明特色的发展牌。正是在这样至关重要的时代背景之下,于 AWE 2025(中国家电及消费电子博览会)这一备受瞩目的舞台上,高端厨房的中国方案
    华尔街科技眼 2025-03-25 16:10 82浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦