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

第一部分

软件实验二是通过采用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

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

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

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

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

 ./generate_hps_qsys_header.sh

出现如下结果,说明成功
在这里插入图片描述
回到该工程文件夹下可看到生成了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"

头文件的说明:
#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) //

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 ); 

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 ); 

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

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

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

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

点亮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 );
}

上板测试,成功点亮LED

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

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

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值