Project

General

Profile

Statistics
| Branch: | Revision:

root / scout_avr / bom / tiny-twi.c @ 5f7a1707

History | View | Annotate | Download (3.32 KB)

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include "tiny-twi.h"
5

    
6
#define SDA PB0
7
#define SCL PB2
8

    
9
#define RECV_BUF_SIZE 3
10
#define SEND_BUF_SIZE 100
11

    
12
uint8_t address;
13

    
14
typedef enum {
15
  IDLE,
16
  ADDRESS,
17
  SEND_READ_ACK,
18
  SEND_WRITE_ACK,
19
  SEND_DATA,
20
  RECV_DATA,
21
  RECV_ACK
22
} state_t;
23

    
24
state_t state;
25
uint8_t recv_idx;
26
uint8_t recv_data[RECV_BUF_SIZE];
27
slave_rx_t onDataRecieved;
28
typedef struct {
29
  uint8_t start;
30
  uint8_t end;
31
  uint8_t data[SEND_BUF_SIZE];
32
  bool    full;
33
} queue;
34

    
35
queue send_queue;
36

    
37
static void twi_set_ctr(char n) {
38
  USISR = n;
39
}
40

    
41
static void twi_ready() {
42
  USISR = _BV(USISIF) | _BV(USIOIF) | _BV(USIPF) | _BV(USIDC);
43
  DDRB |= _BV(SCL);  //SCL output
44
  DDRB &= ~_BV(SDA); //SDA input
45

    
46
  // enable start condition interrupt
47
  USICR |= _BV(USISIE);
48

    
49
  // disable counter overflow interrupt
50
  USICR &= ~_BV(USIOIE);
51

    
52
  state = IDLE;
53
}
54

    
55
static void twi_start() {
56
  // enable counter overflow interrupt (fires after 8 bits)
57
  USICR |= _BV(USIOIE);
58

    
59
  USISR = _BV(USISIF);
60

    
61
  // zero counter
62
  twi_set_ctr(0);
63

    
64
  state = ADDRESS;
65
  recv_idx = 0;
66
}
67

    
68
static void twi_send_ack(char is_read) {
69

    
70
  // set counter to 14
71
  twi_set_ctr(14);
72

    
73
  // set SDA low
74
  USIDR = 0;
75
  DDRB |= _BV(SDA);
76

    
77
  // set state
78
  state = is_read? SEND_READ_ACK : SEND_WRITE_ACK;
79
}
80

    
81
static void twi_send_data() {
82
  twi_set_ctr(0);
83
  if (send_queue.start == send_queue.end && !send_queue.full) {
84
    USIDR = 0xFF;
85
  } else {
86
    USIDR = send_queue.data[send_queue.start++];
87
    if (send_queue.start == SEND_BUF_SIZE)
88
      send_queue.start = 0;
89
    send_queue.full = 0;
90
  }
91
  DDRB |= _BV(SDA);
92
  state = SEND_DATA;
93
}
94

    
95
static void twi_recv_ack() {
96
  twi_set_ctr(14);
97
  DDRB &= ~_BV(SDA);
98
  state = RECV_ACK;
99
}
100

    
101
ISR(USI_START_vect) {
102
  twi_start();
103
}
104

    
105
ISR(USI_OVF_vect) {
106
  uint8_t input = USIDR; // TODO make sure this is correct
107
  switch (state) {
108

    
109
    case IDLE:
110
      // shouldn't reach here
111
      twi_ready();
112
      break;
113

    
114
    case ADDRESS:
115
      if (input >> 1 == address) {
116
        // send ack
117
        twi_send_ack(input & 1);
118
      } else {
119
        // go back to listening for start condition
120
        twi_ready();
121
      }
122
      break;
123

    
124
    case SEND_WRITE_ACK:
125
      if (recv_idx == RECV_BUF_SIZE) {
126
        twi_ready();
127
        onDataRecieved(recv_data, RECV_BUF_SIZE);
128
      } else {
129
        DDRB &= ~_BV(SDA);
130
        state = RECV_DATA;
131
      }
132
      break;
133

    
134
    case RECV_DATA:
135
      twi_send_ack(0);
136
      recv_data[recv_idx] = input;
137
      recv_idx++;
138
      break;
139

    
140
    case SEND_READ_ACK:
141
      twi_send_data();
142
      break;
143

    
144
    case SEND_DATA:
145
      twi_recv_ack();
146
      break;
147

    
148
    case RECV_ACK:
149
      if (input & 1) {
150
        // received nak
151
        twi_ready();
152
      } else {
153
        // received ack
154
        twi_send_data();
155
      }
156
      break;
157
  }
158
  USISR = _BV(USIOIF) | (USISR & 0xF);
159
}
160

    
161
void smb_init(slave_rx_t callback) {
162
  // TODO do we want USICS0 (3)?
163
  USICR = _BV(USIWM1) | _BV(USIWM0) | _BV(USICS1);
164
  PORTB |= _BV(SDA) | _BV(SCL);
165
  twi_ready();
166
  onDataRecieved = callback;
167
}
168

    
169
void smb_set_address(uint8_t addr) {
170
  address = addr;
171
}
172

    
173
void smb_send_data(uint8_t* data, uint8_t length) {
174
  uint8_t i;
175
  for (i=0; i<length && !send_queue.full; i++) {
176
    send_queue.data[send_queue.end++] = data[i];
177
    if (send_queue.end == SEND_BUF_SIZE)
178
      send_queue.end = 0;
179
    if (send_queue.end == send_queue.start)
180
      send_queue.full = 1;
181
  }
182
}