esp_hal/i2c/lp_i2c.rs
1//! Low-power I2C driver
2
3use crate::{
4 gpio::lp_io::LowPowerOutputOpenDrain,
5 peripherals::{LP_AON, LP_I2C0, LP_IO, LP_PERI, LPWR},
6 time::Rate,
7};
8
9const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7;
10
11/// I2C-specific transmission errors
12#[derive(Debug, Clone, Copy, PartialEq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub enum Error {
15 /// The transmission exceeded the FIFO size.
16 ExceedingFifo,
17 /// The acknowledgment check failed.
18 AckCheckFailed,
19 /// A timeout occurred during transmission.
20 TimeOut,
21 /// The arbitration for the bus was lost.
22 ArbitrationLost,
23 /// The execution of the I2C command was incomplete.
24 ExecIncomplete,
25 /// The number of commands issued exceeded the limit.
26 CommandNrExceeded,
27 /// The response received from the I2C device was invalid.
28 InvalidResponse,
29}
30
31#[allow(unused)]
32enum OperationType {
33 Write = 0,
34 Read = 1,
35}
36
37#[allow(unused)]
38#[derive(Eq, PartialEq, Copy, Clone)]
39enum Ack {
40 Ack,
41 Nack,
42}
43
44#[derive(PartialEq)]
45#[allow(unused)]
46enum Command {
47 Start,
48 Stop,
49 End,
50 Write {
51 /// This bit is to set an expected ACK value for the transmitter.
52 ack_exp: Ack,
53 /// Enables checking the ACK value received against the ack_exp
54 /// value.
55 ack_check_en: bool,
56 /// Length of data (in bytes) to be written. The maximum length is
57 /// 255, while the minimum is 1.
58 length: u8,
59 },
60 Read {
61 /// Indicates whether the receiver will send an ACK after this byte
62 /// has been received.
63 ack_value: Ack,
64 /// Length of data (in bytes) to be read. The maximum length is 255,
65 /// while the minimum is 1.
66 length: u8,
67 },
68}
69
70// https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122
71// TX/RX RAM size is 16*8 bit
72// TX RX FIFO has 16 bit depth
73// The clock source of APB_CLK in LP_I2C is CLK_AON_FAST.
74// Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK.
75// When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source,
76// and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source.
77// Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK.
78// Adjust the timing registers accordingly when the clock frequency changes.
79
80/// Represents a Low-Power I2C peripheral.
81pub struct LpI2c {
82 i2c: LP_I2C0<'static>,
83}
84
85impl LpI2c {
86 fn lp_i2c_configure_io(ionum: usize, pullup_en: bool) {
87 let lp_io = LP_IO::regs();
88 let lp_aon = LP_AON::regs();
89 unsafe {
90 // Set the IO pin to high to avoid them from toggling from Low to
91 // High state during initialization. This can register a spurious
92 // I2C start condition.
93 lp_io
94 .out_data_w1ts()
95 .write(|w| w.out_data_w1ts().bits(1 << ionum));
96
97 lp_aon
98 .gpio_mux()
99 .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << ionum)));
100
101 // Set output mode to Open Drain
102 lp_io.pin(ionum).modify(|_, w| w.pad_driver().set_bit());
103
104 // Enable output (writing to write-1-to-set register, then internally the
105 // `GPIO_OUT_REG` will be set)
106 lp_io
107 .out_enable_w1ts()
108 .write(|w| w.enable_w1ts().bits(1 << ionum));
109
110 lp_io.gpio(ionum).modify(|_, w| {
111 // Enable input
112 w.fun_ie().set_bit();
113 // Disable the internal weak pull-down
114 w.fun_wpd().clear_bit();
115 // Configure the internal weak pull-up
116 w.fun_wpu().bit(pullup_en)
117 });
118 }
119 }
120
121 /// Creates a new instance of the `LpI2c` peripheral.
122 pub fn new(
123 i2c: LP_I2C0<'static>,
124 _sda: LowPowerOutputOpenDrain<'_, 6>,
125 _scl: LowPowerOutputOpenDrain<'_, 7>,
126 frequency: Rate,
127 ) -> Self {
128 let me = Self { i2c };
129
130 // Configure LP I2C GPIOs
131
132 // Initialize IO Pins
133 // NOTE: We always initialize the SCL pin (GPIO7) first, then the
134 // SDA (GPIO6) pin. This order of initialization is important to
135 // avoid any spurious I2C start conditions on the bus.
136 Self::lp_i2c_configure_io(7, true);
137 Self::lp_i2c_configure_io(6, true);
138 unsafe {
139 let lp_io = LP_IO::regs();
140 // Select LP I2C function for the SDA and SCL pins
141 lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1));
142 lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1));
143 }
144
145 // Initialize LP I2C HAL */
146 me.i2c
147 .register_block()
148 .clk_conf()
149 .modify(|_, w| w.sclk_active().set_bit());
150
151 let lp_peri = LP_PERI::regs();
152 // Enable LP I2C controller clock
153 lp_peri
154 .clk_en()
155 .modify(|_, w| w.lp_ext_i2c_ck_en().set_bit());
156
157 lp_peri
158 .reset_en()
159 .modify(|_, w| w.lp_ext_i2c_reset_en().set_bit());
160 lp_peri
161 .reset_en()
162 .modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit());
163
164 // Set LP I2C source clock
165 LPWR::regs()
166 .lpperi()
167 .modify(|_, w| w.lp_i2c_clk_sel().clear_bit());
168
169 // Initialize LP I2C Master mode
170 me.i2c.register_block().ctr().modify(|_, w| unsafe {
171 // Clear register
172 w.bits(0);
173 // Use open drain output for SDA and SCL
174 w.sda_force_out().set_bit();
175 w.scl_force_out().set_bit();
176 // Ensure that clock is enabled
177 w.clk_en().set_bit()
178 });
179
180 // First, reset the fifo buffers
181 me.i2c
182 .register_block()
183 .fifo_conf()
184 .modify(|_, w| w.nonfifo_en().clear_bit());
185
186 me.i2c.register_block().ctr().modify(|_, w| {
187 w.tx_lsb_first().clear_bit();
188 w.rx_lsb_first().clear_bit()
189 });
190
191 me.reset_fifo();
192
193 // Set LP I2C source clock
194 LPWR::regs()
195 .lpperi()
196 .modify(|_, w| w.lp_i2c_clk_sel().clear_bit());
197
198 // Configure LP I2C timing paramters. source_clk is ignored for LP_I2C in this
199 // call
200
201 let source_clk = 16_000_000;
202 let bus_freq = frequency.as_hz();
203
204 let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1;
205 let sclk_freq: u32 = source_clk / clkm_div;
206 let half_cycle: u32 = sclk_freq / bus_freq / 2;
207
208 // SCL
209 let scl_low = half_cycle;
210 // default, scl_wait_high < scl_high
211 // Make 80KHz as a boundary here, because when working at lower frequency, too
212 // much scl_wait_high will faster the frequency according to some
213 // hardware behaviors.
214 let scl_wait_high = if bus_freq >= 80 * 1000 {
215 half_cycle / 2 - 2
216 } else {
217 half_cycle / 4
218 };
219 let scl_high = half_cycle - scl_wait_high;
220 let sda_hold = half_cycle / 4;
221 let sda_sample = half_cycle / 2; // TODO + scl_wait_high;
222 let setup = half_cycle;
223 let hold = half_cycle;
224 // default we set the timeout value to about 10 bus cycles
225 // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2)
226 let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2;
227
228 // According to the Technical Reference Manual, the following timings must be
229 // subtracted by 1. However, according to the practical measurement and
230 // some hardware behaviour, if wait_high_period and scl_high minus one.
231 // The SCL frequency would be a little higher than expected. Therefore, the
232 // solution here is not to minus scl_high as well as scl_wait high, and
233 // the frequency will be absolutely accurate to all frequency
234 // to some extent.
235 let scl_low_period = scl_low - 1;
236 let scl_high_period = scl_high;
237 let scl_wait_high_period = scl_wait_high;
238 // sda sample
239 let sda_hold_time = sda_hold - 1;
240 let sda_sample_time = sda_sample - 1;
241 // setup
242 let scl_rstart_setup_time = setup - 1;
243 let scl_stop_setup_time = setup - 1;
244 // hold
245 let scl_start_hold_time = hold - 1;
246 let scl_stop_hold_time = hold - 1;
247 let time_out_value = tout;
248 let time_out_en = true;
249
250 // Write data to registers
251 unsafe {
252 me.i2c.register_block().clk_conf().modify(|_, w| {
253 w.sclk_sel().clear_bit();
254 w.sclk_div_num().bits((clkm_div - 1) as u8)
255 });
256
257 // scl period
258 me.i2c
259 .register_block()
260 .scl_low_period()
261 .write(|w| w.scl_low_period().bits(scl_low_period as u16));
262
263 me.i2c.register_block().scl_high_period().write(|w| {
264 w.scl_high_period().bits(scl_high_period as u16);
265 w.scl_wait_high_period().bits(scl_wait_high_period as u8)
266 });
267 // sda sample
268 me.i2c
269 .register_block()
270 .sda_hold()
271 .write(|w| w.time().bits(sda_hold_time as u16));
272 me.i2c
273 .register_block()
274 .sda_sample()
275 .write(|w| w.time().bits(sda_sample_time as u16));
276
277 // setup
278 me.i2c
279 .register_block()
280 .scl_rstart_setup()
281 .write(|w| w.time().bits(scl_rstart_setup_time as u16));
282 me.i2c
283 .register_block()
284 .scl_stop_setup()
285 .write(|w| w.time().bits(scl_stop_setup_time as u16));
286
287 // hold
288 me.i2c
289 .register_block()
290 .scl_start_hold()
291 .write(|w| w.time().bits(scl_start_hold_time as u16));
292 me.i2c
293 .register_block()
294 .scl_stop_hold()
295 .write(|w| w.time().bits(scl_stop_hold_time as u16));
296
297 me.i2c.register_block().to().write(|w| {
298 w.time_out_en().bit(time_out_en);
299 w.time_out_value().bits(time_out_value.try_into().unwrap())
300 });
301 }
302
303 // Enable SDA and SCL filtering. This configuration matches the HP I2C filter
304 // config
305
306 me.i2c
307 .register_block()
308 .filter_cfg()
309 .modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) });
310 me.i2c
311 .register_block()
312 .filter_cfg()
313 .modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) });
314
315 me.i2c
316 .register_block()
317 .filter_cfg()
318 .modify(|_, w| w.sda_filter_en().set_bit());
319 me.i2c
320 .register_block()
321 .filter_cfg()
322 .modify(|_, w| w.scl_filter_en().set_bit());
323
324 // Configure the I2C master to send a NACK when the Rx FIFO count is full
325 me.i2c
326 .register_block()
327 .ctr()
328 .modify(|_, w| w.rx_full_ack_level().set_bit());
329
330 // Synchronize the config register values to the LP I2C peripheral clock
331 me.lp_i2c_update();
332
333 me
334 }
335
336 /// Update I2C configuration
337 fn lp_i2c_update(&self) {
338 self.i2c
339 .register_block()
340 .ctr()
341 .modify(|_, w| w.conf_upgate().set_bit());
342 }
343
344 /// Resets the transmit and receive FIFO buffers.
345 fn reset_fifo(&self) {
346 self.i2c
347 .register_block()
348 .fifo_conf()
349 .modify(|_, w| w.tx_fifo_rst().set_bit());
350
351 self.i2c
352 .register_block()
353 .fifo_conf()
354 .modify(|_, w| w.tx_fifo_rst().clear_bit());
355
356 self.i2c
357 .register_block()
358 .fifo_conf()
359 .modify(|_, w| w.rx_fifo_rst().set_bit());
360
361 self.i2c
362 .register_block()
363 .fifo_conf()
364 .modify(|_, w| w.rx_fifo_rst().clear_bit());
365 }
366}