赞
踩
// 由存储器的映射可知,片上外设基地址0x4000 0000 #define PERIPH_BASE ((unsigned int)0x4000000) // APB2 总线的基地址 #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) // AHB 总线基地址 #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) // GPIO 外设基地址,由系统框图可知,GPIO挂靠在APB2总线上 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) // RCC 外设基地址 在AHB总线上 #define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define __IO volatile //volatile 表示易变的变量,防止编译器的优化 typedef unsigned int uint32_t //一个int型可看做四个字节,32位 typedef unsigned short uint16_t // 定义GPIO寄存器结构体 typedef struct{ __IO uint32_t CRL; // 端口配置低寄存器, 地址偏移 0X00 __IO uint32_t CRH; // 端口配置高寄存器, 地址偏移 0X04 __IO uint32_t IDR; // 端口数据输入寄存器, 地址偏移 0X08 __IO uint32_t ODR; // 端口数据输出寄存器, 地址偏移 0X0C __IO uint32_t BSRR; // 端口位设置/清除寄存器,地址偏移 0X10 __IO uint32_t BRR; // 端口位清除寄存器, 地址偏移 0X14 __IO uint32_t LCKR; // 端口配置锁定寄存器, 地址偏移 0X18 }GPIO_typeDef; // 定义时钟CRR结构体 typedef struct{ __IO uint32_t CR; // 时钟控制寄存器, 地址偏移 0X00 __IO uint32_t CFGR; // 时钟配置寄存器, 地址偏移 0X04 __IO uint32_t CIR; // 时钟中断寄存器, 地址偏移 0X08 __IO uint32_t APB2RSTR; // APB2外设复位寄存器,地址偏移 0X0C __IO uint32_t APB1RSTR; // APB1外设复位寄存器,地址偏移 0X10 __IO uint32_t AHBENR; // AHB外设时钟使能寄存器,地址偏移 0X14 __IO uint32_t APR2ENR; // APB2外设时钟使能寄存器,地址偏移 0X18 __IO uint32_t ApR1ENR: //APB1外设时钟使能寄存器, 地址偏移 0X1C __IO uint32_t BDCR; //备份域控制寄存器, 地址偏移 0X20 __IO uint32_t CSR; //控制/状态寄存器, 地址偏移 0X24 }RCC_TypeDef;
在stm32中,程序的存储器,数据存储器,寄存器和输入输出端口被组织在一个4GB(4294967296)的线性空间中,即从地址0x0000 0000 到 地址0xFFFF FFFF。且被分为8个block。一个block的大小为512MB,注意,这里的存储单位是一个字节。而GPIO中一个寄存器的存储空间是4个字节,上面的七个寄存器的存储空间是连续的。一个uint32_t刚好也为4个字节。因此只需要给结构体传入GPIO的基地址即可。
// GPIOA_BASE只是一串数字,将它转换成GPIO_TypeDef类型指针
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define RCC ((RCC_TypeDef *) RCC_BASE)
int main(void)
{
// 开启GPIOB的时钟
RCC -> APB2ENR |= (1<<3);
// 清空控制PB0的端口位
GPIO -> CRL &= ~( 0x0F << (4*0));
// 配置 PB0 为通用的推挽输出,速度为10M
GPIOB -> CRL |= (1<<4*0);
// PB0输出低电平
GPIOB -> ODR |= (0<<0);
while(1);
}
// 对某一位赋值先清零,再|=,某一位清零用&=再取反,这样可避免对其他位置造成影响
typedef struct{
uint16_t GPIO_Pin; // 选择要配置的 GPIO 引脚
uint16_t GPIO_Speed; // 选择 GPIO 引脚的速率
uint16_t GPIO_Mode; // 选择 GPIO 引脚的工作模式
} GPIO_InitTypeDef;
typedef enum{ GPIO_Speed_10MHz = 1, // 10MHZ (01)b GPIO_Speed_2MHz, // 2MHZ (10)b GPIO_Speed_50MHz // 50MHZ (11)b } GPIOSpeed_TypeDef; // 枚举会自动递增,即1,2,3(01,10,11) typedef enum{ GPIO_Mode_AIN = 0x0,// 模拟输入 (0000 0000)b GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入 (0000 0100)b GPIO_Mode_IPD = 0x28, // 下拉输入 (0010 1000)b GPIO_Mode_IPU = 0x48, // 上拉输入 (0100 1000)b GPIO_Mode_Out_OD = 0x14, // 开漏输出 (0001 0100)b GPIO_Mode_Out_PP = 0x10, // 推挽输出 (0001 0000)b GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出 (0001 1100)b GPIO_Mode_AF_PP = 0x18 // 复用推挽输出 (0001 1000)b } GPIOMode_TypeDef; // 定义枚举类型是为了来限定输入参数
/*GPIO 引脚号定义 */ #define GPIO_Pin_0 (uint16_t)0x0001) // 0000 0000 0000 0001 #define GPIO_Pin_1 ((uint16_t)0x0002) // 0000 0000 0000 0010 #define GPIO_Pin_2 ((uint16_t)0x0004) // 0000 0000 0000 0100 #define GPIO_Pin_3 ((uint16_t)0x0008) // 0000 0000 0000 1000 #define GPIO_Pin_4 ((uint16_t)0x0010) // 0000 0000 0001 0000 #define GPIO_Pin_5 ((uint16_t)0x0020) // 0000 0000 0010 0000 #define GPIO_Pin_6 ((uint16_t)0x0040) // 0000 0000 0100 0000 #define GPIO_Pin_7 ((uint16_t)0x0080) // 0000 0000 1000 0000 #define GPIO_Pin_8 ((uint16_t)0x0100) // 0000 0001 0000 0000 #define GPIO_Pin_9 ((uint16_t)0x0200) // 0000 0010 0000 0000 #define GPIO_Pin_10 ((uint16_t)0x0400) // 0000 0100 0000 0000 #define GPIO_Pin_11 ((uint16_t)0x0800) // 0000 1000 0000 0000 #define GPIO_Pin_12 ((uint16_t)0x1000) // 0001 0000 0000 0000 #define GPIO_Pin_13 ((uint16_t)0x2000) // 0010 0000 0000 0000 #define GPIO_Pin_14 ((uint16_t)0x4000) // 0100 0000 0000 0000 #define GPIO_Pin_15 ((uint16_t)0x8000) // 1000 0000 0000 0000 #define GPIO_Pin_All ((uint16_t)0xFFFF) // 1111 1111 1111 1111
/** * @brief Initializes the GPIOx peripheral according to the specified * parameters in the GPIO_InitStruct. * @根据GPIO_InitStruct中的特定参数初始化GPIO外设 * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @参数中的x可以用来选择GPIO外设 * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that * contains the configuration information for the specified GPIO peripheral. *指向GPIO_InitTypeDef结构体的指针,包含了指定的GPIO外设的配置信息 * @retval None(没有返回值) */ void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) { uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00; uint32_t tmpreg = 0x00, pinmask = 0x00; /*---------------------------- GPIO Mode Configuration -----------------------*/ //输入参数GPIO_Mode的低四位暂存在currentmode中 currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); //bit4 是1表示输出,bit4是0表示输入 //判断bit4是1还是0,即首先判断的输入还是输出模式 if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00) { /* Output mode */ //输出模式设置输出的速度 currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed; } /*---------------------------- GPIO CRL Configuration ------------------------*/ /* Configure the eight low port pins */ //配置低八位 if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00) { // 备份CRL寄存器的值 tmpreg = GPIOx->CRL; // 循环,找到具体对应的是哪一个管脚pin for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = ((uint32_t)0x01) << pinpos; /* Get the port pins position */ // 得到具体管脚pin位置 currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; //如果currentpin=pos ,则找到相应的管脚 if (currentpin == pos) { // pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚 pos = pinpos << 2; /* Clear the corresponding low control register bits */ // 把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变 pinmask = ((uint32_t)0x0F) << pos; tmpreg &= ~pinmask; /* Write the mode configuration in the corresponding bits */ // 向寄存器写入将要配置的引脚的模式 tmpreg |= (currentmode << pos); /* Reset the corresponding ODR bit */ // 判断是否为下拉输入模式 if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) { // 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 对引脚置 0 GPIOx->BRR = (((uint32_t)0x01) << pinpos); } else { // 判断是否为上拉输入模式 /* Set the corresponding ODR bit */ if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) { // 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 对引脚 置 1 GPIOx->BSRR = (((uint32_t)0x01) << pinpos); } } } } // 把前面处理后的暂存值写入到 CRL 寄存器之中 GPIOx->CRL = tmpreg; } /*---------------------------- GPIO CRH Configuration ------------------------*/ /* Configure the eight high port pins */ // 配置端口高 8 位,即 Pin8~Pin15 if (GPIO_InitStruct->GPIO_Pin > 0x00FF) { // 先备份 CRH 寄存器的值 tmpreg = GPIOx->CRH; // 循环,从 Pin8 开始配对,找出具体的 Pin for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = (((uint32_t)0x01) << (pinpos + 0x08)); /* Get the port pins position */ // pos 与输入参数 GPIO_PIN 作位与运算 currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos); //若 currentpin=pos, 则找到使用的引脚 if (currentpin == pos) { //pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚 pos = pinpos << 2; /* Clear the corresponding high control register bits */ //把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变 pinmask = ((uint32_t)0x0F) << pos; tmpreg &= ~pinmask; /* Write the mode configuration in the corresponding bits */ // 向寄存器写入将要配置的引脚的模式 tmpreg |= (currentmode << pos); /* Reset the corresponding ODR bit */ // 判断是否为下拉输入模式 if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) { // 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 可对引脚置 0 GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08)); } /* Set the corresponding ODR bit */ if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) { // 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 可对引脚置1 GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08)); } } } // 把前面处理后的暂存值写入到 CRH 寄存器之中 GPIOx->CRH = tmpreg; } }
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BSRR = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BRR = GPIO_Pin;
}
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18) int main(void) { // 定义一个 GPIO_InitTypeDef 类型的结构体 GPIO_InitTypeDef GPIO_InitStructure; // 开启 GPIO 端口时钟 RCC_APB2ENR |= (1<<3); // 选择要控制的 GPIO 引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 设置引脚模式为通用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置引脚速率为 50MHz GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 调用库函数,初始化 GPIO 引脚 GPIO_Init(GPIOB, &GPIO_InitStructure); // 使引脚输出低电平, 点亮 LED1 GPIO_ResetBits(GPIOB,GPIO_Pin_0); while (1) { GPIO_ResetBits(GPIOB,GPIO_Pin_0); // 延时一段时间 Delay(0xFFFF); // 使引脚输出高电平,关闭 LED1 GPIO_SetBits(GPIOB,GPIO_Pin_0); // 延时一段时间 Delay(0xFFFF); } } void Delay(int i) { i--; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。