STM32学习中比较常见的一些C语言基础知识

C语言是单片机开发中的必备基础知识,本文列举了部分STM32学习中比较常见的一些C语言基础知识。移位操作在单片机开发中非常重要,下面是delay_init函数的一行代码:单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。

C语言是单片机开发必不可少的基础知识。 本文列出了STM32学习中一些常用的C语言基础知识。

1 位操作

下面我们先讲解几个位运算符,然后讲解使用位运算的技巧。 C语言支持以下六位运算:

STM32学习中比较常见的一些C语言基础知识

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

嵌入式物联网确实有很多东西需要学习。 不要学错路线和内容,导致你的薪资水涨船高!

我免费给大家分享一个数据包,差不多150G。 学习内容、面试、项目都比较新、全面! 据估计,在网上购买某种鱼至少要花费几十美元。

点此找助手0元获取:嵌入式物联网学习资料(今日头条)

STM32学习中比较常见的一些C语言基础知识

STM32学习中比较常见的一些C语言基础知识

1.1 设置某些位的值而不改变其他位的值

这种场景在单片机开发中经常使用。 方法是我们先使用&运算符清除需要设置的位,然后使用| 运算符来设置值。

例如,如果我想改变GPIOA的状态,我可以先清除寄存器值:

然后与需要设置的值进行|OR运算:

1.2 移位操作提高代码可读性

移位操作在微控制器开发中非常重要。 下面是delay_init函数的一行代码:

SysTick->CTRL |= 1 << 1;

这个操作就是将CTRL寄存器的第一位(从0开始计数)设置为1,为什么我们需要左移而不是直接设置一个固定值呢?

其实这是为了提高代码的可读性和可重用性。 这行代码可以非常直观清晰,将bit 1设置为1。如果写成:

SysTick->CTRL |= 0X0002;

这样虽然可以达到同样的效果,但是可读性稍差,修改起来也比较麻烦。

1.3 ~ 使用按位取反运算的技巧

按位取反常用于设置寄存器时,常用于清除一位/几位。 下面是delay_us函数的一行代码:

SysTick->CTRL &= ~(1 << 0) ;    /* 关闭SYSTICK */

这段代码可以理解为:只将CTRL寄存器的第0位(最低位)设置为0,其他位的值不变。

同样,我们不使用按位取反,将代码写为:

SysTick->CTRL &= 0XFFFFFFFE;        /* 关闭SYSTICK */

可见前者的可读性和可维护性比后者要好很多。

1.4 ^使用按位异或运算的技巧

该函数非常适合控制某个位的翻转。 一个常见的应用场景是控制LED闪烁,如下:

GPIOB->ODR ^= 1 << 5;

执行一次该代码将翻转PB5的输出状态一次。 如果我们的 LED 连接到 PB5,我们可以看到 LED 闪烁。

2 定义宏定义

Define是C语言中的预处理命令。 用于宏定义(定义常量),可以提高源代码的可读性,为编程提供方便。 常见格式:

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

定义的标识符HSE_VALUE的值为8000000,数字后面的U表示无符号。 至于关于define宏定义的其他知识,比如带参数的宏定义,这里不再赘述。

3 ifdef条件编译

在单片机程序开发过程中,我们经常会遇到这样的情况:满足某个条件时编译一组语句,不满足条件时编译另一组语句。

条件编译命令最常见的形式是:

#ifdef 标识符    程序段1#else    程序段2#endif

其作用是:当标识符已经定义(一般用#define命令定义)时,编译程序段1,否则编译程序段2。

#else部分不需要包含,即:

  #ifdef    程序段1    #endif

条件编译在HAL库中被大量使用。 在stm32mp1xx_hal_conf.h头文件中经常可以看到这样的语句:

#if !defined  (HSE_VALUE)      #define HSE_VALUE       24000000U    #endif

如果没有定义HSE_VALUE宏,则定义HSE_VALUE宏,HSE_VALUE的值为24000000U。 条件编译也是C语言的基础知识。

这里提一下,24000000U中的U代表无符号整数类型。 通常,UL表示无符号长整型,F表示浮点型。

这里加上U后,系统在编译时就不会进行类型检查,直接将值以U的形式赋给对应的内存,如果超出了定义的变量范围,就会被拦截。

4 外部变量声明

在C语言中,extern可以放在变量或函数之前,表示该变量或函数的定义在另一个文件中,提示编译器遇到这个变量或函数时到其他模块中查找其定义。

这里需要注意的是,extern变量可以声明多次,但只能定义一次。 在我们的代码中你会看到这样的语句:

extern uint16_t g_usart_rx_sta;

该语句声明g_usart_rx_sta变量已在其他文件中定义,并将在此处使用。

所以,你肯定可以在某处找到带有变量定义的语句:

 uint16_t g_usart_rx_sta;

extern的使用比较简单,但是使用频繁,需要掌握。

5 typedef 类型别名

Typedef 用于为现有类型创建新名称或类型别名,以简化变量的定义。 Typedef 在 HAL 库中最常用于定义结构的类型别名和枚举类型。

struct _GPIO    {        __IO uint32_t CRL;        __IO uint32_t CRH;        …    };

定义了一个结构体GPIO,所以我们定义结构体变量的方式是:

struct  _GPIO  gpiox;       /* 定义结构体变量gpiox */

但这是非常麻烦的。 HAL库中有很多这样的结构体变量需要定义。

这里我们可以为结构体定义一个别名GPIO_TypeDef,这样我们就可以在其他地方通过别名GPIO_TypeDef定义结构体变量。 方法如下:

typedef struct    {            __IO uint32_t CRL;            __IO uint32_t CRH;            …    } GPIO_TypeDef;

Typedef为该结构体定义了一个别名GPIO_TypeDef,这样我们就可以通过GPIO_TypeDef定义结构体变量:

GPIO_TypeDef gpiox;

这里的 GPIO_TypeDef 和 struct _GPIO 的功能是一样的,但是 GPIO_TypeDef 使用起来方便很多。

单片机

基于单片机的电子时钟答辩按键时钟复位电路-上海怡健

2024-4-6 1:11:26

单片机

单片机的应用属于芯片级应用,你知道吗?

2024-4-6 2:05:00

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索