DIY编程器网

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1183|回复: 0
打印 上一主题 下一主题

[开发板] ARM开发板上uClinux内核移植

[复制链接]
跳转到指定楼层
楼主
发表于 2012-1-17 23:40:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
简述:针对“如何在以S3C44B0X为核心的ARMSYS开发板上建立uClinux内核移植”的一个总结,其内容包括对Bootloader的功能分析和uClinux2.4.24发行版内核基础上针对S3C44B0X开发板进行修改的重点内容的逐一列举。2.Bootloader2.1Bootloader概述Boot Loader 就是在操作系统内核运行之前运行的一段程序。通过这段程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。因此,正确建立uClinux的移植的前提条件是具备一个与uClinux配套、易于使用的 Bootloader。ARMSYS开发板提供了这样一个uClinux专用的Bootloader,该Bootloader程序烧录在系统的地址0x0处,每次上电即运行,能够正确完成硬件系统的初始化和uClinux的引导。理论上,uClinux引导时并非一定需要一个独立于内核的Bootloader。然而,将Bootloader与内核分开设计能够使软件架构更加清晰,也有助于灵活地支持多种引导方式,实现一些有用的辅助功能。ARMSYS提供的Bootloader的主要任务可以概括如下:  1.硬件初始化;  2.从主机下载新的内核映像和文件系统映像;3.烧写NorFlash和Nandflash;  4.加载uClinux 内核映像并启动运行;  5.提供串行超级终端上的人机操作界面。2.2存储空间分布Bootloader采用默认的存储空间分布地址来加载uClinux内核、文件系统,并按照正确引导uClinux的运行。在ARMSYS的Bootloader中,默认的存储空间分布如下表:             内容                      起始地址             存储介质Bootloader程序空间         0x00000000           Flash压缩内核映像                     0x00010000           FlashROM文件系统映像               0x000e0000           Flash内核运行地址                     0x0c008000           SDRAM压缩内核解压地址             0x0c100000           SDRAM文件系统加载                     0x0c700000           SDRAM这个存储空间的分配方式也不是固定不变的,可以通过修改Bootloader中的相关代码来改变。2.3Bootloader的工作完整的Bootloader引导流程可描述如下:硬件初始化阶段一◎ 硬件初始化◎ 复制二级中断异常矢量表◎ 初始化各种处理器模式◎ 复制RO和RW,清零ZI (跳转到C代码入口函数)硬件初始化阶段二◎ 初始化本阶段使用到的硬件设备;◎ 建立人机界面◎ 实现映像文件的下载和烧录工具◎ 实现映像文件的加载和运行工具下面对上述各步骤进行逐一说明,并对与uClinux相关的内容详细加以说明。2.3.1 硬件初始化板子上电或复位后,程序从位于地址0x0的Reset Exception Vector处开始执行,因此需要在这里放置Bootloader的第一条指令:b ResetHandler,跳转到标号为ResetHandler处进行第一阶段的硬件初始化,主要内容为:关Watchdog Timer,关中断,初始化PLL和时钟,初始化存储器控制器。比较重要的是PLL的输出频率要计算正确,ARMSYS中把它设置为64MHz;这实际上就是处理器的工作主频,这个时间参数在第二阶段计算SDRAM的刷新计数值和UART的波特率等参数时还要用到。2.3.2建立二级异常中断矢量表   异常中断矢量表(Exception Vector Table)是Bootloader与uClinux内核发生联系关键的地方之一。即使uClinux内核已经得到处理器的控制权运行,一旦发生中断,处理器还是会自动跳转到从0x0地址开始的第一级异常中断矢量表中的某个表项(依据于中断类型)处读取指令运行。在编写 Bootloader时,地址0x0处的一级异常中断矢量表只需简单地包含向二级异常中断矢量表的跳转指令就可以。这样,就能够正确地将发生的事件交给 uClinux的中断处理程序来处理。对于uClinux内核,它在RAM空间中基地址为0xc000000处建立了自己的二级异常中断矢量表,因此, Bootloader的第一级异常中断矢量表如下所示:b ResetHandler ;Reset Handlerldr pc,=0x0c000004 ;Undefined Instruction Handlerldr pc,=0x0c000008 ;Software Interrupt Handlerldr pc,=0x0c00000c refetch Abort Handlerldr pc,=0x0c000010 ;Data Abort Handlerb .ldr pc,=0x0c000018 ;IRQ Handlerldr pc,=0x0c00001c ;FIQ HandlerLTORG如果在Bootloader执行的全过程中都不必响应中断,那么上面的设置已能满足要求。但在我们的 ARMSYS上提供了USB下载器,需要用到中断,那么Bootloader必须在同样的地址(0xc000000)处配置自己的二级异常中断矢量表,以便同uClinux兼容。这张表事先存放在 Flash Memory里,引导过程中由Bootloader将其复制到RAM地址0x0C000000:存放矢量表:;IRQ ==the program put this phrase to 0xc000000ExceptionHanlderBeginb .ldr pc, MyHandleUndef ; HandlerUndefldr pc, MyHandleSWI ; HandlerSWIldr pc, MyHandlePabort ; HandlerPabortldr pc, MyHandleDabort ; HandlerDAbortb . ; HandlerReservedldr pc, MyHandleIRQ ; HandlerIRQldr pc, MyHandleFIQ ; HandlerFIQMyHandleUndef DCD HandleUndef ;reserve a word(32bit)MyHandleSWI DCD HandleSWIMyHandlePabort DCD HandlePabortMyHandleDabort DCD HandleDabortMyHandleIRQ DCD HandleIRQMyHandleFIQ DCD HandleFIQExceptionHanlderEnd建立二级矢量表:;****************************************************;* Setup IRQ handler *;****************************************************ldr r0,=(_IRQ_BASEADDRESS + 0x100)ldr r2,=_IRQ_BASEADDRESSadd r3,r0, #0x1000CMP r0, r3STRCC r2, [r0], #4;cc:Carry clear;save R2 to R0 address, R0 =R0+ 4。BCC %B0ldr r1,=_IRQ_BASEADDRESSldr r0,=ExceptionHanlderBegin ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1cldr r3,=ExceptionHanlderEnd0CMP r0, r3 ;put the vector table at _IRQ_BASEADDRESS(0xc000000)LDRCC r2, [r0], #4STRCC r2, [r1], #4BCC %B0ldr r1,=DIsrIRQ;put the IRQ judge program at _IRQ_BASEADDRESS+0x80(0xc000080)ldr r0,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1cldr r3,=IsrIRQEnd0CMP r0, r3LDRCC r2, [r0], #4STRCC r2, [r1], #4BCC %B0ldr r1, =MyHandleIRQ ;MyHandleIRQ point to DIsrIRQldr r0, =ExceptionHanlderBeginldr r4, =_IRQ_BASEADDRESS;sub r0, r1, r0add r0, r0,r4ldr r1, =DIsrIRQstr r1, [r0]定义Handlexxx:^ (_IRQ_BASEADDRESS)HandleReset # 4HandleUndef # 4HandleSWI # 4HandlePabort # 4HandleDabort # 4HandleReserved # 4HandleIRQ # 4HandleFIQ # 4^ (_IRQ_BASEADDRESS+0x80)DIsrIRQ # 4;IntVectorTable^ (_IRQ_BASEADDRESS+0x100)HandleADC # 4HandleRTC # 4HandleUTXD1 # 4HandleUTXD0 # 4HandleSIO # 4HandleIIC # 4HandleURXD1 # 4HandleURXD0 # 4HandleTIMER5 # 4HandleTIMER4 # 4HandleTIMER3 # 4HandleTIMER2 # 4HandleTIMER1 # 4HandleTIMER0 # 4HandleUERR01 # 4HandleWDT # 4HandleBDMA1 # 4HandleBDMA0 # 4HandleZDMA1 # 4HandleZDMA0 # 4HandleTICK # 4HandleEINT4567 # 4HandleEINT3 # 4HandleEINT2 # 4HandleEINT1 # 4HandleEINT0 # 4将异常中断矢量重构到SDRAM,这样的好处就是可以在其它的功能程序内对中断处理程序的地址任意赋值。为此,我们在44b.h文件中定义:/* ISR */#define pISR_RESET (*(unsigned *)(_IRQ_BASEADDRESS+0x0))#define pISR_UNDEF (*(unsigned *)(_IRQ_BASEADDRESS+0x4))#define pISR_SWI (*(unsigned *)(_IRQ_BASEADDRESS+0x8))#define pISR_PABORT (*(unsigned *)(_IRQ_BASEADDRESS+0xc))#define pISR_DABORT (*(unsigned *)(_IRQ_BASEADDRESS+0x10))#define pISR_RESERVED (*(unsigned *)(_IRQ_BASEADDRESS+0x14))#define pISR_IRQ (*(unsigned *)(_IRQ_BASEADDRESS+0x18))#define pISR_FIQ (*(unsigned *)(_IRQ_BASEADDRESS+0x1c))#define pISR_ADC (*(unsigned *)(_IRQ_BASEADDRESS+0x100))//0x20))#define pISR_RTC (*(unsigned *)(_IRQ_BASEADDRESS+0x104))//0x24))#define pISR_UTXD1 (*(unsigned *)(_IRQ_BASEADDRESS+0x108))//0x28))#define pISR_UTXD0 (*(unsigned *)(_IRQ_BASEADDRESS+0x10c))//0x2c))#define pISR_SIO (*(unsigned *)(_IRQ_BASEADDRESS+0x110))//0x30))#define pISR_IIC (*(unsigned *)(_IRQ_BASEADDRESS+0x114))//0x34))#define pISR_URXD1 (*(unsigned *)(_IRQ_BASEADDRESS+0x118))//0x38))#define pISR_URXD0 (*(unsigned *)(_IRQ_BASEADDRESS+0x11c))//0x3c))#define pISR_TIMER5 (*(unsigned *)(_IRQ_BASEADDRESS+0x120))//0x40))#define pISR_TIMER4 (*(unsigned *)(_IRQ_BASEADDRESS+0x124))//0x44))#define pISR_TIMER3 (*(unsigned *)(_IRQ_BASEADDRESS+0x128))//0x48))#define pISR_TIMER2 (*(unsigned *)(_IRQ_BASEADDRESS+0x12c))//0x4c))#define pISR_TIMER1 (*(unsigned *)(_IRQ_BASEADDRESS+0x130))//0x50))#define pISR_TIMER0 (*(unsigned *)(_IRQ_BASEADDRESS+0x134))//0x54))#define pISR_UERR01 (*(unsigned *)(_IRQ_BASEADDRESS+0x138))//0x58))#define pISR_WDT (*(unsigned *)(_IRQ_BASEADDRESS+0x13c))//0x5c))#define pISR_BDMA1 (*(unsigned *)(_IRQ_BASEADDRESS+0x140))//0x60))#define pISR_BDMA0 (*(unsigned *)(_IRQ_BASEADDRESS+0x144))//0x64))#define pISR_ZDMA1 (*(unsigned *)(_IRQ_BASEADDRESS+0x148))//0x68))#define pISR_ZDMA0 (*(unsigned *)(_IRQ_BASEADDRESS+0x14c))//0x6c))#define pISR_TICK (*(unsigned *)(_IRQ_BASEADDRESS+0x150))//0x70))#define pISR_EINT4567 (*(unsigned *)(_IRQ_BASEADDRESS+0x154))//0x74))#define pISR_EINT3 (*(unsigned *)(_IRQ_BASEADDRESS+0x158))//0x78))#define pISR_EINT2 (*(unsigned *)(_IRQ_BASEADDRESS+0x15c))//0x7c))#define pISR_EINT1 (*(unsigned *)(_IRQ_BASEADDRESS+0x160))//0x80))#define pISR_EINT0 (*(unsigned *)(_IRQ_BASEADDRESS+0x164))//0x84))    例如,我们要使用到Exint4567中断,定义好中断处理程序Meint4567Isr()后,仅需要一条语句:    pISR_EINT4567=(int)MEint4567Isr;就能使中断发生后正确跳转到我们编写的处理程序上。2.3.3 初始化各种处理器模式ARM7TDMI支持7种Operation Mode:User,FIQ,IRQ,Supervisor,Abort,System和Undefined。Bootloader需要依次切换到每种模式,初始化其程序状态寄存器(SPSR)和堆栈指针(SP)。2.3.4 复制RO和RW,清零ZI一个ARM由RO,RW和ZI三个段组成,其中RO为代码段,RW是已初始化的全局变量,ZI是未初始化的全局变量(对于GNU工具,对应的概念是 TEXT,DATA和BSS)。Bootloader要将RW段复制到RAM中,并将ZI段清零。编译器使用下列符号来记录各段的起始和结束地址:|Image$$RO$$Base| :RO段起始地址|Image$$RO$$Limit| :RO段结束地址加1|Image$$RW$$Base| :RW段起始地址|Image$$RW$$Limit| :ZI段结束地址加1|Image$$ZI$$Base| :ZI段起始地址|Image$$ZI$$Limit| :ZI段结束地址加1需要注意的是,这些标号的值是根据链接器中设置的中ro-base和rw-base的设置来计算的,我们的Bootloader的对应设置是:ro-base = 0xc000000, rw-base = 0xc5f0000。完成这个步骤后,第一阶段的硬件初始化就完成了。    BL Main跳转到C语言程序,开始第二阶段的初始化和系统引导。2.3.5 C语言中的硬件初始化继续对硬件进行初始化,主要包括对以下设备的初始化:GPIO,Cache,Interrupt Controller,Watchdog Timer和UARTs。S3C44B0X处理器内置data/instruction合一的8KB Cache,且允许按地址范围设置两个Non-Cacheable区间。合理的配置是打开对RAM区间的Cache,关闭对其它地址区间(非存储器设备, I/O设备 )的Cache。所有硬件初始化完毕之后,开中断。2.3.6 建立人机界面引导过程的最后一步是在串行终端上建立人机界面,并等待用户输入命令。若接收到用户输入,则显示菜单模式或命令行模式的交互界面,等待用户进一步的命令。这里就不对此详细讨论了。2.4加载uClinux内核ARMSYS 提供的Bootloader支持两种uClinux启动运行方式:直接从SDRAM中的内核映像中运行;从flash将压缩格式的内核映像加载到 SDRAM,再从SDRAM运行。前者需要利用Bootloader提供的对映像文件下载的工具;后者则需要利用Bootloader提供的 flash烧录工具进行烧录,然后再加载运行。压缩格式的uClinux内核映像文件都是由开头的一段自解压代码和后面的压缩数据部分组成。对于Kernel而言,由于是以压缩格式存放,因次只能以非XIP方式执行。自解压类型的uClinux 内核映像文件首先存放在Flash Memory中,由Bootloader加载到SDRAM中的0xc100000地址处,然后将控制权交给它。可执行的uClinux Kernel将被解压到最终的执行空间,然后开始运行。压缩格式Image所占据的临时SDRAM空间可在随后由uClinux回收利用。可以从flash拷贝到SDRAM解压运行,自然同样也可以直接下载到SDRAM运行。这对于调试内核都是非常方便的。对于压缩格式的内核映像文件(image.rom和image.ram)都可以直接下载到SDRAM的特定地址处,并从该地址开始运行(参考2.2节)。2.5调用KernelBootloader调用uClinux 内核的方法是直接跳转到Kernel的第一条指令处。采用C语句:((void (*)(void))ram_addr)();2.6工具ARMSYS 的Bootloader在人机界面上提供了8个功能项目,其中包括支持从主机通过USB口下载文件到目标板的SDRAM和Nandflash上;用 SDRAM中的数据烧写Flash Memory。由于USB口下载速度快,利用这些功能项能够轻松地调试uClinux的内核(具体使用方法参考《uClinux移植包在ARMSYS上的使用说明》一文)。对uClinux专用Bootloader的介绍到此,下面开始对uClinux的内核部分的移植进行说明。3.uClinux2.4.24内核组成◎arch: arch目录下有多个子目录,它的每一个子目录都代表内核支持的一种CPU体系结构,每个子目录中又进一步分解为boot、mm、 kernel等子目录,分别包含与系统引导、内存管理、系统调用的进入和返回、终端处理以及其它内核中依赖于CPU和系统结构的底层代码。与ARM处理器(不带有MMU)相关的代码放在目录arch/armnommu下,与S3C44B0X相关的代码则放在目录arch/armnommu/match- S3C44B0X。◎ include:include子目录包括编译核心所需要的大部分头文件。与平台无关的头文件在 include/linux子目录下,与ARM处理器(不带MMU)相关的头文件在include/asm-armnommu子目录下,与 S3C44B0X相关的代码在include/asm-armnommu/arch-S3C44B0X目录下;◎ init:这个目录包含核心的初始化代码(注意:不是系统的引导代码),包含两个文件main.c和Version.c,这是研究核心如何工作的一个非常好的起点。◎ kernel:主要的核心代码,此目录下的文件实现了大多数linux系统的内核函数,其中最重要的文件当属sched.c;同样,和体系结构相关的代码在arch/*/kernel中;◎ drivers: 放置系统所有的设备驱动程序;每种驱动程序又各占用一个子目录:如,/block 下为块设备驱动程序,比如ide(ide.c)。◎ 其他:例如mm ,这个目录包括所有独立于处理器体系结构的内存管理代码,如页式存储管理内存的分配和释放等;lib放置核心的库代码;net,核心与网络相关的代码; ipc,这个目录包含核心的进程间通讯的代码;fs,所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录支持一个文件系统,例如fat和 ext2;Scripts,此目录包含用于配置核心的脚本文件等。Linux Kernel Tree List一般在每个目录下,都有一个.depend 文件和一个 Makefile 文件,这两个文件都是编译时使用的辅助文件,仔细阅读这两个文件对弄清各个文件这间的联系和依托关系很有帮助;而且,在有的目录下还有Readme 文件,它是对该目录下的文件的一些说明,同样有利于我们对内核源码的理解。uClinux-dist-20040408发行包中的内核对S3C44B0X处理器的支持是不完整的,因此,我们不能够希望在make config配置选项中选中44B0X目标板后,直接编译它来得到一个很好地支持44B0X开发板的内核映像,我们必须为内核打上补丁。针对ARMSYS 的补丁文件可以在http://www.hzlitai.com.cn/download/default.asp地址处下载得到。这款补丁是众多爱好 uClinux和44B0X处理器的网友们共同完成的。下一节我们就对这个补丁中的主要修改项目进行介绍和分析,从中了解uClinux移植中的要点。
                          
                       
                          
                                4.移植项目及说明4.1压缩内核代码起始地址修改修改文件:uClinux-dist/linux-2.4.x/arch/armnommu/boot/Makefile修改内容:ifeq ($(CONFIG_BOARD_MBA44),y)ZTEXTADDR = 0x0c100000ZRELADDR = 0x0c008000endif说明:ZTEXTADDR:自解压代码的起始地址。ZRELADDR:内核解压后代码输出起始地址。4.2处理器配置选项的修改修改文件:uClinux-dist/linux-2.4.x/arch/armnommu/config.in修改内容:define_bool CONFIG_NO_PGT_CACHE ydefine_bool CONFIG_CPU_WITH_CACHE ydefine_bool CONFIG_CPU_WITH_MCR_INSTRUCTION n- define_int CONFIG_ARM_CLK 60000000- define_bool CONFIG_SERIAL_S3C44B0X y- define_int CONFIG_FORCE_MAX_ZONEORDER 5+ define_int CONFIG_ARM_CLK 64000000 #72000000+# define_bool CONFIG_SERIAL_S3C44B0X y+# define_int CONFIG_FORCE_MAX_ZONEORDER 5if [ "$CONFIG_SET_MEM_PARAM" = "n" ]; then- define_hex DRAM_BASE 0x00000000+ define_hex DRAM_BASE 0x0C000000define_hex DRAM_SIZE 0x00800000- define_hex FLASH_MEM_BASE 0x01000000+ define_hex FLASH_MEM_BASE 0x00000000define_hex FLASH_SIZE 0x00200000fifi……说明:修改了对ARM处理器主频的定义:define_int CONFIG_ARM_CLK 64000000修改了存储器大小和起始地址的定义:define_hex DRAM_BASE 0x0C000000;SDRAM的起始地址define_hex DRAM_SIZE 0x00800000;SDRAM的大小define_hex FLASH_MEM_BASE 0x00000000;flash的起始地址define_hex FLASH_SIZE 0x00200000;flash的大小4.3内核起始地址的修改修改文件:uClinux-dist/linux-2.4.x/arch/armnommu/Makefile修改内容:ifeq ($(CONFIG_BOARD_MBA44),y)-TEXTADDR = 0x0c000000+TEXTADDR = 0x0c008000MACHINE = S3C44B0XINCDIR = $(MACHINE)-CORE_FILES := $(CORE_FILES) romfs.o+CORE_FILES := $(CORE_FILES) #romfs.oendif说明:TEXTADDR:内核的起始地址,通常取值:DRAM_BASE+0x8000。4.4ROM文件系统的定位修改修改文件:uClinux-dist/linux-2.4.x/drivers/block/blkmem.c修改内容:+#ifdef CONFIG_BOARD_MBA44+ {0, 0xc700000, -1},/*{0, 0x100000, -1},*/#endif说明:将ROM file system在SDRAM中的地址定位在0xc700000。4.5修改存储空间配置修改文件:uClinux-dist/linux-2.4.x/include/asm-armnommu/arch-S3C44B0X/memory.h修改内容:-#define PHYS_OFFSET (DRAM_BASE + 2*1024*1024)+#define PHYS_OFFSET (DRAM_BASE)//(DRAM_BASE + 2*1024*1024)#define PAGE_OFFSET (PHYS_OFFSET)-#define END_MEM (DRAM_BASE + DRAM_SIZE - 2*1024*1024)+#define END_MEM (DRAM_BASE+DRAM_SIZE)//(DRAM_BASE + DRAM_SIZE - 2*1024*1024)说明:PHYS_OFFSET:RAM第一个bank的起始地址。4.6初始化节拍定时器修改文件:uClinux-dist/linux-2.4.x/include/asm-armnommu/arch-S3C44B0X/time.h修改内容:-extern void s3c44b0x_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs);+//extern void s3c44b0x_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs);+static inline void s3c44b0x_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)+{+ do_leds();+ do_timer(regs);+}//modified by hzh/* TODO: THE, 2003-08-13, do timer setup like in eCos */-#define S3C44B0X_TIMER5_PRESCALER 16+#define S3C44B0X_TIMER5_PRESCALER 32extern __inline__ void setup_timer (void){u_int32_t tmod;u_int32_t period;+ __u32 rw_tmp;period = (CONFIG_ARM_CLK/S3C44B0X_TIMER5_PRESCALER)/HZ;outl(period, S3C44B0X_TCNTB5);@@ -36,8 +42,14 @@outl(tmod, S3C44B0X_TCON);/* initialize the timer period and prescaler */- outl((5-1) base_addr = base_addr = 0x08000000;说明:修改了以太网设备的基地址。5.移植的步骤5.1解压uClinux-dist发行包到以下地址下载uClinux-dist-20040408.tar.gz源代码包:http://www.uclinux.org/pub/uClinux/dist/uClinux-dist-20040408.tar.gz该版本在很多方面比早先的20030522版本要完善很多,这也使我们的移植工作变得方便很多。其中使用的内核版本是Linux 2.4.24。以下工作在装有Linux操作系统(例如RedHat9.0)的PC机上进行。将uClinux-dist-20040408.tar.gz拷贝到/home/下(或者其它目录都可以),运行解压命令:tar xvzf uClinux-ARMSYS-20040801.tar.gz解压结束后会在/home/下生成uClinux-dist目录。5.2安装补丁到以下地址下载补丁文件:http://www.hzlitai.com.cn/download/uClinux-20040408-ARMSYS.rar解压后产生patch文件,安装patch文件:patch –p1 出现以上信息后,可以尝试从键盘输入ls、ping命令,来查看系统的运行情况。我们还建议读者按照uClinux-dist Documentation下的Adding-User-Apps-HOWTO文档编写一个简单的Helloworld应用程序,看是否能够正确运行。
                          
                       
                          
                               
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|文字版|手机版|DIY编程器网 ( 桂ICP备14005565号-1 )

GMT+8, 2024-4-24 00:14 , 耗时 0.083411 秒, 18 个查询请求 , Gzip 开启.

各位嘉宾言论仅代表个人观点,非属DIY编程器网立场。

桂公网安备 45031202000115号

DIY编程器群(超员):41210778 DIY编程器

DIY编程器群1(满员):3044634 DIY编程器1

diy编程器群2:551025008 diy编程器群2

QQ:28000622;Email:libyoufer@sina.com

本站由桂林市临桂区技兴电子商务经营部独家赞助。旨在技术交流,请自觉遵守国家法律法规,一旦发现将做封号删号处理。

快速回复 返回顶部 返回列表