Tang Nano系列FPGA例程汇总

一、前言

基本上手使用和相关的历程


二、Tang Nano

板载下载器为CH552

Github: https://github.com/sipeed/Tang-Nano-examples


三、Tang Nano 1K

Github: https://github.com/sipeed/TangNano-1K-examples


四、Tang Nano 4K

Github: https://github.com/sipeed/TangNano-4K-example

其他:

  • 使用 Cortex-M3 点灯例程:Github (英文)



五、Tang Nano 9K

Github: https://github.com/sipeed/TangNano-9K-example


】驱动RGB屏教程


】先介绍时序

RGB LCD 显示协议和 VGA 类似,通信都有专用的行同步、场同步信号线。它们的主要区别是前者传输用的是数字信号,后者传输走的是模拟信号。

下面就介绍 VGA 的时序



上图分别是 VGA 在数据传输中的行同步、场同步时序

从时序图中可以看出,不论是显示一行数据还是一列数据,都需要一个同步(sync)信号,数据的传输需要在两个同步信号的脉冲之间完成

每一行的数据包括显示前沿(back porch)、有效数据(active video)、显示后沿(front porch)


其中的有效数据就是我们常说的分辨率,而显示前后沿的参数需要参考具体的分辨率与帧数进行设置,相关参数可以参考典型参数,链接在此: http://www.tinyvga.com/vga-timing


这块5寸屏幕的控制时序略有不同,相关参数的设置可以查看规格书


其他尺寸的屏幕相关规格书均可以在这里下载 点我

下面提供了 LCD 相关时序的截图


上面一张图是时序中的参数表,下面的图是时序图

从时序图中看出,这块屏幕可以不用设置前后沿,可以只设置消影(blanking)时间,通过实际的程序证明,两种方式都是可以的


生成屏幕时钟

  • 这里需要用到高云半导体官方的IP核


】rPll

板载的晶振时钟为 27MHz ,但是我们的屏幕要求 33.3MHZ 的时钟,所以我们需要使用对应的ip核来生成相应的时钟

这里需要使用到 IP Core Generate ,位置在 Tools -> IP Core Generate


双击 rpll ,在弹出窗口 language 选择 Verilog ,CLKIN 为 27MHz ,CLKOUT 为 33.00MHz。


点击ok后提示是否需要添加到当前工程,此时应当选择确定

接着会出现一个例化的tmp文件,用来例化所设置的ip。比如下图中例子


】屏幕驱动代码

  • 首先新建一个额外的verilog文件来保存下面要编写的代码

[] 端口定义

首先需要先定义出驱动屏幕所需要的端口

module VGAMod
(
    input                   CLK,
    input                   nRST,

    input                   PixelClk,

    output                  LCD_DE,
    output                  LCD_HSYNC,
    output                  LCD_VSYNC,

    output          [4:0]   LCD_B,
    output          [5:0]   LCD_G,
    output          [4:0]   LCD_R
);

本例程使用RGB565作为驱动方式;

[] 时序常量

接着定义出时序图上所要求的常量


localparam      V_BackPorch = 16'd6; //0 or 45
localparam      V_Pluse 	= 16'd5; 
localparam      HightPixel  = 16'd480;
localparam      V_FrontPorch= 16'd62; //45 or 0

localparam      H_BackPorch = 16'd182; 	
localparam      H_Pluse 	= 16'd1; 
localparam      WidthPixel  = 16'd800;
localparam      H_FrontPorch= 16'd210;

localparam      PixelForHS  =   WidthPixel + H_BackPorch + H_FrontPorch;  	
localparam      LineForVS   =   HightPixel + V_BackPorch + V_FrontPorch;

首先是设置时序相关的参数:前沿、后沿、有效像素

关于显示前沿、后沿,前面也说了,可以合并为一个消影时间,就是可以把其中一个设置为0,另一个设置为消影时间。反正前后沿的时间加起来符合表中的时间要求就可以

[] 定义变量

  • 定义一些变量能够容易编写程序

reg [15:0] LineCount;
reg [15:0] PixelCount;

reg	[9:0]  Data_R;
reg	[9:0]  Data_G;
reg	[9:0]  Data_B;

[] 同步信号

这段代码产生同步信号,需要注意的是,这块屏幕的同步信号是负极性使能

    always @(  posedge PixelClk or negedge nRST  )begin
        if( !nRST ) begin
            LineCount       <=  16'b0;    
            PixelCount      <=  16'b0;
            end
        else if(  PixelCount  ==  PixelForHS ) begin
            PixelCount      <=  16'b0;
            LineCount       <=  LineCount + 1'b1;
            end
        else if(  LineCount  == LineForVS  ) begin
            LineCount       <=  16'b0;
            PixelCount      <=  16'b0;
            end
        else
            PixelCount      <=  PixelCount + 1'b1;
    end

   always @(  posedge PixelClk or negedge nRST  )begin
        if( !nRST ) begin
    		Data_R <= 9'b0;
    		Data_G <= 9'b0;
    		Data_B <= 9'b0;
            end
        else begin
    		end
    end

//注意这里HSYNC和VSYNC负极性
assign  LCD_HSYNC = (( PixelCount >= H_Pluse)&&( PixelCount <= (PixelForHS-H_FrontPorch))) ? 1'b0 : 1'b1;
assign  LCD_VSYNC = ((( LineCount  >= V_Pluse )&&( LineCount  <= (LineForVS-0) )) ) ? 1'b0 : 1'b1;

[] 使能信号

这段代码设置 LCD 使能图像显示,这块屏幕需要控制一个管脚用作显示开关,实际这个信号就是传输图像有效的那 800*480 的数据时置 1

assign  LCD_DE = (  ( PixelCount >= H_BackPorch )&&
                    ( PixelCount <= PixelForHS-H_FrontPorch ) &&
                    ( LineCount >= V_BackPorch ) &&
                    ( LineCount <= LineForVS-V_FrontPorch-1 ))  ? 1'b1 : 1'b0;
                    //这里不减一,会抖动

[] 测试彩条

  • 这段代码用来产生 LCD 的测试数据,产生彩条显示
    localparam          Colorbar_width   =   WidthPixel / 16;

    assign  LCD_R     = ( PixelCount < ( H_BackPorch +  Colorbar_width * 0  )) ? 5'b00000 :
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 1  )) ? 5'b00001 : 
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 2  )) ? 5'b00010 :    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 3  )) ? 5'b00100 :    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 4  )) ? 5'b01000 :    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 5  )) ? 5'b10000 :  5'b00000;

    assign  LCD_G    =  ( PixelCount < ( H_BackPorch +  Colorbar_width * 6  )) ? 6'b000001: 
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 7  )) ? 6'b000010:    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 8  )) ? 6'b000100:    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 9  )) ? 6'b001000:    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 10 )) ? 6'b010000:    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 11 )) ? 6'b100000:  6'b000000;

    assign  LCD_B    =  ( PixelCount < ( H_BackPorch +  Colorbar_width * 12 )) ? 5'b00001 : 
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 13 )) ? 5'b00010 :    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 14 )) ? 5'b00100 :    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 15 )) ? 5'b01000 :    
                        ( PixelCount < ( H_BackPorch +  Colorbar_width * 16 )) ? 5'b10000 :  5'b00000;

当然在最后的驱动文件最后别忘记换行加上 endmodule

到这里驱动模块的编写已经完成了。


】在顶层模块中例化

  • 这里也是要新建verilog文件的
  • 新建文件之后直接把下面的内容复制进去保存即可
module TOP //设置顶层模块
(
    input			nRST,
    input           XTAL_IN,

    output			LCD_CLK,
    output			LCD_HYNC,
    output			LCD_SYNC,
    output			LCD_DEN,
    output	[4:0]	LCD_R,
    output	[5:0]	LCD_G,
    output	[4:0]	LCD_B

); // 列出需要的端口

    wire		CLK_SYS;	
    wire		CLK_PIX;

    //例化pll
    Gowin_rPLL chip_pll(
        .clkout(CLK_SYS), //output clkout     //200M
        .clkoutd(CLK_PIX), //output clkoutd   //33.00M
        .clkin(XTAL_IN)    //input clkin
    );	

    VGAMod	VGAMod_inst //例化vga驱动
    (
    	.CLK		(	CLK_SYS     ),
    	.nRST		(	nRST		),

    	.PixelClk	(	CLK_PIX		),
    	.LCD_DE		(	LCD_DEN	 	),
    	.LCD_HSYNC	(	LCD_HYNC 	),
    	.LCD_VSYNC	(	LCD_SYNC 	),

    	.LCD_B		(	LCD_B		),
    	.LCD_G		(	LCD_G		),
    	.LCD_R		(	LCD_R		)
    );

    assign		LCD_CLK		=	CLK_PIX;

endmodule

】综合、约束、布局布线

[] 综合

完成上面步骤后转到“Process”界面下,对编辑好的代码进行综合,即运行“Synthesize”

且下方结果栏不出现任何从报错,说明前面编辑的代码无误,如果有错,根据错误提示进行改正即可。


[] 约束

  • 此处仅管脚约束

对应的管脚约束如下表格所示;
感觉麻烦的话也可以直接复制准备好的文件(如下),将页面里的内容复制到工程目录里 .cst 文件中(如果没有.cst 文件那么自己新建一个物理管脚约束文件) 即可。

  • 直接把下面的内容复制进".cst"文件即可
  • Copt the following content into the ".cst" file
IO_LOC "LCD_B[4]" 41;
IO_PORT "LCD_B[4]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_B[3]" 42;
IO_PORT "LCD_B[3]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_B[2]" 51;
IO_PORT "LCD_B[2]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_B[1]" 53;
IO_PORT "LCD_B[1]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_B[0]" 54;
IO_PORT "LCD_B[0]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_G[5]" 55;
IO_PORT "LCD_G[5]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_G[4]" 56;
IO_PORT "LCD_G[4]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_G[3]" 57;
IO_PORT "LCD_G[3]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_G[2]" 68;
IO_PORT "LCD_G[2]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_G[1]" 69;
IO_PORT "LCD_G[1]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_G[0]" 70;
IO_PORT "LCD_G[0]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_R[4]" 71;
IO_PORT "LCD_R[4]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_R[3]" 72;
IO_PORT "LCD_R[3]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_R[2]" 73;
IO_PORT "LCD_R[2]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_R[1]" 74;
IO_PORT "LCD_R[1]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_R[0]" 75;
IO_PORT "LCD_R[0]" IO_TYPE=LVCMOS33;
IO_LOC "LCD_DEN" 33;
IO_PORT "LCD_DEN" IO_TYPE=LVCMOS33;
IO_LOC "LCD_SYNC" 34;
IO_PORT "LCD_SYNC" IO_TYPE=LVCMOS33;
IO_LOC "LCD_HYNC" 40;
IO_PORT "LCD_HYNC" IO_TYPE=LVCMOS33;
IO_LOC "LCD_CLK" 35;
IO_PORT "LCD_CLK" IO_TYPE=LVCMOS33;
IO_LOC "XTAL_IN" 52;
IO_PORT "XTAL_IN" IO_TYPE=LVCMOS33 PULL_MODE=NONE;
IO_LOC "nRST" 4;

PORTI/OPINPORTI/OPIN
LCD_B[4]output41LCD_B[3]output42
LCD_B[2]output51LCD_B[1]output53
LCD_B[0]output54LCD_G[5]output55
LCD_G[4]output56LCD_G[3]output57
LCD_G[2]output68LCD_G[1]output69
LCD_G[0]output70LCD_R[4]output71
LCD_R[3]output72LCD_R[2]output73
LCD_R[1]output74LCD_R[0]output75
LCD_DENoutput33LCD_SYNCoutput34
LCD_HYNCoutput40LCD_CLKoutput35
XTAL_INinput52nRSTinput4

[] 布局布线

管脚约束之后需要在设置里面开启引脚复用才能完成布局布线。

具体位置在 软件顶部菜单栏 Project -> Configuration -> Place&Route -> Dual-Purpose Pin


设置完上面的之后。
就可以进行布局布线(Place&Route)了。
结束后就可以给开发板验证代码内容了。

】烧录

布局布线结束后生成比特流,就可以烧录开发板了。


】结束

上面差不多叙述了所有代码。
整个工程可以在参考 这里 ,对应在lcd_led目录下。


】PicoRV在9k上运行的示例


相关环境

  • Python
  • Gowin IDE


】相关步骤

[] 烧录Bitstream
  • 打开 TangNano-9K-example\picotiny\project 目录下的 picotiny.gprj 文件
  • 在顶部菜单栏 Project->Configuration->Place&Route->Dual-Purpose Pin 里面勾选 Use MSPI as regular IO
  • 在 IDE 的 Process 窗口右键 Place&Route 选择 Clean&Rerun All
  • 将生成的文件下载到 Nano 9K 的 Embedded Flash

完成上面步骤后我们已经成功将PicoRV固化到FPGA里面了


[] 烧录例程文件
  • 在 TangNano-9K-example\picotiny 目录下执行
python .\sw\pico-programmer.py .\example-fw-flash.v COM13

上面命令行中最后的 COM13 指的是开发板在系统中的串口编号,
比如在系统中显示为COM14的话就需要将它改成对应的COM14。

成功执行上面命令后会出现 - Waiting for reset - 的计时,
这时候按下开发板的S1按键就可以完成烧录。
附带完成的烧录log:

\TangNano-9K-example\picotiny> python .\sw\pico-programmer.py .\example-fw-flash.v COM13
Read program with 11760 bytes
  - Waiting for reset -
    ...
Total sectors 3
Total pages 46
Flashing 1 / 3
Flashing 2 / 3
Flashing 3 / 3

Flashing completed

然后就可以使用在串口工具来执行我们的代码了,当然也可以用HDMI来显示代码界面

FPGA驱动1.14 SPI屏幕工程

HDMI 示例:

参考 PicoRV 在9K上运行的示例

litex 在 9K 支持:

https://github.com/litex-hub/litex-boards


Lushay Labs 有意做以太网、图形卡或者双核处理器的 FPGA 开发教程计划,有意者可以阅读本文末尾处他们提供的 部分教程 来了解他们。


六、Tang Nano 20K

Github: https://github.com/sipeed/TangNano-20K-example



七、哔哩哔哩视频

感谢 ZQ坐看云起时 友情制作

TANG NANO 9K 开发板应用《1: 云源IDE安装》
TANG NANO 9K 开发板应用《2: 云源软件基本使用》
TANG NANO 9K 开发板应用《3: 新建FPGA工程》
TANG NANO 9K 开发板应用《4:时钟分频器使用之IP调用法》
TANG NANO 9K 开发板应用《5:时钟分频器使用之直接例化法》
TANG NANO 9K 开发板应用《6:38译码器设计与测试》
TANG NANO 9K 开发板应用《7:高云逻辑分析仪之配置篇》
TANG NANO 9K 开发板应用《8:高云逻辑分析仪之使用篇》
TANG NANO 9K 开发板应用《9:存储器BSRAM使用方法介绍》
TANG NANO 9K 开发板应用《10:高云逻辑分析仪调试BSRAM》
TANG NANO 9K 开发板应用《11:BSRAM设置初始化数据》
TANG NANO 9K 开发板应用《12:基于BSRAM的pROM使用》
TANG NANO 9K 开发板应用《13:输入时钟CLK信号捕获技巧》
TANG NANO 9K 开发板应用《14:基于ST7789芯片1.14寸LCD屏幕驱动》
TANG NANO 9K 开发板应用《15:基于ST7789芯片LCD寄存器配置和显示》
TANG NANO 9K 开发板应用《16: 1.14寸LCD图片显示实验》
TANG NANO 9K 开发板应用《17: 1.14寸LCD图片滚动实验》
TANG NANO 9K 开发板应用《18:手搓简易标准SPI驱动及仿真》
TANG NANO 9K 开发板应用《19:SPI读flash P25Q32HS/W25Q32 芯片ID实验》
TANG NANO 9K 开发板应用《20:SPI读写/擦除P25Q32HS/W25Q32实验》
TANG NANO 9K 开发板应用《21:串口发送模块的实现》
TANG NANO 9K 开发板应用《22:串口接收模块的实现》
TANG NANO 9K 开发板应用《23:环形队列(FIFO)的实现》
TANG NANO 9K 开发板应用《24:串口转SPI操作flash》
TANG NANO 9K 开发板应用《25:串口转SPI文件传输上位机》
TANG NANO 9K 开发板应用《26:PWM》
iverilog + gtkwave 开源仿真工具《1:软件安装篇》

iverilog + gtkwave 开源仿真工具《2:计数器仿真实验》

八、其他上手说明

Tang Nano 9K 合作代理教程(英文):


Lushay Labs

  1. Installation & Getting Started
  2. Debugging & UART
  3. OLED 101
  4. Creating a Text Engine
  5. Data Conversion & Visualization
  6. Reading from the External Flash
  7. Generating Random Numbers
  8. Sharing Resources
  9. I2C, ADC and Micro Procedures
  10. Our First CPU