手把手教你如何写单片机的结构体

发布者:心灵飞翔最新更新时间:2024-07-12 来源: elecfans关键字:单片机  结构体  STM32 手机看文章 扫描二维码
随时随地手机看文章

摘要:听说还有好多学单片机的小伙伴不会用结构体?指针和结构体是学单片机必须要掌握的,如果你C语言掌握的不牢,单片机根本学不到精髓,只能完成一些低级的项目。看得懂结构体并且能够灵活运用结构体才能说你入门了单片机。本篇将以最通俗的方式结合STM32单片来讲讲结构体的运用。解决你学完C语言、考过了计算机二级还是看不懂单片机结构体的苦恼。宝藏文章,记得点赞转发收藏。


大家知道指针和结构体是单片机的难点,所以就去学习C语言,找视频看书......


这里面每一个视频的播放量都非常高。对于单纯的学习C语言,这里讲的很清楚。看完你不禁在下面评论一句:哇!讲的真的太清楚了吧!但是等你真正的学单片机的时候,你会发现我不是学过C语言吗?计算机二级我也过了啊!怎么这个指针和结构体都不懂啊?难道我学了一个假的C语言?

其实这不是你的错,也不是单片机的错,而是在C语言和单片机之间需要一个过渡!这个需要过渡的点在很多单片机视频教程中并没有去讲解。因为教育机构默认你是知道的,所以在讲流水灯时他们并不会讲解GPIO初始化这个结构体,因为默认你是知道如何操作的。

68637578-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

申明一个GPIO_InitTypeDef的结构体,然后在LED_Init(void)函数中定义一个GPIO_InitStructure的变量GPIO_InitStructure,那么这个变量就可以设置这个GPIO_InitTypeDef的结构体中的成员。这里先做了解,请接着往下看。

1、为什么需要结构体

这里先不说什么是结构体,说说为什么需要结构体?只有知道为什么需要,才能按照你的需要去学习,这样效率才会高。你才知道在什么情况下我们需要写一个结构体,怎么样去用结构体。

这里我们以一个智能家居的项目为例。

先来看一个实际的问题

话说有一个项目上有4个传感器:光照传感器、烟雾传感器、酒精传感器、湿度传感器。然后这四个各个传感器还有设置报警的阈值范围。

一般都是这样写

#include'sys.h' #include'delay.h' #include'usart.h' /*记录传感器的数值*/ floattemperature;//温度 charhumidity;//湿度 charalcohol;//酒精浓度 intillumination;//光照强度 /*记录传感器高低阈值*/ floattemperature_threshold[2]; floathumidity_threshold[2]; floatalcohol_threshold[2]; floatillumination_threshold[2]; intmain(void) { uart_init(115200);//串口初始化 delay_init(); while(1) { } }

689da6d0-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

当然你做一个项目肯定还定义了很多其他的变量,还需要记录其它变量

68ee5a30-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

然后过了几天又增加了个一氧化碳传感器

69037834-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

然后过了几天,每个传感器还需要加个是否正常工作的标志位

6937d84a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

因为项目的需要,然后又增加了4个相同的传感器:温湿度、光照强度、烟雾浓度、酒精浓度。

69868738-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

然后又增加了4个相同的传感器:温湿度、光照强度、烟雾浓度、酒精浓度。

截图截不开了....

6a41ebf4-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

满屏的变量......

满屏的变量......

满屏的变量......

在项目刚开始做的时候如果不能未雨绸缪,接着干下去整个程序代码别说维护了,就是接着写都让人头疼!

满屏的变量...

满屏的变量...

2、结构体闪亮登场

然后搞C语言那帮家伙就造了个功能struct

1、结构体就是可以把变量包含到里面的东西

struct就代表要定义一个结构体,sensors是这个结构体的名字,然后是一个大括号 { }

大括号里面就随意定义变量啦~

6a619c4c-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

怎么使用里面的变量呢?

注意结构体是一个数据类型就像是int和char一样的这种类型

既然是一种数据类型,那么就可以用这个数据类型定义变量

定义一个该结构体的变量

6a76f736-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

为啥要那样子定义啊?

答:你去问造C语言的那帮家伙去!问问他们为啥要设计成这样子!

然后操作结构体变量里面的成员变量。当我们定义好结构体变量后,在初始化变量里面的成员变量时就会自动出现结构体里面的成员变量,如果这个代码是你一个一个敲出来的话,你就会感叹结构体在单片机中是那么的奇妙!

6a8758ba-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


6ae1c0c0-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


6af1166a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

有人会问为啥是结构体变量中间加个点?

答:你去问造C语言的那帮家伙去!问问他们为啥要设计成这样子。

2、其实定义结构体变量可以下面这样子

6b01f70a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

也可以定义多个

6b2acea0-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


6b3b3128-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

发现了没,每个结构体变量都是单独拥有结构体里面的全部成员变量。

就像是最开始说的,如果再增加一套传感器:温湿度、光照强度、烟雾浓度、酒精浓度。

使用结构体的话只需要再定义一个结构体变量即可。

但是很多时候我们在单片机中见到的结构体并不是上面那样定义的,而是在前面加了一个typedef 关键字。

这样的例子在库函数的头文件中我们经常会看到如下结构体

6b6a32fc-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

3、typedef关键字

先看一下百度百科对typedef的定义

6baee910-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

总结一句就是:typedef可以把一个数据类型取一个别的名字

typedef {数据类型} {别的名字}

#include'sys.h' #include'delay.h' #include'usart.h' typedefintzhjiguoxin;//zhjiguoxin就是int zhjiguoxinvalue=0; intmain(void) { uart_init(115200);//串口初始化 delay_init(); printf('value=%d ',value); while(1) { } }

6bc32d3a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

6bcfbb9a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


虽然typedef可以给变量取别名,但是没有谁会像上面那样取名字,我这里只是举一个例子。

4、结构体的精髓

注意下:

1、下面的代表了这个结构体数据类型

6c0e7718-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

2、给这个数据类型起一个别名

注意是三部分, typedef {数据类型} {别的名字}。所以sensor就代表了这个结构体了。

建议初学者把下面这张图保存到你的电脑,这样你就永远也不会忘记typedef在结构体中的用法了,也能很快的记住结构体这个东东。

6c1cefaa-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

3、以后定义结构体变量的时候就不需要像最开始那样struct sensors sen;这样的定义结构体变量了,只需要sensor sen;即可。

6c73958a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

4、结构体名字可以省略

注意结构体定义可以不写结构体名,对C语言来说,那个sensors不叫结构体名,而是叫标签(tag)。C语言结构体名是struct关键字 + tag。所以为了简便我们看到的单片机中的结构体都是写成如下的形式。

6ca8e294-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

5、结构体的变量可以放任何变量

1、结构体变量可以放任何变量(int型指针)

#include'sys.h' #include'delay.h' #include'usart.h' typedefstruct { floattemperature;//温度 charhumidity;//湿度 charalcohol;//酒精浓度 intillumination;//光照强度 charCO;//一氧化碳浓度 int*p;//int型的指针变量 }sensor; sensorsen; intvalue=0; intmain(void) { uart_init(115200);//串口初始化 delay_init(); sen.p=&value;//把value的地址赋值 //打印p代表的地址里面的值(其实就是打印value的值) printf('value=%d ',*(sen.p)); while(1) { } }

既然是指针变量,所以给指针变量赋值时当然是赋值的是一个地址。

6cb5b578-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

6cc2d51e-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

2、结构体变量可以放任何变量(函数指针)

#include'sys.h' #include'delay.h' #include'usart.h' typedefstruct { floattemperature;//温度 charhumidity;//湿度 charalcohol;//酒精浓度 intillumination;//光照强度 charCO;//一氧化碳浓度 int*p;//int型的指针变量 void(*fun)(); }sensor; sensorsen; voidfunction() { printf('zhiguoxin '); } intvalue=0; intmain(void) { uart_init(115200);//串口初始化 delay_init(); sen.fun=function; sen.fun(); while(1) { } }

既然是函数指针变量,所以给函数指针变量赋值时当然是赋值的也是地址,并且还要是一个函数的地址,而一个函数的函数名就是该函数的地址。所以才会有下面的把函数function();的地址function赋值给函数指针fun。这样大家是不是很清楚了。如果不清楚建议看个3遍以上!

6ccde88c-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


6cd93b42-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

3、结构体变量可以放任何变量(结构体变量)

这就是结构体嵌套,在一个结构体内包含了另一个结构体作为其成员。当出现结构体嵌套时,必须以级联方式访问结构体成员,即通过成员选择运算符逐级找到最底层的成员时再引用。


#include'sys.h' #include'delay.h' #include'usart.h' typedefstruct { inti; }zhiguoxin; typedefstruct { floattemperature;//温度 charhumidity;//湿度 charalcohol;//酒精浓度 intillumination;//光照强度 charCO;//一氧化碳浓度 int*p;//int型的指针变量 void(*fun)(); zhiguoxinguougo; }sensor; sensorsen; intmain(void) { uart_init(115200);//串口初始化 delay_init(); sen.guougo.i=100; printf('i=%d ',sen.guougo.i); while(1) { } }

6d0fb5b4-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

6d42530c-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


4、结构体变量可以放任何变量(结构体指针)

结构体是一个数据类型。数据类型当然也可以定义对应的指针变量啦。

就像是int 类型可以定义 int *p; 一样

6d583df2-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

所以当大家如果发现你的代码中结构体是通过—>访问的话,那么这个结构体变量一定是指针类型的变量。同理如果代码中结构体是通过.访问的话,那么这个结构体变量就不是指针变量,而是一般的变量。

6d7a2dae-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000


6e08bc4a-b328-11eb-bf61-12bb97331649.png?imageView2/2/w/1000

总结:到这里结构体在单片机中的应用你已经掌握的差不多了,大家可能感觉本期讲的内容太简单了,不过只有你把这个简单的基础性知识打牢,你就会进步的更快。否则你总感觉你的代码差点意思。


关键字:单片机  结构体  STM32 引用地址:手把手教你如何写单片机的结构体

上一篇:怎么才能知道单片机程序占了多少字节
下一篇:单片机数据通信学习中串口有多重要?

推荐阅读最新更新时间:2026-03-19 14:11

stm32之滴答定时器(2):滴答定时器地址与stm32结构
上一篇讲了滴答定时器在内核什么位置,这篇讲讲滴答定时器结构体和地址的联系 不知道大家搜滴答定时地址的时候有没有注意到,SysTick_BASE的下面就是SysTick的定义 #define SysTick ((SysTick_Type *) SysTick_BASE) 这个东西第一次接触还是感觉挺绕的(初学者强烈推荐一本入门书《C和指针》(*^__^*) ),这句什么意思呢,通俗地讲,就是把SysTick定义为了指向SysTick_Type结构体的指针,而这个指针的地址呢,是个固定值, SysTick_BASE,也就是上一篇写的0xE000E010。 这个SysTick_Type结构体是什么,同样
[单片机]
<font color='red'>stm32</font>之滴答定时器(2):滴答定时器地址与<font color='red'>stm32</font><font color='red'>结构</font><font color='red'>体</font>
单片机开发过程中使用结构简化程序
首先,作为刚入行不久的新人,我在单片机开发这块并没有太多的经验,所以可能在写一些相关的文档的时候存在一些错误,希望大家多多包含!也希望各位不吝赐教,指点迷津! 好记性不如烂笔头,之所以选择开通博客是因为我想把自己在工作和学习过程中碰到的一些问题以及疑惑记录下来,同时积极地定位问题的源头以及寻求解决方案,或许在碰到相同的问题时就能很快地解决。同时在博客上也可以学习到很多工程师长期积累的经验,分享自己的一些心得,这对个人的自我提升有很好的帮助! 好的,现在进入主题。刚开始接触单片机的时候,看到别人的程序中会使用很多的宏定义以及结构体等等,当时总认为这样的做法很累赘,显得花里胡哨的。后来进入工作之后,看了一些量产产品的代码框架,我才知道
[单片机]
<font color='red'>单片机</font>开发过程中使用<font color='red'>结构</font><font color='red'>体</font>简化程序
单片机中的结构运用
结构体: 在单片机开发中,经常会遇到要初始化一个外设,比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性及模式。对于这种情况,在我们没有学习结构体时,我们一般的方法是: void USART_Init( u8 usartx , u32 BandRate , u8 parity , u8 mode ); 这种方式是有效的同时在一定场合是可取的。但是试想,如果有一天,我们希望往这个函数里再传入一个参数,那么势必我们需要修改这个函数的定义,重新加入字长这个入口参数。于是我们的定义被修改为: void USART_Init( u8 usartx , u32 BandRate , u8 parity
[单片机]
关于结构/联合体 字节对齐及位域操作出现的问题
使用的开发环境为MAPLAB X IDE 1字节对齐的实现如下 #pragma pack(1) typedef union { uint8_t buf ; struct Message { uint8_t repeat : 2; uint8_t msgid : 6; ...... } } #pragma pack() 上面的操作为对联合体的1字节对齐,解决了结构体的对齐是根据数据类型最大的变量对齐的情况节省了空间的分配,这里也做了位域的操作将8位拆分为2个变量其中一个占8位里的2bit另外一个占6bit。 位域操作的目的是为了
[单片机]
c51中定义联合体和结构
联合体 union { unsigned char Ch; // 无符号数 unsigned char CHR ; // 无符号数组 unsigned long I; // 无符号整型数 long L; // 有符号长整型数 float F; // 浮点数 }EEP; float shu; EEP.F =shu; // 结构体 struct realti { uchar Second; uchar Minute; uchar Hour; uchar Day; uchar Month; uchar Week; uchar Year; ucha
[单片机]
ARM优化之结构的定义
偶然的一次发现,看到自己定义的结构体编译出来的大小和我想象的不一样,于是便追溯了一下根源。做了一系列实验之后发现,ARM的结构体的内存分配上有着一些固有的做法。废话不多说,看例子。 实验一: 我定义一个结构体: typedef struct TestStruct { unsinged char Test1; unsigned int Test2; unsigned char Test3; unsigned int Test4; unsigned short Test5; }TEST_STRUCT; TEST_STRUCT TestStruct1; 然后我分别给这些成员都赋值,以便于在Memo
[单片机]
c语言定义结构指针
int main() { typedef struct { u8 KeyCurrentIndex;//当前状态索引号 u8 KeyEnterState;//按下【enter】键时转向的索引号 u8 KeyCancelState;//按下【cancel】键时转向的索引号 u8 KeyUpState;//按下【up】键时转向的索引号 u8 KeyDownState;//按下【down】键时转向的索引号 void (*CurrentOperate)(); //当前状态下执行的功能操作 }KbdTabStruct; const KbdTabStruct KBD = { {0,1,2,3,4,(*main)}, {6,7,
[单片机]
c语言定义<font color='red'>结构</font><font color='red'>体</font>指针
niosII中串口RS232程序使用结构和联合体结合的用法
这一节,我们针对大家提出的有关定义寄存器结构体的问题进行解析。在NIOS II软件开发过程中,如果使用我们提出的寄存器操作方式的话,首先需要定义一个寄存器结构体,之所以这样做是为了在软件书写过程中操作方便,更是为了增强程序的可读性。我们就拿UART来举例说明。       首先,我们看一下UART的寄存器说明,如下表所示       我们通过上表可以看到,UART包括6个寄存器(由于最后一个寄存器一般不用,所以建立的结构体中没有加入它),假设基地址为0x00的话,那么他们的地址分别为0x00,0x01,0x02,0x03,0x04,0x05。也就是说,各个寄存器之间是存在顺序的。那么,在我们建立结构体过程中也要注意他们的顺序
[嵌入式]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved