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

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Foxhound
Открыл глаза
Сообщения: 60
Зарегистрирован: Вт июл 17, 2018 09:52:27

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

Сообщение Foxhound »

Не получается реализовать работу 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 на свой адрес?
Я что то забыл включить? Как это поправить?
Реклама
Аватара пользователя
azhel12
Встал на лапы
Сообщения: 145
Зарегистрирован: Пн апр 02, 2012 15:56:23

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

Сообщение azhel12 »

Не использую 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
}
Вроде примерно так.
Реклама
Foxhound
Открыл глаза
Сообщения: 60
Зарегистрирован: Вт июл 17, 2018 09:52:27

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

Сообщение Foxhound »

Определил функцию 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 вызывается.
Но результат такой же, на шине вот так
Изображение
Foxhound
Открыл глаза
Сообщения: 60
Зарегистрирован: Вт июл 17, 2018 09:52:27

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

Сообщение Foxhound »

В общем раскурил я эту историю.... Работает. Но вопросов только прибавилось.
Нашел вот такую статью 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 то контроллер уходит в непрерывную перезагрузку.... Почему?
Реклама
Эиком - электронные компоненты и радиодетали
Ответить

Вернуться в «ARM»