-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathi2C.c
224 lines (165 loc) · 7.03 KB
/
i2C.c
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
* File: I2C.c
* Author: Jamie
*
* Created on 30 November 2018, 17:24
*/
#include <xc.h>
#include "globals.h"
#include "i2C.h"
//Pages 204 - 236 in data sheet
/*
* Sets up I2C module as Master mode clock = FOSC/(4 * (SSPADD + 1)) - 8MHz/(4 * (SSPADD + 1))
* Uses TRIS0 and TRIS1 for data and clock
*/
void i2C_Setup(void) {
//Two pins are used for data transfer:
//Serial clock (SCL) - RB1/AN10/INT1/SCK/SCL
//Serial data (SDA) - RB0/AN12/INT0/FLT0/SDI/SDA
//The user must configure these pins as inputs by setting
//the associated TRIS bits.
//Setting a TRISB bit (=1) will make the corresponding PORTB pin an input
TRISBbits.TRISB0 = 1;
TRISBbits.TRISB1 = 1;
//MSSP Control Register 1 (SSPCON1)
//MSSP Control Register 2 (SSPCON2)
//MSSP Status Register (SSPSTAT)
//Serial Receive/Transmit Buffer Register (SSPBUF)
//MSSP Shift Register (SSPSR) ? Not directly accessible
//MSSP Address Register (SSPADD) SSPCON1, SSPCON2 and SSPSTAT
//SSPSTAT [MMSP Status Register] (I2C mode) - page 203
SSPSTATbits.SMP = 0; //Slew Rate Control bit
SSPSTATbits.CKE = 0; //SMBus Select bit
//SSPSTATbits.DA //[READ ONLY] Data/Address bit
//SSPSTATbits.STOP //[READ ONLY] Stop bit
//SSPSTATbits.START //[READ ONLY] Start bit
//SSPSTATbits.RW //[READ ONLY] Read/Wright Information bit
//SSPSTATbits.UA //[READ ONLY] Update Address bit
//SSPSTATbits.BF //[READ ONLY] Buffer Full Status bit
//SSPCON1 [MMSP Control Register 1] (I2C mode) - page 204
SSPCON1bits.WCOL = 0; //Write Collision Detect bit
SSPCON1bits.SSPOV = 0; //Receive Overflow Indicator bit
SSPCON1bits.SSPEN = 1; //Master Synchronous Serial Port Enable bit
SSPCON1bits.CKP = 0; //SCK Release Control bit (Stretching pulse)
SSPCON1bits.SSPM3 = 1; //Master Synchronous Serial Port Mode Select bits
SSPCON1bits.SSPM2 = 0;
SSPCON1bits.SSPM1 = 0;
SSPCON1bits.SSPM0 = 0;
//SSPM3:SSPM0 @ 1000 = I2C Master mode, clock = FOSC/(4 * (SSPADD + 1))
//SSPCON2 [MMSP Control Register 2] (I2C master mode) - page 205
SSPCON2bits.GCEN = 0; //General Call Enable bit (Slave mode only)
SSPCON2bits.ACKSTAT = 0;//Acknowledge Status bit (Master Transmit mode only)
SSPCON2bits.ACKDT = 0; //Acknowledge Data bit (Master Receive mode only)
SSPCON2bits.ACKEN = 0; //Acknowledge Sequence Enable bit
SSPCON2bits.RCEN = 0; //Receive Enable bit (Master Receive mode only)
SSPCON2bits.PEN = 0; //Stop Condition Enable bit
SSPCON2bits.RSEN = 0; //Repeated Start Condition Enable bit
SSPCON2bits.SEN = 0; //Start Condition Enable/Stretch Enable bit
//clock = FOSC/(4 * (SSPADD + 1)) -> 2MHz/(SSPADD + 1)
SSPADD = 0x27; // 100KHz - page 223 - 39
}
/*
* Waits if the I2C module is busy
*/
void i2C_Wait(void) {
while(SSPSTATbits.RW == 1 || SSPCON2bits.RCEN == 1 || SSPCON2bits.PEN == 1 || SSPCON2bits.RSEN == 1 || SSPCON2bits.SEN == 1 );
}
/*
* I2C start for master
*/
void i2C_Start(void) {
i2C_Wait();
SSPCON2bits.SEN = 1; //Enable start condition
while (SSPCON2bits.SEN == 1); //Wait for start condition to finish
PIR1bits.SSPIF = 0; //Clear SSPIF
}
/*
* I2C repeated start for master
*/
void i2C_RepeatedStart(void) {
i2C_Wait();
SSPCON2bits.RSEN = 1; //Enable repeated start condition
while (SSPCON2bits.SEN == 1); //Wait for start condition to finish
PIR1bits.SSPIF = 0; //Clear SSPIF
}
/*
* I2C stop for master
*/
void i2C_Stop(void) {
SSPCON2bits.PEN = 1; //Initiate stop condition
while(SSPCON2bits.PEN == 1); //Wait for stop condition to finish
}
/*
* I2C send data
* NOTE: i2C_address must be given as bits 7:1 Address, last bit as 0
* This is so the last bit can be altered to give read or wright
*/
void i2C_SendData(uint8_t i2C_address, uint8_t bytes[], uint8_t numberOfBytes) {
i2C_Start();
SSPBUF = i2C_address; //Load byte into buffer (Sent automatically)
while(PIR1bits.SSPIF == 0); //This is set after 9th clock
PIR1bits.SSPIF = 0; //Clear SSPIF
if (SSPCON2bits.ACKSTAT == 1) { //No response was made
i2C_Stop();
return;
}
for (uint8_t i = 0; i < numberOfBytes; i++) {
SSPBUF = bytes[i]; //Load byte into buffer (Sent automatically)
while(PIR1bits.SSPIF == 0); //This is set after 9th clock
PIR1bits.SSPIF = 0; //Clear SSPIF
if (SSPCON2bits.ACKSTAT == 1) { //No response was made
break;
}
}
i2C_Stop();
}
/*
* I2C receive data
* NOTE: i2C_address must be given as bits 7:1 Address, last bit as 0
* This is so the last bit can be altered to give read or wright
*/
void i2C_ReceiveData(uint8_t i2C_address, uint8_t sendBytes[], uint8_t numberOfSendBytes, uint8_t secondStartType, uint8_t *recievedBytesPointer, uint8_t numberOfReceivedBytes) {
i2C_Start();
//Send i2C address
SSPBUF = i2C_address; //Load byte into buffer (Sent automatically)
while(PIR1bits.SSPIF == 0); //This is set after 9th clock
PIR1bits.SSPIF = 0; //Clear SSPIF
if (SSPCON2bits.ACKSTAT == 1) { //No response was made
i2C_Stop();
return;
}
// Send required data over I2C to request data
for (uint8_t i = 0; i < numberOfSendBytes; i++) {
SSPBUF = sendBytes[i]; //Load byte into buffer (Sent automatically)
while(PIR1bits.SSPIF == 0); //This is set after 9th clock
PIR1bits.SSPIF = 0; //Clear SSPIF
if (SSPCON2bits.ACKSTAT == 1) { //No response was made
i2C_Stop();
return;
}
}
if (secondStartType == RepeatedStart) {
i2C_RepeatedStart();
} else if (secondStartType == StopStart) {
i2C_Stop();
i2C_Start();
}
//Send i2C address (but modify to be for reading)
SSPBUF = i2C_address + 1; //Load byte into buffer (Sent automatically)
while(PIR1bits.SSPIF == 0); //This is set after 9th clock
PIR1bits.SSPIF = 0; //Clear SSPIF
if (SSPCON2bits.ACKSTAT == 1) { //No response was made
i2C_Stop();
return;
}
// Save received bytes
for (uint8_t i = 0; i < numberOfReceivedBytes; i++) {
SSPCON2bits.RCEN = 1; //Receive byte from slave
while (SSPSTATbits.BF == 0); //Wait for all bits to be received
recievedBytesPointer[i] = SSPBUF; //Save received byte
SSPCON2bits.ACKDT = 1; //Prepare to send NACK
SSPCON2bits.ACKEN = 1; //Initiate NACK
while (SSPCON2bits.ACKEN); //Wait for NACK to be finished
}
i2C_Stop();
}