说了那么多,我们终于开始写代码了.毫不例外,我们从世界上最著名的程序"helloword"开始,硬件的"helloword"就是点亮一个LED.
硬件原理图:
从原理图看出,LED (D1)连接电源和地,上电就亮. LED D2连接VCC和PC13,所以我们只要给PC13引脚一个低电平就可以点亮这个LED.
准备点灯
俗话说,让我点亮一个LED我就能上天. 看把你能的,LED 可不是那么好点的,要准备准备.
如果你学过51,你会告诉我点灯很容易.
如果你学过STM32,也许你会告诉我很容易.(为什么我说也许?因为我知道你心虚,你用的是别人的库吧?你有很多不明白的地方吧.哈哈哈)
今天我们就从头来,明明白白的点一回灯.
其实这个STM32点灯的却不是那么容易,因为在真实点灯之前有很多配置,往往这些配置是最让人头疼的.
这也是用库编程的问题所在:虽然你用了库点亮了LED灯,干了很多别的事情,但是有些事情还是模模糊糊不清楚.
扯远了,其实那些烦人的配置是用来配置时钟的,STM32这样设计也是有原因的:默认所有外设时钟关闭,使用的时候再开启,这样可以省电啊.有地方省,就有地方多付出,比如芯片设计人员和软件开发人员--我们.幸好STM32有默认内部时钟,虽然不咋地,但是能用,我们先凑合着用内部时钟点亮我们的LED.
点灯
先使能portC的时钟(我们要用PC13),然后将PC13设置为输出模式.
while(1)循环设置PC13高低电平,就可以看见LED灯一闪一闪的了
/* main.c */
#define RCC_BASE 0x40021000
#define RCC_CR (*(volatile unsigned long *)(RCC_BASE))
#define RCC_APB2ENR (*(volatile unsigned long *)(RCC_BASE+0x18))
#define GPIOC_BASE 0x40011000
#define GPIOC_CRH (*(volatile unsigned long *)(GPIOC_BASE+0x04))
#define GPIOC_ODR (*(volatile unsigned long *)(GPIOC_BASE+0x0c))
int main(){
int i;
RCC_APB2ENR = 1<<4; //portC 时钟使能
GPIOC_CRH = 3<<20; // pin13 输出模式
GPIOC_ODR = 0;
while(1){
GPIOC_ODR |= 1<<13; //pin13 高电平
for(i=0;i<1000000;i++);
GPIOC_ODR &= ~(1<<13); //pin13 低电平
for(i=0;i<1000000;i++);
}
return 0;
}
好像本章应该结束了?可是想想还有很多疑问:为什么从mian()函数开始执行?怎么编译成APP.bin?
为什么从mian()函数开始执行?
很多写应用的人往往会忽略忽这点,因为平时的应用都是从main开始的所以就理所当然的认为STM32也认识main().
要回答这个问题,这就要说说cortexM3的启动流程(复位流程).
如上图,CPU一上电就从0x00000000地址处取一个值赋给MSP(R13)寄存器.然后从接下来的一个32bit对齐的地址0x00000004处读取第一条要执行的指令执行.
那么我们的程序怎么保证0x00000004处就是main()函数呢?
/* start.S */
.text
.globl _start
_start:
.word 0x20000000+20480 /*sram form 0x20000000,size 20K*/
.word main /*跳转到main函数*/
从_start开始,第一个 .word 后面就是放入MSP中的值,因为.word占用4个字节所有.word main 就0x00000004处了.
这就保证了程序一上电就执行main()函数.
其实,入口也不一定非要是main()函数,你写exit()也是可以的,只要你愿意,main只是约定俗成.
怎么编译?
//将start.S编译为start.o 但是不链接
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c start.S -o start.o
//将main.c编译为main.o 但是不链接
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c main.c -o main.o
//将start.o 和start.o 链接成nuttx. 第一个字节放在0x00000000
//这样main就在0x00000004
arm-none-eabi-ld -Ttext=0x00000000 start.o main.o -o nuttx
//最后转换格式,确保CPU可以正常运行
arm-none-eabi-objcopy -O binary -S nuttx nuttx.bin
上面解释的很详细了,就不再多说,可是每次编译都敲这么多命令也太麻烦了,干脆写一个脚本(针对make的Makefle)
# file :Makefile
All:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c start.S -o start.o
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c main.c -o main.o
arm-none-eabi-ld -Ttext=0x08000000 start.o main.o -o nuttx
arm-none-eabi-objcopy -O binary -S nuttx nuttx.bin
arm-none-eabi-objdump -D -m arm nuttx > nuttx.dis
clean:
-rm *.o nuttx*
flash:
sudo stm32flash -w nuttx.bin -v -g 0 /dev/ttyUSB0
这样创建led目录,创建3个文件
main.c Makefile start.S 内容如上文.
这样以后编译烧写就方便多了.
//编译
PC#make
//清除编译产生的文件
PC#make clean
//烧写到STM32
PC#make flash
576648661@qq.com
- 微信号:find_xiaohei
- 名称:找小黑