• STM32(2)——GPIO


          对于初学者而言,最简单的是对芯片上的IO进行操作,我们学习ARM时候,第一个工程就是点亮LED,STM32F103ZET6通用输入输出接口(General-Purpose Inputs/Outputs),每个GPIO都可以由软件配置成输出(推免或开漏)、输入(带或不带上拉或下拉)或复用的外设功能端口。多数GPIO引脚都与数字或模拟的复用外设共用。具体的细节请参考Datasheet。

         回到MDK开发平台,现在要在main.c中加入相关代码了。代码清单如下:

    #include "stm32f10x_lib.h"
     
    int main()
    {
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB, ENABLE);     //开启外设时钟
           GPIOD->CRL = 0x33333333;                           //设置端口配置寄存器
           GPIOB->CRL = 0x33333333;
           while(1)
           {             
                  GPIOD->ODR = 0xffffffbf;                       //设置端口输出寄存器
                  for(i=0;i<1000000;i++);                                   //延时
                  GPIOD->ODR = 0xffffffff7;
                  for(i=0;i<1000000;i++);
                  GPIOD->ODR = 0x00000000;
                  GPIOB->ODR = 0xffffffff;
                  for(i=0;i<1000000;i++);
                  GPIOB->ODR = 0x00000000;
           }
    }
    

      上述代码中,#include "stm32f10x_lib.h"包含了开发stm32f10x系列芯片所需的基本头文件,在进行程序编写的时候,务必要包含此头文件。

           RCC_APB2PeriphClockCmd()函数是设置外设时钟ARMC51单片机不同的是,不用外设的时候,如IO口、ADC、定时器等等,都是禁止时钟的,以达到节能的目的,只有要用到的外设,才开启它的时钟因此在需要用到GPIOB和GPIOD的时候,我们需要先开启它的时钟,具体用到的是函数库里面的函数:

    void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
    

      其中,第一个参数需要指示要开启什么端口的时钟,RCC_APB2Periph_GPIOx就是开启GPIOx的时钟,第二个参数需要指示是开启还是关闭,ENABLE/DISABLE。

           开启外设时钟之后,然后就开始对GPIO的配置寄存器进行设置了。具体设置方式参考《基于MDK的STM32处理器开发应用》一书中,“7.1 通用IO端口”。While循环里面就是给GPIO的端口输出寄存器赋值,由于我手上这款开发板三个LED灯分别接的是PG13、PG14和PG15,所以只要将G端口相应的位上置1就可以了。

           编译之后我们发现编译器报错,Undefined symbol RCC_APB2PeriphClockCmd,是因为我们使用了的RCC_APB2PeriphClockCmd()函数在头文件中声明了,却没有在C文件中定义,这个函数在KeilARMRV31LIBSTSTM32F10xstm32f10x_rcc.c中,将这个文件复制到工程的根目录下,然后在屏幕左边的Workspace中添加进来,就可以了。

           其实在MDK的库中,还定义了很多宏,可以避免让我们自己去查找相关资料来设置寄存器的各个位。

    对于LED的亮灭可用以下代码进行实现:

    #include "stm32f10x.h"
    static u8  fac_us=0; //us延时倍乘数		
    static u16 fac_ms=0; //ms延时倍乘数,在ucos下代表每个节拍的ms数
    void delay_init(void);
    void delay_ms(u16 nms);
    void LED_Init(void);//初始化
    int main(void)
    {
           
           delay_init();		  //初始化延时函数
           LED_Init();		  //初始化LED端口
    	
    	
    	while(1)
    	{
    	GPIO_ResetBits(GPIOG,GPIO_Pin_13);  //亮  等同LED0=0;
    	GPIO_SetBits(GPIOG,GPIO_Pin_14);    //灭  等同LED1=1;
    	GPIO_SetBits(GPIOG,GPIO_Pin_15);    //灭  等同LED2=1;
    	delay_ms(500);  		    //延时500ms
    	GPIO_SetBits(GPIOG,GPIO_Pin_13);   
    	GPIO_ResetBits(GPIOG,GPIO_Pin_14);  
    	GPIO_SetBits(GPIOG,GPIO_Pin_15);    
    	delay_ms(500);                      
    	GPIO_SetBits(GPIOG,GPIO_Pin_13);    
    	GPIO_SetBits(GPIOG,GPIO_Pin_14);    
    	GPIO_ResetBits(GPIOG,GPIO_Pin_15);  
    	delay_ms(500);
    	}
    }
    void LED_Init(void)   //LED对应IO初始化
    {    	 
      GPIO_InitTypeDef  GPIO_InitStructure;	
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);	 //使能GPIO时种	
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;		//LED0,LED1,LED2对应IO口
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推免输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO速度为50Mhz
      GPIO_Init(GPIOG, &GPIO_InitStructure);			 //初始化GPIO
      GPIO_SetBits(GPIOG,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);	  //GPIOG13,G14,G15设置高,灯灭
    
    void delay_init()   //延时函数初始化
    {
     	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    	fac_us=SystemCoreClock/8000000;	 
    	fac_ms=(u16)fac_us*1000; //每个ms需要的systick时钟数  
    }		
    void delay_ms(u16 nms) //延时nms
    {	 		  	  
    	u32 midtime;		   
    	SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
            //SysTick->LOAD为24位寄存器,所以最大延时为:nms<=0xffffff*8*1000/SYSCLK对72M条件下,nms<=1864ms
    	SysTick->VAL =0x00;           //清空计时器÷
    	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数 
    	do
    	{
    		midtime=SysTick->CTRL;
    	}
    	while((midtime&0x01)&&!(midtime&(1<<16)));//等待时间到达  
    	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器
    	SysTick->VAL =0X00;       //清空计数器	  	    
    }  

    由于我们使用了GPIO_InitTypeDef类型,所以我们需要找到它的定义,这个定义包含在“…KeilARMRV31LIBSTSTM32F10xstm32f10x_gpio.c”中,将文件复制到工程根目录下,然后再添加进入工程中,编译才不会报错。

       在绝大多数C编译器中,要求所有的变量声明都在执行语句块之前,也就是说如果需要定义的变量需要先在进入main函数一开始就全部定义好,如果执行了某一条语句之后再定义变量的话,就会报错。

    部分参考:http://blog.sina.com.cn/s/blog_49cb42490100robb.html

    补充(一):

    有三种方式可以控制LED亮灭:

    1、通过位段操作实现IO口控制;
    int main(void)
    { 
     
    	delay_init();		  //初始化延时函数
    	LED_Init();		  //初始化LED端口
    	
      while(1)
    	{
        GPIO_bits_OUT(GPIOG,13,3,0x0006);
    	       delay_ms(500);
    	  GPIO_bits_OUT(GPIOG,13,3,0x0005);
    		delay_ms(500);
    	  GPIO_bits_OUT(GPIOG,13,3,0x0003);
    		delay_ms(500);
    	}
    }
    
    
    /*以下:*GPIOx:对应的IO口,start_bit:并行输出的起始值,bit_size:并行输出的位数*/
    
    void GPIO_bits_OUT(GPIO_TypeDef* GPIOx, u8 start_bit, u8 bit_size,u16 outdata)
    {
      u8 i=0;
    	u16 bu1=0;u16 middata=1;
    
    	if( bit_size>(16-start_bit) ) 
         bit_size=16-start_bit;
    	
    	i=start_bit;
    	if(i>0)
    		 {
    			 while(i--)
             { bu1+=middata; middata*=2;}
    		 }
    	
       assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
       
    	 GPIOx->ODR&=(  ( (0xffff<<bit_size) <<start_bit ) |bu1   ); 
    	 GPIOx->ODR|=(outdata<<start_bit);		 
    }

    2、通过位带操作实现IO口控制;

    3、通过库函数直接操作实现IO控制;

    补充(二):STM32中GPIO的8种工作模式

          

    • 模拟输入  用于模拟量的输入,或者低功耗下省电。
    • 下拉输入  端口内下拉电阻电路导通。默认为低电平,外部高电平动作。
    • 上拉输入  端口内上拉电阻电路导通,默认为高电平,外部低电平动作。
    • 浮空输入  端口内上下MOSFET均不导通,高阻态,输入状态仅由端口决定。
    • 开漏输出  端口输出为0时内部下拉电阻电路接地,输出1时端口相当于悬空,即默认只能输出0,如果外部需要输出1,需要外接上拉电阻电路
    • 推挽输出  端口输出为0时内部下拉电阻电路接地,输出1时内部接上拉电阻电路,默认输出为0。
    • 复用开漏输出 内部设置同开漏,但被其他外设复用。
    • 复用推挽输出 内部设置同推挽,但被其他外设复用。

        小补充:

      • 上拉电阻就是:将一个不确定信号(高或低电平),通过一个电阻与电源VCC相连,固定在高电平;
      • 下拉电阻就是:将一个不确定信号(高或低电平),通过一个电阻与地GND相连,固定在低电平。 
    1. 作为普通的GPIO输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。
    2. 作为普通的GPIO输出:根据需要配置该引脚为推免输出或开漏输出,同时不要使能该引脚对应的所有复用功能。
    3. 作为普通模拟输入:配置该引脚为模拟模拟输入模式,同时不要使能该引脚对应的所有复用功能。
    4. 作为内置外设的输入:根据需要配置该引脚为浮空输入,带弱上拉输入或带弱下拉输入,同时使能该引脚的复用功能。
    5. 作为内置外设的输出:根据需要配置引脚为复用推挽输出或复用开漏输出,同时使能该引脚的对应的所有复用功能模块。
  • 相关阅读:
    032 Gradle 下载的依赖jar包在哪?
    031 can't rename root module,Android Studio修改项目名称
    030 Cannot resolve symbol'R' 问题解决汇总大全
    029 Android Studio层级显示目录文件
    028 You are about to commit CRLF line separators to the Git repository.It is recommended to set the core. autocrlf Git attribute to true to avoid line separator issues If you choose Fix and Comit ,
    027 【Android基础知识】Android Studio 编译慢及 Adb connection Error:远程主机强迫关闭了一个现有的连接
    026 Android Studio 和Gradle版版本对应关系
    025 Cause: org.jetbrains.plugins.gradle.tooling.util.ModuleComponentIdentifierIm
    024 Android Studio上传项目到Github 最全记录
    023 解决AndroidStudio下载gradle慢的问题
  • 原文地址:https://www.cnblogs.com/happying30/p/9438279.html
Copyright © 2020-2023  润新知