Author:onceday Date:2022年9月3日
也信美人终做土,不堪幽梦太匆匆!
前言: 本文章基于PikaScript脚本语言在阿波罗Stm32F429开发板的实践记录。PikaScript是跨平台的超轻量级嵌入式 Python 引擎,零依赖,零配置,中文资料非常丰富。可获取关于它们的详细资料:
上次说到,PikaScript上面缺少必要的BSP包,所需暂时无法使用PikaScript,所以我们需要编一个最简单的BSP包。
对于Stm32F429来说,其基于Cortex-M4处理器,属于ARMv7-M架构,支持Thumb-2指令集,能同时在16位和32指令下工作,如何理解呢?
简单来说,16位指令可以提高代码密度,即二进制文件更小,比如立即数数据只存2个字节。
32指令执行更快,一次可以取更多的数据。
但是这些都不重要,因为指令集,或者说ISA就是用来屏蔽底层硬件的差异。另外一方面,编译器会帮助我们搞定这些优化任务。贯穿我们本文的核心思想就是,简单能用。
在ARM处理器结构中,作为应用软件开发人员,只需要了解常见的汇编指令、编程模型、调试方法。
至于接口信号、指令执行时序和流水线阶段的相关实现细节,则属于处理器设计相关。但是它们也会影响我们编程的方式,即必须遵循C语言的规范,比如数据对齐,如果我们严格对齐数据,就不会触发错误。
在这里,我们需要先准备一些资料,对于嵌入式开发,查阅官方手册,是最有效的解决方法!
首先是arm keil,集成IDE的用户手册,这是里面有如何建立工程,对编译工具链的解释和预处理指令手册:
这些手册很多,大部分还要求专业域的知识,但是你应该知道它们在哪里,并且在遇到难以解决问题的时候可以翻阅查找。
然后是ARM官网对处理器架构设计和解释的手册,在官网上也可以免费下载:
最后是ST意法半导体生产的单片机手册,在官网也可以免费下载,还有很多编程和应用实例:
此外还有什么地方有参考手册可下载呢?对,没错,就是服务和培训机构的教程手册,比如野火和正点原子,可见PikaScript实践记录(1)之hello world_Once_day的博客-CSDN博客。
这里虽然提到的是具体的Keil、Arm、Stm32、野火/正点原子,但是并不限于他们,因为MCU的种类很多,CPU架构也不一定都是Arm结构,生产厂家很多,服务培训机构也很多,所以你的选择同样很多。
但是背后的规律就是,CPU架构手册、MCU手册、编译手册、教程手册等等,这些一定基本都有,这就是学习的方法,归纳总结。
当然如果某些厂家不提供怎么办?最简单的方法就是不用他们的产品,学习单片机的时候,咱们不受这气。
Cortex-M处理器包含的种类非常多了,性能一般都比较强大,它们之间都是非常相似的,所以程序一般不用改进多少就能移植。
Cortex-M4和Cotex-M3都是基于ARMv7-M架构,因此它们很类似,例如Stm32F1系列和Stm32F4系列。
主要特点如下:
具体特点可以取手册了解,这里必须着重强调的是Cortex-M4处理器没有MMU内存管理单元,虽然这很明显,但是这会给我们的编程带来显而易见的影响,即我们访问的都是物理地址,如果在某个地址做了不该做的事情,那就不是”coredump“(核心转储,Linux上常见于内存访问错误),而是“hardfault”(一个固有异常中断)。
Cortex-M处理器有一套标准的软件接口—CMSIS。主要有五部分:
一般编程有用的,是CMSIS-Core和CMSIS-DSP,至于调试器和OS,相关提供服务商会自行搞定。
常见的用户级CPU,如PC和手机,一般都是有多个操作模式,Cortex-M4也不例外,它具有两个栈指针:
我们一般用的,一个main函数,里面一个大while形式的,基本都是直接一个MSP指针就行了。
操作模式也有两种,特权模式和非特权模式,在系统启动之后,默认运行的就是特权模式。非特权模式会有一些限制,比如对NVIC寄存器的访问。
Cotex-M4处理器是支持可选大端和小端模式的,在内核寄存器能看到这个设置,但一般情况下都被指定为小端模式。
关于Cortex-M4和Cotex-M3处理器的详细介绍可以在《Arm Cortex M3/M4 权威指南》里面查看:
Cortex-M4处理器的架构很适合用C语言来编程,这是它的一大优势所在。
C语言学习可以参考书籍《C Primer Plus》。
基础语法总结:C语言之基础语法_Onceday CSDN博客。
通常意义上C代码编译成可执行程序需要经过以下步骤:
但这里简化了,对于Cortex-M4处理器而言,需要额外的汇编启动文件,以及分散加载脚本—用于指定代码所处的地址位置。
关于这部分可在《Arm Cortex M3/M4 权威指南》的第二章:嵌入式软件开发简介,里面找到具体的介绍。
在集成的keil开发环境里面,我们仅仅只需要关注源代码,即source.c这个层次,下面的编译工具,各类库代码等Keil都帮助我们搞定了。
首先,我们需要通过以下的资料来查阅有关该单片机的信息,详细的获取方式上面已经列出来了。
我们这里用到的PDF全部来自正点原子的资料:
可自行在正点原子资料网站下载:正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档
或者百度网盘:链接:stm32f429 pdf资料,提取码:tezy。
其中最重要、最基础的是《STM32F4xx参考手册》和《数据手册》,请注意大部分厂家的参考手册和数据手册都是一个或多个系列通用的,所以不要光看表面的名字,需要看内容是否支持手上的产品。
如Stm32F4xx就表示支持F4系列的常见MCU,如F407,F411等,但是它们之间也有细微差别,这些在手册里都会特别提示出来。
首先打开数据手册,在这里面可以快速概览该MCU的各类参数,如CPU信息,存储器信息,外设信息等,可以快速把握了解MCU。
但这不代表所有该系列的CPU都具有这个性能,因为还要根据封装的不同来确定具体的参数:
关于封装和产品的种类和不同,可以在《STM mcu的选型手册》里面查看:
现在ST意法半导体推出了一个应用程序ST-MCU-Finder,可以更方便的进行选型和查看相关参数资料,具体下载连接:
ST-MCU-Finder 官方下载页面
ST-MCU-Finder使用教程 跳动的字节 芯片之家 微信公众号
下面来看一下阿波罗Stm32F429开发板的信息:
首先,可以从芯片的丝印上读取具体型号:Stm32F429IGT6,根据选型手册上面,可以得知具有176管脚和1M内置Flash,256KB的组合SRAM。其他的存储芯片都是外扩,我们熟悉他们的样貌和接口,以便日后快速在一块新的开发板上将他们识别出来。
在一些资料不充分的MCU开发板上,我们只能根据以前积累的经验去快速判断未知芯片类型,从而进一步确定型号和作用。
在最开始的时候,我们只能使用内置的flash和SRAM,想驱动外置的flash、EEPROM和SDRAM并不是一键简单的事情。
下载烧录Flash的接口是USART1串口和SWD调试口,推荐使用调试口,串口下载并不适合程序开发测试,特别是新手。淘宝买一个仿真器很便宜。
这部分信息需要在《Stm32f42x数据手册》中查看,一般在第二章部分,会对基本的芯片外接电源电路做一个介绍。
这里需要强烈注意,虽然整个Stm32F1/2/4系列的供电设计是兼容的,但不同封装之间差异是真实存在的。
而且千万不要根据自己的“浅显理解”擅自改变或减少供电管脚和GND管脚的实际连接,除非你明确清楚自己在做什么。 比如芯片内部的模拟电路和数字电路可能是分开供电的,不一定所有的VCC都是并联一起的。
每一个电容和电阻的接法和值大小都是有限制范围的,切记乱接。
对初学者的建议是抄袭已有的设计,至少保证它能正常运行,但是千万不要在商业或盈利项目中抄袭。
没错,仅供学习和交流!
一般MCU参考都是至少500页往上,正常都是1K页以上,对于复杂的高级MCU,4-5K页都有可能。
如何有效的阅读这些手册就是一个非常有效的技能。建议如下:
特别是第三部分,包含了非常重要信息!
总线架构最关键的是架构图:
那些信息比较关键?主要涉及内存的可访问性,比如64KB的CCM数据RAM,挂在D总线上,那么只有CPU能够访问,其他的外设是不能够访问。
也许你好奇,什么外设可以访问内存?其实最简单的例子就是DMA,在基础编程是遇不到这个问题,但是如果一开始就不养成习惯,后面接触到高级MCU使用,就会被这些“小问题”弄到心态爆炸。
此外,千万不要以为所有CPU都是这副矩阵总线,Cortex-M处理器上这个总线矩阵是标配,但是部分高级MCU可能拥有多个总线矩阵。没错,比如Stm32H743就有3个总线矩阵,其可访问特性更是受限颇多,需要极其注意。 在一些其他的MCU中,可能完全是另外一套架构,比如TI(德州仪器)自有的MSP430x架构。
其次我们还要获取有关存储器映射的信息,外设寄存器这部分MCU厂家已经帮我们写好头文件了,我们可以直接使用结构体去设置寄存器的值。 但我想说得是,如果没有这个头文件怎么?你不能排除这个可能性,某些恶心的厂家可能懒得写这个,此时,可以根据MCU参考手册中寄存器的位信息和偏移量,以及这个外设映射的存储器位置,自行编写相关结构体定义。
如果某些厂家连寄存器信息都不给呢?直接掀桌子吧!Fuck these people !
回到主题,我们需要关注flash和SRAM的位置,其是Cortex-M结构的存储位置大体上是有分区的:
总线接口 | 描述 |
---|---|
I-CODE | 0x0-0x1fffffff,主要用于程序存储器,取指令,如flash,一般都从0x80000000开始。 |
D-CODE | 0x0-0x1fffffff,主要用于程序存储器,取数据,如flash。 |
系统总线 | 0x20000000-0xfffffff(除去PPB),外设和存储器,如SRAM就是从0x20000000开始的,外设是从0x40000000开始的。 |
PPB | 0xE0040000-0xE00FFFFF,外部私有外设总线,用于私有调试器件 |
下面是一张简化的接口图:
从这张图上应该可以非常清楚看出为什么说DMA无法访问CCM数据RAM了吧。 我们不需要非常深入了解它们具体的工作原理,这是芯片设计人员的责任。但我们需要有一个整体的宏观认知,以便于合理的进行编程。
在存储器这里接下来需要了解的地方就和Flash烧录程序有关了,即自举配置:
自举配置和Cortex-M处理器是无关的,在复位后,它总是通过I-CODE总线从地址0x00000000处开始执行。在初学阶段,我们只会通过两个部分来自举,即主flash和系统存储器。
主flash就是存放我们写的代码位置,系统存储器是系统bootloader,使我们通过串口能下载代码的关键程序,很多单片机都会有这个BootLoader。但关键是不同单片机的启动方法很不同,Stm32系列单片机只需要给定BOOT0和BOOT1管脚的电平值就可以切换,而其他的单片机,如TI,可能需要通过下载线发送一段特殊的序列值才能启动。 这个地方需要根据官方手册来确定使用方法。
需要注意,使用Bootloader,可以通过以下方法下载程序(重新编程flash):
在这里存在一个问题,CPU从0x00000000启动,可是MCU的主flash可是在0x80000000开始的,那怎么启动我们的代码?
有人可能会觉得是系统存储器是不是放在0x00000000?这个想法很好,比如STC宏晶科技的C51/52单片机就是从BootLoader启动,如果启动阶段收到特殊指令,就会进入FLASH编程程序并接收串口数据,否则就直接去运行主程序。
但是很可惜,Stm32的MCU不是这样做的,而是通过存储器重映射来实现的,默认情况下会把主flash(0x80000000)重映射到0x00000000。如下图所示:
可能第一眼看见这个图,感觉无法理解。这样来看,地址竖栏写明了具体的地址范围,后面四个竖栏代表不同自举模式的内存视图,其中FSMC是属于自定义的外扩存储器。 比如对于自举模式为嵌入式的SRAM,flash在地址0x80000000,系统存储器在地址0x1fff0000,sram还是从0x20000000开始。不同的是此时0x00000000也可以访问SRAM1,因此,CPU就会从SRAM1开始执行代码了。
系统总线和flash接口的信息非常丰富,而且也影响我们编程的具体细节,因此一定要在一款新的MCU仔细阅读该部分信息。
时钟系统是单片机初始化最重要的部分,不仔细阅读这部分,初始化这部分就无法自行完成设计。
我们首先要有一个概念,我们的单片机是从复位开始运行的,什么是复位?CPU从0x00000000开始执行代码,这里是中断向量的入口,离执行我们的main程序还有十万八千里远。中间CPU在干啥?这里我们要建立第一步概念,即Reset_Handler,复位处理程序,这个向量其实可以简单看是函数(指针)地址。
当然,这个Reset_Handler只是大家都喜欢这么取名,实际上这个名字在程序里可以随便取,只要你高兴,叫它First_function也行。
复位的发生有很多种,系统复位和电源复位,里面又细分很多细节,我们常用有一个按钮key,按一下它,就重启单片机,那么这个就是NRST引脚复位,此外还有软件(代码)、看门狗复位。电源复位有掉电/上电复位、欠压复位等,具体细节可以在参考手册的电源控制器PWR一章浏览。
所以说,单片第一次上电,就会触发复位中断,这是优先级最高的中断,不可屏蔽。
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
;IMPORT SystemInit
;寄存器代码,不需要在这里调用SystemInit函数,故屏蔽掉,库函数版本代码,可以留下
;不过需要在外部实现SystemInit函数,否则会报错.
IMPORT __main
LDR R0, =0xE000ED88 ; 使能浮点运算 CP10,CP11
LDR R1,[R0]
ORR R1,R1,#(0xF << 20)
STR R1,[R0]
;LDR R0, =SystemInit ;寄存器代码,未用到,屏蔽
;BLX R0 ;寄存器代码,未用到,屏蔽
LDR R0, =__main
BX R0
ENDP
这就是复位中断处理函数。在0x00000000地址存放的,实际是MSP主栈指针初始值,然后0x00000004是Reset_Handler函数的地址,后面就是其他中断向量的地址了。
简而言之,复位之后,先初始化栈指针,然后执行Reset_Handler。
这里屏蔽了SystemInit
函数,这个函数就是CMSIS-Core里面的系统时钟初始化函数,我们将自己完成这部分代码。
__main
函数是C语言的初始化函数,比如你在sram里面定义了一个有很多数据的数组,那么在C语言运行环境初始化的时候,就会将这段数据从flash搬运至sram中。
这里需要注意,C语言执行是有环境的,比如全局变量,静态变量,但是这不代表C代码在C环境未初始化时就无法使用。因此,我们可以在复位的地方直接使用C函数来初始化,这常用于某些重要外设的初始化,因为C语言运行环境的初始化也要颇费一段时间。 必须注意此时C代码将丧失部分特性。
这是最重要的MCU必看知识,常位于RCC,Reset and Clock control。
这种图要挑重点来看,其他外设时候可以用时在看:
LSE clock即low-speed externa clock,低速外部时钟,HSI clock就是 high-speed internal clock,高速内部时钟,名字就是几个词的搭配,非常好记。
两个低速晶振都是32.768Khz,一般都是用于RTC(Real-time clock)和看门狗模块,也可以用这个时钟倍频给SYSCLK使用,倍频的方法不止PLL这一种,例如TI MSP系列的DCO数控振荡器。
MCO可输出内部的时钟,如果连接到示波器,可用判断当前时钟的频率是否符合预期。其他的情况可用于给其他设备供给时钟信号。
在Stm32单片机复位启动后,一般都是默认选为HSI RC时钟,16Mhz,不是很准。 此时SYSCLK=16MHz,其后的AHB和APB分频系数都是默认值,即最小值分频值1。
需要注意,内部时钟是集成在芯片内部,而外部晶振需要在最小系统板上额外焊接。
在硬件上,就需要设计外部晶振电路,新手以模仿最好。在参考手册和数据手册的RCC章节都可以找到相关的晶振参数限制和电路模板。
时钟切换并非是一个简单的过程,最大的原因是,对于外部晶振,我们一般需要先要初始化它,然后再切换SYSCLK时钟源,这部分也会引申出最刺激的部分CPU超频,可以超出官方限定的频率来运行CPU。
总所周知,CPU运行频率和CPU供电有关,Cortex-M处理器也不例外,供电电压的大小限制了CPU最大频率的上限。此外还有来自flash的限制,flash的读取速度是受限的,而SRAM静态随机存储器的速度是非常快的,因为桌面级CPU就是拿SRAM做缓存的。
关于如何改变CPU的运行频率SYSCLK的步骤在参考手册的嵌入Flash章节的read interface部分有详细介绍。在后续的文章也会专门进行讲解。
在Stm32单片机上的参考手册,一般对中断向量只是简单展示具体的向量类别。更多的细节展示了外部中断的设置方法,我们这里暂时不讨论外部中断,也就是事件的设置方法,后续章节会继续这部分内容。
对于Cortex-M处理器,最多支持240个中断请求(IRQ),以及多个预设的系统异常。如下:
异常优先级 | 类型 | 描述 | 地址 |
---|---|---|---|
- | - | MSP栈地址 | 0x0000 0000 |
-3(固定) | 复位 | 各类复位事件触发 | 0x0000 0004 |
-2(固定) | NMI | 不可屏蔽中断,外部输入,如RCC安全系统 | 0x0000 0008 |
-1(固定) | 硬件错误 | HardFault,所有类型的错误都可能引发 | 0x0000 000C |
可编程 | 存储器管理错误 | MemManage错误 | 0x0000 0010 |
可编程 | 总线错误 | BusFault,预取指失败,存储器访问失败 | 0x0000 0014 |
可编程 | 使用错误 | UsageFault,未定义的指令或非法状态,用于协处理器 | 0x0000 0018 |
保留 | 未使用 | 4个保留的向量 | 0x0000 001C-28 |
可编程 | SVCall | 通过 SWI 指令调用的系统服务 | 0x0000 002C |
可编程 | 调试监控 | Debug Monitor,调试监控器 | 0x0000 0030 |
保留 | 未使用 | 1个保留的向量 | 0x0000 0034 |
可编程 | PendSV | 可挂起的系统服务,OS一般通过它进行进程切换 | 0x0000 0038 |
可编程 | SysTick | 系统嘀嗒定时器 | 0x0000 003C |
可编程 | 外部中断 | MCU厂家绑定到外设中断源 | 0x0000 0040-198 |
我们使用单片机,用的中断几乎都是外部中断,即MCU厂家已经跟我们绑定好外部外设中断源。
因此,我们必须把写好的中断,如串口中断的处理函数地址填写到对应外部中断的地址处,即0x0000 0040-0x0000 00198
处,随着不同封装和MCU型号,其外部中断向量的实际数量也就不同,因此其结束地址(0x0000 00198)也就不同。
大部分部分优先级是可以手动设置的,但有三个异常的优先级是固定的,他们正常情况下是不可屏蔽的,即复位、NMI、HardFault。
关于中断的部分属于内核部分,在《Arm Cortex M3/M4 权威指南》的第7章异常和中断里面有详细介绍,这里不具体展开,后续将会继续讨论。
我们设置中断的时候可以简单起见,即无需中断分组,只需要中断优先级,因为常见的RTOS都不支持中断分组,那我们也无需使用复杂的中断分组功能。此外也只需要使能和关闭中断,无需过多操作,在后面搭载RTOS时再考虑复杂的功能使用。
为什么GPIO放在一开始就去需要了解呢?因为这实际上是单片机最基础的使用功能了,很简单,所以大部分人都忽略了它,但往往带来致命的问题。
此外,不同的MCU厂家的GPIO的特性也不一样,但整体来说,可以归纳出一些共同点:
Stm32的IO功能做得非常简洁易看,某些厂家,IO功能长达数十篇章,引脚之间差异较大,使用不兼容,体验很差。
这张图可以看出什么呢?最开始有一堆保护二极管,这是用来容忍5v管脚的,单片机的电压域是3.3v域(1.8v-3.6v),但可以和5v电平直接相连,而无需串联限流电阻(一般4.7KΩ),但诸如C51/52和arduino单片机是5v电压域。
然后输入和输出走的是不同的路线,输入方面,无论是复用的数字输入还是模拟输入,都无需经过IO的控制。输出量需要经过开漏和推挽的控制,开漏状态下,“0”会激活N-MOS,因此,在低电平上具有较高的驱动能力,但在高电平下负载能力较弱,需要通过上拉来增强。 推挽模式下,高低电平都具有较大的驱动能力,但是需要注意过大的电流烧毁管脚。
关于这部分可参考文档:开漏输出与推挽输出 Kevin Zhang - 知乎 (zhihu.com)。
我们一般都会使用推挽模式来从管脚输出,最常见的应用是LED灯,自然驱动能力较强比较好。外设复用情况下,可能需要根据外设的要求选择推挽和开漏模式,这部分可在参考手册GPIO部分了解。
在输出模式下,有以下情况,此外还有速率OSPEEDE可以配置:
MODER模式 | 类型 | 上下拉电阻 | 配置情况 |
---|---|---|---|
01普通输出 | 推挽 | 无/上拉/下拉电阻 | 带可选上下拉电阻的普通推挽输出 |
01普通输出 | 开漏 | 无/上拉/下拉电阻 | 带可选上下拉电阻的普通开漏输出 |
10复用输出 | 推挽 | 无/上拉/下拉电阻 | 带可选上下拉电阻的复用推挽输出 |
10复用输出 | 开漏 | 无/上拉/下拉电阻 | 带可选上下拉电阻的复用开漏输出 |
输入模式下,速率配置和开漏推挽配置是无效的,图4.13可以看出其电路是不同的,主要情况如下:
MODER模式 | 上下拉电阻 | 配置情况 |
---|---|---|
00复用输入 | 浮空/上拉/下拉电阻 | 带可选上下拉电阻的普通输入 |
11模拟输入 | 00 | ADC等模拟外设的输入,无需额外配置 |
此外需要注意的是,有部分管脚一开始被用于调试端口,在复位时默认是复用状态:
这些管脚,若是想用于IO功能或其他外设复用功能,必须清除到已有的状态。另外一方面,如果调试器连接芯片失败,很有可能是程序代码使用了这些管脚,因此要把自举模式调到系统存储器或者SRAM,然后就可以进入调试模式了,此外,一直按住复位键也可以。
管脚具体的复用功能如何配置需要查阅数据手册->第四章,引脚排列和引脚说明->表12,复用功能映射,可以查看仔细信息。
首先需要对keil最一些配置,第一个是编辑器,在edit
->configuration
,首先注意这个编码是ANSI,这个不是ASCII编码,而是多字节编码,是Windows的产物,对应中文就是GBK等,另外一种常见的编码是UTF-8,这两个之间不能直接线性转换,因此我们必须规定其中一种,现在一般选择UTF-8就好。
然后是下面的各类文件选项,主要是勾选Insert spaces for tabs,我们的文件不需要tab符号,而且每个tab符号都应该被转化为4个空格,即Tab size为4。
这里并不在keil上编码,因为界面属实太糟糕了,当然,可以美化,可以自行去网上寻找相关配置文件。
关于如何创建keil工程,这里不再赘述,在正点原子的教程里面是有详细步骤,晚上也能收到大把的教程。
先创建项目文件夹,结构如下,这套模板非常常见,没有为什么,只是几个文件夹而已,高兴可以随便放:
E:\F429\429BSP
├─HARDWARE //存放外设代码
├─OBJ //存放编辑的中间文件
├─README //帮助说明文档
├─SYSTEM //系统关键代码,即初始化、串口输出,时钟等
├─SYSTEM-CORE //存放CMSIS-core和MCU头文件
└─USER //存放主函数文件
首先是获取启动汇编文件,一般而言,可以直接拿别人工程里面的直接使用,如正点原子的例程。这里想起来keil下载了一个Stm32Fx系列的支持包,那里面有没有我们需要的文件呢?确实有,如:
//F是安装keil的盘符,cg是系统的用户名字
目录: F:\Users\cg\AppData\Local\Arm\Packs\Keil\STM32F4xx_DFP\2.16.0\Drivers\CM
SIS\Device\ST\STM32F4xx\Source\Templates
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2022/8/26 22:56 arm
d----- 2022/8/26 22:56 gcc
d----- 2022/8/26 22:56 iar
-ar--- 2021/8/9 13:44 27018 system_stm32f4xx.c
STM32F4xx_DFP
就是我们下载的支持包。system_stm32F4xx.c是ST厂家给我们写好的系统时钟初始化代码。arm、gcc、iar里面就是三种不同编译工具链所需的启动文件,keil属于arm范畴,所以复制这些文件到工程目录下的SYSTEM文件夹。
接下来在下面文件夹获取对应的头文件:
//F是安装keil的盘符,cg是系统的用户名字
F:\Users\cg\AppData\Local\Arm\Packs\Keil\STM32F4xx_DFP\2.16.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include
-ar--- 2021/8/9 13:44 1463228 stm32f429xx.h
-ar--- 2021/8/9 13:44 12261 stm32f4xx.h
-ar--- 2021/8/9 13:44 3674 system_stm32f4xx.h
注意,这里我们选择的是Stm32F429开发板,所以要选取对应的头函数和启动汇编文件,但如果是其他的F4系列,就要选取对应的头文件了,很容易就能类推到其他单片机上。
所以目前SYSTEM下有以下文件:
+---SYSTEM
| startup_stm32f429xx.s
| stm32f429xx.h
| stm32f4xx.h
| system_stm32f4xx.c
| system_stm32f4xx.h
但是还缺了一部分,即内核的头文件,还有一些内核定义和内核寄存器,以及一些相关的函数,即CMSIS-Core部分代码。
需要注意,我们是完全可以自己写这些内核函数,并且用C代码直接读写寄存器,但是问题是这部分代码并不冗余,自己去写也简化不了多少,但不如直接使用比较好。
这部分内核代码在以下路径,里面有所有的Cortex-M内核头文件:
//F是安装keil的盘符,cg是系统的用户名字
F:\Users\cg\AppData\Local\Arm\Packs\ARM\CMSIS\5.9.0\CMSIS\Core\Include
-ar--- 2022/5/2 11:07 27999 cmsis_armclang.h
-ar--- 2022/5/2 11:07 9481 cmsis_compiler.h
-ar--- 2022/5/2 11:07 1680 cmsis_version.h
-ar--- 2022/5/2 11:07 120975 core_cm4.h
-ar--- 2022/5/2 11:07 11731 mpu_armv7.h
Stm32F4xx需要的内核头文件是core_cmFunc.h
,此外还有几个内核函数头文件,包含编译和内存保护相关的信息。
这里已经很明确了,内核架构为armv7,编译器为armclang,但查看这些头文件,可以发现支持非常多的编译工具链。
在armclang和armcc都是keil所支持的,一个对应版本5(armcc),一个对应版本6(支持C++,以及C的新标准)。
具体可见:ARM 之七 主流编译器(armcc、iar、gcc for arm、LLVM(clang))详细介绍_ShouZC的博客-CSDN博客_armcc。
我们将用最新的版本ARMCLANG六来学习,不再使用老版本的ARMCC。
接下来,我们的项目文件将要创建在429BSP文件夹下,也就是SYSTEM和USER的根目录下,开发板类型选择Stm32F429IGTx,支持包一个都不选。
然后在导航栏的魔法棒这里开始项目配置:
其实keil会默认包含STM32F4xx_DFP
里面的设备头文件,但这不影响我们放在工程下一份,无论是分发给别人还是使用VSCode进行开发,都将比较方便。
然后在红绿白三色矩形选项配置项目目录,整体如下,具体步骤可在正点原子的教程PDF前几章中查看:
然后需要在魔法棒里面的C/C++选项卡里添加预设宏和头文件包含路径。
这里主要就是把自己的头文件路径包含进去,注意,这是不能递归子路径的,必须包含所有路径。
到这里,工程就搭建完毕了,下面是目录拓扑图:
BSP429:
| keilkill.bat //一些del命令,删除OBJ里面的编译中间件内容
| 429bsp.uvguix.cg
| 429bsp.uvoptx
| 429bsp.uvprojx
|
+---HARDWARE
+---OBJ
|
+---README
+---SYSTEM
| delay.c
| delay.h
| sys.c
| sys.h
| usart.c
| usart.h
|
+---SYSTEM-CORE
| cmsis_armclang.h
| cmsis_compiler.h
| cmsis_version.h
| core_cm4.h
| mpu_armv7.h
| startup_stm32f429xx.s
| stm32f429xx.h
| stm32f4xx.h
| system_stm32f4xx.c
| system_stm32f4xx.h
|
+---USER
| main.c
|
\---DebugConfig
写到这里已经好多字了,本来想这里移植完Pikascript并输出helloworld的,但是篇幅太大了,那就分成两篇来些吧,MCU的初始化代码,编程风格等等还有好多要讲述。内容挺繁琐了,不过周末闲来也无事,就下周再见吧!
下周应该是真正的编码介绍了。 O(∩_∩)O哈哈~