目录
一、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的状态。