## 数字频率计设计
### 全国大学生电子设计竞赛相关题目
#### 1997年B题 - 简易数字频率计设计
##### 1 任务 - 设计并制作一台数字显示的简易频率计
##### 2 要求
2.1 基本要求
- 频率测量
- 测量范围
- 信号:方波、正弦波
- 幅度:0.5-5V
- 频率:1Hz - 1MHz
- 测量误差:《0.1%
- 周期测量
- 测量范围
* 信号:方波、正弦波
* 幅度:0.5-5V
* 频率:1Hz - 1MHz
- 测量误差:《0.1%
- 脉冲宽度测量
- 测量范围:
- 信号:脉冲波
- 幅度:0.5-5V
- 脉冲宽度:> 100us
- 测量误差:《1%
- 显示器:10进制数字显示,显示刷新时间1-10s连续可调,对上述三种测量功能分别用不同颜色的发光二极管指示
- 具有自校功能,时标信号频率为1MHz
- 自行设计并制作满足本设计任务要求的稳压电源
2.2 发挥部分
- 扩展频率测量范围为0.1Hz - 10MHz(信号幅度0.5-5V), 测试误差降低为0.01%(最大闸门时间《10s)
- 测量并显示周期脉冲信号(幅度0.5-5V,频率1Hz - 1KHz)的占空比。占空比变化范围为10%-90%,测试误差《1%
- 在1Hz-1MHz范围内及测试误差《0.1%的条件下,进行小信号的频率测量,提出并实现抗干扰的措施
--------
#### 2015年F题 - 数字频率计
##### 1 任务
设计并制作一台闸门时间为1s的数字频率计
##### 2 要求
2.1 基本要求
- 频率和周期测量功能
- 被测信号为正弦波,频率范围为1Hz ~ 10MHz
- 被测信号有效值电压范围为50mV - 1V
- 测量相对误差的绝对值不大于10-4
- 时间间隔测量功能
- 被测信号为方波,频率范围为100Hz-1MHz
- 被测信号峰峰值电压为50mV - 1V
- 被测时间间隔的范围为0.1us - 100ms
- 测量相对误差的绝对值不小于10-2
- 测量数据刷新时间不大于2s,测量结果稳定,并能够自动显示单位
2.2 发挥部分
- 频率和周期测量的正弦波信号频率范围为1H ~ 100MHz,其它要求同基本要求(1)和(3)
- 频率和周期测量时被测正弦信号的最小有效值电压为10mV, 其它要求同基本要求(1)和(3)
- 增加脉冲信号占空比的测量功能,要求:
- 被测信号为矩形波,频率范围为1Hz ~ 5MHz
- 被测信号峰峰值电压范围为50mV ~ 1V
- 被测脉冲信号占空比范围为10% ~ 90%
- 显示的分辨率为0.1%, 测量相对误差的绝对值不大于10-2
- 其它(例如,进一步降低被测信号电压电压的幅度等)
--------
#### 2015年全国大学生电子设计竞赛 - F题 - 数字频率计设计
本设计基于 step-max0-08sam核心板 和 training功能板 平台,测量模拟信号调理后的脉冲信号 fxa 的频率、周期、占空比、以及 fxa 和 fxb 信号的相位差。整体RTL图如下所示
{{:20210121.png?1500|RTL框图}}
设计只包含数字部分,系统主要由4个模块组成:
- 频率采样测量
- 频率数据运算
- BCD转码实现
- OLED显示实现
1. 频率采样测量
频率采样测量主要分为三种方法,直接测频法、周期测频法、等精度测频法。
- 直接测频法:是给定一个闸门时间 Tgate 在此时间内对被测信号Tx计数Nx,Nx量化误差| dNx |<=1。因此有Tgate = NxTx 即:Fx = Nx / Tgate,Nx越大相对误差越小,也就是说对同一被测信号,闸门时间越长,相对误差越小。
- 周期测频法:是在被测信号的一个周期时间Tx内,对标准信号Ts 脉冲进行计数,计数结果Ns,Ns 量化误差| dNs | <=1。 因此有Tx =NsT s 即:Fx = Fs / Ns,其中 Fx 与 Fs 分别对应被测信号和标准信号的频率。在实际计算测量中一般不考虑标准信号 Fs 的误差。Ns 越大相对误差越小,也就是说对同一被测信号选择频率越高的标准信号相对误差越小
- 等精度测频法:闸门时间不是固定的值,而是被测信号周期的整数倍,即与被测信号同步,误差仅存在于时钟计数的±1误差,对于输入信号计数不存在误差。前两种方案在其测量范围内可以达到精度要求,但等精度测频法与前两种的不同之处在于其在测量范围内的精度是固定的,精度足够,而且可以满足测量范围,不用切换。闸门时间内,对 被测信号 和 标准信号 同时计数,标准信号频率为 Fs,计数为 Ns,被测信号计数 Nx,频率为 Fx,所以 Ns / Fs = Nx / Fx,因此 Fx = Fs * Nx / Ns。
{{:等精度测量原理.jpg?1000|等精度测量原理}}
通过对比,最终选择等精度测频法。
占空比测量在等精度测量的基础上,测量闸门时间内,使用标准信号对被测信号为高电平状态时间计数,占空比结果等于 Nduty * 100% / Ns
---
Verilog代码
//==========================================================================
//
// Author: STEP
// Module: freq_measure
// Description:
// Web: www.stepfapga.com
//--------------------------------------------------------------------------
// Info:
// V1.0---yyyy/mm/dd---Initial version
//
//==========================================================================
//timescale
`timescale 1ns / 1ns
module freq_measure (
input clk_200m, //采用200MHz时钟信号
input rst_n,
input fxa,
input fxb,
input start,
output reg done,
output reg done1,
output reg [WIDTH-1:0] FxCnt,
output reg [WIDTH-1:0] FsCnt,
output reg [WIDTH-1:0] DutyCnt,
output reg [WIDTH-1:0] DelayDutyCnt,
output reg [WIDTH-1:0] DelayCnt
);
//The defination of parameters
parameter WIDTH = 32; //
parameter CNT_1S = 'd199_999_999; //CLK=200M,then time=1S
reg t1s_gate;
reg fDelay_gate;
reg fxa_r1, fxa_r2, fxa_r3, fxa_pos;
reg fxb_r1, fxb_r2, fxb_r3, fxb_pos;
reg start_r, start_pos;
reg freq_real_gate;
reg delay_real_gate;
reg gate_done_r;
reg gate_done1_r;
reg [WIDTH-1:0] cnt_1s;
reg [WIDTH-1:0] fsCntTemp;
reg [WIDTH-1:0] fxCntTemp;
reg [WIDTH-1:0] DutyCntTemp;
reg [WIDTH-1:0] DelayDutyCntTemp;
reg [WIDTH-1:0] DelayCntTemp;
//检测两路信号的上升沿
////////////////////////////////////////////////////////////////////
//logic for fxa_r1, fxa_r2, fxa_r3
always @(posedge clk_200m) begin
fxa_r1 <= fxa;
fxa_r2 <= fxa_r1;
fxa_r3 <= fxa_r2;
end
always @(posedge clk_200m) fxa_pos <= (!fxa_r3) && fxa_r2;
//logic for fxb_r1, fxb_r2, fxb_r3
always @(posedge clk_200m) begin
fxb_r1 <= fxb;
fxb_r2 <= fxb_r1;
fxb_r3 <= fxb_r2;
end
always @(posedge clk_200m) fxb_pos <= (!fxb_r3) && fxb_r2;
//检测测试的start信号
////////////////////////////////////////////////////////////////////
//start signal detect
always @(posedge clk_200m) start_r <= start;
always @(posedge clk_200m) start_pos <= (!start_r) && start;
//产生1秒时间的闸门参考信号
////////////////////////////////////////////////////////////////////
always @(posedge clk_200m or negedge rst_n) begin
if(!rst_n) cnt_1s <= 1'b0;
else if(cnt_1s >= CNT_1S) cnt_1s <= 1'b0;
else if(t1s_gate) cnt_1s <= cnt_1s + 1'b1;
else cnt_1s <= 1'b0;
end
always @(posedge clk_200m or negedge rst_n) begin
if(!rst_n) t1s_gate <= 1'b0;
else if(start_pos) t1s_gate <= 1'b1;
else if(cnt_1s >= CNT_1S) t1s_gate <= 1'b0;
else t1s_gate <= t1s_gate;
end
//根据被测信号的上升沿,产生被测信号周期整数倍的闸门信号
////////////////////////////////////////////////////////////////////
//synchronous gate of frequency count of signal fx
always @(posedge fxa_r2) begin
if(t1s_gate) freq_real_gate <= 1'b1;
else freq_real_gate <= 1'b0;
end
//根据闸门信号对 标准信号 和 被测信号 计数
always @(posedge clk_200m) fsCntTemp <= (freq_real_gate)? (fsCntTemp + 1'b1) : 1'b0;
always @(posedge fxa_r2) fxCntTemp <= (freq_real_gate)? (fxCntTemp + 1'b1) : 1'b0;
//对闸门有效期间中,被测信号为高电平的时间计数
////////////////////////////////////////////////////////////////////
//Count input signal's duty
always @(posedge clk_200m) begin
if(start_pos) DutyCntTemp <= 1'b0;
else if(freq_real_gate && fxa_r2) DutyCntTemp <= DutyCntTemp + 1; //Count the high time
else DutyCntTemp <= DutyCntTemp;
end
//根据两路信号的上升沿,得到两路信号的相位差信号
////////////////////////////////////////////////////////////////////
//Produce time delay count gate
always @(posedge clk_200m or negedge rst_n) begin
if(!rst_n) fDelay_gate <= 1'b0;
else if(fxa_pos) fDelay_gate <= 1'b1;
else if(fxb_pos) fDelay_gate <= 1'b0;
else fDelay_gate <= fDelay_gate;
end
//对相位差信号进行等精度测量
//synchronous gate of delay time count
always @(posedge fDelay_gate) begin
if(t1s_gate) delay_real_gate <= 1'b1;
else delay_real_gate <= 1'b0;
end
always @(posedge clk_200m) begin
if(start_pos) DelayDutyCntTemp <= 1'b0;
else if(delay_real_gate && fDelay_gate) DelayDutyCntTemp <= DelayDutyCntTemp + 1; //Count the high time
else DelayDutyCntTemp <= DelayDutyCntTemp;
end
always @(posedge clk_200m) DelayCntTemp <= (delay_real_gate)? (DelayCntTemp + 1'b1) : 1'b0;
//闸门信号结束,返回测量结果
////////////////////////////////////////////////////////////////////
//Output count data when mesure done
always @(negedge freq_real_gate) begin
FxCnt <= fxCntTemp;
FsCnt <= fsCntTemp;
DutyCnt <= DutyCntTemp;
end
always @(negedge delay_real_gate) begin
DelayDutyCnt <= DelayDutyCntTemp;
DelayCnt <= DelayCntTemp;
end
//产生测量结束信号
////////////////////////////////////////////////////////////////////
wire gate_done = /*delay_real_gate || */freq_real_gate;
always @(posedge clk_200m) gate_done_r <= gate_done;
always @(posedge clk_200m) done <= (!gate_done) && gate_done_r;
wire gate_done1 = delay_real_gate/* || freq_real_gate*/;
always @(posedge clk_200m) gate_done1_r <= gate_done1;
always @(posedge clk_200m) done1 <= (!gate_done1) && gate_done1_r;
endmodule
2. 频率数据运算
在频率采样测量环节我们提到等精度测量,最后通过公式 Fx = Fs * Nx / Ns 运算出 被测信号 的频率,通过 Nduty * 100% / Ns 运算出 被测信号 的占空比
这里涉及乘法运算和除法运算,我们的FPGA器件MAX10系列 10M08SAM153C8G 集成了48个9位的乘法器,可以直接调用乘法器功能块。MAX10系列没有集成除法器功能块,直接使用除法逻辑会占用大量逻辑资源,性能也不能保证,所以我们使用流水的算法实现,稳定可靠。
对于64位的无符号数除法,被除数 numer 除以除数 denom,他们的商和余数一定不会超过64位,首先将 numer 转换成高64位为0,低64位为 numer 的temp_a,再将 denom 转换成高64位为 denom,低64位为0的temp_b。在每个周期开始前,先将temp_a左移一位,末尾补0,然后与temp_b相比较看是否大于temp_b,若大于temp_b,则temp_a = temp_a - temp_b + 1,否则继续往下执行。上面的移位操作、比较和减法要执行64次,执行完成后得到的 temp_a 的高64位为两数 numer 和 denom 相除的余数,低64位表示商。具体的算法流程可从下图的例子中得到体现
{{:divisor.png?600|除法器算法原理}}
---
任意位宽除法器设计,Verilog代码
//==========================================================================
//
// Author: STEP
// Module: divider
// Description:
// Web: www.stepfapga.com
//--------------------------------------------------------------------------
// Info:
// V1.0---yyyy/mm/dd---Initial version
//
//==========================================================================
//timescale
`timescale 1ns / 1ns
module divider #(
parameter DATA_WIDTH = 64
)
(
input clk,
input rst_n,
input start,
input [DATA_WIDTH-1:0] numer,
input [DATA_WIDTH-1:0] denom,
output done,
output reg [DATA_WIDTH-1:0] quotient,
output reg [DATA_WIDTH-1:0] remain
);
reg [7:0] cnt;
reg [2*DATA_WIDTH-1:0] temp_a;
reg [2*DATA_WIDTH-1:0] temp_b;
reg done_r;
//------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n) cnt <= 1'b0;
else if(start && (cnt < (DATA_WIDTH + 1))) cnt <= cnt + 1'b1;
else cnt <= 1'b0;
end
//------------------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n) done_r <= 1'b0;
else if(cnt == DATA_WIDTH) done_r <= 1'b1;
else if(cnt == DATA_WIDTH + 1) done_r <= 1'b0;
assign done = done_r;
//------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
temp_a <= 64'h0;
temp_b <= 64'h0;
end
else if(start) begin
if(cnt == 1'b0) begin
temp_a = {{DATA_WIDTH{1'b0}},numer};
temp_b = {denom,{DATA_WIDTH{1'b0}}};
end
else begin
temp_a = temp_a << 1;
if(temp_a >= temp_b) temp_a = temp_a - temp_b + 1'b1;
else temp_a = temp_a;
end
end
end
always @(posedge clk) quotient <= (cnt == DATA_WIDTH + 1)? temp_a[31: 0] : quotient;
always @(posedge clk) remain <= (cnt == DATA_WIDTH + 1)? temp_a[63:32] : remain;
endmodule
---
我们最终计算频率、周期、占空比、以及相位差,将四路运算例化,得到整体的运算模块,Verilog代码
//==========================================================================
//
// Author: Step
// Module: arithmetic
// Description:
// Web: www.stepfapga.com
//--------------------------------------------------------------------------
// Info:
// V1.0---2021/01/14---Initial version
//
//==========================================================================
//timescale
`timescale 1ns / 1ns
//module
module arithmetic #
(
parameter WIDTH = 32
)
(
input clk,
input rst_n,
input start,
input [WIDTH-1:0] FxCnt,
input [WIDTH-1:0] FsCnt,
input [WIDTH-1:0] DutyCnt,
input [WIDTH-1:0] DelayDutyCnt,
input [WIDTH-1:0] DelayCnt,
output [WIDTH-1:0] Fxcnt_quotient,
output [WIDTH-1:0] FxPeriod_quotient,
output [WIDTH-1:0] DutyCnt_quotient,
output [WIDTH-1:0] DelayDutyCnt_quotient
);
reg [2*WIDTH-1:0] Fxcnt_numer;
always @(posedge clk)
if(start) Fxcnt_numer <= FxCnt * 200_000_000;
else Fxcnt_numer = Fxcnt_numer;
reg [2*WIDTH-1:0] DutyCnt_numer;
always @(posedge clk)
if(start) DutyCnt_numer <= DutyCnt * 'd1000;
else DutyCnt_numer = DutyCnt_numer;
reg [2*WIDTH-1:0] DelayDutyCnt_numer;
always @(posedge clk)
if(start) DelayDutyCnt_numer <= DelayDutyCnt * 'd1000;
else DelayDutyCnt_numer = DelayDutyCnt_numer;
wire [WIDTH-1:0] Fxcnt_denom = FsCnt;
wire [WIDTH-1:0] FxPeriod_denom = Fxcnt_quotient;
wire [WIDTH-1:0] DutyCnt_denom = FsCnt;
wire [WIDTH-1:0] DelayDutyCnt_denom = DelayCnt;
divider Fxcnt_u_div (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.numer ( Fxcnt_numer ),
.denom ( Fxcnt_denom ),
.quotient ( Fxcnt_quotient )
);
divider FxPeriod_u_div (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.numer ( 1_000_000_000 ),
.denom ( FxPeriod_denom ),
.quotient ( FxPeriod_quotient )
);
divider DutyCnt_u_div (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.numer ( DutyCnt_numer ),
.denom ( DutyCnt_denom ),
.quotient ( DutyCnt_quotient )
);
divider DelayDutyCnt_u_div (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.numer ( DelayDutyCnt_numer ),
.denom ( DelayDutyCnt_denom ),
.quotient ( DelayDutyCnt_quotient )
);
endmodule //arithmetic
3. BCD转码实现
通过运算得到的二进制数据,想要显示在OLED屏幕,要以十进制形式显示,需要对二进制数据进行BCD转码。
{{:7-bin2bcd.png?600|BCD转码实现原理}}
---
Verilog代码
//==========================================================================
//
// Author: STEP
// Module: bin2bcd
// Description:
// Web: www.stepfapga.com
//--------------------------------------------------------------------------
// Info:
// V1.0---yyyy/mm/dd---Initial version
//
//==========================================================================
//timescale
`timescale 1ns / 1ns
module bin2bcd #(
parameter BIN_WIDTH = 32,
parameter BCD_WIDTH = 40
)
(
input clk,
input rst_n,
input start,
input [BIN_WIDTH-1:0] bin_code,
output reg done,
output reg [BCD_WIDTH-1:0] bcd_code
);
//-------------------------------------------------------
localparam IDLE = 3'b001;
localparam SHIFT = 3'b010;
localparam DONE = 3'b100;
//-------------------------------------------------------
reg [2:0] state_c;
reg [2:0] state_n;
reg [BIN_WIDTH-1:0] shift_cnt;
reg [BIN_WIDTH-1:0] bin_reg;
reg [BCD_WIDTH-1:0] bcd_reg;
wire [BCD_WIDTH-1:0] bcd_temp;
//-------------------------------------------------------
//FSM step1
always @(posedge clk or negedge rst_n)begin
if(!rst_n) state_c <= IDLE;
else state_c <= state_n;
end
//FSM step2
always @(*) begin
case(state_c)
IDLE: state_n = (start)? SHIFT : IDLE;
SHIFT: state_n = (shift_cnt==BIN_WIDTH-1)? DONE : SHIFT;
DONE: state_n = IDLE;
default:state_n = IDLE;
endcase
end
//FSM step3
always @(posedge clk) begin
case(state_c)
IDLE:begin
shift_cnt <= 1'b0;
bin_reg <= bin_code;
bcd_reg <= 1'b0;
end
SHIFT:begin
shift_cnt <= shift_cnt + 1'b1;
bin_reg <= bin_reg << 1;
bcd_reg <= {bcd_temp[BCD_WIDTH-2:0], bin_reg[BIN_WIDTH-1]};
end
default:begin
shift_cnt <= 1'b0;
bin_reg <= 1'b0;
bcd_reg <= bcd_reg;
end
endcase
end
//-------------------------------------------------------
assign bcd_temp[ 0+:4] = (bcd_reg[ 0+:4] > 4'd4)? (bcd_reg[ 0+:4] + 4'd3) : bcd_reg[ 0+:4];
assign bcd_temp[ 4+:4] = (bcd_reg[ 4+:4] > 4'd4)? (bcd_reg[ 4+:4] + 4'd3) : bcd_reg[ 4+:4];
assign bcd_temp[ 8+:4] = (bcd_reg[ 8+:4] > 4'd4)? (bcd_reg[ 8+:4] + 4'd3) : bcd_reg[ 8+:4];
assign bcd_temp[12+:4] = (bcd_reg[12+:4] > 4'd4)? (bcd_reg[12+:4] + 4'd3) : bcd_reg[12+:4];
assign bcd_temp[16+:4] = (bcd_reg[16+:4] > 4'd4)? (bcd_reg[16+:4] + 4'd3) : bcd_reg[16+:4];
assign bcd_temp[20+:4] = (bcd_reg[20+:4] > 4'd4)? (bcd_reg[20+:4] + 4'd3) : bcd_reg[20+:4];
assign bcd_temp[24+:4] = (bcd_reg[24+:4] > 4'd4)? (bcd_reg[24+:4] + 4'd3) : bcd_reg[24+:4];
assign bcd_temp[28+:4] = (bcd_reg[28+:4] > 4'd4)? (bcd_reg[28+:4] + 4'd3) : bcd_reg[28+:4];
assign bcd_temp[32+:4] = (bcd_reg[32+:4] > 4'd4)? (bcd_reg[32+:4] + 4'd3) : bcd_reg[32+:4];
assign bcd_temp[36+:4] = (bcd_reg[36+:4] > 4'd4)? (bcd_reg[36+:4] + 4'd3) : bcd_reg[36+:4];
always @(posedge clk) begin
case(state_c)
DONE: begin
done <= 1'b1;
bcd_code <= bcd_reg;
end
default: begin
done <= 1'b0;
bcd_code <= bcd_code;
end
endcase
end
endmodule
---
例化多个BCD转码模块,实现多个数据的BCD转码功能,Verilog代码
//==========================================================================
//
// Author: STEP
// Module: bin2bcd_top
// Description:
// Web: www.stepfapga.com
//--------------------------------------------------------------------------
// Info:
// V1.0---yyyy/mm/dd---Initial version
//
//==========================================================================
//timescale
`timescale 1ns / 1ns
module bin2bcd_top #(
parameter BIN_WIDTH = 32,
parameter BCD_WIDTH = 40
)
(
input clk,
input rst_n,
input [BIN_WIDTH-1:0] Fxcnt_quotient,
input [BIN_WIDTH-1:0] FxPeriod_quotient,
input [BIN_WIDTH-1:0] DutyCnt_quotient,
input [BIN_WIDTH-1:0] DelayDutyCnt_quotient,
output [BCD_WIDTH-1:0] Fxcnt_quotient_code,
output [BCD_WIDTH-1:0] FxPeriod_quotient_code,
output [BCD_WIDTH-1:0] DutyCnt_quotient_code,
output [BCD_WIDTH-1:0] DelayDutyCnt_quotient_code
);
bin2bcd u_bin2bcd_Fxcnt (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.bin_code ( Fxcnt_quotient ),
.bcd_code ( Fxcnt_quotient_code )
);
bin2bcd u_bin2bcd_FxPeriod (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.bin_code ( FxPeriod_quotient ),
.bcd_code ( FxPeriod_quotient_code )
);
bin2bcd u_bin2bcd_DutyCnt (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.bin_code ( DutyCnt_quotient ),
.bcd_code ( DutyCnt_quotient_code )
);
bin2bcd u_bin2bcd_DelayDutyCnt (
.clk ( clk ),
.rst_n ( rst_n ),
.start ( 1'b1 ),
.bin_code ( DelayDutyCnt_quotient ),
.bcd_code ( DelayDutyCnt_quotient_code )
);
endmodule
---
BCD转码之后的数据可以直接显示在OLED屏上了,但是为了看起来方便,我们对数据进行高位消零的操作。Verilog代码
//==========================================================================
//
// Author: Step
// Module: zero_clear
// Description:
// Web: www.stepfapga.com
//--------------------------------------------------------------------------
// Info:
// V1.0---2021/01/14---Initial version
//
//==========================================================================
//timescale
`timescale 1ns / 1ns
//include
//module
module zero_clear (
input [39:0] Fxcnt_quotient_code,
input [39:0] FxPeriod_quotient_code,
input [11:0] DutyCnt_quotient_code,
input [11:0] DelayDutyCnt_quotient_code,
output [79:0] data1,
output [79:0] data2,
output [31:0] data3,
output [31:0] data4
);
assign data1[72+:8] = Fxcnt_quotient_code[36+: 4]? Fxcnt_quotient_code[36+:4] : " ";
assign data1[64+:8] = Fxcnt_quotient_code[32+: 8]? Fxcnt_quotient_code[32+:4] : " ";
assign data1[56+:8] = Fxcnt_quotient_code[28+:12]? Fxcnt_quotient_code[28+:4] : " ";
assign data1[48+:8] = Fxcnt_quotient_code[24+:16]? Fxcnt_quotient_code[24+:4] : " ";
assign data1[40+:8] = Fxcnt_quotient_code[20+:20]? Fxcnt_quotient_code[20+:4] : " ";
assign data1[32+:8] = Fxcnt_quotient_code[16+:24]? Fxcnt_quotient_code[16+:4] : " ";
assign data1[24+:8] = Fxcnt_quotient_code[12+:28]? Fxcnt_quotient_code[12+:4] : " ";
assign data1[16+:8] = Fxcnt_quotient_code[ 8+:32]? Fxcnt_quotient_code[ 8+:4] : " ";
assign data1[ 8+:8] = Fxcnt_quotient_code[ 4+:36]? Fxcnt_quotient_code[ 4+:4] : " ";
assign data1[ 0+:8] = Fxcnt_quotient_code[ 0+: 4];
assign data2[72+:8] = FxPeriod_quotient_code[36+:4]? FxPeriod_quotient_code[36+:4] : " ";
assign data2[64+:8] = FxPeriod_quotient_code[32+:8]? FxPeriod_quotient_code[32+:4] : " ";
assign data2[56+:8] = FxPeriod_quotient_code[28+:12]? FxPeriod_quotient_code[28+:4] : " ";
assign data2[48+:8] = FxPeriod_quotient_code[24+:16]? FxPeriod_quotient_code[24+:4] : " ";
assign data2[40+:8] = FxPeriod_quotient_code[20+:20]? FxPeriod_quotient_code[20+:4] : " ";
assign data2[32+:8] = FxPeriod_quotient_code[16+:24]? FxPeriod_quotient_code[16+:4] : " ";
assign data2[24+:8] = FxPeriod_quotient_code[12+:28]? FxPeriod_quotient_code[12+:4] : " ";
assign data2[16+:8] = FxPeriod_quotient_code[ 8+:32]? FxPeriod_quotient_code[ 8+:4] : " ";
assign data2[ 8+:8] = FxPeriod_quotient_code[ 4+:36]? FxPeriod_quotient_code[ 4+:4] : " ";
assign data2[ 0+:8] = FxPeriod_quotient_code[ 0+: 4];
assign data3[24+:8] = DutyCnt_quotient_code[8+:4]? DutyCnt_quotient_code[8+:4] : " ";
assign data3[16+:8] = DutyCnt_quotient_code[4+:4];
assign data3[ 8+:8] = ".";
assign data3[ 0+:8] = DutyCnt_quotient_code[0+:4];
assign data4[24+:8] = DelayDutyCnt_quotient_code[8+:4]? DelayDutyCnt_quotient_code[8+:4] : " ";
assign data4[16+:8] = DelayDutyCnt_quotient_code[4+:4];
assign data4[ 8+:8] = ".";
assign data4[ 0+:8] = DelayDutyCnt_quotient_code[0+:4];
endmodule //zero_clear
4. OLED显示实现
---
板载的OLED屏模块采用SSD1306驱动芯片,128*32分辨率,网上有很多使用单片机驱动的案例可以参考,直接上代码,Verilog代码如下
// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: OLED12832
//
// Author: Step
//
// Description: OLED12832_Driver,使用8*8点阵字库,每行显示128/8=16个字符
//
// Web: www.stepfpga.com
//
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date: |Changes Made:
// V1.0 |2015/11/11 |Initial ver
// --------------------------------------------------------------------
module OLED12832 (
input clk, //12MHz系统时钟
input rst_n, //系统复位,低有效
input [79:0] data1, //
input [79:0] data2, //
input [79:0] data3, //
input [79:0] data4, //
output reg oled_csn, //OLCD液晶屏使能
output reg oled_rst, //OLCD液晶屏复位
output reg oled_dcn, //OLCD数据指令控制
output reg oled_clk, //OLCD时钟信号
output reg oled_dat //OLCD数据信号
);
localparam INIT_DEPTH = 16'd25; //LCD初始化的命令的数量
localparam IDLE = 6'h1, MAIN = 6'h2, INIT = 6'h4, SCAN = 6'h8, WRITE = 6'h10, DELAY = 6'h20;
localparam HIGH = 1'b1, LOW = 1'b0;
localparam DATA = 1'b1, CMD = 1'b0;
reg [7:0] cmd [24:0];
reg [39:0] mem [122:0];
reg [7:0] y_p, x_ph, x_pl;
reg [(8*21-1):0] char;
reg [7:0] num, char_reg; //
reg [4:0] cnt_main, cnt_init, cnt_scan, cnt_write;
reg [15:0] num_delay, cnt_delay, cnt;
reg [5:0] state, state_back;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= IDLE; state_back <= IDLE;
end else begin
case(state)
IDLE:begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= MAIN; state_back <= MAIN;
end
MAIN:begin
if(cnt_main >= 5'd8) cnt_main <= 5'd5;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main) //MAIN状态
5'd0: begin state <= INIT; end
5'd1: begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "F: Hz";state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "T: ns";state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "Duty: . %";state <= SCAN; end
5'd4: begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "Phase: . %";state <= SCAN; end
5'd5: begin y_p <= 8'hb0; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd10; char <= data1; state <= SCAN; end
5'd6: begin y_p <= 8'hb1; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd10; char <= data2; state <= SCAN; end
5'd7: begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h08; num <= 5'd4; char <= data3; state <= SCAN; end
5'd8: begin y_p <= 8'hb3; x_ph <= 8'h15; x_pl <= 8'h08; num <= 5'd4; char <= data4; state <= SCAN; end
default: state <= IDLE;
endcase
end
INIT:begin //初始化状态
case(cnt_init)
5'd0: begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end //复位有效
5'd1: begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end //延时大于3us
5'd2: begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end //复位恢复
5'd3: begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end //延时大于220us
5'd4: begin
if(cnt>=INIT_DEPTH) begin //当25条指令及数据发出后,配置完成
cnt <= 1'b0;
cnt_init <= cnt_init + 1'b1;
end else begin
cnt <= cnt + 1'b1; num_delay <= 16'd5;
oled_dcn <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;
end
end
5'd5: begin cnt_init <= 1'b0; state <= MAIN; end //初始化完成,返回MAIN状态
default: state <= IDLE;
endcase
end
SCAN:begin //刷屏状态,从RAM中读取数据刷屏
if(cnt_scan == 5'd11) begin
if(num) cnt_scan <= 5'd3;
else cnt_scan <= cnt_scan + 1'b1;
end else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;
else cnt_scan <= cnt_scan + 1'b1;
case(cnt_scan)
5'd 0: begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end //定位列页地址
5'd 1: begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end //定位行地址低位
5'd 2: begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end //定位行地址高位
5'd 3: begin num <= num - 1'b1;end
5'd 4: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 5: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 6: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 7: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end
5'd 8: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end
5'd 9: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end
5'd10: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
5'd11: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end
5'd12: begin state <= MAIN; end
default: state <= IDLE;
endcase
end
WRITE:begin //WRITE状态,将数据按照SPI时序发送给屏幕
if(cnt_write >= 5'd17) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
5'd 0: begin oled_csn <= LOW; end //9位数据最高位为命令数据控制位
5'd 1: begin oled_clk <= LOW; oled_dat <= char_reg[7]; end //先发高位数据
5'd 2: begin oled_clk <= HIGH; end
5'd 3: begin oled_clk <= LOW; oled_dat <= char_reg[6]; end
5'd 4: begin oled_clk <= HIGH; end
5'd 5: begin oled_clk <= LOW; oled_dat <= char_reg[5]; end
5'd 6: begin oled_clk <= HIGH; end
5'd 7: begin oled_clk <= LOW; oled_dat <= char_reg[4]; end
5'd 8: begin oled_clk <= HIGH; end
5'd 9: begin oled_clk <= LOW; oled_dat <= char_reg[3]; end
5'd10: begin oled_clk <= HIGH; end
5'd11: begin oled_clk <= LOW; oled_dat <= char_reg[2]; end
5'd12: begin oled_clk <= HIGH; end
5'd13: begin oled_clk <= LOW; oled_dat <= char_reg[1]; end
5'd14: begin oled_clk <= HIGH; end
5'd15: begin oled_clk <= LOW; oled_dat <= char_reg[0]; end //后发低位数据
5'd16: begin oled_clk <= HIGH; end
5'd17: begin oled_csn <= HIGH; state <= DELAY; end //
default: state <= IDLE;
endcase
end
DELAY:begin //延时状态
if(cnt_delay >= num_delay) begin
cnt_delay <= 16'd0; state <= state_back;
end else cnt_delay <= cnt_delay + 1'b1;
end
default:state <= IDLE;
endcase
end
end
//OLED配置指令数据
always@(posedge rst_n)
begin
cmd[ 0] = {8'hae};
cmd[ 1] = {8'h00};
cmd[ 2] = {8'h10};
cmd[ 3] = {8'h00};
cmd[ 4] = {8'hb0};
cmd[ 5] = {8'h81};
cmd[ 6] = {8'hff};
cmd[ 7] = {8'ha1};
cmd[ 8] = {8'ha6};
cmd[ 9] = {8'ha8};
cmd[10] = {8'h1f};
cmd[11] = {8'hc8};
cmd[12] = {8'hd3};
cmd[13] = {8'h00};
cmd[14] = {8'hd5};
cmd[15] = {8'h80};
cmd[16] = {8'hd9};
cmd[17] = {8'h1f};
cmd[18] = {8'hda};
cmd[19] = {8'h00};
cmd[20] = {8'hdb};
cmd[21] = {8'h40};
cmd[22] = {8'h8d};
cmd[23] = {8'h14};
cmd[24] = {8'haf};
end
//5*8点阵字库数据
always@(posedge rst_n)
begin
mem[ 0] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E}; // 48 0
mem[ 1] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00}; // 49 1
mem[ 2] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46}; // 50 2
mem[ 3] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31}; // 51 3
mem[ 4] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10}; // 52 4
mem[ 5] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39}; // 53 5
mem[ 6] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30}; // 54 6
mem[ 7] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03}; // 55 7
mem[ 8] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36}; // 56 8
mem[ 9] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E}; // 57 9
mem[ 10] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C}; // 65 A
mem[ 11] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36}; // 66 B
mem[ 12] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22}; // 67 C
mem[ 13] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C}; // 68 D
mem[ 14] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41}; // 69 E
mem[ 15] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01}; // 70 F
mem[ 32] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00}; // 32 sp
mem[ 33] = {8'h00, 8'h00, 8'h2f, 8'h00, 8'h00}; // 33 !
mem[ 34] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h00}; // 34
mem[ 35] = {8'h14, 8'h7f, 8'h14, 8'h7f, 8'h14}; // 35 #
mem[ 36] = {8'h24, 8'h2a, 8'h7f, 8'h2a, 8'h12}; // 36 $
mem[ 37] = {8'h62, 8'h64, 8'h08, 8'h13, 8'h23}; // 37 %
mem[ 38] = {8'h36, 8'h49, 8'h55, 8'h22, 8'h50}; // 38 &
mem[ 39] = {8'h00, 8'h05, 8'h03, 8'h00, 8'h00}; // 39 '
mem[ 40] = {8'h00, 8'h1c, 8'h22, 8'h41, 8'h00}; // 40 (
mem[ 41] = {8'h00, 8'h41, 8'h22, 8'h1c, 8'h00}; // 41 )
mem[ 42] = {8'h14, 8'h08, 8'h3E, 8'h08, 8'h14}; // 42 *
mem[ 43] = {8'h08, 8'h08, 8'h3E, 8'h08, 8'h08}; // 43 +
mem[ 44] = {8'h00, 8'h00, 8'hA0, 8'h60, 8'h00}; // 44 ,
mem[ 45] = {8'h08, 8'h08, 8'h08, 8'h08, 8'h08}; // 45 -
mem[ 46] = {8'h00, 8'h60, 8'h60, 8'h00, 8'h00}; // 46 .
mem[ 47] = {8'h20, 8'h10, 8'h08, 8'h04, 8'h02}; // 47 /
mem[ 48] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E}; // 48 0
mem[ 49] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00}; // 49 1
mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46}; // 50 2
mem[ 51] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31}; // 51 3
mem[ 52] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10}; // 52 4
mem[ 53] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39}; // 53 5
mem[ 54] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30}; // 54 6
mem[ 55] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03}; // 55 7
mem[ 56] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36}; // 56 8
mem[ 57] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E}; // 57 9
mem[ 58] = {8'h00, 8'h36, 8'h36, 8'h00, 8'h00}; // 58 :
mem[ 59] = {8'h00, 8'h56, 8'h36, 8'h00, 8'h00}; // 59 ;
mem[ 60] = {8'h08, 8'h14, 8'h22, 8'h41, 8'h00}; // 60 <
mem[ 61] = {8'h14, 8'h14, 8'h14, 8'h14, 8'h14}; // 61 =
mem[ 62] = {8'h00, 8'h41, 8'h22, 8'h14, 8'h08}; // 62 >
mem[ 63] = {8'h02, 8'h01, 8'h51, 8'h09, 8'h06}; // 63 ?
mem[ 64] = {8'h32, 8'h49, 8'h59, 8'h51, 8'h3E}; // 64 @
mem[ 65] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C}; // 65 A
mem[ 66] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36}; // 66 B
mem[ 67] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22}; // 67 C
mem[ 68] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C}; // 68 D
mem[ 69] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41}; // 69 E
mem[ 70] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01}; // 70 F
mem[ 71] = {8'h3E, 8'h41, 8'h49, 8'h49, 8'h7A}; // 71 G
mem[ 72] = {8'h7F, 8'h08, 8'h08, 8'h08, 8'h7F}; // 72 H
mem[ 73] = {8'h00, 8'h41, 8'h7F, 8'h41, 8'h00}; // 73 I
mem[ 74] = {8'h20, 8'h40, 8'h41, 8'h3F, 8'h01}; // 74 J
mem[ 75] = {8'h7F, 8'h08, 8'h14, 8'h22, 8'h41}; // 75 K
mem[ 76] = {8'h7F, 8'h40, 8'h40, 8'h40, 8'h40}; // 76 L
mem[ 77] = {8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F}; // 77 M
mem[ 78] = {8'h7F, 8'h04, 8'h08, 8'h10, 8'h7F}; // 78 N
mem[ 79] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h3E}; // 79 O
mem[ 80] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h06}; // 80 P
mem[ 81] = {8'h3E, 8'h41, 8'h51, 8'h21, 8'h5E}; // 81 Q
mem[ 82] = {8'h7F, 8'h09, 8'h19, 8'h29, 8'h46}; // 82 R
mem[ 83] = {8'h46, 8'h49, 8'h49, 8'h49, 8'h31}; // 83 S
mem[ 84] = {8'h01, 8'h01, 8'h7F, 8'h01, 8'h01}; // 84 T
mem[ 85] = {8'h3F, 8'h40, 8'h40, 8'h40, 8'h3F}; // 85 U
mem[ 86] = {8'h1F, 8'h20, 8'h40, 8'h20, 8'h1F}; // 86 V
mem[ 87] = {8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F}; // 87 W
mem[ 88] = {8'h63, 8'h14, 8'h08, 8'h14, 8'h63}; // 88 X
mem[ 89] = {8'h07, 8'h08, 8'h70, 8'h08, 8'h07}; // 89 Y
mem[ 90] = {8'h61, 8'h51, 8'h49, 8'h45, 8'h43}; // 90 Z
mem[ 91] = {8'h00, 8'h7F, 8'h41, 8'h41, 8'h00}; // 91 [
mem[ 92] = {8'h55, 8'h2A, 8'h55, 8'h2A, 8'h55}; // 92 .
mem[ 93] = {8'h00, 8'h41, 8'h41, 8'h7F, 8'h00}; // 93 ]
mem[ 94] = {8'h04, 8'h02, 8'h01, 8'h02, 8'h04}; // 94 ^
mem[ 95] = {8'h40, 8'h40, 8'h40, 8'h40, 8'h40}; // 95 _
mem[ 96] = {8'h00, 8'h01, 8'h02, 8'h04, 8'h00}; // 96 '
mem[ 97] = {8'h20, 8'h54, 8'h54, 8'h54, 8'h78}; // 97 a
mem[ 98] = {8'h7F, 8'h48, 8'h44, 8'h44, 8'h38}; // 98 b
mem[ 99] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h20}; // 99 c
mem[100] = {8'h38, 8'h44, 8'h44, 8'h48, 8'h7F}; // 100 d
mem[101] = {8'h38, 8'h54, 8'h54, 8'h54, 8'h18}; // 101 e
mem[102] = {8'h08, 8'h7E, 8'h09, 8'h01, 8'h02}; // 102 f
mem[103] = {8'h18, 8'hA4, 8'hA4, 8'hA4, 8'h7C}; // 103 g
mem[104] = {8'h7F, 8'h08, 8'h04, 8'h04, 8'h78}; // 104 h
mem[105] = {8'h00, 8'h44, 8'h7D, 8'h40, 8'h00}; // 105 i
mem[106] = {8'h40, 8'h80, 8'h84, 8'h7D, 8'h00}; // 106 j
mem[107] = {8'h7F, 8'h10, 8'h28, 8'h44, 8'h00}; // 107 k
mem[108] = {8'h00, 8'h41, 8'h7F, 8'h40, 8'h00}; // 108 l
mem[109] = {8'h7C, 8'h04, 8'h18, 8'h04, 8'h78}; // 109 m
mem[110] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h78}; // 110 n
mem[111] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h38}; // 111 o
mem[112] = {8'hFC, 8'h24, 8'h24, 8'h24, 8'h18}; // 112 p
mem[113] = {8'h18, 8'h24, 8'h24, 8'h18, 8'hFC}; // 113 q
mem[114] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h08}; // 114 r
mem[115] = {8'h48, 8'h54, 8'h54, 8'h54, 8'h20}; // 115 s
mem[116] = {8'h04, 8'h3F, 8'h44, 8'h40, 8'h20}; // 116 t
mem[117] = {8'h3C, 8'h40, 8'h40, 8'h20, 8'h7C}; // 117 u
mem[118] = {8'h1C, 8'h20, 8'h40, 8'h20, 8'h1C}; // 118 v
mem[119] = {8'h3C, 8'h40, 8'h30, 8'h40, 8'h3C}; // 119 w
mem[120] = {8'h44, 8'h28, 8'h10, 8'h28, 8'h44}; // 120 x
mem[121] = {8'h1C, 8'hA0, 8'hA0, 8'hA0, 8'h7C}; // 121 y
mem[122] = {8'h44, 8'h64, 8'h54, 8'h4C, 8'h44}; // 122 z
end
endmodule