mini2440 支持 sst39vf1601,实现saveenv。

mini2440 支持 sst39vf1601,实现saveenv。
http://blogimg.chinaunix.net/blog/upfile2/090927210004.tar

把board/100askxxx/makefile里的flash.o去掉
用上面的文件分别去替换你的config文件和cfi_flash.c文件,就可以了,本来想做个patch,但是我的diff不好用。有的时候saveenv不成功,索性直接把写超时设置为 (0),就ok了。就是红色字体部分。


韦东山 大神的 详解里对没有支持sst39vf1601 nor flash,所以总是bad crc,更不用说saveenv命令了。
下面使用cfi(common flash interface),实现对sst39vf1601的支持。

#define CFG_ENV_ADDR         (CFG_FLASH_BASE + 0X0F0000)
#define CFG_ENV_SIZE        0x10000    /* Total Size of Environment Sector */
配置是上面这样的,我用的是sst39vf1601,共32个block,每个block 32k word = 64k = 0x10000
也就是说env放在了 第15块上(逻辑上是第16),前三块是uboot的代码,第15块是存放环境参数的块。
这里的block = sector,下面说到的扇区等于这里的block。即 扇区 = block = 64k = 0x10000
                                                       
    00000000 (RO) 00010000 (RO) 00020000 (RO) 00030000      00040000            
    00050000      00060000      00070000      00080000      00090000            
    000A0000      000B0000      000C0000      000D0000      000E0000            
    000F0000 (RO) 00100000      00110000      00120000      00130000            
    00140000      00150000      00160000      00170000      00180000            
    00190000      001A0000      001B0000      001C0000      001D0000            
    001E0000      001F0000 


用kscope搜索saveenv字符串,找到这里来了。

#if defined(CFG_ENV_IS_IN_NVRAM) || defined(CFG_ENV_IS_IN_EEPROM) || /
    ((CONFIG_COMMANDS & (CFG_CMD_ENV|CFG_CMD_FLASH)) == /
      (CFG_CMD_ENV|CFG_CMD_FLASH)) || /
    ((CONFIG_COMMANDS & (CFG_CMD_ENV|CFG_CMD_NAND)) == /
      (CFG_CMD_ENV|CFG_CMD_NAND))
U_BOOT_CMD(
    saveenv, 1, 0,    do_saveenv,
    "saveenv - save environment variables to persistent storage/n",
    NULL
);
#endif    /* CFG_CMD_ENV */


上面就是这个函数,如下
int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    extern char * env_name_spec;

    printf ("Saving Environment to %s.../n", env_name_spec);

    return (saveenv() ? 1 : 0);
}

在env_flash.c中,有这样的字符。
#if defined(CFG_ENV_IS_IN_FLASH) /* Environment is in Flash */
char * env_name_spec = "Flash";
#endif /* CFG_ENV_IS_IN_FLASH */

#define PHYS_FLASH_1        0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE        0x00000000
#define CFG_MONITOR_BASE      0x00000000
#define CFG_ENV_ADDR        0x0F0000
#define CFG_ENV_SIZE        0x10000
#define CFG_ENV_OFFSET         0x0F0000
#define CFG_ENV_SECT_SIZE    0x10000


#define    CFG_ENV_IS_IN_FLASH    1
#define CFG_ENV_SIZE        0x10000    /* Total Size of Environment Sector */

给 saveenv(),去掉宏之后
env_t *env_ptr = (env_t *)CFG_ENV_ADDR;
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;
#define PHYS_FLASH_1        0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE        PHYS_FLASH_1
#define CFG_ENV_ADDR         (CFG_FLASH_BASE + 0X0F0000)
#define CFG_ENV_SIZE        0x10000    /* Total Size of Environment Sector */
int saveenv(void)
{
    int    len, rc;
    ulong    end_addr;
    ulong    flash_sect_addr;
    uchar *env_buffer = (uchar *)env_ptr;
    int rcode = 0;

    flash_sect_addr = (ulong)flash_addr;
    len     = CFG_ENV_SIZE;

    end_addr = flash_sect_addr + len - 1;

    if (flash_sect_protect (0, flash_sect_addr, end_addr)) 首先取消保护
        return 1;

    puts ("Erasing Flash...");
    if (flash_sect_erase (flash_sect_addr, end_addr))    然后开始擦除
        return 1;

    puts ("Writing to Flash... ");
    rc = flash_write((char *)env_buffer, flash_sect_addr, len); 开始写进去
    if (rc != 0) {
        flash_perror (rc);
        rcode = 1;
    } else {
        puts ("done/n");
    }

    /* try to re-protect */
    (void) flash_sect_protect (1, flash_sect_addr, end_addr);    最后在保护起来
    return rcode;
}
逻辑很清晰
逐个分析下面的实现:
1.首先取消保护
2.然后开始擦除
3.开始写进去

1.首先取消保护,有个疑问,什么时候加的保护,都那些块加了保护呢?先不管。
int flash_sect_protect (0, flash_sect_addr, end_addr);
int flash_sect_protect (0, 0xf0000, 0x10000);
可以从函数原型分析出,0代表解锁,地址是从0xf0000-0x100000,如果成功将返回0。

int flash_sect_protect (int p, ulong addr_first, ulong addr_last)
{
    flash_info_t *info;
    ulong bank;
    int s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS]; 这里CFG_MAX_FLASH_BANKS=1
    int protected, i;
    int planned;
    int rcode;
    /*下面计算出要撤销保护的扇区范围,和 扇区数量,成功返回0*/
    rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned );
    protected = 0;

    if (planned && (rcode == 0)) { 这里说明flash_fill_sect_ranges成功的完成了任务
        for (bank=0,info=&flash_info[0]; bank < CFG_MAX_FLASH_BANKS; ++bank, ++info) { 遍历所有的块,显然是一次
            if (info->flash_id == FLASH_UNKNOWN) {
                continue;
            }

            if (s_first[bank]>=0 && s_first[bank]<=s_last[bank]) {
                protected += s_last[bank] - s_first[bank] + 1;
                for (i=s_first[bank]; i<=s_last[bank]; ++i) {
                    info->protect[i] = p; 撤销保护的具体操作就是这里了,可见这是uboot在自己软件层次的保护方法
                }                            到底是取消保护,还是加上保护,取决于p的取值,这里假设0,是解锁。
            }
        }

        printf ("%sProtected %d sectors/n",
            p ? "" : "Un-", protected);
    } else if (rcode == 0) { 到了这里,说明给的撤销保护的地址范围不正确
        puts ("Error: start and/or end address"
            " not on sector boundary/n");
        rcode = 1;
    }
    return rcode;
}


static int
flash_fill_sect_ranges (ulong addr_first, ulong addr_last,
            int *s_first, int *s_last,
            int *s_count )
{
    flash_info_t *info;
    ulong bank;
    int rcode = 0;

    *s_count = 0;

    for (bank=0; bank < CFG_MAX_FLASH_BANKS; ++bank) {
        s_first[bank] = -1;    /* first sector to erase    */
        s_last [bank] = -1;    /* last  sector to erase    */
    }

    for (bank=0,info=&flash_info[0]; 只遍历一次
         (bank < CFG_MAX_FLASH_BANKS) && (addr_first <= addr_last);
         ++bank, ++info) {
        ulong b_end;
        int sect;
        short s_end;

        if (info->flash_id == FLASH_UNKNOWN) {
            continue;
        }

        b_end = info->start[0] + info->size - 1;    /* bank end addr */ 就是结束地址
        s_end = info->sector_count - 1;            /* last sector   */        最后一个扇区数,这些内容在初始化是添好了。


        for (sect=0; sect < info->sector_count; ++sect) { 遍历这块nor的所有扇区
            ulong end;    /* last address in current sect    */

            end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;

            if (addr_first > end)
                continue;
            if (addr_last < info->start[sect])
                continue;

            if (addr_first == info->start[sect]) {
                s_first[bank] = sect; 找到起始的那个扇区
            }
            if (addr_last  == end) {
                s_last[bank]  = sect; 和最后的那个扇区,可知对 addr_first和addr_last的要求比较苛刻。
            }
        }
        if (s_first[bank] >= 0) { 
            if (s_last[bank] < 0) {
                if (addr_last > b_end) {
                    s_last[bank] = s_end;
                } else {
                    puts ("Error: end address"
                        " not on sector boundary/n");
                    rcode = 1;
                    break;
                }
            }
            if (s_last[bank] < s_first[bank]) {
                puts ("Error: end sector"
                    " precedes start sector/n");
                rcode = 1;
                break;
            }
            sect = s_last[bank]; 改到最后
            addr_first = (sect == s_end) ? b_end + 1: info->start[sect + 1];
            (*s_count) += s_last[bank] - s_first[bank] + 1; 计算出共几个扇区,指针传参。
        } else if (addr_first >= info->start[0] && addr_first < b_end) {
            puts ("Error: start address not on sector boundary/n");
            rcode = 1;
            break;
        } else if (s_last[bank] >= 0) {
            puts ("Error: cannot span across banks when they are"
                   " mapped in reverse order/n");
            rcode = 1;
            break;
        }
    }
    return rcode;    返回0
}

现回顾下 flash_info_t 结构,然后在看上面的代码。
typedef struct {
    ulong    size;            /* total bank size in bytes        */
    ushort    sector_count;        /* number of erase units        */
    ulong    flash_id;        /* combined device & manufacturer code    */
    ulong    start[CFG_MAX_FLASH_SECT];   /* physical sector start addresses */
    uchar    protect[CFG_MAX_FLASH_SECT]; /* sector protection status    */ 这就保护的初始数值是多少,那里赋值的呢?
#ifdef CFG_FLASH_CFI
    uchar    portwidth;        /* the width of the port        */
    uchar    chipwidth;        /* the width of the chip        */
    ushort    buffer_size;        /* # of bytes in write buffer        */
    ulong    erase_blk_tout;        /* maximum block erase timeout        */
    ulong    write_tout;        /* maximum write timeout        */
    ulong    buffer_write_tout;    /* maximum buffer write timeout        */
    ushort    vendor;            /* the primary vendor id        */
    ushort    cmd_reset;        /* Vendor specific reset command    */
    ushort    interface;        /* used for x8/x16 adjustments        */
    ushort    legacy_unlock;        /* support Intel legacy (un)locking    */
#endif
} flash_info_t;

2.然后开始擦除,成功返回0,猜想,有保护的,应该不能执行擦出操作。
这个可就不简单了,因为要深入到cfi借口的具体操作上。
int flash_sect_erase (0, flash_sect_addr, end_addr);
int flash_sect_erase (0, 0xf0000, 0x10000);

int flash_sect_erase (ulong addr_first, ulong addr_last)
{
    flash_info_t *info;
    ulong bank;
    int s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS];
    int erased = 0;
    int planned;
    int rcode = 0;

    rcode = flash_fill_sect_ranges (addr_first, addr_last,
                    s_first, s_last, &planned ); 代码复用了呵呵。

    if (planned && (rcode == 0)) { 成功到了这里
        for (bank=0,info=&flash_info[0];
             (bank < CFG_MAX_FLASH_BANKS) && (rcode == 0);
             ++bank, ++info) {
            if (s_first[bank]>=0) {
                erased += s_last[bank] - s_first[bank] + 1; 计算出要擦出的总扇区数量
                rcode = flash_erase (info, s_first[bank], s_last[bank]); 执行擦出操作
            }
        }
        printf ("Erased %d sectors/n", erased);
    } else if (rcode == 0) {
        puts ("Error: start and/or end address"
            " not on sector boundary/n");
        rcode = 1;
    }
    return rcode;
}

主要的操作函数是这个,他的参数是 擦出nor的 flash_info结构 和 起始 结束 扇区 数
成功返回0。我们跟进去。
int flash_erase (info, 15, 15);
int flash_erase (flash_info_t * info, int s_first, int s_last)
{
    int rcode = 0;
    int prot;
    flash_sect_t sect;

    if (info->flash_id != FLASH_MAN_CFI) {
        puts ("Can't erase unknown flash type - aborted/n");
        return 1;
    }
    if ((s_first < 0) || (s_first > s_last)) {
        puts ("- no sectors to erase/n");
        return 1;
    }

    prot = 0;
    for (sect = s_first; sect <= s_last; ++sect) { 这里计算出即将要擦出的扇区中有保护的扇区数
        if (info->protect[sect]) {
            prot++;
        }
    }
    if (prot) { 可见,这个函数不会理会flash_info结构中的保护,他只管擦出。
        printf ("- Warning: %d protected sectors will not be erased!/n", prot);
    } else {
        putc ('/n');
    }


    for (sect = s_first; sect <= s_last; sect++) {
        if (info->protect[sect] == 0) { /* not protected */
            switch (info->vendor) {
            case CFI_CMDSET_INTEL_STANDARD:
            case CFI_CMDSET_INTEL_EXTENDED:
                flash_write_cmd (info, sect, 0, FLASH_CMD_CLEAR_STATUS);
                flash_write_cmd (info, sect, 0, FLASH_CMD_BLOCK_ERASE);
                flash_write_cmd (info, sect, 0, FLASH_CMD_ERASE_CONFIRM);
                break;
            case CFI_CMDSET_AMD_STANDARD:
            case CFI_CMDSET_AMD_EXTENDED:
                flash_unlock_seq (info, sect);
                flash_write_cmd (info, sect, AMD_ADDR_ERASE_START,
                            AMD_CMD_ERASE_START);
                flash_unlock_seq (info, sect);
                flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR);
                break;
            case CFI_SST_1601_LZD: 上面两个是intel和amd的nor flash擦出方法,这里是我加的sst39vf1601的擦出方法
                flash_write_cmd (info, 0, 0x5555, 0xaa);
                flash_write_cmd (info, 0, 0x2aaa, 0x55);
                flash_write_cmd (info, 0, 0x5555,0x80);
                flash_write_cmd (info, 0, 0x5555,0xaa);
                flash_write_cmd (info, 0, 0x2aaa, 0x55);
                flash_write_cmd (info, sect, 0, 0x50); 确定要擦除的扇区
                break;
                
            default:
                debug ("Unkown flash vendor %d/n",
                       info->vendor);
                break;
            }

            if (flash_full_status_check 擦出要花费一段时间,这里进行检查擦除是否ok。在下面
                (info, sect, info->erase_blk_tout, "erase")) {
                rcode = 1;
            } else
                putc ('.');
        }
    }
    puts (" done/n");
    return rcode;
}

先看看下面这个函数的行为。
typedef unsigned long flash_sect_t;
它是把cmd这个命令写到一个地址处,这个地址是经过精心安排的,让我们看看这个地址是怎么来的 ^_^
static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd)

上面函数用到的数据结构

typedef union {
    volatile unsigned char *cp;
    volatile unsigned short *wp;
    volatile unsigned long *lp;
    volatile unsigned long long *llp;
} cfiptr_t;

typedef union {
    unsigned char c;
    unsigned short w;
    unsigned long l;
    unsigned long long ll;
} cfiword_t;

#define FLASH_CFI_8BIT        0x01
#define FLASH_CFI_16BIT        0x02 我的是16bit数据宽度的,就是每个地址产生16bit数据
#define FLASH_CFI_32BIT        0x04
#define FLASH_CFI_64BIT        0x08

/*
 * Write a proper sized command to the correct address
 */
static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd)
{

    volatile cfiptr_t addr;
    cfiword_t cword;

    addr.cp = flash_make_addr (info, sect, offset);  这个函数产生一个地址,根据sect和offset计算出,在下面
    flash_make_cmd (info, cmd, &cword);
    switch (info->portwidth) {
    case FLASH_CFI_8BIT:
        debug ("fwc addr %p cmd %x %x 8bit x %d bit/n", addr.cp, cmd,
               cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
        *addr.cp = cword.c;
        break;
    case FLASH_CFI_16BIT:
        debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit/n", addr.wp,
               cmd, cword.w,
               info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
        *addr.wp = cword.w; 这里就是写命令的过程。
        break;
    case FLASH_CFI_32BIT:
        debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit/n", addr.lp,
               cmd, cword.l,
               info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
        *addr.lp = cword.l;
        break;
    case FLASH_CFI_64BIT:
        *addr.llp = cword.ll;
        break;
    }
}

inline uchar *flash_make_addr (flash_info_t * info, flash_sect_t sect, uint offset)
{
    return ((uchar *) (info->start[sect] + (offset * info->portwidth)));
}
上面就是计算地址的过程,这个地址根据sect和offset计算出确切的地址,比如我们要访问0x5555这个地址,可以是sect= 0,offset = 0x5555
然后把offset左移一位,这个地址就是nor flash想要的地址,从nor的角度看到的是0x5555这个地址,从cpu来说,它输出的地址是 0x5555<<1。
如果要访问第n块的第0x5555这个地址,那么从cpu的角度输出的地址是 n*0x10000+0x5555<<1
下面是产生命令的过程
static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf)
{
    int i;
    uchar *cp = (uchar *) cmdbuf;

    for (i = info->portwidth; i > 0; i--)
        *cp++ = (i & (info->chipwidth - 1)) ? '/0' : cmd; 小端模式,在最低的那个byte放上命令字节,很妙
}

地址和命令字节就是这样产生的。回去看erase。
下面的是检查nor的状态的函数,也就相当于检查擦除操作成功与否的函数,擦除成功要返回0
成功后还要复位一下,使nor进入到read mode
他的第三个参数是最大的等待时间,毫秒为单位,最大为32毫秒
static int flash_full_status_check(info, 15, info->erase_blk_tout, "erase")
static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
                    ulong tout, char *prompt)
{
    int retcode;

    retcode = flash_status_check (info, sector, tout, prompt);
    switch (info->vendor) {
    case CFI_CMDSET_INTEL_EXTENDED:
    case CFI_CMDSET_INTEL_STANDARD:
        if ((retcode == ERR_OK)
            && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) {
            retcode = ERR_INVAL;
            printf ("Flash %s error at address %lx/n", prompt,
                info->start[sector]);
            if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) {
                puts ("Command Sequence Error./n");
            } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) {
                puts ("Block Erase Error./n");
                retcode = ERR_NOT_ERASED;
            } else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) {
                puts ("Locking Error/n");
            }
            if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) {
                puts ("Block locked./n");
                retcode = ERR_PROTECTED;
            }
            if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS))
                puts ("Vpp Low Error./n");
        }
        flash_write_cmd (info, sector, 0, info->cmd_reset);
        break;
    default: 我添加的复位命令,为sst39vf1601
        flash_write_cmd (info, 0, 0, info->cmd_reset);
        break;
    }
    return retcode;
}
它把参数全部传递给了 flash_status_check 函数,寄希望于他能够返回0。
static int flash_status_check (flash_info_t * info, flash_sect_t sector,
                   ulong tout, char *prompt)
{
    ulong start;
    tout *= CFG_HZ/1000;

    /* Wait for command completion */
    start = get_timer (0);
    while (flash_is_busy (info, sector)) {
        if (get_timer (start) > tout) {
            printf ("Flash %s timeout at address %lx data %lx/n",
                prompt, info->start[sector],
                flash_read_long (info, sector, 0));
            flash_write_cmd (info, sector, 0, info->cmd_reset);
            return ERR_TIMOUT;
        }
        udelay (1);        /* also triggers watchdog */
    }
    return ERR_OK;
}

/* the PWM TImer 4 uses a counter of 15625 for 10 ms, so we need */
/* it to wrap 100 times (total 1562500) to get 1 sec. */
#define    CFG_HZ            1562500
#define ERR_OK                0

显然希望flash_is_busy (info, sector) 在时间到期之前返回0
这个函数完成的任务是查询flash忙不忙,不忙返回0
flash_is_busy (info, sector)
static int flash_is_busy (flash_info_t * info, flash_sect_t sect)
{
    int retval;

    switch (info->vendor) {
    case CFI_CMDSET_INTEL_STANDARD:
    case CFI_CMDSET_INTEL_EXTENDED:
        retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE);
        break;
    case CFI_CMDSET_AMD_STANDARD:
    case CFI_CMDSET_AMD_EXTENDED:
        retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE);
        break;
    default:
        我添加的 for sst39vf1601,这个函数的功能见下面
        retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); 经过这里的取反后,返回0
        retval = 0;
    }
    debug ("flash_is_busy: %d/n", retval);
    return retval;
}

下面的这个函数测试FLASH_STATUS_DONE(flash 状态完成标志),返回这个标志的数值
flash_isset (info, sect, 0, FLASH_STATUS_DONE)
static int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd)
{
    cfiptr_t cptr;
    cfiword_t cword;
    int retval;

    cptr.cp = flash_make_addr (info, sect, offset); 这里无所谓
    flash_make_cmd (info, cmd, &cword); 
    switch (info->portwidth) {
    case FLASH_CFI_8BIT:
        retval = ((cptr.cp[0] & cword.c) == cword.c);
        break;
    case FLASH_CFI_16BIT:
        retval = ((cptr.wp[0] & cword.w) == cword.w); 从地址处取得一个16bit数,与要求的poll date相比较,不忙返回真
        break;
    case FLASH_CFI_32BIT:
        retval = ((cptr.lp[0] & cword.l) == cword.l);
        break;
    case FLASH_CFI_64BIT:
        retval = ((cptr.llp[0] & cword.ll) == cword.ll);
        break;
    default:
        retval = 0;
        break;
    }
    return retval;
}

到了这里,我们的程序,已经擦除了要求的扇区了。^_^
返回int saveenv(void)

3.开始写进去
写成功,返回0,这个(char *)0xxxxx 是什么呢?下面告诉你,反正是个内存地址而不是0xf0000
rc = flash_write((char *)0xxxxx, 0xf0000, 0x10000); 开始写进去
env_t *env_ptr = (env_t *)CFG_ENV_ADDR = 0xf0000;
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR = 0xf0000;
/*-----------------------------------------------------------------------
 * Copy memory to flash.
 * Make sure all target addresses are within Flash bounds,
 * and no protected sectors are hit.
 * Returns:
 * ERR_OK          0 - OK
 * ERR_TIMOUT      1 - write timeout
 * ERR_NOT_ERASED  2 - Flash not erased
 * ERR_PROTECTED   4 - target range includes protected sectors
 * ERR_INVAL       8 - target address not in Flash memory
 * ERR_ALIGN       16 - target address not aligned on boundary
 *            (only some targets require alignment)
 */
int
flash_write (char *src, ulong addr, ulong cnt)
{
    int i;
    ulong         end        = addr + cnt - 1; 环境参数的结束地址?在flash里的结束地址
    flash_info_t *info_first = addr2info (addr); 这是在做什么呢?下面有解释,他返回了这个地址对应的flash_info结构
    flash_info_t *info_last  = addr2info (end ); 同样,返回了对应flash_info结构,这两个flash_info可能不同,但是这里肯定相同。
    flash_info_t *info;

    if (cnt == 0) { 不写也可以认为是成功的写操作
        return (ERR_OK);
    }

    if (!info_first || !info_last) { 出错判断
        return (ERR_INVAL);
    }

    for (info = info_first; info <= info_last; ++info) { 这个多余了,只有一个flash_info结构
        ulong b_end = info->start[0] + info->size;    /* bank end addr */ 结束地址2M
        short s_end = info->sector_count - 1;  扇区数量
        for (i=0; i<info->sector_count; ++i) { 扫描所有扇区
            ulong e_addr = (i == s_end) ? b_end : info->start[i + 1];

            if ((end >= info->start[i]) && (addr < e_addr) &&
                (info->protect[i] != 0) ) {
                return (ERR_PROTECTED);
            }
        }
    }

    /* finally write data to flash */
    for (info = info_first; info <= info_last && cnt>0; ++info) { 只有一遍
        ulong len;

        len = info->start[0] + info->size - addr;
        if (len > cnt)
            len = cnt;
        if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) { 这里是真正的写操作
            return (i);
        }
        cnt  -= len;
        addr += len;
        src  += len;
    }
    return (ERR_OK);
}

addr2info 他根据给定的地址,确认自己所在那个bank,然后返回这个bank的flash_info结构。
flash_info_t *
addr2info (ulong addr)
{
    flash_info_t *info;
    int i;

    for (i=0, info=&flash_info[0]; i<CFG_MAX_FLASH_BANKS; ++i, ++info) { 扫描所有bank
        if (info->flash_id != FLASH_UNKNOWN &&
            addr >= info->start[0] &&
            /* WARNING - The '- 1' is needed if the flash
             * is at the end of the address space, since
             * info->start[0] + info->size wraps back to 0.
             * Please don't change this unless you understand this.
             */
            addr <= info->start[0] + info->size - 1) {
            return (info);
        }
    }

    return (NULL);
}

希望这个函数返回0
write_buff(info, (uchar *)src, addr, len)
write_buff(info, (uchar *)0x0xxxxx, 0xf0000, 0x10000)

/*-----------------------------------------------------------------------
 * Copy memory to flash, returns:
 * 0 - OK
 * 1 - write timeout
 * 2 - Flash not erased
 */
int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
{
    ulong wp;
    ulong cp;
    int aln;
    cfiword_t cword;
    int i, rc;

    /* get lower aligned address */
    /* get lower aligned address */
    wp = (addr & ~(info->portwidth - 1)); 不要地址的最x(0,1,2)地位,分别对应8,16,32bit数据的nor

    /* handle unaligned start */ 处理没有对齐的部分
    if ((aln = addr - wp) != 0) {
        cword.l = 0;
        cp = wp;
        for (i = 0; i < aln; ++i, ++cp)
            flash_add_byte (info, &cword, (*(uchar *) cp));

        for (; (i < info->portwidth) && (cnt > 0); i++) {
            flash_add_byte (info, &cword, *src++);
            cnt--;
            cp++;
        }
        for (; (cnt == 0) && (i < info->portwidth); ++i, ++cp)
            flash_add_byte (info, &cword, (*(uchar *) cp));
        if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
            return rc;
        wp = cp;
    }

    /* handle the aligned part */
    while (cnt >= info->portwidth) { 处理对齐的部分,从这里分析 呵呵。
        cword.l = 0;
        for (i = 0; i < info->portwidth; i++) { 我的是16位的,所以循环了两次
            flash_add_byte (info, &cword, *src++); 下边有解释
        }
        if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
            return rc;
        wp += info->portwidth;
        cnt -= info->portwidth;
    }
    if (cnt == 0) {
        return (0);
    }

    /*
     * handle unaligned tail bytes
     */
    cword.l = 0;
    for (i = 0, cp = wp; (i < info->portwidth) && (cnt > 0); ++i, ++cp) {
        flash_add_byte (info, &cword, *src++);
        --cnt;
    }
    for (; i < info->portwidth; ++i, ++cp) {
        flash_add_byte (info, &cword, (*(uchar *) cp));
    }

    return flash_write_cfiword (info, wp, cword);
}

将内存中2字节的数据组合到一起。小端模式。
static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
{
    unsigned short    w;
    unsigned int    l;
    unsigned long long ll;

    switch (info->portwidth) {
    case FLASH_CFI_8BIT:
        cword->c = c;
        break;
    case FLASH_CFI_16BIT: 这里,
        w = c;
        w <<= 8;
        cword->w = (cword->w >> 8) | w; 这就是小端的方式,后来的那个8bit 放到了这个字的高 byte,把这个16BIT 的数据组合到一起
        break;
    case FLASH_CFI_32BIT:
        l = c;
        l <<= 24;
        cword->l = (cword->l >> 8) | l;
        break;
    case FLASH_CFI_64BIT:
        ll = c;
        ll <<= 56;
        cword->ll = (cword->ll >> 8) | ll;
        break;
    }
}

wp是待写入的地址,cword是要写入的数据
flash_write_cfiword (info, wp, cword)

static int flash_write_cfiword (flash_info_t * info, ulong dest,
                cfiword_t cword)
{
    cfiptr_t ctladdr;
    cfiptr_t cptr;
    int flag;

    ctladdr.cp = flash_make_addr (info, 0, 0);
    cptr.cp = (uchar *) dest;

    /* Check if Flash is (sufficiently) erased */
    switch (info->portwidth) {
    case FLASH_CFI_8BIT:
        flag = ((cptr.cp[0] & cword.c) == cword.c);
        break;
    case FLASH_CFI_16BIT:
        flag = ((cptr.wp[0] & cword.w) == cword.w);  这里,如果刚才的擦除操作成功的话,给定地址处应该为0xffff,这里flag应该为1
        break;
    case FLASH_CFI_32BIT:
        flag = ((cptr.lp[0] & cword.l) == cword.l);
        break;
    case FLASH_CFI_64BIT:
        flag = ((cptr.llp[0] & cword.ll) == cword.ll);
        break;
    default:
        return 2;
    }
    if (!flag)
        return 2;

    /* Disable interrupts which might cause a timeout here */
    flag = disable_interrupts ();

    switch (info->vendor) {
    case CFI_CMDSET_INTEL_EXTENDED:
    case CFI_CMDSET_INTEL_STANDARD:
        flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
        flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
        break;
    case CFI_CMDSET_AMD_EXTENDED:
    case CFI_CMDSET_AMD_STANDARD:
        flash_unlock_seq (info, 0);
        flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE);
        break;
    default: 我为sst39vf1601加的写命令序列
        flash_write_cmd (info, 0, 0x5555, 0xaa);
        flash_write_cmd (info, 0, 0x2aaa, 0x55);
        flash_write_cmd (info, 0, 0x5555, 0xa0);
    }

    switch (info->portwidth) {
    case FLASH_CFI_8BIT:
        cptr.cp[0] = cword.c;
        break;
    case FLASH_CFI_16BIT:
        cptr.wp[0] = cword.w; 到了这里才真正把数据写到数据线上
        break;
    case FLASH_CFI_32BIT:
        cptr.lp[0] = cword.l;
        break;
    case FLASH_CFI_64BIT:
        cptr.llp[0] = cword.ll;
        break;
    }

    /* re-enable interrupts if necessary */
    if (flag)
        enable_interrupts ();

    return flash_full_status_check (info, find_sector (info, dest), 这一步检查写入操作的成功吗?这个函数上面已经作了修改了
                    info->write_tout, "write");     注意这里的info->write_tout初始化的时候正确不正确,不然可有你好看的
}

到了这里,写入操作完毕。
上面的所有操作都依赖正确的初始化配置,下面分析下flash的初始化过程
这个函数填写flash_info[i].flash_id,获取flash的大小,然后把需要保护的区间,软件保护起来。而flash_get_size完成了大部分初始化工作

static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST;
flash_info_t flash_info[CFG_MAX_FLASH_BANKS];        /* FLASH chips info */

unsigned long flash_init (void)
{
    unsigned long size = 0;
    int i;

/* Init: no FLASHes known */
    for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) {
        flash_info[i].flash_id = SST_ID_xF1601; 填写flash_id
        size += flash_info[i].size = flash_get_size (bank_base[i], i); 获取nor容量
        if (flash_info[i].flash_id == FLASH_UNKNOWN) {
            printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB/n",
                i, flash_info[i].size, flash_info[i].size << 20);
        }
    }

    /* Monitor protection ON by default */
    flash_protect (FLAG_PROTECT_SET,    这里对uboot所在的扇区加锁
               CFG_MONITOR_BASE,
               CFG_MONITOR_BASE + monitor_flash_len  - 1,
               flash_get_info(CFG_MONITOR_BASE));

    /* Environment protection ON by default */
    flash_protect (FLAG_PROTECT_SET,        这里对存储 环境参数 的扇区加锁
               CFG_ENV_ADDR,
               CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1,
               flash_get_info(CFG_ENV_ADDR));

    return (size);
}

主要分析下flash_get_size这个函数
现看看bank_base[0]是什么
static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST;
#define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE }=0x00000000
就是bank_base[1] = {0x00000000};所以

flash_get_size (0x00000000,0)
/*
 * The following code cannot be run from FLASH!
 *
 */
ulong flash_get_size (ulong base, int banknum)
{
    flash_info_t *info = &flash_info[banknum];
    int i, j;
    flash_sect_t sect_cnt;
    unsigned long sector;
    unsigned long tmp;
    int size_ratio;
    uchar num_erase_regions;
    int erase_region_size;
    int erase_region_count;

    info->start[0] = base;

    if (flash_detect_cfi (info)) { 首先探测这个nor是不是cfi接口兼容的。
        debug ("pass flash_detect_cfi()/n");
        info->vendor = flash_read_ushort (info, 0, 0); 填充flash_info.vendor 这个vendor是0x00bf,但是在这里读错了,
                                                        因为他返回的是0x4bbf,不是0xbf
        info->vendor = CFI_SST_1601_LZD;        所以我人为的加了下面的语句,呵呵。可能是读的太快了吧,不管了。
        switch (info->vendor) {
            case CFI_SST_1601_LZD: 这个是我加的,CFI_SST_1601_LZD = 0x00bf
            default:
                info->cmd_reset = SST_FLASH_CMD_RESET; 填充flash_info.cmd_reset,设置复位命令
                break;    
        }
        debug ("manufacturer is %x/n", info->vendor);
        size_ratio = info->portwidth / info->chipwidth;
        /* if the chip is x8/x16 reduce the ratio by half */
        if ((info->interface == FLASH_CFI_X8X16) 这里不满足条件
            && (info->chipwidth == FLASH_CFI_BY8)) {
            size_ratio >>= 1;
        }
        num_erase_regions = flash_read_uchar (info, FLASH_OFFSET_NUM_ERASE_REGIONS);
        debug ("size_ratio %d port %d bits chip %d bits/n",
               size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH,
               info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
        debug ("found %d erase regions/n", num_erase_regions);
        sect_cnt = 0;
        sector = base;
            erase_region_size = 0xffff+1;
            erase_region_count = 32;
            debug ("erase_region_count = %d erase_region_size = %d/n",
                erase_region_count, erase_region_size);
            for (j = 0; j < erase_region_count; j++) { 这里完成了各个扇区地址的确定
                info->start[sect_cnt] = sector;
                sector += (erase_region_size * size_ratio);

                info->protect[sect_cnt] = 0;        起初没有任何保护

                sect_cnt++;
            }
    

        info->sector_count = sect_cnt;        确定扇区的数量
        /* multiply the size by the number of chips */
        info->size = (1 << flash_read_uchar (info, FLASH_OFFSET_SIZE)) * size_ratio; 确定大小,2^15=2M byte

        info->buffer_size = (1 << flash_read_ushort (info, 0, FLASH_OFFSET_BUFFER_SIZE)); 不支持

        tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_ETOUT);
        info->erase_blk_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_EMAX_TOUT)));
debug ("info->erase_blk_tout:%d/n", info->erase_blk_tout);

        tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WBTOUT)) *
            (1 << flash_read_uchar (info, FLASH_OFFSET_WBMAX_TOUT));
        info->buffer_write_tout = tmp / 1000 + (tmp % 1000 ? 1 : 0); /* round up when converting to ms */
debug ("info->buffer_write_tout:%d/n", info->buffer_write_tout);

        tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WTOUT)) *
              (1 << flash_read_uchar (info, FLASH_OFFSET_WMAX_TOUT));
        info->write_tout = ~0; 这里要把得到的us变成ms,应该是1ms了,因为datasheet上说最大25us。
debug ("info->write_tout:%d/n", info->write_tout);                但是我改成了~0,因为这个参数让我写不成功,所以我增加了timeout


        info->flash_id = FLASH_MAN_CFI;

        if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) {
            info->portwidth >>= 1;    /* XXX - Need to test on x8/x16 in parallel. */
        }
    }

    flash_write_cmd (info, 0, 0, info->cmd_reset); 这里必须要复位一下,回到read 模式,否则就处在cfi模式里,出不来了。150+70ns就ok。
    debug ("info->size : %d/n", info->size);
    return (info->size);
}

到了这里就ok了。

这个函数将进入cfi的QRY模式,并完成chipwidth和portwidth的设置,都是2,返回1
static int flash_detect_cfi (flash_info_t * info)
{
    debug ("flash detect cfi/n");

    for (info->portwidth = CFG_FLASH_CFI_WIDTH;
         info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) {
        for (info->chipwidth = FLASH_CFI_BY8;
             info->chipwidth <= info->portwidth;
             info->chipwidth <<= 1) {
            flash_write_cmd (info, 0, 0x5555, 0xaa); 进入cfi的QRY模式
            flash_write_cmd (info, 0, 0x2aaa, 0x55);
            flash_write_cmd (info, 0, 0x5555, 0x98);
            if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
                && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
                && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
                info->interface = flash_read_ushort (info, 0, FLASH_OFFSET_INTERFACE);
                debug ("device interface is %d/n",
                       info->interface);
                debug ("found port %d chip %d ",
                       info->portwidth, info->chipwidth);
                debug ("port %d bits chip %d bits/n",
                       info->portwidth << CFI_FLASH_SHIFT_WIDTH,
                       info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
                return 1;
            }
        }
    }
    debug ("not found/n");
    return 0;
}

下面我们来看看这个神秘的0xxxxx地址是怎么确定的,可以猜测到,他是个内存地址,而且,这个地址里有环境变量的值,
而且这个环境变量的值是从0xf0000这块flash里拷贝到内存的。:-)

在board.c里有下面的初始化指针数组
init_fnc_t *init_sequence[] = {
    cpu_init,        /* basic cpu dependent setup */
    board_init,        /* basic board dependent setup */
    interrupt_init,        /* set up exceptions */
    env_init, 就是这个函数负责环境参数的初始化
......

在common/env_flash.c中
不要忘了这个 env_t *env_ptr = (env_t *)CFG_ENV_ADDR 哦!
CFG_ENV_ADDR 这个预定义 确定了我们的环境变量在nor flash的什么位置。

int  env_init(void)
{
    if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { 如果这段数据通过了crc校验,就设置下面两个变量。
        gd->env_addr  = (ulong)&(env_ptr->data);    这个全局量确定了环境数据的地址
        gd->env_valid = 1;        这个量指示,用norflash 里的环境变量数据,程序当然会运行到这里。
        return(0);
    }
    gd->env_addr  = (ulong)&default_environment[0]; 否则就用默认的环境参数
    gd->env_valid = 0;            相反
    return (0);
}

typedef    struct environment_s {
    unsigned long    crc;        /* CRC32 over data bytes    */ 前四个字节是crc校验的值
    unsigned char    data[ENV_SIZE]; /* Environment data        */ 这里是环境变量字符串
} env_t;

# define ENV_HEADER_SIZE    (sizeof(unsigned long))
#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE)

出于好奇,看看 default_environment是什么。
uchar default_environment[] = {
#ifdef    CONFIG_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="    MK_STR(CONFIG_BOOTDELAY)    "/0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
    "baudrate="    MK_STR(CONFIG_BAUDRATE)        "/0"
#endif
#ifdef    CONFIG_LOADS_ECHO
    "loads_echo="    MK_STR(CONFIG_LOADS_ECHO)    "/0"
#endif
#ifdef    CONFIG_ETHADDR
    "ethaddr="    MK_STR(CONFIG_ETHADDR)        "/0"
#endif
#ifdef    CONFIG_ETH1ADDR
    "eth1addr="    MK_STR(CONFIG_ETH1ADDR)        "/0"
#endif
#ifdef    CONFIG_ETH2ADDR
    "eth2addr="    MK_STR(CONFIG_ETH2ADDR)        "/0"
#endif
#ifdef    CONFIG_ETH3ADDR
    "eth3addr="    MK_STR(CONFIG_ETH3ADDR)        "/0"
#endif
#ifdef    CONFIG_IPADDR
    "ipaddr="    MK_STR(CONFIG_IPADDR)        "/0"
#endif
#ifdef    CONFIG_SERVERIP
    "serverip="    MK_STR(CONFIG_SERVERIP)        "/0"
#endif
#ifdef    CFG_AUTOLOAD
    "autoload="    CFG_AUTOLOAD            "/0"
#endif
#ifdef    CONFIG_PREBOOT
    "preboot="    CONFIG_PREBOOT            "/0"
#endif
#ifdef    CONFIG_ROOTPATH
    "rootpath="    MK_STR(CONFIG_ROOTPATH)        "/0"
#endif
#ifdef    CONFIG_GATEWAYIP
    "gatewayip="    MK_STR(CONFIG_GATEWAYIP)    "/0"
#endif
#ifdef    CONFIG_NETMASK
    "netmask="    MK_STR(CONFIG_NETMASK)        "/0"
#endif
#ifdef    CONFIG_HOSTNAME
    "hostname="    MK_STR(CONFIG_HOSTNAME)        "/0"
#endif
#ifdef    CONFIG_BOOTFILE
    "bootfile="    MK_STR(CONFIG_BOOTFILE)        "/0"
#endif
#ifdef    CONFIG_LOADADDR
    "loadaddr="    MK_STR(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="    MK_STR(CONFIG_PCI_BOOTDELAY)    "/0"
#endif
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
#endif
    "/0"
};

符合道理,呵呵。现在
gd->env_addr  = (ulong)&(env_ptr->data) = 0xf0000 + 4
gd->env_valid = 1;
显然,环境变量如果只在flash中,setenv这样的命令,将不能工作,因为setenv只是在内存中改变环境参数的值,
printenv可以查看到改变的值。
在下面的函数中
void env_relocate (void)
{
    
    /*
     * We must allocate a buffer for the environment
     */
    env_ptr = (env_t *)malloc (CFG_ENV_SIZE); 首先分配内存,看到希望了,^_^

    /*
     * After relocation to RAM, we can always use the "memory" functions
     */
    env_get_char = env_get_char_memory;

    if (gd->env_valid == 0) { 经过刚才env_init的初始化,gd->env_valid 这个值是1
        if (sizeof(default_environment) > ENV_SIZE)
        {
            puts ("*** Error - default environment is too large/n/n");
            return;
        }
        memset (env_ptr, 0, sizeof(env_t));
        memcpy (env_ptr->data,
            default_environment,
            sizeof(default_environment));
        env_crc_update ();
        gd->env_valid = 1;
    }
    else {  所以到了这里
        env_relocate_spec ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);

}        

void env_relocate_spec (void)
{
    memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE);
}

memcpy (env_ptr, 0xf0000, 0x10000);
上面分析0xxxxx地址是什么的过程是一个反推过程。
(完)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值