All guides
TECHNICAL GUIDE STM32L4R5 I2C 2026

STM32L4R5 I2C
TIMINGR & master transfers.

The STM32L4R5 uses the "I2Cv2" peripheral: no more CCR/TRISE — one TIMINGR register sets the whole bus timing, and CR2 (NBYTES + AUTOEND + START) drives fully hardware-sequenced master reads and writes. Register-level and HAL code for I2C1–I2C4.

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.

InstanceBase addressBusClock enable bitKernel-clock select
I2C10x4000 5400APB1RCC_APB1ENR1.I2C1EN (bit 21)RCC_CCIPR.I2C1SEL[13:12]
I2C20x4000 5800APB1RCC_APB1ENR1.I2C2EN (bit 22)RCC_CCIPR.I2C2SEL[15:14]
I2C30x4000 5C00APB1RCC_APB1ENR1.I2C3EN (bit 23)RCC_CCIPR.I2C3SEL[17:16]
I2C40x4000 8400APB1RCC_APB1ENR2.I2C4EN (bit 1)RCC_CCIPR2.I2C4SEL[1:0]
I2C4 IS THE ODD ONE

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:

I2CxSELSourceNotes
00PCLK1 (APB1)Reset default. Changes whenever you retune the APB1 prescaler → TIMINGR must be recomputed.
01SYSCLKUp to 120 MHz. Highest resolution, but recompute if SYSCLK changes.
10HSI16Fixed 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.
11reserved
RECOMMENDATION

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.

BitsFieldMeaning
31:28PRESCPrescaler. tPRESC = (PRESC + 1) / fI2CCLK
27:24reserved (write 0)
23:20SCLDELData setup time before SCL rising edge: tSCLDEL = (SCLDEL + 1) × tPRESC
19:16SDADELData hold time after SCL falling edge: tSDADEL = SDADEL × tPRESC
15:8SCLHSCL high period: tSCLH = (SCLH + 1) × tPRESC
7:0SCLLSCL 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 = ...;.

ModefSCLPRESCSCLLSCLHSDADELSCLDELTIMINGR
Standard10 kHz0x30xC70xC30x20x40x3042C3C7
Standard100 kHz0x30x130x0F0x20x40x30420F13
Fast400 kHz0x10x090x030x20x30x10320309
Fast+1000 kHz0x00x040x020x00x20x00200204
C — how TIMINGR is assembled
// 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
OTHER KERNEL FREQUENCIES

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)

BitNameFunction
0PEPeripheral enable. Clearing PE resets the I2C state machine (soft recovery).
7:1*IEInterrupt enables: TXIE, RXIE, ADDRIE, NACKIE, STOPIE, TCIE, ERRIE.
11:8DNF[3:0]Digital noise filter length (0 = off, N = suppress spikes < N·tI2CCLK). Set only while PE=0.
12ANFOFFAnalog noise filter OFF (0 = filter ON, ~50–260 ns spike suppression). Set only while PE=0.
14TXDMAENDMA request enable on transmit (TXIS).
15RXDMAENDMA request enable on receive (RXNE).
17NOSTRETCHDisable clock stretching — slave mode only. Must stay 0 in master mode.
19GCENGeneral call (address 0x00) enable.
23:20SMBusSMBHEN / SMBDEN / ALERTEN / PECEN — SMBus host/device, alert, PEC.

CR2 — control register 2 (the transaction)

BitNameFunction
9:0SADD[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].
10RD_WRNTransfer direction: 0 = master write, 1 = master read.
11ADD100 = 7-bit addressing, 1 = 10-bit addressing.
12HEAD10R10-bit read: 0 = send full header, 1 = send read header only after a restart.
13STARTWrite 1 to generate START (+ address). Self-clears. This launches the transfer.
14STOPWrite 1 to generate STOP (used when AUTOEND = 0 and you end manually).
15NACKSlave mode: send NACK. Ignored in master mode (master NACKs automatically after NBYTES on read).
23:16NBYTES[7:0]Number of bytes to transfer (0–255) before TC/TCR/STOP. See RELOAD for > 255.
24RELOAD1 = NBYTES reloads (chained transfer). TCR is set after each NBYTES; AUTOEND is ignored while RELOAD=1.
25AUTOEND1 = hardware issues STOP automatically after NBYTES. 0 = TC is set instead (needed for repeated-START).
26PECBYTESMBus: append/check the PEC byte as the last byte.

ISR (read) & ICR (write-1-to-clear)

ISR bitFlagMeaning / when you poll itClear
0TXETXDR empty (1 = ready). Write 1 to flush TXDR.write TXDR
1TXISTransmit interrupt status — hardware wants the next byte in TXDR.write TXDR
2RXNERXDR not empty — a received byte is ready to read.read RXDR
4NACKFNACK received from slave (no ACK on address/data).ICR.NACKCF
5STOPFSTOP detected on the bus (end of your transfer with AUTOEND).ICR.STOPCF
6TCTransfer complete: NBYTES done, RELOAD=0, AUTOEND=0. Bus held — issue restart or STOP.START/STOP
7TCRTransfer complete reload: NBYTES done, RELOAD=1. Program next NBYTES to continue.write NBYTES
8/9/10BERR/ARLO/OVRBus error / arbitration lost / overrun-underrun.ICR.BERRCF/ARLOCF/OVRCF
15BUSYBus is busy. Poll for 0 before starting a new transfer.hardware
MUST CLEAR STOPF / NACKF

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.

SignalPinsAF
I2C1_SCL / SDAPB6/PB7, PB8/PB9, PG14/PG13AF4
I2C2_SCL / SDAPB10/PB11, PB13/PB14, PF1/PF0AF4
I2C3_SCL / SDAPC0/PC1, PG7/PG8, PA7/PB4AF4
I2C4_SCL / SDAPD12/PD13, PF14/PF15AF4
I2C4_SCL / SDA (alt)PB10/PB11, PB6/PB7, PC0/PC1AF3 / AF5 — check DS12023 per package
VERIFY THE AF NUMBER

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.

C — register-level I2C1 init (PB8=SCL, PB9=SDA, 400 kHz, HSI16)
// 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
    
C — master TX / RX / register-read helpers
#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 > 255 → USE RELOAD

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.

C — HAL init + blocking transfers
#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);
}
HAL_I2C_Master_Transmit / _ReceiveBlocking single write / read to a device address (with timeout).
HAL_I2C_Mem_Write / _Mem_ReadRegister/EEPROM access: sends the memory address then does a repeated-START read (exactly the i2c_read_reg pattern).
*_IT / *_DMA variantsNon-blocking interrupt- or DMA-driven versions; completion via HAL_I2C_MasterTxCpltCallback etc.
HAL_I2C_IsDeviceReadyProbe/ping a device (address-only frame) — handy for bus scans and EEPROM write polling.

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 requestDMAMUX1 request IDPeripheral requestDMAMUX1 request ID
I2C1_RX16I2C1_TX17
I2C2_RX18I2C2_TX19
I2C3_RX20I2C3_TX21
I2C4_RX22I2C4_TX23
DIRECTION

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.

C — register-level DMA TX (I2C1_TX, request 17)
#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;
}
C — HAL DMA equivalent
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.

#MistakeSymptomFix
1Passing the 7-bit address unshifted to HALAlways NACK / wrong deviceHAL wants 8-bit: use addr7 << 1. Register SADD also needs addr7 << 1.
2Not clearing STOPF/NACKF via ICRSecond transfer never starts, or exits instantlyWrite ICR.STOPCF / ICR.NACKCF at the end of every transfer.
3Writing TIMINGR / DNF / ANFOFF while PE=1Ignored / undefined timingClear PE, program, then set PE. These fields are write-protected while enabled.
4TIMINGR computed for the wrong I2CCLKSCL 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.
5No external pull-ups, or push-pull GPIOSDA/SCL stuck, BERR, garbageOpen-drain AF (GPIO_MODE_AF_OD) + external 2.2k–4.7k to Vdd. Internal pull-ups are far too weak for 400 kHz+.
6Setting AUTOEND in the write phase of a register readSTOP instead of repeated-START; sensor returns stale dataPhase 1: AUTOEND=0, wait TC (not STOPF), then a second START.
7NBYTES > 255 in one shotTruncated transferNBYTES is 8-bit; use RELOAD and refill NBYTES on TCR.
8Reading RXDR before RXNE / writing TXDR before TXISOVR/underrun, corrupt bytesAlways gate TXDR on TXIS and RXDR on RXNE.
9Bus locked: a slave holds SDA lowBUSY never clearsThere 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.
10I2C4 configured like I2C1Peripheral dead / no clockEnable via RCC_APB1ENR2.I2C4EN (bit 1); select source via RCC_CCIPR2.I2C4SEL; base 0x40008400.
FILTERS AFFECT TIMING & MINIMUM PULSE

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).

SANITY CHECKLIST

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.