STM32F103C8T6 I2 C Slave с использованием DMA - PullRequest
0 голосов
/ 11 апреля 2020

Это конфигурация I2 C со всеми связанными конфигурациями DMA, при использовании в качестве подчиненного передатчика I2 C указатель для i2c1_output_FIFO не сбрасывается, даже если я отключаю соответствующий канал, когда отправляется NACK для последнего байта указатель, который использует DMA, должен быть сброшен, в моем случае это не так. Любое решение?

Поскольку это ведомое устройство, DMA не знает заранее размер передачи. В серии F4 DMA можно установить в режим периферийной синхронизации, а когда канал отключен, это сигнализирует об окончании передачи, как это делается в серии F1.

В этом коде в i2c1_output_FIFO предварительно загружены некоторые значения отладки, чтобы вернуть некоторые данные обратно. Это также часть I2 C для более крупной программы, и вся конфигурация I2 C происходит в этом программном сегменте, поэтому проблема, вероятно, будет в ЭТОМ разделе.

I2 C сам по себе работает, как и ожидалось, это просто указатели DMA, которые не сбрасываются при отключении канала DMA.

#include "main.h"


volatile uint8_t i2c1_output_FIFO[64]; // data that is actually going to be sent out
volatile uint8_t i2c1_input_FIFO[64]; // data that is read
volatile uint8_t i2c1_output_buffer[64]; // data to be loaded into the output buffer

volatile _Bool stop_enable;
volatile _Bool ack_fail;

void I2C1_init() {
    // I2C1 TX DMA - DMA1, Channel 6
    // I2C1 RX DMA - DMA1, Channel 7

    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // enable I2C1 clock, 36 MHz input clock
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; // enable DMA1 clock

    // Channel 6 for transmitted data
    DMA1_Channel6->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; //increment memory, memory-to-peripheral transfer, enable transfer completion interrupt
    DMA1_Channel6->CPAR = (uint32_t)(&I2C1->DR); // set peripheral address
    DMA1_Channel6->CMAR =  (uint32_t)(&i2c1_output_FIFO); // set memory address to the sample output structure

    // Channel 7 for received data
    DMA1_Channel7->CCR |= DMA_CCR_MINC | DMA_CCR_TCIE; //increment memory, peripheral-to-memory transfer, enable transfer completion interrupt
    DMA1_Channel7->CPAR =  (uint32_t)(&I2C1->DR); // set peripheral address to the I2C data register
    DMA1_Channel7->CMAR =  (uint32_t)(&i2c1_input_FIFO); // set memory

    i2c1_output_FIFO[0] = 0xB8;
    i2c1_output_FIFO[1] = 0xC2;
    i2c1_output_FIFO[2] = 0xC3;
    i2c1_output_FIFO[3] = 0xC4;
    i2c1_output_FIFO[4] = 0xC5;
    i2c1_output_FIFO[5] = 0xC6;
    i2c1_output_FIFO[6] = 0xC7;
    i2c1_output_FIFO[7] = 0xC8;

    I2C1->OAR1 |= (0x75<<1); // set 0x75 as the slave address

    I2C1->CR2 |= (36<<I2C_CR2_FREQ_Pos) | I2C_CR2_DMAEN | I2C_CR2_ITERREN | I2C_CR2_ITEVTEN; // set up input clock, enable DMA, error interrupt enable
    I2C1->CCR |= I2C_CCR_FS | (30<<0); // Fast mode, set up for 400 KHz
    I2C1->TRISE = 35;

    I2C1->CR1 |= I2C_CR1_PE | I2C_CR1_ACK; // enable peripheral, enable acknowledge
}

uint8_t I2C1_start(uint8_t slave_address, uint8_t rw) {
    ack_fail = 0;
    I2C1->CR1 |= I2C_CR1_START; // generate start condition
    while(((I2C1->SR1>>I2C_SR1_SB_Pos)&1) == 0) { // wait for start condition to be completed

    }
    I2C1->DR = slave_address<<1 | rw; // load slave address and RW bit
    while(((I2C1->SR1>>I2C_SR1_ADDR_Pos)&1) == 0) { // wait for address to be transmitted

    }
    I2C1->SR2; // read status register 2 to clear ADDR flag

    return (I2C1->SR1>>I2C_SR1_AF_Pos) & 1; // return 1 if NACK was received
}

void I2C1_stop() {
    I2C1->CR1 |= I2C_CR1_STOP; // generate stop condition
}

void I2C1_transfer_TX(uint8_t slave_address, uint8_t byte_num, uint8_t stop_) { // main function to write data via DMA
    I2C1_start(slave_address, 0); // generate start condition, send slave address and RW bit
    if(ack_fail == 0) {
        for(uint16_t i=0; i<byte_num; i++) {
            i2c1_output_FIFO[i] = i2c1_output_buffer[i]; // transfer data over to the output buffer
        }
        stop_enable = stop_; // set up the stop enable flag

        DMA1_Channel6->CNDTR = byte_num; // set up DMA to transfer a certain number of bytes
        DMA1_Channel6->CCR |= DMA_CCR_EN; // start writing data from the output FIFO that was set up
    }
    else {
        if(stop_ == 1)
        I2C1_stop();
    }
}

void I2C1_transfer_RX(uint8_t slave_address, uint8_t byte_num, uint8_t stop_) { // main function to receive data via DMA
    I2C1_start(slave_address, 1); // generate start condition, send slave address and RW bit
    if(ack_fail == 0) {
        stop_enable = stop_; // set up the stop enable flag

        I2C1->CR2 |= I2C_CR2_LAST; // last byte is at the end of transfer
        DMA1_Channel7->CNDTR = byte_num; // set up DMA to transfer a certain number of bytes
        DMA1_Channel7->CCR |= DMA_CCR_EN; // start writing data from the output FIFO that was set up
    }
    else {
        if(stop_ == 1)
        I2C1_stop();
    }

}

void I2C1_ER_IRQHandler() {
    if(((I2C1->SR1>>I2C_SR1_AF_Pos)&1)==1 && ((I2C1->SR2>>I2C_SR2_MSL_Pos)&1) == 1) { // if acknowledge failure was detected... (Master Mode)
        I2C1->SR1 &= ~I2C_SR1_AF; // clear ack fail bit
        // terminate transfer
        DMA1_Channel6->CCR &= ~DMA_CCR_EN; // disable DMA channel to end transfer (Causes end of transfer interrupt)
        ack_fail = 1;
    }
}

void I2C1_EV_IRQHandler() { // Slave Configuration
    uint16_t temp1 = I2C1->SR1;
    uint16_t temp2 = I2C1->SR2;

    if(((temp2>>I2C_SR2_MSL_Pos)&1) == 0) { // if in slave mode...
        if(((temp1>>I2C_SR1_ADDR_Pos)&1) == 1) { //and address was received...
            if(((temp2>>I2C_SR2_TRA_Pos)&1)==0) { // if in slave receiver mode...
                DMA1_Channel7->CNDTR = 64;
                DMA1_Channel7->CCR |= DMA_CCR_EN; // start sending received data to the input FIFO
            }
            else if (((temp2>>I2C_SR2_TRA_Pos)&1)==1) { // if in slave transmitter mode...
                I2C1->SR1 &= ~I2C_SR1_AF; // write a 0 to the AF bit in SR1
                DMA1_Channel6->CNDTR = 64;
                DMA1_Channel6->CCR |= DMA_CCR_EN; // start writing data from the output FIFO that was set up
            }
        }
        if(((temp1>>I2C_SR1_STOPF_Pos)&1) == 1) {
            DMA1_Channel7->CCR &= ~DMA_CCR_EN; // disable DMA channel to end transfer
            DMA1->IFCR |= DMA_IFCR_CTCIF7; // clear interrupt flag

        }
        if(((temp1>>I2C_SR1_AF_Pos)&1) == 1) { // end of slave transmission
            I2C1->SR1 &= ~I2C_SR1_AF; // write a 0 to the AF bit in SR1
            DMA1_Channel6->CCR &= ~DMA_CCR_EN; // disable DMA channel to end transfer
            DMA1->IFCR |= DMA_IFCR_CTCIF6; // clear interrupt flag
            GPIOB->ODR |= (1<<0);
        }
    }
}

void DMA1_Channel6_IRQHandler() { // Transfer complete ISR (TX)
    DMA1->IFCR |= DMA_IFCR_CTCIF6; // clear interrupt flag
    DMA1_Channel6->CCR &= ~DMA_CCR_EN; // disable DMA channel to end transfer
    //DMA1_Channel6->CCR |= DMA_CCR_PFCTRL; // Slave Configuration, enable peripheral control
    while(((I2C1->SR1>>I2C_SR1_BTF_Pos) & 1) == 0) {}
    if(stop_enable == 1) {
        stop_enable = 0;
        I2C1_stop();
    }
}

void DMA1_Channel7_IRQHandler() { // Transfer complete ISR (RX)
    DMA1->IFCR |= DMA_IFCR_CTCIF7; // clear interrupt flag
    DMA1_Channel7->CCR &= ~DMA_CCR_EN; // disable DMA channel to end transfer
    //DMA1_Channel7->CCR |= DMA_CCR_PFCTRL; // Slave Configuration, enable peripheral control
    if(stop_enable == 1) {
        stop_enable = 0;
        I2C1_stop();
    }
}
...