- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在构建一个只有微不足道的微型微控制器,用于自学。这样,我可以刷新有关链接描述文件,启动代码等主题的知识。
编辑:
我有很多评论指出,下面显示的“绝对最小的STM32应用程序”是不好的。当注意到向量表不完整,.bss
部分未处理,外围地址不完整时,您是完全正确的,请允许我解释原因。
在此特定章节中编写完整而有用的应用程序从来不是作者的目的。他的目的是逐步解释链接描述文件的工作方式,启动代码的工作方式,STM32的启动过程是什么样的……纯粹出于教育目的。我很欣赏这种方法,并且学到了很多东西。
我在下面给出的示例取自相关章节的中间。本章将继续在链接器脚本和启动代码(例如.bss
-节的初始化)中添加更多部分。
之所以将文件放在他本章的中间,是因为我陷入了特定的错误消息中。我想先解决此问题,然后再继续。
有问题的章节在他书的结尾。它是为那些经验丰富或好奇的读者准备的,他们希望获得有关大多数人甚至不考虑的主题的更深入的知识(大多数人使用制造商提供的标准链接描述文件和启动代码,而不用阅读它们)。
请记住,请让我们专注于手头的技术问题(如以下错误消息中所述)。也请接受我没有早点澄清作者意图的真诚道歉。但是我现在已经完成了,所以我们可以继续;-)
1.绝对最小的STM32应用
我要学习的教程是本书的第20章:“精通STM32”(https://leanpub.com/mastering-stm32)。本书介绍了如何使用两个文件(main.c
和linkerscript.ld
)制作微型微控制器应用程序。由于我没有使用IDE(例如Eclipse),因此我还添加了build.bat
和clean.bat
来生成编译命令。所以我的项目文件夹如下所示:
在继续之前,我可能应该提供有关系统的更多详细信息:
作业系统:Windows 10,64-bit
微控制器:带有STM32F401RE微控制器的NUCLEO-F401RE板。
编译器:arm-none-eabi-gcc
版本6.3.1 20170620(发行版)[ARM /嵌入式6分支修订版249437]。
主文件如下所示:
/* ------------------------------------------------------------ */
/* Minimal application */
/* for NUCLEO-F401RE */
/* ------------------------------------------------------------ */
typedef unsigned long uint32_t;
/* Memory and peripheral start addresses (common to all STM32 MCUs) */
#define FLASH_BASE 0x08000000
#define SRAM_BASE 0x20000000
#define PERIPH_BASE 0x40000000
/* Work out end of RAM address as initial stack pointer
* (specific of a given STM32 MCU) */
#define SRAM_SIZE 96*1024 //STM32F401RE has 96 KB of RAM
#define SRAM_END (SRAM_BASE + SRAM_SIZE)
/* RCC peripheral addresses applicable to GPIOA
* (specific of a given STM32 MCU) */
#define RCC_BASE (PERIPH_BASE + 0x23800)
#define RCC_APB1ENR ((uint32_t*)(RCC_BASE + 0x30))
/* GPIOA peripheral addresses
* (specific of a given STM32 MCU) */
#define GPIOA_BASE (PERIPH_BASE + 0x20000)
#define GPIOA_MODER ((uint32_t*)(GPIOA_BASE + 0x00))
#define GPIOA_ODR ((uint32_t*)(GPIOA_BASE + 0x14))
/* Function headers */
int main(void);
void delay(uint32_t count);
/* Minimal vector table */
uint32_t *vector_table[] __attribute__((section(".isr_vector"))) = {
(uint32_t*)SRAM_END, // initial stack pointer (MSP)
(uint32_t*)main // main as Reset_Handler
};
/* Main function */
int main() {
/* Enable clock on GPIOA peripheral */
*RCC_APB1ENR = 0x1;
/* Configure the PA5 as output pull-up */
*GPIOA_MODER |= 0x400; // Sets MODER[11:10] = 0x1
while(1) { // Always true
*GPIOA_ODR = 0x20;
delay(200000);
*GPIOA_ODR = 0x0;
delay(200000);
}
}
void delay(uint32_t count) {
while(count--);
}
/* ------------------------------------------------------------ */
/* Linkerscript */
/* for NUCLEO-F401RE */
/* ------------------------------------------------------------ */
/* Memory layout for STM32F401RE */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
}
/* The ENTRY(..) directive overrides the default entry point symbol _start.
* Here we define the main-routine as the entry point.
* In fact, the ENTRY(..) directive is meaningless for embedded chips,
* but it is informative for debuggers. */
ENTRY(main)
SECTIONS
{
/* Program code into FLASH */
.text : ALIGN(4)
{
*(.isr_vector) /* Vector table */
*(.text) /* Program code */
*(.text*) /* Merge all .text.* sections inside the .text section */
KEEP(*(.isr_vector)) /* Don't allow other tools to strip this off */
} >FLASH
_sidata = LOADADDR(.data); /* Used by startup code to initialize data */
.data : ALIGN(4)
{
. = ALIGN(4);
_sdata = .; /* Create a global symbol at data start */
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .; /* Define a global symbol at data end */
} >SRAM AT >FLASH
}
build.bat
文件在main.c上调用编译器,然后在链接器上调用:
@echo off
setlocal EnableDelayedExpansion
echo.
echo ----------------------------------------------------------------
echo. )\ ***************************
echo. ( =_=_=_=^< ^| * build NUCLEO-F401RE *
echo. )( ***************************
echo. ""
echo.
echo.
echo. Call the compiler on main.c
echo.
@arm-none-eabi-gcc main.c -o main.o -c -MMD -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -g3 -Wall -fmessage-length=0 -Werror-implicit-function-declaration -Wno-comment -Wno-unused-function -ffunction-sections -fdata-sections
echo.
echo. Call the linker
echo.
@arm-none-eabi-gcc main.o -o myApp.elf -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -specs=nosys.specs -specs=nano.specs -T linkerscript.ld -Wl,-Map=output.map -Wl,--gc-sections
echo.
echo. Post build
echo.
@arm-none-eabi-objcopy -O binary myApp.elf myApp.bin
arm-none-eabi-size myApp.elf
echo.
echo ----------------------------------------------------------------
clean.bat
文件删除所有编译器输出:
@echo off
setlocal EnableDelayedExpansion
echo ----------------------------------------------------------------
echo. __ **************
echo. __\ \___ * clean *
echo. \ _ _ _ \ **************
echo. \_`_`_`_\
echo.
del /f /q main.o
del /f /q main.d
del /f /q myApp.bin
del /f /q myApp.elf
del /f /q output.map
echo ----------------------------------------------------------------
C:\Users\Kristof\myProject>build
----------------------------------------------------------------
)\ ***************************
( =_=_=_=< | * build NUCLEO-F401RE *
)( ***************************
""
Call the compiler on main.c
Call the linker
Post build
text data bss dec hex filename
112 0 0 112 70 myApp.elf
----------------------------------------------------------------
main.c
文件现在看起来像这样:
/* ------------------------------------------------------------ */
/* Minimal application */
/* for NUCLEO-F401RE */
/* ------------------------------------------------------------ */
typedef unsigned long uint32_t;
/* Memory and peripheral start addresses (common to all STM32 MCUs) */
#define FLASH_BASE 0x08000000
#define SRAM_BASE 0x20000000
#define PERIPH_BASE 0x40000000
/* Work out end of RAM address as initial stack pointer
* (specific of a given STM32 MCU) */
#define SRAM_SIZE 96*1024 //STM32F401RE has 96 KB of RAM
#define SRAM_END (SRAM_BASE + SRAM_SIZE)
/* RCC peripheral addresses applicable to GPIOA
* (specific of a given STM32 MCU) */
#define RCC_BASE (PERIPH_BASE + 0x23800)
#define RCC_APB1ENR ((uint32_t*)(RCC_BASE + 0x30))
/* GPIOA peripheral addresses
* (specific of a given STM32 MCU) */
#define GPIOA_BASE (PERIPH_BASE + 0x20000)
#define GPIOA_MODER ((uint32_t*)(GPIOA_BASE + 0x00))
#define GPIOA_ODR ((uint32_t*)(GPIOA_BASE + 0x14))
/* Function headers */
void __initialize_data(uint32_t*, uint32_t*, uint32_t*);
void _start (void);
int main(void);
void delay(uint32_t count);
/* Minimal vector table */
uint32_t *vector_table[] __attribute__((section(".isr_vector"))) = {
(uint32_t*)SRAM_END, // initial stack pointer (MSP)
(uint32_t*)_start // _start as Reset_Handler
};
/* Variables defined in linkerscript */
extern uint32_t _sidata;
extern uint32_t _sdata;
extern uint32_t _edata;
volatile uint32_t dataVar = 0x3f;
/* Data initialization */
inline void __initialize_data(uint32_t* flash_begin, uint32_t* data_begin, uint32_t* data_end) {
uint32_t *p = data_begin;
while(p < data_end)
*p++ = *flash_begin++;
}
/* Entry point */
void __attribute__((noreturn,weak)) _start (void) {
__initialize_data(&_sidata, &_sdata, &_edata);
main();
for(;;);
}
/* Main function */
int main() {
/* Enable clock on GPIOA peripheral */
*RCC_APB1ENR = 0x1;
/* Configure the PA5 as output pull-up */
*GPIOA_MODER |= 0x400; // Sets MODER[11:10] = 0x1
while(dataVar == 0x3f) { // Always true
*GPIOA_ODR = 0x20;
delay(200000);
*GPIOA_ODR = 0x0;
delay(200000);
}
}
void delay(uint32_t count) {
while(count--);
}
main(..)
函数上方添加了初始化代码。链接描述文件也进行了一些修改:
/* ------------------------------------------------------------ */
/* Linkerscript */
/* for NUCLEO-F401RE */
/* ------------------------------------------------------------ */
/* Memory layout for STM32F401RE */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
}
/* The ENTRY(..) directive overrides the default entry point symbol _start.
* In fact, the ENTRY(..) directive is meaningless for embedded chips,
* but it is informative for debuggers. */
ENTRY(_start)
SECTIONS
{
/* Program code into FLASH */
.text : ALIGN(4)
{
*(.isr_vector) /* Vector table */
*(.text) /* Program code */
*(.text*) /* Merge all .text.* sections inside the .text section */
KEEP(*(.isr_vector)) /* Don't allow other tools to strip this off */
} >FLASH
_sidata = LOADADDR(.data); /* Used by startup code to initialize data */
.data : ALIGN(4)
{
. = ALIGN(4);
_sdata = .; /* Create a global symbol at data start */
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .; /* Define a global symbol at data end */
} >SRAM AT >FLASH
}
main.c
到
main.o
的编译仍然可以。但是链接过程陷入困境:
C:\Users\Kristof\myProject>build
----------------------------------------------------------------
)\ ***************************
( =_=_=_=< | * build NUCLEO-F401RE *
)( ***************************
""
Call the compiler on main.c
Call the linker
c:/gnu_arm_embedded_toolchain/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m/fpv4-sp/hard/crt0.o: In function `_start':
(.text+0x64): undefined reference to `__bss_start__'
c:/gnu_arm_embedded_toolchain/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m/fpv4-sp/hard/crt0.o: In function `_start':
(.text+0x68): undefined reference to `__bss_end__'
collect2.exe: error: ld returned 1 exit status
Post build
arm-none-eabi-objcopy: 'myApp.elf': No such file
arm-none-eabi-size: 'myApp.elf': No such file
----------------------------------------------------------------
-nostdlib
和
-ffreestanding
添加到gcc和链接器。
build.bat
文件现在如下所示:
@echo off
setlocal EnableDelayedExpansion
echo.
echo ----------------------------------------------------------------
echo. )\ ***************************
echo. ( =_=_=_=^< ^| * build NUCLEO-F401RE *
echo. )( ***************************
echo. ""
echo.
echo.
echo. Call the compiler on main.c
echo.
@arm-none-eabi-gcc main.c -o main.o -c -MMD -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -g3 -Wall -fmessage-length=0 -Werror-implicit-function-declaration -Wno-comment -Wno-unused-function -ffunction-sections -fdata-sections -ffreestanding -nostdlib
echo.
echo. Call the linker
echo.
@arm-none-eabi-gcc main.o -o myApp.elf -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -specs=nosys.specs -specs=nano.specs -T linkerscript.ld -Wl,-Map=output.map -Wl,--gc-sections -ffreestanding -nostdlib
echo.
echo. Post build
echo.
@arm-none-eabi-objcopy -O binary myApp.elf myApp.bin
arm-none-eabi-size myApp.elf
echo.
echo ----------------------------------------------------------------
main.c
文件的一些有趣的评论。我已经应用了大多数:
volatile
关键字
RCC_AHB1ENR
而不是
RCC_APB1ENR
)。
HardFault_Handler
,
MemManage_Handler
...了,因为这只是出于教育目的的微小测试。
main.c
文件现在如下所示:
/* ------------------------------------------------------------ */
/* Minimal application */
/* for NUCLEO-F401RE */
/* ------------------------------------------------------------ */
typedef unsigned long uint32_t;
/**
\brief Data Synchronization Barrier
\details Acts as a special kind of Data Memory Barrier.
It completes when all explicit memory accesses before this instruction complete.
*/
__attribute__((always_inline)) static inline void __DSB(void)
{
__asm volatile ("dsb 0xF":::"memory");
}
/* Memory and peripheral start addresses (common to all STM32 MCUs) */
#define FLASH_BASE 0x08000000
#define SRAM_BASE 0x20000000
#define PERIPH_BASE 0x40000000
/* Work out end of RAM address as initial stack pointer
* (specific of a given STM32 MCU) */
#define SRAM_SIZE 96*1024 //STM32F401RE has 96 KB of RAM
#define SRAM_END (SRAM_BASE + SRAM_SIZE)
/* RCC peripheral addresses applicable to GPIOA
* (specific of a given STM32 MCU) */
#define RCC_BASE (PERIPH_BASE + 0x23800)
#define RCC_AHB1ENR ((volatile uint32_t*)(RCC_BASE + 0x30))
/* GPIOA peripheral addresses
* (specific of a given STM32 MCU) */
#define GPIOA_BASE (PERIPH_BASE + 0x20000)
#define GPIOA_MODER ((volatile uint32_t*)(GPIOA_BASE + 0x00))
#define GPIOA_ODR ((volatile uint32_t*)(GPIOA_BASE + 0x14))
/* Function headers */
void __initialize_data(uint32_t*, uint32_t*, uint32_t*);
void _start (void);
int main(void);
void delay(uint32_t count);
/* Minimal vector table */
uint32_t *vector_table[] __attribute__((section(".isr_vector"))) = {
(uint32_t*)SRAM_END, // initial stack pointer (MSP)
(uint32_t*)_start // _start as Reset_Handler
};
/* Variables defined in linkerscript */
extern uint32_t _sidata;
extern uint32_t _sdata;
extern uint32_t _edata;
volatile uint32_t dataVar = 0x3f;
/* Data initialization */
inline void __initialize_data(uint32_t* flash_begin, uint32_t* data_begin, uint32_t* data_end) {
uint32_t *p = data_begin;
while(p < data_end)
*p++ = *flash_begin++;
}
/* Entry point */
void __attribute__((noreturn,weak)) _start (void) {
__initialize_data(&_sidata, &_sdata, &_edata);
asm volatile("":::"memory"); // <- Did I put this instruction at the right spot?
main();
for(;;);
}
/* Main function */
int main() {
/* Enable clock on GPIOA peripheral */
*RCC_AHB1ENR = 0x1;
__DSB();
/* Configure the PA5 as output pull-up */
*GPIOA_MODER |= 0x400; // Sets MODER[11:10] = 0x1
while(dataVar == 0x3f) { // Always true
*GPIOA_ODR = 0x20;
delay(200000);
*GPIOA_ODR = 0x0;
delay(200000);
}
}
void delay(uint32_t count) {
while(count--){
asm volatile("");
}
}
最佳答案
您可以告诉gcc
不要使用该库。
编译器
默认情况下,gcc假定您使用的是标准C库,并且可以发出调用某些函数的代码。例如,启用优化后,它会检测到复制内存的循环,并可以用对memcpy()
的调用来代替它们。用-ffreestanding
禁用它。
链接器
链接器还假定您要链接程序与C库和启动代码。库启动代码负责初始化库和程序执行环境。它具有一个名为_start()
的函数,必须在重置后调用它。其功能之一是用零填充.bss
段(见下文)。如果未定义分隔.bss
的符号,则无法链接_startup()
。如果您将启动函数命名为_startup()
以外的任何其他名称,则链接器会将库启动作为未使用的函数悄无声息地删除,并且可以链接代码。
您可以告诉链接器不要将任何标准库或启动代码与-nostdlib
链接,那么库提供的启动函数名称将不会与您的库冲突,并且每次意外调用库函数时,您都会收到链接器错误。
缺少volatile
您的寄存器定义缺少volatile
限定符。没有它,随后对*GPIOA_ODR
的写入将被优化。编译器会将这个“不变代码”移出循环。将寄存器定义中的类型更改为(volatile uint32_t*)
将解决此问题。
空循环
优化器可以识别出延迟循环没有任何作用,并完全消除了延迟循环以加快执行速度。在延迟循环中添加一个空但不可移动的asm volatile("");
指令。
缺少内存屏障
您正在初始化在C函数中保存.data
的dataVar
部分。 *p
中的__initialize_data()
实际上是dataVar
的别名,并且编译器无法知道它。从理论上讲,优化器可以在dataVar
之前重新安排__initialize_data()
的测试。即使dataVar
是volatile
,也不是*p
,因此不能保证排序。
数据初始化循环之后,应告诉编译器程序变量是由编译器未知的机制更改的:
asm volatile("":::"memory");
__DSB();
*RCC_APB1ENR = 0x1;
之后(应称为其他名称)
GPIOA
中启用
RCC
的地址似乎正确,但该寄存器在文档中称为
RCC_AHB1ENR
。这会使试图理解您的代码的人感到困惑。
__attribute__ ((section(".isr_vector"),used))
void (* const _vectors[]) (void) = {
(void (*const)(void))(&__stack),
Reset_Handler,
NMI_Handler,
HardFault_Handler,
MemManage_Handler,
BusFault_Handler,
UsageFault_Handler
}
.rodata
:只读数据,
const
数组和结构在此处。它们保持闪烁状态。 (简单的
const
变量通常放在代码中)
.data
:初始化变量,您声明的所有带有
=
符号且没有
const
的变量。
.bss
:应在C中进行零初始化的变量,即全局变量和
static
变量。
.rodata
或
.bss
,所以很好。
关于gcc - 真正最小的STM32应用:链接器故障,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49885034/
缓冲区溢出问题是众所周知的。因此,我们有幸使用标准库函数,例如 wcscat_s()。 Microsoft 的好心人已经创建了类似的安全字符串函数,例如 StringCbCat()。 但是我遇到了一个
HTTP缓存相关的问题好像是前端面试中比较常见的问题了,上来就会问什么cache-control字段有哪些,有啥区别啥的。嗯……说实话,我觉得至少在本篇来说,HTTP缓存还算不上复杂,只是字段稍
代理,其实全称应该叫做代理服务器,它是客户端与服务器之间得中间层,本质上来说代理就是一个服务器,在HTTP的链路中插入的一个中间环节,就是代理服务器啦。所谓的代理服务就是指:服务本身不生产内容,
我们在前两篇的内容中分别学习了缓存和代理,大致了解了缓存有哪些头字段,代理是如何服务于服务器和客户端的,那么把两者结合起来,代理缓存,也就是说代理服务器也可以缓存,当客户端请求数据的时候,未必一
在前面的章节,我们把HTTP/1.1的大部分核心内容都过了一遍,并且给出了基于Node环境的一部分示例代码,想必大家对HTTP/1.1已经不再陌生,那么HTTP/1.1的学习基本上就结束了。这两
我们前一篇学习了HTTP/2,相比于HTTP/1,HTTP/2在性能上有了大幅的改进,但是HTTP/2因为底层还是基于TCP协议的,虽然HTTP/2在应用层引入了流的概念,利用多路复用解决了队头
前面我们花了很大的篇幅来讲HTTP在性能上的改进,从1.0到1.1,再到2.0、3.0,HTTP通过替换底层协议,解决了一直阻塞性能提升的队头阻塞问题,在性能上达到了极致。 那么,接下
上一篇噢,我们搞明白了什么是安全的通信,这个很重要,特别重要,敲黑板!! 然后,我们还学了HTTPS到底是什么,以及HTTPS真正的核心SSL/TLS是什么。最后我们还聊了聊TLS的实
经过前两章的学习,我们知道了通信安全的定义以及TLS对其的实现~有了这些知识作为基础,我们现在可以正式的开始研究HTTPS和TLS协议了。嗯……现在才真正开始。 我记得之前大概聊过,当
这一篇文章,我们核心要聊的事情就是HTTP的对头阻塞问题,因为HTTP的核心改进其实就是在解决HTTP的队头阻塞。所以,我们会讲的理论多一些,而实践其实很少,要学习的头字段也只有一个,我会在最开始
我们在之前的文章中介绍HTTP特性的时候聊过,HTTP是无状态的,每次聊起HTTP特性的时候,我都会回忆一下从前辉煌的日子,也就是互联网变革的初期,那时候其实HTTP不需要有状态,就是个浏览页面
前面几篇文章,我从纵向的空间到横向的时间,再到一个具体的小栗子,可以说是全方位,无死角的覆盖了HTTP的大部分基本框架,但是我聊的都太宽泛了,很多内容都是一笔带过,再加上一句后面再说就草草结束了。
我的问题确实很简单,是否应该对适配器(设计模式)类进行单元测试,以及如何进行测试? 例子: 我想用PHP创建一个ClientSocket类,它是fsockopen,fread,fwrite的适配器。
目前,我在 PHP 脚本中使用此查询: SELECT * FROM `ebooks` WHERE `id`!=$ebook[id] ORDER BY RAND() LIMIT 125; 数据库最多大约
我们都知道可以使用 GetCustomAttributes 方法查询程序集的属性。我想用它来识别我的应用程序的扩展模块。但是,为了避免加载每个程序集,我更喜欢防御性方法: 使用 Assembly.Re
我正在移植一个非常大的代码库,我在处理旧代码时遇到了更多困难。 例如,这会导致编译器错误: inline CP_M_ReferenceCounted * FrAssignRef(CP_M_Refere
[关于此主题还有其他类似的问题,但是它们都没有回答我在这里提出的问题,即AFAICT。 (即,我已经阅读了所有答案,解释了为什么特定构造无法与发问者尝试进行的操作,在某些情况下,它们提供了获得所需结果
嗨 我想为需要全屏运行的网络艺术应用程序构建一个控制面板,因此所有控制颜色和速度值等内容的面板都必须位于不同的窗口中。 我的想法是建立一个数据库来存储所有这些值,当我在控制面板窗口中进行更改时,应用程
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
假设我想实现一个分布式数据库(每个节点都是其他节点的副本);我听说 cdb 能够轻松地在两个节点之间进行同步,并且至少支持某种形式的冲突解决。 不幸的是我不知道 couchdb 因此我不得不问:节点“
我是一名优秀的程序员,十分优秀!