你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32开发中常用的C语言知识

[复制链接]
STMCU小助手 发布时间:2022-6-4 15:09
1 位操作
    位操作与位带操作并不相同,位操作就是对一个变量的每一位做运算,而逻辑位操作是对这个变量整体进行运算。
    下面是六种常用的操作运算符:
640.png
按位取反
  1. void test01()
  2. {
  3.   int num = 7;
  4.   printf("~num = %d\n", ~num);//-8

  5. // 0111  按位取反   1000   机器中存放的都是补码   
  6. //补码转换原码需要分有符号数和无符号数两种
  7. }
复制代码


按位与
  1. void test02()
  2. {
  3.   int num = 128;
  4. //换算为八位,1换算就是00000001, 这样只要所给数字的二进制最后一位是1.那么就是奇数,否则就是偶数
  5.   if ( (num & 1) == 0)   
  6.   {
  7.     printf("num为偶数\n");
  8.   }
  9.   else
  10.   {
  11.     printf("num为奇数\n");
  12.   }
  13. }
复制代码

按位异或
  1. void test03()
  2. {
  3.   //按位异或的意思是,两个数字相同为0,不同为1。我们可以利用按位异或实现两个数的交换
  4.   num01 = 1; // 0001
  5.   num02 = 4; // 0100
  6.   printf("num01 ^ num02 = %d", num01 ^ num02); // 5  两个二进制按位异或之后是: 0101

  7.   printf("交换前\n");
  8.   printf("num01 = %d\n", num1);
  9.   printf("num02 = %d\n", num2);

  10.   num01 = num01 ^  num02;
  11.   num02 = num01 ^  num02;
  12.   num01 = num01 ^  num02;
  13.   //不用临时数字实现两个变量交换
  14.   printf("交换后\n");
  15.   printf("num01 = %d\n", num1);
  16.   printf("num02 = %d\n", num2);
  17. }
复制代码

按位或
    计算方法:
    参加运算的两个数,换算为二进制(0、1)后,进行与运算。只有当 相应位上全部为1时取1, 存在0时为0。
    printf是格式化输出函数,它可以直接打印十进制,八进制,十六进制,输出控制符分别为%d, %o, %x, 但是它不存在二进制,如果输出二进制,可以手写,但是也可以调用stdlib.h里面的itoa函数,他不是标准库里面的函数,但是大多数编译器里面都有这个函数。
  1. #include <stdio.h>
  2. #include <stdlib.h>

  3. int main()
  4. {
  5.   test04();   
  6. }

  7. int test04()
  8. {
  9.     int a = 6;                  //二进制0110
  10.     int b = 3;                  //二进制0011
  11.     int c = a | b;              //a、b按位或,结果8,二进制111,赋值给c
  12.     char s[10];
  13.     itoa(c, s, 2);
  14.     printf("二进制 --> %s\n", s);//输出:二进制 -->111
  15. }
复制代码


左移运算符
  1. void test05()
  2. {
  3.   int num = 6;
  4.   printf("%d\n", num << 3);//左移三位,就是0000
  5. }
复制代码


右移运算符
  1. void test06()
  2. {
  3.   int num = 6; //0110
  4.   printf("%d\n", num >> 1); //右移一位,就是0011,输出3
  5. }
复制代码


    上面是用普通c代码举得栗子,下面我们看一下STM32中操作通常用的代码:
    (1)比如我要改变 GPIOA-> BSRRL 的状态,可以先对寄存器的值进行& 清零操作
  1. GPIOA-> BSRRL &= 0xFF0F; //将第4位到第7位清零(注意编号是从0开始的)
复制代码

    然后再与需要设置的值进行|或运算:
  1. GPIOA-> BSRRL |= 0x0040; //将第4位到第7位设置为我们需要的数字
复制代码

    (2)通过位移操作提高代码的可读性:
  1. GPIOx->ODR = (((uint32_t)0x01) << pinpos);
复制代码

    上面这行代码的意思就是,先将"0x01"这个八位十六进制转换为三十二位二进制,然后左移"pinpos"位,这个"pinpos"就是一个变量,其值就是要移动的位数。也就是将ODR寄存器的第pinpos位设置为1。
    (3)取反操作使用:
    SR寄存器的每一位代表一个状态,如果某个时刻我们想设置一个位的值为0,与此同时,其它位置都为1,简单的作法是直接给寄存器设置一个值:
  1. TIMx->SR=0xFFF7;
复制代码

    这样的作法设置第 3 位为 0,但是这样的作法可读性较差。看看库函数代码中怎样使用的:
  1. TIMx->SR = (uint16_t)~TIM_FLAG;
复制代码

    而 TIM_FLAG 是通过宏定义定义的值:
  1. #define TIM_FLAG_Update                    ((uint16_t)0x0001)
  2. #define TIM_FLAG_CC1                       ((uint16_t)0x0002)
复制代码


2 define宏定义
    define 是 C 语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供 方便。
    常见的格式:
  1. #define 标识符 字符串
复制代码

    标识符意思是所定义的宏名,字符串可以是常数、表达式或者格式串等,例如:
  1. #define PLL_Q 7  //注意,这个定义语句的最后不需要加分号
复制代码

3 ifdef条件编译
    在程序开发的过程中,经常会用到这种条件编译:
  1. #ifdef PLL_Q
  2.    程序段1
  3. #else
  4.   程序段2
  5. #endif
复制代码
    上面这段代码作用就是当这个标识符已经被定义过,那么就进行程序程序段1,如果没有则进行程序段2。当然,和我们设计普通的c代码是一样的,"#else"也可以没有,就是上面的代码减去"#else"和程序段2。
  1. #ifndef PLL_Q    //意思就是如果没有定义这个标识符
复制代码

4 extern变量申明
    C 语言中 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义(一个变量只能定义一次,而extern可以申明很多次)使用例子如下:

extern u16 USART_RX_STA;
    上面例子意思就是申明 “USART_RX_STA” 这个变量在其他文件中已经定义了,"u16"的意思是16位的。

5 结构体
    定义一个结构体的一般形式为:
  1. struct 结构名
  2. {
  3.   成员列表
  4. };
复制代码

    成员列表由若干个成员组成,每个成员都是该结构体的一个组成部分。对每个成员也必须作类型说明,其形式:
  1. 类型说明符  成员名;//比如:int num;
复制代码

    结合上面的说明,我们可以构建一个简单的结构体例子:
  1. struct sutdent
  2. {
  3.   int num;
  4.   char name[20];  //20个字节长的字符
  5.   char sex;
  6.   int age;
  7.   float score;
  8.   char addr[30]; //30个字节长的字符
  9. }
复制代码


    而如果我们想定义结构体变量,那么我们在定义这个结构体的时候直接定义,或者定义完结构体再另外定义结构体变量,比如:
  1. struct sutdent
  2. {
  3.   int num;
  4.   char name[20];  //20个字节长的字符
  5.   char sex;
  6.   int age;
  7.   float score;
  8.   char addr[30]; //30个字节长的字符
  9. }student01,student02; //变量名表列(如果由结构体变量名,那么我们可以不写结构体名称)
复制代码

    有时候我们可能需要用到结构体的嵌套,比如:
  1. struct date
  2. {
  3.   int year, month,day;
  4. };
  5. struct sutdent
  6. {
  7.   int num;
  8.   char name[20];  //20个字节长的字符
  9.   char sex;
  10.   struct date birthday; //这里就用到了结构体的嵌套
  11.   int age;
  12.   float score;
  13.   char addr[30]; //30个字节长的字符
  14. }student01,student02; //变量名表列(如果由结构体变量名,那么我们可以不写结构体名称)
复制代码

    如果需要引用结构体里面的成员内容,可以使用下面的方式:
  1. student01.name = 小李;
  2. // 结构体变量名.成员名(注意这里用的是点),这里是对这个成员的赋值
复制代码

    结构指针变量说明的一般形式为:
  1. struct 结构名 *结构指针变量名
复制代码

    假如说我们想定义一个指向结构体"student"的指针变量pstu,那么我们可以使用如下代码:
  1. struct student *pstu;
复制代码

    如果我们要给一个结构体指针变量赋初值,那么我们可以使用如下的方式:
  1. struct student
  2. {
  3.   char name[66];
  4.   int num;
  5.   char sex;
  6. }stu;


  7. pstu = &stu;
复制代码

    注意上边的赋值方式,我们如果要进行赋值,那必须使用结构体变量,而不能使用结构体名,像下边这样就是错误的。
  1. struct student
  2. {
  3.   char name[66];
  4.   int num;
  5.   char sex;
  6. }stu;


  7. pstu = &student;
复制代码

    这是因为结构名和结构体变量是两个不同的概念,结构名只能表示一个结构形式,编译系统并不会给它分配内存空间(就是说不会给它分配地址),而结构体变量作为一个变量,编译系统会给它分配一个内存空间来存储。
访问结构体成员的一般形式:
  1. (*pstu).name;   //(1)(*结构指针变量).成员名;
  2. pstu->name;   //(2)结构指针变量->成员名
复制代码
    结构体的知识就简单说上边这些。
6 typedef类型别名
    typedef用来为现有类型创建一个新的名字,或者称为类型别名,用来简化变量的定义(上边extern变量申明的例子中,"u16"就是对"uint16_t"类型名称的简化)。typedef在MDK中用得最多的就是定义结构体的类型别名和枚举类型。
    我们定义一个结构体GPIO:
G_L7~%JN`QSARXKVP)1Y]GY.png
    定义这样一个结构体以后,如果我们想定义一个结构体变量比如"GPIOA",那么我们需要使用这样的代码:
  1. struct _GPIO GPIOA;
复制代码

    虽然也可以达到我们的目的,但是这样会比较麻烦,而且在MDK中会有很多地方用到,所以,我们可以使用"typedef"为其定义一个别名,这样直接通过这个别名就可以定义结构体变量,来达到我们的目的:
  1. typedef struct
  2. {
  3.   _IO uint32_t MODER;
  4.   _IO uint32_t OTYPER;
  5. }GPIO_typedef;
复制代码

    这样定义完成之后,如果我们需要定义结构体变量,那么我们只需要这样:
  1. GPIO_typedef _GPIOA,_GPIOB;
复制代码

收藏 评论0 发布时间:2022-6-4 15:09

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版