STM32F2: Makefile, script de vinculador e combinação de arquivos de inicialização sem IDE comercial


16

Trabalho com um STM32F2 (especificamente, o STM32F217IGH6 em uma placa de desenvolvimento) há cerca de dois meses. De longe, meu maior problema tinha a ver com a "configuração", que inclui makefile, script de vinculador e arquivo de inicialização.

Em particular, não consegui configurar corretamente minha tabela de vetores de interrupção e chamei os manipuladores de interrupção. A ST fornece exemplos personalizados para IDEs comerciais. Em vez disso, estou usando a recompilação gratuita do Yagarto da cadeia de ferramentas GCC (e do OpenOCD para carregar a imagem via JTAG).

Existem exemplos de projetos para minha diretoria (ou um primo próximo dela) que contenham a combinação apropriada de makefile, script de vinculador e arquivo de inicialização para IDEs não comerciais configurados para que os manipuladores de interrupção sejam chamados?


2
Você deve procurar exemplos do Cortex M3, a placa e o processador exatos não são tão importantes para as coisas que você pede. Você provavelmente precisará modificar o layout da memória no script do vinculador e o método para piscar no makefile, mas isso deve ser tudo.
starblue

11
Você pode colocar tudo isso em um repositório git e colocá-lo no github ou algo assim?
AngryEE

11
Foi exatamente por isso que parei de usar o STM assim que o experimentei. Se eles vão dificultar minha vida com a desagradável cadeia de ferramentas, então vou para outro lugar. Quando experimentei o IDE para o PSoC3 e PSoC5, havia um mundo de diferença.
Rocketmagnet

Você está comprometido com Yagarto? Isso é ótimo e faz uma ótima pergunta, mas eu estou familiarizado com a cadeia de ferramentas CodeSourcery Lite . Uma resposta para uma cadeia de ferramentas diferente provavelmente poderia ser adaptada, mas não funcionaria imediatamente.
Kevin Vermeer

Respostas:


20

http://github.com/dwelch67

stm32f4 e stm32vld em particular, mas os outros também podem ser úteis para você. mbed e o diretório mzero em mbed (córtex-m0).

Eu gosto de manter uma abordagem simples e estúpida, scripts mínimos de vinculador, código mínimo de inicialização etc. O trabalho é feito pelo código e não por qualquer cadeia de ferramentas específica.

A maioria das formas de gcc e binutils (capazes de usar o polegar) funcionará um pouco com esses exemplos, pois eu uso o compilador para compilar não como um recurso para chamadas de biblioteca, não uso scripts de vinculador de ações etc. O gcc e binutils mais antigos não sabem sobre as partes mais recentes do thumb2, portanto, algumas alterações podem ser necessárias.

Eu construo meu próprio gcc, binutils e llvm / clang, além de usar o codesourcery, por exemplo (agora mentor dos gráficos, mas você ainda pode obter a versão free / lite).

Esp Ao começar a montar um projeto para um novo alvo, você precisa desmontar. Em particular, para garantir que os itens estejam onde você deseja, a tabela de vetores, por exemplo.

Veja stm32f4d / blinker02 por exemplo. Começa com vectors.s, a tabela de exceção / vetor, além de algumas rotinas de suporte 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

Sem interrupções neste exemplo, mas as outras coisas que você precisa estão aqui.

O blinker02.c contém o corpo principal do código C com o ponto de entrada C que eu chamo de notmain () para evitar chamá-lo de principal (alguns compiladores adicionam lixo ao seu binário quando você tem um main ()).

você vai economizar um recorte e colar. o makefile conta a história sobre compilação e vinculação. Observe que vários dos meus exemplos compilam dois ou mais binários do mesmo código. compilador gcc, compilador clang do llvm, thumb only e thumb2, otimizações diferentes etc.

Comece criando arquivos de objeto a partir dos arquivos de origem.

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

o vinculador, ld, usa um script vinculador que eu chamo de memmap, que pode ser extremamente doloroso, às vezes por uma boa razão, às vezes não. Prefiro que menos seja mais abordagem para o tamanho único, tudo, menos a abordagem da pia da cozinha.

Eu não uso .data normalmente (bem quase nunca) e este exemplo não precisa .bss, então aqui está o script do vinculador, apenas o suficiente para colocar o programa (.text) onde ele precisa estar para esse processador do jeito que eu sou usando isso.

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

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

Eu tenho uma região de memória para definir isso, não há nada de especial no nome ram que você pode chamar de foo ou bar ou bob ou ted, não importa, apenas vincula os itens de memória às seções. As seções definem coisas como .text, .data, .bss, .rodata e para onde elas vão no mapa de memória.

quando você constrói isso, você vê que eu desmonte tudo (objdump -D) você vê isso

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}

O ponto principal a ser observado é que o endereço à esquerda é o local onde desejávamos, o código vectors.s é o primeiro no binário (como é o primeiro na linha de comando ld, a menos que você faça algo no script do vinculador, os itens serão mostrados no binário na ordem em que estão na linha de comando ld). Para inicializar corretamente, você deve garantir que sua tabela de vetores esteja no lugar certo. O primeiro item é o endereço da minha pilha, tudo bem. O segundo item é o endereço para _start e deve ser um número ímpar. o uso de .thumb_func antes de um rótulo faz com que isso aconteça, assim você não precisa fazer outras coisas feias.

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

08000056 <hang>:
 8000056:       e7fe          

portanto, 0x08000051 e 0x08000057 são as entradas de vetor adequadas para _start e travar. iniciar chamadas não principais ()

08000098 <notmain>:
 8000098:       b510            push    {

Isso parece bom (eles não mostram o endereço numerado ímpar na desmontagem).

Tudo está bem.

Vá para o exemplo blinker05, este suporta interrupções. e precisa de um pouco de ram, então .bss é definido.

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

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

lembre-se ram e rom são nomes arbitrários, bob e ted, foo e bar funcionam bem.

Não mostrará os vetores inteiros porque o córtex-m3 possui um zilhão de entradas na tabela de vetores se você fizer um completo (varia de núcleo para núcleo e talvez dentro do mesmo núcleo, dependendo das opções escolhidas pelo fornecedor do chip) As partes relevantes estão aqui após a desmontagem:

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}

é preciso algumas tentativas e erros para colocar o manipulador exatamente no lugar certo, verifique com o seu chip onde ele precisa estar, não necessariamente no mesmo local que este e, com tantas interrupções, você pode estar procurando uma interrupção diferente. os processadores córtex-m, diferentemente dos braços normais, fazem com que você não precise de códigos de trampolim para interrupções, eles preservam um certo número de registros e gerenciam a alternância de modos de processador através do conteúdo do registro de links. desde que o hardware e a abi do compilador estejam próximos o suficiente, tudo funcionará. Neste caso, eu fiz o manipulador em C, ao contrário de outras plataformas e no passado, você não precisa fazer nada de especial com o compilador / sintaxe, basta criar uma função (mas não faz coisas estúpidas na função / manipulador)

//-------------------------------------------------------------------
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
//-------------------------------------------------------------------

O makefile para blinker05 deve se parecer com o exemplo blinker02, principalmente recortar e colar para a maioria deles. transforme os arquivos de origem individuais em objetos e vincule-os. Eu construo para thumb, thumb2 usando gcc e clang. você pode alterar a linha all: no momento para incluir apenas os itens do gcc se não tiver / deseja clang (llvm) envolvido. Eu uso binutils para montar e vincular a saída clang btw.

Todos esses projetos usam ferramentas gratuitas, prontas para uso, de código aberto. sem IDE, apenas linha de comando. Sim, eu apenas mexo no Linux e não no Windows, mas essas ferramentas também estão disponíveis para os usuários do Windows, alteram coisas como rm -f algo para deletar algo no makefile, coisas assim ao criar no Windows. Isso ou execute o linux no vmware, no virtualbox ou no qemu. Não usar um IDE significa que você escolhe seu editor de texto também, não vou entrar nisso, tenho meus favoritos. Note que uma característica extremamente irritante do programa gnu make é que ele requer abas reais no makefile, eu odeio abas invisíveis com paixão. Então, um editor de texto para makefiles que sai de abas, outro para código fonte que cria espaços. Eu não sei sobre janelas,

Espero que isso ajude, não é o chip / placa exato, mas um córtex-m4 bem m4 não m3, perto o suficiente para esta discussão. veja o dir mbed ou stm32vld para um córtex-m3 real (não há diferenças suficientes do m4 para makefiles e código de inicialização, etc.), mas não é feito por st. Os núcleos do córtex-m3 devem ser os mesmos entre os fornecedores, o córtex-m3 e o córtex-m4 são ambos ARMv7m e estão mais próximos do que diferentes. O córtex-m0 é um ARMv6m, dificilmente tem instruções para o polegar2 o suficiente para se preocupar, os compiladores não o alcançaram; portanto, use apenas o polegar (finja que você está construindo para um ARMv4T (apenas polegar), se necessário). Meu simulador de polegar é apenas polegar, sem polegar2, pode ser útil para você também, acho que o fiz executar interrupções de alguma forma ou moda.


Eu estava lendo a resposta e acho que o autor dessa resposta seria VOCÊ. Suas respostas me ajudaram muito e me motivaram a mudar para processadores ARM do que ser um AVR e PIC Fanboy. Obrigado
MaNyYaCk

Você é bem-vindo ... Pay It Forward ...
old_timer

2

Você pode dar uma olhada neste site, onde ele tenta explicar o básico do vinculador e low_level_init no código.

Observe que a página está focada na descrição do problema, portanto o vetor nvic é mínimo.

Então você tem exemplos mais completos na "biblioteca de periféricos padrão STM32F2xx", basta olhar nas seções gcc (já que o Yagarto é baseado no gcc). E há um código de exemplo que o ajudará com uma configuração correta da nvic (tabela de vetores de interrupção).

Portanto, mesmo que essa não seja uma resposta completa, espero que seja útil.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.