Forum: Mikrocontroller und Digitale Elektronik TWI Slave Code für ATtiny 2-series


von Ben S. (theben)


Lesenswert?

Hallo Leute

ausgehend von dieser Frage hier: 
Beitrag "I2C Slave mit 2 Adressen" konnte ich meinen Code 
weiter entwickeln. Doch beim übertragen der der Daten an den Master 
(natürlich vom Master angefordert) habe ich noch so meine Probleme.
Daher würde ich mich gern an einem TWI Slave Beispiel orintieren. Alle 
Examples, die ich im Netz finden kann behandeln die alten ATtinys. Ich 
habe jedoch einen ATtiny1626 aus der 2-series vor mir zu liegen. Bei 
diesen neuen Typen ist die Register Zugriffs Syntax anders:
herkömmlich:
1
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
2-series:
1
TWI0.SADDR = addr0|0x01;
2
  TWI0.SCTRLA = TWI_ENABLE_bm |  
3
  TWI_APIEN_bm |
4
  TWI_PIEN_bm |
5
  TWI_DIEN_bm |
6
  TWI_SMEN_bm;
(ja ich weis, es sind zwei unterschiedliche zeilen code, die was anders 
machen. Ich will nur die Syntax Unterschiede aufzeigen TWCR = (1<<TWINT) 
vs. TWI0.SCTRLA = TWI_ENABLE_bm)

Kenn einer von euch ne Lib mit dieser Code Schreibweise?

von Ben S. (theben)


Lesenswert?

Vielleicht sieht auch einer direkt meinen fehler.
Hier der Code vom Slave:
1
ISR(TWI0_TWIS_vect)
2
{
3
  twirxbuf_size_t tmphead;
4
  uint8_t lastRxError;
5
  uint8_t temp;
6
  temp = isTransmissionStart;
7
  
8
  if(TWI0.SSTATUS & TWI_APIF_bm)          //Address match/stop interrupt
9
  {
10
    if (TWI0.SSTATUS & TWI_COLL_bm)
11
    {
12
      TWI0.SSTATUS |= TWI_COLL_bm;      //Clear Collision flag
13
      TWI0_SCTRLB = TWI_SCMD_COMPTRANS_gc;  //complete transaction
14
      // TODO: Buffer löschen
15
      isTransmissionStart = 0;
16
      isTransmissionStop = 0;
17
      command = 0xff;
18
      return;
19
    }
20
    if(TWI0.SSTATUS & TWI_AP_bm)
21
    {
22
      if(isTransmissionStart)
23
      {
24
        // nix machen
25
      }
26
      else
27
      {
28
        dstAddress = TWI0.SDATA;        // Adresse merken
29
        isTransmissionStart = 1;        // Start markieren (für ankommende Nachrichten blockieren)
30
        command = 0xff;
31
      }
32
      TWI0_SCTRLB = TWI_SCMD_RESPONSE_gc;    //Send ACK after address match
33
    }
34
    else
35
    {
36
      TWI0_SCTRLB = TWI_SCMD_COMPTRANS_gc;  //complete transaction after Stop
37
      if(command == TWI_CMD_TX)
38
      {
39
        isTransmissionStop = 1;  
40
      }
41
      isTransmissionStart = 0;
42
    }
43
  }
44
  
45
  if((TWI0.SSTATUS & TWI_DIF_bm))          //Data interrupt
46
  {
47
    if(TWI0.SSTATUS & TWI_DIR_bm)
48
    {
49
      switch(command)
50
      {
51
        case TWI_CMD_GET_BUFFERCOUNT:
52
          // TODO: Zahlen größer 255
53
          if(dstAddress == ADDR_UART0)
54
            TWI0.SDATA = (uint8_t)main_getBuf0FillLevel();
55
          else
56
            TWI0.SDATA = (uint8_t)main_getBuf1FillLevel();
57
          break;
58
        case TWI_CMD_RX:
59
          if(dstAddress == ADDR_UART0)
60
            TWI0.SDATA = main_getRx0();
61
          else
62
            TWI0.SDATA = main_getRx1();
63
            
64
          
65
          break;
66
        default:
67
          // TODO: Fehlerbehandlung
68
          break;
69
      }
70
      TWI0_SCTRLB = TWI_SCMD_RESPONSE_gc;
71
    }
72
    else
73
    {
74
      TWI0_SCTRLB = TWI_SCMD_RESPONSE_gc;
75
      if(temp == 1)
76
      {
77
        if(command == 0xff)
78
        {
79
          command = TWI0.SDATA;
80
        }
81
        else if(command == TWI_CMD_TX)
82
        {
83
          setInRxBuffer(TWI0.SDATA);
84
        }
85
        else
86
        {
87
          // TODO: Fehlerbehandlung...
88
        }          
89
      }     
90
    }
91
  }
92
}

Und hier der Code vom Master (es wird eine "funktionierende Lib 
verwendet"). Das Erfragen des BUFFERCOUNT funktioniert ganz gut:
1
i2c_start(addrVal+I2C_WRITE);
2
i2c_write(TWI_CMD_GET_BUFFERCOUNT);
3
i2c_start(addrVal+I2C_READ);
4
uint8_t result = i2c_read(0);
5
i2c_stop();

Und das ist der Code zum Erfagen des Buffer Inhaltes. dort bekomme ich 
nur 0x02 pro ausgelesener Bufferstelle zurück
1
ret = i2c_start(addr+I2C_WRITE);
2
i2c_write(TWI_CMD_RX);
3
for(i = 0; i < count - 1;i++)
4
{
5
  c = i2c_read(1);
6
  UART_PUT_C(c);
7
}    
8
    
9
c = i2c_read(0);
10
i2c_stop();

von Stefan H. (stefan_h570)


Lesenswert?

Hallo Ben S.

Ich habe meinen TWI Client auf Basis von ATMEL Start 
(https://start.atmel.com/#) aufgebaut.
Hatte zum Testen jetzt nur das "ATtiny817 Xplained Mini" mit dem 
"ATtiny817" zur Hand, werde in Zukunft aber auch einen 2-series ATtiny 
(wahrscheinlich 827) einsetzen.

IDE: MPLAB X v6.15, Compiler: XC8 v2.45, ATtiny_dfp: 3.1.260
1
/*****************************************************
2
 Date               : 08.01.2024
3
 Author             : stefan h.
4
 Chip type          : ATtiny817 / ATtiny817 Xplained Mini 
5
 Program type       : Application
6
 Clock frequency    : 20,000000 MHz
7
 Data Stack size    :
8
*****************************************************/
9
//------------------------------------- INCLUDE --------------------------------
10
#include <xc.h> // include processor files - each processor file is guarded.
11
#include <util/atomic.h>
12
#include <util/twi.h>
13
14
//------------------------------------- DEFINES --------------------------------
15
#define TRUE                    1
16
#define FALSE                   0
17
#define INVALID                 -1
18
#define ON                      1
19
#define OFF                     0
20
#define START                   1
21
#define STOP                    0
22
#define SET                     1
23
#define CLEAR                   0
24
#define TOGGLE                  1
25
#define BOOL                    signed char
26
#define CHAR                    char
27
#define U8                      unsigned char
28
#define S8                      signed char
29
30
//-------------------------------------- FUSES ---------------------------------
31
// ATtiny817 Configuration Bit Settings
32
33
FUSES = {
34
    .WDTCFG = 0x08, // WDTCFG {PERIOD=1KCLK, WINDOW=OFF}
35
    .BODCFG = 0xE5, // BODCFG {SLEEP=ENABLED, ACTIVE=ENABLED, SAMPFREQ=1KHz, LVL=BODLEVEL7}
36
    .OSCCFG = 0x7E, // OSCCFG {FREQSEL=20MHZ, OSCLOCK=CLEAR}
37
    .TCD0CFG = 0x00, // TCD0CFG {CMPA=CLEAR, CMPB=CLEAR, CMPC=CLEAR, CMPD=CLEAR, CMPAEN=CLEAR, CMPBEN=CLEAR, CMPCEN=CLEAR, CMPDEN=CLEAR}
38
    .SYSCFG0 = 0xF6, // SYSCFG0 {EESAVE=CLEAR, RSTPINCFG=UPDI, CRCSRC=NOCRC}
39
    .SYSCFG1 = 0xFF, // SYSCFG1 {SUT=64MS}
40
    .APPEND = 0x00, // APPEND {APPEND=User range:  0x0 - 0xFF}
41
    .BOOTEND = 0x00, // BOOTEND {BOOTEND=User range:  0x0 - 0xFF}
42
};
43
44
LOCKBITS = 0xC5; // {LB=NOLOCK}
45
46
//------------------------------------ PORTMUX ---------------------------------
47
/** PortMux
48
 * alternate Pins of TWI0 to Pin A2 and A1
49
 */ 
50
#define PORTMUX_CONFIG        PORTMUX.CTRLB = (PORTMUX_TWI0_ALTERNATE_gc)
51
52
//------------------------------------- PORT -----------------------------------
53
/** Port A Configuration Register
54
 *  PIN     FUNCTION        DDR     PORT
55
 *  PIN7:   DIAG            input   pull-up     
56
 *  PIN6:   NC              input   pull-up
57
 *  PIN5:   NC              input   pull-up
58
 *  PIN4:   NC              input   pull-up
59
 *  PIN3:   NC              input   pull-up
60
 *  PIN2:   TWI0 SCL        input   pull-up     (TWI0 config overwrites)
61
 *  PIN1:   TWI0 SDA        input   pull-up     (TWI0 config overwrites)
62
 *  PIN0:   RESET           input   pull-up
63
 **/ 
64
#define PORTA_CONFIG            PORTA.PIN7CTRL  = (PORT_PULLUPEN_bm); \
65
                                PORTA.PIN6CTRL  = (PORT_PULLUPEN_bm); \
66
                                PORTA.PIN5CTRL  = (PORT_PULLUPEN_bm); \
67
                                PORTA.PIN4CTRL  = (PORT_PULLUPEN_bm); \
68
                                PORTA.PIN3CTRL  = (PORT_PULLUPEN_bm); \
69
                                PORTA.PIN2CTRL  = (PORT_PULLUPEN_bm); \
70
                                PORTA.PIN1CTRL  = (PORT_PULLUPEN_bm); \
71
                                PORTA.PIN0CTRL  = (PORT_PULLUPEN_bm); \
72
                                PORTA.OUT       = (0x00); \
73
                                PORTA.DIR       = 0x00
74
75
//------------------------------------ CLOCK -----------------------------------
76
/** Clock Configuration
77
 *  CLK_MAIN:           20MHz
78
 *  CLK_PER:            2MHz
79
 **/
80
#define F_CPU                   20000000UL
81
#define CLK_PER                 2000000UL
82
#define UNLOCK_CCP              CPU_CCP = CCP_IOREG_gc                          //System critical I/O register settings are protected from accidental modification. Changes to the protected I/O registers or bits, or execution of protected instructions, are only possible after the CPU writes a signature to the CCP register  
83
#define CLKCTRL_PEN             ((1<<CLKCTRL_PEN_bp)&CLKCTRL_PEN_bm)
84
#define CLKCTRL_PDIV            CLKCTRL_PDIV_10X_gc                             
85
#define CLKCTRL_LOCKEN          ((1<<CLKCTRL_LOCKEN_bp)&CLKCTRL_LOCKEN_bm)
86
#define CLK_CONFIG              UNLOCK_CCP; CLKCTRL.MCLKCTRLB = (CLKCTRL_PDIV | CLKCTRL_PEN); UNLOCK_CCP; CLKCTRL.MCLKLOCK = CLKCTRL_LOCKEN;  \
87
                                while (!(CLKCTRL.MCLKSTATUS&CLKCTRL_OSC32KS_bm) || !(CLKCTRL.MCLKSTATUS&CLKCTRL_OSC20MS_bm) || (CLKCTRL.MCLKSTATUS&CLKCTRL_SOSC_bm)){}
88
89
//------------------------------------- WDT ------------------------------------
90
/** WDT Configuration
91
  * Reset:              1K cycles (1.0s)
92
 **/
93
#define WDT_RESET               __asm("WDR")
94
                                
95
//------------------------------------ TWI -------------------------------------    
96
#define SLAVE_ADDRESS            0x00
97
#define TWI_CONFIG              TWI0.SCTRLA = (TWI_DIEN_bm | TWI_APIEN_bm | TWI_PIEN_bm | TWI_ENABLE_bm); TWI0.SADDR = SLAVE_ADDRESS      
98
#define TWI_ADDRESS_MATCH_IF    ((TWI0.SSTATUS & TWI_APIF_bm) && (TWI0.SSTATUS & TWI_AP_bm)) 
99
#define TWI_DATA_IF             (TWI0.SSTATUS & TWI_DIF_bm)
100
#define TWI_STOP_IF             ((TWI0.SSTATUS & TWI_APIF_bm) && (!(TWI0.SSTATUS & TWI_AP_bm)))
101
#define TWI_RXACK               (!(TWI0.SSTATUS & TWI_RXACK_bm))    
102
// Host wishes to read from client
103
#define TWI_MRST                (TWI0.SSTATUS & TWI_DIR_bm)
104
//send ACK after receiving data / send requested data
105
#define TWI_ACK                 TWI0.SCTRLB = (TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc);
106
//send NACK after receiving data   
107
#define TWI_NACK                TWI0.SCTRLB = (TWI_ACKACT_NACK_gc | TWI_SCMD_COMPTRANS_gc);
108
//switch to the non adressed client mode...
109
#define TWI_RESET               TWI0.SSTATUS |= (TWI_DIF_bm | TWI_APIF_bm); TWI0.SCTRLB = (TWI_SCMD_COMPTRANS_gc);
110
111
#define TWI_MRST_SIZE    4
112
#define TWI_MTSR_SIZE    4
113
enum {
114
    NO_TRANSACTION = 0,
115
    MRST,    //host receive, client transmit
116
    MTSR    //host transmit, client receive
117
};   
118
119
120
//----------------------------------- VARIABLES --------------------------------
121
volatile U8 twi_mrst[TWI_MRST_SIZE];
122
volatile U8 twi_mtsr[TWI_MTSR_SIZE];
123
volatile U8 twi_transaction_mode     = NO_TRANSACTION;    //flag to know if other program parts can alter twi_mrst/twi_mtsr buffers
124
volatile U8 twi_received             = FALSE;            //flag for rx handler (e.g. update commands)
125
volatile U8 twi_transmitted         = FALSE;            //flag for tx handler (e.g. monitoring output)
126
127
//------------------------------------ MAIN ------------------------------------
128
129
void main_init(void) {
130
    //disable global interrupt
131
    cli();
132
133
    CLK_CONFIG;
134
135
    WDT_RESET;
136
137
    PORTMUX_CONFIG;
138
139
    PORTA_CONFIG;
140
//    PORTB_CONFIG;
141
//    PORTC_CONFIG;    
142
    
143
    TWI_CONFIG;
144
145
    //enable global interrupts
146
    sei();
147
}
148
149
int main(void) {
150
    main_init();
151
152
    while (1) {
153
        WDT_RESET;
154
        if (twi_received == TRUE) {
155
            twi_received = FALSE;
156
            if (twi_transaction_mode != MTSR) {
157
                ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
158
                    //TODO: rx_handler
159
                }
160
            }
161
        }
162
        if (twi_transmitted == TRUE) {
163
            twi_transmitted = FALSE;
164
            if (twi_transaction_mode != MRST) {
165
                ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
166
                    //TODO: tx_handler
167
                }
168
            }
169
        }
170
    }
171
}
172
173
//-------------------------------------- ISR -----------------------------------
174
ISR(TWI0_TWIS_vect) {
175
    static U8 mrst_cnt = 0;
176
    static U8 mtsr_cnt = 0;
177
    
178
    static U8 isFirstByte = TRUE; // to bypass the NACK flag for the first byte in a transaction
179
180
    //TODO
181
//    if (TWI0.SSTATUS & TWI_COLL_bm) {
182
//        I2C_0_collision_callback();
183
//        return;
184
//    }
185
//
186
//    if (TWI0.SSTATUS & TWI_BUSERR_bm) {
187
//        I2C_0_bus_error_callback();
188
//        return;
189
//    }
190
191
    //ADDRESS
192
    if (TWI_ADDRESS_MATCH_IF) {
193
        if (TWI_MRST) {
194
            twi_transaction_mode = MRST;
195
            mrst_cnt = 0;
196
            isFirstByte = TRUE;
197
        } else {
198
            twi_transaction_mode = MTSR;
199
            mtsr_cnt = 0;
200
        }
201
        return;
202
    }
203
204
    //DATA
205
    if (TWI_DATA_IF) {
206
        if (TWI_MRST) {
207
            // Master wishes to read from slave
208
            if (TWI_RXACK || isFirstByte) {
209
                // Received ACK from master or First byte of transaction
210
                isFirstByte = FALSE;
211
                if (mrst_cnt >= TWI_MRST_SIZE) {
212
                    TWI0.SDATA = 0xFF;
213
                } else {
214
                    TWI0.SDATA = twi_mrst[mrst_cnt];
215
                    mrst_cnt++;
216
                }
217
                TWI_ACK;
218
            } else {
219
                // Received NACK from master
220
                TWI_RESET;
221
            }
222
        } else { // Master wishes to write to slave
223
            twi_mtsr[mtsr_cnt] = TWI0.SDATA;
224
            mtsr_cnt++;
225
            if (mtsr_cnt > TWI_MTSR_SIZE) {
226
                TWI_NACK;   
227
            } else {
228
                TWI_ACK;
229
            }
230
        }
231
        return;
232
    }
233
234
    // STOP
235
    if (TWI_STOP_IF) {
236
        if (twi_transaction_mode == MRST) {
237
            twi_transmitted = TRUE;
238
        } else {
239
            if (mtsr_cnt <= TWI_MTSR_SIZE) {
240
                twi_received = TRUE;
241
            }
242
        }
243
        twi_transaction_mode = NO_TRANSACTION;
244
        TWI_RESET;
245
        return;
246
    }
247
    
248
    TWI_RESET;
249
}

von Ulf P. (bastler2004)


Lesenswert?

Wichtige Regeln - erst lesen, dann posten!

    Groß- und Kleinschreibung verwenden
*** Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang 
***

von Stefan H. (stefan_h570)


Lesenswert?

Hallo Ulf P.

Ich kann den Beitrag nicht löschen: 
MikrocontrollerNet::Application::PermissionDeniedException

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.