STM32F2: Makefile, tập lệnh liên kết và kết hợp tập tin khởi động mà không có IDE thương mại


16

Tôi đã làm việc với STM32F2 (cụ thể là STM32F217IGH6 trên bảng phát triển) trong khoảng hai tháng. Cho đến nay, vấn đề lớn nhất của tôi là liên quan đến "thiết lập", bao gồm makefile, tập lệnh liên kết và tập tin khởi động.

Cụ thể, tôi đã không thể thiết lập đúng bảng vectơ ngắt của mình và có các trình xử lý ngắt được gọi. ST không cung cấp các ví dụ phù hợp với IDE thương mại. Thay vào đó, tôi đang sử dụng trình biên dịch lại Yagarto miễn phí của chuỗi công cụ GCC (và OpenOCD để tải hình ảnh qua JTAG).

Có các dự án ví dụ cho hội đồng quản trị của tôi (hoặc một người anh em họ thân của nó) có chứa tệp tạo tệp, tập lệnh liên kết và tập tin khởi động thích hợp cho các IDE phi thương mại được thiết lập để xử lý ngắt được gọi không?


2
Bạn nên tìm các ví dụ về Cortex M3, bo mạch và bộ xử lý chính xác không quan trọng đối với những thứ bạn yêu cầu. Bạn có thể cần phải sửa đổi bố cục bộ nhớ trong tập lệnh liên kết và phương thức để flash trong tệp thực hiện, nhưng đó là tất cả.
starblue

1
Bạn có thể đặt tất cả những thứ này trong một repo git và đặt nó trên github hay cái gì đó không?
AngryEE

1
Đây chính xác là lý do tôi ngừng sử dụng STM ngay khi tôi thử nó. Nếu họ sẽ làm cho cuộc sống của tôi trở nên khó khăn với chuỗi công cụ khó chịu, thì tôi sẽ đi nơi khác. Khi tôi thử IDE cho PSoC3 và PSoC5, đó là một thế giới khác biệt.
Rocketmagnet

Bạn có cam kết với Yagarto? Điều đó hoàn toàn tốt và tạo ra một câu hỏi hay, nhưng tôi quen thuộc với chuỗi công cụ CodeSourcery Lite . Một câu trả lời cho một chuỗi công cụ khác nhau có thể có thể được điều chỉnh, nhưng sẽ không hoạt động tốt.
Kevin Vermeer

Câu trả lời:


20

http://github.com/dwelch67

stm32f4 và stm32vld nói riêng, nhưng những người khác cũng có thể hữu ích cho bạn. mbed và thư mục mzero dưới mbed (cortex-m0).

Tôi thích cách tiếp cận ngu ngốc đơn giản, tập lệnh liên kết tối thiểu, mã khởi động tối thiểu, v.v ... Công việc được thực hiện bởi mã không phải bởi bất kỳ chuỗi công cụ cụ thể nào.

Hầu hết các dạng gcc và binutils (có khả năng ngón tay cái) sẽ hoạt động phần nào với các ví dụ này vì tôi sử dụng trình biên dịch để biên dịch không phải là tài nguyên cho các cuộc gọi thư viện, tôi không sử dụng các tập lệnh liên kết chứng khoán, v.v. Gcc và binutils cũ hơn sẽ không biết về các phần thumb2 mới hơn để một số thay đổi có thể được yêu cầu.

Tôi xây dựng gcc, binutils và llvm / clang của riêng mình cũng như sử dụng mã hóa chẳng hạn (bây giờ là đồ họa cố vấn nhưng bạn vẫn có thể có phiên bản miễn phí / lite).

Esp khi bắt đầu kết hợp một dự án cho một mục tiêu mới, bạn cần thực hiện một số thao tác tháo gỡ. Đặc biệt để đảm bảo các mục là nơi bạn muốn, ví dụ bảng vectơ.

Nhìn vào stm32f4d / blinker02 chẳng hạn. Nó bắt đầu với vectors.s bảng ngoại lệ / vector cộng với một số thói quen hỗ trợ asm:

/* vectors.s */
.cpu cortex-m3
.thumb

.word   0x20002000  /* stack top address */
.word   _start      /* 1 Reset */
.word   hang        /* 2 NMI */
.word   hang        /* 3 HardFault */
.word   hang        /* 4 MemManage */
.word   hang        /* 5 BusFault */
.word   hang        /* 6 UsageFault */
.word   hang        /* 7 RESERVED */
.word   hang        /* 8 RESERVED */
.word   hang        /* 9 RESERVED*/
.word   hang        /* 10 RESERVED */
.word   hang        /* 11 SVCall */
.word   hang        /* 12 Debug Monitor */
.word   hang        /* 13 RESERVED */
.word   hang        /* 14 PendSV */
.word   hang        /* 15 SysTick */
.word   hang        /* 16 External Interrupt(0) */
.word   hang        /* 17 External Interrupt(1) */
.word   hang        /* 18 External Interrupt(2) */
.word   hang        /* 19 ...   */

.thumb_func
.global _start
_start:
    /*ldr r0,stacktop */
    /*mov sp,r0*/
    bl notmain
    b hang

.thumb_func
hang:   b .

/*.align
stacktop: .word 0x20001000*/

;@-----------------------
.thumb_func
.globl PUT16
PUT16:
    strh r1,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl GET16
GET16:
    ldrh r0,[r0]
    bx lr

.end

Không có gián đoạn trong ví dụ này, nhưng những thứ khác bạn cần đều ở đây.

blinker02.c chứa phần chính của mã C với điểm nhập C mà tôi gọi là notmain () để tránh gọi nó là chính (một số trình biên dịch thêm rác vào tệp nhị phân của bạn khi bạn có tệp chính ()).

sẽ dành cho bạn một vết cắt và dán. makefile kể câu chuyện về biên dịch và liên kết. Lưu ý rằng một số ví dụ của tôi biên dịch hai hoặc nhiều nhị phân từ cùng một mã. trình biên dịch gcc, trình biên dịch clang của llvm, chỉ ngón cái và ngón cái2, tối ưu hóa khác nhau, v.v.

Bắt đầu bằng cách tạo các tệp đối tượng từ các tệp nguồn.

vectors.o : vectors.s
    $(ARMGNU)-as vectors.s -o vectors.o

blinker02.gcc.thumb.o : blinker02.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c blinker02.c -o blinker02.gcc.thumb.o

blinker02.gcc.thumb2.o : blinker02.c
    $(ARMGNU)-gcc $(COPS) -mthumb -mcpu=cortex-m3 -march=armv7-m -c blinker02.c -o blinker02.gcc.thumb2.o

blinker02.gcc.thumb.bin : memmap vectors.o blinker02.gcc.thumb.o
    $(ARMGNU)-ld -o blinker02.gcc.thumb.elf -T memmap vectors.o blinker02.gcc.thumb.o
    $(ARMGNU)-objdump -D blinker02.gcc.thumb.elf > blinker02.gcc.thumb.list
    $(ARMGNU)-objcopy blinker02.gcc.thumb.elf blinker02.gcc.thumb.bin -O binary

blinker02.gcc.thumb2.bin : memmap vectors.o blinker02.gcc.thumb2.o
    $(ARMGNU)-ld -o blinker02.gcc.thumb2.elf -T memmap vectors.o blinker02.gcc.thumb2.o
    $(ARMGNU)-objdump -D blinker02.gcc.thumb2.elf > blinker02.gcc.thumb2.list
    $(ARMGNU)-objcopy blinker02.gcc.thumb2.elf blinker02.gcc.thumb2.bin -O binary

Trình liên kết, ld, sử dụng tập lệnh liên kết mà tôi gọi là memmap, những điều này có thể cực kỳ đau đớn, đôi khi vì một lý do chính đáng, đôi khi không. Tôi thích ít hơn là cách tiếp cận nhiều hơn với một kích thước phù hợp với tất cả, tất cả mọi thứ trừ cách tiếp cận bồn rửa nhà bếp.

Tôi không sử dụng .data thông thường (hầu như không bao giờ) và ví dụ này không cần .bss vì vậy đây là tập lệnh liên kết, chỉ đủ để đặt chương trình (.text) nơi cần có cho bộ xử lý này sử dụng nó.

MEMORY
{
    ram : ORIGIN = 0x08000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > ram
}

Tôi có một vùng bộ nhớ để xác định rằng, không có gì đặc biệt về tên ram mà bạn có thể gọi nó là foo hoặc bar hoặc bob hoặc ted, không quan trọng nó chỉ liên kết các mục bộ nhớ với các phần. Các phần xác định những thứ như .text, .data, .bss, .rodata và nơi chúng đi trong bản đồ bộ nhớ.

Khi bạn xây dựng cái này, bạn thấy tôi tháo rời mọi thứ (objdump -D) bạn thấy cái này

Disassembly of section .text:

08000000 <_start-0x50>:
 8000000:       20002000        andcs   r2, r0, r0
 8000004:       08000051        stmdaeq r0, {r0, r4, r6}
 8000008:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}
 800000c:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}
 8000010:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}

Điều quan trọng cần lưu ý là địa chỉ bên trái là nơi chúng tôi muốn, mã vectors.s là đầu tiên trong tệp nhị phân (Vì nó là đầu tiên trên dòng lệnh ld, trừ khi bạn làm gì đó trong tập lệnh liên kết, các mục sẽ hiển thị lên trong nhị phân theo thứ tự chúng nằm trên dòng lệnh ld). Để khởi động đúng cách, bạn phải đảm bảo bảng vectơ của bạn ở đúng nơi. Mục đầu tiên là địa chỉ ngăn xếp của tôi, đó là tốt. Mục thứ hai là địa chỉ để bắt đầu và nó phải là một số lẻ. việc sử dụng .thumb_func trước khi một nhãn gây ra điều này xảy ra để bạn không phải làm những việc trông xấu xí khác.

08000050 <_start>:
 8000050:       f000 f822       bl      8000098 <notmain>
 8000054:       e7ff            b.n     8000056 <hang>

08000056 <hang>:
 8000056:       e7fe          

vì vậy 0x08000051 và 0x08000057 là các mục nhập vectơ thích hợp cho _start và hang. bắt đầu cuộc gọi notmain ()

08000098 <notmain>:
 8000098:       b510            push    {

Điều đó có vẻ tốt (Họ không hiển thị địa chỉ được đánh số lẻ trong quá trình tháo gỡ).

Tất cả đều tốt.

Bỏ qua ví dụ blinker05, cái này không hỗ trợ ngắt. và cần một số ram, vì vậy .bss được xác định.

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x100000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1C000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .bss  : { *(.bss*) } > ram
}

hãy nhớ ram và rom là những cái tên tùy ý, bob và ted, foo và bar đều hoạt động tốt.

Sẽ không hiển thị toàn bộ vectơ vì cortex-m3 có hàng triệu mục trong bảng vectơ nếu bạn tạo một vectơ hoàn chỉnh (thay đổi từ lõi này sang lõi khác và có thể trong cùng một lõi tùy thuộc vào các tùy chọn do nhà cung cấp chip chọn) Các phần có liên quan ở đây sau khi tháo gỡ:

08000000 <_start-0x148>:
 8000000:       20020000        andcs   r0, r2, r0
 8000004:       08000149        stmdaeq r0, {r0, r3, r6, r8}
 8000008:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
...
8000104:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000108:       08000179        stmdaeq r0, {r0, r3, r4, r5, r6, r8}
 800010c:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}

mất một số thử nghiệm và lỗi để đặt trình xử lý đó chính xác vào đúng vị trí, kiểm tra chip của bạn ở nơi cần thiết, nó không nhất thiết phải ở cùng một nơi với cái này và với rất nhiều ngắt bạn có thể đang tìm kiếm một ngắt khác. các bộ xử lý cortex-m, không giống như các nhánh thông thường, làm cho nó không cần mã trampoline cho các ngắt, chúng bảo toàn một số thanh ghi nhất định và quản lý chuyển đổi các chế độ của bộ xử lý thông qua nội dung thanh ghi liên kết. miễn là phần cứng và abi cho trình biên dịch đủ gần thì tất cả đều hoạt động. Trong trường hợp này tôi đã thực hiện trình xử lý trong C, không giống như các nền tảng khác và trước đây bạn không cần phải làm gì đặc biệt với trình biên dịch / cú pháp chỉ cần tạo một hàm (nhưng không làm những điều ngu ngốc trong hàm / trình xử lý)

//-------------------------------------------------------------------
volatile unsigned int intcounter;
//-------------------------------------------------------------------
// CAREFUL, THIS IS AN INTERRUPT HANDLER
void tim5_handler ( void )
{
    intcounter++;
    PUT32(TIM5BASE+0x10,0x00000000);
}
// CAREFUL, THIS IS AN INTERRUPT HANDLER
//-------------------------------------------------------------------

Makefile cho blinker05 phải giống với ví dụ blinker02, chủ yếu là cắt và dán cho hầu hết những thứ này. biến các tệp nguồn riêng lẻ thành các đối tượng sau đó liên kết. Tôi xây dựng cho ngón tay cái, ngón cái2 bằng cách sử dụng gcc và tiếng kêu. bạn có thể thay đổi tất cả: dòng tại thời điểm để chỉ bao gồm các mục gcc nếu bạn không có / muốn clang (llvm) tham gia. Tôi sử dụng binutils để lắp ráp và liên kết btw đầu ra clang.

Tất cả các dự án này sử dụng miễn phí, ngoài giá, nguồn mở, công cụ. không có IDE, chỉ dòng lệnh. Có, tôi chỉ gặp rắc rối với Linux chứ không phải Windows, nhưng những công cụ này cũng có sẵn cho người dùng windows, thay đổi những thứ như rm -f một cái gì đó để xóa thứ gì đó trong tệp tạo, những thứ tương tự khi xây dựng trên windows. Điều đó hoặc chạy linux trên vmware hoặc virtualbox hoặc qemu. Không sử dụng IDE có nghĩa là bạn cũng chọn trình soạn thảo văn bản của mình, tôi sẽ không hiểu điều đó, tôi có (các) mục yêu thích của mình. Lưu ý rằng một tính năng cực kỳ khó chịu của chương trình tạo gnu là nó yêu cầu các tab thực tế trong tệp tạo tệp, tôi ghét các tab vô hình với niềm đam mê. Vì vậy, một trình soạn thảo văn bản cho tệp thực hiện để lại các tab, cái còn lại cho mã nguồn tạo khoảng trắng. Tôi không biết về windows

Tôi hy vọng điều này có ích, nó không phải là chip / bo mạch chính xác mà là vỏ não-m4 cũng không phải m3, đủ gần cho cuộc thảo luận này. xem thư mục mbed hoặc stm32vld để biết cortex-m3 thực tế (không đủ khác biệt so với m4 đối với tệp tạo tệp và mã khởi động, v.v.), nhưng không được tạo bởi st. Các lõi của vỏ não phải giống nhau giữa các nhà cung cấp, vỏ não-m3 và vỏ não-m4 đều là ARMv7m và gần hơn là khác nhau. Cortex-m0 là ARMv6m, hầu như không có hướng dẫn thumb2 đủ để bận tâm, trình biên dịch không bắt kịp với nó, vì vậy chỉ sử dụng ngón tay cái (giả vờ bạn đang xây dựng ARMv4T (chỉ dùng ngón tay cái) nếu cần). Trình giả lập ngón tay cái của tôi chỉ là ngón tay cái, không có ngón tay cái2, nó cũng có thể hữu ích với bạn, tôi nghĩ rằng tôi đã làm cho nó thực hiện các ngắt trong một số hình thức hoặc thời trang.


Tôi đã đọc câu trả lời và tôi đoán rằng tác giả cho câu trả lời này sẽ là BẠN. Câu trả lời của bạn đã giúp tôi rất nhiều và thúc đẩy tôi chuyển sang bộ xử lý ARM hơn là một Fanboy của AVR và PIC. Cảm ơn
MaNyYaCk

Bạn được chào đón ... trả nó về phía trước ...
old_timer

2

Bạn có thể xem trang web này nơi nó cố gắng giải thích những điều cơ bản của trình liên kết và low_level_init trong mã.

Xin lưu ý rằng trang này tập trung vào mô tả vấn đề, vì vậy vectơ nvic là loại tối thiểu.

Sau đó, bạn có một ví dụ đầy đủ hơn trong "thư viện thiết bị ngoại vi tiêu chuẩn STM32F2xx", chỉ cần xem các phần gcc (vì Yagarto dựa trên gcc). Và có mã ví dụ trong đó sẽ giúp bạn thiết lập chính xác nvic (bảng vectơ ngắt).

Vì vậy, ngay cả khi đây không phải là một câu trả lời hoàn chỉnh, tôi hy vọng nó vẫn hữu ích.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.