3

I am dealing with a new microcontroller LPC4088 from NXP. I needed 2 weeks to study and write a working examples for peripherals: IOCONFIG, GPIO, TIMERS, PWM and ADC. Please take a look at my repositories here. This is how you will get a feeling for how I work and what my skill level is.

Until now I could simply disable interrupts and work without them. Now I want to deal with UART peripheral device which needs interrupts. I have never programmed interupts but know something about ARM interrupts. Sadly just in theory. Currently I am studying these two documents:

It became clear to me that I need to study ARM Cortex-M4 microprocessor besides the LPC4088 microcontroller which I got the hang of somehow. I know that I should put ARM exception vectors at the beginning of the program - usually in the startup code. But I don't know how to do this because what I got with the microcontroller is already compiled startup code (object file) which presumably defines exception vectors, reset handler which sets stacks for C and then jumps to function main() in C source code written by user.

After compilation of my programs using GCC ARM compiler I allways get this prompt, which must allso be the clue which I don't understand because of my inexperience with ARM mcpu's directly:

*****
***** You must modify vector checksum value in *.bin and *.hex files.
*****

I was thinking of reverse ingeneering the startup code using the Segger Jlink and fixing the exception vectors there, but there must be any other way besides writing my own open source startup code... So do you have any suggestions or examples which would be even better for me.


ADD: I really looked hard and got no source code for the startup code. This is what I got:

enter image description here

The only way to somehow manipulate vectors therefore must be hidden inside the linker script, which is the only part that is still a source code and it looks like this:

/* Linker script for mbed LPC1768 */

/* Linker script to configure memory regions. */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K
  RAM (rwx) : ORIGIN = 0x100000E8, LENGTH = (64K - 0xE8)

  USB_RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 16K
  ETH_RAM(rwx) : ORIGIN = 0x20004000, LENGTH = 16K
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory regions FLASH and RAM.
 * It references following symbols, which must be defined in code:
 *   Reset_Handler : Entry of reset handler
 * 
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 */
ENTRY(Reset_Handler)

SECTIONS
{
    .text :
    {
        KEEP(*(.isr_vector))
        *(.text*)

        KEEP(*(.init))
        KEEP(*(.fini))

        /* .ctors */
        *crtbegin.o(.ctors)
        *crtbegin?.o(.ctors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
        *(SORT(.ctors.*))
        *(.ctors)

        /* .dtors */
        *crtbegin.o(.dtors)
        *crtbegin?.o(.dtors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
        *(SORT(.dtors.*))
        *(.dtors)

        *(.rodata*)

        KEEP(*(.eh_frame*))
    } > FLASH

    .ARM.extab : 
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > FLASH

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > FLASH
    __exidx_end = .;

    __etext = .;

    .data : AT (__etext)
    {
        __data_start__ = .;
        Image$$RW_IRAM1$$Base = .;
        *(vtable)
        *(.data*)

        . = ALIGN(4);
        /* preinit data */
        PROVIDE (__preinit_array_start = .);
        KEEP(*(.preinit_array))
        PROVIDE (__preinit_array_end = .);

        . = ALIGN(4);
        /* init data */
        PROVIDE (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE (__init_array_end = .);


        . = ALIGN(4);
        /* finit data */
        PROVIDE (__fini_array_start = .);
        KEEP(*(SORT(.fini_array.*)))
        KEEP(*(.fini_array))
        PROVIDE (__fini_array_end = .);

        . = ALIGN(4);
        /* All data end */
        __data_end__ = .;

    } > RAM


    .bss :
    {
        __bss_start__ = .;
        *(.bss*)
        *(COMMON)
        __bss_end__ = .;
        Image$$RW_IRAM1$$ZI$$Limit = . ;
    } > RAM


    .heap :
    {
        __end__ = .;
        end = __end__;
        *(.heap*)
        __HeapLimit = .;
    } > RAM

    /* .stack_dummy section doesn't contains any symbols. It is only
     * used for linker to calculate size of stack sections, and assign
     * values to stack symbols later */
    .stack_dummy :
    {
        *(.stack)
    } > RAM

    /* Set stack top to end of RAM, and stack limit move down by
     * size of stack_dummy section */
    __StackTop = ORIGIN(RAM) + LENGTH(RAM);
    __StackLimit = __StackTop - SIZEOF(.stack_dummy);
    PROVIDE(__stack = __StackTop);

    /* Check if data + heap + stack exceeds RAM limit */
    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")


    /* Code can explicitly ask for data to be 
       placed in these higher RAM banks where
       they will be left uninitialized. 
    */
    .AHBSRAM0 (NOLOAD):
    {
        Image$$RW_IRAM2$$Base = . ;
        *(AHBSRAM0)
        Image$$RW_IRAM2$$ZI$$Limit = .;
    } > USB_RAM

    .AHBSRAM1 (NOLOAD):
    {
        Image$$RW_IRAM3$$Base = . ;
        *(AHBSRAM1)
        Image$$RW_IRAM3$$ZI$$Limit = .;
    } > ETH_RAM
}

There is allso a makefile which looks like this and is contributing the prompt that I get at the end of every compilation:

# This file was automagically generated by mbed.org. For more information, 
# see http://mbed.org/handbook/Exporting-to-GCC-ARM-Embedded

GCC_BIN = 
PROJECT = executaable
OBJECTS = ./main.o 
SYS_OBJECTS = ./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM/startup_LPC408x.o ./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM/retarget.o ./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM/system_LPC407x_8x_177x_8x.o ./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM/board.o ./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM/cmsis_nvic.o 
INCLUDE_PATHS = -I. -I./mbed -I./mbed/TARGET_LPC4088 -I./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM -I./mbed/TARGET_LPC4088/TARGET_NXP -I./mbed/TARGET_LPC4088/TARGET_NXP/TARGET_LPC408X -I./mbed/TARGET_LPC4088/TARGET_NXP/TARGET_LPC408X/TARGET_LPC4088 
LIBRARY_PATHS = -L./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM 
LIBRARIES = -lmbed 
LINKER_SCRIPT = ./mbed/TARGET_LPC4088/TOOLCHAIN_GCC_ARM/LPC4088.ld

############################################################################### 
AS      = $(GCC_BIN)arm-none-eabi-as
CC      = $(GCC_BIN)arm-none-eabi-gcc
CPP     = $(GCC_BIN)arm-none-eabi-g++
LD      = $(GCC_BIN)arm-none-eabi-gcc
OBJCOPY = $(GCC_BIN)arm-none-eabi-objcopy
OBJDUMP = $(GCC_BIN)arm-none-eabi-objdump
SIZE    = $(GCC_BIN)arm-none-eabi-size

CPU = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
CC_FLAGS = $(CPU) -c -g -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer
CC_FLAGS += -MMD -MP
CC_SYMBOLS = -DTARGET_LPC4088 -DTARGET_M4 -DTARGET_CORTEX_M -DTARGET_NXP -DTARGET_LPC408X -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M4 -DARM_MATH_CM4 -D__FPU_PRESENT=1 -DMBED_BUILD_TIMESTAMP=1429428454.91 -D__MBED__=1 

LD_FLAGS = $(CPU) -Wl,--gc-sections --specs=nano.specs -u _printf_float -u _scanf_float -Wl,--wrap,main
LD_FLAGS += -Wl,-Map=$(PROJECT).map,--cref
LD_SYS_LIBS = -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys

ifeq ($(DEBUG), 1)
  CC_FLAGS += -DDEBUG -O0
else
  CC_FLAGS += -DNDEBUG -Os
endif

all: $(PROJECT).bin $(PROJECT).hex 

clean:
    rm -f $(PROJECT).bin $(PROJECT).elf $(PROJECT).hex $(PROJECT).map $(PROJECT).lst $(OBJECTS) $(DEPS)

.s.o:
    $(AS) $(CPU) -o $@ $<

.c.o:
    $(CC)  $(CC_FLAGS) $(CC_SYMBOLS) -std=gnu99   $(INCLUDE_PATHS) -o $@ $<

.cpp.o:
    $(CPP) $(CC_FLAGS) $(CC_SYMBOLS) -std=gnu++98 -fno-rtti $(INCLUDE_PATHS) -o $@ $<


$(PROJECT).elf: $(OBJECTS) $(SYS_OBJECTS)
    $(LD) $(LD_FLAGS) -T$(LINKER_SCRIPT) $(LIBRARY_PATHS) -o $@ $^ $(LIBRARIES) $(LD_SYS_LIBS) $(LIBRARIES) $(LD_SYS_LIBS)
    @echo ""
    @echo "*****"
    @echo "***** You must modify vector checksum value in *.bin and *.hex files."
    @echo "*****"
    @echo ""
    $(SIZE) $@

$(PROJECT).bin: $(PROJECT).elf
    @$(OBJCOPY) -O binary $< $@

$(PROJECT).hex: $(PROJECT).elf
    @$(OBJCOPY) -O ihex $< $@

$(PROJECT).lst: $(PROJECT).elf
    @$(OBJDUMP) -Sdh $< > $@

lst: $(PROJECT).lst

size:
    $(SIZE) $(PROJECT).elf

DEPS = $(OBJECTS:.o=.d) $(SYS_OBJECTS:.o=.d)
-include $(DEPS)
4

2 回答 2

4

好的,花了我几分钟。签出此zip中的项目之一。有各种启动代码。顺便说一句:编写自己的并不那么复杂。大多数时候,无论如何都必须为“真实”项目修改它。

压缩包来自这个页面。第二个 zip 可能包含 liker 文件,但可能不适用于 gcc(“keil”可能是一个好的开始)。但是你已经有一个开始了。

我刚刚看了 periph_blinky。请注意,启动始终必须与链接描述文件相对应,因为有一些特殊部分。对于阅读,我建议查看 binutils 文档,当然还有 gcc 文档。

正如我在 CMSIS 函数和带有 MCU 定义的标头的评论中所述,还应该有一些库。CMSIS 的东西也可以从 ARM 获取,但可能需要一些调整才能适应实际的实现(MPU 区域的数量等)。

哦,我建议不要将供应商库用于外围设备访问。它们可能被称为“标准”,但实际上并非如此,但大多数情况下包括大量膨胀,例如运行时初始化(对每个成员使用单独的写入!),这些结构永远不会改变。不确定 NXP,但 STM 提供了我见过的最糟糕的“std”库之一。

于 2015-05-03T15:46:05.580 回答
2

快速扫了一眼问题和答案。首先,为什么你认为你需要中断 uart?到目前为止,我从未遇到过这样一个需要的野兽,也许你有一个想要的用例,但需要?

我有很多例子,全是裸机,没有 hal 或标准库等。在 github 上搜索 thumbulator,然后从那里横着看几个。我很少使用中断,但为了举例,我可能在某处做了一些。

正如评论中提到的,arm docs,只是尝试一下,你会发现对于 cortex-m,堆栈指针可以由硬件根据向量表中的第一个条目设置,从那里你不必搞砸它用于中断或异常。这不是一个全尺寸的手臂如何与它的许多堆栈一起工作,所有这些都必须设置。

如果您的编译器符合 (E)ABI,则 cortex-m 可以让您使用 C 函数的地址填充向量表。使用 gcc 会的。可能需要一些组装,但不像您在其他地方处理的那么多。

arm 制造核心而不是芯片,因此 arm 文档只能让您了解核心的边缘,其余部分来自芯片供应商,并且可能会有很大差异,例如如何启用和清除中断。

于 2015-05-04T01:55:08.377 回答