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.