本节有两个目标:
- 板子启动
- 基本驱动,printf重定向
1. 板子启动
一般来讲,MCU启动之后运行的第一段程序叫做BootRom,是厂商固化在芯片内部的,主要功能是检测启动方式和做一些必要的准备工作。大多数MCU都支持从多种存储器中启动,如NorFlash、NandFlash、EMMC等。对于STM32F1来说,可以选择3种存储器作为启动源分别是:flash,系统存储,片上RAM。flash的大小是512KB;片上RAM的大小是64KB;系统存储区存储内嵌的自举程序,由ST在生产线上写入,用于通过可用的串口对flash进行编程。我们的代码最终要放在flash上运行,但是前期调试阶段可以先放在RAM上跑,这样下载比较快速,方便调试,在RAM上跑的方法可以参考我写的分散加载相关的文章。
BootROM之后就是编程人员可以控制的部分了,程序会从地址0开始执行,但并不是执行0地址处的指令,而是取出0地址处的值赋给SP,然后取4地址处的值(其实是复位处理函数的地址)赋给PC,接着就会运行复位处理函数进行系统初始化,调用main()等一系列操作。4地址往后是一系列的异常服务函数的地址。这点跟以前的ARM7系列有所不同,ARM7系列的开头虽然也是中断向量表,但是每个地址存放的都是一条跳转指令,而Cortex-M系列存放的是服务函数的地址。以上功能一般以汇编语言编写,存放在一个单独的文件中,称为启动文件。ST官方已经准备好了一个启动文件的模板供我们使用,即startup_stm32f10x_hd.s,我们目前只需要修改一个地方,就是屏蔽SystemInit函数,这个函数是用来初始化时钟的,我们把这个步骤放到main()函数里去做。 完整的启动文件代码如下:
1 |
|
接下来把ARM和ST官方提供的寄存器定义等文件放到hyyos/Arch/stm32f10x/目录中,此时目录中内容如下:
1 | |-stm32f10x # STM32F10X系列相关的代码 |
接下本来应该是helloworld亮相了,我们计划把它输出到USART1上面,通过PC上的串口调试助手进行显示和交互。所以先要把USART1读写功能做出来,而USART1是挂在APB2上面的,所以要先配置一下对应的时钟。我们在hw目录下新增两个文件USART1.c和sys.c,分别用来放USART1相关和时钟初始化代码。然后依次编写实现以下功能的代码:
- 配置系统时钟。参考我的往期文章《STM32F1学习(一)时钟》
- USART1端口配置,中断配置。参考我的往期文章《STM32F1学习(三)USART》
- 编写USART1接收中断服务函数
USART1_IRQHandler此函数名在启动文件中定义。这里我们使用了一个256字节大小的接收缓冲,由于只是用来跟PC通信,因此通信协议非常简单,把\r\n作为每次接收结束标志。 - 编写USART1发送函数
int Usart1SendByte(int data) - 重定向
printf到USART1。printf调用了C库中的fputc()函数进行输出,因此我们只需要定义一下fputc()即可。在sys.c中定义int fputc (int c, File *fp)并在其中调用Usart1SendByte()函数实现串口输出
hw/sys.c文件如下:
1 | // 用于printf重定向 |
hw/usart1.c文件如下:
1 | #define USART1_RCV_BUF_MAX 256 |
至此,准备工作终于做完了,helloworld亮相!
1 | // main.c |
