DM365的UBL源码分析

Firmware固件的打包、设计、实现与使用 RT-OTA 是 RT-Thread 开发的跨 OS、跨芯片平台的固件空中升级技术(Firmware Over-the-Air Technology),轻松实现对设备端固件的管理、升级与维护。本文从BootLoader 的构建,app应用程序的开发以及RBL打包器的原理一步步实现,帮助大家更好更快的理解OTA的流程和设计理念。 阅读详情

DM365是的启动方式有两种,通过BOOTSEL[2:0]引脚决定。当其为001时,直接从AEMIF上启 动,比如NOR和OneNAND。除此之外皆是从RBL启动,顺序为RBL-UBL-UBOOT-KERNEL,比如NAND,串口,SD卡等。RBL会 搜寻block1到block24去找UBL,关于RBL启动的详细细节可以参考用户指南关于ARM子系统的那篇文档,很详尽,下面只分析UBL的源码。

 
      UBL源码在PSP包里的board_utilities\flash_utils目录下,主要是COMMON目录和各子平台的目录如DM36x等,内中除了UBL的源码外还有CCS下JTAG的擦除烧写源码,串口烧写源码等。下面只分析UBL的启动代码。
 
      入门代码是汇编文件start.S,主要是切换操作模式,建立堆栈等,然后跳转到main函数,进入到Common\ubl\src目录下的C文件ubl.c中。main函数如下:
 

// Main entry point

void main(void)
{
    
    // Call to real boot function code

    LOCAL_boot();
    
    // Jump to entry point

    DEBUG_printString("\r\nJumping to entry point at ");
    DEBUG_printHexInt(gEntryPoint);
    DEBUG_printString(".\r\n");
    APPEntry = (void(*)(void)) gEntryPoint;
    (*APPEntry)();     
}

      main函数主要调用了LOCAL_boot函数来进行实质的引导功能,下面是此函数的内容:

static Uint32 LOCAL_boot(void)
{
    DEVICE_BootMode bootMode;
    
    // Read boot mode

    bootMode = DEVICE_bootMode();
    
    if (bootMode== DEVICE_BOOTMODE_UART)
    {
        // Wait until the RBL is done using the UART.

        while((UART0->LSR& 0x40)== 0 );
    }
    
    // Platform Initialization

    if ( DEVICE_init()!= E_PASS)
    {
        DEBUG_printString(devString);
        DEBUG_printString(" initialization failed!\r\n");
        asm(" MOV PC, #0");
    }
    else
    {
        DEBUG_printString(devString);
        DEBUG_printString(" initialization passed!\r\n");
    }
    
    // Set RAM pointer to beginning of RAM space

    UTIL_setCurrMemPtr(0);
    
    // Send some information to host

    DEBUG_printString("TI UBL Version: ");
    DEBUG_printString(UBL_VERSION_STRING);
    DEBUG_printString("\r\nBooting Catalog Boot Loader\r\nBootMode = ");
    
    // Select Boot Mode

#if defined(UBL_NAND)
    {
        //Report Bootmode to host

        DEBUG_printString("NAND\r\n");
        
        // Copy binary image application from NAND to RAM

        if (NANDBOOT_copy()!= E_PASS)
        {
            DEBUG_printString("NAND Boot failed.\r\n");
            LOCAL_bootAbort();
        }
    }
#elif defined(UBL_NOR)
    {
        //Report Bootmode to host

        DEBUG_printString("NOR \r\n");
        
        // Copy binary application image from NOR to RAM

        if (NORBOOT_copy()!= E_PASS)
        {
            DEBUG_printString("NOR Boot failed.\r\n");
            LOCAL_bootAbort();
        }
    }
#elif defined(UBL_SD_MMC)
    {
        //Report Bootmode to host

        DEBUG_printString("SD/MMC \r\n");
        
        // Copy binary of application image from SD/MMC card to RAM

        if (SDMMCBOOT_copy()!= E_PASS)
        {
            DEBUG_printString("SD/MMC Boot failed.\r\n");
            LOCAL_bootAbort();
        }
    }
#else
    {
        //Report Bootmode to host

        DEBUG_printString("UART\r\n");
        UARTBOOT_copy();
    }
    
    
#endif
    
    DEBUG_printString(" DONE");
    
    UTIL_waitLoop(10000);
    
    DEVICE_TIMER0Stop();
    
    return E_PASS;
}


      先通过调用DEVICE_bootMode函数来判断启动方式(通过读取SYS寄存器实现),而后调用了DEVICE_init函数来进行平台的最底层初始化,包括电源域,时钟,DDR,EMIF,UART,I2C,TIMER等,另有专篇分析。

      而后通过UTIL_setCurrMemPtr函数对全局变量currMemPtr赋值,以后用到。接着通过判断不同的引导方式,采取不同的处理办法,以 NAND启动为例,将调用NANDBOOT_copy函数,此函数另有专篇分析。此函数将NAND中的某些内容(就是UBOOT)搬移到RAM中,而后 UBL结束,控制权正式交给UBOOT。



书接上回,看看UBL对平台的初始化,主要是调用了DEVICE_init函数,函数内容如下:

Uint32 DEVICE_init()
{
    Uint32 status = E_PASS;
    
    // Mask all interrupts
    AINTC->INTCTL= 0x4;
    AINTC->EABASE= 0x0;
    AINTC->EINT0= 0x0;
    AINTC->EINT1= 0x0;        
    
    // Clear all interrupts
    AINTC->FIQ0= 0xFFFFFFFF;
    AINTC->FIQ1= 0xFFFFFFFF;
    AINTC->IRQ0= 0xFFFFFFFF;
    AINTC->IRQ1= 0xFFFFFFFF;
    
#ifndef SKIP_LOW_LEVEL_INIT
    
    POR_RESET();
    
    // System PSC setup - enable all
    DEVICE_PSCInit();
    
    DEVICE_pinmuxControl(0,0xFFFFFFFF,0x00FD0000);// All Video Inputs

    DEVICE_pinmuxControl(1,0xFFFFFFFF,0x00145555);// All Video Outputs

    DEVICE_pinmuxControl(2,0xFFFFFFFF,0x000000DA);// EMIFA

    DEVICE_pinmuxControl(3,0xFFFFFFFF,0x00180000);// SPI0, SPI1, UART1, I2C, SD0, SD1, McBSP0, CLKOUTs

    DEVICE_pinmuxControl(4,0xFFFFFFFF,0x55555555);// MMC/SD0 instead of MS, SPI0

    
    GPIO->DIR02&= 0xfeffffff;
    GPIO->CLRDATA02= 0x01000000;
    
    // System PLL setup
    if (status== E_PASS) status|= DEVICE_PLL1Init(PLL1_Mult);
    
    // DDR PLL setup
    if (status== E_PASS) status|= DEVICE_PLL2Init();
    
    // DDR2 module setup
    if (status== E_PASS) status|= DEVICE_DDR2Init();
#endif
    
    // AEMIF Setup
    if (status== E_PASS) status|= DEVICE_EMIFInit();
    
    // UART0 Setup
    if (status== E_PASS) status|= DEVICE_UART0Init();
    
    // TIMER0 Setup
    if (status== E_PASS) status|= DEVICE_TIMER0Init();
    
    // I2C0 Setup
    if (status== E_PASS) status|= DEVICE_I2C0Init();
    
    return status;
}

      首先屏蔽和清除中断,然后调用DEVICE_PSCInit函数实现对各模块的电源时钟使能,实质是调用PSC电源时钟管理模块的寄存器实现,函数内容如下:

void DEVICE_PSCInit()
{    
    unsigned char i=0;
    unsigned char lpsc_start;
    unsigned char lpsc_end,lpscgroup,lpscmin,lpscmax;
    unsigned int PdNum= 0;
    
    lpscmin =0;
    lpscmax =2;
    
    for(lpscgroup=lpscmin; lpscgroup<=lpscmax; lpscgroup++){
        if(lpscgroup==0)
        {
            lpsc_start = 0;// Enabling LPSC 3 to 28 SCR first

            lpsc_end = 28;
        }
        else if(lpscgroup== 1){/* Skip locked LPSCs [29-37] */
            lpsc_start = 38;
            lpsc_end = 47;
        } else{
            lpsc_start = 50;
            lpsc_end = 51;
        }
        
        //NEXT=0x3, Enable LPSC's
        for(i=lpsc_start; i<=lpsc_end; i++){
            PSC->MDCTL[i]|= 0x3;
        }
        
        //Program goctl to start transition sequence for LPSCs
        PSC->PTCMD=(1<<PdNum);
        
        //Wait for GOSTAT = NO TRANSITION from PSC for Pdomain 0
        while(!(((PSC->PTSTAT>> PdNum)& 0x00000001)== 0));
        
        //Wait for MODSTAT = ENABLE from LPSC's
        for(i=lpsc_start; i<=lpsc_end; i++){
            while(!((PSC->MDSTAT[i]& 0x0000001F)== 0x3));             
        }    
    }    
    
}

      然后调用DEVICE_pinmuxControl函数决定复用引脚的功能选择,详见数据手册查看引脚功能。

      接着调用DEVICE_PLL1Init函数实现了PLL1的配置,预分频,倍频,后分频,分频到各个模块,其设置顺序可以参看用户指南ARM子系统文档,有详细的介绍,PLL2类似不再赘述,函数内容如下:

Uint32 DEVICE_PLL1Init(Uint32 PLLMult)
{
    unsigned int CLKSRC=0x0;             
    unsigned int j;
    
    /*Power up the PLL*/
    PLL1->PLLCTL&= 0xFFFFFFFD;        
    
    PLL1->PLLCTL&= 0xFFFFFEFF;             
    PLL1->PLLCTL|= CLKSRC<<8;
    
    /*Set PLLENSRC '0', PLL Enable(PLLEN) selection is controlled through MMR*/
    PLL1->PLLCTL&= 0xFFFFFFDF;    
    
    /*Set PLLEN=0 => PLL BYPASS MODE*/
    PLL1->PLLCTL&= 0xFFFFFFFE;
    
    UTIL_waitLoop(150);                 
    
    // PLLRST=1(reset assert)
    PLL1->PLLCTL|= 0x00000008; 
    
    UTIL_waitLoop(300);
    
    /*Bring PLL out of Reset*/
    PLL1->PLLCTL&= 0xFFFFFFF7;
    
    //Program the Multiper and Pre-Divider for PLL1
    PLL1->PLLM= 0x51;// VCO will 24*2M/N+1 = 486Mhz

    PLL1->PREDIV= 0x8000|0x7;
    
    PLL1->SECCTL= 0x00470000;// Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 1 
    PLL1->SECCTL= 0x00460000;// Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 0 
    PLL1->SECCTL= 0x00400000;// Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 0 
    PLL1->SECCTL= 0x00410000;// Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 1    
  
    //Program the PostDiv for PLL1
    PLL1->POSTDIV= 0x8000;
    
    // Post divider setting for PLL1     
    PLL1->PLLDIV2= 0x8001;
    PLL1->PLLDIV3= 0x8001;// POST DIV 486/2 -> MJCP/HDVICP
    PLL1->PLLDIV4= 0x8003;// POST DIV 486/4 -> EDMA/EDMA CFG
    PLL1->PLLDIV5= 0x8001;// POST DIV 486/2 -> VPSS
    PLL1->PLLDIV6= 0x8011;// 27Mhz POST DIV 486/18 -> VENC
    PLL1->PLLDIV7= 0x8000;// POST DIV 486/2 -> DDR
    PLL1->PLLDIV8= 0x8003;// POST DIV 486/4 -> MMC0/SD0
    PLL1->PLLDIV9= 0x8001;// POST DIV 486/2 -> CLKOUT

    UTIL_waitLoop(300);
    
    /*Set the GOSET bit */
    PLL1->PLLCMD= 0x00000001;// Go

    UTIL_waitLoop(300);
        
    /*Wait for PLL to LOCK */
    while(!(((SYSTEM->PLL0_CONFIG)& 0x07000000)== 0x07000000));    
    
    /*Enable the PLL Bit of PLLCTL*/
    PLL1->PLLCTL|= 0x00000001;// PLLEN=0
  
    return E_PASS;
}



继续在DEVICE_init函数中,下面是调用DEVICE_DDR2Init函数来配置DDR控制器,这是 UBL中重要的一部分,如果硬件电路需要更换内存芯片的话,需要在UBL中修改这个函数,即按照芯片手册来配置DDR控制寄存器中的相关参数,比如时 序,BANK数,页大小等。这个函数主要是操作SYS模块和DDR模块的相关寄存器来配置内存,函数中调用的DEVICE_LPSCTransition 函数用来实现模块的电源时钟状态的改变,函数内容如下:

Uint32 DEVICE_DDR2Init()
{
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE);
    
    SYSTEM->VTPIOCR=(SYSTEM->VTPIOCR)& 0xFFFF9F3F;
    
    // Set bit CLRZ (bit 13)
    SYSTEM->VTPIOCR=(SYSTEM->VTPIOCR)| 0x00002000;
    
    // Check VTP READY Status
    while( !(SYSTEM->VTPIOCR& 0x8000));
    
    // Set bit VTP_IOPWRDWN bit 14 for DDR input buffers)
    //SYSTEM->VTPIOCR = SYSTEM->VTPIOCR | 0x00004000; 

    // Set bit LOCK(bit7) and PWRSAVE (bit8)
    SYSTEM->VTPIOCR=SYSTEM->VTPIOCR| 0x00000080;
    
    // Powerdown VTP as it is locked (bit 6)
    // Set bit VTP_IOPWRDWN bit 14 for DDR input buffers)
    SYSTEM->VTPIOCR=SYSTEM->VTPIOCR| 0x00004040;
    
    // Wait for calibration to complete 
    UTIL_waitLoop( 150 );
    
    // Set the DDR2 to synreset, then enable it again
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_SYNCRESET);
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE);
    
    DDR->DDRPHYCR= 0x000000C5;   
    DDR->SDBCR= 0x08D34832;        //Program SDRAM Bank Config Register
    DDR->SDBCR= 0x0853C832;    
    DDR->SDTIMR=0x3C934B51;        //Program SDRAM Timing Control Register1
    DDR->SDTIMR2=0x4221C72;        //Program SDRAM Timing Control Register2
    DDR->PBBPR= 0x000000FE;   
    DDR->SDBCR= 0x08534832;        //Program SDRAM Bank Config Register   
    DDR->SDRCR= 0x00000768;        //Program SDRAM Refresh Control Register
 
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_SYNCRESET);
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE);
    
    return E_PASS;
}

void DEVICE_LPSCTransition(Uint8 module, Uint8 domain, Uint8 state)
{
    // Wait for any outstanding transition to complete
    while ((PSC->PTSTAT)&(0x00000001<< domain));
    
    // If we are already in that state, just return
    if (((PSC->MDSTAT[module])& 0x1F) == state)return;
    
    // Perform transition
    PSC->MDCTL[module]=((PSC->MDCTL[module])&(0xFFFFFFE0))|(state);
    PSC->PTCMD|=(0x00000001 << domain);
    
    // Wait for transition to complete
    while ((PSC->PTSTAT)&(0x00000001<< domain));
    
    // Wait and verify the state
    while (((PSC->MDSTAT[module])& 0x1F) != state);    
}

      而后调用DEVICE_EMIFInit函数来配置EMIF模块,这个模块用来接外存,比如NAND,NOR等。DM365有两个片选空间,如果某一空间配置成NAND,则需要在寄存器中设置,其函数内容如下:

Uint32 DEVICE_EMIFInit()
{    
    AEMIF->AWCCR= 0xff;   
    AEMIF->A1CR= 0x40400204;  
    AEMIF->NANDFCR|= 1;  
    AEMIF->A2CR= 0x00a00505;
    
    return E_PASS;   
}

      而后调用DEVICE_UART0Init函数来配置串口0,调用DEVICE_TIMER0Init函数来配置TIMER0,调用 DEVICE_I2C0Init函数来配置I2C控制器,都是操作某一模块的控制寄存器实现,具体如何设置可以参考相关模块的手册,这三个函数的内容如 下:

Uint32 DEVICE_UART0Init()
{    
    UART0->PWREMU_MGNT= 0;// Reset UART TX & RX components

    UTIL_waitLoop( 100 );
    
    UART0->MDR= 0x0;
    UART0->DLL= 0xd;// Set baud rate    
    UART0->DLH= 0;
     
    UART0->FCR= 0x0007;// Clear UART TX & RX FIFOs
    UART0->FCR= 0x0000;// Non-FIFO mode
    UART0->IER= 0x0007;// Enable interrupts

    UART0->LCR= 0x0003;// 8-bit words
    // 1 STOP bit generated,
    // No Parity, No Stick paritiy,
    // No Break control
 
    UART0->MCR= 0x0000;// RTS & CTS disabled,
    // Loopback mode disabled,
    // Autoflow disabled
 
    UART0->PWREMU_MGNT= 0xE001;// Enable TX & RX componenets
 
    return E_PASS;
}

Uint32 DEVICE_I2C0Init()
{
    I2C0->ICMDR= 0;// Reset I2C
    I2C0->ICPSC= 26;// Config prescaler for 27MHz
    I2C0->ICCLKL= 20;// Config clk LOW for 20kHz
    I2C0->ICCLKH= 20;// Config clk HIGH for 20kHz
    I2C0->ICMDR|= I2C_ICMDR_IRS;// Release I2C from reset
    
    return E_PASS;
}

Uint32 DEVICE_TIMER0Init()
{
    // Put timer into reset
    TIMER0->EMUMGT_CLKSPD= 0x00000003;
    TIMER0->TCR= 0x00000000;
    
    // Enable TINT0, TINT1 interrupt
    TIMER0->INTCTL_STAT= 0x00000001;
    
    // Set to 64-bit GP Timer mode, enable TIMER12 & TIMER34
    TIMER0->TGCR= 0x00000003;
    
    // Reset timers to zero 
    TIMER0->TIM12= 0x00000000;
    TIMER0->TIM34= 0x00000000;
    
    // Set timer period (5 second timeout = (24000000 * 5) cycles = 0x07270E00) 
    TIMER0->PRD34= 0x00000000;
    TIMER0->PRD12= 0x07270E00;
    
    return E_PASS;
}

      至此,DEVICE_init函数结束,程序返回至LOCAL_boot函数中,接着就调用NANDBOOT_copy函数了。


下面继续分析,由于后面的代码和函数量相对的多且复杂,所以不再贴上代码,只说流程,只要把程序源码打开对着看很容易就明白了。

 
      在DEVICE_init()这个大的平台初始化函数结束以后,下面要做的工作就是将NAND的中UBOOT复制到DDR中并且跳转到那里,然后UBL的历史使命结束,UBOOT的执行开始,那么就是调用这个函数,NANDBOOT_copy()。
 
      NANDBOOT_copy()函数主要分三大块,第一大块是调用NAND_open()函数对NAND控制器和NAND芯片进行初始化,它传递的参数是CE的地址(一般是CE0)和总线位宽(一般是8位)。好,我们跳到NAND_open()函数中。
 
      NAND_open()函数在common\driver\src\nand.c中,与平台无关。首先定义一个NAND_InfoHandle结构 hNandInfo,这个结构包含了所有与NAND有关的参数,比如厂商ID,设备ID,基址,位宽,块数,页数每块,页大小及与ECC,BB有关的一些 函数。然后对这个hNandInfo进行赋值,这里最重要的是把 DEVICE_NAND_PAGE_layout,DEVICE_NAND_ECC_info,DEVICE_NAND_BB_info,DEVICE_NAND_CHIP_infoTable 这四大全局结构赋给了hNandInfo的相关字段。这四大全局结构在每个平台目录下都有定义(比如dm36x\common\src \device_nand.c),编译不同平台的UBL,会将不同平台的四大全局结构传递给nand.c中使用,这样就做到了NAND的驱动分离中平台无 关部分和平台有关部分,这种思想也贯穿在整个LINUX的架构中,先不提这个。然后,操作AEMIF寄存器使能NAND的哪一个CE中,这个由传递的基址 决定,一般是CE0,并使能这段片选之上有ECC功能。
 
      接着调用NAND_reset()函数对NAND芯片进行复位,这个函数本身很简单,就是先发复位命令0xFF,再等待操作完成,懂得NAND操作过程的 会很容易这个操作。但是这里多说一点,一般我们遇到的芯片操作NAND,其CPU内部会有一个NAND控制器,对NAND的操作会通过这个寄存器来操作, 发命令发地址等都是操作这些寄存器来完成。但是DM365不是这样,它没有实质上的NAND控制器,它还是将NAND当做是一个类RAM一样的操作,通过 总线上的时序来完成。CLE和ALE连在地址总线A2和A1上,这样的话发命令实质上就是在总线地址0x00000010u上写值(注意DM365的地址 线问题,有个32位问题),发地址实质就是在总线地址0x00000008u上写值,读写数据实质就是在总线地址0x00000000u上读写值。这些地 址的操作隐含着CLE和ALE的高低电平,再加上配好的时序参数就把NAND的操作弄成了类RAM的操作。这种操作以前没有碰到过,TI的工程师很牛。
 
      接着调用LOCAL_flashGetDetails()函数,这个函数很重要,它会得到NAND的很多详细信息。首先它会判断NAND是否是ONFI, 这个没有细究过,跳过不管。然后它会向NAND发读ID命令,从而得到NAND的厂商ID和设备ID值。有了这两个值就好办了,通过一个循环把它们通四全 大局结构中的DEVICE_NAND_CHIP_infoTable中定义的各种NAND的资料进行对比,通过ID来定位是哪一种NAND,然后就得出了 页数,块数,页大小等重要信息。另外,还要区分NAND是大型还是小型(一般看页大小,大于2K为大),因为它们的地址周期不同。这个函数后面还有一些东 西,没有深究。调用完成后再复位一下NAND,整个NAND_open()函数就结束了。
 
      NAND_open()函数完成后,会调用NAND_readPage()函数从第25块开始一直搜索到第50块,只读第0页。因为UBOOT放在第25 块开始的地方,这是手册规定的,而且第0页放一些启动信息等等。通过MAGIC_NUMBER来判断是否有效,这个知识点详见手册。一旦 MAGIC_NUMBER正确,就会将后面的几个字段,比如UBOOT的进入点,页数,UBOOT在哪一个块,哪一个页,UBOOT的装载点等信息就会得 到。而后循环调用NAND_readPage()函数将整个UBOOT从NAND拷贝到DDR里,最后将刚才得到的UBOOT的进入点地址复制到 gEntryPoint变量中。然后主函数跳转到这里,从此UBL结束,UBOOT正式启动。
 
      这里的分析没有涉及到坏块处理,ECC校验的部分,一方面现在太忙没时间细看。另一方面UBL不是重点,要想深究这个东西,还是到Linux内核里的 NAND驱动中深究,这里能用就行了。还有就是一开始担心的是我们项目用到的NAND是512B每页的老式NAND,担心UBL不支持,需要修改UBL源 码。后来读了UBL源码以后,特别是平台相关的device_nand.c文件,才发现TI的工程师已经把该考虑的都考虑的,都能用,老外就是牛X。不像 国内的代码,根本没有普适性和跨平台性。UBL整个源码,架构较清晰,注释也多,代码整体很完整和美观,很有参考和学习价值。读了UBL源码后,其实很容 易就能读懂NANDEraser,NANDWriter等的源码了,说白了就是平台初始化加NAND读写,呵呵。


TI Davinci DM6441嵌入式Linux移植攻略——UBL移植篇 声明:本文参考网友zjb_integrated的文章《TI Davinci DM6446开发攻略——UBL移植》和《DAVINCI DM365-DM368开发攻略——U-BOOT-2010.12及UBL的移植》,内容有增删,特此声明!另有系列网文《DM365的UBL源码分析》(作者不详)对UBL源码的分析比较透彻,有兴趣的也可以看看。 一、DM6441的Boot过程简介 在搭好 TI Da 阅读详情

相关推荐

RBL查询脚本-开源

检查106个RBL中给定的IP /主机是否存在。 现在,您不必检查很多www网站,只需在控制台中运行此脚本即可。

【DSP】【第五篇】RBL源代码分析

RBL源码分析

Kshine2017的博客 1927

CentOS校园网Web Portal认证自动化实战

Web Portal认证是一种基于HTTP重定向与表单提交的强制网络准入机制,其原理是网关劫持未认证流量并跳转至登录页,验证通过后通过Cookie维持会话授权。该技术广泛应用于高校、机场、酒店等场景,核心价值在于细粒度访问控制与用户身份绑定。典型应用包括Linux命令行环境下的校园网接入、无GUI设备的自动联网、以及实验室集群的集中认证管理。本文聚焦CentOS系统,深入解析curl、cookie持久化、动态表单参数提取等关键技术环节,提供可调试、可复现、可工程化的完整认证流水线。

weixin_30522983的博客 265

STM32通用Bootloader——FOTA

1.固件升级简述 固件升级,通常称为 OTA(Over the Air)升级或者 FOTA(Firmware Over-The-Air)升级,即固件通过空中下载进行升级的技术。 1.1 bootloader 升级模式 bootloader 的升级模式常见有以下两种: bootloader 分区 + app1 分区 + app2 分区模式 该模式下, bootloader 启动后,检查 app1 和 app2 分区,哪个固件版本最新就运行哪个分区的固件。当有新版本的升级固件时,固件下载程序会将新的固件下载到

sinat_31039061的博客 1万+

BUCK降压斩波电路设计实战:从IGBT选型到续流二极管优化

本文详细解析了BUCK降压斩波电路的设计实战,从IGBT选型到续流二极管优化,涵盖了关键参数设计、调试技巧及电磁兼容问题解决方案。通过实际案例和数据对比,帮助工程师高效完成DC-DC转换电路设计,提升系统性能和可靠性。

weixin_29253711的博客 95

NXP MCU CAN波特率(位时间)配置详解

1. 概述 本文将会详细讲解如何设置NXP MCU的CAN波特率、位时间、采样点等属性。 波特率即CAN总线传输频率,位时间是波特率的倒数,例如波特率是500K,那么位时间 = 1 / 500000 = 0.000002s = 2000ns。 我们先来看一下S32K144单片机的EB中与CAN总线波特率相关的可配置参数有哪些: ...

weixin_42967006的博客 9691

【DSP】【第三篇】C6678启动过程

启动过程

Kshine2017的博客 7799

VirtualBox虚拟机拖放文件总失败?手把手教你配置双向拖放与共享文件夹(Ubuntu版)

本文详细指导如何在VirtualBox的Ubuntu虚拟机中配置高效的数据交换方案。核心是安装VirtualBox Guest Additions,并在此基础上开启双向拖放与共享粘贴板功能,同时建立稳定的共享文件夹,彻底解决主机与虚拟机间文件传输失败的问题,构建无缝的跨系统工作环境。

weixin_29245767的博客 232

DM365的UBL源码分析(转)

DM365是的启动方式有两种,通过BOOTSEL[2:0]引脚决定。当其为001时,直接从AEMIF上启动,比如NOR和OneNAND。除此之外皆是从RBL启动,顺序为RBL-UBL-UBOOT-KERNEL,比如NAND,串口,SD卡等。RBL会搜寻block1到block24去找UBL,关于RBL启动的详细细节可以参考用户指南关于ARM子系统的那篇文档,很详尽,下面只分析UBL的源码。

pointfish的专栏 1063

本地AI助手部署指南:DeepSeek Harness与Ollama集成实践

AI Agent作为当前人工智能领域的热门技术,通过模拟人类决策过程,能够自主规划、调用工具并执行复杂任务。其核心原理在于将大语言模型的推理能力与外部工具API相结合,形成可闭环的工作流。这种技术架构的价值在于显著提升了AI系统的实用性和自动化水平,使其从单纯的对话工具转变为能够解决实际问题的智能助手。在应用场景上,AI Agent广泛应用于代码生成、数据分析、自动化办公等领域。本文聚焦于如何通过DeepSeek Harness平台与Ollama本地模型的集成,实现隐私安全、离线可用的AI助手部署方案,特别

dengjuanshou7445的博客 475

Yolo11改进:Block改进|MixDehazeNet,用于图像去雾网络的混合结构块|即插即用|附代码

https://arxiv.org/pdf/2305.17654 图像去雾是低级视觉领域中的一项典型任务。先前的研究验证了大型卷积核和注意力机制在去雾中的有效性。然而,存在两个缺点:引入大型卷积核时容易忽略图像的多尺度特性,而注意力模块的标准串联方式没有充分考虑雾分布的不均匀性。在本文中,我们提出了一种名为混合结构图像去雾网络(MixDehazeNet)的新型框架,该框架解决了上述两个问题。具体而言,它主要由两部分组成:多尺度并行大型卷积核模块和增强并行注意力模块。与单个大型卷积核相比,多尺度的并行大型卷积

m0_47867638的博客 1273

DM365的UBL源码分析(一)

转自:http://blog.chinaunix.net/space.php?uid=7214824&do=blog&id=160521 DM365是的启动方式有两种,通过BOOTSEL[2:0]引脚决定。当其为001时,直接从AEMIF上启 动,比如NOR和OneNAND。除此之外皆是从RBL启动,顺序为RBL-UBL-UBOOT-KERNEL,比如NAN...

weixin_34378045的博客 214

PD详解 扩展消息——Source_Capabilities_Extended Message

PD的协议层分为控制消息,数据消息还有扩展消息,今天大师匈讲一下扩展消息中的source_cap_extended。这个消息在扩展消息中算是比较关键的东西。 扩展消息中的source_cap_extended 大师匈先上图,然后逐一解释; 上面的图表说了source_cap_ext中包含了哪些内容。 我们注意到在source_cap扩展消息中是以字节为单位的,并不是像source_cap中以一个object为单位,当然有注意到PD标准spec中说明的扩展消息的话,那里有说明,在header之后的字节

大师匈的博客 3519

DM365的UBL源码分析(三)

转自:http://blog.chinaunix.net/space.php?uid=7214824&do=blog&id=160523 继续在DEVICE_init函数中,下面是调用DEVICE_DDR2Init函数来配置DDR控制器,这是 UBL中重要的一部分,如果硬件电路需要更换内存芯片的话,需要在UBL中修改这个函数,即按照芯片手册来配置DDR控制寄...

weixin_34343689的博客 138

【文献笔记】ICCV 2021 | Point Transformer

作者提到,特别适合点云处理,因为自注意力机制是的核心,本质上就是一个集合算子:它对输入元素具有顺序不变性和数量不变性3D点云是无序的,即使改变点云txt文件中的点顺序也不会有任何影响。自注意力机制天然具有排列不变性,因为它通过对所有"点对"的关系进行建模来处理输入,而不依赖于任何特定的输入顺序点云数据通常覆盖了3D空间中的对象或场景,理解这些数据需要捕捉点与点之间复杂的空间关系。自注意力机制能够有效的捕捉这些关系,因为它为每个"点对"赋予一个。

m0_55908255的博客 965

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

DM365的UBL源码分析(二)

转自:http://blog.chinaunix.net/space.php?uid=7214824&do=blog&id=160522 书接上回,看看UBL对平台的初始化,主要是调用了DEVICE_init函数,函数内容如下: Uint32 DEVICE_init()...

weixin_34380296的博客 231

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

DM365的UBL源码分析(四)

转自:http://blog.chinaunix.net/space.php?uid=7214824&do=blog&id=160528 下面继续分析,由于后面的代码和函数量相对的多且复杂,所以不再贴上代码,只说流程,只要把程序源码打开对着看很容易就明白了。 在DEVICE_init()这个大的平台初始化函数结束以后,下面要做的工作就是将...

weixin_34113237的博客 163

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

RBL 启动流程 代码分析

DM365是的启动方式有两种,通过BOOTSEL[2:0]引脚决定。当其为001时,直接从AEMIF上启 动,比如NOR和OneNAND。除此之外皆是从RBL启动,顺序为RBL-UBL-UBOOT-KERNEL,比如NAND,串口,SD卡等。RBL会 搜寻block1到block24去找UBL,关于RBL启动的详细细节可以参考用户指南关于ARM子系统的那篇文档,很详尽,下面只分析UBL的源码。  ...

sickbe的博客 978

DAVINCI DM365-DM368开发攻略——U-BOOT-2010.12及UBL的移植

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://zjbintsystem.blog.51cto.com/964211/713240     从盛夏走到深秋,我们继续DAVINCI DM365-DM368的开发。说来惭愧,人家51CTO热情支持本博客,而本人却一直没有像其他博客之星一样频繁更新博客,心里确实说不过去。管理

sydjm的专栏 1018

DAVINCI DM365-DM368开发攻略——U-BOOT-2010.12

本篇及以后的DM36X文章基本以DM368为主的介绍,因为DM365--- dvsdk_dm365_4_02_00_06和DM368----dvsdk_dm368_4_02_00_06的UBL,U-BOOT, KERNEL的源码完全是一样的,如果不考虑2A算法,DM368就是DM365的超频超出来的高清! 一、DM36X的BOOT过程介绍 DM36x的BOOT过程和

pointfish的专栏 4491

底层移植之RBL, UBL, Uboot的关系

首先RBL=ROM Bootloader,UBL=user Bootloader。         RBL为TI固化在芯片ROM中的bootloader,OMAP上电启动过后首先将运行RBL,然后通过RBL加载UBL,再通过UBL加载uboot,而uboot就是用来加载Linux内核的。如果在具体应用过程中不需要使用到Linux(实际上一般都会用到的),uboot甚至是UBL都可以省去。

张子傲(C/C++) 3209
上一篇: GSG: DM365 DVEVM Software Setup(continue)
下一篇: linux sed 批量替换字符串
pointfish
博客等级 码龄17年 58粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值