树莓引出的IO口中还有两条SPI通道,既然引出了,那不用白不用,而且SPI速度很可观。
只是不像I2C通信有地址,数据,比较好分开具化成存储器总线。
通过飞线,把树莓的CS,SCK,MOSI,MISO引到FPGA引脚上,测试了几个速率
500kbps
1Mbps
2Mbps
5Mbps
10Mbps
25Mbps
最后在25Mbps的时候,通信出现错误,在FPGA端看到是树莓的MOSI有时候是一直低电平,估计用示波器抓,
树莓的MOSI在25Mbps时已经有很大的畸变了,没法通信。
在10Mbps以下,通信是非常好的。
贴上测试代码
-- ============================================================
-- File Name : spi_sla.vhd
-- Author :
-- Build Date :
-- Version : V 1.1
-- ChangeLog :
-- V1.1 remove the dependency with sp_en,so the outside
-- of sclk shall = (sclk and (not sp_en));
-- and sp_en shall be the CS signal for sub module
-- ============================================================
-- ************************************************************
-- Description : the spi slave device de/encoder
--
--
-- ************************************************************
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity spi_sla is
port
(
clk : in std_logic; --sclk freq*8
rst : in std_logic;
sclk : in std_logic;
mosi : in std_logic;
miso : out std_logic;
dout : in std_logic_vector(7 downto 0);
dprd : out std_logic;
din : out std_logic_vector(7 downto 0);
dpwr : out std_logic
);
end spi_sla;
architecture rtl of spi_sla is
type mosi_state_type is(idle,shift_in);
type miso_state_type is(idle,prepare,shift_out);
type d_type is record
mosi_state : mosi_state_type;
miso_state : miso_state_type;
sclk : std_logic_vector(1 downto 0);
mosi : std_logic_vector(1 downto 0);
dpwr : std_logic;
dprd : std_logic;
rxcnt : integer range 0 to 8;
rxbuf : std_logic_vector(7 downto 0);
txcnt : integer range 0 to 9;
txbuf : std_logic_vector(7 downto 0);
miso : std_logic;
poped : std_logic;
sclk_gap : integer range 0 to 7;
end record;
signal d,q : d_type;
begin
comb:process(q,rst,sclk,mosi,dout)
variable w : d_type;
variable sclk_rising : std_logic;
variable sclk_falling : std_logic;
begin
w := q;
w.sclk := q.sclk(0) & sclk;
w.mosi := q.mosi(0) & mosi;
sclk_rising := q.sclk(0) and (not q.sclk(1));
sclk_falling := q.sclk(1) and (not q.sclk(0));
if (q.sclk(0) xor q.sclk(1)) = '1' then
w.sclk_gap := 0;
else
if q.sclk_gap < 7 then
w.sclk_gap := q.sclk_gap + 1;
end if;
end if;
w.dpwr := '0';
w.dprd := '0';
case q.mosi_state is
when idle =>
w.rxcnt := 0;
if sclk_rising = '1' then
w.rxbuf := q.rxbuf(6 downto 0) & q.mosi(1);
w.rxcnt := 1;
w.mosi_state := shift_in;
end if;
when shift_in =>
if sclk_rising = '1' then
w.rxbuf := q.rxbuf(6 downto 0) & q.mosi(1);
w.rxcnt := q.rxcnt + 1;
if w.rxcnt = 8 then
w.rxcnt := 0;
w.dpwr := '1';
w.mosi_state := idle;
end if;
end if;
if q.sclk_gap > 4 then
w.mosi_state := idle;
end if;
end case;
case q.miso_state is
when idle =>
if q.poped = '1' then
w.dprd := '1';
end if;
w.miso_state := prepare;
when prepare =>
w.miso := dout(7);
w.txcnt := 1;
if q.poped = '1' then
w.txbuf := dout(6 downto 0) & '0';
end if;
w.poped := '0';
if sclk_falling = '1' then
w.miso_state := shift_out;
w.miso := q.txbuf(7);
w.txcnt := q.txcnt + 1;
w.txbuf := q.txbuf(6 downto 0) & '0';
end if;
when shift_out =>
if sclk_falling = '1' then
w.miso := q.txbuf(7);
w.txbuf := q.txbuf(6 downto 0) & '0';
w.txcnt := q.txcnt + 1;
if w.txcnt = 9 then
w.txcnt := 0;
w.miso_state := idle;
w.poped := '1';
end if;
end if;
if q.sclk_gap > 4 then
w.miso_state := prepare;
end if;
end case;
if rst = '1' then
w.miso_state := idle;
w.mosi_state := idle;
w.rxcnt := 0;
w.txcnt := 0;
w.rxbuf := (others => '0');
w.txbuf := (others => '0');
w.miso := '0';
w.poped := '1';
end if;
dprd <= w.dprd;
din <= q.rxbuf;
dpwr <= q.dpwr;
miso <= w.miso;
d <= w;
end process;
regs:process(clk)
begin
if rising_edge(clk) then
q <= d;
end if;
end process;
end rtl;
树莓上的
#include
#include
int main()
{
if (!bcm2835_init())
return 1;
uint8_t txbuf = 0x00;
uint8_t err_cnt = 0;
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default
bcm2835_spi_setClockDivider(25); // The default
bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // The default
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default
while(1)
{
uint8_t data = bcm2835_spi_transfer(txbuf);
printf("Write to SPI : %02X\n", txbuf);
printf("Read from SPI: %02X\n", data);
bcm2835_delay(100);
if((data + 1) != txbuf)
{
err_cnt = err_cnt + 1;
}
printf("err_cnt is : %d\n", err_cnt);
printf("***********\n\n");
if(err_cnt >= 2)
{
break;
}
txbuf = txbuf++;
}
bcm2835_spi_end();
bcm2835_close();
return 0;
}
在首次读写时,因为根本就没上一次写,所以读出来的数据也就无从比较,err_cnt 记录1次,
在写0x00时,读回来0xFF,相差1,不过渣渣测试代码不会回转,err_cnt 记录2次
可以看出SPI在10Mbps下通信良好。
PS:树莓的CS在空闲时不一定保持高电平,如图,因为不仅会有bcm2835_spi_begin();bcm2835_spi_end();这些在影响,而且在测试中发现CS引脚的驱动能力不强,经常受外界干扰会抖动,所以不能以CS的下降沿来触发,你懂的。
完