AnsweredAssumed Answered

standalone I2C (and I2C+DMA) behaviour on 0 byte read.

Question asked by dhaka.kuldeep on May 13, 2014
Code to test the behaviour of I2C in a 0-byte read transaction.
When  I2C_READ_BYTES = 1
everything works as expected (TC interrupt).
if dma is enabled, it will copy the data to buffer
when I2C_READ_BYTES = 0
this will generate ARLO (ariberation lost) or NACK (negative ACK).
but not TC.

NOTE: if DMA isnt enable while 1byte read, I2C will pull SCL low.

Another Question:
is NBYTES decremented by I2C peripherial after a successfully send/receive. (afai experience, it do NOT decrement the value).


#include <stdlib.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/f0/nvic.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/stm32/syscfg.h>
#include <libopencm3/stm32/i2c.h>

#include <stdint.h>
//STM32F072

//tested on AT30TS75A (temp sensor)
//LM75 should work
//below code is only applicable to bus read only
#define I2C_SLAVE_ADDR 0x91
#define I2C_READ_BYTES 0

//uncomment for DMA
//#define WITH_DMA

uint8_t i2c_buffer[I2C_READ_BYTES];

void i2c_do();
void i2c_init();

uint32_t timeout_counter;

void main()
{
    i2c_init();

    timeout_counter = 0;

    while(1) {
        if(timeout_counter == 0) {
            timeout_counter = 200000;
            timeout_counter = 200000;
            i2c_do();
        }
        //NOTE: uncomment below to perform repeated write
        timeout_counter--;
    }
}

void i2c_init()
{
    //pin
    rcc_periph_clock_enable(RCC_GPIOB);
    gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7);
    gpio_set_af(GPIOB, GPIO_AF1, GPIO6 | GPIO7);

    #ifdef WITH_DMA
    //dma
    rcc_periph_clock_enable(RCC_DMA);
    //map dma cha6,7 to i2c
    //SYSCFG_CFGR1_I2C1_DMA_RMP = (1 << 27)
    rcc_periph_clock_enable(RCC_SYSCFG_COMP);
    SYSCFG_CFGR1 |= 1 << 27;
    
    //dma ch7 = i2c rx
    dma_channel_reset(DMA1, DMA_CHANNEL7);
    dma_set_priority(DMA1, DMA_CHANNEL7, DMA_CCR_PL_LOW);
    
    dma_set_memory_size(DMA1, DMA_CHANNEL7, DMA_CCR_MSIZE_8BIT);
    dma_set_peripheral_size(DMA1, DMA_CHANNEL7, DMA_CCR_PSIZE_8BIT);
    dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL7);
    dma_set_read_from_peripheral(DMA1, DMA_CHANNEL7);
    dma_set_peripheral_address(DMA1, DMA_CHANNEL7, (uint32_t)&I2C1_RXDR);
    //dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL7);
    //dma_enable_transfer_error_interrupt(DMA1, DMA_CHANNEL7);
    //nvic_enable_irq(NVIC_DMA1_CHANNEL4_5_IRQ);

    #endif

    //i2c
    rcc_periph_clock_enable(RCC_I2C1);
    I2C1_CR1 &= ~I2C_CR1_PE;
    while(I2C1_CR1 & I2C_CR1_PE);
    
    //enable dma rx, dma tx, err interr, transfer complete interr, stop intrr, NACK interru
    I2C1_CR1 = I2C_CR1_ERRIE | I2C_CR1_STOPIE | I2C_CR1_NACKIE | I2C_CR1_TCIE;
    I2C1_TIMEOUTR = 0;
    I2C1_OAR1 = 0;
    I2C1_OAR2 = 0;

    //8MHz, 100khz
    //PRESC = 1
    //SCLL = 0x13
    //SCLH = 0xF
    //SDADEL = 0x2
    //SCLDEL = 0x4
    I2C1_TIMINGR = (1 << 28) | (0x4 << 20) | \
        (0x2 << 16) | (0xF << 8) | (0x13 << 0);
    
    I2C1_CR1 |= I2C_CR1_PE;

    nvic_set_priority(NVIC_I2C1_IRQ, 2);
    nvic_enable_irq(NVIC_I2C1_IRQ);
}

#ifdef WITH_DMA
void dma1_channel4_5_isr(void)
{
    dma_clear_interrupt_flags(DMA1, DMA_CHANNEL7, DMA_GIF);
}
#endif

//void i2c0_bulk_out(usbd_device *_usbd_dev, uint8_t _ep)
void i2c_do()
{
    //STOP i2c (if running)
    I2C1_CR1 &= ~I2C_CR1_PE;
    while(I2C1_CR1 & I2C_CR1_PE);

    #ifdef WITH_DMA
    //STOP dma (if running)
    dma_disable_channel(DMA1, DMA_CHANNEL7);
    #endif
    
    I2C1_CR1 |= I2C_CR1_PE;

    #ifdef WITH_DMA
    DMA1_IFCR = DMA_IFCR_CGIF7;
    dma_set_memory_address(DMA1, DMA_CHANNEL7, (uint32_t)&i2c_buffer);
    dma_set_number_of_data(DMA1, DMA_CHANNEL7, I2C_READ_BYTES);
    //dma_enable_channel(DMA1, DMA_CHANNEL7);
    #endif

    //address writing
    I2C1_CR2 = I2C_CR2_RD_WRN | I2C_SLAVE_ADDR | I2C_CR2_NBYTES_VAL(I2C_READ_BYTES) | I2C_CR2_AUTOEND | I2C_CR2_START;
}

uint32_t isr_times = 0;
uint32_t nack_times = 0;
uint32_t stop_times = 0;
uint32_t arlo_times = 0;
uint32_t berr_times = 0;

void i2c1_isr(void)
{
    uint32_t isr = I2C1_ISR;
    I2C1_ICR = isr & (I2C_ICR_TIMEOUTCF | I2C_ICR_NACKCF |
        I2C_ICR_STOPCF | I2C_ICR_ARLOCF | I2C_ICR_OVRCF | I2C_ICR_BERRCF);

    if(isr & I2C_ISR_NACKF)
        nack_times++;

    if(isr & I2C_ISR_STOPF)
        stop_times++;

    if(isr & I2C_ISR_BERR)
        berr_times++;

    if(isr & I2C_ISR_ARLO)
        arlo_times++;

    //if nack then STOP
    if(isr & I2C_ISR_NACKF) {
        I2C1_CR2 |= I2C_CR2_STOP;
    }

    //release the bus
    I2C1_CR1 &= ~I2C_CR1_PE;
    while(I2C1_CR1 & I2C_CR1_PE);
    #ifdef WITH_DMA
    dma_disable_channel(DMA1, DMA_CHANNEL7);
    #endif

    isr_times++;
}

Outcomes