#include #include #include #include "i2c_dma.h" #define DMA_CHANNEL_TX DMA_CHANNEL2 #define DMA_CHANNEL_RX DMA_CHANNEL3 static uint8_t m_wdata[2]; static uint8_t m_wlen; static uint8_t m_rdata[32]; static uint8_t m_rlen; static uint8_t m_addr; static uint32_t m_i2c; typedef enum { IDLE, SEND_DATA, RECEIVE_DATA } i2c_dma_state_t; static i2c_dma_state_t m_state; static i2c_dma_callback_t m_callback; static void setup_txdma(void) { dma_channel_reset(DMA1, DMA_CHANNEL_TX); dma_set_priority(DMA1, DMA_CHANNEL_TX, DMA_CCR_PL_LOW); dma_set_memory_size(DMA1, DMA_CHANNEL_TX, DMA_CCR_MSIZE_8BIT); dma_set_peripheral_size(DMA1, DMA_CHANNEL_TX, DMA_CCR_PSIZE_8BIT); dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL_TX); dma_set_read_from_memory(DMA1, DMA_CHANNEL_TX); dma_set_peripheral_address(DMA1, DMA_CHANNEL_TX, (uint32_t) &I2C_TXDR(m_i2c)); // send m_wlen bytes from m_wdata dma_set_memory_address(DMA1, DMA_CHANNEL_TX, (uint32_t)m_wdata); dma_set_number_of_data(DMA1, DMA_CHANNEL_TX, m_wlen); dma_enable_channel(DMA1, DMA_CHANNEL_TX); i2c_enable_txdma(m_i2c); } static void setup_rxdma(void) { dma_channel_reset(DMA1, DMA_CHANNEL_RX); dma_set_priority(DMA1, DMA_CHANNEL_RX, DMA_CCR_PL_LOW); dma_set_memory_size(DMA1, DMA_CHANNEL_RX, DMA_CCR_MSIZE_8BIT); dma_set_peripheral_size(DMA1, DMA_CHANNEL_RX, DMA_CCR_PSIZE_8BIT); dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL_RX); dma_set_read_from_peripheral(DMA1, DMA_CHANNEL_RX); dma_set_peripheral_address(DMA1, DMA_CHANNEL_RX, (uint32_t) &I2C_RXDR(m_i2c)); // receive m_rlen bytes and store them in m_rdata dma_set_memory_address(DMA1, DMA_CHANNEL_RX, (uint32_t)m_rdata); dma_set_number_of_data(DMA1, DMA_CHANNEL_RX, m_rlen); dma_enable_channel(DMA1, DMA_CHANNEL_RX); i2c_enable_rxdma(m_i2c); } void i2c_dma_init(i2c_dma_callback_t callback) { m_callback = callback; m_state = IDLE; } void i2c_dma_loop(void) { switch(m_state) { case IDLE: // nothing to do return; case SEND_DATA: //if(i2c_transfer_complete(m_i2c)) { // does not work for some reason :( if(dma_get_interrupt_flag(DMA1, DMA_CHANNEL_TX, DMA_TCIF)) { dma_clear_interrupt_flags(DMA1, DMA_CHANNEL_TX, DMA_TCIF); if(m_rlen) { m_state = RECEIVE_DATA; i2c_set_7bit_address(m_i2c, m_addr); i2c_set_read_transfer_dir(m_i2c); i2c_set_bytes_to_transfer(m_i2c, m_rlen); i2c_send_start(m_i2c); i2c_enable_autoend(m_i2c); // important to do it after start() for a proper repeated start! setup_rxdma(); } else { // no RX requested => we are done here m_state = IDLE; m_callback(NULL, 0); } } break; case RECEIVE_DATA: //if(i2c_transfer_complete(m_i2c)) { // does not work for some reason :( if(dma_get_interrupt_flag(DMA1, DMA_CHANNEL_RX, DMA_TCIF)) { dma_clear_interrupt_flags(DMA1, DMA_CHANNEL_RX, DMA_TCIF); m_state = IDLE; m_callback(m_rdata, m_rlen); } break; } } void i2c_dma_start_transfer7(uint32_t i2c, uint8_t addr, const uint8_t *w, size_t wn, size_t rn) { m_wlen = wn; m_rlen = rn; memcpy(m_wdata, w, wn); m_state = SEND_DATA; m_i2c = i2c; m_addr = addr; i2c_set_7bit_address(i2c, addr); i2c_set_write_transfer_dir(i2c); i2c_set_bytes_to_transfer(i2c, wn); if(rn) { i2c_disable_autoend(i2c); // prepare repeated start } else { i2c_enable_autoend(i2c); // stop condition after write transfer } i2c_send_start(i2c); // start the transfer. The rest is handled by the DMA. setup_txdma(); }