Project

General

Profile

Statistics
| Branch: | Revision:

root / scout_avr / bom / tiny-twi-sync.c @ e92b8d00

History | View | Annotate | Download (3.3 KB)

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

    
6
#define SDA PB0
7
#define SCL PB2
8

    
9
#define RECV_BUF_SIZE 70
10
#define SEND_BUF_SIZE 30
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
  uint8_t 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
  state = IDLE;
47
}
48

    
49
static void twi_start() {
50
  // zero counter
51
  twi_set_ctr(0);
52

    
53
  state = ADDRESS;
54
  recv_idx = 0;
55
}
56

    
57
static void twi_send_ack(char is_read) {
58

    
59
  // set counter to 14
60
  twi_set_ctr(14);
61

    
62
  // set SDA low
63
  USIDR = 0;
64
  DDRB |= _BV(SDA);
65

    
66
  // set state
67
  state = is_read? SEND_READ_ACK : SEND_WRITE_ACK;
68
}
69

    
70
static void twi_send_data() {
71
  twi_set_ctr(0);
72
  if (send_queue.start == send_queue.end && !send_queue.full) {
73
    USIDR = 0xFF;
74
  } else {
75
    USIDR = send_queue.data[send_queue.start++];
76
    if (send_queue.start == SEND_BUF_SIZE)
77
      send_queue.start = 0;
78
    send_queue.full = 0;
79
  }
80
  DDRB |= _BV(SDA);
81
  state = SEND_DATA;
82
}
83

    
84
static void twi_recv_ack() {
85
  twi_set_ctr(14);
86
  DDRB &= ~_BV(SDA);
87
  state = RECV_ACK;
88
}
89

    
90
static void twi_on_overflow(void) {
91
  uint8_t input = USIDR; // TODO make sure this is correct
92
  switch (state) {
93

    
94
    case IDLE:
95
      // shouldn't reach here
96
      break;
97

    
98
    case ADDRESS:
99
      if (input >> 1 == address) {
100
        // send ack
101
        twi_send_ack(input & 1);
102
      } else {
103
        // go back to listening for start condition
104
        twi_ready();
105
      }
106
      break;
107

    
108
    case SEND_WRITE_ACK:
109
      DDRB &= ~_BV(SDA);
110
      state = RECV_DATA;
111
      break;
112

    
113
    case RECV_DATA:
114
      twi_send_ack(0);
115
      if (recv_idx < RECV_BUF_SIZE) {
116
        recv_data[recv_idx] = input;
117
        recv_idx++;
118
      }
119
      break;
120

    
121
    case SEND_READ_ACK:
122
      twi_send_data();
123
      break;
124

    
125
    case SEND_DATA:
126
      twi_recv_ack();
127
      break;
128

    
129
    case RECV_ACK:
130
      if (input & 1) {
131
        // received nak
132
        twi_ready();
133
      } else {
134
        // received ack
135
        twi_send_data();
136
      }
137
      break;
138
  }
139
}
140

    
141
static void twi_on_stop(void) {
142
  if (state == RECV_DATA) {
143
    onDataRecieved(recv_data, recv_idx);
144
    twi_ready();
145
  }
146
}
147

    
148
void smb_init(slave_rx_t callback) {
149
  // TODO do we want USICS0 (3)?
150
  USICR = _BV(USIWM1) | _BV(USIWM0) | _BV(USICS1);
151
  PORTB |= _BV(SDA) | _BV(SCL);
152
  twi_ready();
153
  onDataRecieved = callback;
154
}
155

    
156
void smb_set_address(uint8_t addr) {
157
  address = addr;
158
}
159

    
160
void smb_send_data(uint8_t* data, uint8_t length) {
161
  uint8_t i;
162
  for (i=0; i<length && !send_queue.full; i++) {
163
    send_queue.data[send_queue.end++] = data[i];
164
    if (send_queue.end == SEND_BUF_SIZE)
165
      send_queue.end = 0;
166
    if (send_queue.end == send_queue.start)
167
      send_queue.full = 1;
168
  }
169
}
170

    
171
void smb_poll(void) {
172
  if (USISR & _BV(USISIF)) {
173
    twi_start();
174
    USISR |= _BV(USISIF);
175
  }
176
  if (USISR & _BV(USIOIF)) {
177
    twi_on_overflow();
178
    USISR |= _BV(USIOIF);
179
  }
180
  if (USISR & _BV(USIPF)) {
181
    twi_on_stop();
182
    USISR |= _BV(USIPF);
183
  }
184
}