当前位置:   article > 正文

DE1-SoC软件实验(二)—— 点亮LED灯(头文件hwlib.h及socal/socal.h等的配置,及hps_0.h头文件的生成,解决soc_cv_av 没有声明)

de1-soc

第一部分

软件实验二是通过采用QSYS创建一个硬核工程,控制HPS端LED和KEY
由于只需要控制HPS端的外设,因此其实现方法和实验一是相同的,编写软件程序即可。

在软件实验二的程序中,通过C程序编写点亮LED灯的时候,包含如下的头文件,这些头文件的环境配置以及hps_0.h头文件的生成都是重点要学习的地方。其中hps_0.h头文件就是通过我们创建的硬核工程通过命令生成的。下面均会一 一讲述。

在这里插入图片描述

环境变量配置

首先上面这些库是 SoC EDS 软件提供的, DS-5 中默认并没有包含该库,所以如果不进行环境变量配置,直接在程序中包含这些文件, DS-5 会提示找不到文件,因此需要在工程中设置头文件包含路径。
方法如下:
在工程的基础上 鼠标右击选择 Properties在这里插入图片描述
首先进行hwlib.h的添加
根据安装的SOCEDS找到hwlib.h的路径

D:\intelFPGA\17.1\embedded\ip\altera\hps\altera_hps\hwlib\include

点击Add,输入路径,然后勾选 Add to all configurations 和 Add to all languages 选项, 然后点击 OK 即可,同样的方式可添加socal./hps.h头文件

D:\intelFPGA\17.1\embedded\ip\altera\hps\altera_hps\hwlib\include\soc_cv_av

最终得到如下:

在这里插入图片描述

hps_0.h头文件的生成

我们在使用qsys进行外设添加后,若想要对这些FPGA侧的外设IP进行操作,我们就要知道这些外设的硬件信息,该头文件定义了这些外设中的各种地址信息(比如外设在轻量级h2f桥上的偏移地址等)。将该文件生成并加入到C工程中。
在这里插入图片描述
生成方法:
此文件需要我们根据Qsys生成的硬件工程来生成。
当我们采用Qsys搭建完系统后,会生成如下的.sopcinfo文件
在这里插入图片描述
通过该文件即可生成hps_0头文件。
首先写一个sh脚本,其内容如下,"./soc_system.sopcinfo" \ 代表用qsys搭建的硬核工程的名字,根据自己的进行修改。

#!/bin/sh
sopc-create-header-files \
"./soc_system.sopcinfo" \  
--single hps_0.h \
--module hps_0
  • 1
  • 2
  • 3
  • 4
  • 5

此时该工程文件夹下有了SH文件。
在这里插入图片描述
打开SOCEDS。定位到当前含有sopcinfo文件的目录下

cd "D:/intelFPGA/project/my_first_hps_fpga/fpga-rtl"
  • 1

通过ls命令可查看,现在定位成功。

在这里插入图片描述
输入如下命令

 ./generate_hps_qsys_header.sh
  • 1

出现如下结果,说明成功
在这里插入图片描述
回到该工程文件夹下可看到生成了hps_0.h头文件。
在这里插入图片描述
将hps_0.h头文件添加到C工程中即可。
在这里插入图片描述

环境配置完成,且有了hps_0.h头文件,那么代码无错误的情况下即可成功编译。

补充:解决soc_cv_av 没有声明

在这里插入图片描述
#include “hwlib.h” 前面直接添加

#define soc_cv_av

第二部分

实验目的

创建一个HPS_GPIO的软件工程,用该工程来控制HPS端的LED和KEY。其中LED和KEY均作为HPS端的外设,因此采用软件工程的方式来控制的方法和软件实验(一)中采用C程序来编写hello_fpga类似,也无需Quartusii工程,只编写C程序即可。

实验原理

在C程序中,我们要采用虚拟地址映射的方式,来控制LED和KEY。其中用到的是Linux内核memory_mapped device(内存映射设备)驱动访问GPIO控制器的寄存器,这些被驱动的寄存器就相当于LED和KEY的I/O接口。在映射的时候我们就需要知道I/O的方向以及写入读出值。(也就是需要知道I/O端口是输入输出,同时输入的是多少,输出的是多少)

HPS GPIO的原理

下图为GPIO例程的方块图,LED和KEY都是连接到DE1-SoC HPS部分的GPIO控制器上的,同时采用memory_mapped device来驱动访问GPIO控制器的寄存器,从而实现GPIO的控制行为在这里插入图片描述
如下是GPIO的接口方块图。可看到HPS提供了三个通用I/O接口模块,GPIO 0、GPIO 1、GPIO 2在这里插入图片描述
前面提到GPIO控制器通过驱动访问GPIO控制器的寄存器,来实现I/O控制,那么下面就展示了对应的寄存器组所控制的I/O引脚的行为。

  • 采用三种32位寄存器来实现引脚方向以及写入写出的控制:
  1. GPIO1_SWPORTA_DDR:配置IO引脚方向 (通过该寄存器组来控制输出高低电平,其中高电平1为输出,低电平0为输入)
  2. GPIO1_SWPORTA_DR : 写数据到输出引脚(如果写入1,则输出高电平,写入0,输出低电平)——相当于LED接收的是0还是1
  3. GPIO1_EXT_PORTA : 从输入引脚读数据(读出来的数据为1,则高电平,为0,低电平)——相当于从KEY读到的数据是0还是1

由此确定了输入输出,并知道了输入引脚和输出引脚的值,那么我们就能知道,输入KEY和输出LED的状态是0还是1。

  • 如下是GPIO寄存器地址映射表:
    其中GPIO 0 控制器的寄存器映射到基地址0xFF708000,共4KB寻址空间。GPIO 1 和 GPIO 2 同理。
    在这里插入图片描述

软件API

分析完原理后,我们就要采用软件的方式来完成地址映射。可以通过如下软件API访问GPIO控制器的寄存器。(所用到的函数)
在这里插入图片描述
还可使用如下MACR0访问寄存器:(宏定义)
在这里插入图片描述
若想要使用上面的函数,那么就要包含如下的头文件:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "hwlib.h"
#include "socal/socal.h"
#include "socal/hps.h"
#include "socal/alt_gpio.h"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

头文件的说明:
#include “hwlib.h” :针对与HPS硬件编程有关的一些常量进行定义
#include “socal/socal.h” :该文件中是一些基本的底层操作函数(位,字节,字的读写)
#include “socal/hps.h” : 对HPS中各种外设地址信息进行了定义(虚拟地址映射的时候会用到)

LED和KEY控制

如下为HPS的KEY和LED的引脚分配,分别连接到54和53,由GPIO1控制器控制,另外该控制器还控制29~57
在这里插入图片描述
在GPIO1控制器中可看到,其中Bit-24,Bit-25分别控制LED和KEY。因此,我们即可通过控制前面介绍的三种32位的寄存器组(GPIO1_SWPORTA_DDR、GPIO1_SWPORTA_DR、GPIO1_EXT_PORTA )中相应的Bit-24,Bit-25来分别控制LED和KEY的方向,以及写入和读取。
在这里插入图片描述

具体代码分析

采用宏定义的方式来控制LED和KEY的方向,以及LED的输出状态

#define USER_IO_DIR     (0x01000000) //LED引脚为输出引脚
#define BIT_LED         (0x01000000) //点亮LED
#define BUTTON_MASK     (0x02000000) //
  • 1
  • 2
  • 3

GPIO1_SWPORTA_DDR寄存器组可设置IO引脚方向,alt_setbits_word 设定寄存器的指定位为1,如下语句是配置LED引脚为输出引脚

alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DDR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), USER_IO_DIR ); 
  • 1

ALT_GPIO1_SWPORTA_DR寄存器组是写数据到输出引脚,alt_setbits_word 设定寄存器的指定位为1,如下语句是点亮LED

alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), BIT_LED ); 
  • 1

alt_clrbits_word设定寄存器的指定位为0

alt_clrbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), BIT_LED );
  • 1

ALT_GPIO1_EXT_PORTA寄存器组是从输入引脚读数据,从而检查KEY是0还是1,(按下还是释放)

alt_read_word( ( virtual_base + ( ( uint32_t )(  ALT_GPIO1_EXT_PORTA_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ) );
  • 1

点亮LED的完整代码

如下是教材中提供的KEY控制LED的C程序:(根据错误提示进行一定修改),这部分采用DS-5软件来进行工程创建,并创建c文件。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define soc_cv_av
#include "hwlib.h"
#include "socal/socal.h"
#include "socal/hps.h"
#include "socal/alt_gpio.h"

#define HW_REGS_BASE ( ALT_STM_OFST )
#define HW_REGS_SPAN ( 0x04000000 )
#define HW_REGS_MASK ( HW_REGS_SPAN - 1 )

//采用宏定义的方式来控制LED和KEY的方向,以及LED的输出状态
#define USER_IO_DIR     (0x01000000)  
#define BIT_LED         (0x01000000)
#define BUTTON_MASK     (0x02000000)

int main(int argc, char **argv) {

	void *virtual_base;
	int fd;
	uint32_t  scan_input;
	int i;
	// map the address space for the LED registers into user space so we can interact with them.
	// we'll actually map in the entire CSR span of the HPS since we want to access various registers within that span
	//打开内存映射设备
	if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) {
		printf( "ERROR: could not open \"/dev/mem\"...\n" ); 
		return( 1 );
	}
//采用mmap函数,将LED寄存器的地址空间映射到用户空间
	virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE ); 

	if( virtual_base == MAP_FAILED ) {
		printf( "ERROR: mmap() failed...\n" );
		close( fd );
		return( 1 );
	}
	// initialize the pio controller
	// led: set the direction of the HPS GPIO1 bits attached to LEDs to output
	//GPIO1_SWPORTA_DDR设置IO引脚方向,配置LED引脚为输出引脚
	alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DDR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), USER_IO_DIR ); 
	printf("led test\r\n");
	printf("the led flash 2 times\r\n");
	//GPIO1_SWPORTA_DR 写数据到输出引脚,目的是用来点亮LED
	for(i=0;i<2;i++)
	{
	//alt_setbits_word是设定指定寄存器指定位为1
		alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), BIT_LED );  
		usleep(500*1000);
    //alt_clrbits_word是设定指定寄存器指定位为0
		alt_clrbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), BIT_LED );
		usleep(500*1000);  
	}
	printf("user key test \r\n");
	printf("press key to control led\r\n");
	//alt_read_word从指定寄存器读取一个值
	while(1){ 
		scan_input = alt_read_word( ( virtual_base + ( ( uint32_t )(  ALT_GPIO1_EXT_PORTA_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ) );	 //GPIO1_EXT_PORTA是用来从输入引脚读数据
		//usleep(1000*1000);
		if(~scan_input&BUTTON_MASK)
			alt_setbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), BIT_LED );
		else    
		    alt_clrbits_word( ( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ), BIT_LED );
	}
	// clean up our memory mapping and exit
	//munmap清除内存映射
	if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) { 
		printf( "ERROR: munmap() failed...\n" );
		close( fd ); //关闭设备驱动
		return( 1 );
	}
	close( fd );
	return( 0 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

上板测试,成功点亮LED

头文件问题解决之后,编译C程序,然后生成可执行文件,将可执行文件拷贝到SD卡,然后上板并进行串口终端调试,即可完成在DE1-SOC开发板点亮LED,其具体步骤和实验一的hello_fpga是一样的。
已上板测试,按压开关,即可点亮LED。
在这里插入图片描述

参考:
环境变量配置
头文件的生成

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/712869
推荐阅读
相关标签
  

闽ICP备14008679号