stm32精简笔记1——GPIO

1 新建工程模版

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cBSqllCN-

stm32精简笔记1——GPIO_第1张图片

2 cortex-M3框图

1)Icode总线:从flash中取指令。

2)Dcode总线:从flash中取常量,从SRAM中取变量。

3)System总线:完成对外设的控制。

4)DMA:直接访问存储器,完成内存之间或内存与外设之间的数据搬运工作。

5)FSMC:挂载静态SRAM。

6)AHB总线:挂载时钟复位控制器、SDIO、APB1、APB2总线。

7)APB2总线:72MHz,挂载所有GPIO、高速USART、TIM、USB、AFIO。

3 内存映射

  • 将4G的内存按照512KB等分为8个块。
  • APB1总线的基地址为0x40000000。
  • APB2总线的基地址为0x40010000。
  • AHB总线的基地址为0x400180000。

4 GPIO框图

1)保护电路:用两个保护二极管来防止电压过大,接电机还需要接其他驱动。

2)推挽输出:接一个CMOS反相器,使得IO口的高低电平均有驱动能力。

3)开漏输出:将CMOS反相器变为一个OD门,使得高电平没有驱动能力,但是高电平依旧可以写。

4)复用推挽输出、复用开漏输出:通过一个与门,实现复用功能引脚的输出。

5)浮空输入:经过施密特触发器来整型电流,获得高低电平的方波。

7)模拟输入:不经过施密特触发器整型电流,获得模拟输入电流。

8)上拉输入:内部上拉,默认为高电平。

9)下拉输入:内部下拉,默认为低电平。

5 GPIO地址映射

  • 7个GPIO,对应的地址如下:
外设名 地址
GPIOA 0x4001 0800
GPIOB 0x4001 0c00
GPIOC 0x4001 1000
GPIOD 0x4001 1400
GPIOE 0x4001 1800
GPIOF 0x4001 1c00
GPIOG 0x4001 2000

6 寄存器介绍

寄存器名 寄存器标识符 偏移量 功能
端口配置低寄存器 GPIOX_CRL 0x00 32位有效,调节ODR低8位的输入输出模式
端口配置高寄存器 GPIOX_CRH 0x04 32位有效,调节ODR高八位的输入输出模式
端口输出数据寄存器 GPIOX_ODR 0x08 低16位为IO口的输出,高16位保留
端口输人数据寄存器 GPIOX_IDR 0x0c 低16位为IO口的输入,高16位保留
端口位设置/清楚寄存器 GPIOX_BSRR 0x10 低16位置1,置1,高16位置1,清零
端口位清楚寄存器 GPIOX_BRR 0x14 低16位置1,清零,高16位保留
端口配置锁定寄存器 GPIOX_LCKR 0x18 位16锁键,0~15只能在16位为0时才能

7 GPIO模式选择

1)CRL和CRH的每4位来控制GPIO的一位的工作模式。

2)[1:0]位控制速度:00-输入,01-2MHz,10-10MHz、11-50MHz。

3)[5]位控制输入输出:0-输入、1-输出。

4)[3:4]位配合[5]位控制输入输出:000-模拟、001-浮空、010-上拉或下拉、100-推挽、101-开漏、110-复推挽、111-复开漏

5)[6:5]控制上拉还是下拉:01-下拉、10-上拉

8 LED原理图

9 寄存器点亮LED

9.1 stm32f10x.h
#ifndef STM32F10X_H__
#define STM32F10X_H__

typedef unsigned int uint32_t;	/*32位整型*/
typedef unsigned short uint16_t;  /*16位整型*/
typedef unsigned char uint8_t;    /*8位整型*/

#define __IO  volatile  /*防止编译器优化*/

#define APB1PERIPH_BASE 0x40000000 /*APB1基地址*/
#define APB2PERIPH_BASE 0x40010000 /*APB2基地址*/
#define AHBPERIPH_BASE  0x40020000 /*AHB基地址,绕开SDIO等*/

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) /*GPIOA基地址*/
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) /*GPIOB基地址*/
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) /*GPIOC基地址*/
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) /*GPIOD基地址*/
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) /*GPIOE基地址*/ 
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) /*GPIOF基地址*/
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) /*GPIOG基地址*/


typedef struct
{
  __IO uint32_t CRL;  /*端口配置低寄存器*/
  __IO uint32_t CRH;  /*端口配置高寄存器*/
  __IO uint32_t IDR;  /*端口输入数据寄存器*/
  __IO uint32_t ODR;  /*端口输出数据寄存器*/
  __IO uint32_t BSRR; /*端口位设置/清楚寄存器*/
  __IO uint32_t BRR;  /*端口位清楚寄存器*/
  __IO uint32_t LCKR; /*端口配置锁定寄存器*/
} GPIO_TypeDef; /*GPIO结构体*/

#define GPIOA	((GPIO_TypeDef *)GPIOA_BASE) /*GPIOA结构体*/
#define GPIOB	((GPIO_TypeDef *)GPIOB_BASE) /*GPIOA结构体*/
#define GPIOC	((GPIO_TypeDef *)GPIOC_BASE) /*GPIOA结构体*/
#define GPIOD	((GPIO_TypeDef *)GPIOD_BASE) /*GPIOA结构体*/
#define GPIOE	((GPIO_TypeDef *)GPIOE_BASE) /*GPIOA结构体*/
#define GPIOF	((GPIO_TypeDef *)GPIOF_BASE) /*GPIOA结构体*/
#define GPIOG	((GPIO_TypeDef *)GPIOG_BASE) /*GPIOA结构体*/

#define RCC_BASE  (AHBPERIPH_BASE + 0x1000)  /*RCC基地址*/

typedef struct {
	__IO uint32_t CR; // 时钟控制寄存器
	__IO uint32_t CFGR; // 时钟配置寄存器
	__IO uint32_t CIR; // 时钟中断寄存器
	__IO uint32_t APB2RSTR; // APB2外设复位寄存器
	__IO uint32_t APB1RSTR; // APB1外设复位寄存器
	__IO uint32_t AHBENR; // AHB外设时钟使能寄存器
	__IO uint32_t APB2ENR; // APB2外设时钟使能寄存器
	__IO uint32_t APB1ENR; // APB1外设时钟使能寄存器
	__IO uint32_t BDCR; // 备份域控制寄存器
	__IO uint32_t CSR; //控制/状态寄存器
	__IO uint32_t AHBRSTR; // AHB外设时钟复位寄存器
	__IO uint32_t CFGR2; // 时钟配置寄存器
}RCC_Typedef; /*RCC结构体*/

#define RCC ((RCC_Typedef *)RCC_BASE)  /*RCC结构体*/

#endif

9.2 stm32f10x_gpio.h
#ifndef STM32F10X_GPIO_H__
#define STM32F10X_GPIO_H__
#include "stm32f10x.h"

typedef enum{ 
	GPIO_Speed_10MHz = 1, /*10MHZ*/
	GPIO_Speed_2MHz,      /*2MHZ*/
	GPIO_Speed_50MHz      /*50MHZ*/
}GPIOSpeed_TypeDef; /*速度结构体*/

typedef enum{ 
	/*
		规则
		1. 只考虑前7位。
		2. 第4位决定输入还是输出,1输出,0输入。
		3. 第5位和第6位共同决定上拉还是下拉,01 下拉,10 上拉。
		4. 第2、3位一起决定是模拟、浮空、复用、上拉、下拉等模式。
	*/
	GPIO_Mode_AIN = 0x0,  		  /*模拟输入*/
	GPIO_Mode_IN_FLOATING = 0x04,   /*浮空输入*/
	GPIO_Mode_IPD = 0x28,			  /*下拉输入*/
	GPIO_Mode_IPU = 0x48, 		  /*上拉输入*/	
	GPIO_Mode_Out_OD = 0x14,		  /*开漏输入*/
	GPIO_Mode_Out_PP = 0x10,		  /*推挽输入*/
	GPIO_Mode_AF_OD = 0x1C,		  /*复用开漏输入*/
	GPIO_Mode_AF_PP = 0x18		  /*复用推挽输入*/
}GPIOMode_TypeDef; /*模式结构体*/

typedef struct
{
	uint16_t GPIO_Pin;  /*引脚*/
	GPIOSpeed_TypeDef GPIO_Speed; /*速度*/
	GPIOMode_TypeDef GPIO_Mode;  /*模式*/
}GPIO_InitTypeDef; /*GPIO初始化结构体*/

#define SET		1   
#define RESET   0

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*Pin 0 */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*Pin 1 */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*Pin 2 */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*Pin 3 */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*Pin 4 */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*Pin 5 */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*Pin 6 */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*Pin 7 */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*Pin 8 */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*Pin 9 */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /*Pin 10*/
#define GPIO_Pin_11                ((uint16_t)0x0800)  /*Pin 11*/
#define GPIO_Pin_12                ((uint16_t)0x1000)  /*Pin 12*/
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*Pin 13*/
#define GPIO_Pin_14                ((uint16_t)0x4000)  /*Pin 14*/
#define GPIO_Pin_15                ((uint16_t)0x8000)  /*Pin 15*/
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*ALL   */

/*
	将GPIO_PIN对应IO口置1,
	GPIO_X:GPIOA-G
	GPIO_Pin:GPIO_Pin_0-15
*/
void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

/*
	将GPIO_PIN对应IO口清零,
	GPIO_X:GPIOA-G
	GPIO_Pin:GPIO_Pin_0-15
*/
void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

/*初始化结构体,设置工作模式*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

#endif

9.3 stm32f10x_gpio.c
#include "stm32f10x_gpio.h"

/*
	将GPIO_PIN对应IO口置1,
	GPIO_X:GPIOA-G
	GPIO_Pin:GPIO_Pin_0-15
*/
void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin){
	GPIOx->BSRR |= GPIO_Pin; 	/*将对应引脚置1,调用BSRR的低16位,BSRR置1*/
}
/*
	将GPIO_PIN对应IO口清零,
	GPIO_X:GPIOA-G
	GPIO_Pin:GPIO_Pin_0-15
*/
void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin){
	GPIOx->BRR |= GPIO_Pin;		/*将对应引脚清0,调用BRR的低16位,BRR置1*/
}


/*初始化结构体,设置工作模式*/
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 模式配置 --------------------------*/
  // 把输入参数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)
  { 
	// 输出模式则要设置输出速度
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
  // 配置端口低8位,即Pin0~Pin7
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
	// 先备份CRL寄存器的值
    tmpreg = GPIOx->CRL;
		
	// 循环,从Pin0开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
	 // pos的值为1左移pinpos位
      pos = ((uint32_t)0x01) << pinpos;
      
	  // 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			
	  //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		// pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
       //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);  
				
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }				
        else
        {
          // 判断是否为上拉输入模式
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
		    // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
		// 把前面处理后的暂存值写入到CRL寄存器之中
    GPIOx->CRL = tmpreg;
  }
/*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
  // 配置端口高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));
			
      // pos与输入参数GPIO_PIN作位与运算
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
			
	 //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		//pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
        
	    //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);
        
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
         // 判断是否为上拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
		  // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
	// 把前面处理后的暂存值写入到CRH寄存器之中
    GPIOx->CRH = tmpreg;
  }
}

9.4 main.c
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

int main(void){
	GPIO_InitTypeDef GPIO_InitStruct;
	
	/*打开时钟*/
	RCC->APB2ENR |= (1 << 3); /*使能GPIOB*/
	
	/*初始化GPIO结构体*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	GPIO_ResetBits(GPIOB,GPIO_Pin_0); /*点灯*/
	GPIO_SetBits(GPIOB,GPIO_Pin_0); /*熄灭*/
	
	while(1){
		
	}
}

void SystemInit(void){

}

10 按键原理图

1)指南者开发板按键原理

  • 用来RC电路实现硬件消抖,高电平时点亮。

stm32精简笔记1——GPIO_第2张图片

2)常见的LED接线方式:

  • 二极管低电平驱动、二极管高电平驱动、三极管低电平驱动、三极管高电平驱动。

11 按键消抖

1)按键抖动:机械开关在按下时由于弹性变形,按下或弹开的瞬间存在电压变化。

2)RC电路消抖:利用电容的充放电来消抖。

3)触发器消抖:将电压值锁存实现消抖,是目前最好的消抖方式。

4)软件消抖:利用延时或者中断来实现消抖。

  • 消抖模版:
uint8_t KeyScan(void){
    if(IO口的值为1){
        	延时20ms;
        	if(IO口的值为1){
                	return 1;
                }else{
                	return 0;
                }
    }else{
         	return 0;
    }
}

12 按键检测点亮LED

12.1 bsp_led.h
#ifndef BSP_LED_H__
#define BSP_LED_H__
#include "stm32f10x.h"

/*引脚定义*/

#define LED_Port	GPIOB			/*对应GPIO口*/
#define LED_Pin_R	GPIO_Pin_5		/*红灯对应引脚*/
#define LED_Pin_B	GPIO_Pin_1		/*蓝灯对应引脚*/
#define LED_Pin_G	GPIO_Pin_0		/*绿灯对应引脚*/

/*
初始化GPIO:
1.打开时钟;
2. 初始化GPIO结构体,设置模式、速度、引脚;
3. 熄灭所有LED
*/
void LED_Init(void);

/*点亮LED*/
void LED_ON(uint16_t LED_Pin);

/*熄灭LED*/
void LED_OFF(uint16_t LED_Pin);

/*翻转LED*/
void LED_Invert(uint16_t LED_Pin);

#endif
12.2 bsp_led.c
#include "bsp_led.h"


void LED_Init(void){
	/*声明初始化结构体*/
	GPIO_InitTypeDef GPIO_InitStruct;
	/*打开RCC时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	/*配置GPIO为:推挽输出、50MHz*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = LED_Pin_B | LED_Pin_G | LED_Pin_R;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(LED_Port,&GPIO_InitStruct);
	/*置1所有LED*/
	LED_Port->BSRR |= (LED_Pin_B | LED_Pin_G | LED_Pin_R);
}

void LED_ON(uint16_t LED_Pin){
	/*置1对应BRR引脚电平*/
	LED_Port->BRR |= LED_Pin;
}

void LED_OFF(uint16_t LED_Pin){
	/*置1对应BSRR引脚电平*/
	LED_Port->BSRR |= LED_Pin;
}

void LED_Invert(uint16_t LED_Pin){
	/*将对应位电平值取反*/
	LED_Port->ODR ^= LED_Pin;
}

12.3 bsp_key.h
#ifndef BSP_KEY_H__
#define BSP_KEY_H__
#include "stm32f10x.h"
/*按键1和按键2的引脚配置*/
#define KEY1_GPIO_Port	GPIOA
#define KEY1_GPIO_Pin	GPIO_Pin_0
#define KEY2_GPIO_Port	GPIOC
#define KEY2_GPIO_Pin	GPIO_Pin_13

/*初始化按键*/
void KEY_Init(void);

/*扫描按键*/
uint32_t KEY_Scan(void);

#endif
12.4 bsp_key.c
#include "bsp_key.h"


void KEY_Init(void){
	/*初始化结构体声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	/*打开对应时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	/*结构体初始化*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_Pin;
	
	GPIO_Init(KEY1_GPIO_Port,&GPIO_InitStruct);

	GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_Pin;
	GPIO_Init(KEY2_GPIO_Port,&GPIO_InitStruct);
}
uint32_t KEY_Scan(void){
	/*检查人对应位是否为1*/
	if(GPIO_ReadInputDataBit(KEY1_GPIO_Port,KEY1_GPIO_Pin) == SET){
		return 1;
	}
	if(GPIO_ReadInputDataBit(KEY2_GPIO_Port,KEY2_GPIO_Pin) == SET){
		return 2;
	}
	return 0;
}

你可能感兴趣的:(精简笔记,笔记,单片机,stm32)