-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathi2c.c
More file actions
156 lines (135 loc) · 4.12 KB
/
i2c.c
File metadata and controls
156 lines (135 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
* File: i2c.c
* -----------
*
* Authored by Anna
* Bug fix by Chris Gregg and Tom Welch
*
*/
#include "gpio.h"
#include "i2c.h"
#include "timer.h"
struct I2C { // I2C registers
int control;
int status;
int data_length;
int peripheral_address;
int data_fifo;
int clock_divider;
int data_delay;
int clock_stretch_timeout;
};
#define CONTROL_READ 0x0001
#define CONTROL_CLEAR_FIFO 0x0010
#define CONTROL_START 0x0080
#define CONTROL_ENABLE 0x8000
#define STATUS_TRANSFER_ACTIVE 0x001
#define STATUS_TRANSFER_DONE 0x002
#define STATUS_FIFO_NEED_WRITE 0x004
#define STATUS_FIFO_NEED_READ 0x008
#define STATUS_FIFO_CAN_WRITE 0x010
#define STATUS_FIFO_CAN_READ 0x020
#define STATUS_FIFO_EMPTY 0x040
#define STATUS_FIFO_FULL 0x080
#define STATUS_ERROR_PERIPHERAL_ACK 0x100
#define STATUS_TIMEOUT 0x200
#define FIFO_MAX_SIZE 16
/*
* There are three I2C controllers on the Raspberry Pi, with the following
* bus addresses:
* • BSC0: 0x7E20_5000
* • BSC1: 0x7E80_4000
* • BSC2: 0x7E80_5000
* We are using BSC1. The other two do not have available GPIO pins
* for use on the A+ model.
*/
#define SDA GPIO_PIN2
#define SCL GPIO_PIN3
#define BSC_BASE 0x20804000
static volatile struct I2C *i2c = (struct I2C *) BSC_BASE;
// #define SHORT_DELAY 50
#define NORM_DELAY 500
void i2c_init(void) {
gpio_set_function(SDA, GPIO_FUNC_ALT0);
gpio_set_function(SCL, GPIO_FUNC_ALT0);
i2c->control = CONTROL_ENABLE;
}
void i2c_read(unsigned peripheral_address, char *data, int data_length) {
// clear out the FIFO
i2c->control |= CONTROL_CLEAR_FIFO;
while (!(i2c->status & STATUS_FIFO_EMPTY))
;
// clear previous transfer's flags
i2c->status |= STATUS_TRANSFER_DONE |
STATUS_ERROR_PERIPHERAL_ACK |
STATUS_TIMEOUT;
// set peripheral address + data length
i2c->peripheral_address = peripheral_address;
i2c->data_length = data_length;
int data_index = 0;
// begin read
i2c->control |= CONTROL_READ | CONTROL_START;
timer_delay_us(NORM_DELAY);
// keep reading until transfer is complete
while ((i2c->status & STATUS_FIFO_CAN_READ) &&
(!(i2c->status & STATUS_TRANSFER_DONE) ||
(data_index < data_length))) {
timer_delay_us(40);
data[data_index++] = i2c->data_fifo;
}
#if 0
// inform end user of potential responses
if (i2c->status & STATUS_ERROR_PERIPHERAL_ACK) {
printf("NACK received.\n");
}
if (i2c->status & STATUS_TIMEOUT) {
printf("Clock timed out.\n");
}
if (data_index < data_length) {
printf("Data transfer incomplete.\n");
}
#endif
}
void i2c_write(unsigned peripheral_address, char *data, int data_length) {
// clear out the FIFO
i2c->control |= CONTROL_CLEAR_FIFO;
while (!(i2c->status & STATUS_FIFO_EMPTY))
;
// clear previous transfer's flags
i2c->status |= STATUS_TRANSFER_DONE |
STATUS_ERROR_PERIPHERAL_ACK |
STATUS_TIMEOUT;
// set peripheral address + data length
i2c->peripheral_address = peripheral_address;
i2c->data_length = data_length;
int data_index = 0;
// write first 16 chunks into FIFO
while ((data_index < FIFO_MAX_SIZE) &&
(data_index < data_length)) {
i2c->data_fifo = data[data_index++];
}
// begin write
i2c->control &= ~CONTROL_READ;
i2c->control |= CONTROL_START;
// as fifo clears up, continue transferring until done
while (!(i2c->status & STATUS_TRANSFER_DONE) &&
(i2c->status & STATUS_FIFO_CAN_WRITE) &&
(data_index < data_length)) {
i2c->data_fifo = data[data_index++];
}
// wait until the FIFO's contents are emptied by the peripheral
while (!(i2c->status & STATUS_FIFO_EMPTY))
;
#if 0
// inform end user of potential responses
if (i2c->status & STATUS_ERROR_PERIPHERAL_ACK) {
printf("NACK received.\n");
}
if (i2c->status & STATUS_TIMEOUT) {
printf("Clock timed out.\n");
}
if (data_index < data_length) {
printf("Data transfer incomplete.\n");
}
#endif
}