尝试了解官方的工程架构,打开Keil,Target配置如下:
我们使用的SWM181CBT6,拥有16KB SRAM,240KB Flash,所以我们可以修改一下。物尽其用。
查看数据手册上面的存储器映射表:
填写Keil的配置(用于自动生成链接脚本)。
IROM 0X0000 0000, 0X0003 C000 ; IRAM 0X 2000 0000,0x0000 4000
上图:
这样就能用上240KB的Flash,16KB SRAM了。
观察startup_xxx.s文件(启动文件),里面做了什么事情呢?
---------------------------------------------------------------------------------------------
(1)开辟栈,并使用标号__initial_sp获得栈顶。
Stack_Size EQU 0x800;
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
---------------------------------------------------------------------------------------------
(2)开辟堆,并使用__heap_limit获取堆的界限。
Heap_Size EQU 0x000;
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
此处明显堆的大小为0,这样的话,按道理来说是无法使用C库里面的malloc()函数分配内存的。
---------------------------------------------------------------------------------------------
(3)异常向量指定:RESET段。
AREA RESET, DATA, READONLY
__Vectors DCD Stack_Mem + Stack_Size ; Top of Stack 栈顶,等于__initial_sp
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD SRAM_SWITCH
DCD SVC_Handler ; SVCall Handler
DCD 0
DCD 0
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD IRQ0_Handler
DCD IRQ1_Handler
DCD IRQ2_Handler
DCD IRQ3_Handler
DCD IRQ4_Handler
DCD IRQ5_Handler
DCD IRQ6_Handler
DCD IRQ7_Handler
DCD IRQ8_Handler
DCD IRQ9_Handler
DCD IRQ10_Handler
DCD IRQ11_Handler
DCD IRQ12_Handler
DCD IRQ13_Handler
DCD IRQ14_Handler
DCD IRQ15_Handler
DCD IRQ16_Handler
DCD IRQ17_Handler
DCD IRQ18_Handler
DCD IRQ19_Handler
DCD IRQ20_Handler
DCD IRQ21_Handler
DCD IRQ22_Handler
DCD IRQ23_Handler
DCD IRQ24_Handler
DCD IRQ25_Handler
DCD IRQ26_Handler
DCD IRQ27_Handler
DCD IRQ28_Handler
DCD IRQ29_Handler
DCD IRQ30_Handler
DCD IRQ31_Handler
__Vectors_End
---------------------------------------------------------------------------------------------
为什么这样设定?
查看sct脚本:工程目录下的输入目录里面存在一个自动生成的KeyLED.sct链接脚本。
脚本的数据来源于,Target设置,作用:指定各段存放的位置。
LR_IROM1 0x00000000 0x00006000 { ; load region size_region
ER_IROM1 0x00000000 0x00006000 { ; 芯片Flash映射在0地址
*.o (RESET, +First) ;指定RESET段链接到头部
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00002000 { ; RW data
.ANY (+RW +ZI) ;变量,堆栈等链接到此处。
}
}
因为芯片内存过小,而且这个芯片不适合扩展内存等操作,在内存管理方面便不多做介绍。
CPU上电后,芯片内部自动读取存放在Flash 0地址上面的栈顶地址进而设置堆栈指针。
CPU 内部电压稳定后,发生reset异常,自动跳转到异常的处理函数中。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
LDR R0, =__main
BX R0
ENDP
可用看到,这里只有一种操作,那就是跳转到__main()函数里面执行了。
__main()和Main() (以前的ARM9使用ADS编写程序用到)是存在差异的,有兴趣自行百度进行科普。
------------------------------------------------------------------------------------------------------
在main()里面,首先便调用以下函数,设置芯片工作频率。
void SystemInit(void)
{
uint32_t i;
SYS->CLKEN |= (1 << SYS_CLKEN_OSC_Pos);
switch(SYS_CLK)
{
case SYS_CLK_24MHz: //0 内部高频24MHz RC振荡器
if(SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC,修改高频RC时钟频率时需要先切到一个稳定时钟源
{
switchToRC32KHz();
}
switchToRC24MHz();
break;
case SYS_CLK_6MHz: //1 内部高频 6MHz RC振荡器
if(SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC,修改高频RC时钟频率时需要先切到一个稳定时钟源
{
switchToRC32KHz();
}
switchToRC6MHz();
break;
case SYS_CLK_48MHz: //2 内部高频48MHz RC振荡器
if(SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC,修改高频RC时钟频率时需要先切到一个稳定时钟源
{
switchToRC32KHz();
}
switchToRC48MHz();
break;
case SYS_CLK_12MHz: //3 内部高频12MHz RC振荡器
if(SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC,修改高频RC时钟频率时需要先切到一个稳定时钟源
{
switchToRC32KHz();
}
switchToRC12MHz();
break;
case SYS_CLK_32KHz: //4 内部低频32KHz RC振荡器
if((SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) == 0)
{
switchToRC24MHz();
}
switchToRC32KHz();
break;
case SYS_CLK_XTAL: //5 外部XTAL晶体振荡器(2-30MHz)
if((SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) == 0)
{
switchToRC24MHz();
}
switchToXTAL();
break;
}
for(i = 0;i <10000;i++); //等待时钟稳定。。。
SystemCoreClockUpdate();
}
以上代码中,存在一条神奇的语句,可能要耗费CPU一点时间。
for(i = 0;i <10000;i++); //等待时钟稳定。。。
MCU IO引脚上电后会存在默认高电平/低电平等情况,如果板子设计时没有考虑到这个原因导致的影响,可用在这行语句之前指定引脚状态,减少影响。
--------------------------------------------------------------------------------------------------
展开宏:#define SYS_CLK SYS_CLK_24MHz
可用看到,官方例程里面,MCU默认工作于24MHz。
理论上,可用修改该宏定义为SYS_CLK_48MHz,将MCU工作频率设置为48MHz。但是不知道是否稳定,目前没有尝试。
#define SYS_CLK_24MHz 0 //0 内部高频24MHz RC振荡器
#define SYS_CLK_6MHz 1 //1 内部高频 6MHz RC振荡器
#define SYS_CLK_48MHz 2 //2 内部高频48MHz RC振荡器
#define SYS_CLK_12MHz 3 //3 内部高频12MHz RC振荡器
#define SYS_CLK_32KHz 4 //4 内部低频32KHz RC振荡器
#define SYS_CLK_XTAL 5 //5 外部XTAL晶体振荡器(2-30MHz)