Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

STM32 не работает I2C в slave режиме.

Вс ноя 06, 2022 19:33:03

Не получается реализовать работу I2C в режиме slave c использованием прерываний.
В качестве мастера используется ESP-12F которая раз в секунду передает строку 11 символом "Hello STM32"

Код:
#include <Arduino.h>
#include <Wire.h>

void setup() {
    Wire.begin(D1, D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}

void loop() {
    Wire.beginTransmission(0x08); /* begin with device address 8 */
    Wire.write("Hello STM32");  /* sends hello string */
    Wire.endTransmission();    /* stop transmitting */

    delay(1000);
}


Со стороны STM32 проект сгенерен в CubeMX включен I2C1. Поправил только функцию main вот так.
Код:
#include "main.h"
#include "i2c.h"
#include "gpio.h"

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();

  uint8_t buf[32];
  while (1) {
        HAL_I2C_Slave_Receive(&hi2c1, buf, 11, 5000);
}



И судя по тому что показывает логический анализатор на шине, все работает......
Изображение

Как только я пытаюсь заменить вот это
Код:
        HAL_I2C_Slave_Receive(&hi2c1, buf, 11, 5000);

вот на это
Код:
HAL_I2C_Slave_Receive_IT(&hi2c1, buf, 11);
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_LISTEN);


Что в моем пониаение эквивалентно. Все резко перестает рботать.
На шиге это выглядит вот так.
Изображение

т.е. slave перестает говорить ACK на свой адрес?
Я что то забыл включить? Как это поправить?

Re: STM32 не работает I2C в slave режиме.

Вс ноя 06, 2022 19:54:35

Не использую HAL, но быстрый гуглеж показал, что в режиме прерывания функцию HAL_I2C_Slave_Receive_IT следует вызывать уже после того, как мастер выставил адрес на шину. Поймать это событие можно через callback-функцию HAL_I2C_AddrCallback. То есть так примерно:
Код:
// Где-то в настройках:
HAL_I2C_EnableListen_IT(...) - включить в принципе прерывания

// В месте, где определены обработчики событий
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
    // Прилетел адрес ведомого, здесь уже можно вызывать HAL_I2C_Slave_Seq_Receive_IT
}

Вроде примерно так.

Re: STM32 не работает I2C в slave режиме.

Вс ноя 06, 2022 20:21:57

Определил функцию HAL_I2C_AddrCallback
Код:
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) {
    // Прилетел адрес ведомого, здесь уже можно вызывать HAL_I2C_Slave_Seq_Receive_IT
    uint8_t buf[32];
    HAL_I2C_Slave_Seq_Receive_IT( &hi2c1, buf, 11, I2C_FIRST_FRAME);
}


Поправил main, включил прерывания
Код:
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();

    HAL_I2C_EnableListen_IT(&hi2c1);
    while (1) {

    }
}


Судя по дебаггеру HAL_I2C_AddrCallback вызывается.
Но результат такой же, на шине вот так
Изображение

Re: STM32 не работает I2C в slave режиме.

Пн ноя 07, 2022 21:19:41

В общем раскурил я эту историю.... Работает. Но вопросов только прибавилось.
Нашел вот такую статью https://community.st.com/s/article/how- ... be-library
Собственно ей руководстовался....
Оказалось разрешать прерывания, т.е. вызывать HAL_I2C_EnableListen_IT(&hi2c1) надо после каждого запроса на прием/передачу.

Что сделал что бы заработало:
1. Функция HAL_I2C_AddrCallback выглядит вот так. Как я понял это call back котороя вызывается когда мастер на шине выставляет адрес ведомого
Код:
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) {
    uint8_t test_buf[] = "Hello ESP-12F";
    if (TransferDirection == I2C_DIRECTION_RECEIVE) {
        HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t *) test_buf, 13, I2C_FIRST_AND_LAST_FRAME);
    } else {
        HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t *) test_buf, 11, I2C_FIRST_AND_LAST_FRAME);
    }
}


2. Вот так вылядит основной цикл main который по флагу завершения передачи разрешате прерывания Xfer_Complete
Код:
    HAL_I2C_EnableListen_IT(&hi2c1);
    while (1) {     
        if (Xfer_Complete == 1) {
            HAL_Delay(1);
            HAL_I2C_EnableListen_IT(&hi2c1);
            Xfer_Complete = 0;
        }
    }

3. Еще две call back функции которые выставляют флаг окончания передачи
Код:
extern __IO uint32_t Xfer_Complete;

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *I2cHandle) {
    Xfer_Complete = 1;
}

void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *I2cHandle) {
    Xfer_Complete = 1;
}


Все этого достаточно что бы все заработало. Сейчас картинка обмена на шине с логического анализатора выглядит вот так:
Изображение

Теперь чего я не понимаю и буду благодарен за подсказку куда смотреть.....
1. В главном цикле перед тем как разрешить прерывания (после того как call back функция об окончание передачи была вызвана) приходиться делать паузу 1 млс. Без этого не работает.... Почему? Из за этого ведущему приходиться делать паузу между запросами в 2млс. что бы все гарантировано работало....
2. Почему нельзя разрешить прерывания прямо в call back функции? Например вот так:
Код:
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *I2cHandle) {
    Xfer_Complete = 1;
    HAL_Delay(1);
    HAL_I2C_EnableListen_IT(&hi2c1);
}

Так не работает...
3. Ну и совсем не объясимо для меня...
Код:
uint8_t aTxBuffer[32];
uint8_t aRxBuffer[32];
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode) {
    uint8_t test_buf[] = "Hello ESP-12F";
    if (TransferDirection == I2C_DIRECTION_RECEIVE) {
        HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t *) test_buf, 13, I2C_FIRST_AND_LAST_FRAME);
    } else {
        HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t *) test_buf, 11, I2C_FIRST_AND_LAST_FRAME);
    }
}

Обратите внимание, что в функциях HAL_I2C_Slave_Seq_Transmit_IT и HAL_I2C_Slave_Seq_Receive_IT используется локально объявленная еремення test_buf.
Так работает.....
Если использовать глобальные переменные aTxBuffer и aRxBuffer то контроллер уходит в непрерывную перезагрузку.... Почему?
Ответить