Forum: FPGA, VHDL & Co. Temperatursensor ADT7420 auslesen


von Torch M. (thelightener)


Lesenswert?

Hallo!

Ich bin FPGA-Neuling und befasse mich gerade mit dem FPGA-Board Nexys 4 
von Digilent:

http://www.digilentinc.com/Data/Products/NEXYS4/Nexys4_RM_VB2_Final_5.pdf

Auf diesem Board ist der Temperatursensor ADT7420 verbaut:

http://www.analog.com/static/imported-files/data_sheets/ADT7420.pdf


Programmiert wird mit der ISE Design Suite 14.7. Mein späteres Ziel ist 
es, die Temperatur auf einer der 7-Segmentanzeigen in dezimal 
darzustellen. Leider weiß ich gerade nicht weiter, wie ich mit dem 
ADT7420 auf dem Board arbeiten kann. Wie ist es mir z.B. möglich, die 
Bausteinregister per VHDL so zu programmieren, dass der IC arbeitet wie 
ich es möchte und wie funktioniert das Auslesen der Temperaturregister? 
Der Umfang des Datenblattes erschlägt mich gerade etwas, so dass ich gar 
nicht weiß wo ich anfangen soll. Der Baustein ermöglicht auch ein 
direktes Auslesen der Temperaturregister beim Starten des ICs (S. 23 im 
Datenblatt ADT7420 bzw. S.22f Datenblatt FPGA-Board). Wenn es mir 
gelingen würde, die zwei 8-bit Register auszulesen und damit evtl. LEDs 
zum Leuchten zu bringen, wäre mir schon sehr geholfen.

MfG
Thelightener

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Torch M. schrieb:
> Wie ist es mir z.B. möglich, die Bausteinregister per VHDL so zu
> programmieren, dass der IC arbeitet wie ich es möchte und wie
> funktioniert das Auslesen der Temperaturregister?
Das ist der zweite Schritt.

> Auf diesem Board ist der Temperatursensor ADT7420 verbaut:
Also musst du erst mal eine Komponente beschreiben, die das I2C 
Protokoll umsetzt und mit der du ein Byte über den I2C Bus versenden und 
empfangen kannst. Auf den Seiten 17 und 18 wird beschrieben wie das 
geht.

Dann brauchst du einen Zustandsautomaten, der nacheinander über die 
vorher implementierte I3C Schnittstelle die richtigen Werte an den 
Sensor schickt und empfängt. Und von den (wenigen) 14 Registern, die der 
Baustein hat, sind für dich nur 5 interessant. Zuerst solltest du 
versuchen, das ID-Register auszulesen, denn dort muss der Wert 0xCB 
zurückkommen.

> Der Baustein ermöglicht auch ein direktes Auslesen der
> Temperaturregister beim Starten des ICs (S. 23 im Datenblatt ADT7420
Wo steht das genau?

von Thomas T. (knibbel)


Lesenswert?

Hallo,

muss es wirklich direkt über VHDL sein?

Bei solchen Dingen (Auslesen, Umwandeln in Dezimal, Anzeigen) kommt es 
nicht wirklich auf die Durchlaufzeit an und von daher denke ich, dass es 
wesentlich einfacher ist, wenn man es prozessorbasiert realisiert.

Als Beispiel sei an dieser Stelle der Picoblaze vorgeschlagen. Einfach 
über zwei Ports mit dem Display und dem Temp-Sensor verbinden und die 
gesamte Ansteuerung und dazwischen das Umwandeln ins Dezimale per 
Software erledigen.

Ich denke, das wird einfacher als die ganzen Module und 
Zustandsautomaten zu schreiben. Erst recht für einen FPGA-Neuling, wie 
Du selbst schreibst...

Gruß,
Thomas

von Torch M. (thelightener)


Lesenswert?

Hallo,

danke für eure Beiträge. Wie der I²C-Bus funktioniert habe ich (denke 
ich) überblickt und welche Bitfolgen ich erzeugen muss, damit der 
Baustein tut was er soll, weiß ich auch. Als nächstes möchte ich die 
Bits einer beliebige Bitfolge taktabhängig auf einen Ausgang geben, weiß 
aber noch nicht wie ich das bewerkstelligen kann. Wie kann ich z.B. die 
Bits von:

signal bit_stream: std_logic_vector(3 downto 0) := "1010";

sequentiell auf einen Ausgang geben, angefangen beim MSB?


@Lothar Miller
Digilent Datenblatt S. 23, Abschnitt 12.3


@Thomas T.
Da ich nun mal das genannte Board in der Hand halte, muss ich es damit 
auch realisieren.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Torch M. schrieb:
> Wie kann ich z.B. die Bits von:
> signal bit_stream: std_logic_vector(3 downto 0) := "1010";
> sequentiell auf einen Ausgang geben, angefangen beim MSB?
Da sind zwei Ansätze denkbar:
1. ein Multiplexer
2. ein Schieberegister
Für den 1. Ansatz brauchst du einen Zähler von 0..3, der dann als Index 
für den Zugriff auf die Bits verwendet wird.
Für den 2. Weg brauchst du den '&' Operator (Concatenation).

Für FPGAs empfehle ich die 2. Lösung, weil sie viele Flipflops haben, 
bei CPLDs ist die Nummer 1. wegen der mächtigen Produktterme besser.

Hier ain paar Anwendungen für Schieberegister (einfach nach dem & 
suchen):
http://www.lothar-miller.de/s9y/categories/42-RS232
http://www.lothar-miller.de/s9y/categories/55-PS2
http://www.lothar-miller.de/s9y/categories/50-RC-5

von Thomas T. (knibbel)


Lesenswert?

Torch M. schrieb:
> Da ich nun mal das genannte Board in der Hand halte, muss ich es damit
> auch realisieren.

Davon bin ich auch ausgegangen...

Gemeint war die Implementation einer kleinen CPU (Picoblaze) auf dem 
FPGA, welche dann über einen angeflanschten I2C-Bus den Sensor 
anspricht. Über die auf dieser CPU laufende Software wäre eine 
Fehlerdetektion auf dem I2C-Bus auch einfacher, als in reiner 
Hardware...

Sicher ist eine reine VHDL-Lösung mittels Statemachines auch möglich. 
Wobei ich den Aufwand der "Binär->Dezimal"-Wandlung der Temperatur als 
nicht trivial einstufe (Das LSB hat eine Wertigkeit von 0.0625°C (oder 
0.0078°C)).

Gruß,
Thomas

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas T. schrieb:
> Wobei ich den Aufwand der "Binär-Dezimal"-Wandlung der Temperatur als
> nicht trivial einstufe
Das ist eine simple Multiplikation mit anschließendem Abschneiden der 
nicht relevanten Stellen...

von Torch M. (thelightener)


Angehängte Dateien:

Lesenswert?

Hallo,

mit dem Code am Threadende versuche ich 7 Bits seriell auf einen Ausgang 
zu geben. Die Ausgabe klappt auch, allerdings nicht so wie erhofft. Im 
Anhang ist ein Screenshot des Simulationsergebnisses zu sehen. Man 
sieht, dass wenn enable auf '1' gesetzt wird, die FSM beim nächsten Takt 
nach shifting springt. Bei diesem Takt müsste allerdings (so habe ich es 
mir zumindest vorgestellt) sda schon das LSB von device_addr ausgeben 
(also 1), da die Wertigkeit von bitcnt 0 ist. Tu es aber nicht. Dieses 
Bit wird irgendwie abgeschnitten. Wo liegt das Problem?
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
-------------------------------------------------------------
5
entity Proj_data_shift is
6
    Port ( clk   : in    STD_LOGIC;
7
           --rst   : in    STD_LOGIC;
8
           --scl   : out    STD_LOGIC;
9
           enable  : in     STD_LOGIC;
10
           sda   : inout  STD_LOGIC);
11
end Proj_data_shift;
12
-------------------------------------------------------------
13
architecture Behavioral of Proj_data_shift  is
14
15
constant  device_addr: STD_LOGIC_VECTOR(6 downto 0) := "1110111";
16
signal   bitcnt: integer range 0 to 7;
17
18
type data_shift is (idle, shifting);
19
signal state : data_shift := idle;
20
21
begin
22
-------------------------------------------------------------
23
process begin
24
wait until rising_edge(clk); 
25
case state is
26
27
  when idle =>
28
    SDA <= '0';
29
    if (enable = '1') then
30
      state <= shifting;
31
    end if;
32
  
33
  when shifting =>
34
    if (bitcnt < 7) then
35
      bitcnt <= bitcnt + 1;
36
      sda <= device_addr(bitcnt);
37
    else
38
      bitcnt <= 0;
39
      state <= idle;
40
    end if;
41
end case;
42
end process;
43
-------------------------------------------------------------
44
end behavioral;

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Torch M. schrieb:
> Wo liegt das Problem?
Dein Stichwort heißt: Latency.

Eigentlich logisch:
Wenn du zusammen mit dem Zustandswechsel etwas ausgeben oder ändern 
willst, dann musst du es zusammen mit dem Zustandswechsel 
aktualisieren.
1
process begin
2
wait until rising_edge(clk); 
3
case state is
4
5
  when idle =>
6
    SDA <= '0';
7
    if (enable = '1') then
8
      state <= shifting;     -- Zustandswechsel
9
      sda <= device_addr(0); -- GLEICHZEITIG Signal ändern
10
      bitcnt <= 1;           -- Vorbereiten für den nächsten Takt
11
    end if;
12
  
13
  when shifting =>
14
    if (bitcnt < 7) then   
15
      sda <= device_addr(bitcnt);
16
      bitcnt <= bitcnt + 1;
17
    else
18
      state <= idle;
19
    end if;
20
end case;
21
end process;
BTW:
Auf einem FPGA wäre es effizienter, die Daten  mit einem Schieberegister 
hinauszubefördern. Ein Multiplexer braucht viel Logik...

: Bearbeitet durch Moderator
von Torch M. (thelightener)


Lesenswert?

Hallo,

ich habe mittlerweile den I²C-Bus zum Laufen gekriegt und kann den 
Sensor schreiben und lesen. Auch die Ausgabe auf der 7-Segment-Anzeige 
funktioniert. Leider ist mir ein Problem aufgefallen, dessen Ursprung 
ich einfach nicht finden kann. Wenn ich die Sensortemperatur (mittels 
Föhn) von Raumtemperatur bis meinetwegen 30°C hochjage und die 
Temperatur wieder abfällt, dann habe ich ab <26°C immer einen Sprung auf 
<=24°C. D.h. er überspringt in diesem Bereich einfach immer 2°C, so dass 
die angezeigte Temperatur letztendlich nicht stimmen kann. Einen Fehler 
bei der Umrechnung des Temperaturregister-Inhaltes und der Anzeige auf 
dem 7-Segment-Display schließe ich aus, da ich mir parallel dazu auch 
den Temperaturregister-Inhalt (16 Bit) auf den 16 LEDs anzeigen lasse. 
Eine Umrechnung der angezeigten Bits ergibt stets den angezeigten Wert 
auf der 7-Segment-Anzeige. Der Fehler muss also meiner Meinung nach in 
der FSM zur Beschreibung des I²C-Protokolls liegen. Leider bin ich auch 
nach langer Sucherei nach wie vor ratlos, wo das Problem liegen könnte.

Mein I²C-Protokoll habe ich so ausgelegt, dass ich über zwei 
entsprechende Bits (rw bzw. tbr) festlegen kann, ob ein Register 
geschrieben oder gelesen werden (rw) soll und ob ein Byte oder zwei Byte 
gelesen werden sollen (tbr). Ich konfiguriere den Sensor auf 16-Bit 
Auflösung, gebe noch einige Schwellwerte für den Temperaturalarm vor und 
ende dann in einer Endlosschleife, in der nur noch die 
Temperaturregister mittels 2-Byte-read ausgelesen werden. Sobald die 
Bits gelesen worden sind, werden sie an das Modul zur Visualisierung 
weitergegeben. Die einzelnen Module habe ich per Schematic verbunden. 
Falls es jemanden gibt, der sich meinem Problem (was ich in der FSM im 
Status "two_byte_read" vermute) annehmen möchte: Der Code für mein 
I²C-Protokoll lautet wie folgt:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity I2C_Master_Controller is
6
    Port ( 
7
clock       : in STD_LOGIC; 
8
rst         : in STD_LOGIC;  -- reset
9
rw          : in STD_LOGIC;  -- read or write
10
tbr         : in STD_LOGIC;  -- two-byte-read
11
reg_addr_in : in STD_LOGIC_VECTOR(7 downto 0);  --Registeradresse vom Konfigurationsmodul
12
data_in     : in STD_LOGIC_VECTOR(7 downto 0); --Daten vom Konfigurationsmodul, die bei rw <= '0' in den Sensor geschrieben werden
13
busy        : out STD_LOGIC;  
14
data_out    : out STD_LOGIC_VECTOR(15 downto 0);--Registerinhalt
15
scl         : out STD_LOGIC;      
16
sda         : inout STD_LOGIC);
17
end I2C_Master_Controller;
18
19
architecture Behavioral of I2C_Master_Controller is
20
21
type state_type is (idle, start_condition, send_dev_addr, loop_send_dev_addr, get_ack, loop_get_ack, send_reg_addr, loop_send_reg_addr, repeat_condition,loop_repeat_condition, loop_again_repeat_condition, one_byte_read, loop_one_byte_read, get_no_ack, loop_get_no_ack, send_stop, loop_send_stop, loop_again_send_stop, write_data, loop_write_data, two_byte_read, loop_two_byte_read);
22
signal state, prev_state: state_type; 
23
24
constant dev_addr_w : std_logic_vector(7 downto 0) := "10010110";-- Sensoradresse mit Write-Bit
25
constant dev_addr_r : std_logic_vector(7 downto 0) := "10010111";--Sensoradresse mit Read-Bit
26
27
signal tmp_data        :  STD_LOGIC_VECTOR(15 downto 0);
28
constant prescaler     :  integer := 100000000/400000;
29
signal data            :  std_logic_vector(7 downto 0);
30
signal bitcnt          :  integer range 0 to 7;
31
signal cnt_clk         :  integer range 0 to prescaler;
32
signal new_clk         :  STD_LOGIC;
33
signal sda_tmp         :  STD_LOGIC;
34
signal first_byte_sent : STD_LOGIC; --Bit für 2-Byte-Read: wird '1', sobald das erste Byte vom Sensor empfangen wurde.
35
36
begin
37
38
sda <= 'Z' when sda_tmp = '1' else '0';
39
-------------------------------------------------------------------------
40
-------------------------------------------------------------------------
41
process(new_clk, rst)
42
begin
43
if (rst <= '0') then    
44
  scl <= '1';
45
  sda_tmp <= '1';  
46
  tmp_data <= x"FFFF";
47
  data_out <= x"FFFF";
48
  first_byte_sent <= '1';
49
  data_out <= x"FFFF";
50
  bitcnt <= 7;  
51
  busy <= '0';
52
  state <= idle;  
53
elsif rising_edge(new_clk) then
54
case state is
55
  when idle =>      
56
      busy <= '0';      
57
      first_byte_sent <= '0';
58
      scl <= '1';
59
      sda_tmp <= '1';
60
      state <= start_condition;      
61
-------------------------------------------------------------------------    
62
  when start_condition =>
63
      busy <= '1';
64
      sda_tmp <= '0';          
65
      bitcnt <= 7;
66
      data <= dev_addr_w;
67
      prev_state <= start_condition;
68
      state <= send_dev_addr;    
69
-------------------------------------------------------------------------
70
  when send_dev_addr =>
71
    scl <= '0';
72
    sda_tmp <= data(bitcnt);
73
    state <= loop_send_dev_addr;
74
    
75
  when loop_send_dev_addr =>
76
    scl <= '1';
77
    if bitcnt /= 0 then
78
      bitcnt <= bitcnt - 1;
79
      state <= send_dev_addr;
80
    else
81
      bitcnt <= 7;
82
      case prev_state is
83
      when start_condition =>
84
        prev_state <= send_dev_addr;
85
      when repeat_condition =>
86
        prev_state <= repeat_condition;
87
      when others => null;
88
      end case;      
89
      state <= get_ack;
90
    end if;
91
-------------------------------------------------------------------------
92
  when get_ack =>
93
    scl <= '0';
94
    sda_tmp <= '1';
95
    state <= loop_get_ack;
96
    
97
  when loop_get_ack =>
98
    scl <= '1';
99
    sda_tmp <= '0';
100
    case prev_state is    
101
    when send_dev_addr =>  
102
      state <= send_reg_addr;      
103
    when send_reg_addr =>
104
      if rw = '0' then
105
        bitcnt <= 7;
106
        state <= write_data;
107
        elsif rw <= '1' then
108
        bitcnt <= 7;
109
        state <= repeat_condition;
110
      end if;
111
    when repeat_condition =>
112
      if tbr <= '0' then
113
        state <= one_byte_read;
114
      elsif tbr <= '1' then
115
        state <= two_byte_read;
116
      end if;
117
    when write_data =>      
118
      state <= send_stop;
119
    when two_byte_read =>
120
      first_byte_sent <= '1';
121
      state <= two_byte_read;
122
    when others =>
123
      state <= idle;
124
    end case;  
125
-------------------------------------------------------------------------    
126
  when send_reg_addr =>
127
    scl <= '0';
128
    sda_tmp <= reg_addr_in(bitcnt);
129
    state <= loop_send_reg_addr;
130
    
131
  when loop_send_reg_addr =>
132
    scl <= '1';
133
    if bitcnt /= 0 then
134
      bitcnt <= bitcnt - 1;
135
      state <= send_reg_addr;
136
    else
137
      bitcnt <= 7;
138
      prev_state <= send_reg_addr;
139
      state <= get_ack;      
140
    end if;
141
-------------------------------------------------------------------------  
142
  when repeat_condition =>
143
    scl <= '0';
144
    sda_tmp <= '1';
145
    state <= loop_repeat_condition;
146
    
147
  when loop_repeat_condition =>
148
    scl <= '1';
149
    sda_tmp <= '1';
150
    state <= loop_again_repeat_condition;
151
    
152
  when loop_again_repeat_condition =>
153
    scl <= '1';
154
    sda_tmp <= '0';    
155
    data <= dev_addr_r;
156
    prev_state <= repeat_condition;
157
    state <= send_dev_addr;
158
-------------------------------------------------------------------------    
159
  when write_data =>
160
    scl <= '0';
161
    sda_tmp <= data_in(bitcnt);
162
    state <= loop_write_data;    
163
    
164
  when loop_write_data =>
165
    scl <= '1';
166
    if bitcnt /= 0 then
167
      bitcnt <= bitcnt - 1;
168
      state <= write_data;
169
    else
170
      bitcnt <= 7;
171
      prev_state <= write_data;
172
      state <= get_ack;      
173
    end if;    
174
-------------------------------------------------------------------------    
175
  when one_byte_read => 
176
    scl <= '0';
177
    sda_tmp <= '1';
178
    state <= loop_one_byte_read;
179
    
180
  when loop_one_byte_read =>
181
    scl <= '1';
182
    data(bitcnt) <= sda;
183
    if bitcnt /= 0 then
184
      bitcnt <= bitcnt - 1;
185
      state <= one_byte_read;
186
    else
187
      bitcnt <= 7;
188
      tmp_data(15 downto 8) <= data;
189
      tmp_data(7 downto 0) <= (others => '0');
190
      state <= get_no_ack;
191
    end if;
192
-------------------------------------------------------------------------
193
  when two_byte_read => 
194
    scl <= '0';
195
    sda_tmp <= '1';
196
    state <= loop_two_byte_read;
197
    
198
  when loop_two_byte_read =>
199
    scl <= '1';
200
    data(bitcnt) <= sda;
201
    if bitcnt /= 0 then
202
      bitcnt <= bitcnt - 1;
203
      state <= two_byte_read;
204
    elsif first_byte_sent <= '0' then
205
        bitcnt <= 7;
206
        tmp_data(15 downto 8) <= data;
207
        state <= get_ack;
208
        prev_state <= two_byte_read;
209
    elsif first_byte_sent <= '1' then
210
        bitcnt <= 7;
211
        tmp_data(7 downto 0) <= data;
212
        state <= get_no_ack;
213
    end if;  
214
-------------------------------------------------------------------------    
215
  when get_no_ack =>
216
    scl <= '0';
217
    sda_tmp <= '1';    
218
    data_out <= tmp_data;
219
    state <= loop_get_no_ack;
220
    
221
  when loop_get_no_ack =>
222
    scl <= '1';
223
    state <= send_stop;
224
-------------------------------------------------------------------------
225
  when send_stop =>
226
    scl <= '0';
227
    sda_tmp <= '0';
228
    state <= loop_send_stop;
229
  
230
  when loop_send_stop =>
231
    scl <= '1';
232
    sda_tmp <= '0';    
233
    state <= loop_again_send_stop;
234
    
235
  when loop_again_send_stop =>
236
    scl <= '1';
237
    sda_tmp <= '1';
238
    state <= idle;    
239
-------------------------------------------------------------------------
240
  when others => 
241
    state <= idle;
242
-------------------------------------------------------------------------    
243
end case;
244
end if;
245
end process;
246
-------------------------------------------------------------------------
247
-------------------------------------------------------------------------
248
process (clock)
249
begin
250
if rising_edge(clock) then
251
  if (cnt_clk < prescaler) then
252
    cnt_clk <= cnt_clk + 1;
253
    if (cnt_clk < prescaler/2) then
254
      new_clk <= '0';
255
    else 
256
      new_clk <= '1';
257
    end if;
258
  else
259
    cnt_clk <= 0;
260
  end if;
261
end if;
262
263
end process;
264
-------------------------------------------------------------------------
265
-------------------------------------------------------------------------
266
end Behavioral;

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Torch M. schrieb:
> er überspringt in diesem Bereich einfach immer 2°C
Nur von "oben nach unten" oder auch von "unten nach oben"?

BTW: ich finde es interessant, ein einzelnes std_logic Bit auf 
"kleiner-gleich" zu vergleichen:
1
      ...
2
    elsif first_byte_sent <= '0' then
3
      ...    
4
    elsif first_byte_sent <= '1' then
5
      ...

von Torch M. (thelightener)


Lesenswert?

Hallo Lothar,

er springt "in beide Richtungen". Das mit dem <= Vergleich ist natürlich 
ein Fehler, ist mir bislang noch nicht aufgefallen. Wundert mich, dass 
es deswegen keine Probleme gibt bei der Synthese.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Torch M. schrieb:
> er springt "in beide Richtungen".
Sind die Temperaturen an sich plausibel? Hast du bei den 
Timing-Constraints wenigstens den Takt angegeben? Passt dein erzeugtes 
Timing zu dem im Bild 2 vom Datenblatt?

von Torch M. (thelightener)


Lesenswert?

Die Temperaturen sind plausibel und liegen immer so zwischen 21°C - 
23°C, also bei Raumtemperatur. Mit "Timing-Constraints" habe ich mich 
noch gar nicht beschäftigt, weiß damit also noch nichts anzufangen. Das 
ändert sich aber jetzt. Geht es dir bei dem Timing-Constraint um den 
Takt, mit dem ich meine I²C-FSM betreibe? Wo kann ich mir mein erzeugtes 
Timing anschauen?

Edit: Also ganz sicher bin ich mir doch nicht mehr ob die Temperaturen 
wirklich plausibel sind... er hat sich jetzt ca. bei 23°C eingependelt, 
bei mir ist es aber sicher ein paar Grad kälter.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.