STM32基础系列包含了三件套,掌握了这三件套,类似于掌握了程咬金三板斧,就可以开始干项目,创造价值了。毕竟,真正的编程是一项实战性很强的技术,掌握编程主要靠实战,而不是靠知识灌输。
STM32的编程三件套就是:
- GPIO初始化;
- TIM定时器(含中断);
- USART串口;
- 为何把这三个功能列成编程基础三件套呢?先说GPIO,熟练掌握GPIO之后,就完全可以利用GPIO组成I2C, 1WIRE, SPI等各种复杂总线波形。虽然没有硬件接口效率高,但是胜在不挑端口,不调平台,方便移植。
第二个是TIM定时器,这个很多人不容易理解,毕竟很多教程中,都仅仅用TIM定时器,作为PWM控制LED灯的亮度。但是想象一下,真正项目需求中,哪怕是LED点灯,一般描述也不是"LED闪两下",而是:“LED熄灭0.9S”,然后“LED点亮0.1S”,并且重复3次。也就是:需要非常精准的定时需求。
三件套之三是串口:串口是重要的调试端口,虽然可以采用单步方式查看程序走向,但是用串口方式调试代码,能够更加了解代码运行的整体流程,以及数据交互情况。而且后面有些系统,比如ESP32类的系统,并不支持采用单步查看,这样我们就只能采用串口进行调试了。串口另外一个重要的功能,就是通信功能。我们日常使用的WIFI模块,蓝牙模块,4G模块,NBIOT模块,LORA模块,都是采用串口和MCU进行通信的。因此,弄清楚串口数据首发,也是编程基础三件套之一。
下面先了解一下GPIO初始化步骤
1. GPIO初始化步骤
总体来说,GPIO初始化分为3步。
第一步:定义一个GPIO_InitTypeDef类型的结构体变量;
第二步: 调用RCC_APB2PeriphClockCmd()这个函数,实现时钟初始化;
第三步:选择需要控制的GPIO引脚,将pin, GPIO_Mode, GPIO_Speed这些参数初始化;并将其写入对应的LED_GPIO_PORT中。
理解了上面3个步骤后,我们就需要了解一个问题:
作为一个程序员,编程的时候,需要自己手工输入这些代码吗?还是复制粘贴就行了?
实际上,网上很多文章都说:要想学会编程,一定要多敲代码。但是像上面的这些代码,其实只是一些简单的配置工作,完全没有必要手动输入。正常情况下,是找到本教程,把其中的GPIO初始化部分,移植到新工程里面即可。
所以,学编程,不要学习敲代码,而要学习移植代码,通过移植,掌握代码块之间的耦合情况。这样就能够快速完成很多功能。
2. 跟着ST学编程
掌握了GPIO初始化三部曲,只是前菜,我们真正需要学习的,是ST官方初始化GPIO的这个方法。学会这个方法,后续反复应用到编程中,才能够不断进步,提升技能。
2.1 定义结构体类型
首先定义一个结构体类型
typedef struct{
}GPIO_InitTypeDef;
再把对应的3个参数放到结构体类型中。
2.2 使用结构体类型定义参数
接下来,就是用结构体类型,定义一个变量,并且给变量参数赋值,再把变量作为参数写到函数中。
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.pin=... ; GPIO_InitStructure.GPIO_Speed=...; GPIO_InitStructure.GPIO_Mode=...;通过这种方式,实现变量的赋值和参数的传递。
后面,在需要实际项目中,可以采用类似的方式进行初始化配置:
typedef struct{} my_Struct_Type;
my_Struct_Type my_Struct;
my_Struct.a=...;
my_Struct.b=...;
上面这些伪代码,是我们学习编程时候,真正需要学习的部分,也是我们真正需要手敲代码的地方。
2.3在keil中获取结构体成员变量的快捷方式
在keil中编辑的时候,我们可以用点.来调出成员变量的快捷方式,这样进行初始化更加方便。
2.4在keil中获取函数参数选项的快捷方式
另外,在有些函数的参数我们记不住的时候,可以采用@ref进行调用
比如如果我们记不住USART_WordLength参数具体是多少,我们可以输入@ref USART_Word_Length,全部可选参数就会列出来。
由于keil的这个卓越功能,可以方便我们在调用库函数时,少输入很多内容,因此,建议如果做STM32开发的话,还是采用KEIL这个编译器。
3.降低代码的耦合性
前面反复提到:学编程不是敲代码,而是整理好代码模块,摸清楚代码之间的耦合性。这里就以GPIO初始化为例,讲解一下,究竟什么是代码的耦合性,如何降低代码的耦合性?
3.1 使用宏定义来降低代码耦合性
宏定义的一个重要作用,就是降低代码的耦合性。下面看下具体例子:
在这个LED_GPIO初始化的代码中,用了3处宏定义。后面如果我们如果在其他项目中,用其他的GPIO做LED点灯或者需要初始化其他的GPIO,就可以只修改宏定义中的内容,而无需修改代码本身。
我们只要找到这里的宏定义,修改这里的宏定义即可,而不需要死记硬背GPIO初始化的步骤。
类似的例子还有很多。所以降低代码耦合度的基本步骤之一,就是通过宏定义,实现软编码,减少硬编码。后续可以通过修改宏定义,方便实现功能移植。
3.2 规范使用头文件
规范,有条理的使用头文件,能够使得代码更加整齐,而且代码的耦合性更低。头文件一般包含下面内容:
1. 最上面一般是宏定义;
2.接下来是结构体类型定义或者枚举类型定义
3.然后是外部使用的全局变量
4.最后是外部使用的函数。
这样,通过宏定义,就可以清楚的确定,哪些代码是.c文件内部使用的,那些代码是暴露给外部使用的。当我们移植代码的时候,根据头文件,按图索骥,即可完成。因此,头文件相当于C文件的地图,便于我们进行查看和梳理 ;
5.避免头文件重复包含
另外值得一提的一个点,就是通过宏定义,避免头文件重复包含。
代码一开始,检查第一句:
#ifndef __LED_H
当第一次包含这个头文件时,发现并没有定义__LED_H,因此会执行第二句
#define __LED_H
当下次其他.c文件再次试图包含此头文件时,会发现__LED_H这个宏已经被定义了,这时就会直接跳转到结尾。
#endif
这样就不会重复包含头文件中的内容。
led点灯代码。代码简单,主要用来验证开发环境。开发环境采用vscode+keil,详见博文-嵌入式文档类资源-CSDN下载
纸上得来终觉浅,绝知此事要躬行!
接下来会一步一步带你完成家庭气象站的开发工作,敬请收藏关注,以免下次找不到了。