struct 结构体类型名
{
成员列表
};
例如:
struct student{
long num;
char name[20];
char sex;
float score;
};
struct student stu_1, *p;//stu_1为结构体类型的变量,p为结构体类型的指针变量
可以将stu的地址赋给指针变量p,然后可以使用p结合指针运算符"->"访问变量stu中的成员,例如:
p=&stu;//可以用p->num访问
结构体所占内存等于各成员所占内存之和,且各成员的存储空间依次连续排列。
STM32F1有7组IO口,分别为GPIOA、GPIOB、……、GPIOG,每组IO口有7个相关寄存器,各寄存器的名称、偏移地址及说明如下表所示。
序号 | 寄存器名称 | 偏移地址 | 寄存器描述 | 备注 |
---|---|---|---|---|
1 | GPIOx_CRL | 0x00 | 端口配置低寄存器 | stm32F1有7组GPIO,每组GPIO有16个GPIO口(0-15),配置每个GPIO口需要4位配置寄存器,所以需要64位,即两个32位,0-7为端口配置低寄存器GPIOx_CRL ,8-15为端口配置高寄存器GPIOx_CRH。 |
2 | GPIOx_CRH | 0x04 | 端口配置高寄存器 | 端口配置高寄存器 |
3 | GPIOx_IDR | 0x08 | 端口输入数据寄存器 | 共32位,高16位保留,低16位每个IO口对应一位。 |
4 | GPIOx_ODR | 0x0C | 端口输出数据寄存器 | 共32位,高16位保留,低16位每个IO口对应一位。 |
5 | GPIOx_BSRR | 0x10 | 端口位设置/清除寄存器 | 共32位,高位BR:每个io对应一位,若设置为1,则设置对应的DDR寄存器位为0,若设置为0,则对对应的DDR位不产生影响。 低位BS:每个io对应一位,若设置为1,则设置对应的DDR寄存器位为1,若设置为0,则对对应的DDR位不产生影响。 BS优先级大于BR。 |
6 | GPIOx_BRR | 0x14 | 端口位清除寄存器 | 共32位,高16位保留,低16位每个IO口对应一位。 |
7 | GPIOx_LCKR | 0x18 | 端口配置锁定寄存器 | 共32位,高16位保留,低16位每个IO口对应一位。 |
STM32F4ZGT6有7组IO口,分别为GPIOA、GPIOB、……、GPIOG,每组IO口有10个相关寄存器,各寄存器的名称、偏移地址及说明如下表所示。
由3.2可以看出,GPIO相关寄存器的偏移地址是连续的,每个寄存器占4个字节,另外,我们在3.1.4 中说到,结构体中各成员的存储空间依次连续排列,所以,若令结构体的首地址与GPIO寄存器的基地址相同,并使结构体的成员大小与寄存器大小相同,就可以通过访问结构体成员的方式访问寄存器。
我们可以将STM32F1系列GPIO模块相关寄存器封装进一个结构体:
struct
{
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
} ;
然后再为该结构体定义一个别名:
typedef struct
{
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
} GPIO_TypeDef;
该结构体中成员大小均为32位,与寄存器大小一致,再令结构体的首地址与GPIO寄存器的基地址相同,即可利用结构体访问寄存器。
用3.3所说的结构体类型定义一个指针变量,比如:
GPIO_TypeDef* GPIOx;
这样,只要给变量GPIOx赋予正确的端口地址,则可以应用 GPIOx->成员 的方式访问特定端口的特定寄存器。比如GPIOB基地址为0x40021400,则只需将数据0x40021400进行如下的强制类型转换并赋值给GPIOx:
GPIOx = (GPIO_TypeDef*)(0X40010C00); (1)
即可通过GPIOx访问GPIOF的各个寄存器,如GPIOx->CRL访问GPIOF的CRL寄存器,GPIOx->CRH访问GPIOB的CRH寄存器。
通常,为了更加直观,我们都是先对代码段(1)的右边进行宏定义后再使用,比如
#define GPIOB ((GPIO_TypeDef *) 0x0X40010C00)
然后执行如下语句
GPIOx = GPIOB;
即可将某GPIO口的基地址赋给指针变量GPIOx。然后使用GPIOx->CRL等方式实现对GPIOF的寄存器的访问。
电路原理与寄存器的配置与本专栏第一篇文章一致,不再赘述,STM32F1系列完整代码如下:
//寄存器定义
#define RCC_APB2ENR (*(volatile unsigned int*)(0x40021018))
//类型定义
#define uint16_t unsigned short
#define uint32_t unsigned int
//GPIO端口定义
typedef struct
{
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
} GPIO_TypeDef;
GPIO_TypeDef* GPIOB;
//函数声明
void LED_Init(void);
void Delay(void);
int main()
{
GPIOB = (GPIO_TypeDef*) 0X40010C00;
//初始化--LED0模块进行初始化
LED_Init();
//灯闪烁
while(1)
{
GPIOB->ODR |= (1<<5);;
Delay();
GPIOB->ODR &=~(1<<5);//LED亮
Delay();
}
}
//延时函数
void Delay()
{
uint16_t i,j;
for(i=0;i<1000;i++)
for(j=0;j<300;j++);
return;
}
//LED0初始化函数
void LED_Init()
{
//使能GPIOB时钟
RCC_APB2ENR |= (1<<3);
//设置PB5的功能
GPIOB->CRL&= ~(15<<20);//对应位清零
GPIOB->CRL|=1<<20;//设置为推挽输出,最大速度为10MHz
//设置PB5输出高电平,LED灭
GPIOB->ODR |= (1<<5);;
}
欧启标O老师STM32F4系列完整代码如下:
/* 常用数据类型定义 */
#define uint8_t unsigned char
#define uint16_t unsigned short
#define uint32_t unsigned int
/* 将寄存器封装成类,抽象成结构体数据类型 */
typedef struct
{
volatile uint32_t MODER; //模式寄存器
volatile uint32_t OTYPER; //电路驱动方式寄存器
volatile uint32_t OSPEEDR; //响应速度寄存器
volatile uint32_t PUPDR; //上下拉电路配置寄存器
volatile uint32_t IDR; //输入数据寄存器
volatile uint32_t ODR; //输出数据寄存器
volatile uint32_t BSRR; //置位复位寄存器
volatile uint32_t LCKR; volatile uint32_t AFR[2];
}GPIO_TypeDef;
#define GPIOF ((GPIO_TypeDef *) 0x40021400) //定义 GPIOF 代表 GPIOF 模块的首地址
/* 时钟系统相关寄存器的定义 */
#define RCC_AHB1ENR (*(volatile unsigned *)0x40023830) //外设时钟使能寄存器。=1 对应外设时钟使能
/*LED0 初始化函数和延时函数的声明*/
void Led_Init(void);
void Delay(void);
/*主函数*/
int main(void)
{
Led_Init(); while(1)
{
GPIOF->ODR &= ~(1<<9); //LED0 亮
Delay();
GPIOF->ODR |= (1<<9); //LED0 灭
Delay();
}
}
/*LED0 的初始化*/
void Led_Init(void)
{
RCC_AHB1ENR |= 1<<5; //使能 PORTF 时钟
GPIOF->MODER &= ~(3<<(9*2)); //将配置 PF9 引脚相关位 bit18,bit19 清 0
GPIOF->MODER |= (1<<(9*2)); //配置 PF9 为输出
GPIOF->OTYPER &= ~(1<<9); //电路工作方式为推挽
GPIOF->OSPEEDR &= ~(3<<(9*2)); //对应位清 0
GPIOF->OSPEEDR |= (2<<(9*2)); //响应速度 50M,其他值亦可
}
/*延时函数*/
void Delay(void)
{
int i, j;
for(i = 0; i < 200; i ++)
for(j = 0; j < 3000; j ++);
}