[转载]自己动手编写嵌入式Bootloader之(2)

从MCU上电启动流程出发编写bootloader(中) 上文已经分析了MCU上电的启动流程。本文尝试编写一个简单的bootloader以及一个实现CAN通信的APP,为我们下一步尝试编写一个通过CAN通信实现刷写APP功能的bootloader作铺垫。 下面直接上干货。 看一下bootloader工程的结构,非常的简单,实现代码也非常简单。 main.h #ifndef _MAIN_H #define _MAIN_H #define APPLICATION_ADDRESS (uint32_t)0x08004000//APP工程的起始地址 typedef 阅读详情
第二部分:通过网口下载内核映像

要实现通过网口下载文件的功能,从底层到上层需要做的工作包括:开发板上的网卡芯片的驱动程序;TCP/IP协议栈的实现;TFTP客户端应用程序的实现。我们使用的OK2440开发板配备CS8900A网卡芯片。 为了简单起见,网络数据包的发送和接收都使用轮询方式,不使用中断;协议栈只使用ARP/IP/UDP协议,不涉及TCP及其他协议;应用程序只实现最简单的TFTP客户端。

1. 全局配置信息

发送和接收的数据缓冲区,使用 全局静态缓冲区,不使用动态内存分配。第一阶段运行结束之后,CPU内部4KB的SteppingStone可以用作其它用途,我们就用它做网络数据接收、发送的缓冲区。亦可用作标准输入输出的缓冲区。
unsigned char *TxBuf = (unsigned char *)0;
unsigned char *RxBuf = (unsigned char *)1024;

使用若干个全局变量来保存网络配置信息:
unsigned char    NetOurEther[6] =            /* Our ethernet address        */
        {0x00, 0x09, 0x58, 0xD8, 0x11, 0x22};
开发板的MAC地址,这个是任意 设置的。

unsigned char    NetServerEther[6] =            /* Boot server enet address    */
    {0x00, 0x14, 0x2A, 0xA5, 0x50, 0x97};
服务器也就是主机的MAC地址,这个要跟主机MAC一致,可以在主机上运行ifconfig命令查到。

unsigned long    NetOurIP = 0xC0A801FC;        /* Our IP addr 192.168.1.252    */
unsigned long    NetServerIP = 0xC0A801F9;       /* Server IP   192.168.1.249    */
网络协议中IP地址一般是用一个4字节整型数表示的。

2. CS8900A以太网驱动程序


硬件电路决定了CS8900的物理地址是在BANK3的区间内,CS8900是16位的寄存器,故我们设置BANK3的BUS WIDTH也为16位。设置BANK3: 总线宽度16,使能nWait,使能UB/LB

BANKCON3:0x1F7C                                                                                                                                                                                                                                                                                              

网卡CS8900的访问基址为0x19000000,之所以再偏移0x300是由它的特性决定的
#define CS8900_BASE 0x19000300

CS8900 读写寄存器的方式有些特别。要读一个寄存器,先向CS8900_PPTR中写入该寄存器地址,再从CS8900_PDATA中读出该寄存器值;要写一个寄存器,先向CS8900PPTR中写入该寄存器地址,再向CS8900_PDATA中写入要写入的值。不管是寄存器地址还是要读写的数值,都是16位的,也就是说都是unsigned short类型的。因此,读写寄存器的函数如下:

static unsigned short get_reg (int regno)
{
    CS8900_PPTR = regno;
    return CS8900_PDATA;
}
 
static void put_reg (int regno, unsigned short val)
{
    CS8900_PPTR = regno;
    CS8900_PDATA = val;
}


读芯片ID: CS8900的芯片ID存放在PP_ChipID寄存器中,读该寄存器得到的正确值应该是0x630E,这可以初步判断一些地址/引脚的设置是否正确,如果读出的不是0x630E,那么CS8900肯定不能正常工作。

设置MAC地址:

MAC地址并不是固定的,可以由我们随意设置。从寄存器PP_IA开始的6个字节存放MAC地址。比如下面的代码把MAC地址设为 00 09 58 D8 11 22:

    put_reg (PP_IA + 0, 0x00 | 0x09 << 8);
    put_reg (PP_IA + 2, 0x58 | 0xD8 << 8);
    put_reg (PP_IA + 4, 0x11 | 0x22 << 8);


因为是Little Endian, 所以0x09<<8, 但是在寄存器内存中还是 0x00放在前面。

 

寄存器初始化: 设置CS8900的工作模式

    /* 只接收目标地址为本网卡的无错误数据包 */
    put_reg (PP_RxCTL, PP_RxCTL_IA | PP_RxCTL_Broadcast | PP_RxCTL_RxOK);
    /* 当进行接收操作时,不要产生任何中断 */
    put_reg (PP_RxCFG, 0);
    /* 当进行发送操作时,不要产生任何中断 */
    put_reg (PP_TxCFG, 0);
    /* 当进行缓存操作时,不要产生任何中断 */
    put_reg (PP_BufCFG, 0);
    /* 使能发送和接收模式 */
    put_reg (PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);


发送数据包:

int eth_send (volatile void *packet, int length)

两个参数:要发送的数据包首地址、长度

TxCMD 和TxLen寄存器用来初始化数据包的发送,其具体含义见CS8900数据手册第70页。这里PP_TxCmd_TxStart_Full被定义为 0x00C0,表示直到整个数据侦都加载到CS8900内部缓存之后才开始发送,数据侦的长度为CS8900_TxLEN.

/* initiate a transmit sequence */
    CS8900_TxCMD = PP_TxCmd_TxStart_Full;
    CS8900_TxLEN = length;


使用TxCMD下达发送数据的命令后,再读取 PP_BusSTAT 总线状态寄存器判断是否做好发送数据的准备。当get_reg (PP_BusSTAT) & PP_BusSTAT_TxRDY 不等于零时表示可以发送了。 使用一个循环进行实际的发送操作:

for (addr = packet; length > 0; length -= 2)
        {
            CS8900_RTDATA = *addr++;
        }


这里 addr 也是unsigned short类型的指针, 每次向CS8900_RTDATA写入两个字节数据。这里假设要发送的数据包长度为偶数。

最后,通过读取PP_TER寄存器可以知道是否发送完毕,是否发送成功。

接收数据包:

首先,通过读取PP_RER寄存器判断是否接收到数据。如果接收到数据,则连续两次读取 CS8900_RTDATA 的值,
    status = CS8900_RTDATA;        /* stat */
    rxlen = CS8900_RTDATA;        /* len */
rxlen 为接收到的数据长度。
然后用一个循环连续读取 rxlen 长度的数据:

for (addr = (unsigned short *) &RxBuf[0], i = rxlen >> 1; i > 0;
         i--)
        *addr++ = CS8900_RTDATA;
    if (rxlen & 1)
        *addr++ = CS8900_RTDATA;


其中 RxBuf 为预先在内存中开辟的一块接收缓冲区。 每次循环读取两个字节,还需要处理长度为奇数的情况。

最后,把RxBuf交给上层的协议处理:net_receive( &RxBuf[0], rxlen );

3. Ethernet MAC层协议的实现

上层的数据包(如IP包、ARP包)到来时,需要添加一个14字节的MAC头, 然后再交给网卡发送出去。 MAC头包含目的MAC地址、源MAC地址、协议类型三个字段。如下图所示。数据包末尾的CRC校验我们不使用。


使用下面的代码填充MAC头。其中协议类型,对IP为0x0800, 对ARP为0x0806

    struct mac_header *p = (struct mac_header*)(buf);
    memcpy (p->dest, NetServerEther, 6);
    memcpy (p->src, NetOurEther, 6);
    p->proto = htons(proto);


4. ARP协议的实现


      一般的方式是建立一个全局的ARP映射缓存表,随着系统的运行不断查找、更新该表。但是我们要完成的功能仅仅是从TFTP服务器下载内核和文件系统映像,而服务器的IPMAC地址都是固定的,因此可以简化ARP映射表,只用两个变量分别保存服务器IPMAC,再用两个变量保存开发板IPMAC即可。并且更新映射表的功能也可以省略,只在系统初始化时把这四个地址都设置好,使用过程中不会发生改变,所以不需要更新。这样,我们的ARP协议只需要完成接受ARP请求、发送ARP应答的功能,而发送ARP请求和接受ARP应答的功能可以省略,这样大大简化了协议栈的设计。

    按照维基百科上的介绍(http://en.wikipedia.org/wiki/Address_Resolution_Protocol),ARP 是一个数据链路层协议,(我感觉它应该是网络层的协议),它的作用是在只知道一个主机网络层IP地址的情况下找到它的硬件地址。在以太网上,它主要用来把 IP地址转换为以太网MAC地址。由于是链路层协议,ARP的作用范围仅限于本地局域网。

    ARP数据包长度为28字节,其中各字节的含义如下图所示:


对各个段作简单的解释:
Hardware type (HTYPE)  每个数据链路层协议都被分配到一个数,比如,Ethernet 是 1
Protocol type (PTYPE)  在这个域,每个网络层协议都被分配到一个数(标号),比如,IP是0x0800
Hardware length (HLEN)  硬件地址的长度。以太网Ethernet的MAC地址长度是6个字节
Protocol length (PLEN)  维基上写的是“逻辑地址”的长度,其实也就是网络层地址的长度。IPv4地址的长度为4个字节。
Operation  表明发送者的操作,也就是数据包的类型:1表示ARP请求;2表示ARP回应;3表示RARP请求;4表示RARP回应。
Sender hardware address (SHA)  发送者的硬件地址
Sender protocol address (SPA)  发送者的协议地址,也就是发送者IP地址。
Target hardware address (THA)  目标接收者的硬件MAC地址。如果是ARP请求,这个域被忽略。
Target protocol address (TPA)  目标接收者的IP地址。

知道了包结构,我们就可以设计一个结构体:

struct arp_header{
    unsigned short        ar_hrd;        /* Format of hardware address    */
    unsigned short        ar_pro;        /* Format of protocol address    */
    unsigned char        ar_hln;     /* Length of hardware address    */
    unsigned char        ar_pln;     /* Length of protocol address    */
    unsigned short        ar_op;        /* Operation            */

    unsigned char        ar_sha[6];    /* Sender hardware address    */
    unsigned long        ar_spa;     /* Sender protocol address    */
    unsigned char        ar_tha[6];    /* Target hardware address    */
    unsigned long        ar_tpa;     /* Target protocol address    */
}__attribute__ ((packed));


属性 __attribute__((packet)) 告诉编译器使用紧缩方式存放结构体内容(1 Byte align), 不使用默认的4字节对齐, 这样就不会产生冗余字节。此时的 sizeof(struct arp_header) = 28。 如果不加packed属性, 运行 sizeof(struct arp_header) 得到 32, 而不是 28。 数据段就产生了错位。

前面已经说过,我们只实现接收ARP请求并发送ARP应答的功能,因此只用一个简单的函数就可实现:

static int arp_handle( unsigned char *buf, unsigned int len )
{
    struct arp_header *pRx, *pTx;
    pRx = (struct arp_header *)(buf);
    pTx = (struct arp_header *)&TxBuf[256];

    switch (htons(pRx->ar_op))
    {
        case ARP_REQUEST:
            if (pRx->ar_tpa == htonl(NetOurIP))
            {
                pTx->ar_hrd = htons(0x01);
                pTx->ar_pro = htons(PROTO_IP);
                pTx->ar_hln = 0x06;
                pTx->ar_pln = 0x04;
                pTx->ar_op = htons(ARP_REPLY);
                memcpy(pTx->ar_sha, NetOurEther, 6);
                pTx->ar_spa = htonl(NetOurIP);
                memcpy (pTx->ar_tha, pRx->ar_sha, 6);      
                pTx->ar_tpa = pRx->ar_spa;
                mac_send( (unsigned char*)pTx, sizeof(struct arp_header), PROTO_ARP);
            }
            break;
        case ARP_REPLY:
            printf("/n/rGot ARP reply/n");
            break;
        default:
            printf("/n/r ar_op Not Support./n");
            break;
    }
    return 0;
}


接收到的数据保存在pRx地址处,要发送的数据地址指定为pTx位于发送缓冲区中。如果接收到的是ARP请求包并且IP地址也符合,则在pTx处构造一个ARP应答包并交给mac_send()发送出去。

5. IP协议的实现

IP 数据包的格式如下表所示:

+

Bits 0–3

4–7

8–15

16–18

19–31

0

Version

Header length

Type of Service

Total Length

32

Identification

Flags

Fragment Offset

64

Time to Live

Protocol

Header Checksum

96

Source Address

128

Destination Address

160

Options

160 or 192+

Data

IP协议的简化:IP协议在网络中主要完成路由选择和网络分段的功能。起始Bit 0-3表示版本号,对IPv4来说取值为40100即可。Header length域指明IP数据包header的长度(不包括数据Data域),以四字节为单位,因为Options域是可选的所以IP Header的长度并不固定。我们不使用Option域,所以取最小值5,表示Header长度为20字节。服务类型域(Type of Service, TOS)是为特殊的应用如VoIP等保留的,我们不使用,赋值为零即可。接下来2个字节的Total Length域表示整个数据包的长度,包括HeaderData,以字节为单位。 标识域(Identification)用来给数据包一个唯一的编号,用于验证和跟踪等,我们不使用,直接赋值为零即可。FlagsOffset用于分段包的重组,我们不使用,把Flags的第2位设为1表示是不可分段的,Offset赋值为零即可。生存时间(Time to Live, TTL)表示该数据包在网络上的有效期,我们简单的把它设为最大值0xFF即可。协议域(Protocol)表示传输层使用什么协议,RFC790文档为每个协议都规定了唯一的编号,如UDP编号为17Header ChecksumHeader区域的校验和,在校验之前该域初始为0,然后计算整个头部的校验和,把结果存放在该域,计算校验的方法是把头部看成以16位为单位的数字组成,依次进行二进制反码求和。接下来的八个字节是源IP地址和目的IP地址,没什么可说的。

综上所述,我们只保留了IP协议中必须的关键字段,因而简化了设计,对IP数据包进行填充的代码段如下:

    struct ip_header *p = (struct ip_header*)(buf);
    p->ver_ihl = 0x45;                  // 1 Byte
    p->tos = 0x00;                      // 1 Byte
    p->tlen = htons(len);               // 2 Byte
    p->identification = htons(0x00);    // 2 Byte
    p->flags_fo = htons(0x4000);        // 2 Byte
    p->ttl = 0xFF;                      // 1 Byte
    p->proto = 17;                      // 1 Byte, 17 for UDP
    p->ip_src = htonl(NetOurIP);        // 4 Byte
    p->ip_dest = htonl(NetServerIP);    // 4 Byte
    p->crc = 0x0;                       // 2 Byte, To be
    p->crc = checksum( buf, sizeof(struct ip_header) );


CheckSum 校验和:
IP,TCP,UDP等许多协议的头部都设置了校验和项,它们采用的算法是一样的,将被校验的数据按16位进行划分(若数据字节长度为奇数,则在数据尾部补一个字节0),对每16位求反码和,然后再对和取反码。 代码如下:

unsigned short checksum(unsigned char *ptr, int len)
{
    unsigned long sum = 0;
    unsigned short *p = (unsigned short *)ptr;
    while (len > 1)
    {
        sum += *p++;
        len -= 2;
    }
    if(len == 1)
        sum += *(unsigned char *)p;
    while(sum>>16)
        sum = (sum&0xffff) + (sum>>16);
    return (unsigned short)((~sum)&0xffff);
}



6. UDP协议的实现

bits0 - 1516 - 31
0Source PortDestination Port
32LengthChecksum
64 
Data
 

       在传输层我们抛弃了复杂的TCP协议而使用简单的UDP协议。虽然UDP是无连接的协议,它不保证数据包一定能够到达目的主机,但是在嵌入式开发中,开发板跟主机通常位于同一内部局域网内,网络环境良好,数据丢失的可能性很小,并且UDP容易实现,占用资源小,因此更适合于嵌入式环境。 UDP头部包含了可选的校验和字段,而校验要涉及到伪报头,为了简化设计和减小开销,我们不使用校验,直接把该字段设为零,表示不使用校验。UDP包填充代码如下:

    struct udp_header *P = (struct udp_header*)(buf);
    P->port_src = htons(0x8DA4); // 2 Byte
    P->port_dest = htons(port);  // 2 Byte
    P->tlen = htons(len);        // 2 Byte
    P->crc = 0x00;               // Do Not Checksum, 2 Byte


关于源端口号和目的端口号的设定,在TFTP实现时会详细说明。

7. TFTP客户端的实现

tftp是一个很简单的文件传输协议,在传输层使用UDP协议。它有四种类型的包: 读请求RRQ包,DATA包,ACK包,ERROR包,每个包的前两个字节Opcode指定包的类型。(RRQ用于请求下载,WRQ用于请求上传,我们只用到RRQ)。



下载文件的过程分析如下: 客户端(A)从任意端口X向服务器(S)的端口69发送一个RRQ包,该包中指明了要求下载的文件名;服务器(S)找到该文件,读取文件内容组成DATA包,从任意端口Y向客户端(A)的端口X发送这个DATA包,第一个DATA包编号为1;从此以后,客户端确定使用端口X,服务器确定使用端口Y, 客户端向服务器发送ACK包,编号为1。服务器接到编号为1的ACK包之后,发送第二个DATA包,如此继续下去。

怎样判断传输结束呢? 按照规定,DATA包中的数据段为512字节, 如果小于512字节,表示这是最后一个DATA包,文件已传输完毕。



(R1) Host A requests to read

(R2) Server S sends data packet 1
(R3) Host A acknowledges data packet 1

注意在这个过程中端口的变化。开始RRQ是69,但是DATA和ACK都不是使用69,而是使用另外一个随机的端口。 服务器在接到RRQ后,不返回任何回应信息,直接发送第一个DATA包,而且DATA包编号从1开始,而不是从0开始。

编程时为简单起见,客户端使用了固定的端口号X=0x8DA4,服务器端口号Y是随机的,只能通过解析UDP数据包获得。

int tftp_download(unsigned char *addr, const char *filename)
{
    int i=0;
    unsigned short curblock = 1;

    tftp_send_request( &TxBuf[256], filename );
    msdelay(100);

    while (1)
    {
        eth_rx();
      
        if( pGtftp == NULL )
            continue;
        
        if ( ntohs(pGtftp->opcode) == TFTP_DATA )
        {
            if (ntohs(pGtftp->u.blocknum) == curblock)
            {
                printf("/r Current Block Number = %d", curblock);
                for (i=0; i<iGLen-4; i++)
                {
                    *(addr++) = *(pGtftp->data+i);
                }
                tftp_send_ack( &TxBuf[256], curblock);
                
                if (iGLen < TFTP_DATASIZE+4)
                {
                    break;
                }
                curblock += 1;
            }
            else if (ntohs(pGtftp->u.blocknum) < curblock)
            {
                tftp_send_ack( &TxBuf[256], ntohs(pGtftp->u.blocknum));
            }
            else
            {
                printf("/n/rBlock Number Not Match.");
                printf("Block Number = %d, curblock = %d/n", ntohs(pGtftp->u.blocknum), curblock);  
            }
        }
        else if ( ntohs(pGtftp->opcode) == TFTP_ERROR )
        {
            switch( ntohs(pGtftp->u.errcode) )
            {
               // 此处省略
            }
        }
        else if ( ntohs(pGtftp->opcode) == TFTP_RRQ )
        {}// 此处省略若干 else if
       
        pGtftp = NULL;
        iGLen = 0;
    }
    
    printf("/n/rTransfer complete: %d Bytes./n/r", (curblock-1)*TFTP_DATASIZE + iGLen-4 );
    
    return 0;
}

 

 TAG ARM Linux Bootloader
嵌入式Linux系统Bootloader启动调试技术(回顾) 嵌入式系统搭建过程中,对于系统平台搭建工程师最初的一步通常是移植Bootloader ,当然移植有几个级别,通常最常见的是参考的EVM 的硬件有了修改(如更改了FLASH ,更改了SDRAM 、DDR SDRAM 等),并且是初次调试硬件,更大的困难是公司为节省成本不打算买上万的EVM 开发板,或者EVM 开发板需要license 才能购买,这时移植Bootloader 是比较难做的,.... 阅读详情

相关推荐

Bybit面经

这段总结描述了求职者在Bybit面试过程中的经历和相关问题,包括面试内容、求职者向面试官提问的问题以及关于Bybit的信息

郑泽洲的博客 2524

官方例程的杰发AC7811的boot程序操作流程详细说明

总结杰发AC7811的CAN升级方法,以及遇到的问题。

智能制造之物料详解

制造业业务系统中的物料流转贯穿需求、采购、生产到交付全流程,各系统协同实现精准追踪。需求阶段由CRM/ERP/PLM生成物料计划;采购入库阶段SCM/WMS/QMS完成采购质检;生产阶段MES/APS协调领料加工;成品交付阶段WMS/SCM处理出库物流。整个流程以BOM为蓝图,ERP为调度中心,通过数据联动实现物料状态、位置和数量的全链路可追溯。

cdfunlove的博客 639

AC7811系列 程序上禁用了SWD之后,复位引脚拉低再拉高下载不成功

在STM32上,如果SWD功能重定向为GPIO导致无法下载,则只需在点击下载前拉低复位引脚,点击下载的同时释放复位引脚就能下载。成功率没有百分百也有百分之九十。 但是在杰发的AC781x上,这个方法行不通。 参考了这篇文章后,我尝试了一下。 将BOOT引脚拉,保持; 使MCU复位; 点击下载; 下载完成后恢复BOOT0为低。 实践证明这个方法可行。 ...

Stack_的博客 870

杰里AC79XX之开发环境搭建(1)

一、安装codeblocks::Blocks 先进入官网http://www.codeblocks.org 然后点击1,下面会有好几个版本,选择最新的就行20.03 点击2 根据自己的电脑系统,点击下面3或者4都可以下载安装包,比如我选的是第一个 然后安装安装包 选择自己要安装的路径 选择是否都可以。比如选择是,现在打开软件。 然后点出现界面的NEXT,出现最后的finish,点击。 桌面上生成了快捷方式 二、Code::Blocks汉化...

心跳包的博客 1万+

嵌入式实战】STM32 Bootloader 快速实现(超详细)

我是 AidenHinGwenW,本文介绍如何快速实现 STM32 BootLoader 的引导。 嵌入式的设备,或多或少都需要对设备进行更新已适配更多的需求,`bootLoader` 的设计就是为了设备能够进行远程升级或者只用指令升级,极大简便了升级需要的步骤,做到傻瓜式升级,极大增强了产品的后续维护性。 下面我来介绍写如何快速实现 STM32 BootLoader 的引导,帮助正在读文章的您更快将此技术运用起来!

HinGwenWoong的博客 7万+

[转载]自己动手编写嵌入式Bootloader之(3)

第三部分:源代码,运行结果这一部分将对前文没有提到的几段关键代码进行简单说明,介绍一下源代码组织结构和Makefile系统,展示一下实验运行结果,并提供全部源代码下载。1. 定时器初始化和延时程序因为在 CS8900A的驱动程序中需要用到延时,因此有必要对S3C2440的计时器进行使能和初始化,并编写延时程序。S3C2440A共有5个定时器,编号为Tim

DerekYao的专栏 1621

[转载]自己动手编写嵌入式Bootloader之(1)

第一部分:基本功能流程CPU上电后会从IO空间的某地址取第一条指令。但此时:PLL没有启动,CPU工作频率为外部输入晶振频率,非常低;CPU工作模式、中断设置等不确定;存储空间的各个BANK(包括内存)都没有驱动,内存不能使用。在这种情况下必须在第一条指令处做一些初始化工作,这段初始化程序与操作系统独立分开,称之为bootloader。实际上,很少有必要自己写一个Bootloader,因为

DerekYao的专栏 1672

BootLoader是什么 转载至百度百科

此处为私人收藏文章,建议大家观看百度百科原文:https://baike.baidu.com/item/BootLoader/8733520?fr=aladdin BootLoader 锁定 本词条由“科普中国”科学百科词条编写与应用工作项目审核 。 在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到...

liangwu5582066930的专栏 591

嵌入式Linux Bootloader在MIPS64上的移植 (转载)

1 引言 Linux具备良好的裁剪性和移植性,并且开源、资源丰富,已成为嵌入式系统的开发热点。一个完整的嵌入式Linux系通通常包括Bootloader、 内核、文件系统三部分,目标板上电后由Bootloader初始化硬件,引导内核和文件系统来启动Linux。但由于在嵌入式开发中基于MIPS体系的 Bootloader较少,开发者在移植MIPS Bootloader需花费大量...

290

转载:Linux bootloader 编写方法

http://www.ibm.com/developerworks/cn/linux/embed/l-bootloader/index.html#N1009BdeveloperWorks 中国  >  Linux  >Linux bootloader 编写方法文档选项<trvalign="top"><img alt="" height="1" width="8"src="//www.ibm

Wenxy's computer science technology blog 1281

优化嵌入式Linux的启动时间之Bootloader

优化嵌入式Linux的启动时间之Bootloader 注:本文转载自微信公众号嵌入式客栈——优化嵌入式Linux的启动时间之Bootloader 原创逸珺嵌入式客栈2020-03-05 导读:嵌入式Linux在应用中往往希望系统能在尽量短的时间内启动,以提高用户体验。而且在有的应用场合,对启动时间具有严格的时间要求,尤其在工业或者医疗器械应用领域。此时如何加快Linux的启动,将成为一个挑战,对于大多数应用开发人员而言,由于Linux系统的复杂性,对于如何提高启动速度,往往无从下手。那么读完优化...

枫潇潇 810

WinCE系统下BootLoader的开发【转载

 引言Windows CE.NET是微软公司向嵌入式领域推出的一款操作系统。它最大程度继承了桌面版Windows操作系统的丰富功能,同时又副入了许多新特性,以适应嵌入式领域的实际情况和要求。无论是商业应用需求,还是人们的多媒体消费需求,都能被采用CE操作系统的设备很轻松的满足。最新的.NET版本较之3.0版本,在实时性和稳定性上有大幅度提高,开始广泛地被平板电脑、数码相机、彩屏手机、PDA等多

menuconfig的专栏 2273

Bootloader

我认为学习和应用u-boot对于刚接触的人来说最大的困难在于其功能比较全,文件(.S .c .h makefile文件)非常多,结构也比较复杂,难以理清各方面的关系。熟悉了一段时间之后,发现还是有很多的规律可以总结的。 一、首先要理解bootloader的作用,以及U-boot具体可以实现什么样的一些功能。 二、弄清U-boot的启动流程,这时候需要跟着整个程序走一遍,先不管实现这个流程的代码

颇锐克(公众号:颇锐克科技共享) 1万+

嵌入式】一种优雅的 bootloader 跳转APP 的方式

嵌入式】一种优雅的 bootloader 跳转APP 的方式

欢迎私信、留言交流 3474

如何学习嵌入式bootloader

本文是摘自一个大侠对vivi源码的分析的一篇文章,个人觉得前面这些文字对那些苦苦挣扎在研究bootloader的同志们会有一些帮助,甚至是鼓励,所以充满感激地将它转载了过来。 通过vivi研究bootloader有一段时间了,基本是在与之相关的基础方面做工作,还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想,真正细致地弄清楚vivi实现的细节,

u012138828的专栏 1009

转载BootLoader

先说系统的引导,BIOS(Basic Input Output System)在主板上电后首先会执行这么一段程序, 1. 上电自检,初始化系统运行环境(基本输入输出,外围设备); 2. 创建中断向量; 3. 通过软盘和硬盘上的引导记录将系统引导拉起; BIOS一般是固化到主板上的一个EEPROM芯片 但是对于嵌入式系统来说,这部分工作就要由Bootloader来完成 bootloa

Programmer 956

STM32BootLoader

STM32BootLoader 为系统的固件更新提供了一种灵活、方便的解决方案。通过合理划分存储区域、编写 BootLoader 代码和应用程序代码,可以实现系统的在线固件更新功能。在实际应用中,需要注意 Flash 操作、中断向量表和通信协议等问题,以确保系统的稳定性和可靠性。

duierrorshuobu的博客 3056

转载——bootloader 开发,最郁闷前三步

转载——bootloader 开发,最郁闷前三步

qq_44884287的博客 325

水果识别数据集-水果检测数据集.zip

水果识别数据集-水果检测数据集.zip水果识别数据集-水果检测数据集包含水果的高质量图像数据集。包括以下水果:苹果(不同品种:金,红,红,红,红),杏,鳄梨,鳄梨成熟,香蕉,樱桃(不同品种,瑞尼尔),克莱门蒂娜,可可,日期,百香果,葡萄(粉红,白,白2),葡萄柚(粉红,白),番石榴,哈克贝利,猕猴桃,卡基,金姆斯,柠檬(普通,梅耶)石灰,荔枝,柑橘,芒果,Maracuja,瓜皮尔萨波,桑树,油桃,橙,木瓜,百香果,桃,Pepino,梨(不同品种,Abate,怪物,威廉姆斯),酸浆(普通,带壳),菠萝(普通,迷你),Pitahaya红,李子,石榴,木瓜,红毛丹,覆盆子,沙拉克,草莓(普通,楔形),塔玛利洛,橘柚。图片总数:47593(每张图片一张)。训练集大小:35625幅图像。验证集大小:11968幅图像。类别数量:71(种水果)。图像大小:100×100像素。文件名格式:image_index_100.jpg(例如32_100.jpg)或r_image_index_100.jpg(例如r_32_100.jpg)或r2_image_index_100.jpg。

上一篇: [转载]自己动手编写嵌入式Bootloader之(1)
下一篇: [转载]自己动手编写嵌入式Bootloader之(3)
NetGeek
博客等级 码龄21年 15粉丝 2原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值