zoukankan      html  css  js  c++  java
  • 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. 作为内置外设的输出:根据需要配置引脚为复用推挽输出或复用开漏输出,同时使能该引脚的对应的所有复用功能模块。
  • 相关阅读:
    02-最简C语言程序
    Go学习笔记-GO编程语言手册
    Go学习笔记-Effective Go
    go学习笔记-语法
    机器学习-数据挖掘
    windows下jupyter notebook的安装及配置
    wpf学习笔记
    windows下安装mingW及控制台启用
    nginx
    MFC学习笔记
  • 原文地址:https://www.cnblogs.com/happying30/p/9438279.html
Copyright © 2011-2022 走看看