-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathi2c.c
161 lines (139 loc) · 3.96 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
#include <string.h>
#include "i2c.h"
#include "stm32f405.h"
#include "clock.h"
#include "uart_dma.h"
#define MAX_I2C_DURATION_US (10000)
static void send_start(void);
static void send_stop(void);
void initialize_i2c(void)
{
//Enable GPIOB
RCC_AHB1ENR |= 0x00000002;
//Enable I2C 1 and 2
RCC_APB1ENR |= 0x00600000;
//PB6, PB7 as AF
GPIOB_MODER |= 0x0000A000;
//PB6 AF as AF4 (I2C1_SCL)
GPIOB_AFRL |= 0x04000000;
//PB7 AF as AF4 (I2C1_SDA)
GPIOB_AFRL |= 0x40000000;
//PB6 and PB7 as open-drain outputs
GPIOB_OTYPER |= 0x000000C0;
//PB6 and PB7 enable pull up resistors
GPIOB_PUPDR |= 0x00005000;
//PB6 and PB7 set output speed to medium
GPIOB_OSPEEDR |= 0x00005000;
//PB10, PB11 as AF
GPIOB_MODER |= 0x00A00000;
//PB10 AF as AF4 (I2C2_SCL)
GPIOB_AFRH |= 0x00000400;
//PB11 AF as AF4 (I2C2_SDA)
GPIOB_AFRH |= 0x00004000;
//PB10 and PB11 as open-drain outputs
GPIOB_OTYPER |= 0x00000C00;
//PB10 and PB11 enable pull up resistors
GPIOB_PUPDR |= 0x00500000;
//PB10 and PB11 set output speed to medium
GPIOB_OSPEEDR |= 0x00500000;
//Clock rate: 9MHz (APB1 -- see clock.c)
I2C1_CR2 = 0x0000009;
I2C1_CR2 = 0x00000012;
//Enable Fm. Set frequency to 375kHz
// I2C1_CCR = 0x00008008;
//disable Fm, actually. The ATTINY402 sucks.
I2C1_CCR = 0x00000008;
I2C1_CCR = 0x00000080;
//TRISE = ciel(300ns/111.111ns)+1. 300ns is from the I2C Fm standard.
I2C1_TRISE = 4;
I2C1_TRISE = 17;
//enable the peripheral
I2C1_CR1 |= 0x00000001;
print_str("i2c status 1\n");
print_str("I2C1_SR1=0x");
print_hex_register(I2C1_SR1);
print_str("\n");
print_str("I2C1_conf: ");
print_hex_register(I2C1_CR1);
print_str("\n");
print_str("I2C1_conf: ");
print_hex_register(I2C1_CR2);
print_str("\n");
print_str("I2C1_conf: ");
print_hex_register(I2C1_CCR);
print_str("\n");
print_str("I2C1_conf: ");
print_hex_register(I2C1_TRISE);
print_str("\n");
}
bool i2c_write(uint8_t addr, uint8_t reg_addr, uint8_t data)
{
uint32_t end_time = get_time_us() + MAX_I2C_DURATION_US;
send_start();
//send slave address
I2C1_DR = (addr << 1); //LSB indicates read/write mode. 0 is write, 1 is read.
//wait for slave address to finish transmitting
while ((I2C1_SR1 & 0x00000002) == 0)
{
if ((I2C1_SR1 & 0x00000400) != 0)
{
print_str("i2c: got NACK on address.\n");
send_stop();
return false;
}
if (((int32_t)(get_time_us() - end_time)) >= 0)
{
print_str("i2c: timeout on address.\n");
send_stop();
return false;
}
}
//The following read sequence is required to clear the ADDR bit in SR1. This must be completed before writing the data byte.
I2C1_SR1;
I2C1_SR2;
char data_buf[2];
size_t data_len = 2;
data_buf[0] = reg_addr;
data_buf[1] = data;
for (size_t i = 0; i < data_len; i++)
{
//send data byte
I2C1_DR = data_buf[i];
//wait for data to finish transmitting
while ((I2C1_SR1 & 0x00000080) == 0)
{
if ((I2C1_SR1 & 0x00000400) != 0)
{
print_str("i2c: got NACK on data.\n");
send_stop();
return false;
}
if (((int32_t)(get_time_us() - end_time)) >= 0)
{
print_str("i2c: timeout on data.\n");
send_stop();
return false;
}
}
}
send_stop();
//TODO: read from page 849 starting at "Master Receiver"
print_str("i2c: transaction succesful.\n");
return true;
}
static void send_start(void)
{
//Do a Start
I2C1_CR1 |= 0x00000100;
//wait for start condition to complete
while ((I2C1_SR1 & 0x00000001) == 0)
;
}
static void send_stop(void)
{
//Do a stop
I2C1_CR1 |= 0x00000200;
//wait for stop bit to finish transmitting
while ((I2C1_SR2 & 0x00000001) != 0)
;
}