stm32f4 hal 4位数码管_「正点原子NANO STM32开发板资料连载」第四章 STM32F4 基础入门...

1)实验平台:ALIENTEK NANO STM32F411 V1开发板

2)摘自《正点原子STM32F4 开发指南(HAL 库版》关注官方微信号公众号,获取更多资料:正点原子

b3ba85266d54c17b8521a482f6b015ae.png

第四章 STM32F4 开发基础知识入门

这一章,我们将着重 STM32 开发的一些基础知识,让大家对 STM32 开发有一个初步的了

解,为后面 STM32 的学习做一个铺垫,方便后面的学习。这一章的内容大家第一次看的时候

可以只了解一个大概,后面需要用到这方面的知识的时候再回过头来仔细看看。这章我们分 7

个小结,

·4.1 MDK 下 C 语言基础复习

·4.2 STM32F4 系统架构

·4.3 STM32F411 时钟系统

·4.4 端口复用和重映射

·4.5 STM32F4 NVIC 中断管理

·4.6 MDK 中寄存器地址名称映射分析

·4.7 MDK 固件库快速开发技巧

·4.8 手把手教你入门 STM32CubeMX 图像配置工具

4.1 MDK 下 C 语言基础复习

这一节我们主要讲解一下 C 语言基础知识。C 语言知识博大精深,也不是我们三言两语能

讲解清楚,同时我们相信学 STM32 这种级别 MCU 的用户,C 语言基础应该都是没问题的。我们

这里主要是简单的复习一下几个 C 语言基础知识点,引导那些 C 语言基础知识不是很扎实的用

户能够快速开发 STM32 程序。同时希望这些用户能够多去复习一下 C 语言基础知识,C 语言毕

竟是单片机开发中的必备基础知识。对于 C 语言基础比较扎实的用户,这部分知识可以忽略不

看。

4.1.1 位操作

C 语言位操作相信学过 C 语言的人都不陌生了,简而言之,就是对基本类型变量可以在位级

别进行操作。这节的内容很多朋友都应该很熟练了,我这里也就点到为止,不深入探讨。下面

我们先讲解几种位操作符,然后讲解位操作使用技巧。

C 语言支持如下 6 中位操作

e3d0468f20e8dbbdf0ff301e0ef98664.png

这些与或非,取反,异或,右移,左移这些到底怎么回事,这里我们就不多做详细,相信

大家学 C 语言的时候都学习过了。如果不懂的话,可以百度一下,非常多的知识讲解这些操作

符。下面我们想着重讲解位操作在单片机开发中的一些实用技巧。

1) 不改变其他位的值的状况下,对某几个位进行设值。

这个场景单片机开发中经常使用,方法就是先对需要设置的位用&操作符进行清零操作,

然后用|操作符设值。比如我要改变 GPIOA 的状态,可以先对寄存器的值进行&清零操作

GPIOA->CRL&=0XFFFFFF0F; //将第 4-7 位清 0

然后再与需要设置的值进行|或运算

GPIOA->CRL|=0X00000040;

//设置相应位的值,不改变其他位的值

2) 移位操作提高代码的可读性。

移位操作在单片机开发中也非常重要,下面让我们看看固件库的 GPIO 初始化的函数里

面的一行代码

GPIOx->BSRR = (((uint32_t)0x01) << pinpos);

这个操作就是将 BSRR 寄存器的第 pinpos 位设置为 1,为什么要通过左移而不是直接设

置一个固定的值呢?其实,这是为了提高代码的可读性以及可重用性。这行代码可以

很直观明了的知道,是将第 pinpos 位设置为 1。如果你写成

GPIOx->BSRR =0x0030;

这样的代码就不好看也不好重用了。

类似这样的代码很多:

GPIOA->ODR|=1<<5;

//PA.5 输出高,不改变其他位

这样我们一目了然,5 告诉我们是第 5 位也就是第 6 个端口,1 告诉我们是设置为 1 了。

3) ~取反操作使用技巧

SR 寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为 0,同时

其他位都保留为 1,简单的作法是直接给寄存器设置一个值:

TIMx->SR=0xFFF7;

这样的作法设置第 3 位为 0,但是这样的作法同样不好看,并且可读性很差。看看库函数

代码中怎样使用的:

TIMx->SR = (uint16_t)~TIM_FLAG;

而 TIM_FLAG 是通过宏定义定义的值:

#define TIM_FLAG_Update

((uint16_t)0x0001)

#define TIM_FLAG_CC1

((uint16_t)0x0002)

看这个应该很容易明白,可以直接从宏定义中看出 TIM_FLAG_Update 就是设置的第 0 位了,

可读性非常强。

4.1.2 define 宏定义

define 是 C 语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供

方便。常见的格式:

#define 标识符 字符串

“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。例如:

#define SYSCLK_FREQ_72MHz 72000000

定义标识符 SYSCLK_FREQ_72MHz 的值为 72000000。

至于 define 宏定义的其他一些知识,比如宏定义带参数这里我们就不多讲解。

4.1.3 ifdef 条件编译

单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而

当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:

#ifdef 标识符

程序段 1

#else

程序段 2

#endif

它的作用是:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编

译,否则编译程序段 2。 其中#else 部分也可以没有,即:

#ifdef

程序段 1

#endif

这个条件编译在 MDK 里面是用得很多的,在 stm32f10x.h 这个头文件中经常会看到这样的

语句:

#ifdef STM32F10X_HD

大容量芯片需要的一些变量定义

#end

而 STM32F10X_HD 则是我们通过#define 来定义的。条件编译也是 c 语言的基础知识,这里

也就点到为止吧。

4.1.4 extern 变量申明

C 语言中 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编

译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于 extern 申明变量可以多

次,但定义只有一次。在我们的代码中你会看到看到这样的语句:

extern u16 USART_RX_STA;

这个语句是申明 USART_RX_STA 变量在其他文件中已经定义了,在这里要使用到。所以,你

肯定可以找到在某个地方有变量定义的语句:

u16 USART_RX_STA;

下面通过一个例子说明一下使用方法。

在 Main.c 定义的全局变量 id,id 的初始化都是在 Main.c 里面进行的。

Main.c 文件

u8 id;//定义只允许一次

main()

{

id=1;

printf("d%",id);//id=1

test();

printf("d%",id);//id=2

}

但是我们希望在 test.c 的 changeId(void)函数中使用变量 id,这个时候我们就需要在

test.c 里面去申明变量 id是外部定义的了,因为如果不申明,变量 id的作用域是到不了 test.c

文件中。看下面 test.c 中的代码:

extern u8 id;//申明变量 id 是在外部定义的,申明可以在很多个文件中进行

void test(void){

id=2;

}

在 test.c 中申明变量 id 在外部定义,然后在 test.c 中就可以使用变量 id 了。

对于 extern 申明函数在外部定义的应用,这里我们就不多讲解了。

4.1.5 typedef 类型别名

typedef 用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。

typedef 在 MDK 用得最多的就是定义结构体的类型别名和枚举类型了。

struct _GPIO

{

__IO uint32_t CRL;

__IO uint32_t CRH;

};

定义了一个结构体 GPIO,这样我们定义变量的方式为:

struct _GPIO GPIOA;//定义结构体变量 GPIOA

但是这样很繁琐,MDK 中有很多这样的结构体变量需要定义。这里我们可以为结体定义一

个别名 GPIO_TypeDef,这样我们就可以在其他地方通过别名 GPIO_TypeDef 来定义结构体变量

了。方法如下:

typedef struct

{

__IO uint32_t CRL;

__IO uint32_t CRH;

} GPIO_TypeDef;

Typedef 为结构体定义一个别名 GPIO_TypeDef,这样我们可以通过 GPIO_TypeDef 来定义结

构体变量:

GPIO_TypeDef _GPIOA,_GPIOB;

这里的 GPIO_TypeDef 就跟 struct _GPIO 是等同的作用了。 这样是不是方便很多?

4.1.6 结构体

经常很多用户提到,他们对结构体使用不是很熟悉,但是 MDK 中太多地方使用结构体以及

结构体指针,这让他们一下子摸不着头脑,学习 STM32 的积极性大大降低,其实结构体并不是

那么复杂,这里我们稍微提一下结构体的一些知识,还有一些知识我们会在下一节的“寄存器

地址名称映射分析”中讲到一些。

声明结构体类型:

Struct 结构体名{

成员列表;

}变量名列表;

例如:

Struct G_TYPE {

uint32_t Pin;

uint32_t Mode;

uint32_t Speed;

}GPIOA,GPIOB;

在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:

Struct 结构体名字

结构体变量列表 ;

例如:struct G_TYPE GPIOA,GPIOB;

结构体成员变量的引用方法是:

结构体变量名字.成员名

比如要引用 GPIOA 的成员

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值