Forum: Mikrocontroller und Digitale Elektronik Wie viele NOP für 100NS-Clock? (Software-SPI)


von Dietmar (Gast)


Lesenswert?

Wie viele NOP brauchts zwischen SPI_SCK_HIGH() und SPI_SCK_LOW() bei 
18.432MHz (54ns pro Takt), wenn das Datenblatt des IC sagt, dass 
High/Low-Phase der Clock jeweils nicht kürzer als 100NS sein dürfen (4 
Mbit/s SC16IS750)? Hätte gedacht zwei, vielleicht einen, aber es geht 
mit 0?!
1
// 100ns Verzögerung
2
3
#define DELAY_100NS()    \
4
__asm__ __volatile__ (   \
5
  "nop\n"          \
6
  "nop\n"          \
7
)
8
9
#define DELAY_50NS()     \
10
__asm__ __volatile__ (   \
11
  "nop\n"          \
12
)
13
14
#define SPI_SCK_HIGH() PORTD |=  BITMASK4
15
#define SPI_SCK_LOW()  PORTD &= ~BITMASK4
16
17
void
18
softspi_write_byte(uint8_t out)
19
{
20
  uint8_t bitmask;
21
22
  // SCK and /CS must be low when arriving here (setup by caller)
23
24
  for (bitmask = 128; bitmask; bitmask >>= 1)
25
  {
26
    if (out & bitmask)
27
      SPI_MOSI_HIGH();
28
    else
29
      SPI_MOSI_LOW ();
30
31
    DELAY_50NS();
32
33
    SPI_SCK_HIGH();
34
35
    DELAY_50NS();
36
37
    SPI_SCK_LOW();
38
39
    DELAY_50NS();
40
  }
41
}

von egon (Gast)


Lesenswert?

Moin,

mach mal ein Disassemble auf den Code. Das Setzen der Bits braucht ja 
auch seine Zeit und da wirst du sehen das du da wahrscheinlich schon 
>100ns bist.

von Wolfgang (Gast)


Lesenswert?

Dietmar schrieb:
> Hätte gedacht zwei, vielleicht einen, aber es geht mit 0?!

Was sprich dagegen, die Pulslänge einfach auszumessen?

von Hermann K. (r2d2)


Lesenswert?

Oder häng einfach mal ein Oszi/Logic-Analyzer dran. Dann siehst du wie 
dein Timing ausschaut.

Generell hast du aber eh ein Problem: Dein Code ist nicht auf andere 
Taktfrequenzen portierbar und das Timing kann sich in Abhängigkeit von 
Compiler und der eingestellten Optimierung ändern.

von Hans (Gast)


Lesenswert?

egon schrieb:
> Disassemble

Damit wäre gemeint dem gcc dem Parameter "-S" mitzugeben...

73

von Hans (Gast)


Lesenswert?

Dietmar schrieb:
> Hätte gedacht zwei, vielleicht einen, aber es geht mit 0?!

Welcher optimizer Level? Bei -Ofast müsstest du unglücklich 
implementiert haben (glaube ich nicht)... Bei -O0 wäre das aber 
normal... Btw -Og ist debugbar und annehmbar klein/schnell

73

von Wolfgang (Gast)


Lesenswert?

Hermann K. schrieb:
> Generell hast du aber eh ein Problem: Dein Code ist nicht auf andere
> Taktfrequenzen portierbar ...

Dafür steckt er hoffentlich im HAL

von Nico W. (nico_w)


Lesenswert?

Ist das ein read modify write? Der braucht mindestens 3 Takte. Also > 
150ns.

von Peter D. (peda)


Lesenswert?

Dietmar schrieb:
> Hätte gedacht zwei, vielleicht einen, aber es geht
> mit 0?!

SBI/CBI dauern schon je 2 Zyklen (108ns). Deine Loop enthält aber noch 
viel mehr Code, ist also deutlich langsamer als 216ns.

Schneller geht es mit dem HW-SPI, noch schneller mit der UART im 
SPI-Mode. Dann sollten 3,072MHz zu schaffen sein.

von Dietmar (Gast)


Lesenswert?

> Oder häng einfach mal ein Oszi/Logic-Analyzer dran

Gemacht. Statt 100ns-Takt war es ein 800ns low/164ns high-Takt (Version 
W1). Oops. Ziemlich verschätzt. Mit aufgerollter Schleife (Version W2) 
sind es jetzt 162ns low / 108ns high.

Die Lese-Schleife war nicht ganz so schlimm. 380ns low, 162ns high (R1). 
Unrolling brachte 108ns low, 109ns high (Version R2), aber das war wohl 
zu knapp - das hat nicht funktioniert. Version R3 geht (160ns low, 164ns 
high).

--- Anhänge

Version W1: 164ns hi, 760ns bis 814ns low
1
for (uint8_t bitmask = 128; bitmask; bitmask >>= 1)
2
{
3
  if (out & bitmask)
4
    SPI_MOSI_HIGH();
5
  else
6
    SPI_MOSI_LOW ();
7
8
  DELAY_50NS();
9
10
  SPI_SCK_HIGH();
11
12
  DELAY_50NS();
13
14
  SPI_SCK_LOW();
15
16
  DELAY_50NS();
17
}

Version W2: 110ns high, 542ns low
1
for (uint8_t bitmask = 128; bitmask;)
2
{
3
  if (out & bitmask)
4
    SPI_MOSI_HIGH();
5
  else
6
    SPI_MOSI_LOW();
7
8
  SPI_SCK_TOGGLE();                    // clock now high
9
10
  bitmask >>= 1;                       // place here to generate pause
11
12
  SPI_SCK_TOGGLE();                    // clock now low
13
}

Version W3: 108ns high, 162ns low
1
// bit 7
2
3
if (out & 128)
4
  SPI_MOSI_HIGH();
5
else
6
  SPI_MOSI_LOW();
7
8
SPI_SCK_TOGGLE();                    // clock now high
9
10
DELAY_50NS();
11
12
SPI_SCK_TOGGLE();                    // clock now low (idle)
13
14
// bit 6
15
16
if (out & 64)
17
  SPI_MOSI_HIGH();
18
else
19
  SPI_MOSI_LOW();
20
21
SPI_SCK_TOGGLE();                    // clock now high
22
23
DELAY_50NS();
24
25
SPI_SCK_TOGGLE();                    // clock now low (idle)
26
27
// bit 7
28
29
if (out & 32)
30
  SPI_MOSI_HIGH();
31
else
32
  SPI_MOSI_LOW();
33
34
SPI_SCK_TOGGLE();                    // clock now high
35
36
DELAY_50NS();
37
38
SPI_SCK_TOGGLE();                    // clock now low (idle)
39
40
if (out & 16)
41
  SPI_MOSI_HIGH();
42
else
43
  SPI_MOSI_LOW();
44
45
SPI_SCK_TOGGLE();                    // clock now high
46
47
DELAY_50NS();
48
49
SPI_SCK_TOGGLE();                    // clock now low (idle)
50
51
if (out & 8)
52
  SPI_MOSI_HIGH();
53
else
54
  SPI_MOSI_LOW();
55
56
SPI_SCK_TOGGLE();                    // clock now high
57
58
DELAY_50NS();
59
60
SPI_SCK_TOGGLE();                    // clock now low (idle)
61
62
if (out & 4)
63
  SPI_MOSI_HIGH();
64
else
65
  SPI_MOSI_LOW();
66
67
SPI_SCK_TOGGLE();                    // clock now high
68
69
DELAY_50NS();
70
71
SPI_SCK_TOGGLE();                    // clock now low (idle)
72
73
if (out & 2)
74
  SPI_MOSI_HIGH();
75
else
76
  SPI_MOSI_LOW();
77
78
SPI_SCK_TOGGLE();                    // clock now high
79
80
DELAY_50NS();
81
82
SPI_SCK_TOGGLE();                    // clock now low (idle)
83
84
if (out & 1)
85
  SPI_MOSI_HIGH();
86
else
87
  SPI_MOSI_LOW();
88
89
SPI_SCK_TOGGLE();                    // clock now high
90
91
DELAY_50NS();
92
93
SPI_SCK_TOGGLE();                    // clock now low (idle)

Version R1: 162ns high, 380ns low
1
uint8_t bitmask, in;
2
3
for (in = 0, bitmask = 128; bitmask; bitmask >>= 1)
4
{
5
  // read 100NS after clock went low
6
7
  if (SPI_MISO_READ())
8
9
    in |= bitmask;
10
11
  SPI_SCK_HIGH();
12
13
  DELAY_50NS();
14
15
  SPI_SCK_LOW();
16
}

Version R2: 109ns high, 108ns low
1
uint8_t in = 0;
2
3
// bit 7
4
5
if (SPI_MISO_READ())
6
7
  in |= 128;
8
9
SPI_SCK_TOGGLE();                    // clock now high
10
11
DELAY_50NS();
12
13
SPI_SCK_TOGGLE();                    // clock now low (idle)
14
15
// bit 6
16
17
if (SPI_MISO_READ())
18
19
  in |= 64;
20
21
SPI_SCK_TOGGLE();                    // clock now high
22
23
DELAY_50NS();
24
25
SPI_SCK_TOGGLE();                    // clock now low (idle)
26
27
// bit 5
28
29
if (SPI_MISO_READ())
30
31
  in |= 32;
32
33
SPI_SCK_TOGGLE();                    // clock now high
34
35
DELAY_50NS();
36
37
SPI_SCK_TOGGLE();                    // clock now low (idle)
38
39
// bit 4
40
41
if (SPI_MISO_READ())
42
43
  in |= 16;
44
45
SPI_SCK_TOGGLE();                    // clock now high
46
47
DELAY_50NS();
48
49
SPI_SCK_TOGGLE();                    // clock now low (idle)
50
51
// bit 3
52
53
if (SPI_MISO_READ())
54
55
  in |= 8;
56
57
SPI_SCK_TOGGLE();                    // clock now high
58
59
DELAY_50NS();
60
61
SPI_SCK_TOGGLE();                    // clock now low (idle)
62
63
// bit 2
64
65
if (SPI_MISO_READ())
66
67
  in |= 4;
68
69
SPI_SCK_TOGGLE();                    // clock now high
70
71
DELAY_50NS();
72
73
SPI_SCK_TOGGLE();                    // clock now low (idle)
74
75
// bit 1
76
77
if (SPI_MISO_READ())
78
79
  in |= 2;
80
81
SPI_SCK_TOGGLE();                    // clock now high
82
83
DELAY_50NS();
84
85
SPI_SCK_TOGGLE();                    // clock now low (idle)
86
87
// bit 0
88
89
if (SPI_MISO_READ())
90
91
  in |= 1;
92
93
SPI_SCK_TOGGLE();                    // clock now high
94
95
DELAY_50NS();
96
97
SPI_SCK_TOGGLE();                    // clock now low (idle)

Version R3:
1
uint8_t in = 0;
2
3
// bit 7
4
5
if (SPI_MISO_READ())
6
7
  in |= 128;
8
9
SPI_SCK_TOGGLE();                                // clock now high
10
11
DELAY_100NS();
12
13
SPI_SCK_TOGGLE();                                // clock now low (idle)
14
15
DELAY_50NS();
16
17
// bit 6
18
19
if (SPI_MISO_READ())
20
21
  in |= 64;
22
23
SPI_SCK_TOGGLE();                                // clock now high
24
25
DELAY_100NS();
26
27
SPI_SCK_TOGGLE();                                // clock now low (idle)
28
29
DELAY_50NS();
30
31
// bit 5
32
33
if (SPI_MISO_READ())
34
35
  in |= 32;
36
37
SPI_SCK_TOGGLE();                                // clock now high
38
39
DELAY_100NS();
40
41
SPI_SCK_TOGGLE();                                // clock now low (idle)
42
43
DELAY_50NS();
44
45
// bit 3
46
47
if (SPI_MISO_READ())
48
49
  in |= 16;
50
51
SPI_SCK_TOGGLE();                                // clock now high
52
53
DELAY_100NS();
54
55
SPI_SCK_TOGGLE();                                // clock now low (idle)
56
57
DELAY_50NS();
58
59
// bit 3
60
61
if (SPI_MISO_READ())
62
63
  in |= 8;
64
65
SPI_SCK_TOGGLE();                                // clock now high
66
67
DELAY_100NS();
68
69
SPI_SCK_TOGGLE();                                // clock now low (idle)
70
71
DELAY_50NS();
72
73
// bit 2
74
75
if (SPI_MISO_READ())
76
77
  in |= 4;
78
79
SPI_SCK_TOGGLE();                                // clock now high
80
81
DELAY_100NS();
82
83
SPI_SCK_TOGGLE();                                // clock now low (idle)
84
85
DELAY_50NS();
86
87
// bit 1
88
89
if (SPI_MISO_READ())
90
91
  in |= 2;
92
93
SPI_SCK_TOGGLE();                                // clock now high
94
95
DELAY_100NS();
96
97
SPI_SCK_TOGGLE();                                // clock now low (idle)
98
99
DELAY_50NS();
100
101
// bit 0
102
103
if (SPI_MISO_READ())
104
105
  in |= 1;
106
107
SPI_SCK_TOGGLE();                                // clock now high
108
109
DELAY_100NS();
110
111
SPI_SCK_TOGGLE();                                // clock now low (idle)
112
113
DELAY_50NS();
114
115
return(in);

von Dietmar (Gast)


Lesenswert?

PS:
1
#define SPI_SCK_HIGH()       PORTD |=  BITMASK4
2
#define SPI_SCK_LOW()        PORTD &= ~BITMASK4
3
#define SPI_SCK_TOGGLE()     PIND   =  BITMASK4 /* setting a bit in PINx (like this, not |=) toggles corresponding bit in PORTx */
4
#define SPI_CS_HIGH()        PORTD |=  BITMASK7
5
#define SPI_CS_LOW()         PORTD &= ~BITMASK7
6
#define SPI_MOSI_HIGH()      PORTD |=  BITMASK5
7
#define SPI_MOSI_LOW()       PORTD &= ~BITMASK5
8
#define SPI_MOSI_TOGGLE()    PIND   =  BITMASK5 /* setting a bit in PINx (like this, not |=) toggles corresponding bit in PORTx */
9
#define SPI_MISO_READ()      PIND   &  BITMASK3

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.