Перейти к содержанию
    

stm32l0 отдельные старт, передача данных и стоп в i2c

Здравствуйте, вопрос, как сделать раздельные функции начала передачи данных, самой передачи и завершения передачи в i2c?
Зачем это нужно? Не всегда есть возможность передать большой массив одним пакетом через стандартные функции типа HAL_I2C_Master_Transmit, т.к. предварительно их придётся поместить в ОЗУ.
Код функции старт:

Спойлер
		while (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_BUSY) == SET) {
		}
		I2C_TransferConf(&hi2c1, (60 << 1), 0, I2C_RELOAD_MODE,
		I2C_GENERATE_START_WRITE);

 

Для функции стоп:

Спойлер
		while (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_TCR) == RESET) {
		}
		I2C_TransferConf(&hi2c1, 0, 0, I2C_SOFTEND_MODE,
		I2C_GENERATE_STOP);
		while (__HAL_I2C_GET_FLAG(&hi2c1, I2C_FLAG_STOPF) == RESET) {
		}
		__HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_STOPF);

 

А вот как передавать между ними что-то, я так и не понял

		for (int i = 0; i <300;i++)
		{
			//ПЕРЕДАЧА данных
		}

Прикрепляю исходник, весь код в функции main, там я пытаюсь отправить 300 байт

test_l052c8t6.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вот как передавать между ними что-то, я так и не понял
А вот на каждый ACK от ведомого и отправлять...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 минут назад, Obam сказал:

А вот как передавать между ними что-то, я так и не понял
А вот на каждый ACK от ведомого и отправлять...

было бы славно, да вот ACK бита там не наблюдаю

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

"Там" это где?
В регистре состояния (SR) I2C нет флага, обозначающего 9-й бит в байтовой транзакции?!! (((-8Ж Серьёзно?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 минут назад, Obam сказал:

"Там" это где?
В регистре состояния (SR) I2C нет флага, обозначающего 9-й бит в байтовой транзакции?!! (((-8Ж Серьёзно?

Спойлер

image.thumb.png.2c10086e89b50a43eb46507cb5b163ee.pngimage.png.b66dee3a6185dfccd00038867beecda3.png

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А ISR (прерываний и состояния регистр) что ж не зацитировали? (((-8Ж

А если чуть серьёзнее: вы же HAL хотите применять? Вот и комбинируйте флаги пустого регистра данных (передаваемых) и NACKF.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 часов назад, Obam сказал:

А ISR

Спойлер

image.png.e4947975c9df8231e806337b96d2dafc.png

15 часов назад, Obam сказал:

Вот и комбинируйте флаги пустого регистра данных (передаваемых) и NACKF.

TXE, я так понимаю? Но ведь нужно и в NBYTES записывать 1

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну да - единожды, автомат I2C будет, если не забудете бит RELOAD установить, требовать новый и новый байт по подтверждении от ведомого, но сами и будете считать сколько отправили: максимально "железяку" разгружаете ;-)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

31 минуту назад, Obam сказал:

Ну да - единожды, автомат I2C будет, если не забудете бит RELOAD установить, требовать новый и новый байт по подтверждении от ведомого, но сами и будете считать сколько отправили: максимально "железяку" разгружаете 😉

Не совсем понял, в какой последовательности это делать, а старт и стоп я правильно генерирую?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

NBYTES и RELOAD в одном регисте находятся, какая там последовательность может быть ? (Четырьмя машинными командами задаются)

а старт и стоп я правильно генерирую?
А это ведомый покажет (без него никак) и логический анализатор\осциллограф.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Старт - одновременная запись адреса ведомого (I2C_CR2_SADD), количества байт (I2C_CR2_NBYTES) и I2C_CR2_START в CR2 (возможно, путаю, и запись I2C_CR2_START нужно делать отдельно, через |=).

Передачу байт делаем записью в TXDR при получении I2C_ISR_TXIS в ISR, этот самый TXIS у вас возникнет ровно столько раз, сколько байт вы задали в CR2.

При получении I2C_ISR_TC генерируете стоп записью I2C_CR2_STOP в CR2.

Если я ничего не упустил, этого минимума должно быть достаточно, чтобы оно завелось.

Если у вас массив больше, чем 255/256/сколько-там байт (ограничение поля NBYTES в CR2) - сначала запустите на небольших объёмах, а потом читайте reference manual, там должно быть описано, что и как надо делать для этого случая.

Но имейте в виду, что так получится очень нехороший драйвер I2C. Для того, чтобы сделать его хорошим, нужно сделать корректную обработку всех получаемых событий, а не только TXIS/TC.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кто как, а у меня на L051 работает вот такой запуск I2C:

    I2C1->CR2 = (I2C_CR2_AUTOEND | DevAddr << 1 | I2C_CR2_RD_WRN | (DataToRead<<16) | I2C_CR2_START);

Сразу пишем в регистр и адрес ведомого и автостоп (после нужного количества байт) и всё прекрасно срабатывает.
Ещё раз убеждаюсь, что в L05x перенесли блок I2C от F030, а не из F407, где всё делалось через тыканье битов на каждое действие: Отправили старт? Шлём адрес. Отправили? Отлично. Ошибка?! Ах ты ж... ну ладно. Или Закончили отправлять байт? Отлично, продолжаем. Ошибка линии? Ну вот опять 😞
А тут? Стартуем на адрес XXXX, отправлять будем YYYY байт! После отправки - стоп.

И всё! Знай только байты подкладывай. И иногда следи за флагами состояний.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только ограничение YYYY на 255 немного портит малину. Если данных мало, то всё просто

Спойлер

 

bool MasterWrite(const uint8_t hw_adr, uint8_t *data, uint8_t len)
{
  base()->CR2 = _VAL2FLD(I2C_CR2_NBYTES,len) | hw_adr | I2C_CR2_AUTOEND | I2C_CR2_START;
  while(len--)
  {
    if(!waitISRBitSet<I2C_ISR_TXIS>()) return false;
    base()->TXDR = *data++;
  }    
  return true;
}

// Ожидание статусных бит с обработкой ошибок и антизацикливанием
static inline bool waitISRBitSet()
{
  uint32_t sr;
  uint32_t timeout_counter=1'000'000;
  do 
  {
    sr=base()->ISR;
    if(sr&( I2C_ISR_NACKF | I2C_ISR_ARLO | I2C_ISR_BERR))
    {
      base()->ICR = I2C_ICR_NACKCF | I2C_ICR_BERRCF | I2C_ICR_ARLOCF;
      return false;      
    }
    if(--timeout_counter == 0) return false;
  }
  while( !(sr&Bit) );
  return true;
}

 

Тут base() понимать как I2C1, I2C2 и т.д. Эта функция член класса позволяет "протащить" указатель на периферию в constexpr контекст. Без плюсов можно просто указателем на I2C_TypeDef обойтись.

template<uint32_t PI2C>
class TI2C
{  
  static inline auto base() { return (I2C_TypeDef *)PI2C;  }
}

 

Если надо передавать много, то чуть сложнее

Спойлер

 

bool MasterWrite(const uint8_t hw_adr, uint8_t *data, uint32_t len)
{
  bool first_part = true;      
  while(len)
  {
    if(len<256)        
    {
      base()->CR2 = _VAL2FLD(I2C_CR2_NBYTES,len) | hw_adr | I2C_CR2_AUTOEND | (first_part?I2C_CR2_START:0);
      while(len)
      {
        if(!waitISRBitSet<I2C_ISR_TXIS>()) return false;
        base()->TXDR = *data++;
        len--;
      }
    }
    else
    {
      base()->CR2 = _VAL2FLD(I2C_CR2_NBYTES,255) | hw_adr | I2C_CR2_RELOAD | (first_part?I2C_CR2_START:0);
      first_part=false;
      for(uint32_t i=255; i--;) 
      {
        if(!waitISRBitSet<I2C_ISR_TXIS>()) return false;
        base()->TXDR = *data++;          
      } 
      if(!waitISRBitSet<I2C_ISR_TCR>()) return false;
      len-=255;
    }
  }    
  return true;
}

 

Оба случая можно под шаблон объединить

Спойлер

 

template<typename TLen>
static inline bool MasterWrite(const uint8_t hw_adr, uint8_t *data, TLen len)
{
  if constexpr(sizeof(TLen)==1)
  {      
    base()->CR2 = _VAL2FLD(I2C_CR2_NBYTES,len) | hw_adr | I2C_CR2_AUTOEND | I2C_CR2_START;
    while(len--)
    {
      if(!waitISRBitSet<I2C_ISR_TXIS>()) return false;
      base()->TXDR = *data++;
    }    
  }
  else
  {
    bool first_part = true;      
    while(len)
    {
      if(len<256)        
      {
        base()->CR2 = _VAL2FLD(I2C_CR2_NBYTES,len) | hw_adr | I2C_CR2_AUTOEND | (first_part?I2C_CR2_START:0);
        while(len)
        {
          if(!waitISRBitSet<I2C_ISR_TXIS>()) return false;
          base()->TXDR = *data++;
          len--;
        }
      }
      else
      {
        base()->CR2 = _VAL2FLD(I2C_CR2_NBYTES,255) | hw_adr | I2C_CR2_RELOAD | (first_part?I2C_CR2_START:0);
        first_part=false;
        for(uint32_t i=255; i--;) 
        {
          if(!waitISRBitSet<I2C_ISR_TXIS>()) return false;
          base()->TXDR = *data++;          
        } 
        if(!waitISRBitSet<I2C_ISR_TCR>()) return false;
        len-=255;
      }
    }    
  }
  return true;
}

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 08.03.2023 в 08:44, AlanDrakes сказал:

автостоп

ну мне без автостопа как-то надо. 

В 07.03.2023 в 20:54, Obam сказал:

А это ведомый покажет (без него никак) и логический анализатор\осциллограф.

Смотрел, старт стоп работают. Но после передачи нескольких пакетов байт затыкается (так в моём исходнике)

Изменено пользователем Mark16

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Каких (что значит) пакетов?
Ведомый 300 байт (то, что вы привели в первом посте) за один заход в состоянии получать?

Но после передачи нескольких пакетов затыкается
Ну в отладчике смотрите в чём причина. Всё определяется ведущим - побайтово можно смотреть, больше того - код можно не писать, с остановленным ядром вручную можно байты в TDR класть и смотреть как работает

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...