koluna 0 31 августа Опубликовано 31 августа (изменено) · Жалоба Здравстуйте! Столкнулся с проблемой при обмене между процессором и датчиком. Для начала пытаюсь считать из датчика всего 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; } Изменено 31 августа пользователем koluna Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 31 августа Опубликовано 31 августа · Жалоба Функция инициализации. 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); } Частоту пробовал разную, ситуация не меняется. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 31 августа Опубликовано 31 августа · Жалоба Резисторы подтяжки изначально 4.7 к. Уменьшал до 1 к, увеличивал до 10 к - не влияет. От процессора до датчика по проводам 10-15 см. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 31 августа Опубликовано 31 августа · Жалоба Чтобы прекратить чтение из слейва (датчика), нужно после последнего принятого байта отправить "NoAck", то есть, не подтверждать байт, при этом ведомый отпускает линии и мастер может после этого сформировать сигнал Stop. Сигнал NoAck - высокий уровень, выставляемый мастер-приёмником. Причем, сигнал NoAck нужно выставить в заданный временной промежуток. В мануале предлагается два варианта этого действия. У вас сигнал NoAck не успевает пройти в заданное время и датчик считает, что мастер продолжает приём данных, но использует clock stretch (не генерирует такты) для ожидания. Сигнал Stop не формируется, поскольку датчик удерживает управление линией SDA. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 3 сентября Опубликовано 3 сентября · Жалоба Да, начал разбираться с I2C, проверил и убедился сам в этом. Спасибо! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 3 сентября Опубликовано 3 сентября (изменено) · Жалоба Другой вопрос. Если мы общаемся (чтение 1 байта) к устройству, которого нет на шине (адрес другой), то что в этом случае должно происходить? По сути мы получаем NAK. И что дальше? Сейчас опять подвисаю после отправления байта адреса: контроллер удерживает SCL в низком уровне, датчик - тоже. Кстати, датчик этот не умеет clock stretch. Изменено 3 сентября пользователем koluna Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 3 сентября Опубликовано 3 сентября · Жалоба NACK и должен происходить. Чтобы это понять, надо разобраться, что такое этот NACK. Это высокий уровень линии SDA в момент 9-го импульса SLK. Когда от мастера идет в линии передача адреса слейва, в течение первых 8-ми импульсов SCL шиной SDA управляет мастер. А на 9-м импульсе он ждет ответа от слейва. Если слейв с запрашиваемым адресом есть на линии, он в этот момент притянет SDA к 0, это будет ACK. А если слейва с запрашиваемым адресом нет на линии, то притянуть SDA будет некому, она останется в высоком уровне, это будет NACK. Если в ответ на запрос мастером адреса слейва пришел NACK (высокий уровень SDA), то мастер должен сформировать Stop. Байт адреса слейва в младшем бите содержит признак направления дальнейших данных - запись или чтение. Это надо учитывать, потому как поведение датчика будет зависеть от этого бита. Надо прочесть мануал на датчик. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 3 сентября Опубликовано 3 сентября · Жалоба 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. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 3 сентября Опубликовано 3 сентября · Жалоба Таймаут не нужен, просто после передачи адреса слейва прочитайте состояние бита ACK. И если нет состояния Ack, тогда просто формируете Stop. В мануале на модуль I2C подробно расписан порядок действий и порядок проверки флагов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 3 сентября Опубликовано 3 сентября · Жалоба На F100 без таймаутов с I2C нельзя. Он там мягко говоря не очень. Всегда есть ненулевая вероятность остаться в цикле ожидания любого бита навсегда. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
x893 60 3 сентября Опубликовано 3 сентября · Жалоба On 8/31/2024 at 12:36 PM, koluna said: Прошу помочь разобраться. Для понимания сделайте обмен ногодрыгом. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться