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

STM32F100C8 + BMP280 - проблемы с I2C

Здравстуйте!

Столкнулся с проблемой при обмене между процессором и датчиком.
Для начала пытаюсь считать из датчика всего 1 байт - идентификатор.
Использую SPL, код простейший, таймауты пока не обрабатывал, но знаю где зависает (см. в коде).
Датчик после обмена удерживает линию SDA в нуле (именно он, проверял отсоединяя его),
Если линию SDA отсоединить, потом присоединить, то функция чтения завершается и возвращается правильный результат - x58.
Приложил осциллограмму (верхний канал - SDA, нижний - SCL).

Прошу помочь разобраться.
 

uint8_t byte = I2C_single_read(I2C1, BMP280_ADDRESS << 1, 0xD0);

...
  
  
uint8_t I2C_single_read(I2C_TypeDef* I2Cx, uint8_t HW_address, uint8_t addr)
{
	uint8_t data;

	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));

	I2C_GenerateSTART(I2Cx, ENABLE);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

	I2C_Send7bitAddress(I2Cx, HW_address, I2C_Direction_Transmitter);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

	I2C_SendData(I2Cx, addr);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	I2C_GenerateSTART(I2Cx, ENABLE);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));

	I2C_Send7bitAddress(I2Cx, HW_address, I2C_Direction_Receiver);
	while(!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));

	data = I2C_ReceiveData(I2Cx);
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));

	I2C_AcknowledgeConfig(I2Cx, DISABLE);
	I2C_GenerateSTOP(I2Cx, ENABLE);
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)); // Зависает здесь!!!

	return data;
}

 

photo_2024-08-31_13-31-00.jpg

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

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


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

Функция инициализации.
 

void I2C_init(I2C_TypeDef* I2Cx, uint32_t speed)
{
    RCC_APB1PeriphClockCmd(I2Cx == I2C1 ? RCC_APB1Periph_I2C1 : RCC_APB1Periph_I2C2, ENABLE);

    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
    delayMSec(10);
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);

    I2C_InitTypeDef I2C_InitStructure;
    //	I2C_StructInit(&I2C_InitStructure);
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_ClockSpeed = speed;
    I2C_InitStructure.I2C_OwnAddress1 = 1;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2Cx, &I2C_InitStructure);
    I2C_Cmd(I2Cx, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

Частоту пробовал разную, ситуация не меняется.

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


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

Резисторы подтяжки изначально 4.7 к.
Уменьшал до 1 к, увеличивал до 10 к - не влияет.
От процессора до датчика по проводам 10-15 см.

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


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

Чтобы прекратить чтение из слейва (датчика), нужно после последнего принятого байта отправить "NoAck", то есть, не подтверждать байт, при этом ведомый отпускает линии и мастер может после этого сформировать сигнал Stop. Сигнал NoAck - высокий уровень, выставляемый мастер-приёмником. Причем, сигнал NoAck нужно выставить в заданный временной промежуток. В мануале предлагается два варианта этого действия. 
У вас сигнал NoAck не успевает пройти в заданное время и датчик считает, что мастер продолжает приём данных, но использует clock stretch (не генерирует такты) для ожидания. Сигнал Stop не формируется, поскольку датчик удерживает управление линией SDA.

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


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

Другой вопрос.

Если мы общаемся (чтение 1 байта) к устройству, которого нет на шине (адрес другой), то что в этом случае должно происходить?
По сути мы получаем NAK. И что дальше?
Сейчас опять подвисаю после отправления байта адреса: контроллер удерживает SCL в низком уровне, датчик - тоже.

Кстати, датчик этот не умеет clock stretch.

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

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


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

NACK и должен происходить. Чтобы это понять, надо разобраться, что такое этот NACK. Это высокий уровень линии SDA в момент 9-го импульса SLK. Когда от мастера идет в линии передача адреса слейва, в течение первых 8-ми импульсов SCL шиной SDA управляет мастер. А на 9-м импульсе он ждет ответа от слейва. Если слейв с запрашиваемым адресом есть на линии, он в этот момент притянет SDA к 0, это будет ACK. А если слейва с запрашиваемым адресом нет на линии, то притянуть SDA будет некому, она останется в высоком уровне, это будет NACK.

Если в ответ на запрос мастером адреса слейва пришел NACK (высокий уровень SDA), то мастер должен сформировать Stop.

 

Байт адреса слейва в младшем бите содержит признак направления дальнейших данных - запись или чтение. Это надо учитывать, потому как поведение датчика будет зависеть от этого бита. Надо прочесть мануал на датчик.

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


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

On 9/3/2024 at 3:22 PM, EdgeAligned said:

NACK и должен происходить. Чтобы это понять, надо разобраться, что такое этот NACK. Это высокий уровень линии SDA в момент 9-го импульса SLK. Когда от мастера идет в линии передача адреса слейва, в течение первых 8-ми импульсов SCL шиной SDA управляет мастер. А на 9-м импульсе он ждет ответа от слейва. Если слейв с запрашиваемым адресом есть на линии, он в этот момент притянет SDA к 0, это будет ACK. А если слейва с запрашиваемым адресом нет на линии, то притянуть SDA будет некому, она останется в высоком уровне, это будет NACK.

Это я уже знаю, все осциллографом видно 🙂

On 9/3/2024 at 3:22 PM, EdgeAligned said:

Если в ответ на запрос мастером адреса слейва пришел NACK (высокий уровень SDA), то мастер должен сформировать Stop.

Да, чувствую, что надо сделать как-то так, но как конкретно - пока не знаю.
Сейчас там в цикле ожидание флагов, нужная комбинация не устанавливается и подвисаем:

#define  I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED        ((uint32_t)0x00070082)  /* BUSY, MSL, ADDR, TXE and TRA flags */

Наверное, по таймауту надо выходить и формировать STOP.

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


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

Таймаут не нужен, просто после передачи адреса слейва прочитайте состояние бита ACK. И если нет состояния Ack, тогда просто формируете Stop.

В мануале на модуль I2C подробно расписан порядок действий и порядок проверки флагов.

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


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

На F100 без таймаутов с I2C нельзя. Он там мягко говоря не очень. Всегда есть ненулевая вероятность остаться в цикле ожидания любого бита навсегда.

 

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


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

On 8/31/2024 at 12:36 PM, koluna said:

Прошу помочь разобраться.

Для понимания сделайте обмен ногодрыгом.

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


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

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

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

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

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

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

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

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

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

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