点亮LED灯-使用结构体语法

一、标准C结构体语法

结构体是C当中一种用户可自定义的数据类型,有点类似于Java中的类,结构体允许用户存储不同类型的数据项。

结构体里面的数据成员可以是基本数据类型(如int,float等),也可以是其他结构体类型,指针类型等。

👇结构体由关键字“struct”和结构体名组成,名字可自定义:

struct tag {
    member-list
    member-list
    member-list  
    ...
} variable-list ;           //👈分号后可以指定一个或多个结构变量。

其中,tag为结构体标签,可理解为结构体的类型名称;member-list为结构体成员,每个结构体成员都是定义了数据类型的变量,例如int a;variable-list为结构体变量名,例如:

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

在结构体里,我们可以在struct的后面加上Books作为结构体类型的类型名称,这有一点点像给这个结构体取一个类似于int、char的特定名称,例如:

struct num
{
    int  a;
    int  b;
};

👆这个结构体当中没有在结尾的分号声明变量,因此,在声明“num”类型的结构体变量时,我们会这么定义:

struct num number; //这样就定义了一个名字叫number,数据类型为“num”的结构体变量。

当然了,你也可以给这个变量赋值,例如:

stryct num number = {1,2}; //这样你就直接给结构体当中的成员a赋值1,b赋值2了。

当然了,你也可以一步到位,直接在创建结构体的时候直接声明完,例如:

struct num
{
    int  a;
    int  b;
}number = {1,2};    //这一行代码既定义了结构体变量名称num,又直接给结构体成员a、b分别赋值1、2

👆“num”和“number”只能少写一个。

结构体可以视为一种数据模板,你可以用这套模板定义很多个结构体变量,例如:

struct num
{
    int  a;
    int  b;
}number1, number2;

然后分别给number1和number2赋值:

stryct num number1  = {1,2};
stryct num number2  = {3,4};

这样,这两个结构体变量里,结构体成员的值就分别为1、2和3、4了。

你同样还需要注意的是,定义结构体类型名在程序中是不可重复的,举一个错误例子:

struct num
{
    int  a;
    int  b;
}number;

struct num
{
    int  a;
    int  b;
}number;

👆你定义了两个完全相同的结构体,但是事实上当你在编辑器编译运行时,会报如:[Error] redefinition of 'struct main()::num'的错误。

这说明了结构体数据类型具有唯一性。

二、新建结构体

👇根据参考手册去定义结构体:

点亮LED灯-使用结构体语法-1

其中uint32_t在程序中定义为typedef unsigned int uint32_t;

请注意!unsigned int的数据所占的内存空间为四个字节!之所以是无符号整型数据是因为刚好单个寄存器的内存控件就是4个字节,例如CRL的的内存地址从0x4001100到0x40011004,中间就是4个字节的空间。

typedef struct {
     uint32_t  CRL;
     uint32_t  CRH;
     uint32_t  IDR;
     uint32_t  BSRR;
     uint32_t  BRR;
     uint32_t  LCKR;
}  GPIO_TypeDef;

在GPIO_TypeDef当中,结构体成员只能是CRL、CRH……LCKR这七个,不能是别的成员。

👇结构体成员在内存中是依次排列的:

点亮LED灯-使用结构体语法-2

我们在新建的结构体当中存放的结构体成员按照寄存器内存地址依次排列,但是这样还不能直接用,因为这些新建的结构成员并没有被赋予相应的内存地址,因此——结构体成员的名字虽然有了,但是地址不能确定,地址不能确定是因为第一个结构体成员的地址还没有被赋值,即首地址还没确定。

三、给新建的结构体赋予内存地址

确定首地址:

#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)

调用结构体成员:

GPIOC -> ODR

四、结构体指针以及其他的一些定义方法

当我们需要访问结构体成员时,可以使用“.”—— 成员访问运算符,例如:

printf("book id is %d", book.book_id);

结构体指针——将一个指针变量指向结构体,例如:

struct num *number;

接下来,为了访问结构体内部的成员,需要进行如下操作:number -> a或(*number).a,例如:

#include<stdio.h>
int main() {
    struct num{
        int a;
        int b;
    };
    struct num number = {1,2
    };
    struct num *p = NULL;
    p = &number;
    printf("%d %d", (*p).a, (*p).b);      //或printf("%d %d", p->a, p->b);
}

先声明结构体变量并给结构体成员赋值,再使用指针指向结构体成员的初地址,就可以使用结构体指针访问成员内容了。

使用typedef定义结构体类型:

typedef struct {
     int a;
}number;

声明结构体变量可写: number a; 完整的代码实现如下所示:

#include<stdio.h>
int main() {
    typedef struct{
        int a;
        short b;
    }number;
    number num = {1,2};
    printf("%d %d", num.a, num.b);
}

五、完整代码

👇stm32f10x.h
#define PERIPH_BASE           ((unsigned int)0x40000000)

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
/* AHB总线基地址 */
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)

/*GPIOC外设基地址*/
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)    

/*RCC外设基地址*/
#define RCC_BASE      (AHBPERIPH_BASE + 0x1000)
typedef unsigned int uint32_t;
typedef struct 
{
    uint32_t CRL;
    uint32_t CRH;
    uint32_t IDR;
    uint32_t ODR;
    uint32_t BSRR;
    uint32_t BRR;
    uint32_t LCKR;
}GPIO_TypeDef;

typedef struct 
{
    uint32_t CR;
    uint32_t CFGR;
    uint32_t CIR;
    uint32_t APB2RSTR;
    uint32_t APB1RSTR;
    uint32_t AHBENR;
    uint32_t APB2ENR;
    uint32_t APB1ENR;
    uint32_t BDCR;
    uint32_t CSR;
}RCC_TypeDef;

#define  GPIOC  ((GPIO_TypeDef*)GPIOC_BASE)
#define  RCC  ((RCC_TypeDef*)RCC_BASE)

👇main.c
#include "stm32f10x.h"

int main(void) {
    RCC->APB2ENR |= 1<<4;
    GPIOC->CRH &=~(0x0F<<(4*5));
    GPIOC->CRH |=(1<<(4*5));
    GPIOC->ODR &=~(1<<13);  
    while(1);
}
void SystemInit() {
}