Skip to content

Commit dc4292d

Browse files
committed
[bsp][n32] n32hxxx: fix H7xx hard I2C abort and add the missing SDK header
The N32H7xx branch of drv_hard_i2c.c had never been compiled. The file takes its peripheral declarations from board.h, and only n32h497zgl7-evb includes n32h49x_i2c.h, so on H7xx every I2C symbol was undefined and the option could not build at all. Include the peripheral header from the driver itself, the way drv_sdram.c already does. I2C_ABORT_ON_TIMEOUT also degraded to a bare `state = I2C_READY` on H7xx, at the four rt_completion_wait() timeouts in n32_i2c_master_xfer(). The H49x implementation is written against the older IP - I2C_ClrIntPendingBit, I2C_STS2_BUSY, I2C_CTRL1_STOPGEN - none of which exist in the H7xx SDK, so give H7xx its own teardown. It disables the interrupt and DMA request sources, disables the DMA channel that would otherwise stay armed against the caller's buffer, releases the bus and clears the status residue. Only flags with a matching INTCLR bit may be passed to I2C_ClrFlag(), which is a plain "INTCLR |= flag" write; TFC (0x40) and BUSY (0x8000) have none. I2C_Configuration() was called from n32_i2c_init() but defined nowhere in the tree. It is a board hook for pin mux, which is not the driver's job - the H49x path configures no pins either - so drop the call. Verified by building n32h760zil7-stb with BSP_USING_HARD_I2C1 and its TX/RX DMA enabled, which now compiles and links with no warnings, and n32h497zgl7-evb with I2C1/I2C2 DMA enabled, whose image size is unchanged. Neither board routes I2C pins yet, so this is build-verified only, not hardware-verified.
1 parent 4da8286 commit dc4292d

1 file changed

Lines changed: 112 additions & 6 deletions

File tree

‎bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c‎

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
#include <rtthread.h>
1212
#include <rthw.h>
1313
#include <board.h>
14+
15+
/* The driver owns the peripheral registers, so it pulls in the peripheral
16+
* header itself instead of relying on board.h to have declared it. Only the
17+
* H49x board happens to include it today.
18+
*/
19+
#if defined(SOC_SERIES_N32H7xx)
20+
#include <n32h7xx_i2c.h>
21+
#elif defined(SOC_SERIES_N32H49x)
22+
#include <n32h49x_i2c.h>
23+
#elif defined(SOC_SERIES_N32H47x_48x)
24+
#include <n32h47x_48x_i2c.h>
25+
#endif
26+
1427
#include "drv_hard_i2c.h"
1528
#include "drv_config.h"
1629
#include <string.h>
@@ -187,11 +200,106 @@ static rt_err_t n32_i2c_master_start_addr(struct n32_i2c *i2c, uint8_t DevAddres
187200
}
188201
#endif /* defined(SOC_SERIES_N32H49x) */
189202

190-
#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x)
203+
#if defined(SOC_SERIES_N32H7xx)
204+
/* Wait for the bus to fall idle before asserting a new STOP or START.
205+
*
206+
* Asserting either while a previous STOP is still on the wire wedges the state
207+
* machine: BUSY stays set, the address never goes out and the transfer stalls.
208+
*/
209+
static rt_err_t n32_i2c_wait_bus_idle(I2C_Module *Instance)
210+
{
211+
rt_uint32_t timeout = 1000000U;
212+
213+
while ((I2C_GetFlag(Instance, I2C_FLAG_BUSY) != RESET) && (--timeout > 0U))
214+
{
215+
}
216+
217+
return (timeout != 0U) ? RT_EOK : -RT_ETIMEOUT;
218+
}
219+
220+
/* Clear residue left by an aborted transfer before the next one starts.
221+
*
222+
* A STOPGEN that was latched with no frame to terminate stays pending and would
223+
* tear down the transfer we are about to set up. Stale status flags would
224+
* likewise fire the moment the new transfer re-enables the error interrupt.
225+
*
226+
* I2C_ClrFlag() is a plain "INTCLR |= flag" write, so only flags that actually
227+
* have a matching INTCLR bit may be passed. TFC (0x40) and BUSY (0x8000) have
228+
* none - passing them lands in reserved bits and clears nothing.
229+
*/
230+
static void n32_i2c_prepare_start(I2C_Module *Instance)
231+
{
232+
I2C_GenerateStop(Instance, DISABLE);
233+
234+
I2C_ClrFlag(Instance, I2C_FLAG_NAKF | I2C_FLAG_BSER | I2C_FLAG_ABLO |
235+
I2C_FLAG_OVF | I2C_FLAG_TMOUT | I2C_FLAG_ALRT |
236+
I2C_FLAG_STOPF | I2C_FLAG_ADR | I2C_FLAG_CRCERR);
237+
}
238+
239+
/* Tear down a transfer that the waiting thread has given up on.
240+
*
241+
* Must disarm the ISR before returning: master_xfer's callers pass stack
242+
* buffers, so a late read writing through transfer.pBuffPtr would corrupt a
243+
* stack frame that no longer exists once the caller unwinds. The DMA channel
244+
* targets that same buffer, so it is disabled here as well - dropping the
245+
* request enable alone leaves the channel armed.
246+
*/
247+
static void n32_i2c_abort_transfer(struct n32_i2c *i2c_obj)
248+
{
249+
I2C_Module *Instance = i2c_obj->config->Instance;
250+
I2C_StateTypeDef was_state = i2c_obj->transfer.state;
251+
252+
/* Disable interrupts and DMA first - stop the ISR touching pBuffPtr */
253+
Instance->CTRL1 &= ~(I2C_CTRL1_TFCIE | I2C_CTRL1_WDRIE | I2C_CTRL1_RDRIE |
254+
I2C_CTRL1_STOPIE | I2C_CTRL1_NAKIE | I2C_CTRL1_ERRIE);
255+
256+
if ((was_state == I2C_BUSY_TX) &&
257+
((i2c_obj->i2c_dma_flag & I2C_USING_TX_DMA_FLAG) == I2C_USING_TX_DMA_FLAG))
258+
{
259+
Instance->CTRL1 &= ~I2C_CTRL1_DMAWREN;
260+
261+
DMA_ChannelCmd(i2c_obj->config->dma_tx->Instance,
262+
i2c_obj->config->dma_tx->dma_channel, DISABLE);
263+
DMA_ChannelEventCmd(i2c_obj->config->dma_tx->Instance,
264+
i2c_obj->config->dma_tx->dma_channel,
265+
DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE, DISABLE);
266+
DMA_ClearChannelEventStatus(i2c_obj->config->dma_tx->Instance,
267+
i2c_obj->config->dma_tx->dma_channel,
268+
DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE);
269+
}
270+
else if ((was_state == I2C_BUSY_RX) &&
271+
((i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG) == I2C_USING_RX_DMA_FLAG))
272+
{
273+
Instance->CTRL1 &= ~I2C_CTRL1_DMARDEN;
274+
275+
DMA_ChannelCmd(i2c_obj->config->dma_rx->Instance,
276+
i2c_obj->config->dma_rx->dma_channel, DISABLE);
277+
DMA_ChannelEventCmd(i2c_obj->config->dma_rx->Instance,
278+
i2c_obj->config->dma_rx->dma_channel,
279+
DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE, DISABLE);
280+
DMA_ClearChannelEventStatus(i2c_obj->config->dma_rx->Instance,
281+
i2c_obj->config->dma_rx->dma_channel,
282+
DMA_CH_EVENT_BLOCK_TRANSFER_COMPLETE);
283+
}
284+
285+
i2c_obj->i2c_isr_callback = RT_NULL;
286+
i2c_obj->transfer.pBuffPtr = RT_NULL;
287+
i2c_obj->transfer.XferCount = 0;
288+
i2c_obj->transfer.XferSize = 0;
289+
i2c_obj->transfer.state = I2C_READY;
290+
291+
/* Release the bus only if we still own it, then clear residue */
292+
if (I2C_GetFlag(Instance, I2C_FLAG_BUSY) != RESET)
293+
{
294+
I2C_GenerateStop(Instance, ENABLE);
295+
(void)n32_i2c_wait_bus_idle(Instance);
296+
}
297+
298+
n32_i2c_prepare_start(Instance);
299+
}
300+
#endif /* defined(SOC_SERIES_N32H7xx) */
301+
191302
#define I2C_ABORT_ON_TIMEOUT(obj) n32_i2c_abort_transfer(obj)
192-
#else
193-
#define I2C_ABORT_ON_TIMEOUT(obj) ((obj)->transfer.state = I2C_READY)
194-
#endif
195303

196304
enum
197305
{
@@ -487,8 +595,6 @@ static rt_err_t n32_i2c_init(struct n32_i2c *i2c_drv)
487595
#if defined(SOC_SERIES_N32H7xx)
488596
I2C_InitType I2C_InitStructure;
489597
uint32_t BusTim_Reg;
490-
/* Call I2C_Configuration() from an external file */
491-
I2C_Configuration();
492598
/* Disable IIC */
493599
I2C_Enable(cfg->Instance, DISABLE);
494600
/* Get IIC BUSTM Register value */

0 commit comments

Comments
 (0)