按键输入及主频与时钟配置实验

1.按键输入实验

按键输入简介
按键就两个状态:按下或弹起,将按键连接到一个 IO 上,通过读取这个 IO 的值就知道按
键是按下的还是弹起的。至于按键按下的时候是高电平还是低电平要根据实际电路来判断。前 面几章我们都是讲解 I.MX6U GPIO 作为输出使用,当 GPIO 连接按键的时候就要做为输入 使用。
我们的主要工作就是配置按键 所连接的 IO 为输入功能,然后读取这个 IO 的值来判断按键是否按下。 I.MX6U-ALPHA 开发板上有一个按键 KEY0 ,我们将会编写代码通过这个 KEY0 按键 来控制开发板上的蜂鸣器,按一下 KEY0 蜂鸣器打开,再按一下蜂鸣器就关闭。
硬件原理分析
可以看出,按键 KEY0 是连接到 I.MX6U UART1_CTS 这个 IO 上的, KEY0
接了一个 10K 的上拉电阻,因此 KEY0 没有按下的时候 UART1_CTS 应该是高电平,当 KEY0 按下以后 UART1_CTS 就是低电平。
实验程序编写
上一章试验例程的基础上完成,重新创建 VSCode 工程,工作区名字为“ key ”,
在工程目录的 bsp 文件夹中创建名为“ key ”和“ gpio ”两个文件夹。按键相关的驱动文件都放
到“ key ”文件夹中,本章试验我们对 GPIO 的操作编写一个函数集合,也就是编写一个 GPIO
驱动文件, GPIO 的驱动文件放到“gpio”文件夹里面。
1.设置UART1_CTS复用为GPIO1_IO18
2.设置UART1_CTS的电气属性
3.配置 GPIO1_IO18为输入模式
4.读取按键值,也就是GPIO1_IO18的高低电平
新建 bsp_gpio.c bsp_gpio.h 这两个文件,将这两个文件都保存到刚刚创建的 bsp/gpio
件夹里面,然后在 bsp_gpio.h 文件夹里面输入如下内容:
#ifndef _BSP_GPIO_H
#define _BSP_GPIO_H
#define _BSP_KEY_H
#include "imx6ul.h"


/* 枚举类型和结构体定义 */
typedef enum _gpio_pin_direction
{
    kGPIO_DigitalInput = 0U,  		/* 输入 */
    kGPIO_DigitalOutput = 1U, 		/* 输出 */
} gpio_pin_direction_t;
	

typedef struct _gpio_pin_config
{
    gpio_pin_direction_t direction; /* GPIO方向:输入还是输出 */
    uint8_t outputLogic;            /* 如果是输出的话,默认输出电平 */
} gpio_pin_config_t;


/* 函数声明 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pinread(GPIO_Type *base, int pin);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);

#endif
bsp_gpio.h 中定义了一个枚举类型 gpio_pin_direction_t 和结构体 gpio_pin_config_t ,枚举类 型 gpio_pin_direction_t 表示 GPIO 方向,输入或输出。结构体 gpio_pin_config_t GPIO 的配置 结构体,里面有 GPIO 的方向和默认输出电平两个成员变量。在 bsp_gpio.c 中输入如下所示内容:
#include "bsp_gpio.h"


/*
 * @description		: GPIO初始化。
 * @param - base	: 要初始化的GPIO组。
 * @param - pin		: 要初始化GPIO在组内的编号。
 * @param - config	: GPIO配置结构体。
 * @return 			: 无
 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
	if(config->direction == kGPIO_DigitalInput) /* 输入 */
	{
		base->GDIR &= ~( 1 << pin);
	}
	else										/* 输出 */
	{
		base->GDIR |= 1 << pin;
		gpio_pinwrite(base,pin, config->outputLogic);/* 设置默认输出电平 */
	}
}

 /*
  * @description	 : 读取指定GPIO的电平值 。
  * @param - base	 : 要读取的GPIO组。
  * @param - pin	 : 要读取的GPIO脚号。
  * @return 		 : 无
  */
 int gpio_pinread(GPIO_Type *base, int pin)
 {
	 return (((base->DR) >> pin) & 0x1);
 }

 /*
  * @description	 : 指定GPIO输出高或者低电平 。
  * @param - base	 : 要输出的的GPIO组。
  * @param - pin	 : 要输出的GPIO脚号。
  * @param - value	 : 要输出的电平,1 输出高电平, 0 输出低低电平
  * @return 		 : 无
  */
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{
	 if (value == 0U)
	 {
		 base->DR &= ~(1U << pin); /* 输出低电平 */
	 }
	 else
	 {
		 base->DR |= (1U << pin); /* 输出高电平 */
	 }
}



文件 bsp_gpio.c 中有三个函数: gpio_init gpio_pinread gpio_pinwrite ,函数 gpio_init
于初始化指定的 GPIO 引脚,最终配置的是 GDIR 寄存器,此函数有三个参数,这三个参数的
含义如下:
base: 要初始化的 GPIO 所属于的 GPIO 组,比如 GPIO1_IO18 就属于 GPIO1 组。
pin 要初始化 GPIO 在组内的标号,比如 GPIO1_IO18 在组内的编号就是 18
config: 要初始化的 GPIO 配置结构体,用来指定 GPIO 配置为输出还是输入。
函数 gpio_pinread 是读取指定的 GPIO 值,也就是读取 DR 寄存器的指定位,此函数有两个
参数和一个返回值,参数含义如下:
base: 要读取的 GPIO 所属于的 GPIO 组,比如 GPIO1_IO18 就属于 GPIO1 组。
pin 要读取的 GPIO 在组内的标号,比如 GPIO1_IO18 在组内的编号就是 18
返回值: 读取到的 GPIO 值,为 0 或者 1
函数 gpio_pinwrite 是控制指定的 GPIO 引脚输入高电平 (1) 或者低电平 (0) ,就是设置 DR
存器的指定位,此函数有三个参数,参数含义如下:
base: 要设置的 GPIO 所属于的 GPIO 组,比如 GPIO1_IO18 就属于 GPIO1 组。
pin 要设置的 GPIO 在组内的标号,比如 GPIO1_IO18 在组内的编号就是 18
value: 要设置的值, 1( 高电平 ) 或者 0( 低电平 )
我们以后就可以使用函数 gpio_init 设置指定 GPIO 为输入还是输出,使用函数 gpio_pinread
gpio_pinwrite 来读写指定的 GPIO ,文件 bsp_gpio.c 文件就讲解到这里。
接下来编写按键驱动文件,新建 bsp_key.c bsp_key.h 这两个文件,将这两个文件都保存
到刚刚创建的 bsp/key 文件夹里面,然后在 bsp_key.h 文件夹里面输入如下内容:
#ifndef _BSP_KEY_H
#define _BSP_KEY_H
#include "imx6ul.h"



/* 定义按键值 */
enum keyvalue{
	KEY_NONE   = 0,
	KEY0_VALUE,
	KEY1_VALUE,
	KEY2_VALUE,
};

/* 函数声明 */
void key_init(void);
int key_getvalue(void);


#endif
bsp_key.h 文件中定义了一个枚举类型: keyvalue ,此枚举类型表示按键值,因为 I.MX6U
ALPHA 开发板上只有一个按键,因此枚举类型里面只到 KEY0_VALUE 。在 bsp_key.c 中输入 如下所示内容:
#include "bsp_key.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"


/*
 * @description	: 初始化按键
 * @param 		: 无
 * @return 		: 无
 */
void key_init(void)
{	
	gpio_pin_config_t key_config;
	
	/* 1、初始化IO复用, 复用为GPIO1_IO18 */
	IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);

	/* 2、、配置UART1_CTS_B的IO属性	
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 11 默认22K上拉
	 *bit [13]: 1 pull功能
	 *bit [12]: 1 pull/keeper使能
	 *bit [11]: 0 关闭开路输出
	 *bit [7:6]: 10 速度100Mhz
	 *bit [5:3]: 000 关闭输出
	 *bit [0]: 0 低转换率
	 */
	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
	
	/* 3、初始化GPIO */
	//GPIO1->GDIR &= ~(1 << 18);	/* GPIO1_IO18设置为输入 */	
	key_config.direction = kGPIO_DigitalInput;
	gpio_init(GPIO1,18, &key_config);
	
}

/*
 * @description	: 获取按键值 
 * @param 		: 无
 * @return 		: 0 没有按键按下,其他值:对应的按键值
 */
int key_getvalue(void)
{
	int ret = 0;
	static unsigned char release = 1; /* 按键松开 */ 

	if((release==1)&&(gpio_pinread(GPIO1, 18) == 0)) 		/* KEY0 	*/
	{	
		delay(10);		/* 延时消抖 		*/
		release = 0;	/* 标记按键按下 */
		if(gpio_pinread(GPIO1, 18) == 0)
			ret = KEY0_VALUE;
	}
	else if(gpio_pinread(GPIO1, 18) == 1)
	{
		ret = 0;
		release = 1; 	/* 标记按键释放 */
	}

	return ret;	
}
bsp_key.c 中一共有两个函数: key_init key_getvalue key_init 是按键初始化函数,用来
初始化按键所使用的 UART1_CTS 这个 IO 。函数 key_init 先设置 UART1_CTS 复用为
GPIO1_IO18 ,然后配置 UART1_CTS 这个 IO 为速度为 100MHz ,默认 22K 上拉。最后调用函 数 gpio_init 来设置 GPIO1_IO18 为输入功能。 函数 key_getvalue 用于获取按键值,此函数没有参数,只有一个返回值,返回值表示按键值,返回值为 0 的话就表示没有按键按下,如果返回其他值的话就表示对应的按键按下了。获取按键值其实就是不断的读取 GPIO1_IO18 的值,如果按键按下的话相应的 IO 被拉低,那么GPIO1_IO18 值就为 0 ,如果按键未按下的话 GPIO1_IO18 的值就为 1。此函数中静态局部变量 release 表示按键是否释放。57 行是按键消抖延时函数,延时时间大约为 10ms,用于消除按
键抖动。理想型的按键电压变化过程如图
按键没有按下的时候按键值为 1 ,当按键在 t1 时刻按键被按下以后按键值
就变为 0,这是最理想的状态。但是实际的按键是机械结构,加上刚按下去的一瞬间人手可能 也有抖动,实际的按键电压变化过程如图
t1 时刻按键被按下,但是由于抖动的原因,直到 t2 时刻才稳定下来, t1 到 t2 这段时间就是抖动。一般这段时间就是十几 ms 左右,从图 15.3.2 中可以看出在抖动期间会 有多次触发,如果不消除这段抖动的话软件就会误判,本来按键就按下了一次,结果软件读取 IO 值发现电平多次跳变以为按下了多次。所以我们需要跳过这段抖动时间再去读取按键的 IO 值,也就是至少要在 t2 时刻以后再去读 IO 值。
main.c 中输入如下代码:


#include "bsp_clk.h"

#include "bsp_delay.h"

#include "bsp_led.h"

#include "bsp_beep.h"

#include "bsp_key.h"



/*

 * @description	: main函数

 * @param 		: 无

 * @return 		: 无

 */

int main(void)

{

	int i = 0;

	int keyvalue = 0;

	unsigned char led_state = OFF;

	unsigned char beep_state = OFF;

	

	clk_enable();		/* 使能所有的时钟 			*/

	led_init();			/* 初始化led 			*/

	beep_init();		/* 初始化beep	 		*/

	key_init();			/* 初始化key 			*/



	while(1)			

	{	

		keyvalue = key_getvalue();

		if(keyvalue)

		{

			switch ((keyvalue))

			{

				case KEY0_VALUE:

					beep_state = !beep_state;

					beep_switch(beep_state);

					break;

			}

		}



		i++;

		if(i==50)

		{

			i = 0;

			led_state = !led_state;

			led_switch(LED0, led_state);

		}

		delay(10);

	}



	return 0;

}

getvalue 来读取按键值,如果 KEY0 按下的话就打开 / 关闭蜂鸣器。 LED0 作为系统提示指
示灯闪烁,闪烁周期大约为 500ms
编译下载验证
编写 Makefile 和链接脚本
Makefile 使用通用 Makefile ,修改变量 TARGET key ,在变量 INCDIRS
SRCDIRS 中追加“ bsp/gpio ”和“ bsp/key ”,修改完成以后如下所示:
CROSS_COMPILE 	?= arm-linux-gnueabihf-

TARGET		  	?= key



CC 				:= $(CROSS_COMPILE)gcc

LD				:= $(CROSS_COMPILE)ld

OBJCOPY 		:= $(CROSS_COMPILE)objcopy

OBJDUMP 		:= $(CROSS_COMPILE)objdump



INCDIRS 		:= imx6ul \

				   bsp/clk \

				   bsp/led \

				   bsp/delay  \

				   bsp/beep \

				   bsp/gpio \

				   bsp/key

				   			   

SRCDIRS			:= project \

				   bsp/clk \

				   bsp/led \

				   bsp/delay \

				   bsp/beep \

				   bsp/gpio \

				   bsp/key

				   

				   

INCLUDE			:= $(patsubst %, -I %, $(INCDIRS))



SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))

CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))



SFILENDIR		:= $(notdir  $(SFILES))

CFILENDIR		:= $(notdir  $(CFILES))



SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))

COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))

OBJS			:= $(SOBJS) $(COBJS)



VPATH			:= $(SRCDIRS)



.PHONY: clean

	

$(TARGET).bin : $(OBJS)

	$(LD) -Timx6ul.lds -o $(TARGET).elf $^

	$(OBJCOPY) -O binary -S $(TARGET).elf $@

	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis



$(SOBJS) : obj/%.o : %.S

	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<



$(COBJS) : obj/%.o : %.c

	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

	

clean:

	rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)



	

编译下载
使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 key.bin 文件
下载到 SD 卡中,命令如下
chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
./imxdownload key.bin /dev/sdd //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 设备里面!
烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。如果代码运行正常的
LED0 会以大约 500ms 周期闪烁, 按下开发板上的 KEY0 按键,蜂鸣器打开,再按下 KEY0 按键,蜂鸣器关闭。

实验成功

2.主频和时钟配置实验 

2.1I.MX6U 时钟系统详解

I.MX6U 的系统主频为 528MHz ,有些型号可以跑到 696MHz ,但是默认情况下内部 boot
rom 会将 I.MX6U 的主频设置为 396MHz,我们在使用 I.MX6U的时候肯定是要发挥它的最大性能,那么主频肯定要设置到 528MHz(其它型号可以设置更高,比如 696MHz),其它的外设时钟也要设置到 NXP 推荐的值。
系统时钟来源
可以看出 I.MX6U-ALPHA 开发板的系统时钟来源于两部分: 32.768KHz
24MHz 的晶振,其中 32.768KHz 晶振是 I.MX6U RTC 时钟源, 24MHz 晶振是 I.MX6U 内核
和其它外设的时钟源,也是我们重点要分析的。
7 PLL 时钟源
I.MX6U 的外设有很多,不同的外设时钟源不同, NXP 将这些外设的时钟源进行了分组, 一共有 7 组,这 7 组时钟源都是从 24MHz 晶振 PLL 而来的,因此也叫做 7 PLL
时钟树简介
一共有三部分: CLOCK_SWITCHER CLOCK ROOT GENERATOR 和 SYSTEM CLOCKS。其中左边的 CLOCK_SWITCHER 就是我们上一小节讲解的那 7 PLL 和 8 路 PFD ,右边的 SYSTEM CLOCKS 就是芯片外设,中间的 CLOCK ROOT GENERATOR 是最 复杂的!这一部分就像“月老”一样,给左边的CLOCK_SWITCHER 和右边的 SYSTEM CLOCKS
进行牵线搭桥。外设时钟源是有多路可以选择的, CLOCK ROOT GENERATOR 就负责从 7
PLL 8 PFD 选择合适的时钟源给外设使用。具体操作肯定是设置相应的寄存器,我们以
ESAI 这个外设为例,ESAI 的时钟图如图
①、此部分是时钟源选择器, ESAI 4 个可选的时钟源: PLL4 PLL5 PLL3_PFD2
pll3_sw_clk 。 具 体 选 择 哪 一 路 作 为 ESAI 的时钟源是由寄存器 CCM->CSCMR2
ESAI_CLK_SEL 位来决定的,用户可以自由配置,配置如图
②、此部分是 ESAI 时钟的前级分频,分频值由寄存器 CCM_CS1CDR ESAI_CLK_PRED
来确定的,可设置 1~8 分频,假如现在 PLL4=650MHz ,我们选择 PLL4 作为 ESAI 时钟,前级
分频选择 2 分频,那么此时的时钟就是 650/2=325MHz
③、此部分又是一个分频器,对②中输出的时钟进一步分频,分频值由寄存器
CCM_CS1CDR ESAI_CLK_PODF 来决定,可设置 1~8 分频。假如我们设置为 8 分频的话,
经过此分频器以后的时钟就是 325/8=40.625MHz 。因此最终进入到 ESAI 外设的时钟就是
40.625MHz

2.2内核时钟设置

时钟树可以看到 ARM 内核时钟如图①、内核时钟源来自于 PLL1,假如此时 PLL1 996MHz

②、通过寄存器 CCM_CACRR ARM_PODF 位对 PLL1 进行分频,可选择 1/2/4/8 分频,
假如我们选择 2 分频,那么经过分频以后的时钟频率是 996/2=498MHz
③、大家不要被此处的 2 分频给骗了,此处没有进行 2 分频 ( 我就被这个 2 分频骗了好久,
主频一直配置不正确! )
④、经过第②步 2 分频以后的 498MHz 就是 ARM 的内核时钟,也就是 I.MX6U 的主频。
经过上面几步的分析可知,假如我们要设置内核主频为 528MHz ,那么 PLL1 可以设置为
1056MHz ,寄存器 CCM_CACRR ARM_PODF 位设置为 2 分频即可。同理,如果要将主频设 置为 696MHz ,那么 PLL1 就可以设置为 696MHz CCM_CACRR ARM_PODF 设置为 1 分 频即可。现在问题很清晰了,寄存器 CCM_CACRR ARM_PODF 位很好设置, PLL1 的频率 可以通过寄存器 CCM_ANALOG_PLL_ARMn 来设置。

实验程序编写 

本试验在上一章试验“ 7_key ”的基础上完成,因为本试验是配置 I.MX6U 的系统时钟,因
此我们直接在文件“ bsp_clk.c ”上做修改,修改 bsp_clk.c 的内容如下:
#include "bsp_clk.h"
 

//添加了函数 imx6u_clkinit(),完成 I.MX6U 的系统时钟初始化
17 /*
18 * @description : 使能 I.MX6U 所有外设时钟
19 * @param : 无
20 * @return : 无
21 */
22 void clk_enable(void)
23 {
24 CCM->CCGR0 = 0XFFFFFFFF;
25 CCM->CCGR1 = 0XFFFFFFFF;
26 CCM->CCGR2 = 0XFFFFFFFF;
27 CCM->CCGR3 = 0XFFFFFFFF;
28 CCM->CCGR4 = 0XFFFFFFFF;
29 CCM->CCGR5 = 0XFFFFFFFF;
30 CCM->CCGR6 = 0XFFFFFFFF;
31 }
32 
33 /*
34 * @description : 初始化系统时钟 528Mhz,并且设置 PLL2 和 PLL3 各个
35 PFD 时钟,所有的时钟频率均按照 I.MX6U 官方手册推荐的值.
36 * @param : 无
37 * @return : 无
38 */
39 void imx6u_clkinit(void)
40 {
41 unsigned int reg = 0;
42 /* 1、设置 ARM 内核时钟为 528MHz */
43 /* 1.1、判断当使用哪个时钟源启动的,正常情况下是由 pll1_sw_clk 驱动的,而
44 * pll1_sw_clk 有两个来源:pll1_main_clk 和 step_clk,如果要
45 * 让 I.MX6ULL 跑到 528M,那必须选择 pll1_main_clk 作为 pll1 的时钟
46 * 源。如果我们要修改 pll1_main_clk 时钟的话就必须先将 pll1_sw_clk 从
47 * pll1_main_clk 切换到 step_clk,当修改完以后再将 pll1_sw_clk 切换
48 * 回 pll1_main_cl,step_clk 等于 24MHz。
49 */
50 
51 if((((CCM->CCSR) >> 2) & 0x1 ) == 0) /* pll1_main_clk */
52 { 
53 CCM->CCSR &= ~(1 << 8); /* 配置 step_clk 时钟源为 24MHz OSC */ 
54 CCM->CCSR |= (1 << 2); /* 配置 pll1_sw_clk 时钟源为 step_clk */
55 }
56 
57 /* 1.2、设置 pll1_main_clk 为 1056MHz,也就是 528*2=1056MHZ,
58 * 因为 pll1_sw_clk 进 ARM 内核的时候会被二分频!
59 * 配置 CCM_ANLOG->PLL_ARM 寄存器
60 * bit13: 1 使能时钟输出
61 * bit[6:0]: 88, 由公式:Fout = Fin * div_select / 2.0,
62 * 1056=24*div_select/2.0, 得出:div_select=88。 
63 */
64 CCM_ANALOG->PLL_ARM = (1 << 13) | ((88 << 0) & 0X7F);
65 CCM->CCSR &= ~(1 << 2);/* 将 pll_sw_clk 时钟切换回 pll1_main_clk */
66 CCM->CACRR = 1; /* ARM 内核时钟为 pll1_sw_clk/2=1056/2=528Mhz */
67 
68 /* 2、设置 PLL2(SYS PLL)各个 PFD */
69 reg = CCM_ANALOG->PFD_528;
70 reg &= ~(0X3F3F3F3F); /* 清除原来的设置 */
71 reg |= 32<<24; /* PLL2_PFD3=528*18/32=297Mhz */
72 reg |= 24<<16; /* PLL2_PFD2=528*18/24=396Mhz */
73 reg |= 16<<8; /* PLL2_PFD1=528*18/16=594Mhz */
74 reg |= 27<<0; /* PLL2_PFD0=528*18/27=352Mhz */
75 CCM_ANALOG->PFD_528=reg; /* 设置 PLL2_PFD0~3 */
76 
77 /* 3、设置 PLL3(USB1)各个 PFD */
78 reg = 0; /* 清零 */
79 reg = CCM_ANALOG->PFD_480;
80 reg &= ~(0X3F3F3F3F); /* 清除原来的设置 */
81 reg |= 19<<24; /* PLL3_PFD3=480*18/19=454.74Mhz */
82 reg |= 17<<16; /* PLL3_PFD2=480*18/17=508.24Mhz */
83 reg |= 16<<8; /* PLL3_PFD1=480*18/16=540Mhz */
84 reg |= 12<<0; /* PLL3_PFD0=480*18/12=720Mhz */
85 CCM_ANALOG->PFD_480=reg; /* 设置 PLL3_PFD0~3 */ 
86 
87 /* 4、设置 AHB 时钟 最小 6Mhz, 最大 132Mhz */
88 CCM->CBCMR &= ~(3 << 18); /* 清除设置*/
89 CCM->CBCMR |= (1 << 18); /* pre_periph_clk=PLL2_PFD2=396MHz */
90 CCM->CBCDR &= ~(1 << 25); /* periph_clk=pre_periph_clk=396MHz */
91 while(CCM->CDHIPR & (1 << 5));/* 等待握手完成 */
92 
93 /* 修改 AHB_PODF 位的时候需要先禁止 AHB_CLK_ROOT 的输出,但是
94 * 我没有找到关闭 AHB_CLK_ROOT 输出的的寄存器,所以就没法设置。
95 * 下面设置 AHB_PODF 的代码仅供学习参考不能直接拿来使用!!
96 * 内部 boot rom 将 AHB_PODF 设置为了 3 分频,即使我们不设置 AHB_PODF,
97 * AHB_ROOT_CLK 也依旧等于 396/3=132Mhz。
98 */
99 #if 0
100 /* 要先关闭 AHB_ROOT_CLK 输出,否则时钟设置会出错 */
101 CCM->CBCDR &= ~(7 << 10);/* CBCDR 的 AHB_PODF 清零 */
102 CCM->CBCDR |= 2 << 10; /* AHB_PODF 3 分频,AHB_CLK_ROOT=132MHz */
103 while(CCM->CDHIPR & (1 << 1));/* 等待握手完成 */
104 #endif
105 
106 /* 5、设置 IPG_CLK_ROOT 最小 3Mhz,最大 66Mhz */
107 CCM->CBCDR &= ~(3 << 8); /* CBCDR 的 IPG_PODF 清零 */
108 CCM->CBCDR |= 1 << 8; /* IPG_PODF 2 分频,IPG_CLK_ROOT=66MHz */
109 
110 /* 6、设置 PERCLK_CLK_ROOT 时钟 */
111 CCM->CSCMR1 &= ~(1 << 6); /* PERCLK_CLK_ROOT 时钟源为 IPG */
112 CCM->CSCMR1 &= ~(7 << 0); /* PERCLK_PODF 位清零,即 1 分频 */
113 }
文件 bsp_clk.c 中一共有两个函数: clk_enable imx6u_clkinit ,其中函数 clk_enable 前面
已经讲过了,就是使能 I.MX6U 的所有外设时钟。函数 imx6u_clkinit 才是本章的重点,
imx6u_clkinit 先设置系统主频为 528MHz ,然后根据我们上一小节分析的 I.MX6U 时钟系统来
设置 8 PFD ,最后设置 AHB IPG PERCLK 的时钟频率。
bsp_clk.h 文件中添加函数 imx6u_clkinit 的声明,最后修改 main.c 文件,在 main 函数里
面调用 imx6u_clkinit 来初始化时钟,如下所示:
1 int main(void)
2 {
3 int i = 0;
4 int keyvalue = 0;
5 unsigned char led_state = OFF;
6 unsigned char beep_state = OFF;
7 
8 imx6u_clkinit(); /* 初始化系统时钟 */
9 clk_enable(); /* 使能所有的时钟 */
10 led_init(); /* 初始化 led */
11 beep_init(); /* 初始化 beep */
12 key_init(); /* 初始化 key */
13
14 /* 省略掉其它代码 */
15 }
编译下载验证
因为本章是在试验“ 7_key ”上修改的,而且本章试验没有添加任何新的文件,因此只需
要修改 Makefile 的变量 TARGET 为“ clk ”即可
使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 clk.bin 文件
下载到 SD 卡中,命令如下:
chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
./imxdownload clk.bin /dev/sdd //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 设备里面!
烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。本试验效果其实和试
验“ 7_key ”一样,但是 LED 灯的闪烁频率相比试验“ 7_key ”要快一点。因为试验“ 7_key ”的
主频是 396MHz ,而本试验的主频被配置成了 528MHz ,因此代码执行速度会变快,所以延时函
数的运行就会加快。

实验成功

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值