学习主要参考https://rocketboards.org/foswiki/Documentation/EmbeddedLinuxBeginnerSGuide
1.软件设计
到这一步就要回头参考前面的硬件修改了,本例程针对前面的自定义PIO组件进行开发控制。
首先把前面生成的hps_0.h
硬件头文件拷贝出来。
打开该文件查看对应的地址:可以看到自定义的PIO组件地址为DYQ_PIO_0_BASE 0x0
#ifndef _ALTERA_HPS_0_H_
#define _ALTERA_HPS_0_H_
/*
* This file was automatically generated by the swinfo2header utility.
*
* Created from SOPC Builder system 'soc_system' in
* file './soc_system.sopcinfo'.
*/
/*
* This file contains macros for module 'hps_0' and devices
* connected to the following masters:
* h2f_axi_master
* h2f_lw_axi_master
*
* Do not include this header file and another header file created for a
* different module or master group at the same time.
* Doing so may result in duplicate macro names.
* Instead, use the system header file which has macros with unique names.
*/
/*
* Macros for device 'dyq_pio_0', class 'dyq_pio'
* The macros are prefixed with 'DYQ_PIO_0_'.
* The prefix is the slave descriptor.
*/
#define DYQ_PIO_0_COMPONENT_TYPE dyq_pio
#define DYQ_PIO_0_COMPONENT_NAME dyq_pio_0
#define DYQ_PIO_0_BASE 0x0
#define DYQ_PIO_0_SPAN 32
#define DYQ_PIO_0_END 0x1f
/*
* Macros for device 'sysid_qsys', class 'altera_avalon_sysid_qsys'
* The macros are prefixed with 'SYSID_QSYS_'.
* The prefix is the slave descriptor.
*/
#define SYSID_QSYS_COMPONENT_TYPE altera_avalon_sysid_qsys
#define SYSID_QSYS_COMPONENT_NAME sysid_qsys
#define SYSID_QSYS_BASE 0x1000
#define SYSID_QSYS_SPAN 8
#define SYSID_QSYS_END 0x1007
#define SYSID_QSYS_ID 2899645186
#define SYSID_QSYS_TIMESTAMP 1648648392
/*
* Macros for device 'jtag_uart', class 'altera_avalon_jtag_uart'
* The macros are prefixed with 'JTAG_UART_'.
* The prefix is the slave descriptor.
*/
#define JTAG_UART_COMPONENT_TYPE altera_avalon_jtag_uart
#define JTAG_UART_COMPONENT_NAME jtag_uart
#define JTAG_UART_BASE 0x2000
#define JTAG_UART_SPAN 8
#define JTAG_UART_END 0x2007
#define JTAG_UART_IRQ 2
#define JTAG_UART_READ_DEPTH 64
#define JTAG_UART_READ_THRESHOLD 8
#define JTAG_UART_WRITE_DEPTH 64
#define JTAG_UART_WRITE_THRESHOLD 8
/*
* Macros for device 'led_pio', class 'altera_avalon_pio'
* The macros are prefixed with 'LED_PIO_'.
* The prefix is the slave descriptor.
*/
#define LED_PIO_COMPONENT_TYPE altera_avalon_pio
#define LED_PIO_COMPONENT_NAME led_pio
#define LED_PIO_BASE 0x3000
#define LED_PIO_SPAN 16
#define LED_PIO_END 0x300f
#define LED_PIO_BIT_CLEARING_EDGE_REGISTER 0
#define LED_PIO_BIT_MODIFYING_OUTPUT_REGISTER 0
#define LED_PIO_CAPTURE 0
#define LED_PIO_DATA_WIDTH 7
#define LED_PIO_DO_TEST_BENCH_WIRING 0
#define LED_PIO_DRIVEN_SIM_VALUE 0
#define LED_PIO_EDGE_TYPE NONE
#define LED_PIO_FREQ 50000000
#define LED_PIO_HAS_IN 0
#define LED_PIO_HAS_OUT 1
#define LED_PIO_HAS_TRI 0
#define LED_PIO_IRQ_TYPE NONE
#define LED_PIO_RESET_VALUE 127
/*
* Macros for device 'dipsw_pio', class 'altera_avalon_pio'
* The macros are prefixed with 'DIPSW_PIO_'.
* The prefix is the slave descriptor.
*/
#define DIPSW_PIO_COMPONENT_TYPE altera_avalon_pio
#define DIPSW_PIO_COMPONENT_NAME dipsw_pio
#define DIPSW_PIO_BASE 0x4000
#define DIPSW_PIO_SPAN 16
#define DIPSW_PIO_END 0x400f
#define DIPSW_PIO_IRQ 0
#define DIPSW_PIO_BIT_CLEARING_EDGE_REGISTER 1
#define DIPSW_PIO_BIT_MODIFYING_OUTPUT_REGISTER 0
#define DIPSW_PIO_CAPTURE 1
#define DIPSW_PIO_DATA_WIDTH 4
#define DIPSW_PIO_DO_TEST_BENCH_WIRING 0
#define DIPSW_PIO_DRIVEN_SIM_VALUE 0
#define DIPSW_PIO_EDGE_TYPE ANY
#define DIPSW_PIO_FREQ 50000000
#define DIPSW_PIO_HAS_IN 1
#define DIPSW_PIO_HAS_OUT 0
#define DIPSW_PIO_HAS_TRI 0
#define DIPSW_PIO_IRQ_TYPE EDGE
#define DIPSW_PIO_RESET_VALUE 0
/*
* Macros for device 'button_pio', class 'altera_avalon_pio'
* The macros are prefixed with 'BUTTON_PIO_'.
* The prefix is the slave descriptor.
*/
#define BUTTON_PIO_COMPONENT_TYPE altera_avalon_pio
#define BUTTON_PIO_COMPONENT_NAME button_pio
#define BUTTON_PIO_BASE 0x5000
#define BUTTON_PIO_SPAN 16
#define BUTTON_PIO_END 0x500f
#define BUTTON_PIO_IRQ 1
#define BUTTON_PIO_BIT_CLEARING_EDGE_REGISTER 1
#define BUTTON_PIO_BIT_MODIFYING_OUTPUT_REGISTER 0
#define BUTTON_PIO_CAPTURE 1
#define BUTTON_PIO_DATA_WIDTH 2
#define BUTTON_PIO_DO_TEST_BENCH_WIRING 0
#define BUTTON_PIO_DRIVEN_SIM_VALUE 0
#define BUTTON_PIO_EDGE_TYPE FALLING
#define BUTTON_PIO_FREQ 50000000
#define BUTTON_PIO_HAS_IN 1
#define BUTTON_PIO_HAS_OUT 0
#define BUTTON_PIO_HAS_TRI 0
#define BUTTON_PIO_IRQ_TYPE EDGE
#define BUTTON_PIO_RESET_VALUE 0
/*
* Macros for device 'ILC', class 'interrupt_latency_counter'
* The macros are prefixed with 'ILC_'.
* The prefix is the slave descriptor.
*/
#define ILC_COMPONENT_TYPE interrupt_latency_counter
#define ILC_COMPONENT_NAME ILC
#define ILC_BASE 0x30000
#define ILC_SPAN 256
#define ILC_END 0x300ff
#endif /* _ALTERA_HPS_0_H_ */
然后新建一个main.c
文件,该c
文件为参考官方提供的HPS_LED_GPIO
例程。
该代码需要注意:
- 定义
LED_CTRL_DATA 0x02
其含义是结合前面自定义的IP组件,其写端口的偏移地址为2,因此在控制PIO的时候,需要在该组件地址下加上2来指定对应的PIO。
#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 "hps_0.h"
#define HW_REGS_BASE ( ALT_STM_OFST )
#define HW_REGS_SPAN ( 0x04000000 )
#define HW_REGS_MASK ( HW_REGS_SPAN - 1 )
#define LED_CTRL_DATA 0x02 //定义led的相对地址,对应硬件自定义IP时,在地址为1时对LED进行读写。
int main() {
void *virtual_base;
int fd;
int loop_count;
int led_direction;
int led_mask;
unsigned long *h2p_lw_led_addr;
// 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 );
}
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 );
}
h2p_lw_led_addr=virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + DYQ_PIO_0_BASE ) & ( unsigned long)( HW_REGS_MASK ) );
// toggle the LEDs a bit
printf( "dyq is ok\n" );
loop_count = 0;
led_mask = 0x01;
led_direction = 0; // 0: left to right direction
while( loop_count < 60 ) {
// control led
*(h2p_lw_led_addr+LED_CTRL_DATA ) = ~led_mask; //PIO组件地址加上控制的偏移地址
// wait 100ms
usleep( 100*1000 );
// update led mask
if (led_direction == 0){
led_mask <<= 1;
if (led_mask == (0x01 << (7-1)))
led_direction = 1;
}else{
led_mask >>= 1;
if (led_mask == 0x01){
led_direction = 0;
loop_count++;
}
}
} // while
// clean up our memory mapping and exit
if( munmap( virtual_base, HW_REGS_SPAN ) != 0 ) {
printf( "ERROR: munmap() failed...\n" );
close( fd );
return( 1 );
}
close( fd );
return( 0 );
}
Makefile文件:
#
TARGET = HPS_FPGA_LED
#
ALT_DEVICE_FAMILY ?= soc_cv_av
SOCEDS_ROOT ?= $(SOCEDS_DEST_ROOT)
HWLIBS_ROOT = $(SOCEDS_ROOT)/ip/altera/hps/altera_hps/hwlib
CROSS_COMPILE = arm-linux-gnueabihf-
CFLAGS = -g -Wall -D$(ALT_DEVICE_FAMILY) -I$(HWLIBS_ROOT)/include/$(ALT_DEVICE_FAMILY) -I$(HWLIBS_ROOT)/include/
LDFLAGS = -g -Wall
CC = $(CROSS_COMPILE)gcc
ARCH= arm
build: $(TARGET)
$(TARGET): main.o
$(CC) $(LDFLAGS) $^ -o $@
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -f $(TARGET) *.a *.o *~
之后使用EDS终端make一下就能生成一个HPS_FPGA_LED
可执行文件。
2.开发运行
回到Linux虚拟机,将前面生成的可执行文件复制过来,将SD卡插到电脑上。将其拷贝到SD卡中的usr文件目录下
sudo cp HPS_FPGA_LED /media/dyq/42867f78-0b58-4fbb-918c-8d4a106ef64e/usr/
之后插到开发板上,进入root,进入usr目录,首先需要修改一下可执行文件的权限,然后才能运行该程序。
Welcome to Buildroot
buildroot login: root
Password:
# cd ..
# ls
HPS_FPGA_LED home media root tmp
bin lib mnt run usr
dev lib32 opt sbin var
etc linuxrc proc sys
# cd usr/
# ls
H0 H2 HPS_FPGA_LED lib sbin
H1 H3 bin lib32 share
# chmod 777 HPS_FPGA_LED
# ./HPS_FPGA_LED
之后就可以看到开发板的流水灯了。