2014-05-13 08:29 AM
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). &sharpinclude <stdlib.h> &sharpinclude <libopencm3/stm32/rcc.h> &sharpinclude <libopencm3/stm32/gpio.h> &sharpinclude <libopencm3/stm32/f0/nvic.h> &sharpinclude <libopencm3/stm32/dma.h> &sharpinclude <libopencm3/stm32/syscfg.h> &sharpinclude <libopencm3/stm32/i2c.h> &sharpinclude <stdint.h> //STM32F072 //tested on AT30TS75A (temp sensor) //LM75 should work //below code is only applicable to bus read only &sharpdefine I2C_SLAVE_ADDR 0x91 &sharpdefine I2C_READ_BYTES 0 //uncomment for DMA //&sharpdefine 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); &sharpifdef 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); &sharpendif //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); } &sharpifdef WITH_DMA void dma1_channel4_5_isr(void) { dma_clear_interrupt_flags(DMA1, DMA_CHANNEL7, DMA_GIF); } &sharpendif //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); &sharpifdef WITH_DMA //STOP dma (if running) dma_disable_channel(DMA1, DMA_CHANNEL7); &sharpendif I2C1_CR1 |= I2C_CR1_PE; &sharpifdef 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); &sharpendif //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); &sharpifdef WITH_DMA dma_disable_channel(DMA1, DMA_CHANNEL7); &sharpendif isr_times++; } #stm32-i2c-dma