打印
[应用相关]

STM32中一些非常重要的C语言知识点汇总

[复制链接]
682|35
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
AD, ST
说在前面的话
一位初学单片机的小伙伴让我推荐C语言书籍,因为C语言基础比较差,想把C语言重新学一遍,再去学单片机,我以前刚学单片机的时候也有这样子的想法。

其实C语言是可以边学单片机边学的,学单片机的一些例程中,遇到不懂的C语言知识,再去查相关的知识点,这样印象才会深刻些。

下面就列出了一些STM32中重要的C语言知识点,初学的小伙伴可以多读几遍,其中大多知识点之前都有写过,这里重新整理一下,更详细地分析解释可以阅读附带的链接。

使用特权

评论回复
沙发
结合国际经验|  楼主 | 2022-2-24 17:24 | 只看该作者
assert_param
断言(assert)就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。

断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。

可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。

使用特权

评论回复
板凳
结合国际经验|  楼主 | 2022-2-24 17:28 | 只看该作者
注意assert()是一个宏,而不是函数。

使用特权

评论回复
地板
结合国际经验|  楼主 | 2022-2-24 17:28 | 只看该作者
在STM32中,常常会看到类似代码:
assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
assert_param(IS_ADC_SINGLE_DIFFERENTIAL(SingleDiff));

使用特权

评论回复
5
结合国际经验|  楼主 | 2022-2-24 17:30 | 只看该作者
这是用来检查函数传入的参数的有效性。STM32中的assert_param默认是不使用的,即:

使用特权

评论回复
6
结合国际经验|  楼主 | 2022-2-24 17:30 | 只看该作者
本帖最后由 结合国际经验 于 2022-2-24 17:37 编辑

如果要使用,需要定义USE_FULL_ASSERT宏,并且需要自己实现assert_failed函数。特别的,使用STM32CubeMX生成代码的话,会在main.c生成:

我们在这进行填充就好。



使用特权

评论回复
7
结合国际经验|  楼主 | 2022-2-24 17:38 | 只看该作者
下面分享一下assert的应用例子:

#include <stdio.h>
#include <assert.h>

int main(void)
{
int a, b, c;
printf("请输入b, c的值:");
scanf("%d %d", &b, &c);
a = b / c;
printf("a = %d", a);
return 0;
}

使用特权

评论回复
8
结合国际经验|  楼主 | 2022-2-24 17:39 | 只看该作者
此处,变量c作为分母是不能等于0,如果我们输入2 0,结果是什么呢?结果是程序会蹦:

使用特权

评论回复
9
结合国际经验|  楼主 | 2022-2-24 17:40 | 只看该作者
这个例子中只有几行代码,我们很快就可以找到程序蹦的原因就是变量c的值为0。但是,如果代码量很大,我们还能这么快的找到问题点吗?

这时候,assert()就派上用场了,以上代码中,我们可以在a = b / c;这句代码之前加上assert(c);这句代码用来判断变量c的有效性。此时,再编译运行,得到的结果为:

使用特权

评论回复
10
结合国际经验|  楼主 | 2022-2-24 17:40 | 只看该作者
可见,程序蹦的同时还会在标准错误流中打印一条错误信息:

Assertion failed:c, file hello.c, line 12

使用特权

评论回复
11
结合国际经验|  楼主 | 2022-2-24 17:41 | 只看该作者
这条信息包含了一些对我们查找bug很有帮助的信息:问题出在变量c,在hello.c文件的第12行。这么一来,我们就可以迅速的定位到问题点了。

这时候细心的朋友会发现,上边我们对assert()的介绍中,有这么一句说明:

如果表达式的值为假,assert()宏就会调用_assert函数在标准错误流中打印一条错误信息,并调用abort()(abort()函数的原型在stdlib.h头文件中)函数终止程序。

使用特权

评论回复
12
结合国际经验|  楼主 | 2022-2-24 17:42 | 只看该作者
所以,针对我们这个例子,我们的assert()宏我们也可以用以下代码来代替:

if (0 == c)
{
puts("c的值不能为0,请重新输入!");
abort();
}

使用特权

评论回复
13
结合国际经验|  楼主 | 2022-2-24 17:42 | 只看该作者
这样,也可以给我们起到提示的作用:

使用特权

评论回复
14
结合国际经验|  楼主 | 2022-2-24 17:43 | 只看该作者
但是,使用assert()至少有几个好处:

1)能自动标识文件和出问题的行号。

2)无需要更改代码就能开启或关闭assert机制(开不开启关系到程序大小的问题)。如果认为已经排除了程序的bug,就可以把下面的宏定义写在包含assert.h的位置的前面:

#define NDEBUG

并重新编译程序,这样编辑器就会禁用工程文件中所有的assert()语句。如果程序又出现问题,可以移除这条#define指令(或把它注释掉),然后重新编译程序,这样就可以重新启用了assert()语句。

使用特权

评论回复
15
结合国际经验|  楼主 | 2022-2-24 17:44 | 只看该作者
预处理指令
1、#error
#error "Please select first the target STM32L4xx device used in your application (in stm32l4xx.h file)"

#error 指令让预处理器发出一条错误信息,并且会中断编译过程。
#error的例子:
#include <stdio.h>

#define  RX_BUF_IDX  100

#if RX_BUF_IDX == 0
static const unsigned int rtl8139_rx_config = 0;
#elif RX_BUF_IDX == 1
static const unsigned int rtl8139_rx_config = 1;
#elif RX_BUF_IDX == 2
static const unsigned int rtl8139_rx_config = 2;
#elif RX_BUF_IDX == 3
static const unsigned int rtl8139_rx_config = 3;
#else
#error "Invalid configuration for 8139_RXBUF_IDX"
#endif

int main(void)
{
printf("hello world\n");
return 0;
}

使用特权

评论回复
16
结合国际经验|  楼主 | 2022-2-24 17:45 | 只看该作者
这段示例代码很简单,当RX_BUF_IDX宏的值不为0~3时,在预处理阶段就会通过#error 指令输出一条错误提示信息:


"Invalid configuration for 8139_RXBUF_IDX"

使用特权

评论回复
17
结合国际经验|  楼主 | 2022-2-24 17:45 | 只看该作者
下面编译看一看结果:

使用特权

评论回复
18
结合国际经验|  楼主 | 2022-2-24 17:47 | 只看该作者
2、#if、#elif、#else、#endif、#ifdef、#ifndef
(1)#if
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
  void (* ConvCpltCallback)(struct __ADC_HandleTypeDef *hadc);            
  // ......
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */


#if的使用一般使用格式如下
#if 整型常量表达式1
  程序段1
#elif 整型常量表达式2
  程序段2
#else
  程序段3
#endif

使用特权

评论回复
评论
结合国际经验 2022-2-24 17:48 回复TA
执行起来就是,如果整形常量表达式为真,则执行程序段1,以此类推,最后#endif是#if的结束标志。 
19
结合国际经验|  楼主 | 2022-2-24 17:48 | 只看该作者
(2)#ifdef、#ifndef

#ifdef HAL_RTC_MODULE_ENABLED
  #include "stm32l4xx_hal_rtc.h"
#endif /* HAL_RTC_MODULE_ENABLED */

使用特权

评论回复
20
结合国际经验|  楼主 | 2022-2-24 17:49 | 只看该作者
#ifdef的作用是判断某个宏是否定义,如果该宏已经定义则执行后面的代码,一般使用格式如下:
#ifdef  宏名
  程序段1
#else
  程序段2
#endif

它的意思是,如果该宏已被定义过,则对程序段1进行编译,否则对程序段2进行编译,通#if一样,#endif也是#ifdef的结束标志。

#ifndef __STM32L4xx_HAL_ADC_EX_H
#define __STM32L4xx_HAL_ADC_EX_H
// ......
#endif


#ifndef的作用与#ifdef的作用相反,用于判断某个宏是否没被定义。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

692

帖子

1

粉丝