2022-03-08 05:42 AM
Greetings!
I have "blue pil" stm32f103c8t6 and I want to send data over I2C via DMA. "Classic" way is working fine (I'm sending data to 2004 display through PCF8574 extender), but I can't get it working with DMA.
BTF flag isn't set and the loop becomes infinite. Looked up through debug and saw that TxE flag is set.
Have no idea what I'm doing wrong.
I use libopencm3:
#define IS_SET(a, b) (((a) & (b)) != 0)
#define NOT_SET(a, b) (!IS_SET(a, b))
#define PCF8574_I2C_ADDRESS 0b0100111
int main(void) {
rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE16_72MHZ]);
rcc_periph_clock_enable(RCC_GPIOB); // I2C2
rcc_periph_clock_enable(RCC_I2C2);
rcc_periph_clock_enable(RCC_DMA1);
gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_I2C2_SCL | GPIO_I2C2_SDA);
// # 72MHz / 8 => 9000000 counts per second
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
// # 9000000/9000 = 1000 overflows per second - every 1ms one interrupt
// # SysTick interrupt every N clock pulses: set reload to N-1
systick_set_reload(8999);
systick_counter_enable();
systick_interrupt_enable();
dma_channel_reset(DMA1, DMA_CHANNEL1);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);
dma_set_read_from_memory(DMA1, DMA_CHANNEL1);
dma_disable_transfer_error_interrupt(DMA1, DMA_CHANNEL1);
dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t)&I2C2_DR);
i2c_enable_dma(I2C2);
i2c_set_clock_frequency(I2C2, 36);
i2c_set_ccr(I2C2, 0x1e);
i2c_set_trise(I2C2, 0x0b);
i2c_peripheral_enable(I2C2);
while ( NOT_SET(I2C_CR1(I2C2), I2C_CR1_PE) );
DATA[0] = 0x1;
DATA[1] = 0x2;
DATA[2] = 0x3;
DATA[3] = 0x4;
DATA[4] = 0x5;
DATA[5] = 0x6;
DATA[6] = 0x7;
DATA[7] = 0x8;
// # write
while ( IS_SET(I2C_SR2(I2C2), I2C_SR2_BUSY) );
i2c_send_start(I2C2);
while ( NOT_SET(I2C_SR1(I2C2), I2C_SR1_SB) );
i2c_send_7bit_address(I2C2, PCF8574_I2C_ADDRESS, I2C_WRITE);
while ( NOT_SET(I2C_SR1(I2C2), I2C_SR1_ADDR) );
(void)I2C_SR2(I2C2);
dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t)&DATA[0]);
dma_set_number_of_data(DMA1, DMA_CHANNEL1, 8);
dma_enable_channel(DMA1, DMA_CHANNEL1);
while ( NOT_SET(I2C_SR1(I2C2), I2C_SR1_BTF) ); // hangs with TxE flag
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL1, DMA_IFCR_CTCIF1);
i2c_send_stop(I2C2);
do {
} while ( true );
return 0;
}