小梅哥AC620开发板NIOS II LWIP实现HTTP网页控制数码管的显示内容

Quartus II 13.0工程下载地址:https://pan.baidu.com/s/1_Vg6WjFp4GcsjzssqoEKCA(提取码:vtue)

【程序运行效果】

浏览器访问板子首页(http://板子IP地址/),里面有一个文本框,输入一个数字后点击确定按钮,输入的数字会显示到板子的数码管上。

小梅哥AC620开发板NIOS II LWIP实现HTTP网页控制数码管的显示内容_第1张图片

网页中的文本框是数字类型(type="number"),因此在Firefox浏览器和Opera浏览器下,文本框右边会显示上下拨动的按钮,而且输入小数,输入负数,或者大于99999999的数都会提示错误,无法提交表单。

小梅哥AC620开发板NIOS II LWIP实现HTTP网页控制数码管的显示内容_第2张图片

小梅哥AC620开发板NIOS II LWIP实现HTTP网页控制数码管的显示内容_第3张图片

【程序说明】

Web服务器采用的是lwip自带的httpd服务器,网页表单提交方式为GET。
在lwipopts.h里面开启LWIP_HTTPD_CGI选项,可解析URL中“?”后面的参数内容,并在cgi_handler函数中使用。
开启LWIP_HTTPD_SSI选项,可将C语言变量的值动态显示到网页中,替换占位符。占位符的名称由ssi_tags字符串数组决定,替换的内容由ssi_handler函数决定。LWIP_HTTPD_SSI_INCLUDE_TAG=0表示不在网页中保留占位符。

板子上的数码管由74HC595芯片三线驱动,驱动程序由SegmentDisplay.v实现。输入的segdisp_num为BCD码,每个数位占4位,8个数位共32位。
输入数值转换成BCD码的任务由C语言完成,转换完毕后是一个uint32_t的32位数值,通过NIOS里面的PIO传入Verilog程序。

【主要C程序代码】

lwipopts.h关于HTTP的选项:

// 配置HTTP服务器
#define HTTPD_FSDATA_FILE "fsdata_custom.h"
#define LWIP_HTTPD_CGI 1
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0

hello_world.c:

/*
 * "Hello World" example.
 *
 * This example prints 'Hello from Nios II' to the STDOUT stream. It runs on
 * the Nios II 'standard', 'full_featured', 'fast', and 'low_cost' example
 * designs. It runs with or without the MicroC/OS-II RTOS and requires a STDOUT
 * device in your system's hardware.
 * The memory footprint of this hosted application is ~69 kbytes by default
 * using the standard reference design.
 *
 * For a reduced footprint version of this template, and an explanation of how
 * to reduce the memory footprint for a given application, see the
 * "small_hello_world" template.
 *
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static const char *ssi_tags[] = {"segdisp"};
static struct netif netif_rtl8201cp;
static tCGI cgis[2];

alt_u32 sys_now(void)
{
	// BSP Editor里面Settings -> Common -> hal -> sys_clk_timer必须选择一个定时器
	// 而且该定时器的计时间隔必须为1ms
	LWIP_ASSERT("incorrect tick rate", alt_ticks_per_second() == 1000);
	return alt_nticks();
}

static void display_ip(void)
{
	const ip_addr_t *addr;
	static uint8_t ip_displayed = 0;
	static uint8_t ip6_displayed = 0;
	int i, ip_present;
	int dns = 0;

	if (netif_dhcp_data(&netif_rtl8201cp) == NULL)
		ip_present = 1; // 使用静态IP地址
	else if (dhcp_supplied_address(&netif_rtl8201cp))
		ip_present = 2; // 使用DHCP获得IP地址, 且已成功获取到IP地址
	else
		ip_present = 0; // 使用DHCP获得IP地址, 且还没有获取到IP地址

	// 显示IPv4地址
	if (ip_present)
	{
		if (ip_displayed == 0)
		{
			ip_displayed = 1;

			if (ip_present == 2)
				printf("DHCP supplied address!\r\n");
			printf("IP address: %s\r\n", ipaddr_ntoa(&netif_rtl8201cp.ip_addr));
			printf("Subnet mask: %s\r\n", ipaddr_ntoa(&netif_rtl8201cp.netmask));
			printf("Default gateway: %s\r\n", ipaddr_ntoa(&netif_rtl8201cp.gw));
			dns = 1;
		}
	}
	else
		ip_displayed = 0;

	// 显示IPv6地址
	for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) // 0号地址是本地链路地址, 不需要显示
	{
		if (ip6_addr_isvalid(netif_ip6_addr_state(&netif_rtl8201cp, i)))
		{
			if ((ip6_displayed & _BV(i)) == 0)
			{
				ip6_displayed |= _BV(i);
				printf("IPv6 address %d: %s\r\n", i, ipaddr_ntoa(netif_ip_addr6(&netif_rtl8201cp, i)));
				dns = 1;
			}
		}
		else
			ip6_displayed &= ~_BV(i);
	}

	// 显示DNS服务器地址
	// 在lwip中, IPv4 DHCP和IPv6 SLAAC获取到的DNS地址会互相覆盖
	if (dns)
	{
		addr = dns_getserver(0);
		if (ip_addr_isany(addr))
			return;
		printf("DNS Server: %s", ipaddr_ntoa(addr));

		addr = dns_getserver(1);
		if (!ip_addr_isany(addr))
			printf(" %s", ipaddr_ntoa(addr));

		printf("\r\n");
	}
}

static void net_config(int use_dhcp)
{
	ip4_addr_t ipaddr, netmask, gw;

	if (use_dhcp)
		netif_add_noaddr(&netif_rtl8201cp, NULL, ethernetif_init, netif_input);
	else
	{
		IP4_ADDR(&ipaddr, 192, 168, 0, 19);
		IP4_ADDR(&netmask, 255, 255, 255, 0);
		IP4_ADDR(&gw, 192, 168, 0, 1);
		netif_add(&netif_rtl8201cp, &ipaddr, &netmask, &gw, NULL, ethernetif_init, netif_input);
	}
	netif_set_default(&netif_rtl8201cp);
	netif_set_up(&netif_rtl8201cp);

	if (use_dhcp)
		dhcp_start(&netif_rtl8201cp);

	netif_create_ip6_linklocal_address(&netif_rtl8201cp, 1);
	printf("IPv6 link-local address: %s\r\n", ipaddr_ntoa(netif_ip_addr6(&netif_rtl8201cp, 0)));
	netif_set_ip6_autoconfig_enabled(&netif_rtl8201cp, 1);
}

static const char *cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
	int i, num, ret;
	int found = 0;
	uint32_t output = 0;

	for (i = 0; i < iNumParams; i++)
	{
		if (strcasecmp(pcParam[i], "num") == 0)
		{
			ret = sscanf(pcValue[i], "%d", &num);
			if (ret == 1 && num >= 0 && num <= 99999999)
				found = 1;
			break;
		}
	}

	if (found)
	{
		for (i = 0; i < 8; i++)
		{
			output |= (num % 10) << (4 * i);
			num /= 10;
		}
		printf("output=0x%08lx\r\n", output);
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, output);
	}

	return "/index.ssi";
}

static u16_t ssi_handler(int iIndex, char *pcInsert, int iInsertLen)
{
	int len;

	if (iIndex == 0)
	{
		len = snprintf(pcInsert, iInsertLen, "%x", IORD_ALTERA_AVALON_PIO_DATA(PIO_0_BASE));
		return len;
	}
	else
		return HTTPD_SSI_TAG_UNKNOWN;
}

int main(void)
{
	printf("Hello from Nios II!\r\n");

	lwip_init();
	net_config(1);

	httpd_init();
	cgis[0].pcCGIName = "/";
	cgis[0].pfnCGIHandler = cgi_handler;
	cgis[1].pcCGIName = "/index.ssi";
	cgis[1].pfnCGIHandler = cgi_handler;
	http_set_cgi_handlers(cgis, LWIP_ARRAYSIZE(cgis));
	http_set_ssi_handler(ssi_handler, ssi_tags, LWIP_ARRAYSIZE(ssi_tags));

	netbiosns_init();
	netbiosns_set_name("EP4CE10F17C8");

	while (1)
	{
		ethernetif_check_link(&netif_rtl8201cp);
		display_ip();

		ethernetif_input(&netif_rtl8201cp);
		sys_check_timeouts();
	}
}

lwip-2.1.2/apps/http/fs/index.ssi:(HTML5)
*.html文件为静态网页文件,*.ssi文件为动态网页文件





8位数码管




8位数码管


这里顺便提一下Win7 IE11浏览器的一个bug:文本框输入中文后,文本框的位置会自动往下偏移1个像素,删除中文,又会往上偏移回来。
解决办法见https://blog.csdn.net/ZLK1214/article/details/80367086。

【Verilog程序代码】

main.v:

module main(
    input clock,
    input uart_rx,
    output uart_tx,
    output sdram_clk,
    output [11:0] sdram_addr,
    output [1:0] sdram_ba,
    output sdram_cas_n,
    output sdram_cke,
    output sdram_cs_n,
    inout [15:0] sdram_dq,
    output [1:0] sdram_dqm,
    output sdram_ras_n,
    output sdram_we_n,
    output eth_rst_n,
    output eth_mdc,
    inout eth_mdio,
    input eth_col,
    input eth_crs,
    input eth_tx_clk,
    output eth_tx_en,
    output [3:0] eth_txd,
    input eth_rx_clk,
    input eth_rx_dv,
    input [3:0] eth_rxd,
    input eth_rx_er,
    output seg_sclk,
    output seg_rclk,
    output seg_dio
    );
    
    /* 产生复位信号 */
    wire nrst;
    Reset reset(clock, nrst);
    
    /* PLL倍频 */
    wire altpll_c0;
    wire altpll_locked;
    altpll_0 altpll_0(
        .areset(~nrst),
        .inclk0(clock),
        .c0(altpll_c0), // 频率等于clock, 但相位相差63度, 供SDRAM使用
        .locked(altpll_locked)
    );
    
    /* 数码管 */
    wire segdisp_clk;
    wire [31:0] segdisp_num;
    ClockDivider #(500) segdisp_divider(clock, nrst, segdisp_clk); // 50MHz/(2*500)=50kHz
    SegmentDisplay segdisp(segdisp_clk, nrst, seg_sclk, seg_rclk, seg_dio, segdisp_num);
    
    /* 缓冲eth_rx_clk和eth_tx_clk时钟输入信号, 避免CRC错误 */
    wire eth_rx_clk_g;
    wire eth_tx_clk_g;
    altclkctrl_0 altclkctrl_0(
        .inclk(eth_rx_clk),
        .outclk(eth_rx_clk_g)
    );
    altclkctrl_1 altclkctrl_1(
        .inclk(eth_tx_clk),
        .outclk(eth_tx_clk_g)
    );
    
    /* NIOS II */
    wire eth_tse_0_mac_mdio_connection_mdio_oen;
    wire eth_tse_0_mac_mdio_connection_mdio_out;
    
    wire eth_tse_0_mac_mii_connection_mii_tx_err;
    
    wire eth_tse_0_mac_misc_connection_ff_rx_a_empty;
    wire eth_tse_0_mac_misc_connection_ff_rx_a_full;
    wire eth_tse_0_mac_misc_connection_ff_rx_dsav;
    wire eth_tse_0_mac_misc_connection_ff_tx_a_empty;
    wire eth_tse_0_mac_misc_connection_ff_tx_a_full;
    wire eth_tse_0_mac_misc_connection_ff_tx_septy;
    wire eth_tse_0_mac_misc_connection_magic_wakeup;
    wire [17:0] eth_tse_0_mac_misc_connection_rx_err_stat;
    wire [3:0] eth_tse_0_mac_misc_connection_rx_frm_type;
    wire eth_tse_0_mac_misc_connection_tx_ff_uflow;
    
    wire eth_tse_0_mac_status_connection_ena_10;
    wire eth_tse_0_mac_status_connection_eth_mode;
    
    assign sdram_clk = altpll_c0;
    assign eth_mdio = (!eth_tse_0_mac_mdio_connection_mdio_oen) ? eth_tse_0_mac_mdio_connection_mdio_out : 1'bz;
    assign eth_rst_n = altpll_locked;
    
    nios nios(
        .clk_clk(clock),
        .reset_reset_n(altpll_locked),
        .eth_tse_0_mac_mdio_connection_mdc(eth_mdc),
        .eth_tse_0_mac_mdio_connection_mdio_in(eth_mdio),
        .eth_tse_0_mac_mdio_connection_mdio_oen(eth_tse_0_mac_mdio_connection_mdio_oen),
        .eth_tse_0_mac_mdio_connection_mdio_out(eth_tse_0_mac_mdio_connection_mdio_out),
        
        .eth_tse_0_mac_mii_connection_mii_rx_d(eth_rxd),
        .eth_tse_0_mac_mii_connection_mii_rx_dv(eth_rx_dv),
        .eth_tse_0_mac_mii_connection_mii_rx_err(eth_rx_er),
        .eth_tse_0_mac_mii_connection_mii_tx_d(eth_txd),
        .eth_tse_0_mac_mii_connection_mii_tx_en(eth_tx_en),
        .eth_tse_0_mac_mii_connection_mii_tx_err(eth_tse_0_mac_mii_connection_mii_tx_err),
        
        .eth_tse_0_mac_misc_connection_ff_rx_a_empty(eth_tse_0_mac_misc_connection_ff_rx_a_empty),
        .eth_tse_0_mac_misc_connection_ff_rx_a_full(eth_tse_0_mac_misc_connection_ff_rx_a_full),
        .eth_tse_0_mac_misc_connection_ff_rx_dsav(eth_tse_0_mac_misc_connection_ff_rx_dsav),
        .eth_tse_0_mac_misc_connection_ff_tx_a_empty(eth_tse_0_mac_misc_connection_ff_tx_a_empty),
        .eth_tse_0_mac_misc_connection_ff_tx_a_full(eth_tse_0_mac_misc_connection_ff_tx_a_full),
        .eth_tse_0_mac_misc_connection_ff_tx_crc_fwd(1'b0),
        .eth_tse_0_mac_misc_connection_ff_tx_septy(eth_tse_0_mac_misc_connection_ff_tx_septy),
        .eth_tse_0_mac_misc_connection_magic_sleep_n(1'b1),
        .eth_tse_0_mac_misc_connection_magic_wakeup(eth_tse_0_mac_misc_connection_magic_wakeup),
        .eth_tse_0_mac_misc_connection_rx_err_stat(eth_tse_0_mac_misc_connection_rx_err_stat),
        .eth_tse_0_mac_misc_connection_rx_frm_type(eth_tse_0_mac_misc_connection_rx_frm_type),
        .eth_tse_0_mac_misc_connection_tx_ff_uflow(eth_tse_0_mac_misc_connection_tx_ff_uflow),
        .eth_tse_0_mac_misc_connection_xoff_gen(1'b0),
        .eth_tse_0_mac_misc_connection_xon_gen(1'b0),
        
        .eth_tse_0_mac_status_connection_ena_10(eth_tse_0_mac_status_connection_ena_10),
        .eth_tse_0_mac_status_connection_eth_mode(eth_tse_0_mac_status_connection_eth_mode),
        .eth_tse_0_mac_status_connection_set_10(1'b0),
        .eth_tse_0_mac_status_connection_set_1000(1'b0),
        
        .eth_tse_0_pcs_mac_rx_clock_connection_clk(eth_rx_clk_g),
        .eth_tse_0_pcs_mac_tx_clock_connection_clk(eth_tx_clk_g),
        
        .new_sdram_controller_0_wire_addr(sdram_addr),
        .new_sdram_controller_0_wire_ba(sdram_ba),
        .new_sdram_controller_0_wire_cas_n(sdram_cas_n),
        .new_sdram_controller_0_wire_cke(sdram_cke),
        .new_sdram_controller_0_wire_cs_n(sdram_cs_n),
        .new_sdram_controller_0_wire_dq(sdram_dq),
        .new_sdram_controller_0_wire_dqm(sdram_dqm),
        .new_sdram_controller_0_wire_ras_n(sdram_ras_n),
        .new_sdram_controller_0_wire_we_n(sdram_we_n),
        
        .uart_0_external_connection_rxd(uart_rx),
        .uart_0_external_connection_txd(uart_tx),
        .pio_0_external_connection_export(segdisp_num)
    );
    
endmodule

ClockDivider.v:

module ClockDivider #(
    parameter N = 2
    )(
    input clk_in,
    input nrst,
    output reg clk_out
    );
    
    function integer clogb2(input integer depth);
        for (clogb2 = 0; depth > 0; clogb2 = clogb2 + 1)
            depth = depth >> 1;
    endfunction
    
    reg [clogb2(N - 1) - 1:0] counter;
    always @(posedge clk_in, negedge nrst) begin
        if (!nrst) begin
            clk_out <= 0;
            counter <= 0;
        end
        else if (counter == N - 1) begin
            counter <= 0;
            clk_out <= ~clk_out;
        end
        else
            counter <= counter + 1'b1;
    end
    
endmodule

Reset.v:

module Reset(
    input clock,
    output nrst
    );
    
    reg [3:0] counter = 4'd15;
    assign nrst = (counter == 0);
    always @(posedge clock) begin
        if (!nrst)
            counter <= counter - 1'b1;
    end
    
endmodule

SegmentDisplay.v:

module SegmentDisplay(
    input clock,
    input nrst,
    output seg_sclk,
    output seg_rclk,
    output reg seg_dio,
    input [31:0] segdisp_num // 8位数字的BCD码
    );
    
    reg [3:0] bit_i; // 当前SPI位号
    reg [2:0] num_i; // 当前数位
    reg [31:0] num; // 显示的BCD码
    reg [1:0] state;
    assign seg_sclk = (state == 1) ? clock : 1'b0;
    assign seg_rclk = (state == 2);
    always @(negedge clock, negedge nrst) begin
        if (!nrst)
            state <= 0;
        else begin
            case (state)
                0: begin
                    bit_i <= 0;
                    num_i <= 0;
                    num <= segdisp_num;
                    state <= 1;
                end
                1: begin
                    if (bit_i == 4'hf)
                        state <= 2;
                    bit_i <= bit_i + 1'b1;
                end
                2: begin
                    if (num_i != 3'd7)
                        state <= 1;
                    else
                        state <= 0;
                    num_i <= num_i + 1'b1;
                end
            endcase
        end
    end
    
    reg [7:0] font;
    always @(*) begin
        // 数码管段码
        case (num[{num_i, 2'b0}+:4])
            0:
                font = 8'hc0;
            1:
                font = 8'hf9;
            2:
                font = 8'ha4;
            3:
                font = 8'hb0;
            4:
                font = 8'h99;
            5:
                font = 8'h92;
            6:
                font = 8'h82;
            7:
                font = 8'hf8;
            8:
                font = 8'h80;
            9:
                font = 8'h90;
            default:
                font = 8'hff;
        endcase
    end
    
    always @(*) begin
        if (!bit_i[3])
            seg_dio = font[~bit_i[2:0]]; // 段选
        else
            seg_dio = (num_i == ~bit_i[2:0]); // 位选
    end
    
endmodule

 

你可能感兴趣的:(FPGA,NIOS,Altera,FPGA,lwip,TSE)