本帖最后由 正点原子运营 于 2024-3-20 16:43 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章着重介绍STM32的一些基础知识,令读者对STM32有一个初步的了解,为后面的章节学习做铺垫。对于本章的内容,读者可以先只看一个大概,在后面需要使用到这部分知识点的时候,再回过头来仔细看。 本章分为如下几个小节: 5.1 C语言基础知识复习 5.2 寄存器基础知识 5.3 STM32F103系统架构
5.1 C语言基础知识复习 本节介绍C语言的基础知识,对C语言比较熟练的读者,可以跳过本节,对于基础比较薄弱的读者,建议好好学习一下本节的内容。 由于C语言博大精深,不可能由一小节的内容就全讲明白,所以本节知识回顾在进行STM32开发时常用的几个C语言知识点,以便读者的更好的学习本书后续的内容,并编写相关的代码。 5.1.1 位操作 C语言的位操作就是对基本类型变量进行位级别的操作。本节的内容比较简单,这里也就点到为止,不深入探讨。下面先讲解几种位操作运算符,然后介绍其相关的使用技巧。C语言支持如下6种位操作的运算符: 下面介绍这些位操作运算符的使用技巧。 ①:在不改变其他位的状况下,对某几个位进行设值 这个场景在单片机开发中经常使用,方法就是先对需要设置的位用&运算符进行清零操作,然后用|运算符设值。例如要设置GPIOA的ODATA寄存器Bit6(第6位)为1,则可以先使用&运算符对该寄存器的Bit6进行清零操作: - GPIOA->ODATA &= 0xFFFFFFBF; /* 将Bit6清0 */
复制代码然后再使用|运算符对该寄存器的Bit6进行置1操作: - GPIOA->ODATA |= 0x00000040; /* 将Bit6置1 */
复制代码②:移位操作提高代码的可读性 例如①中|操作使用到的0x00000040,虽然通过换算,可以知道是将Bit6置1,但是这样的表达可读性比较差,可以通过移位操作对其进行改进: - GPIOA->ODATA |= (1 << 6); /* 将Bit6置1 */
复制代码这么一来,就可以非常直观地看出是将Bit6置1了。 ③:按位取反操作使用技巧 ②中使用移位操作改进①中仅使用|运算将Bit6置1的可读性,但要改进①中使用&运算将Bit6清0的可读性还需借助按位取反操作: - GPIOA->ODATA &= ~(1 << 6); /* 将Bit6清0 */
复制代码这么一来,就可以非常直观地看出是将Bit6清0了。 ④:按位异或操作使用技巧 按位异或可以很方便地对Bit位进行翻转,例如不考虑LED当前是何种状态,只要求控制LED翻转状态等情况(假设LED的亮灭状态由PA6输出的高低电平控制): - GPIOA->ODATA ^= (1 << 6); /* Bit6的值取反 */
复制代码这么一来,就可以很方便地操作PA6引脚输出相反的电平,而不用先读取PA6输出的电平状态然后才输出相反的电平。 5.1.2 define宏定义 define是C语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便,其常见的格式如下: “标识符”为所定义宏的名称;“字符串”可以是常数、表达式、格式串等。例如: - #define HSE_VALUE 8000000U
复制代码定义标识符HSE_VALUE的值为8000000U,数字后的U是unsigned(无符号)的意思,随后便可在程序代码中使用HSE_VALUE来代替8000000U。 至于define宏定义的一些其他高级用法,例如宏定义带参数等,本章不过多介绍。 5.1.3 ifdef条件编译 在单片机程序开发过程中,经常会遇到需要在满足某些条件时对一段代码进行编译,而当条件不满足或满足另一条件时编译另一段代码,这就可以使用条件编译,条件编译最常见的形式如下: - #ifdef 标识符
- 代码段1
- #else
- 代码段2
- #endif
复制代码如上的条件编译,当标识符被定义过(一般使用define进行定义),则代码段1会被编译,否则会编译代码段2。 5.1.4 extern外部申明 C语言中extern关键字用于修饰变量或函数,以表示变量或函数定义在别的文件中,提示编译器遇到此变量或函数时,需在其他文件中寻找其定义。这里要注意的是,可以使用extern多次在不同文件中修饰同一个变量或函数,但该变量或函数只能被定义一次 5.1.5 typedef类型别名 typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。例如在编写程序时经常使用到的uint8_t、uint16_t和uint32_t等都是由typedef定义的类型别名,其定义如下: - typedef unsigned char uint8_t;
- typedef unsigned short int uint16_t;
- typedef unsigned int uint32_t;
复制代码这么一来就可以在编写程序代码的时候使用uint8_t等代替unsigned char等,极大地提高的代码的可读性可编写代码的效率。 5.1.6 struct结构体 struct用于定义结构体,结构体就是一堆变量的集合,结构体中的成员变量的作用一般都是相互关联的,定义结构体的形式如下: - struct 结构体名
- {
- 成员变量1的定义;
- 成员变量2的定义;
- ......
- };
- 例如:
- struct lcd_device_struct
- {
- uint16_t width;
- uint16_t height;
- };
复制代码如上举例的结构体定义,一堆描述LCD屏幕的变量的集合,其中包含了LCD屏幕的宽度和高度。 结构体变量的定义如下: - struct lcd_device_struct lcd_device;
复制代码如上,就定义了一个名为lcd_device的结构体变量,那么怎么访问这个结构体变量中的成员变量呢?如下: - lcd_device.width = 240;
- printf("LCD Height: %d\n", lcd_device.height);
复制代码如上就展示了结构体变量中成员变量的访问操作。 5.1.7 指针 指针是另一个变量的变量,指针变量在内存中保存的是另一个变量在内存中的地址,通过指针可以访问到另一个变量所在内存地址中的数据。 举一个定义指针的例子,如下: - char *p_str = "This is a string!";
复制代码如上,就定义一个名为p_str的指针变量,并将p_str指针指向了字符串“This is a string!”保存在内存中首地址,对于STM32来说,此时p_str的值就是一个32位的数,这个数就是一个内存地址,这个内存地址就是上述字符串保存在内存中的首地址。 通过p_str指针就可以访问到字符串“This is a string!”,那具体是如何访问的呢?前面说p_str保存的是一个内存地址,那么就可以通过这个内存地址去内存中读取数据,通过*p_str就可以访问地址为p_str的内存数据,*(p_str + 1)可以访问下一个内存地址中的数据。 知道了如何访问内存中的数据,但是读取到的数据要如何解析呢?这就有p_str指针的类型决定了。在这个例子中p_str是一个char类型的指针,那么访问*p_str就是访问地址为p_str,大小为sizeof(char)(一般为一个字节)的一段内存数据,在这个例子中就可以读取到字符“T”, 读取*(p_str + 1)就是“h”,以此类推。 指针是C语言的精髓,但也是初学者望而生畏的一个知识点,若读者一时半会无法理解指针的用法也没关系,善用搜索引擎,网上有很多参考的学习资料。
5.2 寄存器基础知识 寄存器(Register)是一种特殊的内存,它主要用于实现控制和访问MCU的内核和各个片上外设。 对于STM32来说,寄存器一般都是32位的,但由于MCU上的内存资源十分宝贵,因此在一个32位寄存器中,会使用其中的1位或多位来控制或访问MCU的内核或各个片上外设的一种功能,但即使如此,STM32上也还是有上百个寄存器,这实际上是因为STM32有很多的片上外设导致的,只要将这些寄存器按照功能分好类,学起来就不难了。 从大方向来区分,STM32中的寄存器可分为两大类,分别为内核寄存器和外设寄存器,大类下还可以分出许多小类,如下表所示: 对于初学者来说仅需在学习MCU的各个片上外设时,再去学习该片上外设相关的寄存器即可。
5.3 STM32F103系统架构 STM32F103是ST公司基于ARM授权Cortex-M3内核设计的一款芯片,而Cortex-M内核采用了ARM v7-M架构,具有低成本、低功耗、实时性好、中断响应快、处理效率高等特点。 5.3.1 Cortex-M3内核&芯片 ARM公司提供内核(例如Cortex-M3内核)授权,完成的MCU芯片还需要很多其他的组件。芯片公司(例如ST)在获取Cortex-M3内核授权后,就可以将其用于自己的芯片设计中,并添加存储器、外设、I/O等其他组件。同于型号的芯片也会有不同的规格,实际当大多数就是存储器容量、外设资源等的差异。 5.3.2 STM32F103系统框图 STM32F103的系统框图,如下图所示: 从上图可以看出,STM32F103就是在ARM提供的Cortex-M3内核上通过各种总线,挂载了各种各样的外设和存储器。 5.3.3 地址映射 STM32是32位的MCU,因此其地址总线的宽度也是32位,因此最大可访问的内存空间为4GB(2^32=4GB),STM32就将这4GB的空间映射给各种寄存器和存储器。 STM32F103地址映射图,如下图所示: |