基于VHDL的SPI通讯协议

一、SPI通讯协议:

        先聊点别人说过的,老生常谈的内容,(关键点和代码再后面):

1. 主从架构与信号线定义

SPI(Serial Peripheral Interface)是一种全双工同步串行通信协议,采用主从模式,包含以下四根信号线

  • SCLK(Serial Clock):主设备生成的时钟信号,控制数据传输速率和时序。
  • MOSI(Master Out Slave In):主设备输出数据线,从设备输入。
  • MISO(Master In Slave Out):主设备输入数据线,从设备输出。
  • CS/SS(Chip Select/Slave Select):片选信号,主设备通过拉低该信号选中特定从设备。
2. 同步通信机制
  • 时钟同步:所有数据传输由SCLK驱动,主设备通过时钟边沿(上升沿或下降沿)控制数据的采样与发送时机。
  • 全双工特性:主从设备可同时通过MOSI和MISO线发送和接收数据,每个时钟周期完成1位数据的双向传输。
3. 四种工作模式

SPI通过**时钟极性(CPOL)时钟相位(CPHA)**的组合定义四种工作模式

  • 模式0(CPOL=0, CPHA=0):空闲时SCLK为低电平,数据在上升沿采样,下降沿发送。
  • 模式1(CPOL=0, CPHA=1):空闲时SCLK为低电平,数据在下降沿采样,上升沿发送。
  • 模式2(CPOL=1, CPHA=0):空闲时SCLK为高电平,数据在下降沿采样,上升沿发送。
  • 模式3(CPOL=1, CPHA=1):空闲时SCLK为高电平,数据在上升沿采样,下降沿发送。

    二、SPI通信过程详解

    1. 通信流程步骤
  • 片选激活:主设备拉低目标从设备的CS信号线,启动通信。
  • 时钟生成:主设备产生SCLK信号,根据CPOL和CPHA设定时钟边沿行为。
  • 数据传输
    • 主→从:主设备通过MOSI线逐位发送数据,从设备在指定时钟边沿采样。
    • 从→主:从设备通过MISO线返回数据,主设备同步接收。
    • 移位寄存器机制:数据通过主从设备的移位寄存器实现同步交换。
  • 片选释放:传输完成后,主设备拉高CS信号,结束本次通信。
  • 硬件设计:每个从设备需独立的CS信号线,主设备通过译码器(如4-16译码器)扩展片选能力。
    • 时序冲突避免:同一时间仅一个从设备被选中,防止数据线冲突。        ​​​​​​​        ​​​​​​​        ​​​​​​​       三、注意事项
    • 1.最重要的是第一个CLK,时钟信号的产生一定是慢于数据信号的,但是不能太慢,保证第一个变化沿的时候能正常采数。
      • 2.如果涉及到与外设通讯,或者MCU通讯功能建议在FPGA端使用异步时钟,保证时常。                                                                                                                              

                四、程序代码

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
--USE IEEE.STD_LOGIC_signed.ALL;
use WORK.ChangeWorldCtrolBoard_PACKAGE.ALL;
-------------------------------------------------------------------------------------------
ENTITY SPI1 IS
	GENERIC(CPHA: INTEGER := 1;CPOL: INTEGER := 1);--模式3空闲高电平,前沿输出后沿采样
	PORT(
		--系统时钟与复位
		sysclk		: IN 	STD_LOGIC;--50MHZ
		sysreset	: IN 	STD_LOGIC;
		
			
		--SPI物理接口
		CS1		: OUT 	STD_LOGIC;--片选信号
		sclk	: OUT 	STD_LOGIC;--串行时钟
		miso	: IN 	STD_LOGIC;--主机输入从机输出
		mosi	: OUT 	STD_LOGIC;--主机输出从机输入
		
		--SPI其他接口
		SenddataTrig	: IN 	STD_LOGIC;
		Senddata		: IN 	std_logic_vector(7 downto 0);
		Send_clear		: IN 	STD_LOGIC;
		CS				: IN 	STD_LOGIC;
		Getdata			: OUT 	std_logic_vector(7 downto 0);
		GetdataTrig		: OUT 	STD_LOGIC

	);
END ENTITY SPI1;

	ARCHITECTURE SPI1 OF SPI1 IS 
	CONSTANT SCLK_NUM: INTEGER := 12;--对系统时钟进行16分频变为2500KHZ(40M)
	CONSTANT SCLK_BEFORE: INTEGER := 2;
	CONSTANT SCLK_AFTER: INTEGER := 6;
	TYPE state IS(IDLE,WAIT_L,DATA,DONE);
	SIGNAL state_c,state_n: state;
	SIGNAL bit_cnt,get_cnt: INTEGER RANGE 0 TO 7;--bit计数器
	SIGNAL sclk_cnt: INTEGER RANGE 0 TO 15;--时钟计数器
	SIGNAL tx_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
	SIGNAL rx_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
	signal t,t_l,tmp,cs_io:STD_LOGIC;
	
	--与总线的交互接口
	signal	din		: 	STD_LOGIC_VECTOR(7 DOWNTO 0);
	signal	req		: 	STD_LOGIC;
	signal	dout    :  	STD_LOGIC_VECTOR(7 DOWNTO 0);
	signal	dout_vld	:  	STD_LOGIC;
	
	--fifo信号
	signal	FIFO_Data_send,FIFO_Data_get:		std_logic_vector(7 downto 0);
	signal	FIFO_wrreq_send,FIFO_wrreq_get,rise_FIFO_wrreq_send:		STD_LOGIC;	
	signal	LLD_FIFO_rdreq_send,LLD_FIFO_rdreq_get:		STD_LOGIC;	
	signal	clear_send,clear_get,tx_flag,sclk_flag,SenddataTrig_flag				:		STD_LOGIC;
	signal	numfifo_send,numfifo_get			:		std_logic_vector(12 downto 0);
	signal	LLD_DATA_FIFORD_send,LLD_DATA_FIFORD_get:	std_logic_vector(7 downto 0);
	signal	LLD_FIFO_empty_send,LLD_FIFO_empty_get,miso_delay,trig_flag	:		STD_LOGIC;
	signal	LLD_FIFO_full_send,LLD_FIFO_full_get,sclk_getflag,sclk_get:		STD_LOGIC;
	
	signal  data_length_send					:std_logic_vector(7 downto 0);
	signal  WorkMode_send,WorkMode_get			:std_logic_vector(7 downto 0);
	signal	byteDataReceivedOK,CRC_check,send_data_req,LLD_FIFO_rden_get,LLD_FIFO_rden_send,rise_FIFO_wrreq_get		:STD_LOGIC;--接收fifo使能信号
	signal	LLD_accept		: 	STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
U1: LLD_UART_FIFO  port map (Data=>FIFO_Data_send,Clk=>sysclk,WrEn=>rise_FIFO_wrreq_send,RdEn=>LLD_FIFO_rdreq_send,Reset=>clear_send
,Wnum=>numfifo_send,Q=>LLD_DATA_FIFORD_send,Empty=>LLD_FIFO_empty_send,Full=>LLD_FIFO_full_send); 
--U2: LLD_UART_FIFO2  port map (Data=>FIFO_Data_get,Clk=>sysclk,WrEn=>rise_FIFO_wrreq_get,RdEn=>LLD_FIFO_rdreq_get,Reset=>clear_get,Wnum=>numfifo_get,Q=>LLD_DATA_FIFORD_get,Empty=>LLD_FIFO_empty_get,Full=>LLD_FIFO_full_get);	
U3:	rising_edge_detect port map(nreset=>sysreset,clk=>sysclk,input=>LLD_FIFO_rdreq_get,output=>LLD_FIFO_rden_get);
U4:	rising_edge_detect port map(nreset=>sysreset,clk=>sysclk,input=>FIFO_wrreq_send,output=>rise_FIFO_wrreq_send);
U5:	rising_edge_detect port map(nreset=>sysreset,clk=>sysclk,input=>SenddataTrig,output=>SenddataTrig_flag);
--U5:	rising_edge_detect port map(nreset=>sysreset,clk=>sysclk,input=>FIFO_wrreq_get,output=>rise_FIFO_wrreq_get);
U6:	rising_edge_detect port map(nreset=>sysreset,clk=>sysclk,input=>sclk_get,output=>sclk_getflag);	
--参数设置	
	process(sysclk,sysreset)
	begin
		if sysreset ='0' then
			FIFO_Data_send 	<= x"00";
			FIFO_wrreq_send <= '0';
			clear_send		<= '0';
			FIFO_Data_get	<= x"00";
			FIFO_wrreq_get	<= '0';
		elsif rising_edge(sysclk) then
			FIFO_Data_send	 <= Senddata;
			FIFO_wrreq_send	 <= SenddataTrig_flag;
			clear_send		 <=	Send_clear;
			FIFO_Data_get	 <=	dout;
			FIFO_wrreq_get 	 <= trig_flag ;
			GetdataTrig		 <= trig_flag ;
			Getdata		     <=	dout;
			req				 <=	CS;	
		end if;
	end process;
	
--spi通讯协议
	PROCESS(sysclk,sysreset)
	BEGIN
		IF(sysreset = '0') THEN
			state_c <= IDLE;
		ELSIF(sysclk'EVENT AND sysclk = '1') THEN 
			state_c <= state_n;
		END IF;
	END PROCESS;
	-------------------------------------------------------------------------------------------
	PROCESS(state_c,req,bit_cnt)
	BEGIN
		IF(sysreset = '0') THEN
			state_n	<= IDLE;
		else
			if req = '0' then 
				state_n	<= IDLE;
			else
					CASE state_c IS 
						WHEN IDLE =>
							IF(req = '1')THEN 
							state_n <= WAIT_L;
							END IF;
						WHEN WAIT_L => 
							state_n <= DATA;
						WHEN DATA =>
							IF(tmp = '1')THEN--() AND 
							state_n <= DONE;
							END IF;
						WHEN DONE =>
							state_n <= IDLE;
						WHEN OTHERS =>
							state_n <= IDLE;
					END CASE;
			end if;
		end if;	
	END PROCESS;
	-----------------------------------bit_cnt ---------------------------------------------------------
	PROCESS(sysclk,sysreset)
	BEGIN
		IF(sysreset = '0') THEN 
			bit_cnt <= 0;
			byteDataReceivedOK   <=	'0';		
		ELSIF(sysclk'EVENT AND sysclk = '1')THEN 
            IF req = '0' then 
               bit_cnt <= 0;
			ELSIF  sclk_cnt = SCLK_NUM -1 THEN 
				IF(bit_cnt = 7)THEN
					bit_cnt <= 0;
						byteDataReceivedOK <='1';	
				ELSE
					bit_cnt <= bit_cnt + 1;
					byteDataReceivedOK <='0';	
				END IF;
			ELSE 
				bit_cnt <= bit_cnt;
			END IF;
			--if LLD_FIFO_empty_send = '1' and tmp = '1' then
				--bit_cnt <= 0;
				--byteDataReceivedOK   <=	'0';
			--end if;
		END IF;	
	END PROCESS;
	----------------------------------sclk计数器---------------------------------------------------------
	PROCESS(sysclk,sysreset)
		BEGIN
			IF(sysreset = '0') THEN 
				sclk_cnt <= 0;
			ELSIF(sysclk'EVENT AND sysclk = '1')THEN 
                 if req = '0' then 
                    sclk_cnt <= 0;
                 else
					IF(sclk_cnt = SCLK_NUM-1)THEN 
						sclk_cnt <= 0;
					ELSE 
						sclk_cnt <= sclk_cnt + 1;
					END IF;
				end if;	
				--if LLD_FIFO_empty_send = '1' and tmp = '1' then
				--	sclk_cnt <= 0;
				--end if;
			END IF;
	END PROCESS;
	----------------------------------16分频串行时钟 CPHA=1 CPOL=1-----------------------------------------------------------
	PROCESS(sysclk,sysreset)
		BEGIN 
			IF(sysreset = '0') THEN 
				IF(CPHA = 0)THEN 
					sclk <= '0';--空闲低电平
				ELSIF(CPHA = 1)THEN
					sclk <= '1';
					sclk_get <= '1';
				END IF;
				sclk_flag <= '0';
			ELSIF(sysclk'EVENT AND sysclk = '1')THEN
				sclk_get <= sclk;
				if req = '1' then
					if	bit_cnt = 0 and sclk_cnt = 5 then
						sclk_flag <= '1';
					else
						sclk_flag <= sclk_flag;
					end if;	
				else
					sclk_flag <= '0';
				end if;	
				if sclk_flag = '1' then
					IF sclk_cnt = 3 THEN 
						sclk <= NOT sclk;
					ELSIF sclk_cnt = 3 + SCLK_NUM/2 then
						sclk <= NOT sclk;
					END IF;
				else 
					sclk <= '1';
				end if;	
				if req = '0' then
					sclk <= '1';
				end if;	
				--if LLD_FIFO_empty_send = '1' and tmp = '1' then
				--	sclk <= '1';
				--end if;
			END IF;
	END PROCESS;
	---------------------------------发送的数据mosi,高位先行------------------------------------------------------------------
	PROCESS(sysclk,sysreset)
	BEGIN
		IF(sysreset = '0')THEN 
			tx_data <= "00000000";
			LLD_FIFO_rdreq_send		<='0';
			tx_flag					<='0';
		ELSIF(sysclk'EVENT AND sysclk = '1')THEN 
			IF(CPOL = 0)THEN
				IF(sclk_cnt = SCLK_AFTER-2) AND (bit_cnt = 1) then
						LLD_FIFO_rdreq_send <= '1';
					else
						LLD_FIFO_rdreq_send <= '0';
				end if;
					IF((sclk_cnt = SCLK_AFTER-1)) and tx_flag='0' THEN --后沿输出
						tx_data <= LLD_DATA_FIFORD_send;
					elsif LLD_FIFO_empty_send = '1' and bit_cnt = 7 and sclk_cnt = SCLK_NUM-1 then 
						tx_data <= "00000000";
						tx_flag	<='1';
					else
						tx_data <=tx_data;
					END IF;
			ELSIF(CPOL = 1)THEN
				IF(sclk_cnt = 1) AND (bit_cnt = 0) then
						LLD_FIFO_rdreq_send <= '1';
				else
						LLD_FIFO_rdreq_send <= '0';	
				end if;
				IF sclk_cnt = 4 and tx_flag = '0' THEN 
					tx_data <= LLD_DATA_FIFORD_send;
				elsif LLD_FIFO_empty_send = '1' and bit_cnt = 7 and sclk_cnt = SCLK_NUM-1 then 
						tx_data <= "00000000";
						tx_flag	<='1';
				else
						tx_data <=tx_data;			
				END IF;
			END IF;
			if LLD_FIFO_empty_send = '1'  then
				LLD_FIFO_rdreq_send		<='0';
			elsif LLD_FIFO_empty_send = '0' and  LLD_FIFO_rdreq_send = '1' then 
				tx_flag	<='0';
			else 
				tx_flag	<=tx_flag;
			end if;
		END IF;
	END PROCESS;
	----------------------------------接收的数据miso-----------------------------------------------------------------------------
	PROCESS(sysclk,sysreset)
		BEGIN
			IF(sysreset = '0')THEN 
				rx_data <= "00000000";
				get_cnt	<= 0;
			ELSIF(sysclk'EVENT AND sysclk = '1')THEN
				miso_delay <= miso;
				IF(CPOL = 0)THEN 
					IF((sclk_cnt = SCLK_BEFORE-1))THEN --前沿采样
						rx_data(7-bit_cnt) <= miso_delay;
					END IF;
				ELSIF(CPOL = 1)THEN 
					if sclk_getflag = '1' and req = '1' then
						get_cnt <= get_cnt + 1;
						rx_data(7-get_cnt) <= miso_delay;
						
						if	get_cnt = 7 then
							get_cnt <= 0;
						end if;	
					END IF;
					
				END IF;
			END IF;
	END PROCESS;
	----------------------------------------出bug后加的--(sclk_cnt=15)AND(bit_cnt = 7)出现状态机不跳转---------------------------------
	PROCESS(sclk_cnt)BEGIN
		IF((sclk_cnt=SCLK_NUM-1))THEN 
			t<='1';
		ELSE 
			t <= '0';
		END IF;
	END PROCESS;
	PROCESS(bit_cnt)BEGIN
		IF(bit_cnt = 7)THEN 
			t_l<='1';
		ELSE 
			t_l <= '0';
		END IF;
	END PROCESS;
	tmp <= t and t_l;
------------------------------------------------------------------------------------------------------------------------------------	
	PROCESS(sysclk,sysreset)
		BEGIN
			IF(sysreset = '0')THEN 
		ELSIF(sysclk'EVENT AND sysclk = '1')THEN 
			if sclk_cnt = 5 then
				mosi <= tx_data(7-bit_cnt);
			else
				mosi <= mosi;
			end if;
		end if;
	END PROCESS;
	CS1 <= not CS;
	PROCESS(sysclk,sysreset)
		BEGIN
			IF(sysreset = '0')THEN 
				dout <= x"00";
				trig_flag <= '0';
		ELSIF(sysclk'EVENT AND sysclk = '1')THEN 
			if get_cnt = 0 then
				dout <= rx_data;
				trig_flag <= '1';
			else
				dout <= dout;
				trig_flag <= '0';
			end if;
		end if;
	END PROCESS;
	
END SPI1;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值