底端硬件必须用到FPGA,但是树莓并没有引出存储器总线(如TI的gpmc总线),只好用i2c将就代替,
经测试,速度从100kbps~2Mbps都可以通信,打算使用固定的2Mbps用到以后的产品上。
通信模式为cpu--> i2c --> FPGA --> 7bit 地址,16 bit 数据宽度Local bus。
树莓有个i2ctool,可以用来检测外部i2c设备
sudo apt-get install i2ctool
sudo i2cdetect -y 0
这货只是发送第一节地址,然后侦测外部设备有没在ACK位拉低,若有,将这个地址标记为有外部设备,即上上图是数字而不是“--”。
拿根线把sda拉到GND,他也认为有响应有设备,当然这只是骗骗自己。
注意!树莓A版在编译bcm2835的c库时,要把
// #define I2C_V1
#define I2C_V1
因为最新的bcm2835的c库是给B版用的,所以默认的把I2C define I2C_V2
装好c库http://www.airspayce.com/mikem/bcm2835/ ,就可以开始写通信的代码了
贴上FPGA的i2c slave 代码
-- ============================================================
-- File Name : i2c_sla.vhd
-- Author :
-- Build Date :
-- Version : V 1.3
-- ChangeLog :
-- v1.1 fixed the rising/falling edge detect of sdi
--
-- v1.2 make some comment,make it suit 7-bit-addressing
-- only
-- v1.3 fixed a THEORY edge detect problem!!
-- ============================================================
-- ************************************************************
-- Description : i2c slave module , 7-bit-addressing & 10-bit
-- -addressing suitable , the data width is 2
-- byte
-- this module WONT auto addr-add-one on the
-- continuesly write or read
--
-- ATTENTION!!!this suit 7bit-addr only!!!
-- ************************************************************
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity i2c_sla is
port
(
clk : in std_logic; -- scl's freq * 8 or more
rst : in std_logic;
scl : in std_logic;
sdi : in std_logic;
sdo : out std_logic;
sdo_rls : out std_logic;
wrn : out std_logic;
oen : out std_logic;
ain : out std_logic_vector(9 downto 0);
din : out std_logic_vector(15 downto 0);
dout : in std_logic_vector(15 downto 0);
led : out std_logic
);
end i2c_sla;
architecture rtl of i2c_sla is
type rxstate_type is(idle,ctrl_byte,sla_ack1,sla_addr,sla_ack2,sla_read,sla_write);
type sla_rd_type is(prepare,shift,ack);
type sla_wr_type is(shift,ack);
type d_type is record
rxstate : rxstate_type;
sla_rd_state : sla_rd_type;
sla_wr_state : sla_wr_type;
scl : std_logic_vector(1 downto 0);
sdi : std_logic_vector(1 downto 0);
wrn : std_logic;
oen : std_logic;
rxcnt : integer range 0 to 8;
txcnt : integer range 0 to 8;
rxbuf : std_logic_vector(7 downto 0);
ctrl_reg : std_logic_vector(7 downto 0);
sdo_release : std_logic;
sdo : std_logic;
sla_addr : std_logic_vector(9 downto 0);
txbuf : std_logic_vector(15 downto 0);
data_reg : std_logic_vector(15 downto 0);
din : std_logic_vector(15 downto 0);
ack_det : std_logic;
led : std_logic;
end record;
signal d,q : d_type;
constant RD : std_logic := '1';
constant WR : std_logic := '0';
begin
comb:process(q,rst,scl,sdi,dout)
variable w : d_type;
variable scl_rising : std_logic;
variable scl_falling : std_logic;
begin
w := q;
w.scl := q.scl(0) & scl;
w.sdi := q.sdi(0) & sdi;
scl_rising := (q.scl(0))and(not q.scl(1));
scl_falling := (q.scl(1))and(not q.scl(0));
w.wrn := '1';
w.oen := '1';
case q.rxstate is
when idle =>
w.rxcnt := 0;
w.txcnt := 0;
w.sdo := '1';
w.sdo_release := '0';
w.led := '1'; --**************************for debug
when ctrl_byte =>
w.led := '0'; --****************************for debug
if scl_rising = '1' then --scl rising edge
w.rxbuf := q.rxbuf(6 downto 0) & q.sdi(0);
w.rxcnt := q.rxcnt + 1;
if w.rxcnt = 8 then
w.rxcnt := 0;
w.ctrl_reg := w.rxbuf;
w.rxstate := sla_ack1;
end if;
end if;
when sla_ack1 =>
if scl_falling = '1' then --scl falling edge
w.sdo_release := '1';
w.sdo := '0';
if q.ctrl_reg(0) = RD then --read
w.rxstate := sla_read;
w.oen := '0';
-- if q.ctrl_reg(7 downto 3) /= "11110" then -- 7-bit-addressing,save addr
w.sla_addr := "000" & q.ctrl_reg(7 downto 1); -- if it is 10-bit-addressing,addr has already save
-- end if; -- when last time write process done
else --write
if q.sdo_release = '1' then
w.sdo := '1';
w.sdo_release := '0';
w.sla_addr := "000" & q.ctrl_reg(7 downto 1);
-- if q.ctrl_reg(7 downto 3) = "11110" then --10-bit-addressing,go to the second addr handle process
-- w.rxstate := sla_addr;
-- else --7-bit-addressing,go to write process directly
w.rxstate := sla_write;
-- end if;
end if;
end if;
end if;
when sla_addr =>
if scl_rising = '1' then --scl rising edge
w.rxbuf := q.rxbuf(6 downto 0) & q.sdi(0);
w.rxcnt := q.rxcnt + 1;
if w.rxcnt = 8 then
w.rxcnt := 0;
w.sla_addr := q.ctrl_reg(2 downto 1) & w.rxbuf;
w.rxstate := sla_ack2;
end if;
end if;
when sla_ack2 =>
if scl_falling = '1' then --scl falling edge
w.sdo_release := '1';
w.sdo := '0';
if q.sdo_release = '1' then --slave has already acknowledge
w.sdo := '1';
w.sdo_release := '0';
w.rxstate := sla_write;
end if;
end if;
when sla_read =>
case q.sla_rd_state is
when prepare =>
w.txbuf := dout;
w.sla_rd_state := shift;
when shift => -- in the read state , it will got the stop bit
if scl_falling = '1' then --scl falling edge
w.sdo_release := '1';
w.sdo := q.txbuf(7);
w.txbuf(7 downto 0) := q.txbuf(6 downto 0) & q.txbuf(7);
w.txcnt := q.txcnt + 1;
if w.txcnt = 8 then
w.txcnt := 0;
w.sla_rd_state := ack;
end if;
end if;
when ack =>
if scl_falling = '1' then
w.sdo_release := '0';
w.ack_det := '1';
end if;
if q.ack_det = '1' then
if scl_rising = '1' then --scl rising edge
w.sla_rd_state := shift;
w.ack_det := '0';
if q.sdi(0) = '0' then --master acknowledge
w.txbuf := q.txbuf(7 downto 0) & q.txbuf(15 downto 8);
else --master did not acknowledge
w.rxstate := idle;
w.sla_rd_state := prepare;
end if;
end if;
end if;
end case;
when sla_write =>
case q.sla_wr_state is
when shift =>
if scl_rising = '1' then --scl rising edge
w.rxbuf := q.rxbuf(6 downto 0) & q.sdi(0);
w.rxcnt := q.rxcnt + 1;
if w.rxcnt = 8 then
w.rxcnt := 0;
w.data_reg := w.rxbuf & q.data_reg(15 downto 8);
w.sla_wr_state := ack;
end if;
end if;
if (((q.sdi(0))and(not q.sdi(1)))and((q.scl(0))and(q.scl(1)))) = '1' then --sdi rising edge and scl is high
w.wrn := '0';
w.din := q.data_reg;
end if;
when ack =>
if scl_falling = '1' then --scl falling edge
w.sdo_release := '1';
w.sdo := '0';
if q.sdo_release = '1' then --slave has already acknowledge
w.sdo := '1';
w.sdo_release := '0';
w.sla_wr_state := shift;
end if;
end if;
end case;
end case;
--=========================== stop bit process
if (((q.sdi(0))and(not q.sdi(1)))and((q.scl(0))and(q.scl(1)))) = '1' then --sdi rising edge and scl is high
w.rxstate := idle; --it means the i2c come to the stop bit
w.sla_rd_state := prepare;
w.sla_wr_state := shift;
end if;
--========================== start/restart process
if (((q.sdi(1))and(not q.sdi(0)))and((q.scl(0))and(q.scl(1)))) = '1' then --sdi falling edge and scl is high
w.rxstate := ctrl_byte; --i2c got a start/restart bit
w.rxcnt := 0;
w.txcnt := 0;
end if;
--=======================
if rst = '1' then
w.rxstate := idle;
w.sla_rd_state := prepare;
w.sla_wr_state := shift;
w.scl := "11";
w.sdi := "11";
w.sdo := '1';
w.sdo_release := '0';
w.wrn := '1';
w.oen := '1';
w.sla_addr := (others => '0');
w.din := (others => '0');
w.rxcnt := 0;
w.txcnt := 0;
w.rxbuf := (others => '0');
w.txbuf := (others => '0');
w.ctrl_reg := (others => '0');
w.data_reg := (others => '0');
w.ack_det := '0';
w.led := '1';
end if;
sdo <= q.sdo;
sdo_rls <= q.sdo_release;
oen <= q.oen;
wrn <= q.wrn;
ain <= q.sla_addr;
din <= q.din;
led <= q.led;
d <= w;
end process;
regs:process(clk)
begin
if rising_edge(clk) then
q <= d;
end if;
end process;
end rtl;
vhdl中把其中几个注释打开,就可以用作10bit-addressing的i2c,但是树莓是7bit-addressing的,所以注释掉。
#include
#include
int main()
{
char wbuf[2] = {0x00,0x00};
char addr = 0x00;
uint8_t result;
char rxbuf[2] = {0x00,0x00};
uint16_t err_cnt = 0;
if (!bcm2835_init()) return 1;
bcm2835_i2c_begin();
bcm2835_i2c_setClockDivider(125);
while(1)
{
bcm2835_i2c_setSlaveAddress(addr);
bcm2835_i2c_write(wbuf, 2);
printf("write addr is 0x%x\n",addr);
printf("write data is 0x%x%x\n\n",wbuf[1],wbuf[0]);
bcm2835_delay(500);
result = bcm2835_i2c_read(rxbuf, 2);
printf("read data is 0x%x%x\n\n",rxbuf[1],rxbuf[0]);
if(wbuf[1] != rxbuf[1]) err_cnt = err_cnt + 1;
else if(wbuf[0] != rxbuf[0]) err_cnt = err_cnt + 1;
printf("err cnt is : %d\n",err_cnt);
printf("***************\n");
bcm2835_delay(500);
if(err_cnt >= 1)
{
break;
}
addr = addr + 1;
wbuf[1] = wbuf[1] + 1;
wbuf[0] = wbuf[0] + 1;
}
bcm2835_i2c_end();
bcm2835_close();
return 0;
}
我的C语言太渣渣,测试代码可以将就用用,软件的还是交给软件工程师去写吧
gcc -o ts01 ts01.c -l bcm2835
完