nandflash驅動程式編寫

来源:http://www.cnblogs.com/veryStrong/archive/2016/12/10/6155502.html
-Advertisement-
Play Games

NAND FLASH是一個存儲晶元 那麼: 這樣的操作很合理"讀地址A的數據,把數據B寫到地址A" 問1. 原理圖上NAND FLASH和S3C2440之間只有數據線, 怎麼傳輸地址? 答1.在DATA0~DATA7上既傳輸數據,又傳輸地址 當ALE為高電平時傳輸的是地址, ... ...


NAND FLASH是一個存儲晶元
那麼: 這樣的操作很合理"讀地址A的數據,把數據B寫到地址A"

問1. 原理圖上NAND FLASH和S3C2440之間只有數據線,
     怎麼傳輸地址?
答1.在DATA0~DATA7上既傳輸數據,又傳輸地址
     當ALE為高電平時傳輸的是地址,

問2. 從NAND FLASH晶元手冊可知,要操作NAND FLASH需要先發出命令
     怎麼傳入命令?

答2.在DATA0~DATA7上既傳輸數據,又傳輸地址,也傳輸命令
     當ALE為高電平時傳輸的是地址,
     當CLE為高電平時傳輸的是命令
     當ALE和CLE都為低電平時傳輸的是數據

問3. 數據線既接到NAND FLASH,也接到NOR FLASH,還接到SDRAM、DM9000等等
     那麼怎麼避免干擾?
答3. 這些設備,要訪問之必須"選中",
     沒有選中的晶元不會工作,相當於沒接一樣

問4. 假設燒寫NAND FLASH,把命令、地址、數據發給它之後,
     NAND FLASH肯定不可能瞬間完成燒寫的,
     怎麼判斷燒寫完成?
答4. 通過狀態引腳RnB來判斷:它為高電平表示就緒,它為低電平表示正忙

問5. 怎麼操作NAND FLASH呢?
答5. 根據NAND FLASH的晶元手冊,一般的過程是:
     發出命令
     發出地址
     發出數據/讀數據

          NAND FLASH                      S3C2440
發命令    選中晶元                   
          CLE設為高電平                   NFCMMD=命令值     
          在DATA0~DATA7上輸出命令值
          發出一個寫脈衝
            
發地址    選中晶元                        NFADDR=地址值
          ALE設為高電平
          在DATA0~DATA7上輸出地址值
          發出一個寫脈衝

發數據    選中晶元                        NFDATA=數據值
          ALE,CLE設為低電平
          在DATA0~DATA7上輸出數據值
          發出一個寫脈衝

讀數據    選中晶元                        val=NFDATA
          發出讀脈衝
          讀DATA0~DATA7的數據

用UBOOT來體驗NAND FLASH的操作:

1. 讀ID
                               S3C2440                 u-boot 
選中                           NFCONT的bit1設為0   md.l 0x4E000004 1; mw.l 0x4E000004  1
發出命令0x90                   NFCMMD=0x90         mw.b 0x4E000008 0x90 
發出地址0x00                   NFADDR=0x00         mw.b 0x4E00000C 0x00
讀數據得到0xEC                 val=NFDATA          md.b 0x4E000010 1
讀數據得到device code          val=NFDATA          md.b 0x4E000010 1
          0xda
退出讀ID的狀態                 NFCMMD=0xff         mw.b 0x4E000008 0xff
     
2. 讀內容: 讀0地址的數據
使用UBOOT命令:
nand dump 0
Page 00000000 dump:
        17 00 00 ea 14 f0 9f e5  14 f0 9f e5 14 f0 9f e5

                               S3C2440                 u-boot 
選中                           NFCONT的bit1設為0   md.l 0x4E000004 1; mw.l 0x4E000004  1
發出命令0x00                   NFCMMD=0x00         mw.b 0x4E000008 0x00 
發出地址0x00                   NFADDR=0x00         mw.b 0x4E00000C 0x00
發出地址0x00                   NFADDR=0x00         mw.b 0x4E00000C 0x00
發出地址0x00                   NFADDR=0x00         mw.b 0x4E00000C 0x00
發出地址0x00                   NFADDR=0x00         mw.b 0x4E00000C 0x00
發出地址0x00                   NFADDR=0x00         mw.b 0x4E00000C 0x00
發出命令0x30                   NFCMMD=0x30         mw.b 0x4E000008 0x30 
讀數據得到0x17                 val=NFDATA          md.b 0x4E000010 1
讀數據得到0x00                 val=NFDATA          md.b 0x4E000010 1
讀數據得到0x00                 val=NFDATA          md.b 0x4E000010 1
讀數據得到0xea                 val=NFDATA          md.b 0x4E000010 1
退出讀狀態                     NFCMMD=0xff         mw.b 0x4E000008 0xff


NAND FLASH驅動程式層次

看內核啟動信息
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 256 at 0x02000000
Bad eraseblock 257 at 0x02020000
Bad eraseblock 319 at 0x027e0000
Bad eraseblock 606 at 0x04bc0000
Bad eraseblock 608 at 0x04c00000
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"

搜"S3C24XX NAND Driver"
S3c2410.c (drivers\mtd\nand)

s3c2410_nand_inithw
s3c2410_nand_init_chip
nand_scan  // drivers/mtd/nand/nand_base.c 根據nand_chip的底層操作函數識別NAND FLASH,構造mtd_info
    nand_scan_ident
        nand_set_defaults
			if (!chip->select_chip)
				chip->select_chip = nand_select_chip; // 預設值不適用

			if (chip->cmdfunc == NULL)
				chip->cmdfunc = nand_command;
									chip->cmd_ctrl(mtd, command, ctrl);
			if (!chip->read_byte)
				chip->read_byte = nand_read_byte;
									readb(chip->IO_ADDR_R);
			if (chip->waitfunc == NULL)
				chip->waitfunc = nand_wait;
									chip->dev_ready
        
        
        nand_get_flash_type
            chip->select_chip(mtd, 0);
            chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
            *maf_id = chip->read_byte(mtd);
            dev_id = chip->read_byte(mtd);
    nand_scan_tail
    		mtd->erase = nand_erase;
    		mtd->read = nand_read;
    		mtd->write = nand_write;
			
			
			
s3c2410_nand_add_partition
    add_mtd_partitions
        add_mtd_device
            list_for_each(this, &mtd_notifiers) { // 問. mtd_notifiers在哪設置
                                                  // 答. drivers/mtd/mtdchar.c,mtd_blkdev.c調用register_mtd_user
                struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
                not->add(mtd);
                // mtd_notify_add  和 blktrans_notify_add
                先看字元設備的mtd_notify_add
                        class_device_create
                        class_device_create
                再看塊設備的blktrans_notify_add
                    list_for_each(this, &blktrans_majors) { // 問. blktrans_majors在哪設置
                                                            // 答. drivers\mtd\mdblock.c或mtdblock_ro.c   register_mtd_blktrans
                        struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);              
                        tr->add_mtd(tr, mtd);
                                mtdblock_add_mtd (drivers\mtd\mdblock.c)
                                    add_mtd_blktrans_dev
                                        alloc_disk
                                        gd->queue = tr->blkcore_priv->rq; // tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
                                        add_disk            



測試4th:
1. make menuconfig去掉內核自帶的NAND FLASH驅動
-> Device Drivers
  -> Memory Technology Device (MTD) support
    -> NAND Device Support
   < >   NAND Flash support for S3C2410/S3C2440 SoC
2. make uImage
   使用新內核啟動, 並且使用NFS作為根文件系統
3. insmod s3c_nand.ko
4. 格式化 (參考下麵編譯工具)
   flash_eraseall  /dev/mtd3  // yaffs
   
5. 掛接
   mount -t yaffs /dev/mtdblock3 /mnt
6. 在/mnt目錄下建文件   



編譯工具:
1. tar xjf mtd-utils-05.07.23.tar.bz2 
2. cd mtd-utils-05.07.23/util
修改Makefile:
#CROSS=arm-linux-
改為
CROSS=arm-linux-
3. make
4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/


 


  

 

struct nand_chip {
/*8 位NAND 晶元的讀寫地址*/
void __iomem    *IO_ADDR_R;
void __iomem    *IO_ADDR_W;

uint8_t    (*read_byte)(struct mtd_info *mtd);
u16    (*read_word)(struct mtd_info *mtd);

void    (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void    (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
/*讀取數據並驗證*/
int    (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
/*選定晶元*/
void    (*select_chip)(struct mtd_info *mtd, int chip);
/*檢查壞塊*/
int    (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
/*mark(標記)bad block*/
int    (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
/*發送命令/地址*/
void    (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
/*讀取晶元狀態*/
int    (*dev_ready)(struct mtd_info *mtd);
/*發送命令*/
void    (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
/*等待就緒*/
int    (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
/*擦除命令函數*/
void    (*erase_cmd)(struct mtd_info *mtd, int page);
/*掃描壞塊表*/
int    (*scan_bbt)(struct mtd_info *mtd);
int    (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
/*高級頁面寫功能*/
int    (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);

int    chip_delay;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
...................
};


struct nand_ecc_ctrl {
nand_ecc_modes_t    mode;
int    steps;
int    size;
int    bytes;
int    total;
int    prepad;
int    postpad;
struct nand_ecclayout    *layout;

/*hwctl函數:
*這個函數用來控制硬體產生ecc 
*其實它主要的工作就是控制NAND controller 向 NAND 晶元發出NAND_ECC_READ 、NAND_ECC_WRITE 和NAND_ECC_READSYN *等命令,與struct nand_chip 結構體中的cmdfunc 類似
*/
void    (*hwctl)(struct mtd_info *mtd, int mode);

/*根據data 計算ecc 值*/
int    (*calculate)(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code);
/*根據ecc 值,判斷讀寫數據時是否有錯誤發生,若有錯,則立即試著糾正,糾正失敗則返回錯誤*/
int    (*correct)(struct mtd_info *mtd, uint8_t *dat,uint8_t *read_ecc,uint8_t *calc_ecc);


/*read_page_raw write_page_raw函數
*從NAND 晶元中讀取一個page 的原始數據和向NAND 晶元寫入一個page 的原始數據,所謂的原始數據,即不對讀寫的數據做ecc處理 *該讀寫什麼值就讀寫什麼值。另外,這兩個函數會讀寫整個page 中的所有內容,即不但會讀寫一個page 中MAIN部分,還會讀寫OOB 部分。
*/
int    (*read_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);
void    (*write_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);

/*
*read_page 和write_page 在讀寫過程中會加入ecc 的計算,校驗,和糾正等處理。
*/
int    (*read_page)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);
void    (*write_page)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);

/*
*讀寫oob 中的內容,不包括MAIN 部分。
*/
int    (*read_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page,int sndcmd);
int    (*write_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page);
};
其實,以上提到的這幾個read_xxx 和write_xxx 函數,最終都會調用struct nand_chip 中的read_buf 和write_buf 這兩個函數,所以如果沒有特殊需求的話,我認為不必自己實現,使用MTD 提供的default 的函數即可。

 

 

 

  1 #include <linux/module.h>
  2 #include <linux/types.h>
  3 #include <linux/init.h>
  4 #include <linux/kernel.h>
  5 #include <linux/string.h>
  6 #include <linux/ioport.h>
  7 #include <linux/platform_device.h>
  8 #include <linux/delay.h>
  9 #include <linux/err.h>
 10 #include <linux/slab.h>
 11 #include <linux/clk.h>
 12 
 13 #include <linux/mtd/mtd.h>
 14 #include <linux/mtd/nand.h>
 15 #include <linux/mtd/nand_ecc.h>
 16 #include <linux/mtd/partitions.h>
 17 
 18 #include <asm/io.h>
 19 
 20 #include <asm/arch/regs-nand.h>
 21 #include <asm/arch/nand.h>
 22 
 23 struct s3c_nand_regs {
 24     unsigned long nfconf  ;
 25     unsigned long nfcont  ;
 26     unsigned long nfcmd   ;
 27     unsigned long nfaddr  ;
 28     unsigned long nfdata  ;
 29     unsigned long nfeccd0 ;
 30     unsigned long nfeccd1 ;
 31     unsigned long nfeccd  ;
 32     unsigned long nfstat  ;
 33     unsigned long nfestat0;
 34     unsigned long nfestat1;
 35     unsigned long nfmecc0 ;
 36     unsigned long nfmecc1 ;
 37     unsigned long nfsecc  ;
 38     unsigned long nfsblk  ;
 39     unsigned long nfeblk  ;
 40 };
 41 
 42 static struct nand_chip *s3c_nand_chip;
 43 static struct mtd_info *s3c_mtd;
 44 static struct s3c_nand_regs *s3c_nand_regs;
 45 
 46 static struct mtd_partition s3c_nand_parts[] = {
 47     [0] = {
 48         .name   = "bootloader",
 49         .size   = 0x00040000,
 50         .offset    = 0,
 51     },
 52     [1] = {
 53         .name   = "params",
 54         .offset = MTDPART_OFS_APPEND,
 55         .size   = 0x00020000,
 56     },
 57     [2] = {
 58         .name   = "kernel",
 59         .offset = MTDPART_OFS_APPEND,
 60         .size   = 0x00200000,
 61     },
 62     [3] = {
 63         .name   = "root",
 64         .offset = MTDPART_OFS_APPEND,
 65         .size   = MTDPART_SIZ_FULL,
 66     }
 67 };
 68 
 69 
 70 /*判斷忙*/
 71 static int s3c_dev_ready(struct mtd_info *mtd)
 72 {
 73     /*返回"NFSTAT的bit[0]";*/
 74     return (s3c_nand_regs->nfstat & (1<<0));
 75 }
 76 
 77 static void s3c_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl)
 78 {
 79 
 80     if (ctrl & NAND_CLE)
 81     {
 82         /* 發命令: NFCMMD=dat */
 83         s3c_nand_regs->nfcmd = dat;
 84         //writeb(cmd, host->io_base + (1 << host->board->cle));/*命令*/
 85     }
 86     else
 87     {
 88         /* 發地址: NFADDR=dat */
 89         s3c_nand_regs->nfaddr = dat;
 90         //writeb(cmd, host->io_base + (1 << host->board->ale));/*地址*/
 91     }
 92 }
 93 
 94 
 95 static void s3c_select_chip(struct mtd_info *mtd, int chip)
 96 {
 97     if(chip ==-1)
 98     {
 99         /*表示取消選中 NFCONT[1]設為1  */
100         s3c_nand_regs->nfcont |=(1<<1);    
101     }
102     else
103     {
104         s3c_nand_regs->nfcont &=~(1<<1);
105         /*選中晶元 NFCONT[1]設為0 */
106     }    
107 }
108 
109 
110 static int s3c_nand_init(void)
111 {
112 
113     struct clk *clk;
114     
115     /*1.分配一個nand_chip結構體*/
116     s3c_nand_chip =  kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
117     
118     s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
119     
120     /*2.設置nandc_chip結構體*/
121     s3c_nand_chip->IO_ADDR_R = &s3c_nand_regs->nfdata;
122     s3c_nand_chip->IO_ADDR_W = &s3c_nand_regs->nfdata;
123     s3c_nand_chip->cmd_ctrl = s3c_cmd_ctrl;
124     s3c_nand_chip->dev_ready = s3c_dev_ready;
125     s3c_nand_chip->select_chip = s3c_select_chip;
126     s3c_nand_chip->ecc.mode    = NAND_ECC_SOFT;
127     //s3c_nand_chip->chip_delay = 20;
128 
129     /* 使能NAND FLASH控制器的時鐘 */
130     clk = clk_get(NULL, "nand");
131     clk_enable(clk);              /* CLKCON'bit[4] */
132     
133 
134     /*初始化nand控制器 設置寄存器*/
135 #define TACLS   0
136 #define TWRPH0  3
137 #define TWRPH1  0
138     /* HCLK=100MHz
139      * TACLS:  發出CLE/ALE之後多長時間才發出nWE信號, 從NAND手冊可知CLE/ALE與nWE可以同時發出,所以TACLS=0
140      * TWRPH0: nWE的脈衝寬度, HCLK x ( TWRPH0 + 1 ), 從NAND手冊可知它要>=12ns, 所以TWRPH0>=1
141      * TWRPH1: nWE變為高電平後多長時間CLE/ALE才能變為低電平, 從NAND手冊可知它要>=5ns, 所以TWRPH1>=0
142      */
143     s3c_nand_regs->nfconf=(TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
144 
145     /* NFCONT: 
146      * BIT1-設為1, 取消片選 
147      * BIT0-設為1, 使能NAND FLASH控制器
148      * BIT4-設為0, 未初始化hardware ECC
149      */
150     s3c_nand_regs->nfcont = (1<<1) | (1<<0);
151     
152     /*分配一個mtd_info結構體*/
153     s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
154 
155     /*設置s3c_mtd結構體*/
156     s3c_mtd->owner = THIS_MODULE;
157     s3c_mtd->priv = s3c_nand_chip;
158 
159     /* 識別NAND FLASH, 構造mtd_info */
160     nand_scan(s3c_mtd, 1); 
161 
162     /*設置分區表*/
163     add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
164     
165     add_mtd_device(s3c_mtd);
166     return 0;
167 }
168 
169 static void s3c_nand_exit(void)
170 {
171     kfree(s3c_mtd);
172     kfree(s3c_nand_chip);
173     iounmap(s3c_nand_regs);
174 }
175 
176 module_init(s3c_nand_init);
177 module_exit(s3c_nand_exit);
178 MODULE_LICENSE("GPL");

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一.jdk的安裝與配置 3.創建軟鏈接方便原目錄/usr/local/src/jdk1.8.0_60 配置到/etc/profile,先創建兩個軟鏈接 : (1)原目錄/usr/local/src/jdk1.8.0_60 ln -s /usr/local/src/jdk1.8.0_60 java ( ...
  • 今天想用一下顯示器自帶的喇叭,忽然發現聲音輸出選項里HDMI的聲音設備沒了。之前開始使用這台顯示器的時是用過一段時間的。 百度了一番,沒發現什麼線索。後來去谷歌找到這麼一段文字: I'm not sure where to report this, but after I upgraded a Le ...
  • 問題描述:當安裝好Ubuntu系統的時候,root用戶沒有密碼,需要設置。 解決方法: ...
  • 本文由ilanniweb提供友情贊助,首發於爛泥行天下 想要獲得更多的文章,可以關註我的微信ilanniweb 最近沒有時間好久沒有寫文章了,今天由於需要安裝docker學習虛擬容器的知識,需要升級OS的內核。目前我這邊使用的OS是centos6.5,內核是2.6版本的,如下: cat /etc/i... ...
  • 字元設備是3大類設備(字元設備、塊設備和網路設備)中較簡單的一類設備,其驅動程式中完成的主要工作是初始化、添加和刪除cdev結構體,申請和釋放設備號,以及填充 file_operations結構體中的操作函數,實現file_operations結構體中的read()、write()和ioctl()等... ...
  • 問題描述:在Windows10下安裝Ubuntu。 使用工具:Windows10、Ubuntu16.04 LTS安裝包、UltraISO、easyBCD。 操作步驟: 1、安裝之前要給Ubuntu分出一定大小的磁碟空間。我用170G來安裝Ubuntu。我的硬碟比較大,如果硬碟較小,可以選擇50G等大 ...
  • 問題概述:在裝系統的時候有時候並不能一下分出完全符合我們使用習慣的分區大小,我們可能需要在後期調整分區大小。以下是有關分區大小調整的操作。 使用工具:Windows磁碟管理工具。 操作步驟: 1、使用組合鍵win+x,彈出菜單,選中“電腦管理”,選擇“磁碟管理”。 2、這裡簡要說明一下Window ...
  • 問題概述:因為在自己學習Linux的時候,按照網上的教程錯誤的刪除了Ubuntu的一個內核驅動,導致Ubuntu不能啟動。我想到的辦法是重新安裝系統,重裝系統的第一步便是將Ubuntu從電腦中卸載。該筆記是有關如何刪除Ubuntu啟動項的。 使用工具:Windows10,Ubuntu16.04 LT ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...