簡體   English   中英

STM32F407 匯編,STR 未寫入內存

[英]STM32F407 Assembly, STR not writing into memory

我正在嘗試在純匯編中對我的 STM32F407 進行編程(學習目的)。 我已將調試范圍縮小到一個奇怪的問題,即 STR 命令根本沒有將寄存器的內容寫入內存。 代碼如下。 本質上,它應該將 0x4 寫入內存位置(設置 GPIO 端口),但沒有。

這是代碼:

    /*SCiFI statements*/
    .syntax unified
    .cpu cortex-m4
    /*.fpu fpv4-sp-d16*/
    .thumb

    /*Vector Table Symbols*/
    .global vtable
    .global reset_handler

    /*vtable setup*/
    .type vtable, %object
vtable:
    .word _estack
    .word reset_handler
    .size vtable, . - vtable

    /*reset handler setup*/
    .type reset_handler, %function
reset_handler:
    /*Stack Pointer reset*/
    LDR r0, =_estack
    MOV sp, r0

    /*GPIOC is at 0x4002 0800 - 0x4002 0BFF*/
    LDR r2, =0x40020800 /*Address register*/
    /*Set up GPIOC as output*/
    /*Set to GPIO output mode*/
    LDR r1, =0x04
    STR r1, [r2] /*******HERE'S THE ISSUE LINE*******/

dummy_loop:
    B dummy_loop
    .size reset_handler, . - reset_handler

鏈接腳本的完整性

estack = 0x2001c000; /*End of RAM*/

MEMORY{
    FLASH ( rx  ) : ORIGIN = 0x08000000, LENGTH = 1M
    SRAM  ( rxw ) : ORIGIN = 0x20000000, LENGTH = 112K
    GPIO  ( rw  ) : ORIGIN = 0x40020000, LENGTH = 36020400
}

我正在編譯它

arm-none-eabi-gcc -x assembler-with-cpp -c -O0 -mcpu=cortex-m4 -mthumb -Wall core.s -o core.o -g
arm-none-eabi-gcc core.o -mcpu=cortex-m4 -mthumb -Wall --specs=nosys.specs -nostdlib -lgcc -T./stm32f407.ld -o pintoggle.bin -g

其中 .s 和 .ld 文件是我在上面粘貼的文件。

懷疑者的相關 gdb 輸出

(gdb) break 30
Breakpoint 1 at 0x8000012: file core.s, line 30.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) continue
Continuing.

Breakpoint 1, reset_handler () at core.s:30
30              STR r1, [r2]
(gdb) x 0x40020800
0x40020800:     0x00000000
(gdb) s
33              B dummy_loop
(gdb) x 0x40020800
0x40020800:     0x00000000
(gdb) info registers
r0             0x2001c000          536985600
r1             0x4                 4
r2             0x40020800          1073874944
r3             0x0                 0
r4             0x0                 0
r5             0x0                 0
r6             0x0                 0
r7             0x0                 0
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0x0                 0
r12            0x0                 0
sp             0x2001c000          0x2001c000
lr             0xffffffff          -1
pc             0x8000014           0x8000014 <reset_handler+12>
xpsr           0x41000000          1090519040
msp            0x2001c000          0x2001c000
psp            0x0                 0x0
control        0x0                 0 '\000'
faultmask      0x0                 0 '\000'
basepri        0x0                 0 '\000'
primask        0x0                 0 '\000'
fpscr          0x0                 0

我不確定我做錯了什么。 該程序不是特別復雜。 操作 CPU 寄存器工作正常,但寫入內存根本不起作用。

芯片/板很好。 我通過 Keil 環境扔了一些舊的 C 代碼,它運行得很好。 我的目標根本不是使用任何 C,而是堅持使用匯編和 gcc。 這是我想學習的。

編輯:因為肯定有人會遇到這個問題並提出更多問題。 https://www.efton.sk/STM32/gotcha/index.html列出了一堆 STM32 問題,有趣的是,這個問題是 #1。

您需要先在 RCC 中啟用 gpioc。

讀-修改-寫 RCC_AHB1ENR 0x40023830 並設置位 2:

ldr r0, =0x40023830
ldr r1, [r0]
orr r1, #2
str r1, [r0]

然后你可以:

LDR r2, =0x40020800
LDR r1, =0x04
STR r1, [r2]

注意/僅供參考,由於競爭條件,這可能不起作用:

ldr r0, =0x40023830
ldr r1, [r0]
orr r1, #2
ldr r2, =0x40020800
ldr r3, =0x4
str r1, [r0]
str r3, [r2]

在啟用和時鍾啟用之間需要一定數量的時鍾來啟動 gpio,但這應該可以工作

ldr r0, =0x40023830
ldr r1, [r0]
orr r1, #2
ldr r2, =0x40020800
ldr r3, =0x4
str r1, [r0]
ldr r4, [r2]
str r3, [r2]

或者只是按照上面的順序做事情(用一些指令准備寫)。

ldr r0, =0x40023830
ldr r1, [r0]
orr r1, #2
str r1, [r0]

LDR r2, =0x40020800
LDR r1, =0x04
STR r1, [r2]

只需使用 binutils 就沒有理由與 gcc 混為一談。

flash.s

.thumb
.syntax unified

.global _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop

.thumb_func
loop:   b .

.type reset, %function
reset:
    ldr r0, =0x40023830
    ldr r1, [r0]
    orr r1, #2
    str r1, [r0]

    LDR r2, =0x40020800
    LDR r1, =0x04
    STR r1, [r2]

    ldr r0, =0x40020818
    ldr r1, =0x00000002
    ldr r2, =0x00020000
loop_top:
    str r1,[r0]
    bl delay
    str r2,[r0]
    bl delay
    b loop_top

.thumb_func
delay:
    ldr r3,=0x200000
delay_loop:
    subs r3,#1
    bne delay_loop
    bx lr
    

閃存文件

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .text   : { *(.text*)   } > rom
    .bss    : { *(.bss*)    } > ram
}

建造:

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m4 flash.s -o flash.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o -o flash.elf
arm-none-eabi-objdump -D flash.elf > flash.list
arm-none-eabi-objcopy -O binary flash.elf flash.bin

查看:

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000013    stmdaeq r0, {r0, r1, r4}
 8000008:   08000011    stmdaeq r0, {r0, r4}
 800000c:   08000011    stmdaeq r0, {r0, r4}

08000010 <loop>:
 8000010:   e7fe        b.n 8000010 <loop>

08000012 <reset>:
 8000012:   480d        ldr r0, [pc, #52]   ; (8000048 <delay_loop+0x8>)
 8000014:   6801        ldr r1, [r0, #0]
 8000016:   f041 0102   orr.w   r1, r1, #2
 800001a:   6001        str r1, [r0, #0]
 800001c:   4a0b        ldr r2, [pc, #44]   ; (800004c <delay_loop+0xc>)
 800001e:   f04f 0104   mov.w   r1, #4
 8000022:   6011        str r1, [r2, #0]
 8000024:   480a        ldr r0, [pc, #40]   ; (8000050 <delay_loop+0x10>)
 8000026:   f04f 0102   mov.w   r1, #2
 800002a:   f44f 3200   mov.w   r2, #131072 ; 0x20000

0800002e <loop_top>:
 800002e:   6001        str r1, [r0, #0]
 8000030:   f000 f804   bl  800003c <delay>
 8000034:   6002        str r2, [r0, #0]
 8000036:   f000 f801   bl  800003c <delay>
 800003a:   e7f8        b.n 800002e <loop_top>

0800003c <delay>:
 800003c:   f44f 1300   mov.w   r3, #2097152    ; 0x200000

08000040 <delay_loop>:
 8000040:   3b01        subs    r3, #1
 8000042:   d1fd        bne.n   8000040 <delay_loop>
 8000044:   4770        bx  lr
 8000046:   38300000    ldmdacc r0!, {} ; <UNPREDICTABLE>
 800004a:   08004002    stmdaeq r0, {r1, lr}
 800004e:   08184002    ldmdaeq r8, {r1, lr}
 8000052:   Address 0x0000000008000052 is out of bounds.

矢量看起來不錯,它不會立即掛起

我滿懷熱情地討厭統一語法,當然也討厭 binutils,但寫下這個:

delay_loop:
    sub r3,#1
    bne delay_loop

如果沒有 gnu 匯編程序下的統一語法,實際上會產生 subs 而不是 sub 並且使人們感到困惑(並且他們發表評論)。 所以我使用了上面的統一語法。 如果您剛開始可能應該啟用統一語法並以這種方式學習它,嘆息。 (對於您現在所處的位置,只需在某處前面的那一行啟用它並繼續做您正在做的事情)。

我對 gdb 沒有用,但也許它也可以在那里工作,但是如果您 telnet 到 openocd,您當然可以這樣做:要么通過重置直接進入無限循環來擊敗您的代碼,要么取決於調試工具只是重置而不啟動代碼,但隨后您可以通過調試接口編寫這些控制寄存器,並在編寫代碼以執行相同操作之前查看它們的工作情況。 可以節省一些時間(或者可能需要更長的時間,取決於您的編碼/調試風格)。

mdw 0x40023830
mww 0x40023830 0x00000002 (need to read-modify-write any other ones in there from the prior read)
mdw 0x40020800
mww 0x40020800 0x4
mdw 0x40020800

你應該在那里看到 0x4。

然后用 0x40020818 來改變輸出引腳的狀態。


DMA 在這里不相關。 你有處理器,然后是它的主 ahb/etc 總線,它可能通過內存控制器和其他總線,直到它遇到處理該控制寄存器的邏輯。 然后該時鍾的使能通過一定數量的門來獲得該邏輯塊的時鍾使能(在這種情況下為 gpioc)。 可能至少需要一個時鍾才能鎖存使能,然后可能至少需要另一個外設時鍾周期來啟用時鍾門,以允許時鍾通過 gpio。 現在,當看到這樣的代碼時,這里的特定貢獻者通常會發表此評論。 並且一些但不是全部的 STM32 文檔專門告訴您需要延遲多少 us 或 ms。

寫入可以是一勞永逸,地址和數據是事務的一部分,最接近芯片的第一級內存控制器在技術上可以接受這兩項並告訴處理器寫入完成(即使不是)允許它做下一件事(例如另一個 STR)。 時鍾控制邏輯之類的東西和 GPIO 之類的外圍設備可能會或可能不會在同一組總線上,但最終它們會分開。

就像從你家寄兩封信到同一個城鎮的兩個地址一樣,你把地址寫在信封上,放進郵箱,就你而言,他們寄出去了,你可以回到家里做點什么別的。 這兩個人可能乘坐相同的卡車和飛機一直到目的地城鎮的同一個郵局,但最終會分開並走不同的路徑,這可能需要不同的時間。

這被稱為競爭條件,它們非常真實,而且發生的次數比我們希望的要多,但這並不是每次出現問題時都開始恐慌或擔心的事情。 通常,供應商會在文檔或勘誤表中指出存在競爭條件。 比賽就像奧運會、馬拉松、NASCAR 中的田徑比賽一樣,有兩個或更多的事情試圖首先到達終點線,或者在這種情況下是按順序到達終點線。

這就是為什么簡單地刪除某些代碼中的 printf 會產生毀滅性的結果,因為 printf 在前后之間造成了很大的延遲。 消除延遲,可能會導致比賽。

這種特殊情況並不少見,其中您有一個控制塊,例如時鍾啟用或控制地址解碼的東西(假設您有處理器地址空間的一個區域,您可以根據他們的設計點在某處,但如果您更改它指向的內容,然后立即嘗試談論您剛剛告訴它指向的內容,您可能會遇到競爭條件)。

寫入是一勞永逸的,但讀取必須一直到外圍設備再返回,所以最壞的情況是時間/路徑。 現在我已經研究了讀路徑和寫路徑分開的邏輯,你也可以在那里進行比賽,但更多的是例外而不是規則。 因此,例如,如果您編寫一些控制寄存器,然后將其讀回,則希望設計人員將其序列化,並在寫入后進行讀取,它會完成到外圍設備和返回的完整行程,因此當它返回並讓處理器繼續,該寄存器肯定被寫入。

在這種情況下,您可能會延遲寫入 rcc 加上可能不同的總線以到達 rcc 與 gpio,從而導致可能的時間差異,從而導致這場比賽。

在您擁有的部分或其他 stm32 部分上,您可以嘗試上面提示的實驗。 使用 STR 執行 rcc 啟用,然后下一條指令執行 STR 以將引腳更改為輸出,然后您可以花時間更改引腳的狀態以說打開 LED。 如果在商店之間延遲一定數量的時鍾時它確實有效,但當它們背靠背時不起作用。 你去吧。

最大的謎是為什么讀取工作,STR,LDR,STR 在我發現競爭條件的芯片上工作。 那沒有意義。 下一個大謎團是,如果您讀取非零的調制解調器寄存器,您可以很好地感覺到您實際上正在讀取該寄存器(在這種情況下為 GPIOA 和 GPIOB 調制解調器),並且在 rcc 中禁用了 gpio時鍾使能。 正確的值回來了。 這對我來說似乎是一種繞過競爭條件的技巧。 這很可能是這樣一種情況,他們有一些芯片,他們有一個已經在使用的庫,然后下一個芯片正在設計中並且無法工作,不會去強制對 hal 進行新的更新當您可以在設計中快速解決時,對於這一部分的每個人。

所以我只測試了少數不同的部分,但是

STR (the write that enables the clock)
LDR (of the moder)
STR (of the moder)

為零件工作

STR (the write that enables the clock)
STR (of the moder)

失敗的。


DMA,直接內存訪問。

因此,讓我們考慮一下具有小緩沖區的 PWM 控制器或 UART,可能只有一個值用於該事務/周期,而一個值位於等待下一個發送緩沖區中。 作為程序員,您可能會根據設計,有一些選擇,有一個代碼循環輪詢狀態寄存器,等待指示表示正在傳輸保持寄存器值,並且現在是“空的”。 然后,您理想地希望/需要在完全傳輸先前值或 pwm 的那個時間段或發生任何事情之前寫入新值。

如果要保持外圍設備的輸出保持線速或沒有間隙或重復或外圍設備所做的任何事情,則需要消耗大量處理時間。 現在,如果您的應用程序沒有做任何其他事情,如果您正在學習使用這個外設,我會強烈主張,這就是您開始的地方。 但是下一個選項可能是您設置處理程序的中斷,當中斷發生時,您從正在維護的更大緩沖區(在 ram 中)中拉出以提供此外圍設備,如果您可以確保處理程序足夠快並且沒有其他更高優先級事情會延遲處理程序的啟動(實時),然后這將起作用。

第三個並不總是可用的東西是 DMA。 您以某種方式告訴外圍設備或第三方 dma 控制器,這是我想要發送的數據塊,這是我想要發送的數據塊,邏輯必須設計為帶有觸發 dma 的連接,導致要移動到外圍設備中的一個或任意數量的項目。 仍然可能存在導致延遲和競爭條件的爭用,但如果您希望/需要在每個周期向外圍設備提供某些內容,這是您最好的選擇。

一個相同的類似概念,你聽說過 dma,人們也認為這個概念是自由的,因為它神奇地發生在后台並且不會影響處理器。 說一個內存傳輸,設置一個 DMA 引擎,它可能是某個系統的一部分,從一個地方到另一個地方進行傳輸或數據,而不是必須做一個 memcpy 或其他類似的事情(通常用於移動幀之類的事情)像素數據到視頻卡或其他類似的東西,現在視頻卡為您生成像素做了很多工作)。 有些系統這是免費的,而許多其他系統則不是,因為它使用相同的總線,因此如果 dma 引擎正在使用總線,它會導致處理器不得不停頓,這肯定會影響處理器。

我見過一些處理器在 dma 傳輸發生時完全停止/停頓的情況,我看到一個浪費的情況,其中總線大約是四分之三的事情,在任何時候都有一個時鍾周期為 dma 保留如果您碰巧想要進行轉移並且該周期/時間塊將被使用。 基本上你總是受到 dma 的影響,即使它沒有發生(這是一個 DSP,其中執行一致性非常重要,顯然比讓代碼運行得盡可能快更重要)。

DMA 用於很多事情,m 是錯誤的,它並不總是意味着內存,它可能是從一個 fifo(技術上是一個 sram)到一些外圍設備。 盡管名稱如此,它通常意味着另一個可以啟動總線事務的總線控制器,這樣您就不必通過代碼通過主處理器創建這些事務。

老實說,您還沒有為 DMA 做好准備,也沒有為中斷做好准備。 輪詢您的方式,創建大量一次性代碼,即使您正在為特定項目工作。 對於您要追求的每個功能的每個外圍設備,創建一個或多個臨時應用程序並弄清楚它是如何工作的。 如果您陷入一個一切正常的神秘情況,並且您添加或刪除甚至與它完全無關的一行代碼並且事情中斷了那么如果它是編譯代碼然后質疑編譯器的輸出,請檢查它。

如果它是 asm(或高級別的),它可能是某種競爭條件(可能是別的東西)。 它們真的很少見,經常被記錄下來(最終),但是當它們發生時,它們可能需要很長時間才能弄清楚,有時你永遠無法確定,但添加延遲修復它,或者閱讀三遍並選擇匹配的兩個,或任何黑客,你繼續生活。 我只是警告你關於這些 STM32 設計的一部分非常真實的事情。 這可能會導致您與最初將您帶到這里的情況相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM