《ARM裸机》4--LED与KEY(基于I.MX6ULL)

目录

一、LED硬件工作原理及原理图查阅

        1、硬件基础

        2、GPIO(通用输入输出)

        3、GPIO相关的寄存器

二、通过GPIO控制LED2

        1.使能GPIO5_03

        2.设置GPIO5_03为GPIO功能

        3.将GPIO5_03设置为输出模式

        4.控制DR寄存器 

三、官方SDK移植

四、用KEY控制LED

        3.1使用GPIO4_IO14 

        3.2使用GPIO5_IO01


一、LED硬件工作原理及原理图查阅

        1、硬件基础

        1.1物理特性

                LED本身两个接线点,一个是LED的正极,一个是LED的负极。LED这个硬件的功能是亮或者不亮。加上正向电压就可以点亮LED。

        1.2查阅原理图了解板载LED硬件接法

         

         这里列出图中的三颗LED原理图,如下:

LED1:正极接3.3V高电平,负极接地,可知,LED1是常量的,也就是电源指示灯,只要上电,LED1就会常亮。

LED2:正极接3.3V高电平,负极由GPIO5_3控制,GPIO5_3为1输出高电平,LED2不亮,GPIO5_3为0输出低电平,LED2亮。

LED3:正极接3.3V高电平,负极由LED_WWAN#控制,LED_WWAN#为1输出高电平,LED3不亮,LED_WWAN#为0输出低电平,LED3亮。

        1.3分析如何点亮及熄灭LED(GPIO)

         分析:LED点亮的要求是:正极和负极上有一个正向的电压差。

2、GPIO(通用输入输出)

        GPIO(general purpose input output),芯片上有一部分引脚,他的功能和特点是可以通过编程控制它的工作模式,也可以通过编程控制它的电压高低。

        想要通过编程操控GPIO来操作LED时,我们需要通读数据手册中关于GPIO的部分。

3、GPIO相关的寄存器

        我们当前要操作的硬件是LED,但是LED实际是通过GPIO来间接控制的,所以当前我们实际要操作的设备其实是soc的GPIO。要操作这些GPIO,必须通过设置他们的寄存器。
        

二、通过GPIO控制LED2

        由上面的原理图分析可知,要控制LED2的开光,就要操控GPIO5_03.操控GPIO5_03的步骤如下:

        1.使能GPIO5_03

        在数据手册的18章(CCM),Table 18-3

         可知寄存器CCGR1的CG15位是控制GPIO5使能的。

        还是在18章,找到CCM的CCM下的CCGR1。

         可以看出,CCGR1的30到31位是控制GPIO5使能的,并且CCRG1的地址是0x20C406C。

将其30,31位置位1就可以使能GPIO5。

CCM_CCGR1   =  (volatile unsigned int *)(0x20C406C);
CCM_CCGR1   |= (3<<30); 

        2.设置GPIO5_03为GPIO功能

        查找数据手册的32.5.6章节

         则需要将地址为0X2290014的SW_MUX_CTL寄存器的0~3位设置位101。

IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = (volatile unsigned int *)(0x2290014);
val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
val &= ~(0xf);
val |= (5);
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val;

        3.将GPIO5_03设置为输出模式

        查阅数据手册28章GPIO Memory MAP/Register Definition,可以找到GPIO5的DR和GDIR寄存器的地址。

 

         GDIR的每一位,就对应GPIO5_x,比如3就对应着GPIO5_03,所致需要就此地方设置为1

GPIO5_GDIR  = (volatile unsigned int *)0x020AC004;
*GPIO5_GDIR |= (1<<3);

        4.控制DR寄存器 

        前三步为初始化内容,要控制LED的亮灭就要控制DR寄存器。

         与DIR寄存一样,GDIR的每一位,就对应GPIO5_x,1代表高电平,0代表低电平。

GPIO5_DR  = (volatile unsigned int *)(0x020AC000);
void led_ctl(int on)
{
	if (on) /* on: output 0*/
	{
		/* d. 设置GPIO5_DR输出低电平
		 * set GPIO5_DR to configure GPIO5_IO03 output 0
		 * GPIO5_DR 0x020AC000 + 0
		 * bit[3] = 0b0
		 */
		*GPIO5_DR &= ~(1<<3);
	}
	else  /* off: output 1*/
	{
		/* e. 设置GPIO5_IO3输出高电平
		 * set GPIO5_DR to configure GPIO5_IO03 output 1
		 * GPIO5_DR 0x020AC000 + 0
		 * bit[3] = 0b1
		 */ 
		*GPIO5_DR |= (1<<3);
	}
}

汇编程序点亮LED2

        led.s

.global _start                    //汇编文件要用一个全局的入口
_start:
	ldr r0,=0X020C406C             //使能CCGR1时钟
	ldr r1,=0XFFFFFFFF
	str r1,[r0]
	ldr r0,=0X02290014	        //设置GPIO5_03控制MUX
	ldr r1,=0X5
	str r1,[r0]
	ldr r0,=0X020AC004	    //设置GPIO5_GDIR为输出
	ldr r1,=0X00000008
	str r1,[r0]
	ldr r0,=0X020AC000	    //设置GPIO5_DR低电平
	ldr r1,=0
	str r1,[r0]
loop:
	b loop

        Makefile

led.bin:led.s
	arm-buildroot-linux-gnueabihf-gcc -g -c led.s -o led.o
	arm-buildroot-linux-gnueabihf-ld -Ttext 0X80100000 led.o -o led.elf
	arm-buildroot-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
	arm-buildroot-linux-gnueabihf-objdump -D led.elf >led.dis
clean:
	rm *.o *.elf *.bin *.dis -rf

C语言点亮LED

        用C语言点亮LED,也需要用到汇编文件,进行初始化配置C语言环境。这里汇编需要,给出起始入口,修改CPU工作模式为SVC(特权)模式,并跳转置main函数。

        start.s

.global _start
    //设置SVC模式
    mrs cpsr,r0
    bic r0,r0, #0X1f
    orr ro, r0, #0X13
    ldr sp, =0X80400000
    b main

        led.c

#include "led.h"
static volatile unsigned int *CCM_CCGR1;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_DR;
static volatile unsigned int *GPIO5_GDIR;

void led_init(void)
{
	CCM_CCGR1				=(volatile unsigned int*)0X20C406C;
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3	=(volatile unsigned int*)0x2290014;
	GPIO5_GDIR				=(volatile unsigned int*)0x020AC004;
	GPIO5_DR				=(volatile unsigned int*)0x020AC000;
	//使能CC_CGGR1
	*CCM_CCGR1					=0XFFFFFFFF;
	//设置MUX为GPIO5
	unsigned int temp			=*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
	temp 					&=~(0xF);
	temp					|=5;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 =temp;
	//设置GPIO5_GDIR 为输出
	*GPIO5_GDIR				|=(1<<3);
}

void led_ctl(int on)
{
	if(on) //传入的参数为1,让灯亮
		*GPIO5_DR &= ~(1<<3);
	else	//传入参数为0,让等不亮
		*GPIO5_DR |= (1<<3);
}
void delay(volatile unsigned int d)
{
	while(d--);
}

        main.c

#include"led.h"
int main(void)
{
	led_init();
	while(1)
	{	
		led_ctl(1);
		delay(1000000);
		led_ctl(0);
		delay(1000000);
	}
	return 0;
}

        Makefile

objs :=Start.o led.o main.o

led.bin:$(objs)
	arm-buildroot-linux-gnueabihf-ld -Ttext 0X80100000 -o led.elf $^
	arm-buildroot-linux-gnueabihf-objcopy -O binary -S  led.elf $@
	arm-buildroot-linux-gnueabihf-objdump -D -m arm led.elf > led.dis
%.o:%.s
	arm-buildroot-linux-gnueabihf-gcc -g -c $< -o $@ 
%.o:%.S
	arm-buildroot-linux-gnueabihf-gcc -g -c $< -o $@
%.o:%.c
	arm-buildroot-linux-gnueabihf-gcc -g -c $< -o $@
clean:
	rm -rf *.o *.elf *.dis *.bin

利用imxdownload烧写到SD卡中,即可看到LED2闪烁。

三、官方SDK移植

        上面编写LED程序是自己根据寄存器地址定义的变量

static volatile unsigned int *CCM_CCGR1;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_DR;
static volatile unsigned int *GPIO5_GDIR;

        为了开发方便,NXP为6ULL提供了SDK包,包含了函数,宏等定义。这样我们就可以利用SDK包调用,提高开发效率。其中fsl_common.h、fsl_iomuxc.h、MCIMX6Y2.h就是官方提供的SDK包。 

        比如LED程序可以修改为。

#include "fsl_common.h"
#include "fsl_iomuxc.h"
#include "MCIMX6Y2.h"

/*
 * @description	: 使能I.MX6U所有外设时钟
 * @param 		: 无
 * @return 		: 无
 */
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;

}

/*
 * @description	: 初始化LED对应的GPIO
 * @param 		: 无
 * @return 		: 无
 */
void led_init(void)
{
	/* 1、初始化IO复用 */
	IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER3_GPIO5_IO03,0);		/* 复用为GPIO1_IO0 */

	/* 2、初始化GPIO,设置GPIO1_IO03设置为输出  */
	GPIO1->GDIR |= (1 << 3);	
	
	/* 3、设置GPIO1_IO03输出低电平,打开LED0 */
	GPIO1->DR &= ~(1 << 3);			
}

/*
 * @description	: 打开LED灯
 * @param 		: 无
 * @return 		: 无
 */
void led_on(void)
{
	/* 将GPIO1_DR的bit3清零 	*/
	GPIO1->DR &= ~(1<<3); 
}

/*
 * @description	: 关闭LED灯
 * @param 		: 无
 * @return 		: 无
 */
void led_off(void)
{
	/* 将GPIO1_DR的bit3置1 */
	GPIO1->DR |= (1<<3); 
}

/*
 * @description	: 短时间延时函数
 * @param - n	: 要延时循环次数(空操作循环次数,模式延时)
 * @return 		: 无
 */
void delay_short(volatile unsigned int n)
{
	while(n--){}
}

/*
 * @description	: 延时函数,在396Mhz的主频下
 * 			  	  延时时间大约为1ms
 * @param - n	: 要延时的ms数
 * @return 		: 无
 */
void delay(volatile unsigned int n)
{
	while(n--)
	{
		delay_short(0x7ff);
	}
}

/*
 * @description	: mian函数
 * @param 		: 无
 * @return 		: 无
 */
int main(void)
{
	clk_enable();		/* 使能所有的时钟 			*/
	led_init();			/* 初始化led 			*/

	while(1)			/* 死循环 				*/
	{	
		led_off();		/* 关闭LED 			*/
		delay(500);		/* 延时500ms 			*/

		led_on();		/* 打开LED 			*/
		delay(500);		/* 延时500ms 			*/
	}

	return 0;
}

在fsl_iomuxc.h设置了相关寄存器的地址,和IOMUXC_SetPinMux函数定义。

#define IOMUXC_SNVS_SNVS_TAMPER3_GPIO5_IO03   0x02290014U,0x5U,0x00000000U,0x0U,0x02290058U
static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
                                    uint32_t muxMode,
                                    uint32_t inputRegister,
                                    uint32_t inputDaisy,
                                    uint32_t configRegister,
                                    uint32_t inputOnfield)
{
    *((volatile uint32_t *)muxRegister) =
        IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) | IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);

    if (inputRegister)
    {
        *((volatile uint32_t *)inputRegister) = IOMUXC_SELECT_INPUT_DAISY(inputDaisy);
    }
}

        相关文件中还有一些变量定义我们添加,比如unit32_t。添加cc.h文件并包含。

#ifndef __CC_H
#define __CC_H
/*
 * 自定义一些数据类型供库文件使用
 */
#define     __I     volatile 
#define     __O     volatile 
#define     __IO    volatile

typedef   signed          char int8_t;
typedef   signed short     int int16_t;
typedef   signed           int int32_t;
typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;
typedef unsigned long     long uint64_t;
typedef	  signed char  	 	   s8;		
typedef	  signed short 	  int  s16;
typedef	  signed int 		   s32;
typedef	  signed long long int s64;
typedef	unsigned char 		   u8;
typedef	unsigned short int     u16;
typedef	unsigned int 		   u32;
typedef	unsigned long long int u64;

#endif

四、用KEY控制LED

        查看KEY的硬件手册,实验使用的6ull开发板有两个供我们使用的按键。

        3.1使用GPIO4_IO14 

        先使用KEY操作LED,要使用GPIO4_IO14。与GPIO控制LED一样,要使用KEY需要对设置相关的GPIO。步骤如下:

        (1):使能GPIO_04。CC_CGGR3

        (2):设置GPIO04_14为GPIO复用。IOMUXC_SW_MUX_CTL_PAD_NAND_CE1_B

        (3):设置GPIO04_GDIR为输入模式。

void led_init(void)
{
	CCM_CCGR1				=(volatile unsigned int*)0X20C406C;
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3	=(volatile unsigned int*)0x2290014;
	GPIO5_GDIR				=(volatile unsigned int*)0x020AC004;
	GPIO5_DR				=(volatile unsigned int*)0x020AC000;

	CCM_CCGR3				=(volatile unsigned int*)0X20C4074;
	IOMUXC_SW_MUX_CTL_PAD_NAND_CE1_B	=(volatile unsigned int*)0x20E01B0;
	GPIO4_GDIR				=(volatile unsigned int*)0x20A8004;
	GPIO4_DR				=(volatile unsigned int*)0x20A8000;
	//使能CC_CGGR1 CC_CGGR3
	*CCM_CCGR1				=0XFFFFFFFF;
	*CCM_CCGR3				=0XFFFFFFFF;
	//设置GPIO5_03、GPIO4_14为GPIO复用
	unsigned int temp1			=*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
	temp1 					&=~(0xF);
	temp1					|=0x5;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 =temp1;

	unsigned int temp2			=*IOMUXC_SW_MUX_CTL_PAD_NAND_CE1_B;
	temp2 					&=~(0xF);
	temp2					|=0x5;
	*IOMUXC_SW_MUX_CTL_PAD_NAND_CE1_B 	=temp2;
	//设置GPIO5_GDIR 为输出,GPIO4_GDIR为输入
	*GPIO5_GDIR				|=(1<<3);
	*GPIO4_GDIR				&=~(1<<14);
	
}

          main.c文件:

#include"led.h"
int main(void)
{
	unsigned int temp2 = 1;
	led_init();
	while(1)
	{			
		if(((*GPIO4_DR)&(1<<14))>>14==0)
		{
			temp2 = (~temp2)&1;
			while(((*GPIO4_DR)&(1<<14))>>14==0);
			
		}
		if(temp2 == 0)
			led_ctl(1);
		else
			led_ctl(0);
	}
	return 0;
}

         实验现象:每次按一次按键key2,就可以改变一次LED状态。

        3.2使用GPIO5_IO01

        实验步骤:

        (1)使能GPIO5。(CC_CGGR1)

        (2)设置GPIO5_IO01和GPIO5_IO03的GPIO复用。

        (3)设置GPIO_IO01为输入,GPIO_IO03为输出。

void led_init(void)
{
	CCM_CCGR1				=(volatile unsigned int*)0X20C406C;
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3	=(volatile unsigned int*)0x2290014;
	GPIO5_GDIR				=(volatile unsigned int*)0x020AC004;
	GPIO5_DR				=(volatile unsigned int*)0x020AC000;

	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1	=(volatile unsigned int*)0x229000C;
	//使能CC_CGGR1 
	*CCM_CCGR1				=0XFFFFFFFF;
	//设置GPIO5_01为GPIO复用
	unsigned int temp1			=*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1;
	temp1 					&=~(0xF);
	temp1					|=0x5;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1 =temp1;
	//设置GPIO5_03为GPIO复用
	unsigned int temp2			=*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
	temp2 					&=~(0xF);
	temp2					|=0x5;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 =temp2;
	//设置GPIO5_GDIR为01输入
	*GPIO5_GDIR				&=~(1<<1);
	//设置GPIO5_GDIR为03输出
	*GPIO5_GDIR				|=(1<<3);
}

        main.c文件:

#include"led.h"
int main(void)
{
	unsigned int temp2 = 1;
	led_init();
	while(1)
	{			
		if(((*GPIO5_DR)&(1<<1))>>1==0)
		{
			temp2 = (~temp2)&1;
			while(((*GPIO5_DR)&(1<<1))>>1==0);	
		}
		if(temp2 == 0)
			led_ctl(1);
		else
			led_ctl(0);
	}
	return 0;
}

        实验现象,没按一次KEY1就可以改变一次LED的状态。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值