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