赞
踩
软件实验二是通过采用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
最终得到如下:
我们在使用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头文件,那么代码无错误的情况下即可成功编译。
#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端口是输入输出,同时输入的是多少,输出的是多少)
下图为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引脚的行为。
由此确定了输入输出,并知道了输入引脚和输出引脚的值,那么我们就能知道,输入KEY和输出LED的状态是0还是1。
分析完原理后,我们就要采用软件的方式来完成地址映射。可以通过如下软件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中各种外设地址信息进行了定义(虚拟地址映射的时候会用到)
如下为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 ) ) ) );
如下是教材中提供的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 ); }
头文件问题解决之后,编译C程序,然后生成可执行文件,将可执行文件拷贝到SD卡,然后上板并进行串口终端调试,即可完成在DE1-SOC开发板点亮LED,其具体步骤和实验一的hello_fpga是一样的。
已上板测试,按压开关,即可点亮LED。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。