STM32L011K4 с DMA RX, использующим данные I2C, не отображаемые в буфере

#c #embedded #stm32 #i2c #dma

#c #встроенный #stm32 #i2c #dma

Вопрос:

Я пытаюсь запросить датчик температуры (TMP007) по шине I2C, используя контроллер DMA L011K4. Мне удалось заставить TMP007 отвечать хорошими данными, но эти данные не отображаются в буферном массиве, который я передаю своей функции приема I2C DMA.

Я ожидаю, что аппаратное обеспечение I2C настраивается программным обеспечением в функции инициализации и чтения, затем генерируется условие запуска, отправляются адрес ведомого устройства и адрес регистра, затем отправляется второй запуск / перезапуск, за которым снова следует адрес ведомого устройства, и, наконец,завершается передачей двух байтов из TMP007 и условием остановки.
Данные, полученные от датчика температуры, должны отображаться в буфере, который передается в Start_Read (как MemoryBaseAddress), но эти данные не отображаются при просмотре процессора в режиме отладки после вызова функции чтения.

Вызывается прерывание завершения передачи по каналу 3, поэтому я знаю, что DMA активировал и управлял передачей RX, но данные не отображаются в нужном буфере.

Вот изображение связи, наблюдаемой логическим анализатором: https://i.imgur.com/SzySsXl.png DIO2 подключен к встроенному светодиоду, который сообщает нам, что передача DMA завершена (это единственный ISR со светодиодным переключателем).

Любая помощь будет высоко оценена!

Спецификация STM32L011K4: https://www.st.com/content/ccc/resource/technical/document/datasheet/42/c0/ab/e5/71/7a/47/0b/DM00206508.pdf/files/DM00206508.pdf/jcr:content/translations/en.DM00206508.pdf

Справочное руководство по STM32L011K4: https://www.st.com/resource/en/reference_manual/dm00108282-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

 void I2C1_DMA_Start_Read(int SlaveAddress, int RegisterAddress, int* MemoryBaseAddress, int BufferSize) {

    I2C1->CR1 amp;= ~I2C_CR1_PE;                       // Disable I2C1
    DMA1_Channel3->CCR amp;= ~DMA_CCR_EN;              // Disable Channel 3 DMA
    DMA1_Channel3->CCR amp;= ~(0x00007FFF);            // Channel 3 DMA mask

    // Configure DMA Channel 3 for 16 bit memory and peripheral, and other aliased settings (reference manual page 249, 10.4.3)
    DMA1_Channel3->CCR |=  (0b11 << 12)|(0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;

    DMA1_Channel3->CPAR =  (uint32_t) amp;(I2C1->RXDR);
    DMA1_Channel3->CMAR =  (uint32_t) MemoryBaseAddress;
    DMA1_Channel3->CNDTR = (uint16_t) BufferSize;

    DMA1_Channel3->CCR |= DMA_CCR_EN;               // Activate DMA channel 3

    I2C1->CR1 |= I2C_CR1_NACKIE;                    // Enable no acknowledge interrupt for I2C1

    I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1));   // Set up the slave address

    I2C1->CR2 amp;= ~I2C_CR2_RD_WRN;                   // Master requests a write transfer in order to send the register
    I2C1->CR1 |= I2C_CR1_PE|I2C_CR1_RXDMAEN;        // Enable I2C1 and RX DMA for I2C1
    I2C1->CR2 amp;= ~(0xFF << I2C_CR2_NBYTES_Pos);     // Mask byte count
    I2C1->CR2 |=  (0x01 << I2C_CR2_NBYTES_Pos);     // Transfer one byte (the slave's register address)

    while(!(I2C1->ISR amp; I2C_ISR_TXE));              // Wait until TX data register is empty
    I2C1->TXDR = RegisterAddress;                   // Set the transmit data to the desired register address

    I2C1->CR2 |= I2C_CR2_START;                     // Generate start condition
    while(I2C1->CR2 amp; I2C_CR2_START);               // Wait until hardware clears the start bit
    while(!(I2C1->ISR amp; I2C_ISR_TC));               // Wait until hardware clears the transmit complete bit

    I2C1->CR2 |= I2C_CR2_RD_WRN;                    // Master requests a read transfer after sending over the register address
    I2C1->CR2 amp;= ~(0xFF << I2C_CR2_NBYTES_Pos);     // Mask byte count
    I2C1->CR2 |=  (0x02 << I2C_CR2_NBYTES_Pos);     // Master looks for two bytes from the slave

    I2C1->CR2 |= I2C_CR2_START;                     // Generate start condition
    while(I2C1->CR2 amp; I2C_CR2_START);               // Wait until hardware clears the start bit

    return;
}

void DMA1_Channel2_3_IRQHandler() {
    if(DMA1->ISR amp; DMA_ISR_TCIF3) {
        GPIOB->ODR ^= 1 << 3;                       // Toggle the onboard LED as an indicator

        I2C1->CR2 |= I2C_CR2_STOP;                  // Generate stop condition
        while(I2C1->CR2 amp; I2C_CR2_STOP);            // Wait until hardware clears the stop bit
        DMA1->IFCR |= DMA_IFCR_CTCIF3;              // Clear transfer complete interrupt bit

        I2C1->CR1 amp;= ~I2C_CR1_PE;                   // Disable I2C1
        DMA1_Channel3->CCR amp;= ~DMA_CCR_EN;          // Disable Channel 3 DMA

    } else if(DMA1->ISR amp; DMA_ISR_TEIF3) {

        I2C1->CR2 |= I2C_CR2_STOP;                  // Generate stop condition (if I2C comm is already stopped on error, delete this and following line)
        while(I2C1->CR2 amp; I2C_CR2_STOP);            // Wait until hardware clears the stop bit

        DMA1->IFCR |= DMA_IFCR_CTEIF3;              // Clear transfer error interrupt bit

        I2C1->CR1 amp;= ~I2C_CR1_PE;                   // Disable I2C1
        DMA1_Channel3->CCR amp;= ~DMA_CCR_EN;          // Disable Channel 3 DMA

    }

    return;
}

void I2C1_IRQHandler() {
    if(I2C1->ISR amp; I2C_ISR_NACKF) {
        //GPIOB->ODR ^= 1 << 3;                     // Toggle the onboard LED as an indicator
        I2C1->ICR |= I2C_ICR_NACKCF;
    }

    return;
}
  

Комментарии:

1. if(magic_numbers == USED) then expext_no_help();

2. Приносим извинения за то, что не включили таблицы данных. Я удалил магические числа, которые можно было заменить макросом stm32. Некоторые значения были взяты прямо из справочного руководства l011k4, поэтому я их не трогал.