草庐IT

数字逻辑实验 9 FPGA数字钟(Verilog)

houhuawei 2023-03-28 原文

目录

实验 9 FPGA数字钟

?请使用SystemVerilog/Verilog实现一个数字钟。
要求:
(1)能够显示时分秒;
(2)能够设置开始时间;
(3)使用你自己的7段数码管显示译码电路实现;
(4)可以使用动态显示方法实现;
(5)依据实现的其他附加功能,酌情加分:秒表、倒计时、闹钟、…
(6)需要在Basys3 FPGA开发板上实现,并通过验收

实验分析:

该实验主要考察对使用硬件设计语言(HDL)进行编程设计,锻炼硬件设计能力。

实现思路:

采用模块化设计的思想,将整个数字钟分解为若干功能模块:

  1. 端口映射:约束文件(代码端口映射到硬件端口)

  2. 处理按键输入的io模块:消抖,判断按下

  3. 特定功能模块:时钟分频,24小时时钟,正/倒计时,秒表,闹钟,秒表,时间设置

  4. 显示模块:译码,选择输出

硬件支持:

FPGA开发板:Artix-7 Basys3 from Xilinx

学习步骤:

  1. 阅读手册,了解结构与功能:
    Basys 3 Reference Manual - Digilent Reference

  2. 若需要更详细的信息,则阅读所用模块的原理图
    Basys 3 Schematic - Digilent

硬件描述语言代码编写:

1 顶层模块

输入:板载时钟CLK,各种按键

输出:按键LED灯信号,数码管12位信号(4位用于位置选择,7位用于7段数码管显示,1位用于小数点显示)

内部:

1 分线接线,设置缓冲区等

2 例化各种子模块

3 选择数码管输出

  • digital_clk.v

    module digital_clk(
            input CLK,
            input BTNU, BTND, BTNL, BTNR, BTNC,
            input SW0, SW1, SW2, SW3, SW4, SW5, SW6,
            input SW13, SW14, SW15,
            output reg[11:0] display_out,
            output LD0, LD1, LD2, LD3, LD4, LD5, LD6,
            output LD13, LD14, LD15
        );  
    //时钟分频
    wire ms_clk;
    wire clock_clk; //1Hz  
    wire scan_clk; //1kHZ  
    wire Reset, reset, set;
    //按键接线
    wire btnu_down, btnd_down, btnl_down, btnr_down, btnc_down;
    wire sw0, sw1, sw2, sw3, sw4, sw5, sw6;
    wire sw13, sw14, sw15;
    //各模块数码管输出区
    wire [11:0]clock_seg ; 
    wire [11:0]countup_seg;
    wire [11:0]countdown_seg;
    wire [11:0]alarm_seg;
    wire [11:0]reset_seg;
    wire [11:0]sw_seg;
    //时间设置接线
    wire [3:0] sethour1;
    wire [3:0] sethour2;
    wire [3:0] setminute1;
    wire [3:0] setminute2;
    wire [3:0] setsecond1;
    wire [3:0] setsecond2;
    
    wire [3:0] hour1;  
    wire [3:0] hour2;  
    wire [3:0] minute1;
    wire [3:0] minute2;
    wire [3:0] second1;
    wire [3:0] second2;
    //wire [5:0] setbit;
    wire beep;
    assign Reset = sw0;
    assign reset = sw13;
    assign set = sw1;
    //LD
    assign LD0 = sw0;
    assign LD1 = sw1;
    assign LD2 = sw2;
    assign LD3 = sw3;
    assign LD4 = sw4;
    assign LD5 = sw5;
    assign LD6 = sw6;
    assign LD13 =sw13;
    assign LD14 = sw14;
    assign LD15 = sw15;
    
    div_n32p #(42950) myscan_clk( //输出1000Hz扫描时钟
            .CLK(CLK), 
            .reset(Reset),
            .clk(scan_clk)
    );
    div_n32p #(43) myclock_clk( //输出1Hz计时时钟
            .CLK(CLK), 
            .reset(Reset),
            .clk(clock_clk)
    );
    div_n32p #(2577) myms_clk(
            .CLK(CLK), 
            .reset(Reset),
            .clk(ms_clk)
    );
    
    // io模块  
    in_out myio(
        .CLK(CLK), 
        .BTNU(BTNU), 
        .BTND(BTND), 
        .BTNL(BTNL), 
        .BTNR(BTNR), 
        .BTNC(BTNC),
        .SW0(SW0), 
        .SW1(SW1), 
        .SW2(SW2), 
        .SW3(SW3),
        .SW4(SW4), 
        .SW5(SW5),
        .SW6(SW6),
        .SW13(SW13),
        .SW14(SW14),
        .SW15(SW15),
        .btnu_down(btnu_down), 
        .btnd_down(btnd_down), 
        .btnl_down(btnl_down), 
        .btnr_down(btnr_down), 
        .btnc_down(btnc_down),
        .sw0(sw0), 
        .sw1(sw1), 
        .sw2(sw2), 
        .sw3(sw3),
        .sw4(sw4), 
        .sw5(sw5),
        .sw6(sw6),
        .sw13(sw13),
        .sw14(sw14),
        .sw15(sw15)
    );
    // 时钟模块
    clock myclock(
        .CLK(CLK),
        .clock_clk(clock_clk), //1Hz  
        .scan_clk(scan_clk), //1kHZ   
        .Reset(Reset), 
        .reset(reset && sw2),
        .set(set && sw2),
        .start_pause(btnc_down && sw2),
        .display_h(sw15),
    
        .sethour1(sethour1),  
        .sethour2(sethour2),  
        .setminute1(setminute1),
        .setminute2(setminute2),
        .setsecond1(setsecond1),
        .setsecond2(setsecond2),
        
        .hour1  (hour1),
        .hour2  (hour2),
        .minute1(minute1),
        .minute2(minute2),
        .second1(second1),
        .second2(second2),
    
        .clock_seg(clock_seg[7:0]),//8
        .seln(clock_seg[11:8])//4
    );
    //正计时模块
    countup mycountup(
        .CLK(CLK),
        .clock_clk(clock_clk), //1Hz
        .scan_clk(scan_clk), //1kHZ
        .Reset(Reset), 
        .reset(reset && sw3),
        .set(set && sw3),
        .display_h(sw15),
        .start_pause(btnc_down && sw3),
        .sethour1(sethour1),  
        .sethour2(sethour2),  
        .setminute1(setminute1),
        .setminute2(setminute2),
        .setsecond1(setsecond1),
        .setsecond2(setsecond2),
        
        .clock_seg(countup_seg[7:0]),//8
        .seln(countup_seg[11:8])//4
    );
    //倒计时模块
    countdown mycountdown(
        .CLK(CLK),  
        .clock_clk(clock_clk), //1Hz
        .scan_clk(scan_clk), //1kHZ
        .Reset(Reset), 
        .reset(reset && sw4),
        .set(set && sw4),
        .display_h(sw15),
        .start_pause(btnc_down && sw4),
        
        .sethour1(sethour1),  
        .sethour2(sethour2),  
        .setminute1(setminute1),
        .setminute2(setminute2),
        .setsecond1(setsecond1),
        .setsecond2(setsecond2),
        
        .clock_seg(countdown_seg[7:0]),//8
        .seln(countdown_seg[11:8])//4
    
    );
    //闹钟模块
    alarm myalarm(
        .CLK(CLK),
        .clock_clk(clock_clk), //1Hz
        .scan_clk(scan_clk), //1kHZ
        .Reset(Reset), 
        .reset(reset && sw5),
        .set(set && sw5),
        .start(sw14),
        .display_h(sw15),
        .hour1(hour1),
        .hour2(hour2),
        .minute1(minute1),
        .minute2(minute2),
        .second1(second1),
        .second2(second2),
        .sethour1(sethour1),  
        .sethour2(sethour2),  
        .setminute1(setminute1),
        .setminute2(setminute2),
        .setsecond1(setsecond1),
        .setsecond2(setsecond2),
        
        .clock_seg(alarm_seg[7:0]),//8
        .seln(alarm_seg[11:8]),//4
        .beep(beep)
    );
    //秒表
    stopwatch mystopwatch(
        .CLK(CLK),       
        .ms_clk(ms_clk),    
        .scan_clk(scan_clk),  
        .Reset(Reset),//全局复
        .reset(reset && sw6),//功能复
        .start_pause(btnc_down && sw6),
        .display_h(display_h), 
        .clock_seg(sw_seg[7:0]),//8
        .seln(sw_seg[11:8])//4
    );
    
    button_set_time myset( 
        .CLK(CLK), 
        .Reset(Reset),
    //    .reset(1'b0),
        .reset(reset && set),
        .set(set),
        .btnu_down(btnu_down), 
        .btnd_down(btnd_down), 
        .btnl_down(btnl_down), 
        .btnr_down(btnr_down), 
        .btnc_down(btnc_down),
    
        //out
        .sethour1(sethour1), 
        .sethour2(sethour2), 
        .setminute1(setminute1),
        .setminute2(setminute2),
        .setsecond1(setsecond1),
        .setsecond2(setsecond2));
    reset_module myreset(
        .CLK(CLK),
        .Reset(Reset),
        .scan_clk(scan_clk),
        .clock_clk(clock_clk),
        .clock_seg(reset_seg[7:0]), 
        .seln(reset_seg[11:8]));
    
    
    //选择输出
    always@(posedge CLK, negedge Reset)begin
        if(~Reset) display_out <= reset_seg;
        else if(sw2) begin//时钟
            if(beep && sw14)display_out <=alarm_seg;//闹钟响了
            else display_out <= clock_seg;
        end
        else if(sw3)display_out <=countup_seg;  //正计时
        else if(sw4)display_out <=countdown_seg;//倒计时
        else if(sw5)display_out <=alarm_seg;    //闹钟
        else if(sw6)display_out <=sw_seg;       //秒表
        else display_out <= reset_seg;          
    end
    endmodule
    

2 时钟分频,(正/倒)计时器模块

时钟分频

将板载输入100MHz分频为所需频率:

1000Hz用于扫描

1Hz用于时钟计时

60Hz用于秒表计时

由分频公式\(f=\frac{p}{2^{N}} f_{0}\),得\(p=\frac{2^{N} f}{f_{0}}\)。采用32位计数器即可求得对应分频器的加数\(p\)

  • div_n32p.v

    module div_n32p #(parameter p = 43)(//1Hz
        input CLK, reset,
        output clk);
    reg [31:0] counter;
    always@(posedge CLK, negedge reset)begin
        if(!reset) counter<=32'b0;
        else begin
            counter = counter + p;
        end
    end
    assign clk = counter[31];
    endmodule
    

1位计时器

通过输入的进位计时增加,满量程后进位。带有设置时间和开始/暂停功能。

  • counter.v

    module counter #(parameter base = 10)(
                input CLK,
                input cin,
                input reset,
                input set,
                input start_pause,
                input [3:0] scount,
                output reg[3:0] count,
                output reg cout
        );
        
    reg [2:0] cin_reg;
    wire cin_p;
    reg mode;//1开始 0暂停
    
    always@(posedge CLK, negedge reset)begin
        if(!reset) mode<=1'b0;
        else if(start_pause) mode<=~mode;
        else begin
            mode<=mode;
        end
    end
    
    always@(posedge CLK, negedge reset)begin
        if(!reset) begin
            cin_reg <=3'b0;
        end
        else begin 
            cin_reg <={cin_reg[1:0], cin};
        end
    end
    assign cin_p = ~cin_reg[2] && cin_reg[1];
    
    always@(posedge CLK, negedge reset)begin
        if(!reset) begin //复位
            count <= 4'b0;
            cout <= 0;
        end
        else if(set==1'b1)begin//设置时间
            count <= scount;
            cout <=0;
        end
        else begin//自增
            if(count >= base)begin
                cout <= 1;
                count <=4'b0;
            end
            else if(cin_p && mode==1'b1) begin
                count <=count+1;
                cout<=0;
            end
            else begin
                count<=count;
                cout<=0;
            end
        end
    end
    endmodule
    

1位倒计时器

与计时器相仿,减一个数等价于加上其补码,-1→+15

  • decounter.v

    `timescale 1ns / 1ps
    
    module decounter #(parameter base = 10)(
        input CLK,
        input cin,
        input reset,
        input set,
        input start_pause,
        input [3:0] scount,
        
        output reg[3:0] count,
        output reg cout);
        
    reg [2:0] cin_reg;
    wire cin_p;
    reg mode;//1开始 0暂停
    
    always@(posedge CLK, negedge reset)begin
        if(!reset) mode<=1'b0;
        else if(start_pause) mode<=~mode;
        else begin
            mode<=mode;
        end
    end
    
    always@(posedge CLK, negedge reset)begin
        if(!reset) begin
            cin_reg <=3'b0;
        end
        else begin 
            cin_reg <={cin_reg[1:0], cin};
        end
    end
    assign cin_p = ~cin_reg[2] && cin_reg[1];
    
    always@(posedge CLK, negedge reset)begin
        if(!reset) begin //复位
            count <= 4'b0;
            cout <= 0;
        end
        else if(set==1'b1)begin//设置时间
            count <= scount;
        end
        else begin//自增
            if(cin_p && mode==1'b1) begin
                if(count !=0 ) count <=count + 4'd15;
                else begin
                    count <=base-1;
                    cout <= 1;
                end
            end
            else begin
                count<=count;
                cout<=0;
            end
        end
    end
    
    endmodule
    

3 输入处理模块in_out.v

输入:未处理的按键接线

输出:处理后按键接线

  • 按键消抖

  • 判断Button按下

按键消抖

由于按键输入可能存在的抖动与对板载时钟的异步输入,所以要进行消抖与同步处理。

这里采用了简单的串联触发器构成的同步器,通过三级或更多级触发器,使得最终采样到的信号为亚稳态的概率尽可能小,即输出趋于稳定。

  • module debounce

    module debounce(
            input CLK,
            input key_in,
            output key_out
    );
    reg delay1;
    reg delay2;
    reg delay3;
    always@(posedge CLK)begin
            delay1 <= key_in;
            delay2 <= delay1;
            delay3 <= delay2;
    end
    assign key_out = delay3;
    endmodule
    

判断Button按下

按下,电位由低变高(或相反),存在一个上升沿,能不能用posedge检测上升沿来检测按键按下?

本着老师所说的控制信号与其他信号分开处理的要求,这里采取了连续采样寄存多次Button信号,通过判断临近两位是否出现相反的电位,来确定是否出现了上升沿。(相当于又做了一次同步??,感觉这里处理的不简练)

  • btn_down

    wire btnu;
    reg [2:0] btnu_reg;
    assign btnu_down = ~btnu_reg[2]&(btnu_reg[1]);
    always@(posedge CLK)begin
       btnu_reg<= {btnu_reg[1:0], btnu};
    end
    
    

5 24小时时钟,计时,秒表模块

?输入:
各时钟与复位信号:CLK,clock_clk,scan_clk,Reset
设置时间,开始/暂停,高位显示信号

?输出:
设置时间值,输出时间值,数码管信号

  • 例化counter,采用逐级进位

    counter #(6) s1_counter(
                .CLK(CLK),
                .reset(Reset&&~reset),
                .set(set),
                .start_pause(start_pause),
                .cin(cout[0]),
                .scount(setsecond1),
                .count(second1),
                .cout(cout[1])
    );
    
  • 扫描

    reg [2:0] sel; 
    always@(posedge scan_clk, negedge Reset)begin
        if(!Reset) sel<=0;
        else if(sel==3) sel<=0;
        else sel <= sel+1;
    end  
    
    always@(posedge scan_clk, negedge Reset)begin
        if(~Reset || full)begin
           {seln, clock_seg} <= 12'b1101_0000001_1;
        end
        else if(~display_h)begin
            case(sel)      
                3'd0:{seln, clock_seg} <= {4'b0111, encoder(minute1), 1'b1};
                3'd1:{seln, clock_seg} <= {4'b1011, encoder(minute2), dot };
                3'd2:{seln, clock_seg} <= {4'b1101, encoder(second1), 1'b1};
                3'd3:{seln, clock_seg} <= {4'b1110, encoder(second2), dot };
                default:{seln, clock_seg} <= 12'b1011_0000000_0;
            endcase
        end
        else begin
            case(sel)      
                3'd0:{seln, clock_seg} <= {4'b0111, encoder(hour1),   1'b1};
                3'd1:{seln, clock_seg} <= {4'b1011, encoder(hour2),   dot };
                3'd2:{seln, clock_seg} <= {4'b1101, encoder(minute1), 1'b1};
                3'd3:{seln, clock_seg} <= {4'b1110, encoder(minute2), dot };
                default:{seln, clock_seg} <= 12'b0111_0000000_0;
            endcase
        end
    end
    
    
  • 译码器(函数)

    function [6:0]encoder;
        input [3:0] dec;
        begin
            case(dec)
               4'd0: encoder = 7'b0000_001;
               4'd1: encoder = 7'b1001_111;
               4'd2: encoder = 7'b0010_010;
               4'd3: encoder = 7'b0000_110;
               4'd4: encoder = 7'b1001_100;
               4'd5: encoder = 7'b0100_100;
               4'd6: encoder = 7'b0100_000;
               4'd7: encoder = 7'b0001_111;
               4'd8: encoder = 7'b0000_000;
           4'd9: encoder = 7'b0000_100;
           default: encoder = 7'b1000_000;
          endcase
        end
    endfunction
    

正倒计时模块:正计时与24小时时钟模块相同,倒计时采用decounter即可

秒表:接入ms_clk即可,其他与时钟相同

6 闹钟

输入同上,另增一闹钟开关输入

输出另增一beep输出,用于响铃

思路:记录所设置的闹钟时间,与时钟时间比较,判断是否触发响铃。

  • 响铃逻辑beep

    output reg beep;
    always@(posedge clock_clk, negedge Reset)begin
        if(~Reset)begin                    
           count <= 4'b0;
           pre <= 1'b0;
           count <=4'b0;
        end  
        else if(onclk && start)begin //到时间
                pre <=1;
                count<=0;
                beep <=1;
        end
        else if(pre==1 && count <4'd5)begin
            count<= count+1;
        end
        else if(pre==1 && count>=4'd5)begin
            pre<=0;
            count<=0;
            beep<=0;
        end
        else begin
            pre<=pre;
            count<=count;
            beep<=beep;
        end
    end
    

7 时间设置

输入:各控制信号,按键信号

输出:所设置的时间

维护reg [5:0] setbit;通过左右按键调整设置哪一位,上下按键增减数值。

按键功能:

SW15显示高位
SW14闹钟开关
SW13功能归零reset高有效
SW6秒表
SW5闹钟
SW4倒计时
SW3正计时
SW2时钟
SW1设置时间
SW0全局复位

按键去抖动:

(154条消息) FPGA中的按键消抖_Super-fei的博客-CSDN博客_fpga按键消抖

Verilog——FPGA按键去抖操作_Footprints明轩的博客-CSDN博客

有关数字逻辑实验 9 FPGA数字钟(Verilog)的更多相关文章

  1. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  2. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  3. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  4. ruby - 将n维数组的每个元素乘以Ruby中的数字 - 2

    在Ruby中,是否有一种简单的方法可以将n维数组中的每个元素乘以一个数字?这样:[1,2,3,4,5].multiplied_by2==[2,4,6,8,10]和[[1,2,3],[1,2,3]].multiplied_by2==[[2,4,6],[2,4,6]]?(很明显,我编写了multiplied_by函数以区别于*,它似乎连接了数组的多个副本,不幸的是这不是我需要的)。谢谢! 最佳答案 它的长格式等价物是:[1,2,3,4,5].collect{|n|n*2}其实并没有那么复杂。你总是可以使你的multiply_by方法:c

  5. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  6. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

  7. ruby-on-rails - rails 中的正则表达式匹配 [\w] 和 "-"但不匹配数字 - 2

    我想为名字验证编写一个正则表达式。正则表达式应包括所有字母(拉丁/法语/德语字符等)。但是我想从中排除数字并允许-。所以基本上它是\w(减)数(加)-。请帮忙。 最佳答案 ^[\p{L}-]+$\p{L}匹配anykindofletterfromanylanguage. 关于ruby-on-rails-rails中的正则表达式匹配[\w]和"-"但不匹配数字,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

  8. ruby-on-rails - 将数字字符串转换为数字数组 - 2

    在我的应用程序中,我有一个文本字段,用户可以在其中输入类似这样的内容"1,2,3,4"存储到数据库中。现在,当我想使用内部数字时,我有两个选择:"1,2,3,4".split(',')或string.scan(/\d+/)do|x|a两种方式我都得到一个像这样的数组["1","2","3","4"]然后我可以通过在每个数字上调用to_i来使用这些数字。有没有更好的方法可以转换"1,2,3"to[1,2,3]andnot["1","2","3"] 最佳答案 str.split(",").map{|i|i.to_i}但是这个想法对你来说

  9. ruby - 递归地将所有数字字符串转换为 Ruby 哈希中的整数 - 2

    我有一个随机大小的散列,它可能有类似"100"的值,我想将其转换为整数。我知道我可以使用value.to_iifvalue.to_i.to_s==value来做到这一点,但我不确定我将如何在我的散列中递归地做到这一点,考虑到一个值可以是一个字符串,或一个数组(哈希或字符串),或另一个哈希。 最佳答案 这是一个非常简单的递归实现(尽管必须同时处理数组和散列会增加一些技巧)。deffixnumifyobjifobj.respond_to?:to_i#IfwecancastittoaFixnum,doit.obj.to_ielsifobj

  10. ruby-on-rails - Rails 格式验证——字母数字,但不是纯数字 - 2

    什么是测试格式验证的最佳方法让我们说一个用户名,使用字母数字的正则表达式,但不是纯数字?我一直在我的模型中使用以下验证validates:username,:format=>{:with=>/^[a-z0-9]+[-a-z0-9]*[a-z0-9]+$/i}数字用户名(例如“342”)通过了验证,这是我不想要的。 最佳答案 您想“向前看”一封信:/\A(?=.*[a-z])[a-z\d]+\Z/i 关于ruby-on-rails-Rails格式验证——字母数字,但不是纯数字,我们在Sta

随机推荐