[黑金笔记二]LED实验

一、建立三个文件夹Main,inc,driver,分别存放main.c,自建.h头文件,驱动.c文件

二、sopc.h程序解析

#ifndef SOPC_H_
#define SOPC_H_

/*-----------------------------------------------------------------------------
 *  Include
 *-----------------------------------------------------------------------------*/
#include "system.h"

/*-----------------------------------------------------------------------------
 *  Define
 *-----------------------------------------------------------------------------*/
#define _PIO_LEDR

/*-----------------------------------------------------------------------------
 *  Peripheral registers structures  
 *-----------------------------------------------------------------------------*/
typedef struct
{
    unsigned long int DATA;
    unsigned long int DIRECTION;
    unsigned long int INTERRUPT_MASK;
    unsigned long int EDGE_CAPTURE;
    
}PIO_STR;

/*-----------------------------------------------------------------------------
 *  Peripheral declaration
 *-----------------------------------------------------------------------------*/
#ifdef _PIO_LEDR
#define PIO_LEDR          ((PIO_STR *) PIO_LEDR_BASE)      
#endif /*_LED*/

#endif /*SOPC_H_*/
  首先我们看红圈2处的结构体,在这里,我们定义了一个结构体,将其命名为PIO_STR,这个结构体的结构是有说法的,它是根据芯片手册来写的。我现在给大家截个图,这个图来自《n2cpu_Embedded Peripherals.pdf》

  这个表格就是PIO Core寄存器映射,我的结构体就是根据它来写的,名字不重要,重要的是它的顺序,也就是偏移量(offset)。第一项是数据data,第二项是IO口方向,第三项是中断控制位,第四项是边沿控制位。他们每一项都有n位(共32位),这个n就是我们在软核构建中的宽度(width),如18个LED,则width=18.

  接下来,我们来讲红框3中的代码,这部分代码中你一定记得PIO_LED_BASE,对,这就是我之前在system.h中特意强调的,PIO_LED的基地址。那我红圈3中的代码意图就很明显了,定义了一个宏,命名为LED,它是指向PIO_LED_BASE的结构体指针,这个结构体就是PIO_STR(如果大家对结构体指针不理解,那你就得回去看C语言书了,我在这就不具体说了)。红圈1的代码也很简单,是为了控制红圈3的定义的,这样做是为了增强代码的严谨性和可控制性。当你没有定义PIO_LED_BASE时,你就可以将红圈1中的宏定义#define _LED去。

      大家理解了sopc.h代码以后,我们就可以进行C代码编程了。忘了说了,在NIOS II IDE中一定要记得修改以后保存,它很幼稚的,编译前不会提醒你去保存的,如果你忘记了保存,相当于你没有修改。

三、main.c程序解析
/*-----------------------------------------------------------------------
 * Include 
 *---------------------------------------------------------------------*/
#include "../inc/sopc.h"
#include <stdio.h>
#include <unistd.h>

/* 
 * ===  FUNCTION  ========================================================
 *         Name:  main
 *  Description:  
 * =======================================================================
 */
int main(void)
{
	int i; 
	
	while(1){
		for(i=0;i<18;i++){
			PIO_LEDR->DATA = 1 << i;
			usleep(100000);
		}
	}

	return 0;
}

  首先说红框1,有些人可能没用过这种方式,这是一种相对路径的方式调用头文件,好处就是可移植性好,不管工程放到其他什么地方,这个头文件和c文件的相对位置都不会变,也就不需要对这个地方进行修改。再说红框2处的代码,关键是LED->DATA = 1<<i;LED大家应该记得,LED是我们在sopc.h中定义的宏,是结构体指针,那么LED->DATA是什么意思大家就应该很清楚了,是它的结构体中DATA的内容(不是地址)。LED->DATA = 1<<i;就是对LED->DATA的四个位循环置1。所以,4个LED就会按顺序闪烁了。usleep是微妙级延时,我们在这里每次延时0.5ms。  

  接下来,我们总结一下这节内容。我们来对比一下寄存器操作方式与API之间有什么联系和不同,上面的程序,如果用NIOS II IDE提供的API来写,那么如下图所示

  

IOWR_ALTERA_AVALON_PIO是一个宏,在altera_avalon_pio_regs.h中,其定义如下

(大家可以按住ctrl键后,用鼠标点击进入定义所在的位置),大家可以看到,它是一个IOWR的宏,而IOWR的具体写法我就在此不详细说了(大家感兴趣的可以去NIOS的源码),反正就是对硬件地址的控制。我的做法就是绕过这个大圈子,直接去控制它的寄存器。

      大家可能可能有点纳闷,我们的机构体中定义了四个变量,但只用了DATA一个,在这说明一下原因,首先是DIRECTION,这个是IO的方向,就是说是输入还是输出,或者是双向的。因为在我们构建PIO模块的构成中有了一个选项,我们选择了输出(Output ports only),也就是我们在底层就已经固定了它的方向,所以在软件中就不需要在定义了。还有两个变量是涉及到中断时才会用到,所以在这个程序中也没有用到。

      这一节是以后内容的基础,希望大家好好的理解,尤其是sopc.h中定义的那个结构体,大家一定要理解清楚。以后,还会涉及到UART,SPI等,都需要建立结构体,这个都是触类旁通,举一反三的东西,弄清楚一个,其他的都好理解了。

      我要提醒大家一句,我虽然提倡大家用操作寄存器方式编程,但并不希望所有的程序都按这种方式来写,比如说对flash的操作,我们就可以使用API来写,因为flash的操作相对复杂,而利用API可以很简单的几个语句就能完成,没必要自己来写。

 
原文地址:https://www.cnblogs.com/spartan/p/2127984.html