libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
stm32_i2c.cpp
1#include "stm32_i2c.hpp"
2
3#ifdef HAL_I2C_MODULE_ENABLED
4
5using namespace LibXR;
6
7STM32I2C* STM32I2C::map[STM32_I2C_NUMBER] = {nullptr};
8
9stm32_i2c_id_t STM32_I2C_GetID(I2C_TypeDef* hi2c)
10{ // NOLINT
11 if (hi2c == nullptr)
12 {
13 return stm32_i2c_id_t::STM32_I2C_ID_ERROR;
14 }
15#ifdef I2C1
16 else if (hi2c == I2C1)
17 { // NOLINT
18 return stm32_i2c_id_t::STM32_I2C1;
19 }
20#endif
21#ifdef I2C2
22 else if (hi2c == I2C2)
23 { // NOLINT
24 return stm32_i2c_id_t::STM32_I2C2;
25 }
26#endif
27#ifdef I2C3
28 else if (hi2c == I2C3)
29 { // NOLINT
30 return stm32_i2c_id_t::STM32_I2C3;
31 }
32#endif
33#ifdef I2C4
34 else if (hi2c == I2C4)
35 { // NOLINT
36 return stm32_i2c_id_t::STM32_I2C4;
37 }
38#endif
39#ifdef I2C5
40 else if (hi2c == I2C5)
41 { // NOLINT
42 return stm32_i2c_id_t::STM32_I2C5;
43 }
44#endif
45#ifdef I2C6
46 else if (hi2c == I2C6)
47 { // NOLINT
48 return stm32_i2c_id_t::STM32_I2C6;
49 }
50#endif
51#ifdef I2C7
52 else if (hi2c == I2C7)
53 { // NOLINT
54 return stm32_i2c_id_t::STM32_I2C7;
55 }
56#endif
57#ifdef I2C8
58 else if (hi2c == I2C8)
59 { // NOLINT
60 return stm32_i2c_id_t::STM32_I2C8;
61 }
62#endif
63 return stm32_i2c_id_t::STM32_I2C_ID_ERROR;
64}
65
66namespace
67{
68
69// 统一输入:
70// - 7-bit 模式:传 0x00~0x7F(不带 R/W 位),内部左移 1 给 HAL
71// - 10-bit 模式:传 0x000~0x3FF(不带 R/W 位),内部直接给 HAL
72static inline uint16_t EncodeHalDevAddress(const I2C_HandleTypeDef* hi2c,
73 uint16_t slave_addr)
74{
75 ASSERT(hi2c != nullptr);
76
77#if defined(I2C_ADDRESSINGMODE_10BIT)
78 if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_10BIT)
79 {
80 ASSERT(slave_addr <= 0x03FF);
81 return static_cast<uint16_t>(slave_addr & 0x03FF);
82 }
83#endif
84
85 // 默认按 7-bit
86 ASSERT(slave_addr <= 0x007F);
87 return static_cast<uint16_t>((slave_addr & 0x007F) << 1);
88}
89
90static inline ErrorCode MapHalStartFailure(const I2C_HandleTypeDef* hi2c,
91 HAL_StatusTypeDef st)
92{
93 // Preserve HAL's immediate start-failure signal without forcing a full controller
94 // reset.
95#ifdef HAL_I2C_ERROR_NONE
96 const uint32_t err = hi2c->ErrorCode;
97#ifdef HAL_I2C_WRONG_START
98 if ((err & (HAL_I2C_ERROR_TIMEOUT | HAL_I2C_WRONG_START)) != 0U)
99#else
100 if ((err & HAL_I2C_ERROR_TIMEOUT) != 0U)
101#endif
102 {
103 return ErrorCode::TIMEOUT;
104 }
105 if (err != HAL_I2C_ERROR_NONE)
106 {
107 return ErrorCode::FAILED;
108 }
109#else
110 UNUSED(hi2c);
111#endif
112 return (st == HAL_BUSY) ? ErrorCode::BUSY : ErrorCode::FAILED;
113}
114
115static void RecoverAfterBlockTimeout(STM32I2C* i2c)
116{
117 ASSERT(i2c != nullptr);
118
119 auto* hi2c = i2c->i2c_handle_;
120 i2c->recovering_ = true;
121 if (hi2c->hdmarx != nullptr)
122 {
123 (void)HAL_DMA_Abort(hi2c->hdmarx);
124 }
125 if (hi2c->hdmatx != nullptr)
126 {
127 (void)HAL_DMA_Abort(hi2c->hdmatx);
128 }
129
130 // Re-open the HAL handle after a detached BLOCK timeout without touching
131 // the larger software-side callback/semaphore semantics.
132 (void)HAL_I2C_DeInit(hi2c);
133 (void)HAL_I2C_Init(hi2c);
134
135 i2c->read_ = false;
136 i2c->read_op_ = {};
137 i2c->write_op_ = {};
138 i2c->read_buff_ = {nullptr, 0};
139 i2c->recovering_ = false;
140}
141
142static inline ErrorCode WaitBlockResultAndRecoverTimeout(STM32I2C* i2c, uint32_t timeout)
143{
144 ASSERT(i2c != nullptr);
145 const ErrorCode ans = i2c->block_wait_.Wait(timeout);
146 if (ans == ErrorCode::TIMEOUT)
147 {
148 RecoverAfterBlockTimeout(i2c);
149 }
150 return ans;
151}
152
153} // namespace
154
155STM32I2C::STM32I2C(I2C_HandleTypeDef* hi2c, RawData dma_buff,
156 uint32_t dma_enable_min_size)
157 : I2C(),
158 id_(STM32_I2C_GetID(hi2c->Instance)),
159 i2c_handle_(hi2c),
160 dma_enable_min_size_(dma_enable_min_size),
161 dma_buff_(dma_buff)
162{
163 map[id_] = this;
164}
165
166ErrorCode STM32I2C::Read(uint16_t slave_addr, RawData read_data, ReadOperation& op,
167 bool in_isr)
168{
169 if (i2c_handle_->State != HAL_I2C_STATE_READY)
170 {
171 return ErrorCode::BUSY;
172 }
173
174 read_ = true;
175
176 const uint16_t dev_addr = EncodeHalDevAddress(i2c_handle_, slave_addr);
177
178 if (read_data.size_ > dma_enable_min_size_)
179 {
180 read_op_ = op;
181 read_buff_ = read_data;
182 if (op.type == ReadOperation::OperationType::BLOCK)
183 {
184 // Arm the BLOCK waiter before HAL exposes completion to IRQ context.
185 block_wait_.Start(*op.data.sem_info.sem);
186 }
187 const HAL_StatusTypeDef st = HAL_I2C_Master_Receive_DMA(
188 i2c_handle_, dev_addr, reinterpret_cast<uint8_t*>(dma_buff_.addr_),
189 read_data.size_);
190 if (st != HAL_OK)
191 {
192 if (op.type == ReadOperation::OperationType::BLOCK)
193 {
194 block_wait_.Cancel();
195 return MapHalStartFailure(i2c_handle_, st);
196 }
197 return ErrorCode::BUSY;
198 }
199 op.MarkAsRunning();
200 if (op.type == ReadOperation::OperationType::BLOCK)
201 {
202 return WaitBlockResultAndRecoverTimeout(this, op.data.sem_info.timeout);
203 }
204 return ErrorCode::OK;
205 }
206 else
207 {
208 auto ans = HAL_I2C_Master_Receive(i2c_handle_, dev_addr,
209 reinterpret_cast<uint8_t*>(read_data.addr_),
210 read_data.size_, 20) == HAL_OK
213 // BLOCK 模式下沿用统一状态更新路径。
214 // Reuse the same status update path for BLOCK mode.
215 if (op.type != ReadOperation::OperationType::BLOCK)
216 {
217 op.UpdateStatus(in_isr, ans);
218 }
219 return ans;
220 }
221}
222
223ErrorCode STM32I2C::Write(uint16_t slave_addr, ConstRawData write_data,
224 WriteOperation& op, bool in_isr)
225{
226 if (i2c_handle_->State != HAL_I2C_STATE_READY)
227 {
228 return ErrorCode::BUSY;
229 }
230
231 read_ = false;
232
233 const uint16_t dev_addr = EncodeHalDevAddress(i2c_handle_, slave_addr);
234
235 Memory::FastCopy(dma_buff_.addr_, write_data.addr_, write_data.size_);
236
237 if (write_data.size_ > dma_enable_min_size_)
238 {
239 write_op_ = op;
240 if (op.type == WriteOperation::OperationType::BLOCK)
241 {
242 // Arm the BLOCK waiter before HAL exposes completion to IRQ context.
243 block_wait_.Start(*op.data.sem_info.sem);
244 }
245#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
246 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t*>(dma_buff_.addr_),
247 write_data.size_);
248#endif
249 const HAL_StatusTypeDef st = HAL_I2C_Master_Transmit_DMA(
250 i2c_handle_, dev_addr, reinterpret_cast<uint8_t*>(dma_buff_.addr_),
251 write_data.size_);
252 if (st != HAL_OK)
253 {
254 if (op.type == WriteOperation::OperationType::BLOCK)
255 {
256 block_wait_.Cancel();
257 return MapHalStartFailure(i2c_handle_, st);
258 }
259 return ErrorCode::BUSY;
260 }
261 op.MarkAsRunning();
262 if (op.type == WriteOperation::OperationType::BLOCK)
263 {
264 return WaitBlockResultAndRecoverTimeout(this, op.data.sem_info.timeout);
265 }
266 return ErrorCode::OK;
267 }
268 else
269 {
270 auto ans = HAL_I2C_Master_Transmit(i2c_handle_, dev_addr,
271 reinterpret_cast<uint8_t*>(dma_buff_.addr_),
272 write_data.size_, 20) == HAL_OK
275 if (op.type != WriteOperation::OperationType::BLOCK)
276 {
277 op.UpdateStatus(in_isr, ans);
278 }
279 return ans;
280 }
281}
282
283ErrorCode STM32I2C::MemRead(uint16_t slave_addr, uint16_t mem_addr, RawData read_data,
284 ReadOperation& op, MemAddrLength mem_addr_size, bool in_isr)
285{
286 ASSERT(read_data.size_ <= dma_buff_.size_);
287
288 if (i2c_handle_->State != HAL_I2C_STATE_READY)
289 {
290 return ErrorCode::BUSY;
291 }
292
293 read_ = true;
294
295 const uint16_t dev_addr = EncodeHalDevAddress(i2c_handle_, slave_addr);
296
297 if (read_data.size_ > dma_enable_min_size_)
298 {
299 read_op_ = op;
300 read_buff_ = read_data;
301 if (op.type == ReadOperation::OperationType::BLOCK)
302 {
303 // Arm the BLOCK waiter before HAL exposes completion to IRQ context.
304 block_wait_.Start(*op.data.sem_info.sem);
305 }
306 const HAL_StatusTypeDef st = HAL_I2C_Mem_Read_DMA(
307 i2c_handle_, dev_addr, mem_addr,
308 mem_addr_size == MemAddrLength::BYTE_8 ? I2C_MEMADD_SIZE_8BIT
309 : I2C_MEMADD_SIZE_16BIT,
310 reinterpret_cast<uint8_t*>(dma_buff_.addr_), read_data.size_);
311 if (st != HAL_OK)
312 {
313 if (op.type == ReadOperation::OperationType::BLOCK)
314 {
315 block_wait_.Cancel();
316 return MapHalStartFailure(i2c_handle_, st);
317 }
318 return ErrorCode::BUSY;
319 }
320 op.MarkAsRunning();
321 if (op.type == ReadOperation::OperationType::BLOCK)
322 {
323 return WaitBlockResultAndRecoverTimeout(this, op.data.sem_info.timeout);
324 }
325 return ErrorCode::OK;
326 }
327 else
328 {
329 auto ans =
330 HAL_I2C_Mem_Read(i2c_handle_, dev_addr, mem_addr,
331 mem_addr_size == MemAddrLength::BYTE_8 ? I2C_MEMADD_SIZE_8BIT
332 : I2C_MEMADD_SIZE_16BIT,
333 reinterpret_cast<uint8_t*>(read_data.addr_), read_data.size_,
334 20) == HAL_OK
337
338 if (op.type != ReadOperation::OperationType::BLOCK)
339 {
340 op.UpdateStatus(in_isr, ans);
341 }
342 return ans;
343 }
344}
345
346ErrorCode STM32I2C::MemWrite(uint16_t slave_addr, uint16_t mem_addr,
347 ConstRawData write_data, WriteOperation& op,
348 MemAddrLength mem_addr_size, bool in_isr)
349{
350 ASSERT(write_data.size_ <= dma_buff_.size_);
351
352 if (i2c_handle_->State != HAL_I2C_STATE_READY)
353 {
354 return ErrorCode::BUSY;
355 }
356
357 read_ = false;
358
359 const uint16_t dev_addr = EncodeHalDevAddress(i2c_handle_, slave_addr);
360
361 Memory::FastCopy(dma_buff_.addr_, write_data.addr_, write_data.size_);
362
363 if (write_data.size_ > dma_enable_min_size_)
364 {
365 write_op_ = op;
366 if (op.type == WriteOperation::OperationType::BLOCK)
367 {
368 // Arm the BLOCK waiter before HAL exposes completion to IRQ context.
369 block_wait_.Start(*op.data.sem_info.sem);
370 }
371#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
372 SCB_CleanDCache_by_Addr(reinterpret_cast<uint32_t*>(dma_buff_.addr_),
373 write_data.size_);
374#endif
375 const HAL_StatusTypeDef st = HAL_I2C_Mem_Write_DMA(
376 i2c_handle_, dev_addr, mem_addr,
377 mem_addr_size == MemAddrLength::BYTE_8 ? I2C_MEMADD_SIZE_8BIT
378 : I2C_MEMADD_SIZE_16BIT,
379 reinterpret_cast<uint8_t*>(dma_buff_.addr_), write_data.size_);
380 if (st != HAL_OK)
381 {
382 if (op.type == WriteOperation::OperationType::BLOCK)
383 {
384 block_wait_.Cancel();
385 return MapHalStartFailure(i2c_handle_, st);
386 }
387 return ErrorCode::BUSY;
388 }
389 op.MarkAsRunning();
390 if (op.type == WriteOperation::OperationType::BLOCK)
391 {
392 return WaitBlockResultAndRecoverTimeout(this, op.data.sem_info.timeout);
393 }
394 return ErrorCode::OK;
395 }
396 else
397 {
398 auto ans =
399 HAL_I2C_Mem_Write(i2c_handle_, dev_addr, mem_addr,
400 mem_addr_size == MemAddrLength::BYTE_8 ? I2C_MEMADD_SIZE_8BIT
401 : I2C_MEMADD_SIZE_16BIT,
402 reinterpret_cast<uint8_t*>(dma_buff_.addr_), write_data.size_,
403 20) == HAL_OK
406
407 if (op.type != WriteOperation::OperationType::BLOCK)
408 {
409 op.UpdateStatus(in_isr, ans);
410 }
411 return ans;
412 }
413}
414
416{
417 if (HasClockSpeed<decltype(i2c_handle_)>::value)
418 {
419 SetClockSpeed<decltype(i2c_handle_)>(i2c_handle_, config);
420 }
421 else
422 {
424 }
425
426 if (HAL_I2C_Init(i2c_handle_) != HAL_OK)
427 {
428 return ErrorCode::INIT_ERR;
429 }
430 return ErrorCode::OK;
431}
432
433extern "C" void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef* hi2c)
434{
435 STM32I2C* i2c = STM32I2C::map[STM32_I2C_GetID(hi2c->Instance)];
436 if (i2c && !i2c->recovering_ &&
437 (i2c->read_op_.type != ReadOperation::OperationType::NONE))
438 {
440#ifdef HAL_I2C_ERROR_NONE
441 ec = (hi2c->ErrorCode == HAL_I2C_ERROR_NONE) ? ErrorCode::OK : ErrorCode::FAILED;
442#endif
443#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
444 SCB_InvalidateDCache_by_Addr(i2c->dma_buff_.addr_, i2c->read_buff_.size_);
445#endif
446 if (ec == ErrorCode::OK)
447 {
448 Memory::FastCopy(i2c->read_buff_.addr_, i2c->dma_buff_.addr_,
449 i2c->read_buff_.size_);
450 }
451 if (i2c->read_op_.type == ReadOperation::OperationType::BLOCK)
452 {
453 (void)i2c->block_wait_.TryPost(true, ec);
454 }
455 else
456 {
457 i2c->read_op_.UpdateStatus(true, ec);
458 }
459 }
460}
461
462extern "C" void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef* hi2c)
463{
464 STM32I2C* i2c = STM32I2C::map[STM32_I2C_GetID(hi2c->Instance)];
465 if (i2c && !i2c->recovering_ &&
466 (i2c->write_op_.type != WriteOperation::OperationType::NONE))
467 {
469#ifdef HAL_I2C_ERROR_NONE
470 ec = (hi2c->ErrorCode == HAL_I2C_ERROR_NONE) ? ErrorCode::OK : ErrorCode::FAILED;
471#endif
472 if (i2c->write_op_.type == WriteOperation::OperationType::BLOCK)
473 {
474 (void)i2c->block_wait_.TryPost(true, ec);
475 }
476 else
477 {
478 i2c->write_op_.UpdateStatus(true, ec);
479 }
480 }
481}
482
483extern "C" void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef* hi2c)
484{
485 STM32I2C* i2c = STM32I2C::map[STM32_I2C_GetID(hi2c->Instance)];
486 if (i2c && !i2c->recovering_ &&
487 (i2c->write_op_.type != WriteOperation::OperationType::NONE))
488 {
490#ifdef HAL_I2C_ERROR_NONE
491 ec = (hi2c->ErrorCode == HAL_I2C_ERROR_NONE) ? ErrorCode::OK : ErrorCode::FAILED;
492#endif
493 if (i2c->write_op_.type == WriteOperation::OperationType::BLOCK)
494 {
495 (void)i2c->block_wait_.TryPost(true, ec);
496 }
497 else
498 {
499 i2c->write_op_.UpdateStatus(true, ec);
500 }
501 }
502}
503
504extern "C" void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef* hi2c)
505{
506 STM32I2C* i2c = STM32I2C::map[STM32_I2C_GetID(hi2c->Instance)];
507 if (i2c && !i2c->recovering_ &&
508 (i2c->read_op_.type != ReadOperation::OperationType::NONE))
509 {
511#ifdef HAL_I2C_ERROR_NONE
512 ec = (hi2c->ErrorCode == HAL_I2C_ERROR_NONE) ? ErrorCode::OK : ErrorCode::FAILED;
513#endif
514#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
515 SCB_InvalidateDCache_by_Addr(i2c->dma_buff_.addr_, i2c->read_buff_.size_);
516#endif
517 if (ec == ErrorCode::OK)
518 {
519 Memory::FastCopy(i2c->read_buff_.addr_, i2c->dma_buff_.addr_,
520 i2c->read_buff_.size_);
521 }
522 if (i2c->read_op_.type == ReadOperation::OperationType::BLOCK)
523 {
524 (void)i2c->block_wait_.TryPost(true, ec);
525 }
526 else
527 {
528 i2c->read_op_.UpdateStatus(true, ec);
529 }
530 }
531}
532
533extern "C" void HAL_I2C_ErrorCallback(I2C_HandleTypeDef* hi2c)
534{
535 STM32I2C* i2c = STM32I2C::map[STM32_I2C_GetID(hi2c->Instance)];
536
537 if (i2c && !i2c->recovering_)
538 {
539 if (i2c->read_)
540 {
541 if (i2c->read_op_.type == ReadOperation::OperationType::BLOCK)
542 {
543 (void)i2c->block_wait_.TryPost(true, ErrorCode::FAILED);
544 }
545 else
546 {
547 i2c->read_op_.UpdateStatus(true, ErrorCode::FAILED);
548 }
549 }
550 else
551 {
552 if (i2c->write_op_.type == WriteOperation::OperationType::BLOCK)
553 {
554 (void)i2c->block_wait_.TryPost(true, ErrorCode::FAILED);
555 }
556 else
557 {
558 i2c->write_op_.UpdateStatus(true, ErrorCode::FAILED);
559 }
560 }
561 }
562}
563
564#endif
常量原始数据封装类。 A class for encapsulating constant raw data.
size_t size_
数据大小(字节)。 The size of the data (in bytes).
const void * addr_
数据存储地址(常量)。 The storage address of the data (constant).
I2C(Inter-Integrated Circuit)接口类。 I2C (Inter-Integrated Circuit) interface class.
Definition i2c.hpp:17
static void FastCopy(void *dst, const void *src, size_t size)
快速内存拷贝 / Fast memory copy
Definition libxr_mem.cpp:5
union LibXR::Operation::@5 data
void UpdateStatus(bool in_isr, Status &&status)
Updates operation status based on type.
Definition libxr_rw.hpp:178
void MarkAsRunning()
标记操作为运行状态。 Marks the operation as running.
Definition libxr_rw.hpp:213
OperationType type
Definition libxr_rw.hpp:237
原始数据封装类。 A class for encapsulating raw data.
size_t size_
数据大小(字节)。 The size of the data (in bytes).
void * addr_
数据存储地址。 The storage address of the data.
STM32 I2C 驱动实现 / STM32 I2C driver implementation.
Definition stm32_i2c.hpp:52
ErrorCode SetConfig(Configuration config) override
配置 I2C 设备参数。 Configures the I2C device settings.
ErrorCode MemRead(uint16_t slave_addr, uint16_t mem_addr, RawData read_data, ReadOperation &op, MemAddrLength mem_addr_size, bool in_isr) override
从 I2C 设备指定寄存器读取数据。 Reads data from a specific register of an I2C device.
ErrorCode MemWrite(uint16_t slave_addr, uint16_t mem_addr, ConstRawData write_data, WriteOperation &op, MemAddrLength mem_addr_size, bool in_isr) override
向 I2C 设备指定寄存器写入数据。 Writes data to a specific register of an I2C device.
ErrorCode Write(uint16_t slave_addr, ConstRawData write_data, WriteOperation &op, bool in_isr) override
向 I2C 设备写入数据。 Writes data to an I2C device.
ErrorCode Read(uint16_t slave_addr, RawData read_data, ReadOperation &op, bool in_isr) override
读取 I2C 设备的数据。 Reads data from an I2C device.
STM32I2C(I2C_HandleTypeDef *hi2c, RawData dma_buff, uint32_t dma_enable_min_size=3)
构造 I2C 对象 / Construct I2C object
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ BUSY
忙碌 | Busy
@ NOT_SUPPORT
不支持 | Not supported
@ FAILED
操作失败 | Operation failed
@ OK
操作成功 | Operation successful
I2C 设备的配置信息结构体。 Configuration structure for an I2C device.
Definition i2c.hpp:24