передача i2c, stm32f103 в качестве мастера, проблема с генерацией условия STOP - PullRequest
1 голос
/ 01 июня 2019

Я использую KEIL.Вот мой код для настройки I2C.Я пытаюсь настроить его с помощью прерываний.На самом деле все работает нормально, как это.Но единственная проблема, которую я имею, состоит в том, что я сделал одну странную вещь, чтобы она заработала.

// Variables passed to I2C1_EV_IRQHandler 
static volatile uint8_t counter=0;
static uint8_t slave_address=0, *data_buff,buff_num_bytes=0;

//I2c event and error interrupts setup
void I2C_NVIC_setup(void){
    NVIC->ISER[0] |=NVIC_ISER_SETENA_31; //I2C_IRQ_EV is at 31 position
    NVIC->ISER[1] |=NVIC_ISER_SETENA_0; //I2C_IRQ_ER is at 32 position
    NVIC->IP[31] &= ~0xF0;
    NVIC->IP[31]= (uint8_t)(0x5<<4);    //Event has lower priority
    NVIC->IP[32] &= ~0xF0;
    NVIC->IP[32]= (uint8_t)(0x4<<4);    //Error has higher priority
    __enable_irq();         //ENABLE interrupts
}

//GPIO settings for PB6 and PB7
void I2C_GPIO_init(void){
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //PORTB clock enable

    GPIOB->CRL |= GPIO_CRL_CNF6 |GPIO_CRL_CNF7;         //PB6 and PB7- open drain
    uint32_t buff=GPIOB->CRL;
    buff &= ~GPIO_CRL_MODE6 & ~GPIO_CRL_MODE7;  
    buff |= GPIO_CRL_MODE6_0 | GPIO_CRL_MODE7_0 ;   //2MHz maximum output speed
    GPIOB->CRL=buff; 
    //Dont care about ODR register
}

// Initialization of I2C
bool I2C_init(void){
I2C_GPIO_init();
    I2C_NVIC_setup();
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

    I2C1->CR2 &= ~I2C_CR2_FREQ;
    I2C1->CR2 |= 0x14; //APB1 frequency is 20MHz

    I2C1->CCR |=  I2C_CCR_FS; //Fast mode
    I2C1->CCR |= I2C_CCR_DUTY;  //Duty cycle 16/9(for fast mode)

    I2C1->CCR &= ~I2C_CCR_CCR;
    I2C1->CCR |=    0x04;           //Thigh=1800ns Tlow=3200ns -200KHz frequency

    I2C1->TRISE &= ~I2C_TRISE_TRISE;
    I2C1->TRISE |= 0x07;    //300ns(max Trise)/50ns(Tclck) +1

    I2C1->CR1 |=    I2C_CR1_PE; //Enable peripheral

    I2C1->CR2 |= I2C_CR2_ITBUFEN; //Enable interupts of TxE and RxNE
    I2C1->CR2 |= I2C_CR2_ITEVTEN; //Enable event interupts
    I2C1->CR2 |= I2C_CR2_ITERREN; //Enable error interupts
}
//Master transmitter
bool I2C_transmit(uint8_t b7_address, uint8_t* data, uint8_t num_bytes){
    counter=num_bytes;
    buff_num_bytes=num_bytes;
    data_buff=data;
    slave_address=b7_address<<1 ;//To enter transmiter mode LSB=0

    I2C1->CR1 |= I2C_CR1_START; //Start condition
    while(counter || (I2C1->SR2 & I2C_SR2_MSL) || (I2C1->CR1 & I2C_CR1_STOP)){ //Stop bit is checked because of the problem I have
    }
}   
//IRQ I2C1 event handler
void I2C1_EV_IRQHandler(void){ //Handle the interrupt of I2C1
    uint8_t buff_sr1=I2C1->SR1;
    if(buff_sr1 & I2C_SR1_SB){ //SB bit is set(unsetting by read SR1 and writing adress to DR) 
        I2C1->DR=slave_address; 
    }else if(buff_sr1 & I2C_SR1_ADDR){ //ADDR bit is set(unsetting by read SR1 and read SR2)            
        (void)I2C1->SR2; 
    }
    if ((buff_sr1 & I2C_SR1_TXE)&& !(counter==0)){  //Checking TxE( clearing by writting to DR)
        I2C1->DR=data_buff[buff_num_bytes-counter];
        counter--;
    } else if((buff_sr1 & I2C_SR1_TXE)&&(buff_sr1 & I2C_SR1_BTF)){ //Checking stop 
      //condition(TxE=1,BTF=1,counter=0)
        (void)I2C1->SR1; //Dont know why, but it works(just read some I2C1 register)
        I2C1->CR1 |= I2C_CR1_STOP;  //Generate stop condition
    }

}

Если вы посмотрите на последние несколько строк:

    } else if((buff_sr1 & I2C_SR1_TXE)&&(buff_sr1 & I2C_SR1_BTF)){ //Checking stop 
      //condition(TxE=1,BTF=1,counter=0)
        (void)I2C1->SR1; //Dont know why, but it works(just read some I2C1 register)
        I2C1->CR1 |= I2C_CR1_STOP;  //Generate stop condition
    }

Здесь, когда я отлаживал (этот код, без строки (void) I2C1-> SR1;),когда я пытался отправить данные, первый пакет отправлялся без проблем.В конце он генерирует условие останова (бит MSL будет очищен, а строка освобождена), но бит STOP останется установленным (поэтому я поставил проверку бита STOP в цикле I2C_transmit while).Если бы я вручную очищал бит STOP в окнах отладки, он продолжался бы, но в следующем цикле передачи он не будет генерировать надлежащий запуск (будет установлен SB, но бит MSL останется в состоянии сброса).

Во время отладки, если бы я поставил точку останова перед этой строкой:

(имейте в виду, что в настоящее время я не нашел решения для этой проблемы, поэтому все без изменений только с этой строкой - (void) I2C1-> SR1;)

I2C1->CR1 |= I2C_CR1_STOP;  //Generate stop condition

После того, как мы остановились там, изучили регистры, а затем продолжили, все работало нормально.Я обнаружил в stackexchage , что:

Особенно часть о чтении регистров может дать вам ад отладки, так как чтение отладчика вызовет конечный автомат, как чтениевашего кода, поэтому, как только вы остановите и осмотрите регистры (иногда отладчик делает все это автоматически), произойдет чтение, и периферийное устройство снова сделает что-то.

Было ясно, что когда я былПри выполнении условия STOP некоторые требования не были выполнены.Поэтому я решил проверить, так ли это, и похоже, что это было решением.Добавление чтения любого регистра I2C ((void) I2C1-> SR1; // или любого другого регистра I2C) решает проблему.Должно быть логичное объяснение, но я не могу его найти.Я очень тщательно изучил справочное руководство , в результате получился код, но я не нашел ничего, что могло бы мне это объяснить.

Вот таблица из Справочное руководство о передаче Master, для вашей справки: enter image description here

Можете ли вы сказать, какое состояние я не сделалудовлетворить, поэтому мой STOP не обрабатывается должным образом.

1 Ответ

1 голос
/ 05 июня 2019

ОК, я только что преобразовал свою тестовую программу для использования передачи, управляемой прерываниями, на основе этого кода.Мой тест проводится на STM32F411RE, который имеет аналогичную реализацию I2C с F103 (другие серии, такие как F0xx, F3xx и F7xx, используют другую реализацию I2C).Во всяком случае, по крайней мере, я могу подтвердить, что без "(void) I2C1-> SR1;"в обработчике прерывания передачи он не работает.Он работает только при добавлении линии.

В ходе теста мигает светодиодная матрица 8x8 с использованием микросхемы расширения I2C, поэтому для передачи светодиодной матрицы имеется 64 * 2 + I2C-сигналы передачи.

Поскольку F411RE является текущим и более новым, чем F103, я предполагаю, что это недокументированное требование!

Теперь, по крайней мере, в моем тесте "(I2C1-> CR1 & I2C_CR1_STOP)" не требуетсяв функции передачи.

...