自学内容网 自学内容网

单片机(STM32)开发中常用的C语言基础语法(一)

1、sprintf函数

sprintf 是一个在 C 语言中常用的函数,用于将格式化的数据写入字符串中。它的函数原型如下:

sprintf函数会将传递给它的可变参数按照指定的格式进行格式化,并将结果写入str` 指向的字符串中。返回值是写入字符串的字符数(不包括字符串末尾的空字符)。

sprintf 函数在 C 语言中是标准库函数,它定义在 stdio.h 头文件中。

[!TIP] 注:在sprintf函数中打印百分号(%),您需要使用两个百分号(%%)。这是因为在sprintf函数中,百分号被用作转义字符,表示要插入格式化输出。

#include <stdio.h>  
  
int main() {  
    char buffer[100];  
    int a = 10;  
    float b = 3.14;  
    char c = 'd';  
    sprintf(buffer, "整数:%d, 浮点数:%f, 字符:%c", a, b, c);  
    printf("%s\n", buffer);  
    return 0;  
}

输出结果

整数:10, 浮点数:3.140000, 字符:d

sprintf所写入的字符串为char类型。而一些屏幕驱动显示函数所需的参量类型为uint8_t。建议强制类型转换以避免warning。一般情况下实测该warning不影响程序运行。

LCD_DisplayStringLine(u8 Line, u8 *ptr)

一种规范的写法:

   char text[30];
   unsigned int i=5;
   sprintf(text,"CNDR:%d%%   ",i);

   LCD_DisplayStringLine(Line9,(unsigned char*)text);

2、bool布尔类型

在C语言中,布尔类型是一种基本的数据类型,用于表示逻辑值truefalse。 在C99标准及其之后的标准中,C语言在<stdbool.h>头文件中提供了内置的布尔类型支持。当你包含这个头文件时,你可以使用bool关键字来声明布尔变量,使用true和false来表示布尔值。例如:

#include <stdbool.h>  
  
bool a = true;  
bool b = false;

请注意,truefalse在C99中是关键字,它们的值分别是1和0。同时,bool实际上是一个宏,通常被定义为_Bool或者int。

3、结构体struct与typedef struct

struct和typedef struct都是用来定义结构体的关键字

struct Student {  
  int id;  
  char name[50];  
  int age;  
};
struct Student stu1;  
stu1.id = 1;  
stu1.name = "John Doe";  
stu1.age = 20;

在这个例子中,我们定义了一个名为Student的结构体,它包含三个成员:idname,和age。其中,id是一个整数,name是一个字符数组(可以存储一个长度为50的字符串),age也是一个整数,创建了一个名为stu1Student结构体实例,并为其成员赋值。

在C语言中,使用typedefstruct来定义结构体类型的语法如下:

typedef struct {  
    // 成员变量定义  
    // ...  
} 结构体别名;

typedef struct StructName {  
    // 成员变量定义  
    // ...  
} StructName;

/*下面举一个例子*/

#include <stdio.h>  
  
typedef struct {  
    char name[50];  
    int age;  
} Person;  
  
int main() {  
    Person p1;  // 使用别名声明结构体实例  
    p1.age = 25;  
    printf("Name: %s\n", p1.name);  
    printf("Age: %d\n", p1.age);  
    return 0;  
}

在上述示例中,我们定义了一个名为Person的结构体类型,并使用别名Person来声明结构体实例p1。然后,我们可以使用点号.来访问结构体成员变量,例如p1.namep1.age

结构体指针成员变量引用方法是通过“->”符号实现,比如要访问 usart3 结构体指针指向的结 构体的成员变量 BaudRate,方法是:Usart3->BaudRate;

4、弱定义extern,__weak

在C语言中,弱定义是一种允许同一个符号(变量或函数)在多个源文件中被定义,但在链接时只有一个定义会被保留的定义方式。弱定义使用extern关键字来声明变量或函数,并且在声明后面不跟任何分号。

下面是一个使用弱定义的示例:

假设我们有一个全局变量global_var,需要在多个源文件中共享。我们可以将其声明为弱变量,并在其中一个源文件中定义它:

// 在头文件中声明弱变量  
extern int global_var;  
  
// 在源文件1.c中定义弱变量  
int global_var = 10;

在其他源文件中,我们可以使用extern关键字来引用global_var变量,例如:

// 在源文件2.c中引用弱变量  
extern int global_var;  
  
void func() {  
  printf("The value of global_var is %d\n", global_var);  
}

在链接时,链接器会选择其中一个定义作为最终的定义,其他定义将被忽略。因此,在最终的可执行程序中,只有一个global_var变量会被定义,并且可以被所有源文件访问。

需要注意的是,弱定义只能用于变量或函数,不能用于函数参数、结构体、枚举等其他类型。此外,弱定义必须保证只有一个强定义(使用static关键字定义的变量或函数),否则会导致链接错误。

[!CAUTION] 需要注意的是:extern int global_var=10;此类写法是不允许的。

__weak关键字用于修饰函数或变量,表示该函数或变量是弱定义的。

大部分中断回调函数都被__weak关键字修饰

具体来说,当你在代码中声明一个弱定义的函数或变量时,如果你没有在其它地方定义这个函数或变量,编译器会报错。

而使用__weak关键字可以告诉编译器,这个函数或变量是弱定义的,如果在其它地方没有定义,则使用这个弱定义的函数或变量。

__weak关键字通常用于在不同的模块之间共享函数或变量,特别是在嵌入式系统中,不同的模块可能会使用相同的函数或变量名,为了避免冲突,使用弱定义是一种有效的解决方法。

需要注意的是,__weak关键字并不是C语言的标准化关键字,而是特定编译器或环境提供的扩展。

5、__IO定义

__IO是一个在嵌入式C语言中常见的关键字,特别是在与硬件相关的编程中。__IO通常用于指定一个变量或地址空间为输入输出(IO)空间。

在大多数处理器架构中,内存被划分为几个不同的空间,例如:RAM、ROM、IO空间等。__IO关键字用于告诉编译器,某个变量或指针引用的地址位于IO空间,而不是常规的RAM或ROM空间。

例如,当您在与硬件寄存器交互时,这些寄存器通常位于特殊的IO地址空间。在这种情况下,您可能会使用__IO关键字来定义一个指向这些寄存器的指针:

__IO uint32_t* register_address = (uint32_t*)0x40000000;

在这个例子中,register_address是一个指向地址0x40000000的指针,并且这个地址被指定为IO空间。这样编译器就知道,当访问这个指针时,需要生成适用于访问IO空间的机器代码。

__IO并不是C语言的标准关键字,而是特定于某些编译器和架构的扩展。

6、 ifdef 条件编译

在C语言中,#ifdef是一个预处理指令,它用于进行条件编译。#ifdef后面跟着一个宏名称,如果这个宏被定义了,那么#ifdef后面的代码就会被编译进去,否则这部分代码会被忽略。

下面是一个简单的例子:

#include <stdio.h>  
  
#define FEATURE_A  
  
int main() {  
    #ifdef FEATURE_A  
        printf("Feature A is enabled.\n");  
    #else  
        printf("Feature A is not enabled.\n");  
    #endif  
    return 0;  
}

在这个例子中,如果宏FEATURE_A被定义了,那么程序会输出"Feature A is enabled."。如果宏FEATURE_A没有被定义,那么程序会输出"Feature A is not enabled."。

在实际开发中,我们通常会使用#ifdef来检查某些编译选项或者平台特性是否被定义,然后根据这些条件来选择性地编译代码。

7、printf串口重定向

这段 printf 函数支持的代码在初始化串口后使用,这段代码加入之后便可以通 过 printf 函数向串口发送我们需要的内容。

代码以STM32的HAL库版本作为示例:

//加入以下代码,支持 printf 函数,而不需要选择 use MicroLIB 标准库版本
#if 1
#pragma import(__use_no_semihosting) 
//标准库需要的支持函数 
struct __FILE 
{ 
int handle; 
}; 
FILE __stdout; 
//定义_sys_exit()以避免使用半主机模式
_sys _exit(int x) 
{ 
x = x; 
} 
//重定义 fputc 函数
int fputc(int ch, FILE *f)
{ 
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); 
 USART_SendData(USART1,(uint8_t)ch); 
return ch;
}
#endif

//HAL库版本
#include <stdio.h>//添加头文件
int fputc(int ch, FILE *f)//在串口文件中添加这段
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}

8、switch case多路分支语法

switch语句是一种多路选择结构,可以根据不同的条件选择不同的执行路径。

switch (expression) {  
   case constant1:  
      // 代码块1  
      break;  
   case constant2:  
      // 代码块2  
      break;  
   ...  
   default:  
      // 默认代码块  
}

这是最简单和最常用的switch语句结构。这里,expression是要评估的表达式,constant1constant2等是可能的值。如果expression的值等于某个case后面的常量,则执行相应的代码块。break语句用于退出switch语句。如果没有break,程序将继续执行下一个case


原文地址:https://blog.csdn.net/2408_88910159/article/details/144024458

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!