嵌入式开发(学习笔记:跑马灯)GPIO_InitTypeDef+SysTick_Type

本文详细介绍了STM32F4系列芯片的GPIO初始化结构体GPIO_InitTypeDef,包括Pin、Mode、Pull、Speed和Alternate参数,并通过实例展示了如何配置GPIO进行跑马灯效果。同时,深入解析了SysTick定时器的工作原理,实现了一个简单的延时函数,用于控制LED灯的亮灭周期。
摘要由CSDN通过智能技术生成

一、GPIO是什么?

百度查的资料复制方便阅读

1、GPIO(英语:General-purpose input/output),通用型之输入输出的简称,功能类似8051的P0—P3,其接脚可以供使用者由程控自由使用,PIN脚依现实考量可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),如当clk generator, chip select等。

既然一个引脚可以用于输入、输出或其他特殊功能,那么一定有寄存器用来选择这些功能。对于输入,一定可以通过读取某个寄存器来确定引脚电位的高低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。

1. GPxCON 寄存器

gpiogpio

用于配置引脚功能。 Configure

PORT A 与 PORT B~PORT H/J 在功能选择上有所不同,GPACON 中每一位对应一根引脚,共 23 个引脚。当某位被设为 0 时候,相应引脚为 输出引脚。此时我们可以在GPADAT 中相应的写入 1或者 0 来让此引脚输出高电平或者低电平;当某位被设为1时,相应引脚为地址线或用于地址控制,此时GPADATA无用。

     一般而言 GPACON 通常被设为 1 ,以便访问外部器件。

     PORT B~PORT H/J

     在寄存器操作方面完全相同,GPxCON 中每两位控制一根引脚,

     00 输入                  01 输出

     10 特殊功能           11 保留不用

2. GPxDAT 寄存器

         GPxDAT用于读写引脚,当引脚被设为输入时候,读此寄存器可知道相应引脚的电平状态高还是低,当引脚被设为输出时候,写此寄存器的位,可令引脚输出高电平还是低电平。

3. GPxUP寄存器

            GPxUP寄存器某位为1时,相应管脚没有内部上拉电阻;为 0 时候 相应管脚有内部上拉电阻。

    上拉电阻作用在于,当GPIO 引脚处于第三种状态时候,既不是输出高电平,也不是输出低电平。而是呈现高阻态,相当于没有接芯片。它的电平状态由上下拉电阻决定

二、了解GPIO_InitTypeDef(芯片是STM32F407ZGT6)

1、GPIO_InitTypeDef 中包含参数。

typedef struct
{
  uint32_t Pin;       /*!< 指定要配置的GPIO引脚。
                           此参数可以是的任何值 @ref GPIO_pins_define */

  uint32_t Mode;      /*!< 指定选定接点的操作模式。
                           此参数的值可以是 @ref GPIO_mode_define */

  uint32_t Pull;      /*!< 指定选定接点的上拉或下拉激活。
                           此参数的值可以是 @ref GPIO_pull_define */

  uint32_t Speed;     /*!< 指定选定接点的速度。
                           此参数的值可以是 @ref GPIO_speed_define */

  uint32_t Alternate;  /*!< 要连接到所选引脚的外围设备。
                            此参数的值可以是 @ref GPIO_Alternate_function_selection */
}GPIO_InitTypeDef;

2、查看每个参数中可以赋的值

     a)GPIO_pins_define 中的所有可选择的值,代表各个引脚。

/** @defgroup GPIO_pins_define GPIO pins define
  * @{
  */
#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
#define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */

#define GPIO_PIN_MASK              0x0000FFFFU /* PIN mask for assert test */
/**
  * @}
  */

b)GPIO_mode_define 中的所有可选择的值,代表可选的GPIO配置模式。

/** @defgroup GPIO_mode_define GPIO模式定义
  * @简要GPIO配置模式
  *        元素值惯例: 0xX0yz00YZ
  *           - X  : GPIO模式或EXTI模式
  *           - y  : 外部IT或事件触发器检测
  *           - z  : 外部IT或事件的IO配置
  *           - Y  : 输出类型(推拉或开漏)
  *           - Z  : IO方向模式(输入、输出、交替或模拟)
  * @{
  */ 
#define  GPIO_MODE_INPUT                        0x00000000U   /*!< 输入浮动模式 */             
#define  GPIO_MODE_OUTPUT_PP                    0x00000001U   /*!< 输出推拉模式 */            
#define  GPIO_MODE_OUTPUT_OD                    0x00000011U   /*!< 输出开漏模式 */               
#define  GPIO_MODE_AF_PP                        0x00000002U   /*!< 交替功能推拉模式 */
#define  GPIO_MODE_AF_OD                        0x00000012U   /*!< 交替功能开漏模式 */

#define  GPIO_MODE_ANALOG                       0x00000003U   /*!< 模拟模式  */
    
#define  GPIO_MODE_IT_RISING                    0x10110000U   /*!< 带上升沿触发检测的外部中断模式 */
#define  GPIO_MODE_IT_FALLING                   0x10210000U   /*!< 带下降沿触发检测的外部中断模式 */
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000U   /*!< 带上升/下降沿触发检测的外部中断模式 */
 
#define  GPIO_MODE_EVT_RISING                   0x10120000U   /*!< 具有上升沿触发检测的外部事件模式 */
#define  GPIO_MODE_EVT_FALLING                  0x10220000U   /*!< 具有下降沿触发检测的外部事件模式 */
#define  GPIO_MODE_EVT_RISING_FALLING           0x10320000U   /*!< 具有上升/下降沿触发检测的外部事件模式 */
/**
  * @}
  */

c)GPIO_pull_define  中的所有可选择的值,配置激活状态。

/** @defgroup GPIO_pull_define GPIO pull define
   * @brief GPIO Pull-Up or Pull-Down 激活
   * @{
   */  
#define  GPIO_NOPULL        0x00000000U   /*!< 无上拉或下拉激活  */
#define  GPIO_PULLUP        0x00000001U   /*!< 上拉激活         */
#define  GPIO_PULLDOWN      0x00000002U   /*!< 下拉激活         */
/**
  * @}
  */

d)GPIO_speed_define 中的所有可选择的值,配置输出最大功率。

/** @defgroup GPIO_speed_define  GPIO 速度定义
  * @brief GPIO 输出最大频率
  * @{
  */
#define  GPIO_SPEED_FREQ_LOW         0x00000000U  /*!< IO工作频率为2 MHz,请参阅产品数据表 */
#define  GPIO_SPEED_FREQ_MEDIUM      0x00000001U  /*!< 范围12.5 MHz至50 MHz */
#define  GPIO_SPEED_FREQ_HIGH        0x00000002U  /*!< 范围为25兆赫至100兆赫 */
#define  GPIO_SPEED_FREQ_VERY_HIGH   0x00000003U  /*!< 范围50兆赫至200兆赫 */
/**
  * @}
  */

e)GPIO_Alternate_function_selection 暂时不知道是干嘛的,以后知道了记得补上。

三、简单写个demo

1、准备做个跑马灯效果,这里用的是GPIO_PIN_3 和GPIO_PIN_4引脚连接的LED。

 a)写个main函数

int main(void){
__HAL_RCC_GPIOE_CLK_ENABLE();   // 开启GPIOE时钟 RCC外围时钟启用后的延迟
// 定义对象
GPIO_InitTypeDef typeDef;
//设置引脚3 和 4(在芯片对应的引脚标记是PE3和PE4,目前不知道是干嘛的)
typeDef.Pin=GPIO_PIN_3|GPIO_PIN_4;
//设置模式为 输出推拉模式
typeDef.Mode=GPIO_MODE_OUTPUT_PP;
//设置激活属性为 上拉激活
typeDef.Pull=GPIO_PULLUP; 
	
/**
  * @brief  根据GPIO_Init中指定的参数初始化GPIOx外围设备。
  * @param  GPIOx其中x可以是(A..K)为STM32F429X设备选择GPIO外围设备,或者x可以是(A..I)为STM32F40XX和STM32F427X设备选择GPIO外围设备。
  * @param  指向包含指定GPIO外围设备配置信息的GPIO_InitTypeDef结构的GPIO_Init指针。
  * @retval 没有
  */
HAL_GPIO_Init(GPIOE,&typeDef);//初始化操作
/**
  * @brief  设置或清除选定的数据端口位。
  *
  * @note   此函数使用GPIOx_BSRR寄存器来允许原子读取/修改访问。这样,在读取和修改访问之间就没有发生IRQ的风险。
  *
  * @param  GPIOx其中x可以是(A..K)为STM32F429X设备选择GPIO外围设备,或者x可以是(A..I)为STM32F40XX和STM32F427X设备选择GPIO外围设备。
  * @param  GPIO_Pin 指定要写入的端口位。此参数可以是GPIO_PIN_x之一,其中x可以是(0..15)。
  * @param  PinState指定要写入选定位的值。此参数可以是GPIO_PinState枚举值之一: 
  *            @arg GPIO_PIN_RESET: 清除端口引脚
  *            @arg GPIO_PIN_SET: 设置端口引脚
  * @retval 没有
  */
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3,GPIO_PIN_SET);	//到这里还没有LED灯还是没有亮    
}

b)这个时候出现了一个GPIO_TypeDef的指针 GPIOE,看一下代码

在stm32f407xx.h中是这么写的(应该是指向了一个地址,暂时没了解)

#define PERIPH_BASE           0x40000000UL /*!< 别名区域中的外设基址 */

#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000UL)

#define GPIOE_BASE            (AHB1PERIPH_BASE + 0x1000UL)

#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)

 GPIO_TypeDef 中包含的参数。

/** 
  * @brief 通用I/O
  */

typedef struct
{
  __IO uint32_t MODER;    /*!< GPIO 端口模式寄存器,             地址偏移量: 0x00      */
  __IO uint32_t OTYPER;   /*!< GPIO 端口输出类型寄存器,         地址偏移量: 0x04      */
  __IO uint32_t OSPEEDR;  /*!< GPIO 端口输出速度寄存器,         地址偏移量: 0x08      */
  __IO uint32_t PUPDR;    /*!< GPIO 端口上拉/下拉寄存器,        地址偏移量: 0x0C      */
  __IO uint32_t IDR;      /*!< GPIO 端口输入数据寄存器,         地址偏移量: 0x10      */
  __IO uint32_t ODR;      /*!< GPIO 端口输出数据寄存器,         地址偏移量: 0x14      */
  __IO uint32_t BSRR;     /*!< GPIO 端口位设置/重置寄存器,      地址偏移量: 0x18      */
  __IO uint32_t LCKR;     /*!< GPIO 端口配置锁寄存器,           地址偏移量: 0x1C      */
  __IO uint32_t AFR[2];   /*!< GPIO 备用功能寄存器,             地址偏移量: 0x20-0x24 */
} GPIO_TypeDef;

 c)看完了代码后,又参考买板子的时候商家送的demo发现只要操作ODR这个输出数据的参数就可以了。

然后接着a)的代码继续写。

HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3,GPIO_PIN_SET);	//到这里还没有LED灯还是没有亮
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_4,GPIO_PIN_SET);	//到这里还没有LED灯还是没有亮
//GPIOE是一个GPIO_TypeDef的指针

//测试结果,跟16进制有关系.
//------ 1111 1111 1111 1111 (全不亮)  
//------ 1111 1111 1110 0111 (全亮)    
//------ 1111 1111 1111 0111 (PE3亮,PE4灭) 
//------ 1111 1111 1110 1111 (PE3灭,PE4亮) 
// 引脚的顺序是倒叙排的,PE15 >> .... >> PE0
// 所以只要保证PE3和PE4的位置对应上就可以了
// PE3亮 PE4灭 的十进制数为:16
// PE3灭 PE4亮 的十进制数为:8
// PE3亮 PE4亮 的十进制数为:0
// PE3灭 PE4灭 的十进制数为:24
//GPIOE->ODR=16;
GPIOE->ODR=8;
//GPIOE->ODR=0;
//GPIOE->ODR=24;

好了!现在能亮能灭了,在来个定时器就可以了!!!

2、怎么写定时器呢?看了一篇文章《SysTick实现延时》后尝试自己试试。

a)首先使用SysTick这个,类型为SysTick_Type的指针来操作,SysTick在core_cm4.h的头文件下。


/*!< 系统控制空间基址 */
#define SCS_BASE            (0xE000E000UL)                            

/*!< SysTick 基址 */
#define SysTick_BASE        (SCS_BASE +  0x0010UL)                    

/*!< SysTick 配置结构 */
#define SysTick             ((SysTick_Type   *)     SysTick_BASE  )

就看SysTick_Type是怎么定义的就可以了。

/**
  \组内 CMSIS_core_register
  \定义组 CMSIS_SysTick     系统计时 (SysTick)
  \简介    系统计时器寄存器的类型定义。
  @{
 */

/**
  \简介  访问系统计时器的结构类型 (SysTick).
 */
typedef struct
{
  __IOM uint32_t CTRL;                   /*!< 偏移量:0x000(R/W)SysTick控制和状态寄存器 */
  __IOM uint32_t LOAD;                   /*!< 偏移量:0x004(R/W)SysTick重新加载值寄存器 */
  __IOM uint32_t VAL;                    /*!< 偏移量:0x008(R/W)SysTick当前值寄存器 */
  __IM  uint32_t CALIB;                  /*!< 偏移量:0x00C(R/)SysTick校准寄存器 */
} SysTick_Type;

/* SysTick控制/状态寄存器定义 */
#define SysTick_CTRL_COUNTFLAG_Pos         16U                                            /*!< SysTick CTRL: 计数标志 Position */
#define SysTick_CTRL_COUNTFLAG_Msk         (1UL << SysTick_CTRL_COUNTFLAG_Pos)            /*!< SysTick CTRL: 计数标志 Mask */

#define SysTick_CTRL_CLKSOURCE_Pos          2U                                            /*!< SysTick CTRL: CLKSOURCE Position */
#define SysTick_CTRL_CLKSOURCE_Msk         (1UL << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */

#define SysTick_CTRL_TICKINT_Pos            1U                                            /*!< SysTick CTRL: TICKINT Position */
#define SysTick_CTRL_TICKINT_Msk           (1UL << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */

#define SysTick_CTRL_ENABLE_Pos             0U                                            /*!< SysTick CTRL: ENABLE Position */
#define SysTick_CTRL_ENABLE_Msk            (1UL /*<< SysTick_CTRL_ENABLE_Pos*/)           /*!< SysTick CTRL: ENABLE Mask */

/* SysTick Reload Register Definitions */
#define SysTick_LOAD_RELOAD_Pos             0U                                            /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/)    /*!< SysTick LOAD: RELOAD Mask */

/* SysTick Current Register Definitions */
#define SysTick_VAL_CURRENT_Pos             0U                                            /*!< SysTick VAL: CURRENT Position */
#define SysTick_VAL_CURRENT_Msk            (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/)    /*!< SysTick VAL: CURRENT Mask */

/* SysTick Calibration Register Definitions */
#define SysTick_CALIB_NOREF_Pos            31U                                            /*!< SysTick CALIB: NOREF Position */
#define SysTick_CALIB_NOREF_Msk            (1UL << SysTick_CALIB_NOREF_Pos)               /*!< SysTick CALIB: NOREF Mask */

#define SysTick_CALIB_SKEW_Pos             30U                                            /*!< SysTick CALIB: SKEW Position */
#define SysTick_CALIB_SKEW_Msk             (1UL << SysTick_CALIB_SKEW_Pos)                /*!< SysTick CALIB: SKEW Mask */

#define SysTick_CALIB_TENMS_Pos             0U                                            /*!< SysTick CALIB: TENMS Position */
#define SysTick_CALIB_TENMS_Msk            (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/)    /*!< SysTick CALIB: TENMS Mask */

/*@} CMSIS_SysTick组结束 */

然后自己写个计时器。


/****************************************************************************
* 名    称: void delay(void)
* 功    能:延时
* 返回参数:无
****************************************************************************/
void delay(u32 time)
{		   	 
//  SysTick->CTRL;                   /*!< 偏移量:0x000(R/W)SysTick控制和状态寄存器 */
//  SysTick->LOAD;                   /*!< 偏移量:0x004(R/W)SysTick重新加载值寄存器 */
//  SysTick->VAL;                    /*!< 偏移量:0x008(R/W)SysTick当前值寄存器 */
//  SysTick->CALIB;                  /*!< 偏移量:0x00C(R/)SysTick校准寄存器 */
	

  u32 temp;	    	 
  SysTick->LOAD=2000*time;  	 
  SysTick->VAL=0x00;
  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk  ;	 
  do
  {
    temp=SysTick->CTRL;
  }while((temp&0x01)&&!(temp&(1<<16)));
  SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
  SysTick->VAL =0X00;

}

然后回到main函数中写一个跑马灯效果。

	//测试结果,跟16进制有关系.
	//------ 1111 1111 1111 1111 (全不亮)  
	//------ 1111 1111 1110 0111 (全亮)    
	//------ 1111 1111 1111 0111 (PE3亮,PE4灭) 
	//------ 1111 1111 1110 1111 (PE3灭,PE4亮) 
	// 引脚的顺序是倒叙排的,PE15 >> .... >> PE0
	// 所以只要保证PE3和PE4的位置对应上就可以了
	// PE3亮 PE4灭 的十进制数为:16
	// PE3灭 PE4亮 的十进制数为:8
	// PE3亮 PE4亮 的十进制数为:0
	// PE3灭 PE4灭 的十进制数为:24
	while(1){//无限循环
		GPIOE->ODR=16;
		delay(500);
		GPIOE->ODR=8;
		delay(500);
		GPIOE->ODR=0;
		delay(500);
		GPIOE->ODR=24;
		delay(500);
	}

跑马灯结束~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值