walker2048

  • 2025-02-21
  • 回复了主题帖: 「当幸狐来敲门」适配Alpine Linux上篇--适配了Alpine Linux有什么好处

    ospider 发表于 2025-2-20 16:11 请问这个是怎么适配的 alpine 的, 可以出个教程吗 https://bbs.eeworld.com.cn/thread-1259967-1-1.html 看这篇文章

  • 2025-02-18
  • 回复了主题帖: DC12V电瓶输出电路保护电路

    去搜搜TI的电子保险丝方案,里面也有功能框图可以学习下,其实就是NMOS加一个电流检测电路,电流过大就关闭回路。

  • 加入了学习《直播回放: Nexperia 理想二极管与负载开关,保障物联网的稳健高效运行》,观看 Nexperia 理想二极管与负载开关,保障物联网的稳健高效运行

  • 2025-02-16
  • 发表了主题帖: 「Tang Primer 25K 测评」4、使用FPGA驱动WS2812

    其实网络上有很多FPGA驱动WS2812的代码,这里,我们使用蹄佬的代码(抄代码和看懂代码也是一种学习),直接驱动WS2812。 蹄佬项目链接https://github.com/BigPig-Bro/Gowin/tree/master/TangPrimer_20k   驱动WS2812的要点: 驱动WS2812的关键是按它的通信时序(见下图),把对应的数据输出到灯的DI引脚上。然后它的   这里可以看到几个关键的内容,0码和1码的高低电平时间,以及RES信号的持续时间。 这部分在代码里可以设置好 parameter DELAY_1_HIGH = (CLK_FRE / 1_000_000 * 0.70 ) - 1; //≈580ns±150ns 1 高电平时间 parameter DELAY_1_LOW = (CLK_FRE / 1_000_000 * 0.40 ) - 1; //≈280ns±150ns 1 低电平时间 parameter DELAY_0_HIGH = (CLK_FRE / 1_000_000 * 0.40 ) - 1; //≈280ns±150ns 0 高电平时间 parameter DELAY_0_LOW = (CLK_FRE / 1_000_000 * 0.70 ) - 1; //≈580ns±150ns 0 低电平时间 parameter DELAY_RESET = (CLK_FRE / 20 ) - 1; //100ms 复位时间 >50us 然后这里可以设置LED灯的数量和CLK输入信号的频率,咱们板子上用的E2信号引脚上输入的50MHz频率,所以设置好就行。 parameter WS2812_NUM = 16 - 1 ; // WS2812的LED数量(1从0开始) parameter WS2812_WIDTH = 24 ; // WS2812的数据位宽 parameter CLK_FRE = 50_000_000 ; // CLK的频率(mHZ) 然后后面的代码就是状态机的详细实现了,这一行就是修改颜色的代码,DELAY_RESET参数,不但是修改颜色的延时设置,也一样是每次终止数据输出的延时时间。 WS2812_data <= {WS2812_data[22:0],WS2812_data[23]};//颜色移位循环显示 完整代码如下: //功能描述:驱动 WS2812_NUM 个的WS2812一起渐变亮 module top ( input clk, //输入 时钟源 output reg WS2812_DI //输出到WS2812的接口 ); parameter WS2812_NUM = 16 - 1 ; // WS2812的LED数量(1从0开始) parameter WS2812_WIDTH = 24 ; // WS2812的数据位宽 parameter CLK_FRE = 50_000_000 ; // CLK的频率(mHZ) parameter DELAY_1_HIGH = (CLK_FRE / 1_000_000 * 0.70 ) - 1; //≈580ns±150ns 1 高电平时间 parameter DELAY_1_LOW = (CLK_FRE / 1_000_000 * 0.40 ) - 1; //≈280ns±150ns 1 低电平时间 parameter DELAY_0_HIGH = (CLK_FRE / 1_000_000 * 0.40 ) - 1; //≈280ns±150ns 0 高电平时间 parameter DELAY_0_LOW = (CLK_FRE / 1_000_000 * 0.70 ) - 1; //≈580ns±150ns 0 低电平时间 parameter DELAY_RESET = (CLK_FRE / 20 ) - 1; //100ms 复位时间 >50us parameter RESET = 0; //状态机声明 parameter DATA_SEND = 1; parameter BIT_SEND_HIGH = 2; parameter BIT_SEND_LOW = 3; reg [ 1:0] state = 0/* synthesis preserve */; //主状态机控制 reg [ 4:0] bit_send = 0; //数据数量发送控制 reg [ 4:0] data_send = 0; //数据位发送控制 reg [31:0] clk_delay = 0; //延时控制 reg [23:0] WS2812_data = 24'd1; // WS2812的颜色数据(初始淡蓝) always@(posedge clk) case (state) RESET:begin WS2812_DI <= 0; if (clk_delay < DELAY_RESET) clk_delay <= clk_delay + 1; else begin clk_delay <= 0; WS2812_data <= {WS2812_data[22:0],WS2812_data[23]};//颜色移位循环显示 state <= DATA_SEND; end end DATA_SEND: if (data_send == WS2812_NUM && bit_send == WS2812_WIDTH)begin data_send <= 0; bit_send <= 0; state <= RESET; end else if (bit_send < WS2812_WIDTH) begin state <= BIT_SEND_HIGH; end else begin// if (bit_send == WS2812_WIDTH) data_send <= data_send + 1; bit_send <= 0; state <= BIT_SEND_HIGH; end BIT_SEND_HIGH:begin WS2812_DI <= 1; if (WS2812_data[bit_send]) if (clk_delay < DELAY_1_HIGH) clk_delay <= clk_delay + 1; else begin clk_delay <= 0; state <= BIT_SEND_LOW; end else if (clk_delay < DELAY_0_HIGH) clk_delay <= clk_delay + 1; else begin clk_delay <= 0; state <= BIT_SEND_LOW; end end BIT_SEND_LOW:begin WS2812_DI <= 0; if (WS2812_data[bit_send]) if (clk_delay < DELAY_1_LOW) clk_delay <= clk_delay + 1; else begin clk_delay <= 0; bit_send <= bit_send + 1; state <= DATA_SEND; end else if (clk_delay < DELAY_0_LOW) clk_delay <= clk_delay + 1; else begin clk_delay <= 0; bit_send <= bit_send + 1; state <= DATA_SEND; end end endcase endmodule 引脚约束修改成以下的内容 IO_LOC "clk" E2; IO_LOC "WS2812_DI" L5; IO_PORT "clk" IO_TYPE=LVCMOS33; IO_PORT "WS2812_DI" IO_TYPE=LVCMOS33; 然后综合布线,烧录到板子上,就可以看到效果啦。 实际上,我们在设计一个WS2812外设时,应该考虑更复杂的东西,例如输入缓冲区,可以设置LED的数量(作为参数),现在就先按这个提交了,以后再考虑自己实现一个复杂一些的版本。   演示视频如下 [localvideo]e46943c7410acf98f9bf734d0bd28008[/localvideo]  

  • 2025-02-15
  • 发表了主题帖: 「Tang Primer 25K 测评」3、点亮数码管

    数码管驱动原理 数码管的驱动原理如下图,实际上就是多个LED封装在数码管里,一般分共阴和共阳两种。现在很多的小家电都使用了数码管,用来显示数字、时间等等东西。 大家平时在做MCU应用的时候,用的比较少,现在可以顺便学一下如何驱动数码管。   sipeed有提供各个PMOD扩展板的原理图,数码管的原理图如下图: 这里可以看到其中一个数码管是通过PIN06引脚供电的,按3.3V和限流电阻3.3K计算,每个灯的电流其实都很小,如果我们想修改板子的限流电阻,必须注意总电流不能超过IO的输出能力。(见规格书的引脚电平部分说明)     我们在使用MCU或者FPGA的时候,很多小白都没注意引脚具体的驱动能力是多大。大部分时候,我们应该认真查看规格书,不要超出引脚的输出能力。如果需要的电流比较大,一定要把引脚当做信号控制引脚,然后控制外部的三极管或者MOS等等设备去控制。     代码分析 我们打开官方的pmod_digitalTube-2bit目录,可以看到项目的目录结构如下图所示。 我们先看一下数码管显示功能: module driver_DigitalTube #( parameter P_CNT = 'd300_000 //Scanning Period for Dynamic Drive of the Seven-Segment Display )( input i_clk , input i_rst , input [3:0] i_add , output [6:0] o_digitalTube ,//ABCDEFG output o_sel ); 这段代码定义了以下内容 扫描周期:通过 P_CNT 参数定义的计数值,控制扫描操作的周期。模块会在时钟信号的控制下,定期切换到不同的数码管位,从而实现动态扫描。同时该扫描周期也是处理按键加法的计算周期。 3个输入信号(clk,rst,add按钮数组):其中clk信号是从顶层模块输入的50MHz时钟,rst是复位按钮,add按钮数组就是PMOD按键模组上的四个按键。 2个输出信号(o_digitalTube,o_sel):其中o_digitalTube就是对应点钟的笔画,然后o_sel是选择哪个数码管字的控制信号。 /***************parameter*************/ localparam P_0 = 7'b0000001 ; localparam P_1 = 7'b1111001 ; localparam P_2 = 7'b0010010 ; localparam P_3 = 7'b0110000 ; localparam P_4 = 7'b1101000 ; localparam P_5 = 7'b0100100 ; localparam P_6 = 7'b0000100 ; localparam P_7 = 7'b1110001 ; localparam P_8 = 7'b0000000 ; localparam P_9 = 7'b0100000 ; localparam P_X = 7'b1111111 ; //Truth Table for Displaying 0-9 on the Seven-Segment Display and for Blank Display 这里定义了数码管显示的真值表,7位全输出1则显示空,对应位输出0则点亮对应字段,例如0就是0000001,除了中间的,其他都点亮。 如果是共阴的数码管,则应该反过来。 /***************reg*******************/ reg [23:0] r_cnt = 0; reg [3:0] r_add = 0; reg [3:0] r_add1d = 0; reg [3:0] r_addBuffer = 0; reg [4:0] r_cntOnes = 0; reg [4:0] r_cntTens = 0; reg [6:0] r_digitalTubeOnes = 0; reg [6:0] r_digitalTubeTens = 0; reg ro_sel = 0; /***************assign****************/ assign o_sel = ro_sel ; assign w_posedge0 = r_add[0] & !r_add1d[0]; assign w_posedge1 = r_add[1] & !r_add1d[1]; assign w_posedge2 = r_add[2] & !r_add1d[2]; assign w_posedge3 = r_add[3] & !r_add1d[3]; //Detecting Rising Edges from 4 Button Modules assign o_digitalTube = ro_sel ? r_digitalTubeTens : r_digitalTubeOnes; 然后接下来的代码,是定义了几个寄存器,以及一些赋值操作。如果是将寄存器赋值给输出信号,那就是寄存器的值会直接被输出信号输出,其他的则是使用逻辑门直接计算对应的值。   //——————<r_cnt>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_cnt <= 0; else if(r_cnt == P_CNT) r_cnt <= 0; else r_cnt <= r_cnt+1; end //——————ro_sel—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) ro_sel <= 0 ; else if(r_cnt == P_CNT) ro_sel <= ~ro_sel ; else ro_sel <= ro_sel ; end 这里是计数器自增和数码管扫描部分,每计数300000次切换个位的数码管和十位的数码管显示。 //——————<r_add>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_add <= 0; else r_add <= i_add; end //——————<r_add1d>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_add1d <= 0; else r_add1d <= r_add; end //——————<r_addBuffer[0]>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_addBuffer[0] <= 0 ; else if(r_cnt == P_CNT) r_addBuffer[0] <= 0 ; else if(w_posedge0) r_addBuffer[0] <= 1 ; else r_addBuffer[0] <= r_addBuffer[0]; end //——————<r_addBuffer[1]>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_addBuffer[1] <= 0 ; else if(r_cnt == P_CNT) r_addBuffer[1] <= 0 ; else if(w_posedge1) r_addBuffer[1] <= 1 ; else r_addBuffer[1] <= r_addBuffer[1]; end //——————<r_addBuffer[2]>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_addBuffer[2] <= 0 ; else if(r_cnt == P_CNT) r_addBuffer[2] <= 0 ; else if(w_posedge2) r_addBuffer[2] <= 1 ; else r_addBuffer[2] <= r_addBuffer[2]; end //——————<r_addBuffer[3]>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_addBuffer[3] <= 0 ; else if(r_cnt == P_CNT) r_addBuffer[3] <= 0 ; else if(w_posedge3) r_addBuffer[3] <= 1 ; else r_addBuffer[3] <= r_addBuffer[3]; end //——————<r_cntOnes>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_cntOnes <= 0; else if((r_cnt == P_CNT) & ((r_cntOnes + r_addBuffer) > 5'd9)) r_cntOnes <= r_cntOnes + r_addBuffer - 5'd10; else if(r_cnt == P_CNT) r_cntOnes <= r_cntOnes + r_addBuffer; else r_cntOnes <= r_cntOnes; end //——————<r_cntTens>—————————————// always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_cntTens <= 0; else if((r_cnt == P_CNT) & ((r_cntOnes + r_addBuffer) > 5'd9) & (r_cntTens == 5'd9)) r_cntTens <= 0 ; else if((r_cnt == P_CNT) & ((r_cntOnes + r_addBuffer) > 5'd9)) r_cntTens <= r_cntTens + 1; else r_cntTens <= r_cntTens; end 这里的一大串代码,则是负责计算个位和十位的加法,这里的功能是分别是4个按钮对应加1,加2,加4,加8功能。 always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_digitalTubeOnes <= 7'b0; else if(r_cnt == P_CNT) case(r_cntOnes[3:0]) 4'd0 : r_digitalTubeOnes <= P_0 ; 4'd1 : r_digitalTubeOnes <= P_1 ; 4'd2 : r_digitalTubeOnes <= P_2 ; 4'd3 : r_digitalTubeOnes <= P_3 ; 4'd4 : r_digitalTubeOnes <= P_4 ; 4'd5 : r_digitalTubeOnes <= P_5 ; 4'd6 : r_digitalTubeOnes <= P_6 ; 4'd7 : r_digitalTubeOnes <= P_7 ; 4'd8 : r_digitalTubeOnes <= P_8 ; 4'd9 : r_digitalTubeOnes <= P_9 ; default : r_digitalTubeOnes <= r_digitalTubeOnes ; endcase else r_digitalTubeOnes <= r_digitalTubeOnes; end //——————<r_digitalTubeTens>—————————————/ always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_digitalTubeTens <= 7'b0; else if(r_cnt == P_CNT) case(r_cntTens[3:0]) 4'd0 : r_digitalTubeTens <= P_X ; 4'd1 : r_digitalTubeTens <= P_1 ; 4'd2 : r_digitalTubeTens <= P_2 ; 4'd3 : r_digitalTubeTens <= P_3 ; 4'd4 : r_digitalTubeTens <= P_4 ; 4'd5 : r_digitalTubeTens <= P_5 ; 4'd6 : r_digitalTubeTens <= P_6 ; 4'd7 : r_digitalTubeTens <= P_7 ; 4'd8 : r_digitalTubeTens <= P_8 ; 4'd9 : r_digitalTubeTens <= P_9 ; default : r_digitalTubeTens <= r_digitalTubeTens ; endcase else r_digitalTubeTens <= r_digitalTubeTens; end 最后这一串代码,分别在扫描时,定期更新个位和十位的寄存器数值。   总结: 我个人对FPGA的开发并不是非常的熟悉,但是通过运行和分析官方提供的demo代码,也让我对FPGA的使用有了更多的了解,很高兴有机会使用这个开发板。     运行的效果如下: [localvideo]d46a89282d9a657546f499c5d6db6232[/localvideo]  

  • 回复了主题帖: 【Tang Primer 25K 测评】3、高云IDE使用及PMOD-LEDx8模块点灯实验

    秦天qintian0303 发表于 2025-2-1 08:23 这个直接有例程吗?看着好像汇编的写法 不是汇编,FPGA只能用Verilog或者VHDL去写。FPGA是纯硬件实现的,不像单片机软件的运行逻辑

  • 发表了主题帖: 「Tang Primer 25K 测评」2、点亮LCD屏幕

    官方仓库是有一个PMOD_LCD目录,但是我们手上是没有这个硬件的,可以使用淘宝上常见的st7735模块进行尝试。 通过搜索,我们可以发现官方使用的屏幕模块如下图:   简单说明一下时序 不管是FPGA还是MCU,驱动屏幕的关键是满足对应屏幕的通信时序,如下图是st7735s规格书里,关于4线spi模式的时序图   如何解读这个时序图呢?在我们使用FPGA驱动屏幕时,有几个重要引脚: 信号和引脚 CSX(芯片选择,通常标注为CS):用于选择设备的信号。当CSX为低电平(CSX = 0)时,设备被选中,可以开始通信。当CSX为高电平(CSX = 1)时,设备被取消选择,SDA线上的任何数据都会被忽略。 D/CX(数据/命令,通常标注为DC):该信号指示SDA线上的数据是命令(D/CX = 0)还是参数/数据(D/CX = 1)。 SCL(串行时钟):这是同步数据传输的时钟信号。数据通常在时钟信号的上升沿被捕获。 SDA(串行数据):这是传输实际数据(命令或数据)的数据线。 RES(硬件复位):该信号用于强制复位st7735s芯片,然后接下来会重新初始化st7735。在规格书里也有使用RES引脚暂停通信的,这个一般用不上。 BLK(背光使能):使用该引脚控制背光。 这里,我们先不管BLK和RES引脚,重点关注通信过程,CS引脚为0时,开启设备通信,此时可以和屏显芯片(这里是st7735s)交互,通过DC引脚告诉屏显芯片,当前SDA的发过来的内容是命令或者参数(数据)。我们可以通过写入厂家提供的屏幕初始化指令序列,完成屏幕的初始化,然后再向屏幕写入相关显示数据,完成最后的屏幕显示。 向屏幕写入数据的图示如下     一般情况下,我们会使用RGB565格式发送屏幕显示数据,选择这个格式,是因为不管是图像质量,还是内存占用方面,这个格式都是目前MCU和PFGA显示图像的最优解(效果能接受,占用资源也能接受)。   IDE里选择设备 在打开工程时,因为我们github的子仓没有下载,会提示需要选择正确的设备,按下图的顺序选择即可。   亮屏代码 `timescale 1ps/1ps module lcd114_test#( parameter clk_frequency = 50_000_000 )( input clk, // 50M input rst, output ser_tx, input ser_rx, output lcd_resetn, output lcd_clk, output lcd_cs, output lcd_dc, output lcd_blk, output lcd_data ); assign resetn = !rst; localparam MAX_CMDS = 87; wire [8:0] init_cmd[MAX_CMDS:0]; assign init_cmd[ 0] = 9'h011; assign init_cmd[ 1] = 9'h0B1; assign init_cmd[ 2] = 9'h105; assign init_cmd[ 3] = 9'h13C; assign init_cmd[ 4] = 9'h13C; assign init_cmd[ 5] = 9'h0B2; assign init_cmd[ 6] = 9'h105; assign init_cmd[ 7] = 9'h13C; assign init_cmd[ 8] = 9'h13C; assign init_cmd[ 9] = 9'h0B3; assign init_cmd[10] = 9'h105; assign init_cmd[11] = 9'h13C; assign init_cmd[12] = 9'h13C; assign init_cmd[13] = 9'h105; assign init_cmd[14] = 9'h13C; assign init_cmd[15] = 9'h13C; assign init_cmd[16] = 9'h0B4; assign init_cmd[17] = 9'h103; assign init_cmd[18] = 9'h0C0; assign init_cmd[19] = 9'h1AB; assign init_cmd[20] = 9'h10B; assign init_cmd[21] = 9'h104; assign init_cmd[22] = 9'h0C1; assign init_cmd[23] = 9'h1C5; assign init_cmd[24] = 9'h0C2; assign init_cmd[25] = 9'h10D; assign init_cmd[26] = 9'h100; assign init_cmd[27] = 9'h0C3; assign init_cmd[28] = 9'h18D; assign init_cmd[29] = 9'h16A; assign init_cmd[30] = 9'h0C4; assign init_cmd[31] = 9'h18D; assign init_cmd[32] = 9'h1EE; assign init_cmd[33] = 9'h0C5; assign init_cmd[34] = 9'h10F; assign init_cmd[35] = 9'h0E0; assign init_cmd[36] = 9'h107; assign init_cmd[37] = 9'h10E; assign init_cmd[38] = 9'h108; assign init_cmd[39] = 9'h107; assign init_cmd[40] = 9'h110; assign init_cmd[41] = 9'h107; assign init_cmd[42] = 9'h102; assign init_cmd[43] = 9'h107; assign init_cmd[44] = 9'h109; assign init_cmd[45] = 9'h10F; assign init_cmd[46] = 9'h125; assign init_cmd[47] = 9'h136; assign init_cmd[48] = 9'h100; assign init_cmd[49] = 9'h108; assign init_cmd[50] = 9'h104; assign init_cmd[51] = 9'h110; assign init_cmd[52] = 9'h0E1; assign init_cmd[53] = 9'h10A; assign init_cmd[54] = 9'h10D; assign init_cmd[55] = 9'h108; assign init_cmd[56] = 9'h107; assign init_cmd[57] = 9'h10F; assign init_cmd[58] = 9'h107; assign init_cmd[59] = 9'h102; assign init_cmd[60] = 9'h107; assign init_cmd[61] = 9'h109; assign init_cmd[62] = 9'h10F; assign init_cmd[63] = 9'h125; assign init_cmd[64] = 9'h135; assign init_cmd[65] = 9'h100; assign init_cmd[66] = 9'h109; assign init_cmd[67] = 9'h104; assign init_cmd[68] = 9'h110; assign init_cmd[69] = 9'h0FC; assign init_cmd[70] = 9'h180; assign init_cmd[71] = 9'h03A; assign init_cmd[72] = 9'h105; assign init_cmd[73] = 9'h036; assign init_cmd[74] = 9'h108; // assign init_cmd[74] = 9'h1C8; // assign init_cmd[74] = 9'h178; // assign init_cmd[74] = 9'h1A8; assign init_cmd[75] = 9'h020; assign init_cmd[76] = 9'h029; assign init_cmd[77] = 9'h02A; assign init_cmd[78] = 9'h100; assign init_cmd[79] = 9'h11A; assign init_cmd[80] = 9'h100; assign init_cmd[81] = 9'h169; assign init_cmd[82] = 9'h02B; assign init_cmd[83] = 9'h100; assign init_cmd[84] = 9'h101; assign init_cmd[85] = 9'h100; assign init_cmd[86] = 9'h1A0; assign init_cmd[87] = 9'h02C; localparam INIT_RESET = 4'b0000; // delay 100ms while reset localparam INIT_PREPARE = 4'b0001; // delay 200ms after reset localparam INIT_WAKEUP = 4'b0010; // write cmd 0x11 MIPI_DCS_EXIT_SLEEP_MODE localparam INIT_SNOOZE = 4'b0011; // delay 120ms after wakeup localparam INIT_WORKING = 4'b0100; // write command & data localparam INIT_DONE = 4'b0101; // all done `ifdef MODELTECH localparam CNT_1MS = clk_frequency / 1000; localparam CNT_100MS = 100 * CNT_1MS ; localparam CNT_120MS = 120 * CNT_1MS; localparam CNT_200MS = 200 * CNT_1MS; `else // speedup for simulation localparam CNT_100MS = 32'd27; localparam CNT_120MS = 32'd32; localparam CNT_200MS = 32'd54; `endif reg [ 3:0] init_state; reg [ 6:0] cmd_index; reg [31:0] clk_cnt; reg [ 4:0] bit_loop; reg [15:0] pixel_cnt; reg lcd_cs_r; reg lcd_dc_r; reg lcd_reset_r; reg lcd_blk_r; reg [7:0] spi_data; assign lcd_resetn = lcd_reset_r; assign lcd_clk = clk; assign lcd_blk = lcd_blk_r; assign lcd_cs = lcd_cs_r; assign lcd_dc = lcd_dc_r; assign lcd_data = spi_data[7]; // MSB // gen color bar wire [15:0] pixel = (pixel_cnt >= 80*55) ? 16'hF800 : (pixel_cnt >= 80*110) ? 16'h07E0 : 16'h001F; always@(posedge clk or negedge resetn) begin if (~resetn) begin clk_cnt <= 0; cmd_index <= 0; init_state <= INIT_RESET; lcd_cs_r <= 1; lcd_dc_r <= 1; lcd_reset_r <= 0; spi_data <= 8'hFF; bit_loop <= 0; pixel_cnt <= 0; lcd_blk_r <= 0; end else begin lcd_blk_r <= 1; case (init_state) INIT_RESET : begin if (clk_cnt == CNT_100MS) begin clk_cnt <= 0; init_state <= INIT_PREPARE; lcd_reset_r <= 1; end else begin clk_cnt <= clk_cnt + 1; end end INIT_PREPARE : begin if (clk_cnt == CNT_200MS) begin clk_cnt <= 0; init_state <= INIT_WAKEUP; end else begin clk_cnt <= clk_cnt + 1; end end INIT_WAKEUP : begin if (bit_loop == 0) begin // start lcd_cs_r <= 0; lcd_dc_r <= 0; spi_data <= 8'h11; // exit sleep bit_loop <= bit_loop + 1; end else if (bit_loop == 8) begin // end lcd_cs_r <= 1; lcd_dc_r <= 1; bit_loop <= 0; init_state <= INIT_SNOOZE; end else begin // loop spi_data <= { spi_data[6:0], 1'b1 }; bit_loop <= bit_loop + 1; end end INIT_SNOOZE : begin if (clk_cnt == CNT_120MS) begin clk_cnt <= 0; init_state <= INIT_WORKING; end else begin clk_cnt <= clk_cnt + 1; end end INIT_WORKING : begin if (cmd_index == MAX_CMDS + 1) begin init_state <= INIT_DONE; end else begin if (bit_loop == 0) begin // start lcd_cs_r <= 0; lcd_dc_r <= init_cmd[cmd_index][8]; spi_data <= init_cmd[cmd_index][7:0]; bit_loop <= bit_loop + 1; end else if (bit_loop == 8) begin // end lcd_cs_r <= 1; lcd_dc_r <= 1; bit_loop <= 0; cmd_index <= cmd_index + 1; // next command end else begin // loop spi_data <= { spi_data[6:0], 1'b1 }; bit_loop <= bit_loop + 1; end end end INIT_DONE : begin if (pixel_cnt == 160*80) begin ; // stop end else begin if (bit_loop == 0) begin // start lcd_cs_r <= 0; lcd_dc_r <= 1; // spi_data <= 8'hF8; // RED spi_data <= pixel[15:8]; bit_loop <= bit_loop + 1; end else if (bit_loop == 8) begin // next byte // spi_data <= 8'h00; // RED spi_data <= pixel[7:0]; bit_loop <= bit_loop + 1; end else if (bit_loop == 16) begin // end lcd_cs_r <= 1; lcd_dc_r <= 1; bit_loop <= 0; pixel_cnt <= pixel_cnt + 1; // next pixel end else begin // loop spi_data <= { spi_data[6:0], 1'b1 }; bit_loop <= bit_loop + 1; end end end endcase end end endmodule   这里我们添加了lcd_blk引脚控制部分,在初始化的时候立即点亮LCD屏幕,如果不想折腾的,也可以把BLK引脚直接接到3.3V高电平上。 然后关闭了屏幕反显功能(根据不同屏幕厂商去设置) 然后我们需要修改约束控制引脚。 打开pmod_lcd.cst文件,将我们连接到LCD的引脚修改成对应的引脚,并添加blk引脚 IO_LOC "lcd_blk" K1; IO_LOC "lcd_cs" L2; IO_LOC "lcd_dc" J4; IO_LOC "lcd_resetn" G2; IO_LOC "lcd_data" K2; IO_LOC "lcd_clk" L4; IO_LOC "rst" H11; IO_LOC "clk" E2; //IO_LOC "lcd_cs" G5; //IO_LOC "lcd_data" G8; //IO_LOC "lcd_dc" H7; //IO_LOC "lcd_clk" J5; //IO_LOC "lcd_resetn" H5; //IO_LOC "rst" H11; //IO_LOC "clk" E2; IO_PORT "lcd_data" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3; IO_PORT "lcd_cs" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3; IO_PORT "lcd_dc" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3; IO_PORT "lcd_clk" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3; IO_PORT "lcd_blk" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3; IO_PORT "lcd_resetn" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3; IO_PORT "rst" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3; IO_PORT "clk" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3; 接线如图所示:     修改完毕后,就可以综合布线、烧录到板子上运行了。烧录时,默认是烧录到SRAM里,其实我们测试的时候,完全不需要烧录到外部Flash,直接按默认的就行。   烧录完毕后,就可以看到屏幕亮起来了。   不知道是分辨率不适配,还是反显没做好,本来应该显示红绿蓝三种颜色的,现在显示的不正确。今天就先折腾到这里,有空找一下分辨率一样的屏幕出来测试。

  • 回复了主题帖: MC9S12XS128读取DS18B20的文题

    上逻辑分析仪看看时序图吧

  • 2025-02-14
  • 发表了主题帖: 「Tang Primer 25K 测评」1、开箱、环境搭建和运行第一个点灯程序

    本帖最后由 walker2048 于 2025-2-15 16:58 编辑 前言 非常感谢社区和平台给我这个机会,参加“年终回炉:FPGA、AI、高性能MCU、书籍等65个测品邀你来~”,这个活动,也有幸拿到了Tang Primer 25K这款开发板,好好学习一下FPGA的相关知识。   Tang Primer 25K 是基于 GW5A-LV25MG121 所设计的一款极小封装的核心板(23x18mm),并配套全引脚引出(除MIPI高速脚外)的25K Dock底板。 该开发板使用的是国产FPGA芯片,即高云GW5A-25系列,这个系列的硬件资源说明如下图:   红框包围部分就是这个系列的硬件资源,有23040个LUT4,可以连接外部DDR3(1066速率)、mipi等设备。   可能大家对这些并不是很了解,我们使用高压官方的IP核,实现简单的I2C和UART,SPI这些设备,每个设备大概占用5、6百的LUT。然后常见的小型化MCU核心,例如PicoRV32大约需要2000个LUT,而arm的M1系列软核大概占用13000个LUT。由此可见,本次活动使用的开发板还是非常强劲的。   开箱环节   整个套件还是非常不错的,板子设计也比较漂亮,是我喜欢的类型。稍微有点遗憾的就是,东西发过来的时候,盒子里面并没有放泡沫或者其他缓冲的东西,在运送过程中,数码管其中一块被震掉了,还弄掉了一个LED。好在平时我自己也存着很多不同规格的元器件,焊接0402元器件也是手到擒来,自己修好了,数码管也用502粘好了(照片上也可以看到部分焊接痕迹)。其他板子目测是没有掉件的,希望没事儿吧。   环境搭建 很早以前就玩过高云的FPGA,对他们家的软件也是比较熟悉了,几年前玩的时候,当时还是用的sipeed服务器授权版本。现在简单了,直接用教育版就行了。 教育版能用的高云设备,基本上把淘宝上常见的产品给覆盖了,直接官网下载软件安装即可。 打开高云官网,点击开发者专区-> 云源软件,然后找到下图的链接,下载对应平台版本安装即可。   由于我是在Windows平台使用,就直接下载和安装win版本就好了。 安装完毕之后,就可以打开sipeed的Tang Primer 25K仓库,下载一些案例源码。例程源码可以在sipeed的wiki页面里找到,这里我们也可以看到有说明,必须安装1.9.9B4或者更新的IDE版本,我直接装最新版,没啥问题。     点灯程序 官方的点灯程序在pmod_led目录下,直接打开目录里的工程文件即可。 module top#( parameter pmod_num = 8, parameter pmod_io_num = pmod_num * 8 - 1, parameter frequency = 50_000_000, parameter count_ms = frequency / 1000 , parameter count_us = count_ms / 1000 ) ( input clk, input key, output led, output led_done, output led_ready, output [pmod_io_num:0] pmod_io ); reg led_output = 'd0 ; reg [7:0] led_8_state_reg = 'b1 ; reg [31:0] count='d0 ; always @(posedge clk) begin if (!key) begin count <= 'b0; led_output <= 'b0; led_8_state_reg <= 'b1; end else if(count <= frequency/5 -1) count <= count + 'b1; else begin count <= 'b0; led_output <= !led_output ; led_8_state_reg <= {led_8_state_reg[6:0], led_8_state_reg[7]} ; end end genvar pmod_sel; generate for ( pmod_sel = 0 ; pmod_sel < 8 ; pmod_sel = pmod_sel + 1) assign pmod_io[ pmod_sel * 8 + 7 : pmod_sel * 8 ] = ~ led_8_state_reg ; endgenerate assign led = led_output ; assign led_done = led_output ; assign led_ready = led_output ; endmodule 似乎原工程的引脚约束配置有问题,直接烧录布线并不能跑。需要将目录里的pmod_led.cst,第69行改成 IO_LOC "key" H11; 同时删除52行原有的H11部分,然后综合、布线、烧录,就可以看见Pmod的LED亮起来了。   [localvideo]4a8fdbb51ec1d0c3b2c2f2d6eaa8c91b[/localvideo]    

  • 2025-02-12
  • 回复了主题帖: 有没有什么好方法实现ESP32C3串口扩展

    XAndxiang 发表于 2025-2-12 09:37 多路选择器,一个串口分时复用 我也觉得多路复用挺适合这个应用

  • 回复了主题帖: 有没有什么好方法实现ESP32C3串口扩展

    9600用软串口完全足够了,如果你要上115200的话,rp2040的pio状态机模拟也没问题。本来你这加热设备的逻辑就是轮巡,又不会多个设备同时发送和接收数据。

  • 2025-02-08
  • 回复了主题帖: 单片机编程理解

    加油,多写代码,多做项目

  • 2025-02-07
  • 回复了主题帖: 这个春节最火爆的AI大模型deepseek,你玩了吗?

    当做高级搜索功能使用就可以了,还挺不错的。

  • 2025-01-29
  • 回复了主题帖: 【Tang Primer 25K 测评】1、硬件开箱

    PMOD也是支持I2C和SPI的,只是和常规淘宝模块接口区别比较大

  • 2025-01-27
  • 回复了主题帖: Luckfox幸狐 RV1106 Linux 开发板问题求助

    SD卡的话,先做rootfs扩容(网上很多教程,自己搜吧),然后再装大型软件。查看剩余空间可以用df -h命令。

  • 2025-01-25
  • 回复了主题帖: 关于误入论坛的高中生,求求解答

    个人建议优先高考。目前AI就业是很吃学历的,985,211本科也没啥用

  • 2025-01-16
  • 回复了主题帖: 国产以太网控制器CH390h试用体验----替代W5500

    zhangjt0713 发表于 2025-1-16 09:49 网口的IC 哪家的比较好用,选型的时候比较纠结  LAN8720A 等等给点意见,沁恒的怎么样呢 我在用872 ... 我用过的不多,这个不好推荐,可以自行了解一下。

  • 2025-01-15
  • 回复了主题帖: 国产以太网控制器CH390h试用体验----替代W5500

    att123 发表于 2025-1-15 08:33 w5500那么发烫,不是合格产品,还投放市场, 其实用w5500的还是很多的,也还有市场。至于烫的问题,这个也没办法,只能说竞品不给力,没尽快推出抢占他的市场。

  • 2025-01-14
  • 回复了主题帖: 为什么单片机会有3.3V和5V电压等级的区分?

    为了节能

  • 2025-01-08
  • 回复了主题帖: 【测评入围名单(最后1批)】年终回炉:FPGA、AI、高性能MCU、书籍等65个测品邀你来~

    个人信息无误,确认可以完成测评计划

统计信息

已有372人来访过

  • 芯积分:610
  • 好友:3
  • 主题:67
  • 回复:186

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言