【linux-IMX6ULL配置GPIO通用流程-以及时钟配置】

1. GPIO模式控制流程

1.1 LED、蜂鸣器、按键

  这些功能的实现都是利用了引脚的GPIO模式,通过GPIO模式进行引脚配置输出或者输入,进而实现LED、蜂鸣器、按键,的功能

1.2 GPIO控制流程

  核心配置思路如下:引脚X->寄存器A->寄存器A1->寄存器B(伪寄存器)

  • 引脚X:顾名思义,看得见,摸得着的物理引脚,一个引脚可以配置成多个模式,X可取范围是1-32,因为这个芯片是32位的,一个寄存器最多能控制32个引脚的输入和输出;
  • 寄存器A:例如对于引脚X,那么我如何把这个引脚配置成我想要的模式呢?例如配置成GPIO模式,答案就是通过寄存器A,也就是IOMUXC_SW_MUX_CTL_PAD_GPIO1_X寄存器,每个引脚都有对应的寄存器A,通过配置寄存器A来确定GPIO的工作模式
  • 寄存器A1:A和A1是一一映射的,一个引脚确定了模式,下一步就是配置电气属性,因此寄存器A1就是配置电气属性的,例如确定了GPIO的工作模式,下一步就是配置这个引脚的电气属性:就是通过寄存器A1来配置的,在数据手册中也叫:IOMUXC_SW_PAD_CTL_PAD_GPIO1_X
  • 寄存器B:引脚确定了模式和电器属性,那么下一步就是确定这个模式下如何进行工作,例如上面确定了GPIO(通用输入输出模式)那么下一步就是配置寄存器B(GPIO寄存器)进行输入或者输出、上下拉、保持、速度等功能的配置
  • 思路如下图:

2. 标准库的简要分析及实现:

2.1 问题引入:

  仔细观察一下,对于以下数据手册中引脚的寄存器地址分布,是不是每个引脚之间的寄存器地址都差4。所以知道了GPIO1_DR的地址后,剩下的寄存器地址我们都可以推出来,因此可以把这一类的寄存器放在一个结构体中,因为结构体存储数据是可以按照等字节连续存储,如何把结构体中的第一个值的地址宏定义一下,那么结构体中的其他寄存器的地址自动获得
在这里插入图片描述

2.2 代码实现:

 &emsp注意,代码把地址强制转换成了结构体指针,因为物理硬件地址不会发生变化,在一些底层的硬件编程中,需要直接访问特定的硬件寄存器。通过将寄存器的地址定义为一个结构体指针,可以方便地进行访问和操作。

//定义结构体中的首地址,那么结构体中的其他寄存器的地址自动获得
#define GPIO1_BASE                  (0x0209C000)
#define GPIO2_BASE                  (0x020A0000)
#define GPIO3_BASE                  (0x020A4000)
#define GPIO4_BASE                  (0x020A8000)
#define GPIO5_BASE                  (0x020AC000)


typedef struct 
{
	volatile unsigned int DR;							
	volatile unsigned int GDIR; 							
	volatile unsigned int PSR;								
	volatile unsigned int ICR1; 							
	volatile unsigned int ICR2; 							 
	volatile unsigned int IMR;								 
	volatile unsigned int ISR;			
	volatile unsigned int EDGE_SEL;  
}GPIO_Type;

#define GPIO1				((GPIO_Type *)GPIO1_BASE)//注意,这里把地址强制转换成了结构体指针,
                                                      //因为物理硬件地址不会发生变化
//在一些底层的硬件编程中,需要直接访问特定的硬件寄存器。
//通过将寄存器的地址定义为一个结构体指针,可以方便地进行访问和操作。
#define GPIO2				((GPIO_Type *)GPIO2_BASE)
#define GPIO3				((GPIO_Type *)GPIO3_BASE)
#define GPIO4				((GPIO_Type *)GPIO4_BASE)
#define GPIO5				((GPIO_Type *)GPIO5_BASE)

  以上就是一个简单的标准库的实现,其实可以把整个模块都按照这样的形式写出来,例如,IO口寄存器,GPIO,USART,CLOCK,等等

3. 时钟配置总结:

3.1 时钟树概要:

  对于每款MCU都有专有的时钟路线,但是这些芯片有共同点,那就是时钟树,时钟树的根就是外部的晶振,通过对外部晶振的倍频和分频可以得到几大主干道,也就是时钟树的枝干,而通过这些枝干在进行分频和倍频可以得到各个外设需要的时钟。

3.2 IMX6ULL时钟概要及时钟树:

  IMX6ULL大体上有7个PLL和8个PFD时钟总线,其余的大都是从这些时钟总线上分频得到的;其中PLL1作为ARM内核的时钟总线,是要首先进行初始化的,初始化过程中注意点ARM时钟切换频率时要先进行时钟的转接,换好后再把系统时钟接回来;配置ARM时钟时主要参考图如下:
在这里插入图片描述
  配置其他时钟主要参考是时钟树,IMX6ULL时钟树部分截图:注意时钟树中黄色的是寄存器对选择器的配置,红色的是寄存器对分频器的的控制,分频过程中注意不要直接清零,清零代表不分频,可能时钟频率会超出下线的承受范围导致板子卡死,注意有的分频过后要进行握手检测,也就是握手换频率不用关时钟,关时钟换频率不用握手;
在这里插入图片描述

3.3 IMX6ULL时钟配置代码:

#include "bsp_clk.h"
/*使能外设时钟*/
void clk_enable(void)
{
    CCM->CCGR0 = 0xFFFFFFFF;
    CCM->CCGR1 = 0xFFFFFFFF;
    CCM->CCGR2 = 0xFFFFFFFF;
    CCM->CCGR3 = 0xFFFFFFFF;
    CCM->CCGR4 = 0xFFFFFFFF;
    CCM->CCGR5 = 0xFFFFFFFF;
    CCM->CCGR6 = 0xFFFFFFFF;
}

/*初始化时钟*/
void imx6u_clkinit(void)
{
    unsigned int reg=0;
    /*初始化6u的主频为528MHz*/
    if((((CCM->CCSR)>>2) & 0x1)==0)  /*当前时钟使用的是pll1_main_clk,也就是pll1,要进行时钟切换*/
    {
        CCM->CCSR &= ~(1<<8);/*设置step_clk=osc_clk=24MHz*/
        CCM->CCSR |= (1<<2);/*pll1_main_clk=step_clk=24MHz*/
    }
    /*系统时钟切换已经设置好了,下一步就是设置PLL1=1056MHz,根据公式设置DIV_SELECT这八位为88,且使能*/
    CCM_ANALOG->PLL_ARM = ((1<<13)|((88<<0) & 0x7f));
    CCM->CACRR = 1;/*设置二分频*/
    /*现在设置好了时钟,下一步就是把时钟切换回来*/
    CCM->CCSR &= ~(1<<2);/*把这一位PLL1_SW_CLK_SEL清零就行,切换回来了,设置pll1_main_clk=1056MHz*/
    /*二位选择器,通过PLL1_SW_CLK_SEL=0/1来进行选择时钟频率*/

    /*设置PL2的4路PFD*/
    reg=CCM_ANALOG->PFD_528;
    reg &=~(0x3f3f3f3f);
    reg |=(32<<24);             /*初始化PLL2_PFD3=297MHz,利用公式的得到:*/
    reg |=(24<<16);             /*初始化PLL2_PFD2=396MHz,利用公式的得到:*/
    reg |=(16<<8);              /*初始化PLL2_PFD1=594MHz,利用公式的得到:*/
    reg |=(27<<0);              /*初始化PLL2_PFD0=352MHz,利用公式的得到:*/
    CCM_ANALOG->PFD_528=reg;    /*设置完毕*/

     /*设置PL3的4路PFD*/
    reg=0;
    reg=CCM_ANALOG->PFD_480;
    reg &=~(0x3f3f3f3f);
    reg |=(19<<24);              /*初始化PLL3_PFD3=454.7MHz,利用公式的得到:*/
    reg |=(17<<16);              /*初始化PLL3_PFD2=508.2MHz,利用公式的得到:*/
    reg |=(16<<8);               /*初始化PLL3_PFD1=540MHz,利用公式的得到:*/
    reg |=(12<<0);               /*初始化PLL3_PFD0=720MHz,利用公式的得到:*/
    CCM_ANALOG->PFD_480=reg;     /*设置完毕*/

    /*设置AHB_CLK_ROOT=132MHZ*/
    CCM->CBCMR &= ~(3<<18);
    CCM->CBCMR |= (1<<18);//四路选择器选择时钟PLL2_PFD2,即设置:PRE_PERIPH_CLK_SEL=400M(396Mhz)
    /*设置第二个选择器,通过CBCDR寄存器进行设置*/
    CCM->CBCDR &= ~(1<<25);
    while (CCM->CDHIPR & (1<<5));  /*等待握手信号*/

    reg=0;
    reg= CCM->CBCDR;
    reg &= ~(7<<10);  //先清零,这里不能直接先 CCM->CBCDR &= ~(7<<10)清零,因为清零直接变成1分频,超过了132Mhz,直接死机
    reg |= (2<<10);   //在赋值3分频
    CCM->CBCDR=reg;
    while((CCM->CDHIPR >> 1) & (0x1));/*等待握手信号*/

   /*设置IPG_CLK_ROOT=66MHz*/
   /*通过设置CBCDR寄存器的IPG_PODF位实现*/
   reg=0;
   reg = CCM->CBCDR;
   reg &= ~(3<<8);
   reg |= (1<<8);
   CCM->CBCDR=reg; /*设置完毕*/


   /*设置PERCLK_CLK_ROOT时钟为66Mhz*/
   /*设置CSCMR1寄存器的PERCLK_CLK_SEL位为0(第六位),两路选择器选择进入66MHz*/
   CCM->CSCMR1 &= ~(1<<6); /*时钟源已经选择了IPG,可以对照着时钟树进行了解*/
   /*分频器不分频*/
   CCM->CSCMR1 &= ~(0x3f << 0);/*设置完毕,1分频,66MHZ*/


}

  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值