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

залипает шина I2C в STM32

STM32 подключен слейвом к другому процу АРМ на Линуксе.

Все работает, но поведение странное. Периодически отваливается шина I2C и мой STM не получает никакие команды из вне. Написана на регистрах инициализация. Подтяжки шины к +3,3 по 7,5 К.

Куда можно смотреть? Может что не проинициализировал в МК?

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


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

Куда можно смотреть? Может что не проинициализировал в МК?

так проведите диагностику с логированием

как на стороне линукса - диагностический выхлоп драйвера i2c в syslog

так и стороне stm32, желательно вывод транзакций с записью с консоли + дамп статусных регистров

ну и осцилл на шину

 

смотрите работу, фиксируете, возник дефект - изучаете картину, ловите виновного..

может слейв NAK глотает, может тайминги не держит, может машина состояния что-то не формирует

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


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

так проведите диагностику с логированием

как на стороне линукса - диагностический выхлоп драйвера i2c в syslog

так и стороне stm32, желательно вывод транзакций с записью с консоли + дамп статусных регистров

ну и осцилл на шину

 

смотрите работу, фиксируете, возник дефект - изучаете картину, ловите виновного..

может слейв NAK глотает, может тайминги не держит, может машина состояния что-то не формирует

 

что значит, NAK глотает? А если тайминги не держит - что значит? Как смотреть?

Логгирование в Линуксе I2C как запустить?

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


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

Изъездил аппаратный I2C Slave на STM32 вдоль и поперек. Сталкивался с тем, что иногда I2C Slave ловит ложное STOP-условие, то есть перепад Low->High на SDA при высоком SCL. Это может происходить, когда мастер меняет SDA близко к фронтам SCL, и учитывая емкостные свойства линий SCL/SDA, аппаратный контроллер фиксирует STOP-условие посреди посылки, что приводит к установке бита 8 (BERR - Bus Error, Misplaced Start or Stop condition) в регистре статуса. Посылка при этом, естественно, теряется, и конечный автомат ждёт снова START-условия, адреса и т. п.

Поэтому, задействуя аппаратный контроллер I2C, необходимо включать прерывания по ошибочным состояниям и корректно обрабатывать их.

Самое печальное в этом то, что отключить такое поведение (отслеживание Bus Error) нельзя, и это очень мешает нормальной работе, особенно в приложениях, где мастер "не любит" повторять запросы, и при неответе слейва тупо фиксирует ошибку.

Что касается меня, то я, изрядно повозившись с аппаратным I2C Slave на STM32, переписал драйвер таким образом, что аппаратный I2C делает для меня только START+сравнение адреса, после чего "будит" контроллер прерыванием "Address matched", после чего ноги переназначаются на GPIO, и дальнейшая работа идёт софтовой эмуляцией I2C Slave.

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


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

выложу инициализацию I2C. Может у кого глаз зацепится за что-то некорректное.

Проц STM32F030

На всякий случай сейчас еще раз перевыкачал CubeMX, так с ним шину I2C Линукс даже не сканирует. Сразу глючит. HAL вообще здесь не применим.

 

void init(void)
{
    i2c.max_rx_count = sizeof(incoming);
    i2c.state = st_idle;
    uint_fast32_t bus_clk;    
    uint_fast32_t scale;
    static const struct gpio_configuration pinconf =
    {
      .open_drain=1,
      .output=0,
      .analog=0,
      .alter=1,
      .speed=0,
      .pullup=0, // было 1
      .pulldn=0,
      .altfunc = 1, // convenient i2c
      .lock=0
    };

    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    gpio_pin_conf(GPIOB, 6,1,&pinconf);
    gpio_pin_conf(GPIOB, 7,1,&pinconf);
    RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
    RCC->APB1RSTR ^= RCC_APB1RSTR_I2C1RST;
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    SystemCoreClockUpdate();
// get sysclk for apb1
    if((RCC->CFGR3 & RCC_CFGR3_I2C1SW) == 0) // RCC_CFGR3_I2C1SW если 1, то от SystemCoreClock
    {
        bus_clk = HSI_VALUE;
    }
    else
    {
        bus_clk = SystemCoreClock;
        if(RCC->CFGR & RCC_CFGR_PPRE_2)
        {
            uint_fast32_t factor = RCC->CFGR & (RCC_CFGR_PPRE_0 | RCC_CFGR_PPRE_1);
            if(factor & RCC_CFGR_PPRE_0) factor |= 1;
            if(factor & RCC_CFGR_PPRE_1) factor |= 2;
            factor &= 3;
            bus_clk >>= factor + 1;
        }
    }
// general scale factor to 8mhz
scale = bus_clk / (HSI_VALUE/2);
scale -= 1;    
scale &= 0x0f;
myI2C->TIMINGR = scale << 28;
// tclk = 250ns refman page 556 - place config from table 100khz
myI2C->TIMINGR |= 0x13 //scll
        |    (0x0f << 8) //sclh
        |    (0x02 << 16) //sdadel
        | (0x04 << 20); //    scldel
//conf i2c
    NVIC_EnableIRQ(I2C1_IRQn);
//set int mask
    myI2C->CR1 = I2C_CR1_ADDRIE
                        | I2C_CR1_RXIE
                          | I2C_CR1_TXIE

                        //|    I2C_CR1_NACKIE
                        |    I2C_CR1_STOPIE
                        |    I2C_CR1_TCIE
                        |    I2C_CR1_ERRIE;
// enable analog noise filtering or digital
//    myI2C->CR1 |= I2C_CR1_ANFOFF;// currently analog
// clock stretch enabled

// set own address
    myI2C->OAR1 = I2C_OAR1_OA1EN | ((get_slave_address() & 0x7F) << 1);
// enable 
    myI2C->CR1 |= I2C_CR1_PE;
}

 

void gpio_pin_conf(
    GPIO_TypeDef *gp,
    uint8_t    num,
    uint8_t    value,
    const struct gpio_configuration *conf)
{
    volatile uint32_t *altreg;
    uint32_t msk2 = 1 << (num*2);
    uint32_t msk4 = 1 << ((num & 0x07)*4);
    num &= 0x0f;
    gp->MODER &= ~(3*msk2);
    if(conf->output) gp->MODER |= msk2;
    if(conf->analog) gp->MODER |= 3*msk2;
    if(conf->alter) gp->MODER |= 2*msk2;

    if(conf->open_drain) gp->OTYPER |= 1 << num;
    else gp->OTYPER &= ~(1 << num);

    gp->OSPEEDR &= ~(3*msk2);
    gp->OSPEEDR |= conf->speed * msk2;

    gp->PUPDR &= ~(3*msk2);
    if(conf->pullup) gp->PUPDR |= msk2;
    if(conf->pulldn) gp->PUPDR |= 2*msk2;

    //set value
    gp->BSRR |= 1 << (num + value?0:16);
    //set af
    altreg = &gp->AFR[(num >= 8)];
    *altreg &= ~(0x0F * msk4);
    *altreg |= conf->altfunc * msk4;
}

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


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

Как по мне, сбрасывать периферию, не включив её клок, бессмысленно, она "мертва".

Последовательность инициализации несколько напрягает - сначала нужно включать клок, потом инитить и включать периферию, только потом конфигурировать "ноги", и уже в самом конце разрешать от неё прерывания. При нарушении такой последовательности возможны всяки-разны казусы, типа невовремя стрельнувшего прерывания и т. п.

Хотя это всё мелочи и придирки, в общем-то. :laughing:

Ах, да, ещё I2C Slave не нуждается в делителях клока, это всё нужно только мастеру. Slave работает на том клоке SCL, который приходит от мастера.

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

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


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

ну а так в целом- все регистры проинициализированы корректно? Или чего-то забыто?

 

Как по мне, сбрасывать периферию, не включив её клок, бессмысленно, она "мертва".

Последовательность инициализации несколько напрягает - сначала нужно включать клок, потом инитить и включать периферию, только потом конфигурировать "ноги", и уже в самом конце разрешать от неё прерывания. При нарушении такой последовательности возможны всяки-разны казусы, типа невовремя стрельнувшего прерывания и т. п.

Хотя это всё мелочи и придирки, в общем-то. :laughing:

Ах, да, ещё I2C Slave не нуждается в делителях клока, это всё нужно только мастеру. Slave работает на том клоке SCL, который приходит от мастера.

не понял по включению клока:

RCC->AHBENR |= RCC_AHBENR_GPIOBEN;

вот включил клок, только потом дальше начинаю инициализировать регистры. Не так?

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


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

ну а так в целом- все регистры проинициализированы корректно? Или чего-то забыто?

В целом, вроде ОК.

не понял по включению клока:

RCC->AHBENR |= RCC_AHBENR_GPIOBEN;

вот включил клок, только потом дальше начинаю инициализировать регистры. Не так?

Это клок GPIOB, имелся в виду клок I2C-контроллера

RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

да, его надо включать в самом начале инициализации, даже до сброса периферии, КМК.

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


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

RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
gpio_pin_conf(GPIOB, 6,1,&pinconf);
gpio_pin_conf(GPIOB, 7,1,&pinconf);
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
RCC->APB1RSTR ^= RCC_APB1RSTR_I2C1RST;

 

так пойдет?

 

Здесь мы ловим всех блох, которые глючат. Поэтому любое, что режет глаз- будем править.

 

Насчет делителя для мастера- можно его не трогать? Я понял, что это просто лишнее написано и не применяется, но не мешает слейву моему.

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


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

так пойдет?

Я бы снес инициализацию ног после инициализации I2C, хотя это и непринципиально, возможно. Дело вкуса, но подключать неинициализированный I2C к реальной шине, а потом его инициализировать, ИМХО, не очень правильно.

Насчет делителя для мастера- можно его не трогать? Я понял, что это просто лишнее написано и не применяется, но не мешает слейву моему.

Да, это не мешает слейву, просто лишняя операция.

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


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

Я бы снес инициализацию ног после инициализации I2C, хотя это и непринципиально, возможно. Дело вкуса, но подключать неинициализированный I2C к реальной шине, а потом его инициализировать, ИМХО, не очень правильно.

 

Не очень понял- как предлагаете сделать?

 

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


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

Не очень понял- как предлагаете сделать?

myI2C->CR1 |= I2C_CR1_PE;

потом

gpio_pin_conf(GPIOB, 6,1,&pinconf);

gpio_pin_conf(GPIOB, 7,1,&pinconf);

и лишь потом

NVIC_EnableIRQ(I2C1_IRQn);

Но, повторюсь, это всё дело вкуса, и вряд ли решит вопрос стабильности работы I2C.

ИМХО, аппаратный I2C Slave в STM32 пригоден для работы лишь с идеальным I2C-мастером, соблюдающим "километровые" тайминги, и не переключающим SDA вблизи фронтов SCL.

Вблизи - это на расстоянии менее 2-3 мкс.

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


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

Можно как-то попросить поделиться драйвером вашим?

Пока что стоит на прогоне плата у меня- ничего не вылетает. Может просто помыл грязь перед этим.

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


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

STM32 подключен слейвом к другому процу АРМ на Линуксе.

Все работает, но поведение странное. Периодически отваливается шина I2C и мой STM не получает никакие команды из вне. Написана на регистрах инициализация. Подтяжки шины к +3,3 по 7,5 К.

Куда можно смотреть? Может что не проинициализировал в МК?

 

Подтяжки шины к +3,3 по 7,5 К. - нормальные, но все равно посмотрите осцилграфом форму фронтов (SDA-SCL). Мало ли что могло затянуть.

Захват данных идет по фронту. Если поплыло - на осцилографе будет видно.

На скорости 300 kHz у меня работает нормально на 10к.

 

В чем проявляется "отваливание" ?

 

Я свой колхоз с I2C HAL (MSP430) отлаживал с активным использованием лог. анализатора.

Сильно рекомендую.

 

Ваш проект "slave" - а он, на мой взгляд существенно сложнее master.

Особенно обратите внимание на ACK - NACK квитирование. Если не соотв-ет протоколу I2C и slave отработал не корректно

(по логике протокола) то можно войти в ступор. Например вход в режим "ожидания готовности slave".

А слейв это сделал "не в том смысле", или наоборот, не сделал.

 

Как альтернативный вариант. Может ошибка в мастере ?

 

 

 

 

 

 

 

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


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

на ночь оставлю на прогоне девайс- посмотрим к утру. Правки внес, что gerber предложил. Еще убрал внутреннюю подтяжку программную

.pullup=0, // было 1

0 выставил.

 

Мастер- это Линукс. Должно быть все отлажено. Проц на линуксе, в смысле.

 

Посмотрим утром. Отпишусь.

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


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

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

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

Гость
Ответить в этой теме...

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

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

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

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

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

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