欢迎来到军工软件开发人才培养基地——学到牛牛

STM32开发中的C语言

时间:2024-05-06 07:01:10 来源:学到牛牛

 

1.指针和内存

假如我们知道一个寄存器GPIOH_ODR的地址是 0x4002 1C14, 该寄存器是32bit,低16bit有效,对应着 16 个外部 IO,对应位写0/1代表输出低/高电平。我们可以通过指针去访问该地址,从而控制IO口输出。例如:

    unsigned int *p = ( unsigned int * )( 0x4002 1C14 );

    *p = 0xFFFF;

 亦可简写为:

*(volatile unsigned int *)( 0x4002 1C14) = 0xFFFF;

 

我们还可以通过宏定义的方式来操作:

#define GPIOH_ODR  (*(volatile unsigned int *)(0x4002 1C14))

GPIOH_ODR=0xFFFF;

2.#if

#if和#endif是一组同时使用的,叫做条件编译指令。#if与#define、#include等指令一样是由预处理器这个强大的工具处理的,预处理器可以在编译前处理c程序。在STM32中,相近平台的编程处理相近,一个库可能支持多个平台,但是平台间也有差异,通过条件编译指令进行编译选择。

1.png

图片1.png

3.#define

#define是 C语言 和 C++ 中的一个预处理指令,其中的“#”表示这是一条预处理命令·。凡是以“#”开头的均为预处理命令,“define”为宏定义命令,“标识符”为所定义的宏名。#define的部分运行效果类似于word中的ctrl+F替换,与常量const相比有着无法替代的优势。

2.png

3.png

 

4.结构体内存对齐

一组寄存器(同一外设下的不同功能寄存器)具有相同的基地址,不同的偏移地址。比如:每个通用 I/O 端口包括 4 个 32 位配置寄存器( GPIOx_MODER、 GPIOx_OTYPER、GPIOx_OSPEEDR 和 GPIOx_PUPDR)、 2 个 32 位数据寄存器(GPIOx_IDR 和GPIOx_ODR)、 1 个 32 位置位/复位寄存器 (GPIOx_BSRR)、 1 个 32 位锁定寄存器(GPIOx_LCKR) 和 2 个 32 位复用功能选择寄存器( GPIOx_AFRH 和 GPIOx_AFRL)。

同组GPIO的基地址相同,但是偏移地址不同。如下图:

4.png

常规映射可通过:

#define GPIOH_MODER  (*(volatile unsigned int *)(0x4002 1C14 + 0x00 ))

#define GPIOH_OTYPER  (*(volatile unsigned int *)(0x4002 1C14 + 0x04 ))

#define GPIOH_OSPEEDR  (*(volatile unsigned int *)(0x4002 1C14 + 0x08)

#define GPIOH_PUPDR (*(volatile unsigned int *)(0x4002 1C14 + 0x0C ))

……

此方法可以解决映射,但是操作繁琐不便。

可通过寄存器分组及内存对齐,以组为一个对象抽象结构体,通过内存对齐,确定排序方式及单属性字节长度。通过结构体指针直接访问基地址,即解决映射问题,后期访问也方便。如下图:

5.png

6.png

 

5.enum

enum是计算机编程语言中的一种数据类型。枚举类型:在实际问题中,有些变量的取值被限定在一个有限的范围内。取值范围闭环,并结合其他方式判断取值是否合法。

7.png

8.png

9.png

6.typedef

C语言允许为一个数据类型起一个新的别名,就像给人起“绰号”一样。起别名的目的不是为了提高程序运行效率,而是为了编码方便,或者简化复杂类型的理解。例如有一个结构体的名字是 student,要想定义一个结构体变量就得这样写:

struct student STU;

或者:

10.png

11.png

 

7.结构体传参

在函数参数传递过程中,如果函数有多个参数需要传递,并且参数之间存在某种关联时,一般定义一个传参结构体进行参数传递。下面我们以GPIO为例进行说明。

每个通用 I/O 端口包括 4 个 32 位配置寄存器( GPIOx_MODER、 GPIOx_OTYPER、GPIOx_OSPEEDR 和 GPIOx_PUPDR),分别控制工作方式、开漏推挽、上拉下拉、工作速度等。

如果需要进行IO口初始化,那么函数最基本需要四个输入参数。比如:

void GPIO_Init(GPIO_TypeDef* GPIOx, int moder, int otyper, int ospeedr, int pupdr )

更好的方式采用:

// 传参结构体

111.png

 

// 函数定义

222.png

 

 

8.位运算

6.1 位清零操作(某些位清零,其他位不变)

GPIOH_ODR &= ~(1<<10); // 第10位清零

GPIOH_ODR &= ~(3<<10); // 第11、12位清零

GPIOH_ODR &= ~((1<<10)|(3<<15)); //第10、15、16位清零

6.2 位置一操作(某些位置一,其他位不变)

GPIOH_ODR |= (1<<10); // 第10位置一

GPIOH_ODR |= (3<<10); // 第11、12位置一

GPIOH_ODR |= ((1<<10)|(3<<15)); //第10、15、16位置一

6.3 寄存器赋值操作

    寄存器位经过上面的清零操作后,接下来就可以方便地对某几位写入所需要的数值了,且其它位不变。

GPIOH_ODR |= (2<<10); // 第11、12位赋值为2(先清零)

6.4位取反操作

  某些情况下,我们需要对寄存器的某个位进行取反操作,即 1 变 0 , 0 变 1,这可以直接用如下操作,其它位不变。

GPIOH_ODR ^= (1<<10); // 第10位取反