莫尔斯或者摩尔斯电码(Morse Code),发明于1837年(另有一说是1836年),通过不同的排列顺序来表达不同的英文字母、数字和标点符号,在这里作一简单处理,仅产生点(Dit)和划(Dah),时长在0.25秒之内为点,超过为划,用按键控制时间模拟实现,5个点或划表示一个数字(0-9),通过数码管显示按键发声频率1350Hz。
//莫尔斯代码发生器
module morse(rst_n,clk,key,seg,dg,beep);
input clk,rst_n,key;//clk50M,rst_n低电平有效,key按下为0
output reg beep; //蜂鸣器
output reg[7:0] seg;//段码
output reg[5:0] dg;//位码
reg [10:0] ct; //1350Hz分频计数
reg [3:0] cnt,kcnt; //50Hz分频计数,按键计数
reg clk50hz,clk1k; //分频得到的时钟
reg [3:0] num,number; //要显示的数,点划编码数
reg [2:0] i=0; //要显示的点划序号
reg [9:0] dh=10'b0000000000; //点划输入,点为10,划为11,不按为00
wire clk5; //PLL输出的5.4M
reg [2:0] state; //按键状态
localparam S0 = 0; //初始状态
localparam S1 = 1; //按键按下0-0.25S
localparam S2 = 2; //按键在0.25S内释放
localparam S3 = 3; //按键按下超过0.25S
localparam S4 = 4; //按键在0.25S后释放
clk5m4 PLLA( //PLL产生5.4M时钟
.refclk(clk) ,
.reset(~rst_n),
.clk0_out(),
.clk1_out(clk5)
);
always @ (posedge clk5 or negedge rst_n) //分频成约1350Hz
if (!rst_n)
ct<=0;
else if (ct>=2000-1) //5.4M->1350Hz
begin clk1k<=~clk1k; ct<=0; end
else ct<=ct+1;
always @ (posedge clk1k or negedge rst_n) //分频成约52Hz
if (!rst_n)
cnt<=0;
else if (cnt>=13-1) //1k->50
begin clk50hz<=~clk50hz; cnt<=0; end
else cnt<=cnt+1;
always @ (posedge clk50hz or negedge rst_n) //按键检测并计数
if (!rst_n) begin state<=S0; kcnt<=0; dh<=10'b0000000000; i<=0; end //初始状态S0
else case(state)
S0:begin if (key) begin state<=S0; kcnt<=0; end
else begin i<=(i>=5)?1:i+1; state<=S1; end
end
S1:begin
if (key) begin state<=S2; kcnt<=0; end //短按(0.25S内)按键,S1状态
else if (kcnt<=13) begin state<=S1; kcnt<=kcnt+1; end
else state<=S3;
end
S2: if (key) begin {dh[11-2*i],dh[10-2*i]}<=2'b10; state<=S2; kcnt<=0; end //短按并释放,S2状态
else begin
state<=S1;
if (i>=5)
begin
i<=1; dh<=10'b0000000000;
end
else
i<=i+1;
end
S3:state<=(!key)?S3:S4; //长按(>0.25S),S3状态
S4:
if (key) begin {dh[11-2*i],dh[10-2*i]}<=2'b11; state<=S4; kcnt<=0; end //长按释放,S4状态
else begin
state<=S1;
if (i>=5)
begin
i<=1; dh<=10'b0000000000;
end
else
i<=i+1;
end
default:;
endcase
always@(state) //发声
case(state)
S1,S3:beep=clk1k;
default:beep=0;
endcase
always@(posedge clk1k or negedge rst_n) //数码管扫描显示(右5位为点划),最左为点划编码数字
if (!rst_n)
dg<=6'b111111;
else case (dg)
6'b111110:begin num=dh[3:2]+10; dg<=6'b111101;end
6'b111101:begin num=dh[5:4]+10; dg<=6'b111011;end
6'b111011:begin num=dh[7:6]+10; dg<=6'b110111;end
6'b110111:begin num=dh[9:8]+10; dg<=6'b101111;end
6'b101111:begin num=number; dg<=6'b011111;end
default:begin num=dh[1:0]+10;dg<=6'b111110;end
endcase
always@(dh)
case({dh[9:8],dh[7:6],dh[5:4],dh[3:2],dh[1:0]})
10'b1011111111:number<=1;
10'b1010111111:number<=2;
10'b1010101111:number<=3;
10'b1010101011:number<=4;
10'b1010101010:number<=5;
10'b1110101010:number<=6;
10'b1111101010:number<=7;
10'b1111111010:number<=8;
10'b1111111110:number<=9;
10'b1111111111:number<=0;
default:if (dh[1:0]==2'b00) number<=14; //等待输入标示
else number<=11; //出错标示
endcase
always @(num) //数码管译码,共阳极,0点亮,段码顺序:DP,G-A
case(num)
0:seg<=8'b11000000;
1:seg<=8'b11111001;
2:seg<=8'b10100100;
3:seg<=8'b10110000;
4:seg<=8'b10011001;
5:seg<=8'b10010010;
6:seg<=8'b10000010;
7:seg<=8'b11111000;
8:seg<=8'b10000000;
9:seg<=8'b10010000;
11:seg<=8'b10000110;//出错标示
12:seg<=8'b01111111; //点标示
13:seg<=8'b11110111;//划标示
14:seg<=8'b10110110; //等待输入标示
default:seg<=8'b11111111; //全暗不亮
endcase
endmodule
运行时,将首先在左侧数码管显示“三”,表示等待输入,随着按键通过控制时间长短输入点和划,并同步显示在数码管上,键入5次后,译码输出数字,若出错,将显示出错符号“E”。