Files
DESD/LAB3/src/digilent_jstk2.vhd
Davide dff2eb439d - update comments
- add led_level_controller Const
2025-06-03 14:55:23 +02:00

199 lines
9.5 KiB
VHDL

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
-- Entity: digilent_jstk2
-- Purpose: Interface controller for the Digilent JSTK2 joystick module via SPI
-- Sends LED color commands and receives joystick/button data
ENTITY digilent_jstk2 IS
GENERIC (
DELAY_US : INTEGER := 25; -- Delay (in microseconds) between two SPI packets
CLKFREQ : INTEGER := 100_000_000; -- Frequency of the aclk signal (in Hz)
SPI_SCLKFREQ : INTEGER := 5_000 -- Frequency of the SPI SCLK clock signal (in Hz)
);
PORT (
aclk : IN STD_LOGIC; -- Main clock input
aresetn : IN STD_LOGIC; -- Active-low asynchronous reset
-- AXI4-Stream Master Interface: Data going TO the SPI IP-Core (and so, to the JSTK2 module)
m_axis_tvalid : OUT STD_LOGIC; -- Output data valid signal
m_axis_tdata : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- 8-bit data to send via SPI
m_axis_tready : IN STD_LOGIC; -- SPI IP-Core ready to accept data
-- AXI4-Stream Slave Interface: Data coming FROM the SPI IP-Core (and so, from the JSTK2 module)
-- Note: There is no tready signal, so you must be always ready to accept incoming data, or it will be lost!
s_axis_tvalid : IN STD_LOGIC; -- Input data valid signal
s_axis_tdata : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- 8-bit data received via SPI
-- Joystick and button values read from the JSTK2 module
jstk_x : OUT STD_LOGIC_VECTOR(9 DOWNTO 0); -- X-axis joystick position (10-bit, 0-1023)
jstk_y : OUT STD_LOGIC_VECTOR(9 DOWNTO 0); -- Y-axis joystick position (10-bit, 0-1023)
btn_jstk : OUT STD_LOGIC; -- Joystick button state (1=pressed)
btn_trigger : OUT STD_LOGIC; -- Trigger button state (1=pressed)
-- LED color values to send to the JSTK2 module
led_r : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- Red LED intensity (0-255)
led_g : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- Green LED intensity (0-255)
led_b : IN STD_LOGIC_VECTOR(7 DOWNTO 0) -- Blue LED intensity (0-255)
);
END digilent_jstk2;
ARCHITECTURE Behavioral OF digilent_jstk2 IS
-- Command code for the SetLEDRGB command, see the JSTK2 datasheet
CONSTANT CMDSETLEDRGB : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"84";
-- Calculate delay in clock cycles: (delay_period + 1_SPI_clock_period) * clock_frequency
-- This ensures proper timing between SPI packets as required by JSTK2 datasheet
CONSTANT DELAY_CLK_CYCLES : INTEGER := (DELAY_US + 1_000_000 / SPI_SCLKFREQ) * (CLKFREQ / 1_000_000) - 1;
-- State machine type definitions
TYPE tx_state_type IS (DELAY, SEND_CMD, SEND_RED, SEND_GREEN, SEND_BLUE, SEND_DUMMY);
TYPE rx_state_type IS (JSTK_X_LOW, JSTK_X_HIGH, JSTK_Y_LOW, JSTK_Y_HIGH, BUTTONS);
-- State machine signals
SIGNAL tx_state : tx_state_type := DELAY; -- Transmit state machine current state
SIGNAL rx_state : rx_state_type := JSTK_X_LOW; -- Receive state machine current state
-- Timing and data storage signals
SIGNAL tx_delay_counter : INTEGER RANGE 0 TO DELAY_CLK_CYCLES := 0; -- Counter for inter-packet delay timing
SIGNAL rx_cache : STD_LOGIC_VECTOR(s_axis_tdata'range); -- Temporary storage for multi-byte data reception
BEGIN
-- Output valid signal control: Set to '1' when we want to send data to SPI IP-Core
-- Only inactive during DELAY state when waiting between packets
WITH tx_state SELECT m_axis_tvalid <=
'0' WHEN DELAY, -- No transmission during delay
'1' WHEN SEND_CMD, -- Send command byte
'1' WHEN SEND_RED, -- Send red LED value
'1' WHEN SEND_GREEN, -- Send green LED value
'1' WHEN SEND_BLUE, -- Send blue LED value
'1' WHEN SEND_DUMMY; -- Send dummy byte to complete transaction
-- Output data multiplexer: Select what data to send based on current TX state
WITH tx_state SELECT m_axis_tdata <=
(OTHERS => '0') WHEN DELAY, -- No data during delay
CMDSETLEDRGB WHEN SEND_CMD, -- SetLEDRGB command (0x84)
led_r WHEN SEND_RED, -- Red LED intensity value
led_g WHEN SEND_GREEN, -- Green LED intensity value
led_b WHEN SEND_BLUE, -- Blue LED intensity value
"01101001" WHEN SEND_DUMMY; -- Dummy byte to complete 5-byte transaction
-- TX State Machine: Sends LED color commands to JSTK2 module
-- Protocol: Command(1) + Red(1) + Green(1) + Blue(1) + Dummy(1) = 5 bytes total > Delay before next command
-- The delay is required by the JSTK datasheet to ensure proper timing between SPI transactions
TX : PROCESS (aclk)
BEGIN
IF rising_edge(aclk) THEN
IF aresetn = '0' THEN
-- Reset: Start in delay state with counter cleared
tx_state <= DELAY;
tx_delay_counter <= 0;
ELSE
CASE tx_state IS
WHEN DELAY =>
-- Wait for required delay period between SPI transactions
IF tx_delay_counter = DELAY_CLK_CYCLES THEN
tx_delay_counter <= 0; -- Reset counter
tx_state <= SEND_CMD; -- Start new transmission
ELSE
tx_delay_counter <= tx_delay_counter + 1; -- Continue counting
END IF;
WHEN SEND_CMD =>
-- Send SetLEDRGB command byte
IF m_axis_tready = '1' THEN
tx_state <= SEND_RED; -- Move to red LED transmission
END IF;
WHEN SEND_RED =>
-- Send red LED intensity value
IF m_axis_tready = '1' THEN
tx_state <= SEND_GREEN; -- Move to green LED transmission
END IF;
WHEN SEND_GREEN =>
-- Send green LED intensity value
IF m_axis_tready = '1' THEN
tx_state <= SEND_BLUE; -- Move to blue LED transmission
END IF;
WHEN SEND_BLUE =>
-- Send blue LED intensity value
IF m_axis_tready = '1' THEN
tx_state <= SEND_DUMMY; -- Move to dummy byte transmission
END IF;
WHEN SEND_DUMMY =>
-- Send dummy byte to complete 5-byte transaction
IF m_axis_tready = '1' THEN
tx_state <= DELAY; -- Return to delay state
END IF;
END CASE;
END IF;
END IF;
END PROCESS TX;
-- RX State Machine: Receives 5 bytes of response data and updates outputs
-- Protocol: X_low(1) + X_high(1) + Y_low(1) + Y_high(1) + Buttons(1) = 5 bytes total
RX : PROCESS (aclk)
BEGIN
IF rising_edge(aclk) THEN
IF aresetn = '0' THEN
-- Reset: Start waiting for X-axis low byte
rx_state <= JSTK_X_LOW;
rx_cache <= (OTHERS => '0');
ELSE
CASE rx_state IS
WHEN JSTK_X_LOW =>
-- Receive X-axis low byte (bits 7:0)
IF s_axis_tvalid = '1' THEN
rx_cache <= s_axis_tdata; -- Store low byte temporarily
rx_state <= JSTK_X_HIGH; -- Wait for high byte
END IF;
WHEN JSTK_X_HIGH =>
-- Receive X-axis high byte (bits 9:8) and assemble complete X value
IF s_axis_tvalid = '1' THEN
-- Combine: high_byte(1:0) & low_byte(7:0) = 10-bit X position
jstk_x <= s_axis_tdata(1 DOWNTO 0) & rx_cache;
rx_state <= JSTK_Y_LOW; -- Move to Y-axis reception
END IF;
WHEN JSTK_Y_LOW =>
-- Receive Y-axis low byte (bits 7:0)
IF s_axis_tvalid = '1' THEN
rx_cache <= s_axis_tdata; -- Store low byte temporarily
rx_state <= JSTK_Y_HIGH; -- Wait for high byte
END IF;
WHEN JSTK_Y_HIGH =>
-- Receive Y-axis high byte (bits 9:8) and assemble complete Y value
IF s_axis_tvalid = '1' THEN
-- Combine: high_byte(1:0) & low_byte(7:0) = 10-bit Y position
jstk_y <= s_axis_tdata(1 DOWNTO 0) & rx_cache;
rx_state <= BUTTONS; -- Move to button reception
END IF;
WHEN BUTTONS =>
-- Receive button states byte
IF s_axis_tvalid = '1' THEN
btn_jstk <= s_axis_tdata(0); -- Joystick button (bit 0)
btn_trigger <= s_axis_tdata(1); -- Trigger button (bit 1)
rx_state <= JSTK_X_LOW; -- Return to start for next packet
END IF;
END CASE;
END IF;
END IF;
END PROCESS RX;
END ARCHITECTURE;