01 I2C peripheral map, clocks & why TIMINGR
The STM32L4R5 (Cortex-M4F, RM0432) has four identical I2C blocks: I2C1, I2C2, I2C3, I2C4. All are the same "I2Cv2" IP as the STM32F0/F3/F7/G0/G4/L4 — SMBus/PMBus capable, with an independent kernel clock and a single 32-bit timing register.
| Instance | Base address | Bus | Clock enable bit | Kernel-clock select |
|---|---|---|---|---|
| I2C1 | 0x4000 5400 | APB1 | RCC_APB1ENR1.I2C1EN (bit 21) | RCC_CCIPR.I2C1SEL[13:12] |
| I2C2 | 0x4000 5800 | APB1 | RCC_APB1ENR1.I2C2EN (bit 22) | RCC_CCIPR.I2C2SEL[15:14] |
| I2C3 | 0x4000 5C00 | APB1 | RCC_APB1ENR1.I2C3EN (bit 23) | RCC_CCIPR.I2C3SEL[17:16] |
| I2C4 | 0x4000 8400 | APB1 | RCC_APB1ENR2.I2C4EN (bit 1) | RCC_CCIPR2.I2C4SEL[1:0] |
I2C4's clock-enable bit lives in RCC_APB1ENR2 (bit 1), not APB1ENR1, and its source mux is in RCC_CCIPR2 (bits 1:0), not CCIPR. Copy-pasting I2C1 init and only changing the instance pointer will leave I2C4 unclocked.
Kernel clock selection (I2CCLK)
Each I2C runs from a selectable kernel clock — this is the frequency you feed into the TIMINGR math, and it is independent of the APB prescaler. The 2-bit I2CxSEL field picks:
| I2CxSEL | Source | Notes |
|---|---|---|
| 00 | PCLK1 (APB1) | Reset default. Changes whenever you retune the APB1 prescaler → TIMINGR must be recomputed. |
| 01 | SYSCLK | Up to 120 MHz. Highest resolution, but recompute if SYSCLK changes. |
| 10 | HSI16 | Fixed 16 MHz, independent of the PLL/APB tree. Lets you use the RM0432 16 MHz TIMINGR table verbatim and keeps I2C alive in low-power/Stop wake. |
| 11 | reserved | — |
For a robust first bring-up, select HSI16 as the I2C kernel clock. It is a rock-solid 16 MHz regardless of your system clock, so the RM0432 example TIMINGR values in the next section apply directly, and the bus keeps working across clock reconfiguration. HSI16 must be enabled (RCC_CR.HSION → wait HSIRDY) before you rely on it.
Why TIMINGR (vs. the old CCR/TRISE)
The legacy STM32F1/F4 I2C had CR2 (freq in MHz), CCR (divider) and TRISE. The I2Cv2 in the L4R5 replaces all three with one TIMINGR that separately programs the prescaler, SCL high/low widths, the SDA data setup/hold delay (SDADEL) and the SCL clock delay (SCLDEL). This makes Standard-mode (100 kHz), Fast-mode (400 kHz) and Fast-mode Plus (1 MHz) exact and lets the hardware guarantee I2C setup/hold timing without software TRISE tricks. There is no DR/SR1/SR2: data goes through TXDR/RXDR and status through ISR.
Vdd
│ Rp (2.2k–4.7k, external, required)
├──┴──┐
SCL ──┤ ├────── to slaves Open-drain: MCU only pulls LOW,
SDA ──┤ ├────── to slaves the resistor pulls HIGH.
└─────┘ GPIO mode = Alternate Function, OTYPE = open-drain.
02 TIMINGR: fields, formula & example table
TIMINGR is a 32-bit register with five fields. tPRESC is the timing unit; everything else counts in units of tPRESC.
| Bits | Field | Meaning |
|---|---|---|
| 31:28 | PRESC | Prescaler. tPRESC = (PRESC + 1) / fI2CCLK |
| 27:24 | — | reserved (write 0) |
| 23:20 | SCLDEL | Data setup time before SCL rising edge: tSCLDEL = (SCLDEL + 1) × tPRESC |
| 19:16 | SDADEL | Data hold time after SCL falling edge: tSDADEL = SDADEL × tPRESC |
| 15:8 | SCLH | SCL high period: tSCLH = (SCLH + 1) × tPRESC |
| 7:0 | SCLL | SCL low period: tSCLL = (SCLL + 1) × tPRESC |
Ignoring bus rise/fall time, the SCL period is tSCL ≈ tSCLL + tSCLH, so f_SCL ≈ f_I2CCLK / ((PRESC+1)·(SCLL+1 + SCLH+1)). SDADEL and SCLDEL do not set the frequency — they guarantee I2C data setup/hold against the slave and the bus RC.
RM0432 example values for fI2CCLK = 16 MHz
These are the reference-manual timing examples (analog filter enabled, DNF = 0). If you clock I2C from HSI16, drop these in unchanged. The rightmost column is the assembled 32-bit value ready for I2C1->TIMINGR = ...;.
| Mode | fSCL | PRESC | SCLL | SCLH | SDADEL | SCLDEL | TIMINGR |
|---|---|---|---|---|---|---|---|
| Standard | 10 kHz | 0x3 | 0xC7 | 0xC3 | 0x2 | 0x4 | 0x3042C3C7 |
| Standard | 100 kHz | 0x3 | 0x13 | 0x0F | 0x2 | 0x4 | 0x30420F13 |
| Fast | 400 kHz | 0x1 | 0x09 | 0x03 | 0x2 | 0x3 | 0x10320309 |
| Fast+ | 1000 kHz | 0x0 | 0x04 | 0x02 | 0x0 | 0x2 | 0x00200204 |
// TIMINGR = PRESC[31:28] | SCLDEL[23:20] | SDADEL[19:16] | SCLH[15:8] | SCLL[7:0]
// 100 kHz @ 16 MHz: PRESC=3, SCLDEL=4, SDADEL=2, SCLH=0x0F, SCLL=0x13
uint32_t timingr = (3u << 28) // PRESC -> tPRESC = 4/16MHz = 250 ns
| (4u << 20) // SCLDEL
| (2u << 16) // SDADEL
| (0x0Fu << 8) // SCLH -> 16*250ns = 4.0 us high
| (0x13u); // SCLL -> 20*250ns = 5.0 us low => ~100 kHz
// timingr == 0x30420F13
The 16 MHz table only applies when I2CCLK = 16 MHz. For any other kernel clock (e.g. PCLK1 = 80 MHz or SYSCLK = 120 MHz) do not scale these by hand — generate the value with STM32CubeMX (Connectivity → I2Cx → "Timing configuration") or the ST I2C timing tool (method described in AN4235). Enter the real I2CCLK and your target speed; it emits the exact TIMINGR accounting for the bus rise/fall time and filter delays.
03 CR1 / CR2 / ISR / ICR bit reference
CR1 holds the enable + feature bits. CR2 is the "transaction descriptor" — you program the address, direction, length and end mode, then set START. ISR is read-only status; write-1-to-clear flags go to ICR.
CR1 — control register 1 (key bits)
| Bit | Name | Function |
|---|---|---|
| 0 | PE | Peripheral enable. Clearing PE resets the I2C state machine (soft recovery). |
| 7:1 | *IE | Interrupt enables: TXIE, RXIE, ADDRIE, NACKIE, STOPIE, TCIE, ERRIE. |
| 11:8 | DNF[3:0] | Digital noise filter length (0 = off, N = suppress spikes < N·tI2CCLK). Set only while PE=0. |
| 12 | ANFOFF | Analog noise filter OFF (0 = filter ON, ~50–260 ns spike suppression). Set only while PE=0. |
| 14 | TXDMAEN | DMA request enable on transmit (TXIS). |
| 15 | RXDMAEN | DMA request enable on receive (RXNE). |
| 17 | NOSTRETCH | Disable clock stretching — slave mode only. Must stay 0 in master mode. |
| 19 | GCEN | General call (address 0x00) enable. |
| 23:20 | SMBus | SMBHEN / SMBDEN / ALERTEN / PECEN — SMBus host/device, alert, PEC. |
CR2 — control register 2 (the transaction)
| Bit | Name | Function |
|---|---|---|
| 9:0 | SADD[9:0] | Slave address. 7-bit mode: put the address in SADD[7:1] (i.e. write addr7 << 1). 10-bit mode: full address in SADD[9:0]. |
| 10 | RD_WRN | Transfer direction: 0 = master write, 1 = master read. |
| 11 | ADD10 | 0 = 7-bit addressing, 1 = 10-bit addressing. |
| 12 | HEAD10R | 10-bit read: 0 = send full header, 1 = send read header only after a restart. |
| 13 | START | Write 1 to generate START (+ address). Self-clears. This launches the transfer. |
| 14 | STOP | Write 1 to generate STOP (used when AUTOEND = 0 and you end manually). |
| 15 | NACK | Slave mode: send NACK. Ignored in master mode (master NACKs automatically after NBYTES on read). |
| 23:16 | NBYTES[7:0] | Number of bytes to transfer (0–255) before TC/TCR/STOP. See RELOAD for > 255. |
| 24 | RELOAD | 1 = NBYTES reloads (chained transfer). TCR is set after each NBYTES; AUTOEND is ignored while RELOAD=1. |
| 25 | AUTOEND | 1 = hardware issues STOP automatically after NBYTES. 0 = TC is set instead (needed for repeated-START). |
| 26 | PECBYTE | SMBus: append/check the PEC byte as the last byte. |
ISR (read) & ICR (write-1-to-clear)
| ISR bit | Flag | Meaning / when you poll it | Clear |
|---|---|---|---|
| 0 | TXE | TXDR empty (1 = ready). Write 1 to flush TXDR. | write TXDR |
| 1 | TXIS | Transmit interrupt status — hardware wants the next byte in TXDR. | write TXDR |
| 2 | RXNE | RXDR not empty — a received byte is ready to read. | read RXDR |
| 4 | NACKF | NACK received from slave (no ACK on address/data). | ICR.NACKCF |
| 5 | STOPF | STOP detected on the bus (end of your transfer with AUTOEND). | ICR.STOPCF |
| 6 | TC | Transfer complete: NBYTES done, RELOAD=0, AUTOEND=0. Bus held — issue restart or STOP. | START/STOP |
| 7 | TCR | Transfer complete reload: NBYTES done, RELOAD=1. Program next NBYTES to continue. | write NBYTES |
| 8/9/10 | BERR/ARLO/OVR | Bus error / arbitration lost / overrun-underrun. | ICR.BERRCF/ARLOCF/OVRCF |
| 15 | BUSY | Bus is busy. Poll for 0 before starting a new transfer. | hardware |
STOPF and NACKF are sticky. If you leave them set, the next START behaves unexpectedly or your poll loop exits immediately. Always write ICR.STOPCF at the end of a transfer and ICR.NACKCF on a NACK before retrying.
04 Pins, GPIO AF setup & register-level init
I2C SCL/SDA must be configured as Alternate Function, open-drain, with external pull-ups. On the STM32L4R5 the I2C1/2/3 signals are AF4; I2C4 has several mappings — confirm the exact AF for your package in datasheet DS12023.
| Signal | Pins | AF |
|---|---|---|
| I2C1_SCL / SDA | PB6/PB7, PB8/PB9, PG14/PG13 | AF4 |
| I2C2_SCL / SDA | PB10/PB11, PB13/PB14, PF1/PF0 | AF4 |
| I2C3_SCL / SDA | PC0/PC1, PG7/PG8, PA7/PB4 | AF4 |
| I2C4_SCL / SDA | PD12/PD13, PF14/PF15 | AF4 |
| I2C4_SCL / SDA (alt) | PB10/PB11, PB6/PB7, PC0/PC1 | AF3 / AF5 — check DS12023 per package |
I2C1/2/3 are uniformly AF4 across the L4/L4+. I2C4 is newer and shares pins with other peripherals, so its AF differs by pin (AF3, AF4 or AF5). Do not guess — open the "Alternate function" table in DS12023 for your exact device/package and read the AF column for the row of the pin you chose.
// STM32L4R5 — CMSIS device header defines RCC, GPIOB, I2C1, and the *_Pos/_Msk macros.
// I2C1_SCL = PB8 (AF4), I2C1_SDA = PB9 (AF4). External 2.2k-4.7k pull-ups required.
#include "stm32l4r5xx.h"
void i2c1_init_400k(void)
{
// 1) Kernel clock = HSI16 (fixed 16 MHz) so the RM0432 16 MHz TIMINGR values apply.
RCC->CR |= RCC_CR_HSION;
while (!(RCC->CR & RCC_CR_HSIRDY)) { }
RCC->CCIPR = (RCC->CCIPR & ~RCC_CCIPR_I2C1SEL_Msk)
| (0x2u << RCC_CCIPR_I2C1SEL_Pos); // 10 = HSI16
// 2) GPIOB clock, then PB8/PB9 -> AF mode, open-drain, very-high speed, weak pull-up.
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
(void)RCC->AHB2ENR; // dummy read: let the clock settle
GPIOB->MODER = (GPIOB->MODER & ~((3u<<(8*2)) | (3u<<(9*2))))
| (2u<<(8*2)) | (2u<<(9*2)); // 10 = Alternate Function
GPIOB->OTYPER |= (1u<<8) | (1u<<9); // 1 = open-drain (mandatory for I2C)
GPIOB->OSPEEDR|= (3u<<(8*2)) | (3u<<(9*2)); // 11 = very high speed
GPIOB->PUPDR = (GPIOB->PUPDR & ~((3u<<(8*2)) | (3u<<(9*2))))
| (1u<<(8*2)) | (1u<<(9*2)); // 01 = pull-up (external Rp still required)
GPIOB->AFR[1] = (GPIOB->AFR[1] & ~((0xFu<<((8-8)*4)) | (0xFu<<((9-8)*4))))
| (4u<<((8-8)*4)) | (4u<<((9-8)*4)); // AF4, AFR[1]=AFRH for pins 8..15
// 3) Enable the I2C1 peripheral clock.
RCC->APB1ENR1 |= RCC_APB1ENR1_I2C1EN;
(void)RCC->APB1ENR1;
// 4) Program timing and features ONLY while PE = 0.
I2C1->CR1 &= ~I2C_CR1_PE; // disable to configure / reset FSM
I2C1->TIMINGR = 0x10320309; // 400 kHz @ 16 MHz kernel (RM0432)
I2C1->CR1 &= ~I2C_CR1_ANFOFF; // analog filter ON (recommended)
I2C1->CR1 &= ~I2C_CR1_NOSTRETCH; // master must allow clock stretching
I2C1->CR1 |= I2C_CR1_PE; // enable
}
05 Master write & read (register level)
The I2Cv2 master is almost fully autonomous: load CR2 with address + direction + NBYTES + AUTOEND + START, then just feed TXDR / drain RXDR. Hardware makes the START, address, ACK checks and STOP.
MASTER WRITE (AUTOEND=1, NBYTES=n) MASTER READ (AUTOEND=1, NBYTES=n)
───────────────────────────────── ────────────────────────────────
set CR2: SADD,RD_WRN=0,NBYTES=n,AUTOEND,START set CR2: SADD,RD_WRN=1,NBYTES=n,AUTOEND,START
loop n: wait TXIS -> TXDR = byte loop n: wait RXNE -> byte = RXDR
hw sends STOP -> wait STOPF -> clear STOPF hw NACK+STOP -> wait STOPF -> clear STOPF
#include "stm32l4r5xx.h"
#include <stdint.h>
// Build the CR2 "transaction descriptor" and fire START in one write.
static void i2c_cr2_start(I2C_TypeDef *i2c, uint8_t addr7,
uint8_t nbytes, int read, int autoend)
{
uint32_t cr2 = (((uint32_t)addr7 << 1) & I2C_CR2_SADD) // 7-bit addr in SADD[7:1]
| (((uint32_t)nbytes << I2C_CR2_NBYTES_Pos) & I2C_CR2_NBYTES)
| (read ? I2C_CR2_RD_WRN : 0u)
| (autoend ? I2C_CR2_AUTOEND : 0u)
| I2C_CR2_START;
i2c->CR2 = cr2; // writing whole CR2 also clears ADD10/RELOAD/etc. for a clean master frame
}
// Master WRITE n bytes. Returns 0 on success, -1 on NACK.
int i2c_write(I2C_TypeDef *i2c, uint8_t addr7, const uint8_t *buf, uint8_t n)
{
while (i2c->ISR & I2C_ISR_BUSY) { } // wait for a free bus
i2c_cr2_start(i2c, addr7, n, 0, 1); // write, AUTOEND
for (uint8_t i = 0; i < n; i++) {
while (!(i2c->ISR & (I2C_ISR_TXIS | I2C_ISR_NACKF))) { }
if (i2c->ISR & I2C_ISR_NACKF) { // slave did not ACK
i2c->ICR = I2C_ICR_NACKCF;
while (!(i2c->ISR & I2C_ISR_STOPF)) { }
i2c->ICR = I2C_ICR_STOPCF;
return -1;
}
i2c->TXDR = buf[i];
}
while (!(i2c->ISR & I2C_ISR_STOPF)) { } // AUTOEND issued STOP
i2c->ICR = I2C_ICR_STOPCF;
return 0;
}
// Master READ n bytes. Returns 0 on success, -1 on address NACK.
int i2c_read(I2C_TypeDef *i2c, uint8_t addr7, uint8_t *buf, uint8_t n)
{
while (i2c->ISR & I2C_ISR_BUSY) { }
i2c_cr2_start(i2c, addr7, n, 1, 1); // read, AUTOEND
for (uint8_t i = 0; i < n; i++) {
while (!(i2c->ISR & (I2C_ISR_RXNE | I2C_ISR_NACKF))) { }
if (i2c->ISR & I2C_ISR_NACKF) {
i2c->ICR = I2C_ICR_NACKCF;
while (!(i2c->ISR & I2C_ISR_STOPF)) { }
i2c->ICR = I2C_ICR_STOPCF;
return -1;
}
buf[i] = (uint8_t)i2c->RXDR;
}
while (!(i2c->ISR & I2C_ISR_STOPF)) { }
i2c->ICR = I2C_ICR_STOPCF;
return 0;
}
// Write register pointer, then repeated-START and read n bytes (classic sensor read).
// Phase 1 uses AUTOEND=0 so the bus is held (TC) instead of STOP -> a real restart.
int i2c_read_reg(I2C_TypeDef *i2c, uint8_t addr7, uint8_t reg, uint8_t *buf, uint8_t n)
{
while (i2c->ISR & I2C_ISR_BUSY) { }
i2c_cr2_start(i2c, addr7, 1, 0, 0); // write 1 byte, NO AUTOEND
while (!(i2c->ISR & (I2C_ISR_TXIS | I2C_ISR_NACKF))) { }
if (i2c->ISR & I2C_ISR_NACKF) { i2c->ICR = I2C_ICR_NACKCF; return -1; }
i2c->TXDR = reg;
while (!(i2c->ISR & I2C_ISR_TC)) { } // transfer complete, bus still owned
i2c_cr2_start(i2c, addr7, n, 1, 1); // repeated START, read, AUTOEND
for (uint8_t i = 0; i < n; i++) {
while (!(i2c->ISR & I2C_ISR_RXNE)) { }
buf[i] = (uint8_t)i2c->RXDR;
}
while (!(i2c->ISR & I2C_ISR_STOPF)) { }
i2c->ICR = I2C_ICR_STOPCF;
return 0;
}
// Example: read 6 bytes of accel data (reg 0x3B) from an MPU-6050 at 7-bit addr 0x68.
void demo(void)
{
uint8_t accel[6];
i2c_read_reg(I2C1, 0x68, 0x3B, accel, 6);
}
NBYTES is 8-bit. For a longer transfer, set RELOAD=1 with NBYTES=255; when the 255 bytes are done the hardware sets TCR (and stretches SCL). Reload NBYTES (and clear RELOAD on the final chunk, set AUTOEND) to continue/finish. While RELOAD=1, AUTOEND is ignored, so the STOP only comes from the last chunk.
06 HAL variant
The STM32Cube HAL wraps the same registers. The one thing to remember: HAL takes the 8-bit address (7-bit value shifted left by one), and you still hand it the raw Timing word — CubeMX computes it for you.
#include "stm32l4xx_hal.h"
I2C_HandleTypeDef hi2c1;
void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x10320309; // 400 kHz @ 16 MHz I2CCLK (from CubeMX)
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks= I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // master: keep stretching enabled
if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); }
HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE);
HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0);
}
// Select HSI16 as the I2C1 kernel clock (matches the 16 MHz Timing value above).
void I2C1_SelectHSI16(void)
{
RCC_PeriphCLKInitTypeDef p = {0};
p.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
p.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&p) != HAL_OK) { Error_Handler(); }
}
// HAL_I2C_MspInit is called by HAL_I2C_Init: enable clocks + configure the AF pins.
void HAL_I2C_MspInit(I2C_HandleTypeDef *h)
{
if (h->Instance == I2C1) {
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_I2C1_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
g.Pin = GPIO_PIN_8 | GPIO_PIN_9; // PB8=SCL, PB9=SDA
g.Mode = GPIO_MODE_AF_OD; // alternate function, open-drain
g.Pull = GPIO_NOPULL; // external pull-ups on the board
g.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
g.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &g);
}
}
void demo_hal(void)
{
uint8_t cfg[2] = { 0x6B, 0x00 }; // MPU-6050: PWR_MGMT_1 = 0 (wake)
uint8_t accel[6];
// NOTE the <<1 : HAL wants the 8-bit address. 7-bit 0x68 -> 0xD0.
HAL_I2C_Master_Transmit(&hi2c1, (0x68 << 1), cfg, 2, HAL_MAX_DELAY);
HAL_I2C_Mem_Read(&hi2c1, (0x68 << 1), 0x3B,
I2C_MEMADD_SIZE_8BIT, accel, 6, HAL_MAX_DELAY);
}
07 DMA with DMAMUX
On the L4R5, DMA1/DMA2 channels are not hard-wired to peripherals — the DMAMUX routes any peripheral request line to any channel. Set CR1.TXDMAEN/RXDMAEN, point a DMA channel at TXDR/RXDR, and tell DMAMUX which I2C request line to listen for.
| Peripheral request | DMAMUX1 request ID | Peripheral request | DMAMUX1 request ID |
|---|---|---|---|
| I2C1_RX | 16 | I2C1_TX | 17 |
| I2C2_RX | 18 | I2C2_TX | 19 |
| I2C3_RX | 20 | I2C3_TX | 21 |
| I2C4_RX | 22 | I2C4_TX | 23 |
For a master write use the TX request (memory → TXDR, DMA reads RAM). For a master read use the RX request (RXDR → memory, DMA writes RAM). The DMA channel's data direction and the DMAMUX request ID must match.
#include "stm32l4r5xx.h"
// Transmit 'n' bytes from 'buf' to a 7-bit slave using DMA1 Channel 1 + DMAMUX.
void i2c1_dma_write(uint8_t addr7, const uint8_t *buf, uint8_t n)
{
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN;
// Route DMAMUX1 channel 0 (drives DMA1_Channel1) to I2C1_TX request line 17.
DMAMUX1_Channel0->CCR = 17u; // DMAMUX_CxCR.DMAREQ_ID = 17
DMA1_Channel1->CCR &= ~DMA_CCR_EN; // disable while configuring
DMA1_Channel1->CPAR = (uint32_t)&I2C1->TXDR; // peripheral = TXDR
DMA1_Channel1->CMAR = (uint32_t)buf; // memory = source buffer
DMA1_Channel1->CNDTR = n;
DMA1_Channel1->CCR = DMA_CCR_MINC // increment memory
| DMA_CCR_DIR // 1 = read-from-memory (mem -> periph)
| DMA_CCR_EN; // byte-wide (PSIZE=MSIZE=00) is fine for I2C
I2C1->CR1 |= I2C_CR1_TXDMAEN; // TXIS now triggers a DMA request
// Kick the frame exactly like the polled case: addr, NBYTES=n, write, AUTOEND, START.
I2C1->CR2 = (((uint32_t)addr7 << 1) & I2C_CR2_SADD)
| (((uint32_t)n << I2C_CR2_NBYTES_Pos) & I2C_CR2_NBYTES)
| I2C_CR2_AUTOEND | I2C_CR2_START;
while (!(I2C1->ISR & I2C_ISR_STOPF)) { } // DMA feeds TXDR; STOP when NBYTES done
I2C1->ICR = I2C_ICR_STOPCF;
I2C1->CR1 &= ~I2C_CR1_TXDMAEN;
DMA1_Channel1->CCR &= ~DMA_CCR_EN;
}
DMA_HandleTypeDef hdma_i2c1_tx;
void I2C1_DMA_Init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMAMUX1_CLK_ENABLE();
hdma_i2c1_tx.Instance = DMA1_Channel1;
hdma_i2c1_tx.Init.Request = DMA_REQUEST_I2C1_TX; // == 17
hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_i2c1_tx);
__HAL_LINKDMA(&hi2c1, hdmatx, hdma_i2c1_tx);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); // + call HAL_DMA_IRQHandler(&hdma_i2c1_tx) in it
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); // + call HAL_I2C_EV_IRQHandler(&hi2c1)
}
void demo_dma(void)
{
static uint8_t buf[32];
HAL_I2C_Master_Transmit_DMA(&hi2c1, (0x50 << 1), buf, sizeof buf);
// completion signalled by HAL_I2C_MasterTxCpltCallback()
}
08 Clock stretching & gotchas
Most "my I2C hangs" bugs on the L4R5 come from a handful of repeatable mistakes. Read this list first.
Clock stretching
Clock stretching is a slave holding SCL low to buy time. As a master, the L4R5 handles this transparently — it always tolerates a stretched clock and there is nothing to configure. The NOSTRETCH bit only affects the peripheral when it acts as a slave: NOSTRETCH=1 tells the hardware "I will keep up, never stretch" (used by fast SMBus slaves). In master mode NOSTRETCH must stay 0; setting it does nothing useful and the RM requires it clear for masters.
| # | Mistake | Symptom | Fix |
|---|---|---|---|
| 1 | Passing the 7-bit address unshifted to HAL | Always NACK / wrong device | HAL wants 8-bit: use addr7 << 1. Register SADD also needs addr7 << 1. |
| 2 | Not clearing STOPF/NACKF via ICR | Second transfer never starts, or exits instantly | Write ICR.STOPCF / ICR.NACKCF at the end of every transfer. |
| 3 | Writing TIMINGR / DNF / ANFOFF while PE=1 | Ignored / undefined timing | Clear PE, program, then set PE. These fields are write-protected while enabled. |
| 4 | TIMINGR computed for the wrong I2CCLK | SCL runs at the wrong speed (e.g. half) | TIMINGR depends on the kernel clock (I2CxSEL), not APB. Recompute if PCLK1/SYSCLK/source changes; or pin it to HSI16. |
| 5 | No external pull-ups, or push-pull GPIO | SDA/SCL stuck, BERR, garbage | Open-drain AF (GPIO_MODE_AF_OD) + external 2.2k–4.7k to Vdd. Internal pull-ups are far too weak for 400 kHz+. |
| 6 | Setting AUTOEND in the write phase of a register read | STOP instead of repeated-START; sensor returns stale data | Phase 1: AUTOEND=0, wait TC (not STOPF), then a second START. |
| 7 | NBYTES > 255 in one shot | Truncated transfer | NBYTES is 8-bit; use RELOAD and refill NBYTES on TCR. |
| 8 | Reading RXDR before RXNE / writing TXDR before TXIS | OVR/underrun, corrupt bytes | Always gate TXDR on TXIS and RXDR on RXNE. |
| 9 | Bus locked: a slave holds SDA low | BUSY never clears | There is no bus reset register — toggle PE (PE=0 for ≥3 APB cycles, then PE=1), or reconfigure SCL as GPIO and clock out up to 9 manual pulses to free the slave. |
| 10 | I2C4 configured like I2C1 | Peripheral dead / no clock | Enable via RCC_APB1ENR2.I2C4EN (bit 1); select source via RCC_CCIPR2.I2C4SEL; base 0x40008400. |
The analog filter (ANFOFF=0) suppresses spikes up to ~50–260 ns and the digital filter (DNF=1..15) suppresses spikes shorter than DNF×tI2CCLK. Both add latency that the RM0432 example TIMINGR values already assume (analog ON, DNF=0). If you enable a large DNF or turn the analog filter off, regenerate TIMINGR with CubeMX so setup/hold stay in spec — especially at Fast-mode Plus (1 MHz).
Kernel clock selected & running (HSI16 ready) → GPIO AF4 open-drain + external Rp → I2C clock enabled → TIMINGR set while PE=0 → PE=1 → transfer sets SADD/NBYTES/dir/AUTOEND then START → poll TXIS/RXNE → clear STOPF. If BUSY is stuck at boot, toggle PE to reset the state machine.