Chapter 5 Code
To comply the Chapter 5 Verilog codes within the book titled Fundamental Digital Circuits and FPGA
Last updated
To comply the Chapter 5 Verilog codes within the book titled Fundamental Digital Circuits and FPGA
Last updated
/*---------------------------------------------------------------------------------------------------
*- File name: LEDchaser.v
*- Top Module name: LEDchaser
*- Submodules: None
*- Description: Implementation of an LED chaser with 8 states
*-
*- Example of Usage:
This code is designed to cycle through LEDs in a chaser pattern. It can be implemented on
any FPGA development board with an 8-bit LED array. To observe the LED chaser effect:
- Connect the LEDs output to the 8 on-board LEDs.
- Provide the system clock to the clk input.
- Reliability:
This code is intended for educational and demonstration purposes and has not been
validated for use in production systems. Users are advised to thoroughly test the
module in their specific application context.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module LEDchaser (
input clk,
output reg [7:0] LEDs
);
/**************** Defining the 8 states with binary numbers *******************/
parameter S0 = 3'b000,
S1 = 3'b001,
S2 = 3'b010,
S3 = 3'b011,
S4 = 3'b100,
S5 = 3'b101,
S6 = 3'b110,
S7 = 3'b111;
/**************** Describing the LED actions in each state *******************/
reg [2:0] state;
always @ (posedge clk) begin
case (state)
S0: LEDs = 8'b11111110;
S1: LEDs = 8'b11111101;
S2: LEDs = 8'b11111011;
S3: LEDs = 8'b11110111;
S4: LEDs = 8'b11101111;
S5: LEDs = 8'b11011111;
S6: LEDs = 8'b10111111;
S7: LEDs = 8'b01111111;
default: LEDs = 8'b11111111; // Default case to turn off all LEDs if state is unknown
endcase
end
/**************** Enable jumping among different states **********************/
reg [23:0] cnt;
parameter CNT_NUM = 600000;
always @ (posedge clk) begin
if (cnt == CNT_NUM-1)
cnt <= 20'b0;
else
cnt <= cnt + 1'b1;
end
always @ (posedge clk) begin
if (cnt == CNT_NUM-1)
state <= state + 1'b1;
if (state > S7) // Reset state to S0 after reaching S7
state <= S0;
end
endmodule
This is a simple version of traffic light system that can be fully implemented by the STEPFPGA board alone. A more sophisticated and delicate traffic light system can be found in this Project for Smart Traffic Light.
/*---------------------------------------------------------------------------------------------------
*- File name: simple_traffic.v
*- Top Module name: simple_traffic
- Submodules: divider_integer
*- Description: Using a state machine to control two RGB LEDs to simulate a traffic light
*- Example of Usage:
- You may assign the output directly to the 2 RGB LEDs on the STEPFPGA board
- Reliability:
This code is intended for educational and demonstration purposes and has not been
validated for use in production systems. Users are advised to thoroughly test the
module in their specific application context.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module simple_traffic (
input clk, rst_n,
output reg [5:0] RGB_out // connects the TWO RGB lights
);
// The four states
reg [1:0] state;
parameter S1 = 2'b00,
S2 = 2'b01,
S3 = 2'b10,
S4 = 2'b11;
// RGB light, inverted logic; 111 means all OFF; 011 means R is ON, G and B are OFF
parameter led_s1 = 6'b101011, // '101' for Green, '011' for RED
led_s2 = 6'b001011, // '001' for Yellow, '011' for RED (yellow is not obvious)
led_s3 = 6'b011101, // '011' for Red, '101' for Green
led_s4 = 6'b011001; // '011' for Red, '001' for Yellow
// RGB light behaviors in each state
always @ (*) begin
case (state)
S1: RGB_out = led_s1;
S2: RGB_out = led_s2;
S3: RGB_out = led_s3;
S4: RGB_out = led_s4;
default: RGB_out = led_s1;
endcase
end
/************************************** Integer Clock Divider **********************************
>> Instantiate the clock divider module, and divide the 12MHz clock frequency by
12000000 time thus generated a 1Hz clock signal
*************************************************************************************************/
wire clk1hz;
divider_integer # (.WIDTH (24),.N (12_000_000)) u1 (
.clk (clk),
.clkout (clk1hz)
);
/*********************************** Traffic Control State Machine ****************************/
// Implementing the state machine; use the 1Hz clock signal
reg [4:0] time_cnt; // Reserve 5 bit register space for timer counter
always @ (posedge clk1hz or negedge rst_n) begin
if(!rst_n) begin
state <= S1;
time_cnt <= 0;
end
else begin
case (state)
S1: if (time_cnt < 4'd15) begin // 15s
state <= S1;
time_cnt <= time_cnt + 1;
end
else begin
state <= S2;
time_cnt <= 0;
end
S2: if (time_cnt < 4'd3) begin // 3s
state <= S2;
time_cnt <= time_cnt + 1;
end
else begin
state <= S3;
time_cnt <= 0;
end
S3: if (time_cnt < 4'd7) begin // 7s
state <= S3;
time_cnt <= time_cnt + 1;
end
else begin
state <= S4;
time_cnt <= 0;
end
S4: if (time_cnt < 4'd3) begin // 3s
state <= S4;
time_cnt <= time_cnt + 1;
end
else begin
state <= S1;
time_cnt <= 0;
end
default: begin
state <= S1;
time_cnt <= 0;
end
endcase
end
end
endmodule
/************************************** Integer Clock Divider **********************************
>> Frequency divider code; seen in Section 3.7 of the book
*************************************************************************************************/
module divider_integer # (
parameter WIDTH = 24,
parameter N = 12000000
)
(
input clk,
output reg clkout
);
reg [WIDTH-1:0] cnt;
always @ (posedge clk) begin
if(cnt>=(N-1))
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
clkout <= (cnt<N/2)?1'b1:1'b0;
end
endmodule
/*---------------------------------------------------------------------------------------------------
- File name: debounce.v
- Top Module name: debounce
- Submodules: dff, divider_integer
- Description: This module debounces a mechanical switch input to produce a stable digital output.
It uses a clock divider to slow down the main clock and then a double flip-flop
setup to filter out any bouncing effects caused by the mechanical switch.
-
- Example of Usage:
To use this debounce module, connect the 'key' input to a mechanical switch and the
'key_deb' output to a digital circuit input that requires a debounced signal. The
module uses a slow clock generated by the 'divider_integer' submodule to sample the
switch state at a lower rate to mitigate bouncing effects.
- Reliability:
This code is intended for educational and demonstration purposes and has not been
validated for use in production systems. Users are advised to thoroughly test the
module in their specific application context.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module debounce (
input clk, key,
output key_deb
);
wire slow_clk;
wire Q1,Q2,Q2_bar;
divider_integer #(.WIDTH(17),.N(240000)) U1 (
.clk(clk),
.clkout(slow_clk)
);
dff U2 (
.clk(slow_clk),
.D(key),
.Q(Q1)
);
dff U3 (
.clk(slow_clk),
.D(Q1),
.Q(Q2)
);
assign Q2_bar = ~Q2;
assign key_deb = Q1 & Q2_bar;
endmodule
/********** Submodule 1 **********/
module dff(input clk, D, output reg Q);
always @ (posedge clk) begin
Q <= D;
end
endmodule
/********** Submodule 2 **********/
module divider_integer # (
parameter N = 12000000, // the divisor
parameter WIDTH = 24 // the minimum bit-width to hold this divisor
)
(
input clk,
output reg clkout
);
reg [WIDTH-1:0] cnt;
always @ (posedge clk) begin
if(cnt>=(N-1))
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
clkout <= (cnt<N/2)?1'b1:1'b0;
end
endmodule
/*---------------------------------------------------------------------------------------------------
*- File name: SR04_display.v
*- Top Module name: SR04_display
*- Submodules: hc_sr04, bin_to_bcd, segment7
*- Description: Main display module that interfaces with the HC-SR04 sensor module,
converts the binary distance to BCD, and drives two 7-segment displays.
*-
*- Example of Usage:
- The `SR04_display` module can be instantiated within a top-level design file.
Connect the `clk`, `rst_n`, and `echo` inputs to the respective signals.
Connect the `trig`, `segment_led_1`, and `segment_led_2` to the HC-SR04 sensor
and 7-segment display inputs respectively.
*-
*- Reliability:
This code is provided 'as is', primarily for educational purposes and is not guaranteed
for any industrial or commercial applications. Users must validate the functionality
before use in any critical systems.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module SR04_display(
input clk, rst_n ,
input echo, // reads the ECHO signal from HC_SR04
output trig, // send trigger pulse to HC_SR04 module
output [8:0] segment_led_1, // 7-Segment display 1
output [8:0] segment_led_2 // 7-Segment display 2
);
hc_sr04 ultrsonic (clk, rst_n, echo, trig, distance);
wire [15:0] distance; // 16-bit binary output from SR-04 sensor
bin_to_bcd bin_to_bcd_U(rst_n, distance, bcd_distance);
wire [19:0] bcd_distance; // 19-bit BCD output from BCD module
wire [3:0] bcd_digit1;
wire [3:0] bcd_digit2;
assign bcd_digit1 = bcd_distance[7:4]; // convert smaller 4 bits BCD to binary
assign bcd_digit2 = bcd_distance[3:0]; // convert higher 4 bits BCD to binary
segment7 seg_x1( // display the first digit on Segment1
.seg_data(bcd_digit1),
.segment_led (segment_led_1)
);
segment7 seg_x10( // display the second digital on Segment2
.seg_data(bcd_digit2),
.segment_led (segment_led_2)
);
endmodule
/*******************************************************************************************************
* Module Name: hc_sr04
* Description:
* This module interfaces with the HC_SR04 ultrasonic sensor to measure distance. The primary
* operations are:
* 1. Generating a 10us pulse for triggering the HC_SR04 sensor.
* 2. Detecting the rising and falling edges of the Echo signal from the sensor.
* 3. Generating a 17kHz pulse, where each pulse corresponds to 1cm of the real distance
* measured by the HC_SR04 sensor.
* 4. Counting the number of 17kHz pulses between the rising and falling edges of the Echo signal
* to determine the distance measurement in centimeters.
- Reliability:
This code is provided 'as is', primarily for educational purposes and is not guaranteed
for any industrial or commercial applications. Users must validate the functionality
before use in any critical systems.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
********************************************************************************************************/
module hc_sr04 (
input clk, rst_n, // STPFFPGA has on board frequency of 12MHz
input echo, // Module input, connects to HC_SR04 -> echo
output trig, // Module output, connects to HC_SR04 -> trig
output reg [15:0] distance
);
/**************************************** Generate a 10us pulse ************************************
>> This piece of code generates a 10us pulse to enable trigger of the HC_SR04 sensor
******************************************************************************************************/
reg [25:0] cnt_10us; // Counter for generating 10us pulse
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
cnt_10us <= 0;
end
else if(cnt_10us == 11_999_999)
cnt_10us <= 0;
else
cnt_10us <= cnt_10us + 1'b1;
end
assign trig = (cnt_10us < 120) ? 1:0;
/********************************* Edge detection of Echo signal *****************************
>> This piece of code detects the rising edge and falling edge of Echo signal
>> when 'pose_echo' is 1, rising edge; when 'nege_echo' is 1, falling edge
*************************************************************************************************/
reg echo_2;
reg echo_1;
wire pose_echo;
wire nege_echo;
always @(posedge clk17k or negedge rst_n)begin
if(!rst_n)begin
echo_1 <= 0;
echo_2 <= 0;
end
else begin
echo_1 <= echo;
echo_2 <= echo_1;
end
end
assign pose_echo = echo_1 && (~echo_2);
assign nege_echo = (~echo_1) && echo_2;
/*********************************** Generate a 17kHz pulse **********************************
>> This piece of code generates a 17kHz pulse signal
>> By calculation (see Chapter 5 of the book), each pulse corresponds to 1cm
of the real distance measured by the HC_SR04 ultrasonic sensor.
*************************************************************************************************/
reg clk17k;
reg [15:0] cnt17k; // Counter for a 17KHz signal (explained in the book)
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt17k <= 0;
end
else if(cnt17k == 706) // divide 12MHz by 706 times will get 17kHz
cnt17k <= 0;
else
cnt17k <= cnt17k + 1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
clk17k <= 0;
else if(cnt17k < 706>>1)
clk17k <= 0;
else
clk17k <= 1;
end
/************************* Convert counter result to the actual distance ************************
>> Since we have generated a 17kHz pulse, as soon as we detected the rising edge
of the Echo, then the system starts counting how many pulses for clk17k, until a
falling edge is received to stop counting.
>> Each pulse of 'clk17k' represents 1cm in the physical measurement
*****************************************************************************************************/
parameter S0 = 2'b00; // state S0 is when rising edge of echo is detected begin of distance measurement
parameter S1 = 2'b01; // state S1 is when echo stays HIGH, and we will calculate distance in this state
parameter S2 = 2'b10; // state S2 is when falling edge of echo is detected, end of distance measurement
reg [1:0] state;
reg [15:0] cnt_dist; // Counter for final measured distance with smallest unit of 'cm'
always@(posedge clk17k or negedge rst_n)begin
if(!rst_n)begin
cnt_dist<= 0;
distance <= 0;
state <= S0;
end
else
begin
case(state)
S0:begin // detected the rising edge of the echo signal
cnt_dist <= 0; // start to count for distance
if (pose_echo)
state <= S1;
else
state <= S0;
end
S1:begin // the echo signal level stays HIGH
cnt_dist <= cnt_dist + 1; // counts for distance, each 'cnt_dist' increments 1cm
if (nege_echo)
state <= S2;
else
state <= S1;
end
S2:begin
distance <= cnt_dist; // detected the falling edge of the echo signal
cnt_dist <= 0; // save the 'cnt_dist' result for the actual distance
state <= S0;
end
default:begin // default
cnt_dist <= 0;
state <= S0;
end
endcase
end
end
endmodule
/*---------------------------------------------------------------------------------------------------
*- File name: bin_to_bcd.v
*- Top Module name: bin_to_bcd
*- Submodules: N/A
*- Description: Conversion of 16-bit binary data to 20-bit Binary Coded Decimal (BCD) representation
using the double dabble algorithm.
*- Example of Usage:
You can implement this code on all variants of STEPFPGA family boards.
If you intend to implement this module on a board and observe its operation:
- Connect a 16-bit binary input to bin_code.
- Observe the 20-bit BCD output on bcd_code.
- Utilize the rst_n signal to reset the conversion process when needed.
*- Additional comments:
This BCD converter is designed to handle up to a 16-bit binary number and convert it into a
20-bit BCD representation. It is especially useful when interfacing with displays that
require decimal number representation, such as 7-segment displays.
- Reliability:
This code is provided 'as is', primarily for educational purposes and is not guaranteed
for any industrial or commercial applications. Users must validate the functionality
before use in any critical systems.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module bin_to_bcd (
input rst_n, // Reset signal (active low)
input [15:0] bin_code,
output reg [19:0] bcd_code
);
// Internal shift register for the algorithm
reg [35:0] shift_reg;
always @(posedge bin_code or negedge rst_n) begin
if (!rst_n) begin
shift_reg <= 36'b0; // Reset the shift register on active low reset signal
end else begin
shift_reg <= {16'b0, bin_code}; // Initialize shift register with binary code at the rightmost position
// Using repeat statement for the conversion process
repeat (16) begin
if (shift_reg[3:0] > 4) shift_reg[3:0] <= shift_reg[3:0] + 3;
if (shift_reg[7:4] > 4) shift_reg[7:4] <= shift_reg[7:4] + 3;
if (shift_reg[11:8] > 4) shift_reg[11:8] <= shift_reg[11:8] + 3;
if (shift_reg[15:12] > 4) shift_reg[15:12] <= shift_reg[15:12] + 3;
if (shift_reg[19:16] > 4) shift_reg[19:16] <= shift_reg[19:16] + 3;
shift_reg <= shift_reg << 1;
end
bcd_code <= shift_reg[19:0]; // Assign the 20-bit BCD result
end
end
endmodule
/*---------------------------------------------------------------------------------------------------
*- File name: segment7.v
*- Top Module name: segment7
- Submodules: N/A
*- Description: Implementation of a 7-segment display driver (common cathod)
*-
*- Example of Usage:
You can implement this code on all variants of STEPFPGA family boards.
If you want to implement this code on board and observe the logic behaviors:
- assign seg_data[3]...seg_data[0] to the 4 on-board swiches
- assign segment_led[8] to SEG, segment_led[7] to DP
- assign segment_led[6]...segment_led[0] to 'g, f, ... a' accordingly
* - Additional comments:
If you want to display decimal numbers only, the 4-bit input data must be in BCD converted
form; will explain in Chapter 5 when implement the Elevator project.
- Reliability:
This code is provided 'as is', primarily for educational purposes and is not guaranteed
for any industrial or commercial applications. Users must validate the functionality
before use in any critical systems.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module segment7 (
input wire [3:0] seg_data, // The 4 bit input data in binary form
output reg [8:0] segment_led // 9 output for the 7-segment LEDs from MSB to LSB: SEG, DP, g, f, e, d, c, b, a
) ;
always @ (seg_data) begin
case (seg_data)
4'b0000: segment_led = 9'h3f; // 0
4'b0001: segment_led = 9'h06; // 1
4'b0010: segment_led = 9'h5b; // 2
4'b0011: segment_led = 9'h4f; // 3
4'b0100: segment_led = 9'h66; // 4
4'b0101: segment_led = 9'h6d; // 5
4'b0110: segment_led = 9'h7d; // 6
4'b0111: segment_led = 9'h07; // 7
4'b1000: segment_led = 9'h7f; // 8
4'b1001: segment_led = 9'h6f; // 9
4'b1010: segment_led = 9'h77; // A
4'b1011: segment_led = 9'h7C; // b
4'b1100: segment_led = 9'h39; // C
4'b1101: segment_led = 9'h5e; // d
4'b1110: segment_led = 9'h79; // E
4'b1111: segment_led = 9'h71; // F
endcase
end
endmodule
/*---------------------------------------------------------------------------------------------------
- File name: keypad_3by4.v
- Top Module name: keypad_3by4
- Description: Implements a 3x4 matrix keypad scanner with debouncing and 7-segment display decoding.
The module scans the keypad, debounces the keypresses, identifies the pressed key,
and translates it to a corresponding 7-segment display code.
- Example of Usage:
This module can be instantiated in a system that requires keypad interfacing with
visual feedback via a 7-segment display. Connect the rows and columns to the
respective keypad terminals and the segment_led1 output to a 7-segment display driver.
- Reliability:
This code is intended for educational and prototype purposes. For commercial
applications, thorough testing and validation are required.
- Copyright: Copyright (c) 2023 by EIM Technology
- License: MIT License
--------------------------------------------------------------------------------------------------- */
module keypad_3by4 (
input clk,
input rst_n,
input [2:0] col, // the 3 output signals for 3 Columns
output reg [3:0] row, // the 4 input signals for 4 Rows
output reg [3:0] keyPressed,
output reg [8:0] segment_led1
);
localparam NUM_FOR_200HZ = 60000; // Used to generate a 200Hz frequency for column scanning
localparam ROW0_SCAN = 2'b00; // the state when scanning first row
localparam ROW1_SCAN = 2'b01; // the state when scanning second row
localparam ROW2_SCAN = 2'b10; // the state when scanning third row
localparam ROW3_SCAN = 2'b11; // the state when scanning forth row
reg [11:0] key,key_r;
reg [11:0] key_out; // debounce all keys
reg [15:0] cnt;
reg clk_200hz;
// generate a 200Hz clock
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 16'd0;
clk_200hz <= 1'b0;
end else begin
if(cnt >= ((NUM_FOR_200HZ>>1) - 1)) begin // >>1 means divide by 2
cnt <= 16'd0;
clk_200hz <= ~clk_200hz;
end else begin
cnt <= cnt + 1'b1;
clk_200hz <= clk_200hz;
end
end
end
reg [1:0] c_state;
always@(posedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
c_state <= ROW0_SCAN;
row <= 4'b1110;
end else begin
case(c_state)
ROW0_SCAN: begin c_state <= ROW1_SCAN; row <= 4'b1101; end
ROW1_SCAN: begin c_state <= ROW2_SCAN; row <= 4'b1011; end
ROW2_SCAN: begin c_state <= ROW3_SCAN; row <= 4'b0111; end
ROW3_SCAN: begin c_state <= ROW0_SCAN; row <= 4'b1110; end
default:begin c_state <= ROW0_SCAN; row <= 4'b1110; end
endcase
end
end
always@(negedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
key_out <= 12'hfff;
end else begin
case(c_state)
ROW0_SCAN: begin // check for colum 0, 1, 2
key[2:0] <= col;
key_r[2:0] <= key[2:0];
key_out[2:0] <= key_r[2:0]|key[2:0]; // double comfirm the pressed key
end
ROW1_SCAN:begin // check for colum 3, 4, 5
key[5:3] <= col;
key_r[5:3] <= key[5:3];
key_out[5:3] <= key_r[5:3]|key[5:3]; // double comfirm the pressed key
end
ROW2_SCAN:begin
key[8:6] <= col; // check for colum 6, 7, 8
key_r[8:6] <= key[8:6];
key_out[8:6] <= key_r[8:6]|key[8:6]; // double comfirm the pressed key
end
ROW3_SCAN:begin
key[11:9] <= col; // check for colum 9, 10, 11
key_r[11:9] <= key[11:9];
key_out[11:9] <= key_r[11:9]|key[11:9]; // double comfirm the pressed key
end
default:key_out <= 12'hfff;
endcase
end
end
reg [3:0] key_code;
reg [11:0] key_out_r;
wire [11:0] key_pulse;
always @ ( posedge clk or negedge rst_n )begin
if (!rst_n) key_out_r <= 12'hfff;
else key_out_r <= key_out;
end
assign key_pulse= key_out_r & (~key_out);
always@(*)begin
case(key_pulse)
12'b0000_0000_0001: key_code=4'd1 ; // key 1
12'b0000_0000_0010: key_code=4'd2 ; // key 2
12'b0000_0000_0100: key_code=4'd3 ; // key 3
12'b0000_0000_1000: key_code=4'd4 ; // key 4
12'b0000_0001_0000: key_code=4'd5 ; // key 5
12'b0000_0010_0000: key_code=4'd6 ; // key 6
12'b0000_0100_0000: key_code=4'd7 ; // key 7
12'b0000_1000_0000: key_code=4'd8 ; // key 8
12'b0001_0000_0000: key_code=4'd9 ; // key 9
12'b0010_0000_0000: key_code=4'd10; // key *
12'b0100_0000_0000: key_code=4'd0 ; // key 0
12'b1000_0000_0000: key_code=4'd12; // key #
default: key_code=4'd15;
endcase
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n) keyPressed <= 4'd15;
else keyPressed<=key_code;
end
always@(posedge clk)begin
case(keyPressed)
4'd0: begin segment_led1 <=9'h3f; end
4'd1: begin segment_led1 <=9'h06; end
4'd2: begin segment_led1 <=9'h5b; end
4'd3: begin segment_led1 <=9'h4f; end
4'd4: begin segment_led1 <=9'h66; end
4'd5: begin segment_led1 <=9'h6d; end
4'd6: begin segment_led1 <=9'h7d; end
4'd7: begin segment_led1 <=9'h07; end
4'd8: begin segment_led1 <=9'h7f; end
4'd9: begin segment_led1 <=9'h6f; end
4'd10: begin segment_led1 <=9'h77; end
4'd12: begin segment_led1 <=9'h39; end
default:begin segment_led1<=segment_led1; end
endcase
end
endmodule
//---------------------------------------------------------------------------------------------------
// File name: RotEncoder.v
// Top Module name: RotEncoder
// Submodules: None
// Description: This module detects and processes rotary encoder signals to generate clean
// pulses indicating clockwise (CW) and counterclockwise (CCW) movements.
// It includes debouncing for the encoder's A and B outputs and detects the
// edges to determine the direction of rotation.
// This code has also included the "Soft debouncing" algorithm so no external
// filtering capacitors are needed
//
// Example of Usage:
// Connect 'key_A' and 'key_B' to the respective outputs of a rotary encoder,
// 'CC_pulse' will go high for one clock cycle on a detected clockwise rotation,
// and 'CCW_pulse' will go high for one clock cycle on a counterclockwise rotation.
// Use these signals to increment or decrement a counter or to navigate through
// a menu interface.
//
// Reliability:
// This code is intended for educational and demonstration purposes and has not been
// validated for use in production systems. Users are advised to thoroughly test the
// module in their specific application context.
//
// Copyright: Copyright (c) 2023 by EIM Technology
// License: MIT License
//---------------------------------------------------------------------------------------------------
module RotEncoder (
input clk, rst_n,
input key_A, key_B,
output reg CC_pulse, CCW_pulse
);
localparam NUM_250US = 3_000;
/************* Eliminate the glitches unstable signals during movement ********/
reg [12:0] cnt; //
//count for clk_500us //
always@(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 0;
else if(cnt >= NUM_250US-1) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
end
reg clk_500us;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) clk_500us <= 0;
else if(cnt == NUM_250US-1) clk_500us <= ~clk_500us;
else clk_500us <= clk_500us;
end
reg key_A_r,key_A_r1,key_A_r2;
always@(posedge clk_500us) begin
key_A_r <= key_A;
key_A_r1 <= key_A_r;
key_A_r2 <= key_A_r1;
end
reg A_state;
always@(key_A_r1 or key_A_r2) begin
case({key_A_r1,key_A_r2})
2'b11: A_state <= 1'b1;
2'b00: A_state <= 1'b0;
default: A_state <= A_state;
endcase
end
reg key_B_r,key_B_r1,key_B_r2;
always@(posedge clk_500us) begin
key_B_r <= key_B;
key_B_r1 <= key_B_r;
key_B_r2 <= key_B_r1;
end
reg B_state;
always@(key_B_r1 or key_B_r2) begin
case({key_B_r1,key_B_r2})
2'b11: B_state <= 1'b1;
2'b00: B_state <= 1'b0;
default: B_state <= B_state;
endcase
end
/*************************************************************************************/
// Detects the edge transition of Signal A on Rotary Encoder
reg A_state_r,A_state_r1;
always@(posedge clk) begin
A_state_r <= A_state;
A_state_r1 <= A_state_r;
end
wire A_pos = (!A_state_r1) && A_state_r;
wire A_neg = A_state_r1 && (!A_state_r);
// If A is posedge and B is HIGH, or if A is falling edge and B is LOW, then clockwise
always@(posedge clk or negedge rst_n) begin
if(!rst_n) CC_pulse <= 1'b0;
else if((A_pos&&B_state)||(A_neg&&(!B_state))) CC_pulse <= 1'b1;
else CC_pulse <= 1'b0;
end
// If A is posedge and B is LOW, or if A is falling edge and B is HIGH, then counterclockwise
always@(posedge clk or negedge rst_n) begin
if(!rst_n) CCW_pulse <= 1'b0;
else if((A_pos&&(!B_state))||(A_neg&&B_state)) CCW_pulse <= 1'b1;
else CCW_pulse <= 1'b0;
end
endmodule
//---------------------------------------------------------------------------------------------------
// File name: motorSpeedCtrl.v
// Top Module name: motorSpeedCtrl
// Submodules: RotEncoder, pwm_ctrl
// Description: This module interfaces with a rotary encoder to control motor speed using PWM.
// It utilizes the 'RotEncoder' submodule to interpret rotary encoder inputs for
// rotational direction, and adjusts the PWM signal accordingly to increase or
// decrease the motor speed using the 'pwm_ctrl' submodule.
//
// Example of Usage:
// Connect the rotary encoder's outputs to 'key_A' and 'key_B'. The 'pwm_out'
// should be connected to the motor controller's PWM input. Rotating the encoder
// clockwise will increase the motor speed, while rotating it counterclockwise
// will decrease the speed.
//
// Reliability:
// The module is designed for educational purposes and prototyping. For production
// use, it should be rigorously tested, especially in safety-critical applications.
//
// Copyright: Copyright (c) 2023 by EIM Technology
// License: MIT License
//---------------------------------------------------------------------------------------------------
module motorSpeedCtrl (
input clk,
input rst_n,
input key_A, key_B,
output pwm_out
);
// Intermediate signals
wire CC_pulse, CCW_pulse;
RotEncoder encoder (
.clk(clk),
.rst_n(rst_n),
.key_A(key_A),
.key_B(key_B),
.CC_pulse(CC_pulse),
.CCW_pulse(CCW_pulse)
);
pwm_ctrl pwmGenerator (
.clk(clk),
.speedUP(CC_pulse),
.speedDOWN(CCW_pulse),
.pwm_out(pwm_out)
);
endmodule
//---------------------------------------------------------------------------------------------------
// File name: pwm_ctrl.v
// Top Module name: pwm_ctrl
// Parameters: PWM_FREQUENCY, DUTY_STEP
// Description: This module generates a Pulse Width Modulation (PWM) signal to control devices
// like motors or LEDs. The PWM frequency and duty cycle step increment are
// configurable via parameters. The duty cycle can be increased or decreased
// dynamically using the 'speedUP' and 'speedDOWN' inputs.
//
// Example of Usage:
// Instantiate this module and connect the 'speedUP' input to a source that
// indicates the need to increase the duty cycle, and 'speedDOWN' to decrease it.
// The generated PWM signal at 'pwm_out' can then be used to control the speed
// of a motor or the brightness of an LED, for example.
//
// Reliability:
// This module is provided as-is for demonstration purposes and should be tested
// thoroughly within its intended application and environment before any production
// use, especially in systems where precise timing and response are critical.
//
// Copyright: Copyright (c) 2023 by EIM Technology
// License: MIT License
//---------------------------------------------------------------------------------------------------
module pwm_ctrl #(
parameter PWM_FREQUENCY = 1000,
parameter DUTY_STEP = 5
)
(
input clk,
input speedUP,
input speedDOWN,
output reg pwm_out
);
localparam duty_INIT = 20;
localparam TOTAL_COUNT = 12000000 / PWM_FREQUENCY;
reg [31:0] counter = 0;
reg [7:0] duty_cycle = duty_INIT;
localparam MAX_DUTY = 100;
localparam MIN_DUTY = 0;
// Calculate counts for the high duration
wire [31:0] HIGH_COUNT = (TOTAL_COUNT * duty_cycle) / 100;
always @(posedge clk) begin
if(speedUP) begin
// Increase duty cycle if it doesn't exceed max limit
if(duty_cycle + DUTY_STEP <= MAX_DUTY) begin
duty_cycle <= duty_cycle + DUTY_STEP;
end else begin
duty_cycle <= MAX_DUTY;
end
end
if(speedDOWN) begin
// Decrease duty cycle if it doesn't go below min limit
if(duty_cycle - DUTY_STEP >= MIN_DUTY) begin
duty_cycle <= duty_cycle - DUTY_STEP;
end else begin
duty_cycle <= MIN_DUTY;
end
end
if(counter < HIGH_COUNT) begin
pwm_out <= 1'b1;
end else begin
pwm_out <= 1'b0;
end
if(counter == TOTAL_COUNT-1) begin
counter <= 0;
end else begin
counter <= counter + 1;
end
end
endmodule
/*---------------------------------------------------------------------------------------------------
*- File name: RotEncoder.v
*- Top Module name: RotEncoder
- Submodules:
*- Description: Interface with an incremental rotary encoder
*- Example of Usage:
- This code interfaces with an incremental rotary encoder. Connect Key_A and Key_B to
the two phase outputs of the encoder; you can also add additional input to interface with
the OK Press function of the encoder which is essentially a pushbutton.
CC_pulse and CCW_pulse indicates for rotating orientations.
*- Copyright of this code: MIT License
--------------------------------------------------------------------------------------------------- */
module RotEncoder (
input clk, rst_n,
input key_A, key_B,
output reg CC_pulse, CCW_pulse
);
localparam NUM_250US = 3_000;
/************* Eliminate the glitches unstable signals during movement ********/
reg [12:0] cnt; //
//count for clk_500us //
always@(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 0;
else if(cnt >= NUM_250US-1) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
end
reg clk_500us;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) clk_500us <= 0;
else if(cnt == NUM_250US-1) clk_500us <= ~clk_500us;
else clk_500us <= clk_500us;
end
reg key_A_r,key_A_r1,key_A_r2;
always@(posedge clk_500us) begin
key_A_r <= key_A;
key_A_r1 <= key_A_r;
key_A_r2 <= key_A_r1;
end
reg A_state;
always@(key_A_r1 or key_A_r2) begin
case({key_A_r1,key_A_r2})
2'b11: A_state <= 1'b1;
2'b00: A_state <= 1'b0;
default: A_state <= A_state;
endcase
end
reg key_B_r,key_B_r1,key_B_r2;
always@(posedge clk_500us) begin
key_B_r <= key_B;
key_B_r1 <= key_B_r;
key_B_r2 <= key_B_r1;
end
reg B_state;
always@(key_B_r1 or key_B_r2) begin
case({key_B_r1,key_B_r2})
2'b11: B_state <= 1'b1;
2'b00: B_state <= 1'b0;
default: B_state <= B_state;
endcase
end
/*************************************************************************************/
// Detects the edge transition of Signal A on Rotary Encoder
reg A_state_r,A_state_r1;
always@(posedge clk) begin
A_state_r <= A_state;
A_state_r1 <= A_state_r;
end
wire A_pos = (!A_state_r1) && A_state_r;
wire A_neg = A_state_r1 && (!A_state_r);
// If A is posedge and B is HIGH, or if A is falling edge and B is LOW, then clockwise
always@(posedge clk or negedge rst_n) begin
if(!rst_n) CC_pulse <= 1'b0;
else if((A_pos&&B_state)||(A_neg&&(!B_state))) CC_pulse <= 1'b1;
else CC_pulse <= 1'b0;
end
// If A is posedge and B is LOW, or if A is falling edge and B is HIGH, then counterclockwise
always@(posedge clk or negedge rst_n) begin
if(!rst_n) CCW_pulse <= 1'b0;
else if((A_pos&&(!B_state))||(A_neg&&B_state)) CCW_pulse <= 1'b1;
else CCW_pulse <= 1'b0;
end
endmodule
//---------------------------------------------------------------------------------------------------
// File name: servoEncoder.v
// Top Module name: servoEncoder
// Submodules: RotEncoder, servoCtrl
// Description: This module interfaces with a rotary encoder to control the angle of a servo motor.
// It uses the signals from the incremental rotary encoder to determine the direction of the
// rotation and accordingly adjust the servo angle. The angle value is then used to
// generate the corresponding PWM signal to drive the servo to the desired position.
//
// Example of Usage:
// Instantiate the servoEncoder module, connect the encoder inputs to 'key_A' and 'key_B', and use
// the 'servo_pwm' signal to drive a servo motor. The motor angle is adjusted via the rotary encoder.
//
// Reliability:
// The provided code is intended for educational and prototype use. It has not been tested for all
// edge cases and operational scenarios. Users should perform rigorous validation for any
// mission-critical applications.
//
// Copyright: Copyright (c) 2023 by EIM Technology
// License: MIT License
//---------------------------------------------------------------------------------------------------
module servoEncoder (
input clk, rst_n,
input key_A, key_B,
output servo_pwm
);
wire CC_pulse, CCW_pulse;
reg [7:0] rotate_angle = 8'd90;
RotEncoder U1 (
.clk(clk),
.rst_n(rst_n),
.key_A(key_A),
.key_B(key_B),
.CC_pulse(CC_pulse),
.CCW_pulse(CCW_pulse)
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rotate_angle <= 8'd90; // Reset to mid-point on reset
end else begin
if (CC_pulse) begin
if (rotate_angle < 179)
rotate_angle <= rotate_angle + 1;
end
if (CCW_pulse) begin
if (rotate_angle > 0)
rotate_angle <= rotate_angle - 1;
end
end
end
servoCtrl U2 (
.clk(clk),
.rst_n(rst_n),
.rotate_angle(rotate_angle),
.servo_pwm(servo_pwm)
);
endmodule
module RotEncoder (
input clk, rst_n,
input key_A, key_B,
output reg CC_pulse, CCW_pulse
);
localparam NUM_250US = 3_000;
/************* Eliminate the glitches unstable signals during movement ********/
reg [12:0] cnt;
//count for clk_500us
always@(posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 0;
else if(cnt >= NUM_250US-1) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
end
reg clk_500us;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) clk_500us <= 0;
else if(cnt == NUM_250US-1) clk_500us <= ~clk_500us;
else clk_500us <= clk_500us;
end
reg key_A_r,key_A_r1,key_A_r2;
always@(posedge clk_500us) begin
key_A_r <= key_A;
key_A_r1 <= key_A_r;
key_A_r2 <= key_A_r1;
end
reg A_state;
always@(key_A_r1 or key_A_r2) begin
case({key_A_r1,key_A_r2})
2'b11: A_state <= 1'b1;
2'b00: A_state <= 1'b0;
default: A_state <= A_state;
endcase
end
reg key_B_r,key_B_r1,key_B_r2;
always@(posedge clk_500us) begin
key_B_r <= key_B;
key_B_r1 <= key_B_r;
key_B_r2 <= key_B_r1;
end
reg B_state;
always@(key_B_r1 or key_B_r2) begin
case({key_B_r1,key_B_r2})
2'b11: B_state <= 1'b1;
2'b00: B_state <= 1'b0;
default: B_state <= B_state;
endcase
end
/*************************************************************************************/
// Detects the edge transition of Signal A on Rotary Encoder
reg A_state_r,A_state_r1;
always@(posedge clk) begin
A_state_r <= A_state;
A_state_r1 <= A_state_r;
end
wire A_pos = (!A_state_r1) && A_state_r;
wire A_neg = A_state_r1 && (!A_state_r);
// If A is posedge and B is HIGH, or if A is falling edge and B is LOW, then clockwise
always@(posedge clk or negedge rst_n) begin
if(!rst_n) CC_pulse <= 1'b0;
else if((A_pos&&B_state)||(A_neg&&(!B_state))) CC_pulse <= 1'b1;
else CC_pulse <= 1'b0;
end
// If A is posedge and B is LOW, or if A is falling edge and B is HIGH, then counterclockwise
always@(posedge clk or negedge rst_n) begin
if(!rst_n) CCW_pulse <= 1'b0;
else if((A_pos&&(!B_state))||(A_neg&&B_state)) CCW_pulse <= 1'b1;
else CCW_pulse <= 1'b0;
end
endmodule
//---------------------------------------------------------------------------------------------------
// File name: servoCtrl.v
// Module name: servoCtrl
// Description: This module generates a Pulse Width Modulation (PWM) signal to control a servo
// motor based on the desired rotation angle provided as an input. The PWM signal
// corresponds to the conventional 20ms servo control cycle, with the duty cycle
// adjusted to represent the angle between 0 and 180 degrees.
//
// Reliability:
// This code is intended for educational and demonstration purposes and should be tested for
// specific applications before production use.
//
// Copyright: Copyright (c) 2023 by EIM Technology
// License: MIT License
//---------------------------------------------------------------------------------------------------
module servoCtrl(
input clk, rst_n,
input [7:0] rotate_angle,
output reg servo_pwm
);
localparam CNT_20MS = 240_000;
localparam pulse_min = 6000;
localparam pulse_max = 30000;
localparam tick_Degree = (pulse_max - pulse_min) / 180;
reg [17:0] cnt_20ms;
reg [17:0] pulse_width;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_20ms <= 0;
end else if(cnt_20ms >= CNT_20MS - 1) begin
cnt_20ms <= 0;
end else begin
cnt_20ms <= cnt_20ms + 1;
end
end
always @(posedge clk) begin
if(rotate_angle > 180) begin
pulse_width <= pulse_max;
end else begin
pulse_width <= pulse_min + (rotate_angle * tick_Degree);
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
servo_pwm <= 1'b0;
end else begin
servo_pwm <= (cnt_20ms < pulse_width) ? 1'b1 : 1'b0;
end
end
endmodule
//---------------------------------------------------------------------------------------------------
// File name: servoScan.v
// Module name: servoScan
// Description: This module controls a servo to sweep back and forth across its full range. It
// gradually increments or decrements the servo angle to create a scanning motion.
// A delay counter is used to provide time for the servo to reach each position
// before moving to the next.
//
// Usage Example:
// - Connect the servoScan module to a higher level controller that provides a clock and reset
// signal, and connect the servo_pwm output to a servo motor.
//
// Reliability:
// - This code is for educational and demonstration purposes and should be validated for each
// specific application.
//
// Copyright: Copyright (c) 2023 by EIM Technology
// License: MIT License
//---------------------------------------------------------------------------------------------------
module servoScan(
input clk,
input rst_n,
output servo_pwm
);
reg [7:0] angle; // Servo angle
reg dir; // Direction of scan: 1 for increasing, 0 for decreasing
reg [15:0] delay_counter; // Delay counter to provide some time for the servo to reach a position
// Instantiate the servo control module
servoControl servoCtrl (
.clk(clk),
.rst_n(rst_n),
.rotate_angle(angle),
.servo_pwm(servo_pwm)
);
// Sweep logic
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
angle <= 8'd0; // Start at 0 degrees
dir <= 1'b1; // Start in increasing direction
delay_counter <= 16'd0; // Reset delay counter
end else begin
if (delay_counter == 16'd60000) begin // Delay to allow servo to reach position
delay_counter <= 16'd0; // Reset delay counter
if (dir) begin
if (angle < 8'd179) angle <= angle + 1;
else dir <= 1'b0;
end else begin
if (angle > 8'd0) angle <= angle - 1;
else dir <= 1'b1;
end
end else begin
delay_counter <= delay_counter + 1;
end
end
end
endmodule
module servoControl(
input clk,
input rst_n,
input [7:0] rotate_angle,
output reg servo_pwm
);
// Local parameters for timing calculations
localparam CNT_20MS = 240_000;
localparam PULSE_MIN = 6_000;
localparam PULSE_MAX = 30_000;
localparam DEG_TICK = (PULSE_MAX - PULSE_MIN) / 180;
// Counters
reg [17:0] cnt_20ms;
reg [17:0] pulse_width;
// 20ms period counter
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_20ms <= 0;
end else if(cnt_20ms >= CNT_20MS - 1) begin
cnt_20ms <= 0;
end else begin
cnt_20ms <= cnt_20ms + 1;
end
end
// Calculate pulse width based on desired angle
always @(posedge clk) begin
if(rotate_angle > 180) begin
pulse_width <= PULSE_MAX;
end else begin
pulse_width <= PULSE_MIN + (rotate_angle * DEG_TICK);
end
end
// Generate PWM signal
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
servo_pwm <= 1'b0;
end else begin
servo_pwm <= (cnt_20ms < pulse_width) ? 1'b1 : 1'b0;
end
end
endmodule