Forum: Mikrocontroller und Digitale Elektronik TWI IC2 Taktrate berechnen


von Michael H. (h_m)


Lesenswert?

Guten Tag,

ich habe hier ein Buch in dem es bei einem Beispiel Code um den IC2 bus 
geht. Und dort wurde die Taktfrequenz nach folgender Rechnung festgelegt

I²C CLK Frequency = 100kHz, CLK = 8000000/(16 + 2*(0x50)*1)

Jetzt ist es aber so das ich 16Mhz nutzen möchte, Und die 0x50 werden ja 
in diesem Register gesetzt.:
TWBR = 0x50;    // Set the TWI clock-frequency in bit rate register

jetzt wollte ich die obige Rechnung erst einmal  eben mit 8000000 
nachrechnen um dann später das mit 16000000 zu machen.


ich habe festgestellt das ich es nicht schaffe das mit den 0x50 zu 
berechnen, könnte mir das bitte jemand erklären, wie ich das richtig 
rechnen kann ?

von Oh Mei (Gast)


Lesenswert?

Michael H. schrieb:
> Jetzt ist es aber so das ich 16Mhz nutzen möchte,

Das ist abhängig vom Controller, den du nicht genannt hast.

Und die Berechnung der Taktrate steht im Datenblatt des
verwendeten Controllers. Du brauchst nur dort nachsehen.

von Stefan F. (Gast)


Lesenswert?

> ich habe festgestellt das ich es nicht schaffe das mit den
> 0x50 zu berechnen

Ich verstehe deine Frage nicht. Was hindert dich daran, in der Formel 
die 8MHz durch 16MHz zu ersetzen?

von Michael H. (h_m)


Lesenswert?

Entschuldigung das habe ich vergessen das ist ein Atmega88PA ich habe 
auch ins Datenblatt geschaut.


Da steht das das TWBR Praktisch der Teiler ist. Heisst das dann,  ich 
kann mit den 8bit einen Teiler von 1-256 einstellen ?


und mit dem 0x50 wurde hier ein Teiler von 144 eingestellt oder wie ?

von Karl M. (Gast)


Lesenswert?

Hallo,

im DB 
http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf 
steht:

22.5 Overview of the TWI Module
unter
22.5.2 Bit Rate Generator Unit

This unit controls the period of SCL when operating in a Master mode.
The SCL period is controlled by settings in the TWI Bit Rate Register 
(TWBR) and the Prescaler bits in the TWI Status Register (TWSR).
Slave operation does not depend on Bit Rate or Prescaler settings, but 
the CPU clock frequency in the Slave must be at least 16 times higher 
than the SCL frequency.
Note that slaves may prolong the SCL low period, thereby reducing the 
average TWI bus clock period.

[Formel]
SCL frequency = F_CPU / (16 +2(TWBR) *PrescalerValue)
[/Formel]

TWBR = Value of the TWI Bit Rate Register
PrescalerValue = Value of the prescaler, see Table 22-7 on page 241.

Schaut man dann nach unter "22.9.1 TWBR – TWI Bit Rate Register", findet 
man:
*Bits 7...0* !!
TWI Bit Rate RegisterTWBR selects the division factor for the bit rate 
generator. The bit rate generator is a frequency divider which generates 
the SCL clock frequency in the Master modes.

von Christian S. (roehrenvorheizer)


Lesenswert?

CLKPR und TWBR bestimmen den TWI-Takt. TWBR teilt von 0 bis 255.


Mfg

von Stefan F. (Gast)


Lesenswert?

Wenn der Prescaler 1 ist, dann haben wir
SCL frequency = F_CPU / (16 +2(TWBR))

Umgestellt ergibt das:

((F_CPU / SCL frequency) - 16) / 2 = TWBR

Wir setzen ein:

((8000000 Hz/ 100000 Hz) - 16) / 2 = 32

Du musst also TWBR=32 einstellen, um auf 100kHz zu kommen.

Zur Kontrolle:
8000000 Hz / (16 +2(32)) = 100000 Hz

Was käme denn bei 0x50 (=80 dezimal) heraus?:

8000000 Hz / (16 +2(80)) = 45455 Hz
16000000 Hz / (16 +2(80)) = 90909 Hz

Bringt dich das weiter?

von Udo S. (urschmitt)


Lesenswert?

Michael H. schrieb:
> I²C CLK Frequency = 100kHz, CLK = 8000000/(16 + 2*(0x50)*1)

Wenn ich 8000000/(16 + 2*(0x50)*1) rechne komme ich aber auf
8000000 / (16 + 2 * 80) =  45455 Hz

Stefanus war schneller :-)

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Falls du auf 400kHz hoch gehen willst, beachte dabei dass TWBR 
mindestesn den Wert 10 haben muss. Die 400kHz wirst du mit einem 8MHz 
Quarz also nicht erreichen können.

von Michael H. (h_m)


Lesenswert?

Danke hilft mir schon etwas weiter, jetzt zeige ich euch hier mal den 
Code, den ich allerdings nicht für den LM75 verwenden will sondern, wenn 
ich es geschafft habe den Takt richtig zu bestimmen, will ich eigentlich 
nur die Funktionen nutzen um dann auf einen MCP4735 einen Wert ins 
Register schreiben,

So steht der Code in meinem Buch. Und aus der while schleife habe ich 
halt schon einiges entfernt aber das tut ja hier nichts zur sache, ich 
will ja nur den richtigen Takt für 16 Mhz einstellen




1
* LM75_Tempsensor.c
2
* Temperaturmessung (5x pro Sekunde) und Ausgabe der Temperatur als Binärwert am PORTD
3
* Eine negative Temperatur wird zusätzlich durch eine LED an PB0 angezeigt
4
* CPU: ATmega88
5
* FCPU: 8MHz
6
* I²C CLK Frequency = 100kHz, CLK = 8000000/(16 + 2*(0x50)*1)
7
*/
8
#include <avr/io.h>
9
#include <util/delay.h>
10
11
/*** Statusregister codes and other defines ***/
12
#define START           0x08    // A START condition has been transmitted
13
#define R_START         0x10    // A REPEATED START condition has been transmitted
14
#define SLA_W           0xC0    // Slave Address & Write 1100 0000
15
#define SLA_R           0xC1    // Slave Address & Read 1100 0001
16
#define MT_SLA_ACK      0x18    // Master Transmit SLA+W has been transmitted & ACK has been received
17
#define MR_SLA_ACK      0x40    // Master Receive SLA + R has been transmitted & ACK has been received
18
#define MT_DATA_ACK     0x28    // Master Transmit Data byte has been transmitted & ACK has been received
19
#define MR_DATA_NACK    0x58    // Master Receive Data byte has been transmitted & NACK has been returned
20
#define BIT_RATE        0x50    // Set value for the bit rate register TWBR
21
#define POINTER_ADDRESS 0x00    // LM75 pointer address for temperature register/ Registerwert
22
void ERROR(void);               // Prototyping of function "ERROR"
23
24
/*** Function to send a START Condition ***/
25
void TWI_START(void)
26
{
27
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
28
  // Wait for TWINT Flag set. This indicates that the START condition has been transmitted
29
  while (!(TWCR & (1<<TWINT)));
30
  // Check value of TWI statusregister. Mask prescaler bits. If status different from START go to ERROR if ((TWSR & 0xF8) != START)
31
  ERROR();
32
}
33
34
/*** Function to send the Slave Address with ACK in Master Transmit Mode ***/
35
void TWI_MT_SLA_ACK(void)
36
{
37
  // Load SLA_W into TWDR Register
38
  TWDR = SLA_W;
39
  // Clear TWINT bit in TWCR to start transmission of address
40
  TWCR = (1<<TWINT) | (1<<TWEN);
41
  // Wait for TWINT Flag set. This indicates that the SLA+W has been transmitted, and ACK/NACK has been received.
42
  while (!(TWCR & (1<<TWINT)));
43
  // Check value of TWI status register. Mask prescaler bits. If status different from MT_SLA_ACK go to ERROR
44
  if ((TWSR & 0xF8) != MT_SLA_ACK)
45
  ERROR();
46
}
47
48
/*** Function to send 8Bit of data with ACK in Master Transmit Mode **/
49
void TWI_MT_DATA_ACK(void)
50
{
51
  // Load DATA into TWDR register
52
  TWDR = POINTER_ADDRESS;
53
  // Clear TWINT bit in TWCR to start transmission of data
54
  TWCR = (1<<TWINT) | (1<<TWEN);
55
  // Wait for TWINT flag set. This indicates that the DATA has been transmitted, and ACK/NACK has been received.
56
  while (!(TWCR & (1<<TWINT)));
57
  // Check value of TWI status register. Mask prescaler bits. If status different from MT_DATA_ACK go to ERROR
58
  if ((TWSR & 0xF8) != MT_DATA_ACK)
59
  ERROR();
60
}
61
62
/*** Function to send a REPEATED START condition **/
63
void TWI_R_START(void)
64
{
65
  // Send a START condition
66
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
67
  // Wait for TWINT Flag set. This indicates that the START condition has been transmitted
68
  while (!(TWCR & (1<<TWINT)));
69
  // Check value of TWI statusregister. Mask prescaler bits. If status different from R_START go to ER-ROR
70
  if ((TWSR & 0xF8) != R_START)
71
  ERROR();
72
}
73
74
/*** Function to send the Slave Address in Master Receive Mode with Acknowledge **/
75
void TWI_MR_SLA_ACK(void)
76
{
77
  // Load SLA_R into TWDR Register
78
  TWDR = SLA_R;
79
  // Clear TWINT bit in TWCR to start transmission of address
80
  TWCR = (1<<TWINT) | (1<<TWEN);
81
  // Wait for TWINT Flag set. This indicates that the SLA+W has been transmitted, and ACK/NACK has been received.
82
  while (!(TWCR & (1<<TWINT)));
83
  // Check value of TWI status register. Mask prescaler bits. If status different from MR_SLA_ACK go to ERROR
84
  if ((TWSR & 0xF8) != MR_SLA_ACK)
85
  ERROR();
86
}
87
88
/*** Function to read one Databyte in Master Receive Mode with NACK ***/
89
uint8_t TWI_READ_DATABYTE_NACK(void)
90
{
91
  // Read one data byte
92
  TWCR = (1<<TWINT)| (1<<TWEN);
93
  // Wait for TWINT flag set. This indicates that the DATA has been transmitted, and ACK/NACK has been received.
94
  while (!(TWCR & (1<<TWINT)));
95
  // Check value of TWI status register. Mask prescaler bits. If status different from MR_DATA_NACK go to ERROR
96
  if ((TWSR & 0xF8) != MR_DATA_NACK)
97
  ERROR();
98
  return TWDR; // Return the value of data register
99
}
100
101
/*** function to send a STOP condition ***/
102
void TWI_STOP(void)
103
{
104
  TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); // Transmit STOP condition
105
}
106
107
/*** function to show a bus error ***/
108
void ERROR(void)
109
{
110
  PORTB |= (1<<PB1); // ERROR LED ON
111
}
112
113
uint8_t LSByte, MSByte;
114
uint16_t data_16bit, temperature, high_byte, low_byte;
115
116
int main(void)
117
{
118
  DDRD = 0xFF;        // PORTD = output
119
  DDRC = 0x3F;        // PC0 ... PC5 = output
120
  DDRB = 0x03;        // PB0 and PB1 = output
121
  TWBR = BIT_RATE;    // Set the TWI clock-frequency in bit rate register
122
  while(1)
123
  {
124
    TWI_START();                        // Send START condition
125
    TWI_MT_SLA_ACK();                   // Master Transmit Slave Address with ACK
126
    TWI_MT_DATA_ACK();                  // Master Transmit Data with ACK
127
    TWI_STOP();                         // Send STOP condition
128
    
129
    
130
    _delay_ms(200);                     // wait 200ms for new readout
131
  }
132
}

von Stefan F. (Gast)


Lesenswert?

Da fehlt ein Zeilenumbruch vor dem "if":

> // Check value of TWI statusregister. Mask prescaler bits. If status
> different from START go to ERROR if ((TWSR & 0xF8) != START)
>   ERROR();

von spess53 (Gast)


Lesenswert?

Hi

>Falls du auf 400kHz hoch gehen willst, beachte dabei dass TWBR
>mindestesn den Wert 10 haben muss.

Diese Einschränkung kenne ich von alter AT90S... . Bei aktuellen AVRs 
finde ich diese Einschränkung nicht mehr.

MfG Spess

von Stefan F. (Gast)


Lesenswert?

Michael H. schrieb:
> das ist ein Atmega88PA

spess53 schrieb:
> Diese Einschränkung kenne ich von alter AT90S... . Bei aktuellen AVRs
> finde ich diese Einschränkung nicht mehr.

Datenblatt Kapitel 19.5.2:
"Note: ...TWBR should be 10 or higher if the TWI operates in master 
mode. If TWBR is lower than 10, the master may produce an incorrect 
output on SDA and SCL for the reminder of the byte. "

von spess53 (Gast)


Lesenswert?

Hi

>Datenblatt Kapitel 19.5.2:

Welches Datenblatt? Bei dem Kapitel stoße ich bei den ATMega88 immer auf 
SPI.

MfG Spess

Beitrag #5751970 wurde von einem Moderator gelöscht.
von Michael H. (h_m)


Lesenswert?

Stefanus F. schrieb:
> Da fehlt ein Zeilenumbruch vor dem "if":
>
>> // Check value of TWI statusregister. Mask prescaler bits. If status
>> different from START go to ERROR if ((TWSR & 0xF8) != START)
>>   ERROR();

Ja, das ist wohl dem Geschuldet das ich es hier rein kopiert habe.

mir geht es eigentlich hautsächlich Darum das ich diesen Code für meine 
16Mhz konfiguriere, und da blicke ich nicht durch, da ich für  die 
Berechnung den Prescaler CLKPR den ich ja setzen soll niergends in dem 
Code finden kann.



ich weiß aber das dieser so mit 8Mhz ohne Probleme funktioniert.

von Stefan F. (Gast)


Lesenswert?


von clkpr (Gast)


Lesenswert?

Moin,
die section zum CLKPR findest du unter "System Clock and Clock Options" 
im Datasheet
gruß (:

von clkpr (Gast)


Lesenswert?

Wenn du den Parameter nicht im Code findest musst du ihn wohl einbauen?!

von clkpr (Gast)


Lesenswert?

"The CKDIV8 fuse(!) determines the initial value of the CLKPS bits. If 
CKDIV8 is unprogrammed, the CLKPS bits will be reset to
“0000”. If CKDIV8 is programmed, CLKPS bits are reset to “0011”, giving 
a division factor of 8 at start up"

von Michael H. (h_m)


Lesenswert?

ja, das ist klar, aber ich verstehe es ja erst einmal nicht, warum es 
genau so wie oben mit 8Mhz funktioniert und, wenn ich 16 Mhz benützen 
will muss ich ja etwas anderes machen, ich kann das auch noch öfters 
lesen ich check es nicht.

ich könnte ja auch einfach den internen oszilator auf 8Mhz einstellen, 
aber dann bin ich immer noch nicht schlauer, wie der Mensch zu diesem 
Code bzw. aus diesem Coder diese Berechnung aufgestellt hat.

I²C CLK Frequency = 100kHz, CLK = 8000000/(16 + 2*(0x50)*1)

den irgendwo muss ja der prescaler *1 herkommen.

ja ich weiß im clkpr, aber das muss ja dann schon irgendwo mit 1 gesetzt 
sein. das sehe ich nicht.

 Und F_CPU8000000UL wird ja auch nicht definiert und stimmt aber dann 
troztdem die Zeiten mit 8Mhz alles überein

von spess53 (Gast)


Lesenswert?

Hi

>http://ww1.microchip.com/downloads/..

Gibt es die ATMeg88 Automobile überhaupt noch?

Der TO hat einen ATMega88PA. Und dort steht der Passus definitiv nicht 
mehr drin.

MfG Spess

von c-hater (Gast)


Lesenswert?

Stefanus F. schrieb:
> spess53 schrieb:
>>>Datenblatt Kapitel 19.5.2:
>> Welches Datenblatt?
>
> 
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-9365-Automotive-Microcontrollers-ATmega88-ATmega168_Datasheet.pdf

Das ist definitiv neu. In älteren Datenblättern findet sich das nicht.

Die erste Frage ist: alter Bug (spät entdeckt) oder neues Silizium mit 
einem neuen Bug?

Die zweite Frage: was muss man sich unter "the master may produce an 
incorrect output on SDA and SCL for the reminder of the byte" 
vorstellen? Was für ein Byte? Welchen Remainder eines Bytes könnte es 
geben? Was genau meint "incorrect"?

In den Errata steht dazu absolut garnix. Das ist doch Bullshit.

Ich würde mal darauf tippen, dass hier ein Inder was an der falschen 
Stelle in die Doku eingepflegt hat, vor allem deshalb, weil mir in der 
Realität auch keinerlei diesbezügliche Probleme begegnet sind...

von Stefan F. (Gast)


Lesenswert?

c-hater schrieb:
> Das ist definitiv neu.

Naja, von 2016. Offensichtlich habe ich das falsche Datenblatt erwischt, 
das "P" fehlt.

von spess53 (Gast)


Lesenswert?

Hi

>Ich würde mal darauf tippen, dass hier ein Inder was an der falschen
>Stelle in die Doku eingepflegt hat, vor allem deshalb, weil mir in der
>Realität auch keinerlei diesbezügliche Probleme begegnet sind...

Sehe ich auch so. Lt.meinen ATMEL-CDs ist beim ATMega88 dieser Absatz 
irgendwann zwischen Oktober 2005 und April 2006 aus dem Datenblatt 
verschwunden.
Das Datenblatt vom ATMega88 Automotive von 2016 ist die Revision A, die 
seit 02/2009 unverändert ist. Wahrscheinlich war das schon damals 
falsch.

MfG Spess

von Gerhard H. (oderlachs)


Lesenswert?

Hallo !

Der Quelltext kommt mir bekannt vor, als wenn er auch  dem dicken "AVR 
Programmierung in C" Buch ist.
Da ich da auch so einiges aus dem Buch durchprobiert habe, aber auch 
keine 8MHz Tacktfrequenz habe/hatte, habe ich mich an diesem 
Codebeispiel orientiert und etwas umgeschrieben dann:
1
definieren im Quelltextkopf :
2
......
3
#define  F_CPU 16000000UL  // 16MHz Q-frequenz
4
#define  SCL_CLOCK 100000UL // 100kHz TWI Clock
5
......
6
*
7
*
8
*
9
void i2c_init()
10
{
11
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
12
  
13
  TWSR = 0;                         /* no prescaler */
14
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  /* must be > 10 for stable operation */
15
16
}/* i2c_init */


Das Ausrechnen ob TWBR > 10 ist überlass ich mal dem Anwender... ;)

Ich hoffe ich konnt ein wenig helfen..

Gerhard

Nachtrag : den Prescaler stellt man an den beiden Bit TWPS1 TWPS0  im 
TWSTATUS-Register "TWSR" ein ..Im Datenblatt nachlesen...

: Bearbeitet durch User
von Da Huaba Bene (Gast)


Lesenswert?

Gerhard H. schrieb:
> Ich hoffe ich konnt ein wenig helfen.

Nö .... damit bringst du nur diejenigen durcheinander die
den Prescaler verwenden wollen oder müssen.

Aber Hauptsache du hast eine Ladung ASCII hier abgeladen,
das macht frei!

Gerhard H. schrieb:
> Nachtrag : den Prescaler stellt man an den beiden Bit TWPS1 TWPS0  im
> TWSTATUS-Register "TWSR" ein ..Im Datenblatt nachlesen...

Gut dass du das geschrieben hast denn bisher hat das keiner
gewusst, und sonst müsste man das mühselig im Datenblatt
nachlesen ... aber vielleicht stehts auch gar nicht drin.

von Gerhard H. (oderlachs)


Lesenswert?

Nun HababaBene,
dann hast Du ja wenigstens des Rätsels Lösung hier völlig dar gelegt.

Gruss Gerhard

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.