forked from atraber/bt_stone
-
Notifications
You must be signed in to change notification settings - Fork 0
/
I2C.c
142 lines (118 loc) · 4.32 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
#include <msp430f5438a.h>
#include "I2C.h"
unsigned char* g_pI2CData;
unsigned char g_I2CCount;
int g_I2CError;
void I2C_init()
{
P10SEL = BIT1 + BIT2; // Assign I2C pins to USCI_B0
UCB3CTL1 |= UCSWRST; // Enable SW reset
UCB3CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB3CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB3BR0 = 160; // fSCL = SMCLK/160 = ~100kHz
UCB3BR1 = 0;
UCB3CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
UCB3IE |= UCTXIE + UCNACKIE + UCRXIE; // Enable TX interrupt, enable NACK interrupt; Enable RX interrupt
}
int I2C_write(unsigned char addr, unsigned char* TxData, unsigned char len)
{
if(len == 0)
return 0;
// load data into globals
g_I2CCount = len;
g_pI2CData = TxData;
g_I2CError = 0;
// set slave address
UCB3I2CSA = addr;
UCB3CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupts
__no_operation(); // Remain in LPM0 until all data
// is TX'd
return g_I2CError;
}
int I2C_read(unsigned char addr, unsigned char* RxData, unsigned char len)
{
// check for invalid parameters
if(len == 0)
return 0;
// load data into globals
g_I2CCount = len;
g_pI2CData = RxData;
g_I2CError = 0;
// set slave address
UCB3I2CSA = addr;
if(len == 1)
{
UCB3CTL1 &= ~UCTR; // I2C RX
UCB3CTL1 |= UCTXSTT; // I2C start condition
while(UCB3CTL1 & UCTXSTT); // Start condition sent?
UCB3CTL1 |= UCTXSTP; // I2C stop condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupts
__no_operation(); // For debugger
}
else
{
UCB3CTL1 &= ~UCTR; // I2C RX
UCB3CTL1 |= UCTXSTT; // I2C start condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupts
__no_operation(); // Set breakpoint >>here<< and
}
return g_I2CError;
}
//------------------------------------------------------------------------------
// The USCIAB0TX_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
// points to the next byte to transmit.
//------------------------------------------------------------------------------
#pragma vector = USCI_B3_VECTOR
__interrupt void USCI_B3_ISR(void)
{
switch(__even_in_range(UCB3IV,12))
{
case 0: // Vector 0: No interrupts
break;
case 2: // Vector 2: ALIFG
break;
case 4: // Vector 4: NACKIFG
// NACK means the device did not respond => set error flag
g_I2CError = 1;
UCB3CTL1 |= UCTXSTP; // I2C stop condition
UCB3STAT &= ~UCNACKIFG;
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
break;
case 6: // Vector 6: STTIFG
break;
case 8: // Vector 8: STPIFG
break;
case 10: // Vector 10: RXIFG
g_I2CCount--; // Decrement RX byte counter
if (g_I2CCount)
{
*g_pI2CData++ = UCB3RXBUF; // Move RX data to address PRxData
// generate stop condition if only one byte is left
if(g_I2CCount == 1)
UCB3CTL1 |= UCTXSTP; // Generate I2C stop condition
}
else
{
*g_pI2CData = UCB3RXBUF; // Move final RX data to PRxData
__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
}
break;
case 12: // Vector 12: TXIFG
if(g_I2CCount) // Check TX byte counter
{
UCB3TXBUF = *g_pI2CData++; // Load TX buffer
g_I2CCount--; // Decrement TX byte counter
}
else
{
UCB3CTL1 |= UCTXSTP; // I2C stop condition
UCB3IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
break;
default:
break;
}
}