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

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		  	?= keyCC 				:= $(CROSS_COMPILE)gccLD				:= $(CROSS_COMPILE)ldOBJCOPY 		:= $(CROSS_COMPILE)objcopyOBJDUMP 		:= $(CROSS_COMPILE)objdumpINCDIRS 		:= imx6ul \bsp/clk \bsp/led \bsp/delay  \bsp/beep \bsp/gpio \bsp/keySRCDIRS			:= project \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/keyINCLUDE			:= $(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 ,因此代码执行速度会变快,所以延时函
数的运行就会加快。

实验成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/484709.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亿道丨三防平板pad丨三防平板是指哪三防丨三防工业级平板电脑

三防工业级平板电脑成为许多行业中的重要工具。本文将介绍三防工业级平板电脑的特点以及其在各个领域中的广泛应用。 三防工业级平板电脑的特点 三防工业级平板电脑是指具备防水、防尘和防震功能的平板电脑。这些特点使得它们能够在恶劣环境中工作&#xff0c;如沙尘飞扬的工地…

基于java springboot+mybatis OA办公自动化系统设计和实现

基于java springbootmybatis OA办公自动化系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末…

普中51单片机学习(串口通信)

串口通信 原理 计算机通信是将计算机技术和通信技术的相结合&#xff0c;完成计算机与外部设备或计算机与计算机之间的信息交换 。可以分为两大类&#xff1a;并行通信与串行通信。并行通信通常是将数据字节的各位用多条数据线同时进行传送 。控制简单、传输速度快&#xff1…

【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

RK3568平台开发系列讲解(Linux系统篇)通过I2C总线访问客户端方法

🚀返回专栏总目录 文章目录 一、普通I2C通信二、系统管理总线(SMBus)兼容函数三、在开发板配置文件中实例化I2C设备(弃用的旧方式)沉淀、分享、成长,让自己和他人都能有所收获!😄 串行总线事务只是访问寄存器来设置/获取其内容。I2C遵循该规则。I2C内核提供两种API,…

如何利用内网穿透工具在企业微信开发者中心实现本地接口服务回调

文章目录 1. Windows安装Cpolar2. 创建Cpolar域名3. 创建企业微信应用4. 定义回调本地接口5. 回调和可信域名接口校验6. 设置固定Cpolar域名7. 使用固定域名校验 企业微信开发者在应用的开发测试阶段&#xff0c;应用服务通常是部署在开发环境&#xff0c;在有数据回调的开发场…

2023年Q4,SSD出货量下降5%,但存储容量增长9.6%

2023年第四季度&#xff08;4Q23&#xff09;的SSD市场表现呈现出单位出货量下降&#xff0c;但存储容量增长的趋势。具体数据显示&#xff0c;该季度SSD总出货量下降5%&#xff0c;降至8820万台&#xff1b; 而存储容量则增长9.6%&#xff0c;达到85.1EB。预计2023全年SSD总容…

ad18学习笔记十八:如何单独设置某一铺铜与导线的间距

网上找的很多内容都是ad18之前的旧版本&#xff0c;ad18对应的介绍特别少。 直接设置全局的铺铜规格比较容易&#xff1a; Altium Designer教程系列&#xff1a;深入学习铺铜操作 (baidu.com) Altium Designer规则及覆铜设计小技巧 (baidu.com) 单独给某一片铺铜区域设置规则…

苍穹外卖学习-----2024/02/21

1.新增员工 /*** 处理SQL异常* param sqlIntegrityConstraintViolationException* return*/ExceptionHandlerpublic Result exceptionHandler(SQLIntegrityConstraintViolationException sqlIntegrityConstraintViolationException){//String message sqlIntegrityConstraintV…

并发编程之深入理解Java线程

并发编程之深入理解Java线程 线程基础知识 线程和进程 进程 程序由指令和数据组成、但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须要将指令加载至CPU、数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的…

C++:STL简介

1. 什么是STL STL(standard template libaray- 标准模板库 ) &#xff1a; 是 C 标准库的重要组成部分 &#xff0c;不仅是一个可复用的组件库&#xff0c;而且 是一个包罗数据结构与算法的软件框架 。 2. STL的版本 3. STL的六大组件 4.STL的缺陷 1. STL库的更新太慢了。这…

C++ Primer 笔记(总结,摘要,概括)——第1章 开始

目录 1.1 编写一个简单的C程序 1.1.1 编译、运行程序 1.2 初识输入输出 1.3 注释简介 1.4 控制流 1.4.1 while语句 1.4.2 for语句 1.4.3 读取数量不定的输入数据 1.4.4 if语句 1.5 类简介 1.5.1 Sales_item类 1.5.2 初识函数成员 1.6 书店程序 小结 术语表 1.1 编写一个…