147 lines
3.5 KiB
C
147 lines
3.5 KiB
C
#include <string.h>
|
|
|
|
#include <libopencm3/stm32/i2c.h>
|
|
#include <libopencm3/stm32/dma.h>
|
|
|
|
#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();
|
|
}
|