GPS Device
Loading...
Searching...
No Matches
I2C Public API

Public functions to interact with the I2C peripheral. More...

Collaboration diagram for I2C Public API:

Functions

void i2c_init (I2C_CFG_ts *i2c_cfg)
 Initializes the I2C peripheral with the given configuration.
void i2c_deinit (I2C_REGDEF_ts const *i2c_instance)
 Deinitializes the I2C peripheral and disables its clock.
void i2c_master_send (I2C_REGDEF_ts *i2c_instance, uint8_t slave_addr, uint8_t *tx_buffer, uint32_t len)
 Blocking I2C master transmit. Sends a start condition, address, and data.
void i2c_master_send_continue (I2C_REGDEF_ts *i2c_instance, uint8_t *tx_buffer, uint32_t len)
 Continues a transmission started by i2c_master_send without a new START or address phase.
void i2c_master_receive (I2C_REGDEF_ts *i2c_instance, uint8_t slave_addr, uint8_t *rx_buffer, uint32_t len)
 Blocking I2C master receive. Sends a start condition, address, and reads data.
void i2c_master_set_comm (I2C_REGDEF_ts *i2c_instance, EN_STATUS_te en_status)
 Enables or disables the I2C peripheral and controls bus ownership.
void i2c_get_name (I2C_REGDEF_ts const *i2c_instance, char *name)
 Returns the name string of an I2C peripheral instance (e.g. "I2C1").

Detailed Description

Public functions to interact with the I2C peripheral.

Function Documentation

◆ i2c_init()

void i2c_init ( I2C_CFG_ts * i2c_cfg)

Initializes the I2C peripheral with the given configuration.

Enables the peripheral clock, configures clock stretching, own address, APB1 frequency, CCR, and TRISE. Does not enable the peripheral for communication; call i2c_master_set_comm with ENABLE to do so.

Default values if fields are zero-initialized:

  • Clock stretching: enabled
  • Own address: 0
  • Speed: 100 kHz
Parameters
[in]i2c_cfgPointer to the I2C configuration structure.
Note
It is recommended to zero-initialize I2C_CFG_ts before use to avoid unintended behaviour from uninitialised fields.
See also
i2c_init

Definition at line 24 of file stm32f401re_i2c.c.

24 {
25 // Enable peripheral clock
26 i2c_set_pclk(i2c_cfg->instance, ENABLE);
27
28 // Configure clock stretching (NOSTRETCH bit in CR1)
29 i2c_cfg->instance->I2C_CR1 &= ~(0x1 << I2C_CR1_NOSTRETCH);
30 i2c_cfg->instance->I2C_CR1 |= (i2c_cfg->clock_strech << I2C_CR1_NOSTRETCH);
31
32 // Configure own 7-bit address in OAR1; bit 14 must be kept set per reference manual
33 i2c_cfg->instance->I2C_OAR1 &= ~(0x7F << I2C_OAR1_ADD7_1);
34 i2c_cfg->instance->I2C_OAR1 |= ((i2c_cfg->address & 0x7F) << I2C_OAR1_ADD7_1);
35 i2c_cfg->instance->I2C_OAR1 |= (0x1 << 14);
36
37 // Set CR2 FREQ field to the APB1 clock frequency in MHz
38 uint32_t i2c_clock_hz = rcc_get_apb1_clk();
39 i2c_cfg->instance->I2C_CR2 &= ~(0x3F << I2C_CR2_FREQ);
40 i2c_cfg->instance->I2C_CR2 |= (((i2c_clock_hz / 1000000) & 0x3F) << I2C_CR2_FREQ);
41
42 // Compute CCR and TRISE based on selected speed
43 uint16_t ccr_value = 0;
44 uint8_t t_rise = 0;
45 if(i2c_cfg->speed == I2C_SPEED_100kHz) {
46 // Standard mode (SM): 100 kHz, 50% duty cycle
47 // CCR = f_PCLK1 / (2 * f_SCL)
48 i2c_cfg->instance->I2C_CCR &= ~(0x1 << I2C_CCR_FS);
49
50 ccr_value = i2c_clock_hz / (2 * 100000);
51
52 // TRISE = (t_rise_max_ns / t_PCLK1_ns) + 1 = (1000 ns / (1/f_PCLK1)) + 1
53 t_rise = (i2c_clock_hz / 1000000) + 1;
54 }
55 else if(i2c_cfg->speed == I2C_SPEED_400kHz) {
56 // Fast mode (FM): 400 kHz, DUTY = 1 gives t_low/t_high = 16/9
57 // CCR = f_PCLK1 / (25 * f_SCL)
58 i2c_cfg->instance->I2C_CCR &= ~(0x1 << I2C_CCR_FS);
59 i2c_cfg->instance->I2C_CCR |= (0x1 << I2C_CCR_FS);
60
61 i2c_cfg->instance->I2C_CCR &= ~(0x1 << I2C_CCR_DUTY);
62 i2c_cfg->instance->I2C_CCR |= (0x1 << I2C_CCR_DUTY);
63
64 ccr_value = i2c_clock_hz / (25 * 400000);
65
66 // TRISE = (t_rise_max_ns * f_PCLK1 / 1e9) + 1 = (300 ns * f_PCLK1 / 1e9) + 1
67 t_rise = ((i2c_clock_hz * 300) / 1000000000) + 1;
68 }
69 i2c_cfg->instance->I2C_CCR &= ~(0xFFF);
70 i2c_cfg->instance->I2C_CCR |= (ccr_value << I2C_CCR_CCR);
71
72 i2c_cfg->instance->I2C_TRISE &= ~(0x3F);
73 i2c_cfg->instance->I2C_TRISE |= (t_rise);
74}
@ ENABLE
Definition common.h:100
static void i2c_set_pclk(I2C_REGDEF_ts const *i2c_instance, EN_STATUS_te en_status)
Enables or disables the peripheral clock for an I2C instance.
@ I2C_SPEED_100kHz
@ I2C_SPEED_400kHz
uint32_t rcc_get_apb1_clk(void)
Returns the current APB1 (low-speed) peripheral bus clock frequency in Hz.
@ I2C_CCR_DUTY
@ I2C_CCR_CCR
@ I2C_CCR_FS
@ I2C_CR2_FREQ
@ I2C_CR1_NOSTRETCH
@ I2C_OAR1_ADD7_1
I2C_REGDEF_ts * instance
I2C_CLOCK_STRECH_te clock_strech
I2C_SPEED_te speed
uint32_t volatile I2C_OAR1
uint32_t volatile I2C_CCR
uint32_t volatile I2C_CR2
uint32_t volatile I2C_TRISE
uint32_t volatile I2C_CR1
Here is the call graph for this function:
Here is the caller graph for this function:

◆ i2c_deinit()

void i2c_deinit ( I2C_REGDEF_ts const * i2c_instance)

Deinitializes the I2C peripheral and disables its clock.

Triggers an RCC peripheral reset for the given instance and then disables the peripheral clock.

Parameters
[in]i2c_instancePointer to the I2C instance to deinitialize.
See also
i2c_deinit

Definition at line 77 of file stm32f401re_i2c.c.

77 {
78 if(i2c_instance == I2C1) {
80 }
81 else if(i2c_instance == I2C2) {
83 }
84 else if(i2c_instance == I2C3) {
86 }
87
88 i2c_set_pclk(i2c_instance, DISABLE);
89}
@ DISABLE
Definition common.h:97
void rcc_reset_periph_apb1(RCC_APB1RSTR_te periph_position)
Resets an APB1 peripheral via RCC_APB1RSTR.
#define I2C3
#define I2C1
#define I2C2
@ RCC_APB1RSTR_I2C3RST
@ RCC_APB1RSTR_I2C1RST
@ RCC_APB1RSTR_I2C2RST
Here is the call graph for this function:

◆ i2c_master_send()

void i2c_master_send ( I2C_REGDEF_ts * i2c_instance,
uint8_t slave_addr,
uint8_t * tx_buffer,
uint32_t len )

Blocking I2C master transmit. Sends a start condition, address, and data.

Generates a START condition, sends the 7-bit slave address with the write bit, then transmits len bytes from tx_buffer. Waits for BTF (byte transfer finished) after the last byte before returning. Does not generate a STOP condition — call i2c_master_set_comm with DISABLE afterwards to release the bus.

Parameters
[in]i2c_instancePointer to the I2C peripheral instance.
[in]slave_addr7-bit slave address (right-aligned, without the R/W bit).
[in]tx_bufferPointer to the data buffer to transmit.
[in]lenNumber of bytes to transmit.
Note
The peripheral must be enabled via i2c_master_set_comm before calling this function.

Blocking I2C master transmit. Sends a start condition, address, and data.

See also
i2c_master_send

Definition at line 92 of file stm32f401re_i2c.c.

92 {
93 uint16_t volatile dummy_read = 0;
94 (void)dummy_read;
95
96 // Generate START condition
97 i2c_instance->I2C_CR1 |= (0x1 << I2C_CR1_START);
98
99 // Wait for SB (start bit) flag; reading SR1 clears SB
100 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_SB) & 0x1));
101 dummy_read = i2c_instance->I2C_SR1;
102
103 // Send 7-bit slave address with write bit (LSB = 0)
104 i2c_instance->I2C_DR = (slave_addr << 1);
105
106 // Wait for ADDR flag; reading SR1 then SR2 clears ADDR
107 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_ADDR) & 0x1));
108 dummy_read = i2c_instance->I2C_SR1;
109 dummy_read = i2c_instance->I2C_SR2;
110
111 // Transmit data bytes
112 while(len != 0) {
113 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_TxE) & 0x1));
114 i2c_instance->I2C_DR = *tx_buffer;
115 tx_buffer++;
116 len--;
117 }
118
119 // Wait for TxE and BTF to confirm the last byte has been shifted out
120 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_TxE) & 0x1));
121 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_BTF) & 0x1));
122}
@ I2C_CR1_START
@ I2C_SR1_TxE
@ I2C_SR1_BTF
@ I2C_SR1_SB
@ I2C_SR1_ADDR
uint32_t volatile I2C_SR1
uint32_t volatile I2C_SR2
uint32_t volatile I2C_DR
Here is the caller graph for this function:

◆ i2c_master_send_continue()

void i2c_master_send_continue ( I2C_REGDEF_ts * i2c_instance,
uint8_t * tx_buffer,
uint32_t len )

Continues a transmission started by i2c_master_send without a new START or address phase.

Transmits len additional bytes into an already-open I2C transaction. Useful for sending a command byte followed by a payload in a single bus transaction (e.g. SSD1309 framebuffer transfer).

Parameters
[in]i2c_instancePointer to the I2C peripheral instance.
[in]tx_bufferPointer to the additional data buffer to transmit.
[in]lenNumber of bytes to transmit.

Continues a transmission started by i2c_master_send without a new START or address phase.

See also
i2c_master_send_continue

Definition at line 125 of file stm32f401re_i2c.c.

125 {
126 uint16_t volatile dummy_read = 0;
127 (void)dummy_read;
128
129 // Transmit additional data bytes into the open transaction
130 while(len != 0) {
131 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_TxE) & 0x1));
132 i2c_instance->I2C_DR = *tx_buffer;
133 tx_buffer++;
134 len--;
135 }
136
137 // Wait for TxE and BTF to confirm the last byte has been shifted out
138 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_TxE) & 0x1));
139 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_BTF) & 0x1));
140}
Here is the caller graph for this function:

◆ i2c_master_receive()

void i2c_master_receive ( I2C_REGDEF_ts * i2c_instance,
uint8_t slave_addr,
uint8_t * rx_buffer,
uint32_t len )

Blocking I2C master receive. Sends a start condition, address, and reads data.

Generates a START condition, sends the 7-bit slave address with the read bit, then receives len bytes into rx_buffer. ACK/NACK is managed automatically: NACK is sent before the last byte to signal end of reception, then ACK is re-enabled before returning.

Parameters
[in]i2c_instancePointer to the I2C peripheral instance.
[in]slave_addr7-bit slave address (right-aligned, without the R/W bit).
[out]rx_bufferPointer to the buffer that will receive the data.
[in]lenNumber of bytes to receive.
Note
The peripheral must be enabled via i2c_master_set_comm before calling this function.

Blocking I2C master receive. Sends a start condition, address, and reads data.

See also
i2c_master_receive

Definition at line 143 of file stm32f401re_i2c.c.

143 {
144 uint16_t volatile dummy_read = 0;
145 (void)dummy_read;
146
147 // Generate START condition
148 i2c_instance->I2C_CR1 |= (0x1 << I2C_CR1_START);
149
150 // Wait for SB flag; reading SR1 clears SB
151 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_SB) & 0x1));
152 dummy_read = i2c_instance->I2C_SR1;
153
154 // Send 7-bit slave address with read bit (LSB = 1)
155 i2c_instance->I2C_DR = ((slave_addr << 1) | 0x1);
156
157 if(len == 1) {
158 // Single-byte reception: disable ACK before clearing ADDR so NACK
159 // is sent immediately after the byte is received
160 i2c_instance->I2C_CR1 &= ~(0x1 << I2C_CR1_ACK);
161
162 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_ADDR) & 0x1));
163 dummy_read = i2c_instance->I2C_SR1;
164 dummy_read = i2c_instance->I2C_SR2;
165
166 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_RxNE) & 0x1));
167 *rx_buffer = i2c_instance->I2C_DR;
168 }
169 else if(len > 1) {
170 // Multi-byte reception: ACK all bytes until the second-to-last
171 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_ADDR) & 0x1));
172 dummy_read = i2c_instance->I2C_SR1;
173 dummy_read = i2c_instance->I2C_SR2;
174
175 while(len != 0) {
176 if(len == 2) {
177 // Disable ACK before reading the penultimate byte so NACK
178 // is sent after the last byte
179 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_RxNE) & 0x1));
180 i2c_instance->I2C_CR1 &= ~(0x1 << I2C_CR1_ACK);
181
182 *rx_buffer = i2c_instance->I2C_DR;
183 rx_buffer++;
184 len--;
185 }
186 else {
187 while(!((i2c_instance->I2C_SR1 >> I2C_SR1_RxNE) & 0x1));
188 *rx_buffer = i2c_instance->I2C_DR;
189 rx_buffer++;
190 len--;
191 }
192 }
193 }
194
195 // Re-enable ACK for subsequent transactions
196 i2c_instance->I2C_CR1 |= (0x1 << I2C_CR1_ACK);
197}
@ I2C_CR1_ACK
@ I2C_SR1_RxNE

◆ i2c_master_set_comm()

void i2c_master_set_comm ( I2C_REGDEF_ts * i2c_instance,
EN_STATUS_te en_status )

Enables or disables the I2C peripheral and controls bus ownership.

  • ENABLE: sets the PE bit, enabling the peripheral, and enables ACK generation. Must be called before any transfer function.
  • DISABLE: generates a STOP condition, waits for the bus to become idle (BUSY = 0), then clears PE to release the bus. Must be called after each complete transaction.

Between ENABLE and DISABLE calls the bus is held and cannot be taken by another master.

Parameters
[in]i2c_instancePointer to the I2C peripheral instance.
[in]en_statusENABLE to take the bus, DISABLE to release it.
See also
i2c_master_set_comm

Definition at line 200 of file stm32f401re_i2c.c.

200 {
201 if(en_status == ENABLE) {
202 // Enable peripheral and ACK generation
203 i2c_instance->I2C_CR1 |= (0x1 << I2C_CR1_PE);
204 i2c_instance->I2C_CR1 |= (0x1 << I2C_CR1_ACK);
205 }
206 else if(en_status == DISABLE) {
207 // Generate STOP condition, wait for bus to go idle, then disable PE
208 i2c_instance->I2C_CR1 |= (0x1 << I2C_CR1_STOP);
209 while((i2c_instance->I2C_SR2 >> I2C_SR2_BUSY) & 0x01);
210 i2c_instance->I2C_CR1 &= ~(0x1 << I2C_CR1_PE);
211 }
212}
@ I2C_CR1_STOP
@ I2C_CR1_PE
@ I2C_SR2_BUSY
Here is the caller graph for this function:

◆ i2c_get_name()

void i2c_get_name ( I2C_REGDEF_ts const * i2c_instance,
char * name )

Returns the name string of an I2C peripheral instance (e.g. "I2C1").

Writes a null-terminated string of the form "I2Cx" into name. The caller must ensure name points to a buffer of at least I2C_NAME_LEN + 1 bytes.

Parameters
[in]i2c_instancePointer to the I2C peripheral instance.
[out]namePointer to the destination buffer.

Returns the name string of an I2C peripheral instance (e.g. "I2C1").

See also
i2c_get_name

Definition at line 215 of file stm32f401re_i2c.c.

215 {
216 const char i2c[] = "I2C";
217 uint8_t i2c_len = get_str_len(i2c);
218 uint8_t pos_counter = 0;
219
220 while(pos_counter != i2c_len) {
221 name[pos_counter] = i2c[pos_counter];
222 pos_counter++;
223 }
224
225 if(i2c_instance == I2C1) name[pos_counter] = '1';
226 else if(i2c_instance == I2C2) name[pos_counter] = '2';
227 else if(i2c_instance == I2C3) name[pos_counter] = '3';
228 pos_counter++;
229
230 name[pos_counter] = '\0';
231}
uint32_t get_str_len(char const *str)
Returns the length of a string, excluding the null terminator.
Definition common.c:22
Here is the call graph for this function: