Statistics
| Branch: | Revision:

root / scout_avr / bom / tiny-twi.c @ cbe25c0a

History | View | Annotate | Download (3.29 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
struct {
29
  uint8_t start;
30
  uint8_t end;
31
  uint8_t data[SEND_BUF_SIZE];
32
  bool    full;
33
} send_queue;
34

    
35
static void twi_set_ctr(char n) {
36
  USISR = n;
37
}
38

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

    
44
  // enable start condition interrupt
45
  USICR |= _BV(USISIE);
46

    
47
  // disable counter overflow interrupt
48
  USICR &= ~_BV(USIOIE);
49

    
50
  state = IDLE;
51
}
52

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

    
57
  USISR = _BV(USISIF);
58

    
59
  // zero counter
60
  twi_set_ctr(0);
61

    
62
  state = ADDRESS;
63
  recv_idx = 0;
64
}
65

    
66
static void twi_send_ack(char is_read) {
67

    
68
  // set counter to 14
69
  twi_set_ctr(14);
70

    
71
  // set SDA low
72
  USIDR = 0;
73
  DDRB |= _BV(SDA);
74

    
75
  // set state
76
  state = is_read? SEND_READ_ACK : SEND_WRITE_ACK;
77
}
78

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

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

    
99
ISR(USI_START_vect) {
100
  twi_start();
101
}
102

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

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

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

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

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

    
138
    case SEND_READ_ACK:
139
      twi_send_data();
140
      break;
141

    
142
    case SEND_DATA:
143
      twi_recv_ack();
144
      break;
145

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

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

    
167
void smb_set_address(uint8_t addr) {
168
  address = addr;
169
}
170

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