精华内容
下载资源
问答
  • 通过上分析,LED能够点亮,但是程序很难理解与书写,本章使用固件库对上内容改进 、使用宏定义简化程序 宏定义优点: 本阶段使用是不带参数宏定义: 1)程序容易理解,可以将复杂宏重定义为...

    前言

    通过上一章的分析,LED能够点亮,但是程序很难理解与书写,本章使用固件库对上一章的内容改进

    一、使用宏定义简化程序

    宏定义的优点:
    本阶段使用的是不带参数的宏定义:
    1)程序容易理解,可以将复杂的宏重定义为简单的;
    2)方便修改,一个宏多用时,修改值仅修改一处即可

    二、STM32固件库理解

    1、地址理解

    下面梳理一下LED使用的寄存器宏定义的值
    在手册上中查找到寄存器的基地址为0x4000 0000在这里插入图片描述
    在定义宏的时候,先定义基地址,其他外设的地址根据基地址的偏移。
    在固件库中,

    外设基地址
    #define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
    
    #define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
    ;APB2地址
    ...
    #define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
    ;GPIOB地址,在APB2基础上增加了0x0C00,在存储器映射中可以查找到
    
    ;//gpiox 每组寄存器 
    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;
    
    <font size=3>GPIO理解
    ;//进行GPIO_TypeDef*转换,可以直接使用GPIOB->CRL,...
    #define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
    

    2、GPIO相关理解

    CPIO相关在stm32f10x_gpio.c stm32f10x_gpio.h

    ;GPIO引脚定义,对应ODR,IDR制定具体哪一位
    #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 */
    ;//gpio 速度 GPIOx_CRL/H下的MODEy
    typedef enum
    { 
      GPIO_Speed_10MHz = 1,
      GPIO_Speed_2MHz, 
      GPIO_Speed_50MHz
    }GPIOSpeed_TypeDef;
    ;//模式 GPIOx_CRL/H 下的CNFy
    typedef enum
    { 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;
    ;//GPIOx控制结构体,CRL/H
    typedef struct
    {
      uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                          This parameter can be any value of @ref GPIO_pins_define */
    
      GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                          This parameter can be a value of @ref GPIOSpeed_TypeDef */
    
      GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                          This parameter can be a value of @ref GPIOMode_TypeDef */
    }GPIO_InitTypeDef;
    
    ;//GPIO还提供了一系列函数,将参数传进即可
    /**
      * @brief  Initializes the GPIOx peripheral according to the specified
      *         parameters in the GPIO_InitStruct.
      * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
      * @param  GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
      *         contains the configuration information for the specified GPIO peripheral.
      * @retval None
      */
    void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
    {
    ...
    }
    

    GPIO_ResetBits在固件库中的定义

    /**
      * @brief  Clears the selected data port bits.
      * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
      * @param  GPIO_Pin: specifies the port bits to be written.
      *   This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
      * @retval None
      */
    void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    {
      /* Check the parameters */
      assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
      assert_param(IS_GPIO_PIN(GPIO_Pin));
      
      GPIOx->BRR = GPIO_Pin;
    }
    

    在这里插入图片描述
    在这里插入图片描述

    3、RCC相关理解

    RCC相关 在stm32f10x_rcc.c stm32f10x_rcc.h

    #define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
    #define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)
    #define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)
    #define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)
    #define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)
    #define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)
    #define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)
    #define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)
    #define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)
    #define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)
    #define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)
    #define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)
    #define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)
    #define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)
    #define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)
    #define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)
    #define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)
    #define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)
    #define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)
    #define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)
    #define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)
    /**
      * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
      * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
      *   This parameter can be any combination of the following values:
      *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
      *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
      *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
      *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
      *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
      *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
      *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
      * @param  NewState: new state of the specified peripheral clock.
      *   This parameter can be: ENABLE or DISABLE.
      * @retval None
      */
    void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
    {
    }
    
    

    从函数的注释可以看到,参数可以是很多外设;GPIOA到G;USART;TIM; AFIO;ADC等等;就是系统框图 挂在到APB2下的时钟。在这里插入图片描述
    库文件还有很多模块的介绍,本文仅剖析LED相关的定义。使用官方提供的固件库函数点亮LED例子如下:

    三、程序实现

    bsp_led.h

    #define LED_GPIO_PORT    	GPIOB	//端口		              
    #define LED_GPIO_CLK 	    RCC_APB2Periph_GPIOB//时钟参数		
    #define LED_GPIO_PIN		GPIO_Pin_5//			        
    

    bsp_led.c

    #include "bsp_led.h"   
    void LED_GPIO_Config(void)
    {		
    		/*定义GPIO结构体*/
    		GPIO_InitTypeDef GPIO_InitStructure;
    
    		/*开GPIOB时钟,APB2*/
    		RCC_APB2PeriphClockCmd( LED_GPIO_CLK, ENABLE);
    		/*具体是哪个引脚pin5*/
    		GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;	
    		/*GPIO模式*/
    		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    		/*GPIO速率 */   
    		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    		/*初始化GPIOB*/
    		GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);	
    					
    }
    
    

    main.c

    int main(void)
    {	
    	LED_GPIO_Config();//配置GPIO	 	  
    	GPIO_ResetBits(LED_GPIO_PORT,LED_GPIO_PIN);// 点亮LED
    }
    
    展开全文
  • STM32学习-0点亮一个LED

    2020-11-29 09:48:03
    组 FWLib 下面存放是 ST 官方提供固件库函数,里面函数我们可以根据需要添加和删除,但是一定要注意头文件 stm32f10x_conf.h 文件注释掉删除源文件对应头文件,这里面文件内容用户不需要修改。...

    0点亮一个LED

    程序树
    在这里插入图片描述

    • 组 FWLib 下面存放的是 ST 官方提供的固件库函数,里面的函数我们可以根据需要添加和删除,但是一定要注意在头文件 stm32f10x_conf.h 文件中注释掉删除的源文件对应的头文件,这里面的文件内容用户不需要修改。
    • 组 CORE 下面存放的是固件库必须的核心文件和启动文件。这里面的文件用户不需要修改。
    • 组SYSTEM是ALIENTEK提供的共用代码,这些代码的作用和讲解在第五章都有讲解,大家可以翻过去看下。
    • 组 HARDWARE 下面存放的是每个实验的外设驱动代码,他的实现是通过调用 FWLib下面的固件库文件实现的,比如 led.c 里面调用 stm32f10x_gpio.c 里面的函数对 led 进行初始化,这里面的函数是讲解的重点。后面的实验中可以看到会引入多个源文件。
    • 组 USER 下面存放的主要是用户代码。但是 system_stm32f10x.c 文件用户不需要修改,同时 stm32f10x_it.c 里面存放的是中断服务函数,这两个文件的作用在 3.1 节有讲解,大家可以翻过去看看。Main.c 函数主要存放的是主函数了,这个大家应该很清楚。

    在这里插入图片描述

    主程序代码

    #include "led.h"
    #include "delay.h"
    #include "sys.h"
    int main(void)
    {
        delay_init(); //延时函数初始化
        LED_Init(); //初始化与 LED 连接的硬件接口
        while(1)
        { 
            LED0=~LED0;
            delay_ms(1000); //延时 300ms
        }
    }
    
    

    延时函数初始化

    delay_init(); //延时函数初始化
    

    具体的实现方式为

    #include "delay.h"
    #include "sys.h"
    // 	 
    //如果使用ucos,则包括下面的头文件即可.
    #if SYSTEM_SUPPORT_UCOS
    #include "includes.h"					//ucos 使用	  
    #endif
    //	 
    //本程序只供学习使用,未经作者许可,不得用于其它任何用途
    //ALIENTEK STM32开发板
    //使用SysTick的普通计数模式对延迟进行管理
    //包括delay_us,delay_ms
    //正点原子@ALIENTEK
    //技术论坛:www.openedv.com
    //修改日期:2012/9/2
    //版本:V1.5
    //版权所有,盗版必究。
    //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
    //All rights reserved
    //********************************************************************************
    //V1.2修改说明
    //修正了中断中调用出现死循环的错误
    //防止延时不准确,采用do while结构!
    
    //V1.3修改说明
    //增加了对UCOSII延时的支持.
    //如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
    //delay_ms和delay_us也进行了针对ucos的改造.
    //delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
    //delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
    //可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.
    
    //V1.4修改说明 20110929
    //修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
    //V1.5修改说明 20120902
    //在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
    // 	 
    static u8  fac_us=0;//us延时倍乘数
    static u16 fac_ms=0;//ms延时倍乘数
    #ifdef OS_CRITICAL_METHOD 	//如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
    //systick中断服务函数,使用ucos时用到
    void SysTick_Handler(void)
    {				   
    	OSIntEnter();		//进入中断
        OSTimeTick();       //调用ucos的时钟服务程序               
        OSIntExit();        //触发任务切换软中断
    }
    #endif
    
    //初始化延迟函数
    //当使用ucos的时候,此函数会初始化ucos的时钟节拍
    //SYSTICK的时钟固定为HCLK时钟的1/8
    //SYSCLK:系统时钟
    void delay_init()	 
    {
    
    #ifdef OS_CRITICAL_METHOD 	//如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
    	u32 reload;
    #endif
    	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
    	fac_us=SystemCoreClock/8000000;	//为系统时钟的1/8  
    	 
    #ifdef OS_CRITICAL_METHOD 	//如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
    	reload=SystemCoreClock/8000000;		//每秒钟的计数次数 单位为K	   
    	reload*=1000000/OS_TICKS_PER_SEC;//根据OS_TICKS_PER_SEC设定溢出时间
    							//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右	
    	fac_ms=1000/OS_TICKS_PER_SEC;//代表ucos可以延时的最少单位	   
    	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
    	SysTick->LOAD=reload; 	//每1/OS_TICKS_PER_SEC秒中断一次	
    	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;   	//开启SYSTICK    
    #else
    	fac_ms=(u16)fac_us*1000;//非ucos下,代表每个ms需要的systick时钟数   
    #endif
    }								    
    
    #ifdef OS_CRITICAL_METHOD	//使用了ucos
    //延时nus
    //nus为要延时的us数.		    								   
    void delay_us(u32 nus)
    {		
    	u32 ticks;
    	u32 told,tnow,tcnt=0;
    	u32 reload=SysTick->LOAD;	//LOAD的值	    	 
    	ticks=nus*fac_us; 			//需要的节拍数	  		 
    	tcnt=0;
    	told=SysTick->VAL;        	//刚进入时的计数器值
    	while(1)
    	{
    		tnow=SysTick->VAL;	
    		if(tnow!=told)
    		{	    
    			if(tnow<told)tcnt+=told-tnow;//这里注意一下SYSTICK是一个递减的计数器就可以了.
    			else tcnt+=reload-tnow+told;	    
    			told=tnow;
    			if(tcnt>=ticks)break;//时间超过/等于要延迟的时间,则退出.
    		}  
    	}; 									    
    }
    //延时nms
    //nms:要延时的ms数
    void delay_ms(u16 nms)
    {	
    	if(OSRunning==TRUE)//如果os已经在跑了	    
    	{		  
    		if(nms>=fac_ms)//延时的时间大于ucos的最少时间周期 
    		{
       			OSTimeDly(nms/fac_ms);//ucos延时
    		}
    		nms%=fac_ms;				//ucos已经无法提供这么小的延时了,采用普通方式延时    
    	}
    	delay_us((u32)(nms*1000));	//普通方式延时,此时ucos无法启动调度.
    }
    #else//不用ucos时
    //延时nus
    //nus为要延时的us数.		    								   
    void delay_us(u32 nus)
    {		
    	u32 temp;	    	 
    	SysTick->LOAD=nus*fac_us; //时间加载	  		 
    	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;       //清空计数器	 
    }
    //延时nms
    //注意nms的范围
    //SysTick->LOAD为24位寄存器,所以,最大延时为:
    //nms<=0xffffff*8*1000/SYSCLK
    //SYSCLK单位为Hz,nms单位为ms
    //对72M条件下,nms<=1864 
    void delay_ms(u16 nms)
    {	 		  	  
    	u32 temp;		   
    	SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
    	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;       //清空计数器	  	    
    } 
    #endif
    

    主要是利用系统时钟SysTick实现的。该函数用来初始化 2 个重要参数:fac_us 以及 fac_ms;同时把 SysTick 的时钟源选择为外部时钟,如果使用了 ucos,那么还会根据 OS_TICKS_PER_SEC 的配置情况,来配置SysTick 的中断时间,并开启 SysTick 中断。

    关于delay_us()与delay_ms()函数

    利用SysTick时钟实现的。
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);这一句把SysTick的时钟选择外部时钟,这里需要注意的是:SysTick 的时钟源自 HCLK 的 8 分频,假设我们外部晶振为 8M,然后倍频到 72M,那么 SysTick 的时钟即为 9Mhz,也就是 SysTick 的计数器VAL 每减 1,就代表时间过了 1/9us。所以 fac_us=SystemCoreClock/8000000;这句话就是计算在 SystemCoreClock 时钟频率下延时 1us 需要多少个 SysTick 时钟周期。同理,fac_ms=(u16)fac_us*1000;就是计算延时 1ms 需要多少个 SysTick 时钟周期,它自然是 1us的 1000 倍。初始化将计算出 fac_us 和 fac_ms 的值。在不使用 ucos 的时候:fac_us,为 us 延时的基数,也就是延时 1us,SysTick->LOAD所应设置的值。fac_ms 为 ms 延时的基数,也就是延时 1ms,SysTick->LOAD 所应设置的值。fac_us 为 8 位整形数据,fac_ms 为 16 位整形数据。正因为如此,系统时钟如果不是 8的倍数,则会导致延时函数不准确,这也是我们推荐外部时钟选择 8M 的原因。这点大家要特别留意。
    当使用 ucos 的时候,fac_us,还是 us 延时的基数,不过这个值不会被写到SysTick->LOAD 寄存器来实现延时,而是通过时钟摘取的办法实现的(后面会介绍)。而fac_ms 则 代 表 ucos 自 带 的 延 时 函 数 所 能 实 现 的 最 小 延 时 时 间 ( 如OS_TICKS_PER_SEC=200,那么 fac_ms 就是 5ms)。

    初始化与 LED 连接的硬件接口

    LED_Init(); //初始化与 LED 连接的硬件接口
    

    实现如下

    #ifndef __LED_H
    #define __LED_H
    #include "sys.h"
    //LED 端口定义
    #define LED0 PAout(8) // PA8
    #define LED1 PDout(2) // PD2
    void LED_Init(void);//初始化
    #endif
    
    
    #include "led.h"
    //LED IO 初始化
    void LED_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE); //使能 PA,PD 端口时钟
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PA.8 端口配置
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度为 50MHz
        GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化 GPIOA.8
        GPIO_SetBits(GPIOA,GPIO_Pin_8); //PA.8 输出高
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //LED1-->PD.2 端口配置, 推挽输出
        GPIO_Init(GPIOD, &GPIO_InitStructure); //推挽输出 ,IO 口速度为 50MHz
        GPIO_SetBits(GPIOD,GPIO_Pin_2); //PD.2 输出高
    }
    
    

    GPIO口的参数配置

    STM32 的 IO 口相比 51 而言要复杂得多,所以使用起来也困难很多。首先 STM32 的 IO 口
    可以由软件配置成如下 8 种模式:
    1、输入浮空
    2、输入上拉
    3、输入下拉
    4、模拟输入
    5、开漏输出
    6、推挽输出
    7、推挽式复用功能
    8、开漏复用功能
    每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问。STM32 的很多 IO 口都是 5V 兼容的,这些 IO 口在与 5V 电平的外设连接的时候很有优势,具体哪些 IO 口是 5V 兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O Level 标 FT 的就是 5V 电平兼容的)。

    由于STM32有很多寄存器,为了提高效率,需要用不同的时钟来配置IO口。
    在使用一个IO口之前,需要对IO口的引脚、GPIO口的速度、GPIO口的模式进行配置。
    为了方便起见,我们使用结构体进行对参数的赋值,使用指针进行对参数传递。

    /* 
      ******************************************************************************
      * @file    stm32f10x_gpio.h
      * @author  MCD Application Team
      * @version V3.5.0
      * @date    11-March-2011
      * @brief   This file contains all the functions prototypes for the GPIO 
      *          firmware library.
      * 
      * 
    */
    typedef struct
    {
    	uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
    	                                  This parameter can be any value of @ref GPIO_pins_define */
    	
    	GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
    	                                  This parameter can be a value of @ref GPIOSpeed_TypeDef */
    	
    	GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
    	                                  This parameter can be a value of @ref GPIOMode_TypeDef */
    }GPIO_InitTypeDef;
    

    能够看出,我们是使用这个函数来进行对GPIO口的参数配置

    GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化 GPIOA.8
    

    函数原型

    void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
    

    用枚举类型enum说明参数

    //GPIO的模式选择
    typedef enum
    { 
    	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;
    
    //GPIO的传输速度选择
    typedef enum
    {
    	GPIO_Speed_10MHz = 1,
    	GPIO_Speed_2MHz,
    	GPIO_Speed_50MHz
    }GPIOSpeed_TypeDef;
    

    枚举类型enum参考

    GPIO口的操作

    • 读写GPIO口电平的状态

    IDR 是一个端口输入数据寄存器,只用了低 16 位。该寄存器为只读寄存器,并且只能以16 位的形式读出。
    要想知道某个 IO 口的电平状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起来是比较简单的。

    ODR 是一个端口输出数据寄存器,也只用了低 16 位。该寄存器为可读写,从该寄存器读
    出来的数据可以用于判断当前 IO 口的输出状态

    BSRR 寄存器是端口位设置/清除寄存器。该寄存器和 ODR 寄存器具有类似的作用,都可
    以用来设置 GPIO 端口的输出位是 1 还是 0。下面我们看看该寄存器的描述如下图:
    需要注意的是:这个寄存器的高16位是用来给端口置0的,低16位是用来给端口置1的。
    在这里插入图片描述
    在这里插入图片描述

    操作GPIO口的输入\输出电平状态

    函数 说明 返回值 使用的寄存器
    uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 读取输入电平的状态 1(Bit_SET)或者 0(Bit_RESET); IDR
    void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); 设置高低电平 ODR
    void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 设置为高电平 BSRR 和 BRR
    void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 设置为低电平 BSRR 和 BRR

    flag = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取PA5的输入电平状态
    GPIO_SetBits(GPIOB, GPIO_Pin_5);   			    //设置为PB5为输出高电平
    GPIO_ResetBits (GPIOB, GPIO_Pin_5);             //设置PB5输出低电平
    

    这里我们还是做个概括性的总结,操作步骤为:
    1) 使能 IO 口时钟。调用函数为 RCC_APB2PeriphClockCmd()。
    2) 初始化 IO 参数。调用函数 GPIO_Init();
    3) 操作 IO。操作 IO 的方法就是上面我们讲解的方法。
    上面我们讲解了 STM32 IO 口的基本知识以及固件库操作 GPIO 的一些函数方法。

    示例-流水灯的实现

    参考我的这一篇文章

    展开全文
  • STM32入坑教程(一)点亮一个LED

    千次阅读 2020-05-28 15:26:10
    STM32入门教程(一)点亮一个LED灯随表聊聊配置流程一.初始化引脚二.主函数实现逻辑备注 随表聊聊 作为所有入坑单片机同学来说,第一个单片机程序必然是点亮一个LED灯,这就类似于学编程第一个程序必然是‘Hello...

    随表聊聊

    作为所有入坑单片机的同学来说,第一个单片机程序必然是点亮一个LED灯,这就类似于学编程第一个程序必然是‘Hello World’一样。可以说点亮了第一个LED灯也就照亮了我们的单片机之路的起点。关于如何安装KEIL以及如何配置文件,这里就不在赘述,CSDN上很多。这里主要讲述一下LED灯的配置流程:

    配置流程

    一.初始化引脚

    第一步:使能LED端口
    第二步:配置端口的工作模式并初始化
    第三步:配置端口默认电平

    #include "LED.h"
    #include "stm32f10x.h"
    
    void LED_Init(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;//定义一个变量
    	
    	//第一步: 使能LED端口-GPIOA时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    	
    	//第二步:配置端口的工作模式并初始化
    	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;     //模式:推挽输出
    	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;             //IO口:8
    	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;     //速度:50M
    	GPIO_Init(GPIOA,&GPIO_InitStructure);               //初始化GPIO
    	//第三步:配置端口默认电平
    	GPIO_SetBits(GPIOA,GPIO_Pin_8);   //A8输出高电平
    }
    

    二.主函数中实现逻辑

    第一步:初始化硬件
    第二步:配置引脚电平

    int main(void)
    {
    	LED_Init();
    	GPIO_ResetBits(GPIOA,GPIO_Pin_8);
    	while(1);
    }
    

    效果如下图
    为什么图片这么大呢,将就看下吧
    是不是很 easy,赶快上机试试吧!!

    备注

    1. 配置时要注意使能配置引脚对应的端口号。
    2. 如果有疑问,请点击下方源码传送门,免费下载
      链接:https://pan.baidu.com/s/1LnUL-Dk9l3GwjRWxqxrMgQ
      提取码:29o0
    展开全文
  • STM32CubeMX 点亮led

    2020-12-15 21:01:10
    1、如图所示点击这两个中的一个都可以 2、点击之后可能会出现下面这种情况,直接关掉就可以 3、选择芯片(根据自己板子选择) 4、引脚配置(根据自己板子型号查找原理图对应得引脚号) 如图所示,这里有个...

    一、打开软件创建工程

    1、如图所示点击这两个中的任一个都可以
    在这里插入图片描述
    2、点击之后可能会出现下面这种情况,直接关掉就可以
    在这里插入图片描述
    3、选择芯片(根据自己板子选择)

    在这里插入图片描述
    4、引脚配置(根据自己板子的型号查找原理图对应得引脚号)
    如图所示,这里有个搜索,方便快捷
    在这里插入图片描述
    如图所示将引脚设置为输出模式
    在这里插入图片描述
    5、暂且使用不到外部时钟,先不设置
    6、文件设置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    7、生成文件
    在这里插入图片描述
    在这里插入图片描述
    这个没有影响,它提示已经创建成功了,只是不能在这直接打开文件,要到保存目录下打开

    二、打开程序添加代码

    1、打开这个文件,先编译一下
    在这里插入图片描述
    2、报错,如图所示
    在这里插入图片描述
    在生成的文件夹下有一个文件,如下图所示
    在这里插入图片描述
    将这个文件添加到工程中就可以了,步骤如下
    (1)
    在这里插入图片描述
    (2)
    在这里插入图片描述
    然后在编译就不会出现错误了
    3、添加代码
    先看一下gpio的初始化
    在这里插入图片描述
    4、在main函数中添加代码
    在这里插入图片描述

    5、下载程序
    我用的是ST-Link下载,配置大体如下
    (1)
    在这里插入图片描述
    (2)
    在这里插入图片描述
    (3)
    在这里插入图片描述
    (4)点击setings
    在这里插入图片描述
    在这里插入图片描述
    (5)最后下载
    在这里插入图片描述

    展开全文
  • STM32F103入门_点亮LED

    千次阅读 2019-02-25 09:58:22
    大家看这个51的程序,只要将LED灯的负极接入电源负极,灯正极串联200欧(经过发光二极管会有一个电压降低值,简称压降,一般为2V,51单片机输入高电平为5V,直接接入LED灯,会将灯烧掉,欧姆定律大家都知道,直接...
  • stm32f4固件库函数点亮LED

    千次阅读 2018-10-14 12:02:11
    这些LED灯阴极都是与STM32的GPIO引脚相连接,我们只需要控制对应引脚输出低电平,即可点亮LED灯,其中彩灯阳极连接到的一个电路图符 号“口口”,它表示引出排针,即此处本身断开,须通过跳线帽连接排针,把...
  • 点亮LED,需要完成LED的驱动, 工程模板上新建一个led.c和led.h文件,将其存放led文件夹内。这两个文件需要我们自己编写。 通常xxx.c文件用于存放编写的驱动程序,xxx.h文件用于存放xxx.c内的stm32头文件、...
  • STM32 实战 按键点亮LED(中断)

    千次阅读 2018-05-20 16:13:03
    来看看最简单的点亮一个实验你把,实验要求使用KEY4*4按键,按键按下灯亮,按键拿起灯灭,如此一直循环:程序的思路如下:1、首先需要设置是时钟:你将灯管脚连哪个端口某个引脚上面,你就开启哪个端口...
  • STM32实战1:按键点亮LED小灯

    千次阅读 2018-05-22 19:01:59
    32的理论知识已经学习完成,之后我们进入实战篇,实战的学习,我完成了第一个项目,用按键点亮led灯下面是我的程序led主程序#include "sys.h"#include "led.h"void LED_Init(void){ ...
  • stm32开发通常windows下用keil进行开发。但是keil编译一个简单的点亮led的程序,新建工程的过程感觉都比较复杂。集成开发环境只需要告诉芯片类型就能自动指定交叉编译工具,而且不需要写makefile ...
  • 而对于当下的stm32f103来说,我们可以0x08000000处写入栈顶地址,也可以汇编程序中使用指令设置SP 二、MDK中实现 修改start.s,0x08000000处写入栈顶地址,如下所示: Stack_Size EQU 0x00000400 ;定义...
  • 调用__main函数 __main函数是C/C++运行时库的一个函数,嵌入式系统进入应用主程序之前必须有一个初始化过程,使用__main标号引导系统时必须将应用程序的入口定义为main()。 ARM启动代码_main 与用户主程序main...
  • 那么在程序中这样可以实现TTL输出电平控制LED的点亮吗?(LED是一个推挽输出IO的定义) ``` int main(void) { delay_init(); LED_Init(); while(1) {LED = 0; if(DOUT==1) LED=1;} ...
  • 因此目前而言,如果重新建工程,请在一开始时候(在新建STM32工程前)通过WIndows—>Preferences把地址改成之前固件库地址。 另外如果,是重新新建工程,他可能会提示你更新,如下图所示 这绝对是伴随着...
  • 本次程序设计和仿真是基于Proteus和keil的环境对STM32F103系列单片机进行流水灯设计,通过配置STM32的GPIO工作模式,实现LED的点亮和熄灭;通过配置8位流水灯程序设计,实现灯的流水实现。 关键字:Proteus、keil、...
  • 单片机作为一种微控制器,最基本的用途便是通过其引脚与外界进行交互,而单片机编程界,有这么一个程序...设计流水灯为间隔固定的时间每次点亮一个LED,因此在点亮一个LED的同时,要关闭上一个LED,并且进行计时,
  • 1、用STM32F103ZET6点亮LED灯(LED灯配置PE4管脚上); 2、通过串口每秒向电脑发送一个“Heart!”。 二、步骤 1、创建环境 2.LED.c写入以下程序 配置 STM32 外设时候,任何时候都要先使能该外设时钟...
  • stm32f030 HAL库定时器中断问题

    千次阅读 2020-04-21 16:07:10
    stm32f030的HAL库编程,发现使用定时器中断时,按照官方示例代码给出的程序,可以点亮LED灯,并且按设定的定时值闪烁。但第次进入中断的时间并不是自己希望的时间间隔,经测试,只有几ms,说明HAL库中在开启...
  • STM32的USART串行口通讯

    2020-11-29 19:28:01
    在LED的点亮中,从寄存器映射开始,把内存跟寄存器建立起一一对应的关系,然后操作寄存器点亮 LED,再把寄存器操作封装成一个个函数。之后调用库函数点亮流水灯。 使用函数来控制 LED 灯与之前直接控制寄存器已经有...
  • 、硬件设计 ...如果使用是其他板子,连接 LED 管脚和极性不一样,那么只需要在程序中修改对应 GPIO 管脚和输出电平状态即可,原理是一样点亮 DS0 发光二极管,即让 STM32 PB5 管脚输出
  • 在上篇帖子(https://www.stmcu.org.cn/module/forum/thread-603787-1-1.html),我们使用gcc和Makefile成功的点亮led灯。却发现程序有点大,占用了6K多代码空间。相比keil mdk下代码量,这确实大多...
  • 运行简单stm32程序点亮LED灯) 打开keil,新建一个工程,并输入工程名和存储路径 新建main.c文件输入mian函数 int main(void) { //打开GPIOB时钟 *(unsigned int *)0x40021018 |= (1 << 3); ...
  • 以下只是个人学习的要点和个人观点,若整理的知识要点有问题,可以提出来,一起学习进步,嘻嘻嘻。 自毕业一年以后,我和我的... 我们就从最简单的程序开始把,点亮一个LED灯。 看一下正点原子的程序: #includ...
  • 嵌入式开发点亮LED灯也是各种架构和开发板一个程序,其中很多东西是和单片机例如stm32是类似,只是,现在我们没有了库函数,我们要自己完成一些东西。 先说启动文件,st官方已结给我们做好了,但是jz...
  • 把上次的例程初始化控制LED的GPIO口的部分拿出来放到一个函数LED_Iint( )。注意,初始化时先LED_Iint( )的最后点亮LED,目的是为了验证初始化部分是成功的,之后再改写程序为熄灭LED。用途:分隔故障。 ...
  • 按键中断 STM32使用按键中断需要配置端口IO,EXTI和NVIC。 当按键按下时候,引脚电平发生转变,同时触发沿触发...首先,key.h定义一个中断模式开启开关宏定义,可以切换按键扫描和按键中断,使在程序设计
  • stm32中的启动文件就是用这作用,也是用汇编写启动文件,由官方编写好了。 arm 目前按照Context 系列进行分类,分为M R A 系列,每系列市场不同,R主要是关注实时性,M广泛应用嵌入式,A应用...
  • 使用STM32的过程,尤其是刚开始学习使用的时候,由于不知道自己的程序写的对不对,就经常需要一点验证的方法,点亮一个LED灯就是最简单的验证方法,但是有的时候还经常需要串口的输出来验证自己的程序是否正确,...

空空如也

空空如也

1 2
收藏数 37
精华内容 14
关键字:

在一个stm32点亮led的程序中