Exynos4412裸机开发系列教程--LED流水灯

        对于任何一门编程语言的学习,绝大部分都是从Hello Word开始的,但是对于大部分嵌入式驱动开发者,通常都是从点Led灯开始的,前人有言,给我一个发光二极管,我将点亮整个世界,从这格层面上讲,点灯已是一种方便快捷的调试手段。同样,本教程也不列外,所有的一切都从点亮一颗Led开始。

  任何一款SOC芯片,其外围必定有一堆扩展IO口,通常这些IO是和各种内部外设复用的,比如串口的TXD、RXD信号既可以做功能管脚,也可以做普通IO,对于点亮Led灯,只需要将对应的管脚配置为GPIO输出模式,并设置相应的输出电平状态即可。

       本系列教程,基于九鼎的X4412开发板,此开发板性价比高,性能稳定,扩展性强,功能完善,并提供X4412核心板,方便企业用户做深度定制版二次开发。开发板截图:

Exynos4412裸机开发系列教程--LED流水灯_第1张图片

Exynos4412裸机开发系列教程--LED流水灯_第2张图片

      任何系统开机后都有一个启动过程,同样EXYNOS4412也不列外。Exynos4412属于应用处理器,非一般微控制器,开机启动是一个比较复杂的过程,因这里是第一篇教程,故不作过多阐述,后续系列教程会此进行深入描述。这里简单说明一下,EXYNOS4412芯片启动时,固化在芯片内部的IROM会首先执行,其最主要的任务是读取外部存储器的前8k或者16k代码,并做相应的校验,校验通过后,然后执行。在此过程中仅仅使用内部的IROM及IRAM,不涉及任何外部存储器。当然,究竟是从哪个存储器读取前8k或者16k,则是根据硬件的OM启动模式配置管脚来决定的,本教程OM配置模式都是从外部SD卡启动,相应的程序也都烧录在外部SD卡中的第一个扇区。

      前面的这8k或者16k代码后,是系统正常启动的关键。一般都会执行以下动作:关看门狗,设置为系统模式,关MMU,CACHE等,初始化系统主时钟,初始化DDR,拷贝剩余部分代码(当然bootloader都会超过8k的),为C代码准备环境,比如将BSS段清零,初始化已初始化数据段等等,最后是调到C语言的Main函数,至此,启动工作已完成,进入相对较为熟悉的main函数了。

    本教程,重点讲述LED灯相关的操作,首先,我们由原理图可以看出,底板上有四颗LED,参考原理图如下:

Exynos4412裸机开发系列教程--LED流水灯_第3张图片


为了能方便快捷的操作各个LED,我们定义一个函数,void led_set_status(enum led_name name, enum led_status status),这样在调用时只需传递特定的参数就可以控制各个LED了,这里定义了两个枚举类型:

enum led_name {
	LED_NAME_LED1		= 1,
	LED_NAME_LED2		= 2,
	LED_NAME_LED3		= 3,
	LED_NAME_LED4		= 4,
};

enum led_status {
	LED_STATUS_OFF		= 0,
	LED_STATUS_ON		= 1,
};
在我们执行具体的操作时,我们需要初始化相应的IO口,将其设置为输出上拉模式,初始化代码如下:

/*
 * LED1 -> EXYNOS4412_GPX1(6)
 * LED2 -> EXYNOS4412_GPX1(7)
 * LED3 -> EXYNOS4412_GPX2(6)
 * LED4 -> EXYNOS4412_GPX2(7)
 */
void led_initial(void)
{
	/* LED1 */
	writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_CON, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_CON) & ~(0xf<<24)) | (0x1<<24));
	writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_PUD, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_PUD) & ~(0x3<<12)) | (0x2<<12));
	writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<6)) | (0x1<<6));

	/* LED2 */
	writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_CON, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_CON) & ~(0xf<<28)) | (0x1<<28));
	writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_PUD, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_PUD) & ~(0x3<<14)) | (0x2<<14));
	writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<7)) | (0x1<<7));

	/* LED3 */
	writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_CON, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_CON) & ~(0xf<<24)) | (0x1<<24));
	writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_PUD, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_PUD) & ~(0x3<<12)) | (0x2<<12));
	writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<6)) | (0x1<<6));

	/* LED4 */
	writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_CON, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_CON) & ~(0xf<<28)) | (0x1<<28));
	writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_PUD, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_PUD) & ~(0x3<<14)) | (0x2<<14));
	writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<7)) | (0x1<<7));
}

在初始各个控制IO后,我们可以实现具体的设置LED状态的函数了。根据传递的参数设置相应的控制寄存器。

void led_set_status(enum led_name name, enum led_status status)

{
	switch(name)
	{
	case LED_NAME_LED1:
		if(status == LED_STATUS_ON)
			writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<6)) | (0x0<<6));
		else if(status == LED_STATUS_OFF)
			writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<6)) | (0x1<<6));
		break;

	case LED_NAME_LED2:
		if(status == LED_STATUS_ON)
			writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<7)) | (0x0<<7));
		else if(status == LED_STATUS_OFF)
			writel(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX1_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<7)) | (0x1<<7));
		break;

	case LED_NAME_LED3:
		if(status == LED_STATUS_ON)
			writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<6)) | (0x0<<6));
		else if(status == LED_STATUS_OFF)
			writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<6)) | (0x1<<6));
		break;

	case LED_NAME_LED4:
		if(status == LED_STATUS_ON)
			writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<7)) | (0x0<<7));
		else if(status == LED_STATUS_OFF)
			writel(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT, (readl(EXYNOS4412_GPX2_BASE + EXYNOS4412_GPIO_DAT) & ~(0x1<<7)) | (0x1<<7));
		break;

	default:
		break;
	}
}
有了以上LED控制函数,我们就可以实现具体的流水灯程序了,闪烁规则如下,从左至右依次点亮,然后再从右至左依次点亮,通过index变量标识是那颗led,flag变量标识流水的方向,从左至右还是从右至左。

int tester_led(int argc, char * argv[])
{
	int index = 0;
	int flag = 0;

	while(1)
	{
		led_set_status(LED_NAME_LED1, LED_STATUS_OFF);
		led_set_status(LED_NAME_LED2, LED_STATUS_OFF);
		led_set_status(LED_NAME_LED3, LED_STATUS_OFF);
		led_set_status(LED_NAME_LED4, LED_STATUS_OFF);

		switch(index)
		{
		case 0:
			led_set_status(LED_NAME_LED1, LED_STATUS_ON);
			break;

		case 1:
			led_set_status(LED_NAME_LED2, LED_STATUS_ON);
			break;

		case 2:
			led_set_status(LED_NAME_LED3, LED_STATUS_ON);
			break;

		case 3:
			led_set_status(LED_NAME_LED4, LED_STATUS_ON);
			break;

		default:
			break;
		}

		mdelay(50);

		if(index == 0 || index == 3)
			flag = !flag;

		if(flag)
			index = (index + 1) % 4;
		else
			index = (index + 4 - 1) % 4;
	}

	return 0;
}

运行状态,一张静态图,将就看看吧,实际效果可以自行下载到SD卡中运行:

Exynos4412裸机开发系列教程--LED流水灯_第4张图片

如果您对本文感兴趣,想必是同道中人,需要源码的朋友,可移步此处,点击下载,有任何疑问欢迎留言,或者直接加QQ: 8192542。第一篇教程就此结束,先有格感性认识吧,尽请期待后续教程,您的支持就是我的动力,还请各路大侠多多捧场。

附上源码:

Exynos4412裸机系列教程源码之流水灯

你可能感兴趣的:(Exynos4412)