裸板驅動總結(makefile+lds鏈接腳本+裸板調試)

来源:http://www.cnblogs.com/lifexy/archive/2017/12/01/7941208.html
-Advertisement-
Play Games

在裸板2440中,當我們使用nand啟動時,2440會自動將前4k位元組複製到內部sram中,如下圖所示: 然而此時的SDRAM、nandflash的控制時序等都還沒初始化,所以我們就只能使用前0~4095地址,在前4k地址里來初始化SDRAM,nandflash,初始化完成後,才能將nandflas ...


在裸板2440中,當我們使用nand啟動時,2440會自動將前4k位元組複製到內部sram中,如下圖所示:

然而此時的SDRAM、nandflash的控制時序等都還沒初始化,所以我們就只能使用前0~4095地址,在前4k地址里來初始化SDRAM,nandflash,初始化完成後,才能將nandflash的4096至後面的地址內容存放到SDRAM里去.

而裸板驅動的步驟如下所示:

  • 1.makefile
  • 2.lds鏈接腳本 (供makefile調用)
  • 3.寫真正要執行的文件代碼,比如初始化nand,sdram,串口等

為什麼要寫lds鏈接腳本?

首先lds鏈接腳本的作用就是將多個*.o文件的各個段鏈接在一起,告訴鏈接器這些各個段存放的地址先後順序,它的好處就是,確保裸板2440的前4k地址里存放的是初始化SDRAM,nandflash的內容


 

1.寫makefile

(參考makefile初步製作:http://www.cnblogs.com/lifexy/p/7065175.html)

在寫裸板之前首先要來寫Makefile,如下所示:

objs := head.o init.o nand.o main.o   
//定義objs變數,表示obj文件,包含生成boot.bin目標文件需要的依賴文件, 使用$(objs)就可以使用這個變數了
//‘:=’:有關位置的等於(比如:”x:=a  y:=$(x)  x:=b”,那麼y的值取決於當時位置的a,而不是b) 
//‘=’:無關位置的等於(比如:”x=a  y=$(x)  x=b”,那麼y的值永遠等於最後的b ,而不是a)                                                     

 

nand.bin : $(objs)   //冒號前面的是表示目標文件, 冒號後面的是依賴文件,這裡是將所有*.o文件編譯出nand.bin可執行文件
arm-linux-ld -Tnand.lds    -o nand_elf $^   //將*.o文件生成nand_elf鏈接文件
//-T:指向鏈接腳本, $^:指向所有依賴文件,

arm-linux-objcopy -O binary -S nand_elf $@ //將nand_elf鏈接文件生成nand.bin文件
//$@:指向目標文件:nand.bin
//-O :選項,其中binary就是表示生成的文件為.bin文件

arm-linux-objdump -D -m arm  nand_elf > nand.dis //將nand.bin文件反彙編出nand.dis文件
//-D :反彙編nand.bin裡面所有的段, -m arm:指定反彙編文件的架構體系,這裡arm架構

 

%.o:%.c            //冒號前面的是目標文件,冒號後面的是依賴文件,%.o表示所有.o文件,

arm-linux-gcc -Wall -c -O2 -o $@ $<         //將*.c文件生成*.o文件
//$<:指向第一個依賴文件, 也就是.c文件
//$@:指向目標文件,也就是.o文件
//-Wall:編譯若有錯,便列印警告信息     -O2:編譯優化程度為2級

 

%.o:%.S                       
    arm-linux-gcc -Wall -c -O2 -o $@ $<    //將*.S文件生成*.o文件

 

clean:                           //輸入make clean,即進入該項,來刪除所有生成的文件
    rm -f  nand.dis nand.bin nand_elf *.o   //通過rm命令來刪除

2.寫lds鏈接腳本

(參考lds腳本解析: http://www.cnblogs.com/lifexy/p/7089873.html)

 SECTIONS {
    . = 0x30000000;             //指定當前的鏈接地址=0x30000000

.text          :   {
head.o(.text)    //添加第一個目標文件,裡面會調用這些函數
init.o(.text)      //添加第二個目標文件,裡面存放關看門狗,初始化SDRAM等函數
nand.o(.text)   //添加第三個目標文件,裡面存放初始化nand函數
*(.text)    // *(.text) 表示添加剩下的全部文件的.text代碼段
}

.rodata ALIGN(4) : {*(.rodata)}       //指定只讀數據段

.data ALIGN(4) : { *(.data) }     //指定讀寫數據段,     *(data):添加所有文件的數據段

__bss_start = .;     //把__bss_start賦值為當前地址位置,即bss段的開始位置

.bss ALIGN(4)  : { *(.bss)  *(COMMON) }     //指定bss段,裡面存放未被使用的變數

__bss_end = .;        //把_end賦值為當前地址位置,即bss段的結束位置

}

上面的鏈接地址=0x30000000,表示程式運行的地方應該位於0x30000000處,0x30000000就是我們的SDRAM基地址,而一上電後,nand的前4k地址會被2440自動裝載到內部ram中,所以我們初始化了sdram和nand後,就需要把程式所有內容都複製到鏈接地址0x30000000上才行

2.1為什麼要在bss段的前後設置兩個符號__bss_start, __bss_end?

定義__bss_start__bss_end符號,是用來程式開始之前將這些未定義的變數清0,節省記憶體
__bss_start -0x30000000就等於該bin文件的位元組大小,實現動態複製

2.3為什麼鏈接地址在0x30000000處,為什麼在初始化sdram和nand之前,還能運行前4k地址的內容?

我們先來看看head.S第一個目標文件,就知道了:

.text                                                           @設置代碼段
@函數disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定義 ldr sp,
=4096 @設置堆棧 bl disable_watch_dog @關WATCH DOG bl memsetup @初始化SDRAM bl nand_init @初始化NAND Flash ldr sp,=0x34000000 @64Msdram,所以設置棧SP=0x34000000,避免堆棧溢出
                          @nand_read_ll函數需要3個參數: ldr r0,
=0x30000000 @1. 目標地址=0x30000000,這是SDRAM的起始地址 mov r1, #0 @2. 源地址 = 0 ldr r2, =__bss_start sub r2,r2,r0 @3. 複製長度= __bss_start-0x30000000 bl nand_read @調用C函數nand_read,將nand的內容複製到SDRAM中 ldr lr, =halt_loop @設置返回地址 ldr pc, =main @使用ldr命令 絕對跳轉到SDRAM地址上 halt_loop: @若main函數跳出後,便進入死迴圈,避免程式跑飛 b halt_loop

(參考位置無關碼(bl)與絕對位置碼(ldr): http://www.cnblogs.com/lifexy/p/7117345.html)

從上面代碼來看,可以發現在複製數據到sdram之前,都是使用的相對跳轉命令bl,bl是一個位置無關碼,也就是說無論該代碼放在記憶體的哪個地址,都能正確運行.

ldr就是絕對跳轉命令,是一個絕對位置碼,當一上電時,我們的鏈接地址0x30000000上是沒有程式的,因為程式都存在nand flash上,也就是0地址上,而如果在複製數據到sdram之前,使用ldr去執行的話,就會直接跳轉到0x30000000上,就會運行出錯.

而且在複製數據到sdram之前,執行的代碼里都不能用靜態變數、全局變數、以及數組,因為這些初始值量的地址與位置有關的,必須將nand的內容複製到sdram地址中,才能用.

 

2.4比如,下麵memsetup ()函數,就是個會出錯的函數

其中的mem_cfg_val[]數組的記憶體是存在鏈接地址0x30000000上,就是與位置有關,在未複製內容之前使用將會出錯

#define   MEM_CTL_BASE            0x48000000           //SDRAM寄存器基地址
void memsetup()
{
   int   i = 0;
   unsigned long *p = (unsigned long *)MEM_CTL_BASE;

 

    /* SDRAM 13個寄存器的值 */

    unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON

                                            0x00000700,     //BANKCON0

                                            0x00000700,     //BANKCON1

                                            0x00000700,     //BANKCON2

                                            0x00000700,     //BANKCON3 

                                            0x00000700,     //BANKCON4

                                            0x00000700,     //BANKCON5

                                            0x00018005,     //BANKCON6

                                            0x00018005,     //BANKCON7

                                            0x008C07A3,     //REFRESH

                                            0x000000B1,     //BANKSIZE

                                            0x00000030,     //MRSRB6

                                            0x00000030,     //MRSRB7

                                    };

 

       for(; i < 13; i++)

              p[i] = mem_cfg_val[i];

}

如下3個圖所示,通過反彙編來看,上面的數組內容都是存在SDRAM的鏈接地址上面的rodata段0x300005d0里,在我們沒有初始化SDRAM,複製數據到SDRAM之前,這些數據是無法讀取到的

圖1,使用bl跳到相對地址0x30000094處:

 

圖2,使用ldr,使ip跳到絕對地址0x300005d0:

 

圖3,0x300005d0里保存的.redata只讀數據段,也就是 mem_cfg_val[]的內容:

 

2.5所以要修改memsetup ()函數為以下才行:

#define   MEM_CTL_BASE            0x48000000           //SDRAM寄存器基地址
void memsetup()
{
       unsigned long *p = (unsigned long *)MEM_CTL_BASE;

    /* 設置SDRAM 13個寄存器的值 */
   p[0]  =0x22011110,     //BWSCON
   p[1]  =0x00000700,     //BANKCON0
   p[2]  =0x00000700,     //BANKCON1
   p[3]  =0x00000700,     //BANKCON2
   p[4]  = 0x00000700,     //BANKCON3 
   p[5]  =0x00000700,     //BANKCON4
   p[6]  =0x00000700,     //BANKCON5
   p[7]  =0x00018005,     //BANKCON6
   p[8]  = 0x00018005,     //BANKCON7
   p[9] =0x008C07A3,     //REFRESH
   p[10] =0x000000B1,     //BANKSIZE
   p[11] = 0x00000030,     //MRSRB6
   p[12] =0x00000030,     //MRSRB7
}

通過反彙編來看,可以看到這些賦值,都是靠mov,add等命令來加加減減拼出來的

如下圖,我們以上面的代碼p[0]  =0x22011110為例:

 

 

3.在裸板中調試有以下幾步

3.1點燈法:

LED_SHOW:
               ldr   r0,  =0x56000050                        
               ldr  r1,  =(1<<(4*2))                 @設置GPFCON寄存器的GPF4為輸出引腳
               str  r1,  [r0]                                           
               ldr  r0,  =0x56000054                       @GPFDAT寄存器
               ldr  r1,  =0                                @設置GPF4=0,亮燈
               ldr  r2,  =(1<<4)                           @設置GPF4=1,滅燈                            

LED_LOOP:                        @死迴圈閃燈

               str  r1,  [r0]           @亮燈
               bl   DELAY
               str  r2,  [r0]         @滅燈
               bl   DELAY   
               b    LED_LOOP 

DELAY:                           @延時

             ldr r3,=30000
1:
               sub  r3,  r3,   #1
               cmp  r3,  #0
               bne     1b
               mov  pc, lr     @跳出迴圈  PS:寄存器之間賦值只能用mov

在調試彙編中:就可以使用 “b  LED_SHOW”,若LED閃爍,便說明程式已跑過,通過點燈來定位程式在哪出錯,

缺點在於需要多次燒寫才能得出結果,調試非常麻煩

3.2串口列印

首先需要通過寄存器來初始化串口

在2440中,當沒有初始化PCLK時鐘時,PCLK=12MHZ,而波特率最高就是57600,因為UBRDIV0=12000000/(57600*16-1)=13.02,所以串口代碼如下所示:

#define PCLK            12000000    // PCLK初始值為12MHz
#define UART_CLK        PCLK        //  UART0的時鐘源設為PCLK
#define UART_BAUD_RATE  57600      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)
/*
 * 初始化UART0
 * 115200,8N1,無流控
 */
void uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3內部上拉
    ULCON0  = 0x03;     // 8N1(8個數據位,無較驗,1個停止位)
    UCON0   = 0x05;     // 查詢方式,UART時鐘源為PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率為115200
}
/*
 * 發送一個字元
 */
void putc(unsigned char c)
{
    /* 等待,直到發送緩衝區中的數據已經全部發送出去 */
    while (!(UTRSTAT0 & TXD0READY));  
    /* 向UTXH0寄存器中寫入數據,UART即自動將它發送出去 */
    UTXH0 = c;
}
/*
 * 列印一串數字
 * num:數據
 */
void putnum(unsigned long num) //0xFFFF FFFF         (7:0)
{
        int i ,start=0;
        unsigned char c;
        putc('0');
        putc('x');
       for(i=7;i>=0;i--)            //從[7:0]中列印數字,去除有效數字前面的0
       {
              c=( num >> (i*4) )&0xf;
              if(c!=0)
              {
                     if(c>9)    
                            putc(c-10+'A');                //列印A~F
                     else
                            putc(c+'0');                   //列印1~9                   
              if(!start)  start=1;                         //start=1,說明為有效數字
              }
              else if((start||!i)&&c==0)       //若是有效數字,便列印0,且在個位上時,不管是否有效都要列印
              {
                     putc('0');
              }
       }
       putc('\r');
       putc('\n');
}

在調試彙編中,就可以使用:

bl      uart0_init    //初始化uart

... ...

mov   r0,#0x100     //參數等於0x100

bl    putnum       //調用列印函數

即可列印0x100數字, 能快速定位出程式在哪出錯

在c中,直接通過調用函數即可

 

3.3 使用JTAG調試器 

JTAG用於晶元的測試與程式調試,JTAG位於CPU內部,當CPU收發引腳上的數據時,都會通過JTAG單元,而JTAG單元會從CPU內部引出TMS,TCK,TDI,TDO,四個引腳,便可以通過OpenJTAG調試器連接電腦USB,而另一端連接這些JTAG腳來控制CPU

OpenJTAG可以實現:

  • 讀寫某個地址上的數據
  • 將文件下載到2440的某個地址上,或讀取出某個地址到文件中
  • 查詢CPU當前狀態、中斷CPU運行、恢復CPU運行、複位CPU等
  • 設置CPU的地址斷點,比如設置為0x30000000,當CPU運行到這個地址時,便會停止運行

斷點在調試中分為兩種:

硬體斷點,在2240中,共有兩個硬體斷點,也就是最多設置兩個硬體斷點

軟體斷點,可以設置無數個斷點

1)為什麼軟體斷點可以設置無數個?

實際JTAG後臺會把每個需要暫停的地址斷點里的數據複製到指定地址里,並賦為某個特殊值(如deeedeee),然後CPU運行時,當某個變數=這個特殊值(如deeedeee),便知道到了軟體斷點,並從指定地址里把原來的值換回去,然後暫停運行

註意:

由於軟體斷點,會後臺保存斷點數據到另一個地址中,前提要必須保證地址可直接讀寫,所以在nor flash,nand flash下則無法實現調試,若鏈接地址在SDRAM地址上,則軟體斷點的地址必須設置在SDRAM初始化後的地址上

3.3.1.通過OCD對JATG進行命令行調試

1)安裝OpenOCD

OpenOCD:既可以燒寫nor flash,也可以燒寫nand flash,並可以通過JTAG調試器來進行調試

接上OpenJTAG,並安裝OpenJTAG驅動

 

2)使用OpenOCD工具連接OpenJTAG調試器

如上圖所示:

步驟1,選擇jtag類型,CPU類型.

步驟2,點擊連接按鈕

步驟3,可以看到2440只支持2個硬體斷點

其中,work dir 就是需要燒寫的文件根目錄, 或讀取CPU某個地址內容到文件的文件根目錄

 

3)然後通過telent控制台進行調試

telent的主要目的,就是發送命令行給連接的OpenJTAG調試器,然後OpenJTAG通過命令來對CPU進行操作

首先,在win7下,若沒打開telnet客服端:

點擊開始 ->控制面板-> 程式和功能-> 打開或關閉Windows功能->打開“telnet客服端”

然後在cmd控制臺下,輸入 “telnet 127.0.0.1 4444”命令,進入telent控制台,如下圖所示:

 

 

4)接下來便可以通過命令行來實現調試(需要參考反彙編文件,來實現調試

常用的命令如下所示:

poll       

查看當前狀態

halt       

暫停CPU運行

step       

單步執行,如果指定了 address,則從 address 處開始執行一條指令

reg        

顯示CPU的r0、r1、r2、sp、lr、pc等寄存器的值(需要halt後才能看到)

resume  [addr]

恢復CPU運行,若指定了地址,便從指定地址運行(需要halt後才能使用)

例如:  resume 0                   //從0地址運行

md<w|h|b> <addr>  [size]      

read讀地址,讀出size個內容,w:字,h:半字,b:位元組.如下圖所示:

 

mw<w|h|b> <addr>  <size> 

word寫地址,寫入size個內容,使用方法和上面類似

(PS:不能直接讀寫nand和nor上的地址,只能讀寫2440的內部地址(4096),若SDRAM已初始化,也可以實現讀寫)

load_image <file> <address>  

將文件<file>載入地址為 address 的記憶體,格式有“bin”, “ihex”、 “elf”

例如: 

load_image  led.bin  0              //燒寫led.bin到0地址

 

(PS:該文件的目錄位於之前在OpenOCD工具的界面里的work dir里)

dump_image <file> <address> <size>

將記憶體從地址 address 開始的 size 位元組數據讀出,保存到文件<file>中

bp <addr> <length> [hw]

在地址 addr 處設置斷點,hw 表示硬體斷點,length為指令集位元組長度,,若未指定表示軟體斷點,比如: stm32是2個位元組長,2440是4個位元組長,部分MCU擁有多套指令集,長度不固定,如下圖所示: 

rbp <addr>

刪除地址 addr 處的斷點

bp

列印斷點信息

3.3.2通過GDB對JATG實現源碼級別的調試 

在linux中,使用arm-linux-gdb軟體

在win7中,則使用arm-none-eabi-gdb軟體

使用GDB工具,就不需要像上個OCD調試那麼麻煩了

1)比如說,想在“int i=0;”處打上斷點:

OCD調試:

就需要查看調試的反彙編文件,找到i=0所在的運行地址,然後通過命令在地址上打斷點

GDB調試:

則可以直接在i=0處的源碼上打斷點,後臺會通過帶調試的鏈接文件,來找到i=0處的運行地址,並向OpenOCD發送打斷點命令

 

2)上面的帶調試的鏈接文件又是怎麼來的?

通過Makefile里的arm-linux-gcc -c -g 來的,         -g:表示生成的鏈接文件里包含gdb調試信息

然後我們將上面第1節的Makefile修改,如下圖:

 

 

3)使用gdb之前,需要保證:

  • 1.調試的源碼裡面的內容必須位於同一個鏈接地址上, 各個段也要分開存儲,調試的鏈接腳本和上面第2節的類似,
  • 2.如果程式的鏈接地址是SDRAM, 使用openocd初始化SDRAM

4)常用命令如下所示(以調試上圖的nand_elf文件為例):

arm-none-eabi-gdb  nand_elf

啟動GDB,指定調試文件為nand_elf

target remote 127.0.0.1:3333            

連接OpenOCD

load      

載入nand_elf調試文件

break [file]:[row]

打斷點,比如:

break main.c:21     //在main.c文件的第21行處打斷點

info br

查看斷點

delete <num>

刪除第幾個斷點,如下圖所示:

 

c

恢復程式運行,若使用load後,使用c便是啟動程式, 按ctrl+c便暫停運行

step

單步執行

monitor  <cmd...>

調用OCD的命令使用,比如 : 

monitor resume 0              //使用OCD的resume命令,使程式從0地址運行

quit

退出

 

 


(PS:也可以通過eclipse平臺軟體來調用GDB,GDB最終轉換為命令行,再調用OCD來實現調試,如下圖所示)

 

 


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

-Advertisement-
Play Games
更多相關文章
  • PD虛擬機修改RemixOS的屏幕解析度 2017年12月02日02:13:55 by SemiconductorKING 最近要用個移動端APP,手機不方便就想在電腦跑一個,然後裝了個以前用過的覺得還行的RemixOS(安裝方法網上很多的:在 Parallels Desktop 上安裝 Remix ...
  • 1.1 linux配置IP地址 1️⃣ ifconfig命令臨時配置IP地址 能不能自動獲取IP地址和DNS地址,要看有沒有DHCP伺服器 win10上,輸入ipconfig /all 會看到是否有DHCP伺服器 ifconfig命令 對此命令更加詳細的解說 傳送門 http://xp9802.it ...
  • 最近自己裝了個ubuntu kylin 在使用的過程中發現,系統的apt-get 的源有毛病,總是安裝不了軟體。 感覺應該是傳說中的牆的原因,所以準備換到阿裡雲的源。 下麵是步驟: 1、複製原文件備份 sudo cp /etc/apt/source.list /etc/apt/source.list ...
  • 我們在學習的時候使用vmware創建自己的虛擬機,但是我們有時學習環境需要多台電腦進行操作演示,如果安裝創建虛擬機、再在虛擬機上安裝操作系統、這樣很花費我們的時間,而且還步能保證服務的一直性,這就用到vmware的強大功能——克隆,廢話不多說,操作如下: 一、虛擬機克隆 1、在vmware上創建一 ...
  • 有時候我們需要修改主機的host主機名,方便管理和識別自己的伺服器,修改步驟如下: 第一步: vi /etc/hosts 正常情況下,修改了第一步就可以了,如果通過hostname命令查看還是修改以前的話,執行第二步 第二步: vi /etc/sysconfig/network 把hostname設 ...
  • 阿裡雲CentOS7.3搭建 Apache+MySQL+PHP環境 參考https://www.cnblogs.com/apro-abra/p/4862285.html 一、安裝Apache 1.安裝 yum -y install httpd yum -y install httpd yum -y ...
  • Ceph官方版本目前支持的糾刪碼很有限,實驗室這塊希望能夠整合我們自主開發的糾刪碼BRS( " B inary R eed– S olomon encoding" ),所以需要編譯Ceph環境。Ceph官方目前推薦的安裝方式都是通過Ceph deploy的工具來安裝配置,搭建起來十分簡單。目前直接通 ...
  • 1、馮洛伊曼的主要思想: (1)用二進位代碼表示程式和數據; (2)電腦採用存儲程式的工作方式; (3)電腦硬體由存儲器、運算器、控制器、輸入設備和輸出設備等五大部件組成。 2、電腦性能的定義 (1)相應時間(Response Time) 從提出請求到被執行的時間。 (2)吞吐率(Throug ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...