STM32编程中枚举和结构体的结合

时间:2022-06-28 04:23:21

STM32编程中枚举和结构体的结合

01 结构体定义

基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法访问修改内部变量。

结构体的定义:

第一种:只有结构体定义

  1. struct stuff{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. };

第二种:附加该结构体类型的“结构体变量”的初始化的结构体定义,如下代码也就是定义结构体时,直接定义一个变量

  1. struct stuff{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. }xiaoming;

其实这就相当于先定义结构体,再用结构体定义一个结构体变量:

  1. struct stuff{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. };
  7. struct stuff xiaoming;

第三种:使用typedef关键字,可以将结构体变量定义时少写一个struct,比较省事。

  1. typedef struct stuff{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. }stuff_s;
  7. stuff_s xiaoming;

使用typedef还可以进一步简化,将结构体名也省略,这也是常用的方式

  1. typedef struct{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. }stuff_s;
  7. stuff_s xiaoming;

STM32的标准外设库有大量这样的应用,如下

  1. typedef struct
  2. {
  3. uint32_t GPIO_Pin;
  4. GPIOMode_TypeDef GPIO_Mode;
  5. GPIOSpeed_TypeDef GPIO_Speed;
  6. GPIOOType_TypeDef GPIO_OType;
  7. GPIOPuPd_TypeDef GPIO_PuPd;
  8. }GPIO_InitTypeDef;

关于结构体指针定义问题,有很多的“骚操作”的写法,我一般按照下面定义指针

  1. stuff_s *cuerrent_student;

02 结构体初始化

在大部分应用中,一般都是定义结构体后,在代码中进行初始化,如下所示

  1. typedef struct{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. }stuff_s;
  7. stuff_s xiaoming;
  8. void xiaoming_inf_init()
  9. {
  10. xiaoming.name = "xiaoming";
  11. xiaoming.num = 1;
  12. xiaoming.age = 18.0;
  13. xiaoming.score = 100;
  14. }

当然也有可以定义时就进行数据初始化的

  1. typedef struct{
  2. char *name; //姓名
  3. int num; //学号
  4. int age; //年龄
  5. float score; //成绩
  6. }stuff_s;
  7. stuff_s xiaoming={"xiaoming",1,18.0,100};

C99和C11为结构提供了指定初始化器(designatedinitializer)。其初始化器使用点运算符和成员名。

关于C99和C11的知识可以看我之前的文章《C语言的发展》,在IAR和Keil中记得勾选C99的选项。

例如,只初始化xiaoming结构中的name成员,可以这样做:

  1. stuff_s xiaoming=
  2. {
  3. .name = "xiaoming"
  4. };

也可以按照任意顺序使用指定初始化器:

  1. stuff_s xiaoming=
  2. {
  3. .age = 18.0,
  4. .name = "xiaoming"
  5. };

这样的赋值方式,在linux方式中很常见,以platform驱动框架为例:

  1. static struct platform_driver leds_platform_driver = {
  2. .driver = {
  3. .name = "imx6ul-led",
  4. .of_match_table = leds_of_match,
  5. },
  6. .probe = leds_probe,
  7. .remove = leds_remove,
  8. };

03 访问结构体成员

结构体成员的访问需要借助结构体成员运算符(.),如下

  1. stuff_s xiaoming,xiaohong;
  2. void student_inf_init()
  3. {
  4. xiaoming.name = "xiaoming";
  5. xiaoming.num = 1;
  6. xiaoming.age = 18.0;
  7. xiaoming.score = 100;
  8.  
  9.  
  10. xiaohong.name = "xiaohong";
  11. xiaohong.num = xiaoming.num+1;
  12. }

使用指针时,使用(->)符号访问结构体成员

  1. stuff_s xiaoming,xiaohong;
  2. stuff_s *cuerrent_student;
  3. void student_inf_init()
  4. {
  5. xiaoming.name = "xiaoming";
  6. xiaoming.num = 1;
  7. xiaoming.age = 18.0;
  8. xiaoming.score = 100;
  9. cuerrent_student = &xiaohong;
  10.  
  11.  
  12. cuerrent_student->name = "xiaohong";
  13. cuerrent_student->num = xiaoming.num+1;
  14. }

04 枚举与结构体的结合

简单介绍下枚举:有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便在后续代码中使用,比如一个星期只有七天,一年只有十二个月,一个班每周有六门课程等。

当然,你可以用宏定义

  1. #define Mon 1
  2. #define Tues 2
  3. #define Wed 3
  4. #define Thurs 4
  5. #define Fri 5
  6. #define Sat 6
  7. #define Sun 7

如果用了枚举则如下

  1. enum week{
  2. Mon,
  3. Tues,
  4. Wed,
  5. Thurs,
  6. Fri,
  7. Sat,
  8. Sun
  9. };

枚举是一种类型,通过它可以定义枚举变量:

  1. enum week a, b, c;

那么枚举和结构体一起用会产生什么效果呢?假设我们要协议一个语音芯片的驱动,需要表示语音芯片的状态

  1. typedef enum//语音芯片状态
  2. {
  3. VOICE_INIT_OK = 0x4A, //语音芯片上电初始化成功后,自动回传命令
  4. VOICE_RECEIVE_OK = 0x41, //语音芯片收到正确的命令帧
  5. VOICE_ORDER_ERROR= 0x45, //语音收到错误的命令帧
  6. VOICE_BUSY = 0x4E, //语音忙(正在合成状态)
  7. VOICE_FREE = 0x4F //语音空闲
  8. } VOICE_STATUS;
  9. typedef struct {
  10. VOICE_STATUS status ; //!< 语音芯片状态
  11. Ouint32 delayTicks; //!< 播放时间
  12. Ouint32 playtimes; //!< 播放次数
  13. } voicechip_Para_S;
  14. voicechip_Para_S voicechip_Para;

那么改变语音芯片状态时,我们可以按照下面这样写

  1. voicechip_Para.status = VOICE_RECEIVE_OK;

判断语音芯片状态时,我们可以按照下面写

  1. if((voicechip_Para.status == VOICE_FREE)

当然,你用宏定义是可以的,代码也很整洁。这里希望你能理解文章最开始的那句话:结构体是某种业务相关属性的聚合。

05 骚操作

关于结构体有很多骚操作,如果全部总结下来,这篇文章就会很臃肿,例如结构体嵌套的骚操作,可以一边定义结构体B,一边就使用上:

  1. struct A{
  2. struct B{
  3. int c;
  4. }b;
  5. struct B sb;
  6. }a;

对于这样的情况,我一般主张能看懂就行,自己写代码时就少点这样的骚操作

  1. struct B{
  2. int c;
  3. }b;
  4. struct A{
  5. struct B sb;
  6. }a;

原文链接:https://mp.weixin.qq.com/s/ggOg6BDZpi7GACwUJkPCiA