从这些知识点入手,学单片机就简单多了

摘要:从STM32新建工程、编译下载程序出发,让新手由浅入深,尽享STM32标准库开发的乐趣。

自从CubeMX等图像配置软件的出现,同学们往往点几下鼠标就解决了单片机的配置问题。对于追求开发速度的业务场景下,使用快速配置软件是合理的,高效的,但对于学生的学习场景下,更为重要的是知其然并知其所以然。

以下是学习(包括但不限于)嵌入式的三个重要内容,

1、学会如何参考官方的手册和官方的代码来独立写自己的程序。

2、积累常用代码段,知道哪里的问题需要哪些代码处理。

3、跟随大佬步伐,一步一个脚印。

首先:我们都知道编程时一般查的是《参考手册》,而进行芯片选型或需要芯片数据时,查阅的是《数据手册》。此外市面上所有关于STM32的书籍都是立足于前二者(+Cortex内核手册)进行编著。

其次:要分清什么是内核外设与内核之外的外设,为了便于区分,按照网上的一种说法,将“内核之外的外设”以“处理器外设”代替。再者:如今很少使用标准库了,都是HAL库,但作为高校目前教学方式,我们将以STM32f10xxx为例对标准库开发进行概览。

一、STM32 系统结构

STM32f10xxx系统结构

内核IP

从结构框图上看,Cortex-M3内部有若干个总线接口,以使CM3能同时取址和访内(访问内存),它们是:指令存储区总线(两条)、系统总线、私有外设总线。有两条代码存储区总线负责对代码存储区(即 FLASH 外设)的访问,分别是 I-Code 总线 D-Code 总线

I-Code用于取指,D-Code用于查表等操作,它们按最佳执行速度进行优化。

系统总线(System)用于访问内存和外设,覆盖的区域包括SRAM,片上外设,片外RAM,片外扩展设备,以及系统级存储区的部分空间。

私有外设总线负责一部分私有外设的访问,主要就是访问调试组件。它们也在系统级存储区。

还有一个DMA总线,从字面上看,DMA是data memory access的意思,是一种连接内核和外设的桥梁,它可以访问外设、内存,传输不受CPU的控制,并且是双向通信。简而言之,这个家伙就是一个速度很快的且不受老大控制的数据搬运工。

处理器外设(内核之外的外设)

从结构框图上看,STM32的外设有串口、定时器、IO口、FSMC、SDIO、SPI、I2C等,这些外设按照速度的不同,分别挂载到AHB、APB2、APB1这三条总线上。

二、寄存器

什么是寄存器?寄存器是内置于各个IP外设中,是一种用于配置外设功能的存储器,并且有想对应的地址。一切库的封装始于映射

是不是“又臭又长”,如果进行寄存器开发,就需要怼地址以及对寄存器进行字节赋值,不仅效率低而且容易出错。

来,开个玩笑。

你也许听说过“国际C语言乱码大赛(IOCCC)”下面这个例子就是网上广为流传的 一个经典作品:

#include 
 
main(t,_,a)char *a;{return!03?main(-79,-13,a+main(-87,1-_,
main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t,
"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\
;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l \
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \
){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \
iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \
;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \
}'+}##(!!/"
)
:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
:02,2,"%s"):*a=='/'||main(0,main(-61,*a,
"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m.vpbks,fxntdCeghiry"),a+1);}

库的存在就是为了解决这类问题,将代码语义化。语义化思想不仅仅是嵌入式有的,前端代码也在追求语义特性。

三、万物始于点灯

(1)内核库文件分析

cor_cm3.h

这个头文件实现了:

1、内核结构体寄存器定义。 

2、内核寄存器内存映射。 

3、内存寄存器位定义。跟处理器相关的头文件stm32f10x.h实现的功能一样,一个是针对内核的寄存器,一个是针对内核之外,即处理器的寄存器。

misc.h

内核应用函数库头文件,对应stm32f10x_xxx.h。

misc.c

内核应用函数库文件,对应stm32f10x_xxx.c。在CM3这个内核里面还有一些功能组件,如NVIC、SCB、ITM、MPU、CoreDebug,CM3带有非常丰富的功能组件,但是芯片厂商在设计MCU的时候有一些并不是非要不可的,是可裁剪的,比如MPU、ITM等在STM32里面就没有。

其中NVIC在每一个CM3内核的单片机中都会有,但都会被裁剪,只能是CM3 NVIC的一个子集。在NVIC里面还有一个SysTick,是一个系统定时器,可以提供时基,一般为操作系统定时器所用。misc.h和mics.c这两个文件提供了操作这些组件的函数,并可以在CM3内核单片机直接移植。

(2)处理器外设库文件分析

startup_stm32f10x_hd.s

这个是由汇编编写的启动文件,是STM32上电启动的第一个程序,启动文件主要实现了

  • 初始化堆栈指针 SP;
  • 设置 PC 指针=Reset_Handler ;
  • 设置向量表的地址,并 初始化向量表,向量表里面放的是 STM32 所有中断函数的入口地址
  • 调用库函数 SystemInit,把系统时钟配置成 72M,SystemInit 在库文件 stytem_stm32f10x.c 中定义;
  • 跳转到标号_main,最终去到 C 的世界。

system_stm32f10x.c

这个文件的作用是里面实现了各种常用的系统时钟设置函数,有72M,56M,48, 36,24,8M,我们使用的是是把系统时钟设置成72M。

Stm32f10x.h

这个头文件非常重要,这个头文件实现了:

1、处理器外设寄存器的结构体定义。

2、处理器外设的内存映射。

3、处理器外设寄存器的位定义。

关于 1 和 2 我们在用寄存器点亮 LED 的时候有讲解。

其中 3:处理器外设寄存器的位定义,这个非常重要,具体是什么意思?

我们知道一个寄存器有很多个位,每个位写 1 或者写 0 的功能都是不一样的,处理器外设寄存器的位定义就是把外设的每个寄存器的每一个位写 1 的 16 进制数定义成一个宏,宏名即用该位的名称表示,如果我们操作寄存器要开启某一个功能的话,就不用自己亲自去算这个值是多少,可以直接到这个头文件里面找。

我们以片上外设 ADC 为例,假设我们要启动 ADC 开始转换,根据手册我们知道是要控制 ADC_CR2 寄存器的位 0:ADON,即往位 0 写 1,即:

ADC->CR2=0x00000001;

这是一般的操作方法。现在这个头文件里面有关于 ADON 位的位定义:

 #define ADC_CR2_ADON ((uint32_t)0x00000001)

有了这个位定义,我们刚刚的代码就变成了:

ADC->CR2=ADC_CR2_ADON

stm32f10x_xxx.h

外设 xxx 应用函数库头文件,这里面主要定义了实现外设某一功能的结构体,比如通用定时器有很多功能,有定时功能,有输出比较功能,有输入捕捉功能,而通用定时器有非常多的寄存器要实现某一个功能。

比如定时功能,我们根本不知道具体要操作哪些寄存器,这个头文件就为我们打包好了要实现某一个功能的寄存器,是以机构体的形式定义的,比如通用定时器要实现一个定时的功能,我们只需要初始化 TIM_TimeBaseInitTypeDef 这个结构体里面的成员即可,里面的成员就是定时所需要操作的寄存器。

有了这个头文件,我们就知道要实现某个功能需要操作哪些寄存器,然后再回手册中精度这些寄存器的说明即可。

stm32f10x_xxx.c

stm32f10x_xxx.c:外设 xxx 应用函数库,这里面写好了操作 xxx 外设的所有常用的函数,我们使用库编程的时候,使用的最多的就是这里的函数。

(3)SystemInit

工程中新建main.c 。

在此文件中编写main函数后直接编译会报错:

Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o).

错误提示说SystemInit没有定义。从分析启动文件startup_stm32f10x_hd.s时我们知道,

;Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
;IMPORT SystemInit
;LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

汇编中;分号是注释的意思

第五行第六行代码Reset_Handler调用了SystemInit该函数用来初始化系统时钟,而该函数是在库文件system_stm32f10x.c中实现的。我们重新写一个这样的函数也可以,把功能完整实现一遍,但是为了简单起见,我们在main文件里面定义一个SystemInit空函数,为的是骗过编译器,把这个错误去掉。

关于配置系统时钟之后会出文章RCC时钟树详细介绍,主要配置时钟控制寄存器(RCC_CR)和时钟配置寄存器(RCC_CFGR)这两个寄存器,但最好是直接使用CubeMX直接生成,因为它的配置过程有些冗长。

如果我们用的是库,那么有个库函数SystemInit,会帮我们把系统时钟设置成72M。

现在我们没有使用库,那现在时钟是多少?答案是8M,当外部HSE没有开启或者出现故障的时候,系统时钟由内部低速时钟LSI提供,现在我们是没有开启HSE,所以系统默认的时钟是LSI=8M。

标签:
EEPWweixin
电子产品世界 微信号:EEPWweixin 扫描二维码关注公众号
优质自媒体

小编推荐

  1. 1 面包机制作面包面包机制作面包的方法(面包机制作面包配方及制作方法窍门)

    大家好,小娟今天来为大家解答面包机制作面包面包机制作面包的方法以下问题,面包机制作面包配方及制作方法窍门很多人还不知道,现在让我们

  2. 2 莫“揩”公家油

    近日,有媒体报道了天津市河北区人民当局办公室原四级调研员房志秀严重违纪违法案的相关细节。房志秀在担当区当局办公室财务科科长时,精神

  3. 3 十进制转二进制算法(十进制转二进制算法小数)

    大家好,小娟今天来为大家解答十进制转二进制算法以下问题,十进制转二进制算法小数很多人还不知道,现在让我们一起来看看吧!1、十进制整数

  4. 4 母婴节特惠丨把礼物选在妈妈的心坎里

    母亲节要到了,你还在为选什么礼品而吃力恼?e家优选早为你挑好了,包管送到妈妈的心坎里~妈妈的身体健康一向都是后代最关心的问题,然则针对

  5. 5 爱学术论文查重公众号(爱学术公众号的查重可靠吗)

    大家好,小伟今天来为大家解答爱学术论文查重公众号以下问题,爱学术公众号的查重可靠吗很多人还不知道,现在让我们一起来看看吧!1、搜索公

  6. 6 著名演员凌晨离世!他的作品家喻户晓……网友痛悼

    点上方蓝字“哈尔滨殡葬”快速存眷据媒体报道曾在片子《泰坦尼克号》中饰演船主一角的伯纳德·希尔(Bernard Hill)已于本地时间5月5日凌晨离世享

  7. 7 四星酒店(四星酒店住一晚大概多少钱)

    大家好,小娟今天来为大家解答四星酒店以下问题,四星酒店住一晚大概多少钱很多人还不知道,现在让我们一起来看看吧!1、地上建筑面积:约

  8. 8 觉悟by小秦子(觉悟by小秦子)

    大家好,小伟今天来为大家解答觉悟by小秦子以下问题,觉悟by小秦子很多人还不知道,现在让我们一起来看看吧!1、链接: https://pan.baidu.s/1sJKoSAQh6

Copyright 2024 优质自媒体,让大家了解更多图文资讯!