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

Вопрос по I2C на STM32F103

Но вот совсем не пойму, а какой смысл использовать на медленной шине I2C прием и передачу по прерываниям, и еще DMA для I2C использовать? Поясните в двух словах, если не сложно! :rolleyes:

Бывают и I2C мегабитные. Не ногодрыгательные канеш.

 

В случае SPI для нескольких микросхем потребуется дешифратор 3-8 для CS и три лини SI, SO, SCK.

Расширяйте свой кругозор:

Для SPI кроме схемы соединения "звезда" (когда каждому слэйву назначается свой CS), бывают ещё слэйвы, поддерживающие соединение "в цепочку" (каждый слэйв имеет MOSI-вход и MOSI-выход, с MISO - аналогично).

А ещё бывают слэйвы, которым можно задать задержку (в тактах от начала CS) начала данных и просто подать все сигналы MOSI/MISO/SCLK/CS на все слэйвы параллельно - все сигналы общие для всех слэйвов. В последнем случае уже получается похоже на I2C - задержку эту можно считать адресом отдельного слэйва.

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


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

Бывают и I2C мегабитные. Не ногодрыгательные канеш.

Да, Массово использую I2C FRAM с мегабитным интерфейсом.

 

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


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

Присоединяюсь к вопросу. Я сразу начал и с прерываниями и с ПДП и под ОС. Все работет. Что я сделал неправильно?

Вы, в итоге, наверняка сделали все правильно. Речь о том, что именно у F1xx I2C очень кривой. Об этом было много тем здесь, еще до появления других Fxxx, просто многие подзабыли. Достаточно напомнить, что при работе с I2C по прерываниям необходимо заботится о том, чтобы это прерывание не было прервано (самый высокий приоритет для I2C, либо вообще запреты прерываний на определенных этапах обработки I2C). Более того, на малых скоростях тактирования ядра (например, с целью энергосбережения) наблюдались комбинации флагов прерывания, которые никак не упоминались в документации, и для которых не было даже определений в STL. Это вводило в ступор обработчик. Поскольку большинство заводит камни сразу на максимуме, на такой косяк натыкались не многие, похоже.

Мне понадобилась пара недель, с последующими доработками при работе с различной I2C периферией, чтобы запустить F103 стабильно. Когда появились F0xx, где I2C переработан, мне хватило и пары часов, чтобы запустить I2C так, что работает до сих пор.

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

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


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

Бывают ситуации когда CPU нечего делать пока идет обмен по I2C и можно без прерываний. У меня в текущем проекте на шине несколько устройств, в то числе разветвитель, которому надо всего один байт в единственный регистр. Все работает, но я довольно криво написал отдельные функции для обмена 1 и 2 байта с адресом и без.

Есть где-нибуть пример универсальной функции с параметрами?

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


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

Бывают ситуации когда CPU нечего делать пока идет обмен по I2C и можно без прерываний.

Если нечего делать, то пусть спит и не тратит энергию. Проснется по прерыванию :)

 

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


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

Вы, в итоге, наверняка сделали все правильно. Речь о том, что именно у F1xx I2C очень кривой. Об этом было много тем здесь, еще до появления других Fxxx, просто многие подзабыли. Достаточно напомнить, что при работе с I2C по прерываниям необходимо заботится о том, чтобы это прерывание не было прервано (самый высокий приоритет для I2C, либо вообще запреты прерываний на определенных этапах обработки I2C). Более того, на малых скоростях тактирования ядра (например, с целью энергосбережения) наблюдались комбинации флагов прерывания, которые никак не упоминались в документации, и для которых не было даже определений в STL. Это вводило в ступор обработчик. Поскольку большинство заводит камни сразу на максимуме, на такой косяк натыкались не многие, похоже.

Мне понадобилась пара недель, с последующими доработками при работе с различной I2C периферией, чтобы запустить F103 стабильно. Когда появились F0xx, где I2C переработан, мне хватило и пары часов, чтобы запустить I2C так, что работает до сих пор.

 

А можно кусок кода или пример по обработке (что б не пройти по тем же граблям).

ОГРОМНОЕ СПАСИБО

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


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

А можно кусок кода или пример по обработке (что б не пройти по тем же граблям).

ОГРОМНОЕ СПАСИБО

Я приведу кусок - обработчик прерывания. Сразу скажу, что он работает в моей, назовем это так, - экосистеме. Это макросы, различные процедуры, не приведенные здесь. Обработчик - один для нескольких I2C периферий. Поэтому используются управляющие структуры данных. Смотрите не столько на инструкции, сколько на комментарии.

// -----------------------------------------------------------------------------
//                  EVENT INTERRUPT HANDLER 
// -----------------------------------------------------------------------------
static void I2Cx_EV_IRQHandler(uint8_t port)
{
   I2C_Control_Type  *ctrl = &control[port];
   I2C_TypeDef       *i2c  = ctrl->i2c;

   switch (ctrl->evnt = I2C_Event(i2c, ctrl->readsr2)) {

// - -  I2C Started  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

       case I2C_EVENT_MASTER_MODE_SELECT_NO_SB: // MASTER, START sent: BUSY, MSL but SB are set

           break;  // ignore, wait for SB set

       case  I2C_EVENT_MASTER_MODE_SELECT: // MASTER, (RE)START sent: BUSY, MSL and SB are set

           if (ctrl->wcnt > 0) {  // PREPARE WRITING: send the slave Address 
                                                   // with R/W bit ==0
               I2C_SEND_ADDR(i2c, ctrl->ptr[0] & ~I2C_Direction_Receiver);

           } else { // PREPARE READING:

               // For N == 2 set POS bit to move 
               // NACK to the second byte.

               if (ctrl->icnt == 2) 
               {
                   I2C_POS(i2c);   // ACK is already set

               } else {
               #if (USE_I2C_RX_DMA1)

                   // if RX DMA enabled, allow below only for N == 1...

                   if (ctrl->icnt < 2)    
               #endif
                   // For pure interrupt mode RX and
                   // cases (N != 2) enable RxNE interrupt: 
                   I2C_BUFIE_SET(i2c); 
               }
               // Prevent reading of SR2 to avoid resetting of ADDR bit, 
               // if two or less bytes to read (cases N == 1 or N == 2).
               // See RM0008.PDF, Doc ID 13902 Rev 14, Page 735.

               if (ctrl->icnt <= 2) 
                   ctrl->readsr2 = 0;  

               // Send I2C ID (slave Address to receive).
               // Writing DR here preceeded by SR1 reading above clears SB
                                               // with R/W bit ==1
               I2C_SEND_ADDR(i2c, ctrl->ptr[0] | I2C_Direction_Receiver);  
           }
           break;

// - -  I2C Transmitting - - - - - - - - - - - - - - - - - - - - - - - - - - - 

       case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: // ADDR sent with /W bit

       #if (USE_I2C_TX_DMA1)

           // Transmit bytes via DMA

           I2C_START_DMA_TX(ctrl);   
       #else
           // WRITE the FIRST data byte: 

           I2C_WRITE_NEXT(ctrl, ctrl->ptr[ctrl->indx++]); // .icnt-- and .wnt-- inside...
       #endif

           break;
//
//  ATTENTION! - since 03.11.11. does not work without the simple
//               case I2C_EVENT_MASTER_BYTE_TRANSMITTING below as if
//               I2C_EVENT_MASTER_BYTE_TRANSMITTING occures sometimes 
//               before I2C_EVENT_MASTER_BYTE_TRANSMITTED.
//               Possible reasons (due to the following changes): 
//               1). Interrupt priority of I2C has been changed to the highest one;
//               2). The I2C speed has been reduced down to 100kHz from 400kHz, 
//                   the driver was tested before.
//                  
       case I2C_EVENT_MASTER_BYTE_TRANSMITTING:    // Data being SENT; TRA, BSY, MSL and
       {                                           // TxE but BTF are set.
           uint8_t ex = 0;
           uint32_t tp = i2c_SBTimeoutInit(port, 10 * BITS_ONE_BYTE);   // Time of two I2C bytes to wait

           while (!I2C_BTF(i2c) && 
                  !(ex = i2c_SBTimeoutExpired(&tp)));  // Wait for BTF! It MUST arrive else
                                                       // it were a severe hardware error...
           if (ex) // BTF still off: expired! 
           {
               I2C_ERROR_HANDLER(port, ctrl->code = ctrl->evnt);
               break;
           } // else CONTINUE:
       }
       case I2C_EVENT_MASTER_BYTE_TRANSMITTED:     // Data byte's been SENT; TRA, BSY, MSL and
                                                   // both TxE and BTF are set.
           if (ctrl->icnt) 
           { 
               if (ctrl->wcnt == 0) 

                   // RESTART after the last byte from [WRITE] transaction.
                   // See RM0008.PDF, Doc ID 13902 Rev 12, Page 733, 
                   // Note to START and Page 734 to "Closing the 
                   // communication"

                   I2C_START(i2c);  

               else    
                   // just write the next byte
                   I2C_WRITE_NEXT(ctrl, ctrl->ptr[ctrl->indx++]); // .icnt-- and .wnt-- inside...

           } else { 
               // The last byte is completely pushed out, ACK from slave got.
               // Bus is stretched (BTF is set), issue STOP.
               I2C_ISSUE_STOP(i2c);  
               I2C_SUCCESS_HANDLER(ctrl);
           }
           break;

       // TRAP: where is TRA bit?!
       case I2C_EVENT_MASTER_BYTE_TRANSMITTED_BUT_TRA:

           I2C_ERROR_HANDLER(port, ctrl->code = ctrl->evnt);
           break;

// - -  I2C Receiving  - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

       // +++ case N == 1 (single byte) and N == 2 begin +++


       //  .icnt is == 1 here   if READ ONLY operation started,
       //        or == 1 + wcnt if RESTART-AFTER-WRITE preceeded.

       // +++ case N == 1 (single byte) and N == 2; I2C_readSR2 == 0
       // See RM0008.PDF, Doc ID 13902 Rev 14, Page 735.

       case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED_SR1: // ADDR sent, bit still set (I2C_readSR2 == 0)
       {
           I2C_PREPARE_CRITICAL_SECTION(sv);

           // To avoid multiply data receiption...
           I2C_BEGIN_CRITICAL_SECTION(sv);

           // Clear ADDR bit by reading SR2 out (SR1's been read at "switch"). 
           // This starts the receiption immediately.
           ctrl->evnt |= I2C_GET_SR2(i2c) << 16; 

           // Prepare NACK (clear ACK); if POS is set it goes to the second (==last) byte.
           // Don't worry about too late NACK setting: all IRQs are disabled!
           I2C_NACK_IT(i2c);    

           if (ctrl->icnt == 1) 
               // Prepare/issue STOP after the SINGLE byte
               I2C_ISSUE_STOP(i2c);   

           I2C_END_CRITICAL_SECTION(sv);

           // Allow SR2 readout at "switch" again to get into RxNE interrupt.
           // CONTINUE...
       }                 
       //  This fucking case is neccessary for reading two bytes @48MHz CPU
       //  while I2C_readSR2 == 0 after the address has been sent.
       //  Occured when trying to read MCP4661 (2x Reostat) out.
       //
       //  It seems that the interrupt is generated by ADDR = 1 while the bit 
       //  has not appeared in SR1 yet! At the same time the SB bit is still
       //  seen in SR1. Madness!
       // 
       case I2C_EVENT_MASTER_MODE_SELECT_SR1:  

           ctrl->readsr2 = 1; // Allow SR2 readout at "switch" again 
           break;

       // --- case N == 1 (single byte) and N == 2 end ---

       // +++ case N > 2:

       case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: // ADDR sent, bit cleared 

           // 1. ADDR has already been cleared at "switch", thus
           // 2. the receiption of the first byte must be in 
           //    progress now; 
       #if (USE_I2C_RX_DMA1)

           // Receive bytes via DMA
           I2C_START_DMA_RX(ctrl);   
       #else
           // 3. RxNE buffer interrupt is enabled.
           // So do nothing here.
       #endif

       break;

       // BTF is set:

       case I2C_EVENT_MASTER_BYTE_RECEIVED_BTF:

           switch (ctrl->icnt) {

               case 2: // N == 2 (two bytes only)

                   // Here both DR and SHIFT are full with expected 
                   // two bytes because RxNE interrupt has not been 
                   // enabled yet. NACK has been issued to the last 
                   // byte because NACK along with POS were prepared 
                   // before.
                   // I2C bus is stretched (BTF set). Issue STOP. 
                   // Read data TWICE (both from DR and SHIFT)!
                   // See Errata about disable IRQ for 
                   // [sTOP-read DATA(N-1)-read DATA(N)]

                   I2C_STOPN_2(ctrl);  // surrounded by disabled IRQs...
                   ctrl->icnt = 0;     // All data got.

                   I2C_SUCCESS_HANDLER(ctrl);
                   break;

               case 3: // N == 3 bytes left to receive

                   // Here Data(N-2) in DR and Data(N-1) in SHIFT;
                   // The bus is stretched/pending (BTF is set).

                   // Prepare NACK for Data(N) and... 
                   I2C_NACK_IT(i2c);

                   // ...read Data(N-2) out:
                   // this starts the receiption of the last byte
                   // with NACK at its end.
                   I2C_STORE(ctrl, ctrl->ptr[ctrl->indx++]);  

                   // Prepare STOP and read Data(N-1) out: 
                   // See Errata about disable IRQ for [sTOP-read DATA(N-1)]
                   I2C_STOPN_1(ctrl); // surrounded by disabled IRQs    
                   ctrl->icnt = 1;

                   // One byte left to read as soon as RxNE set 
                   // Enable RxNE interrupt again to read it out.
                   I2C_BUFIE_SET(i2c);
                   break;

               default:
                  // RxNE _AND_ BTF are set: store and check if complete.
                  // This occures also at enabled RxNE interrupt if
                  // there were no possibility to process RxNE below
                  // because of too long interrupts of higher priority.
                  // Store, check if complete:
                   I2C_STORE_NEXT(ctrl, ctrl->ptr[ctrl->indx++]); 
           }
           break;

       case I2C_EVENT_MASTER_BYTE_RECEIVED:    // RxNE is set

           if (ctrl->icnt == 3)   // 3 bytes left to receive: 

               // Here Data(N-2) in DR and the receiption of Data(N-1)
               // must/can be in progress.

               // See RM0008.PDF, Doc ID 13902 Rev 12, Page 737:
               // DON'T READ the data byte, just disable RxNE interrupt and
               // let be interrupted by BTF above (both DR and SHIFT are full)

               I2C_BUFIE_CLEAR(i2c);

           else // Store, check if complete:
               I2C_STORE_NEXT(ctrl, ctrl->ptr[ctrl->indx++]); 

           break;
           // --- case N > 2 end ---

       default: 
       //
       //  19.03.2014: At 24MHz an interrupt can occure while no status bits set yet!
       //              This case is not to consider as an error: exit the interrupt 
       //              and let it shoot again with all bits set.
       //              It seems to occure when prepared to read only 2 bytes (when
       //              ctrl->readsr2 is 0).
       //
           if (ctrl->readsr2 && ctrl->evnt) 
               I2C_ERROR_HANDLER(port, ctrl->code = ctrl->evnt);
   }
   ctrl->guard = 1;
}

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

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


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

Я бы еще вспомнил процедуру "I2C bus recovery", описанную, например, здесь:

http://www.cs.unc.edu/~stc/FAQs/Interfaces/I2C-AN10216_1.pdf

на странице 17.

Мне приходилось прибегать.

 

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


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

Если я раз в секунду читаю пару датчиков за 1 мсек, много энергии не с'экономить. Тем более питание от USB.

 

Но неожиданно оказалось что в HAL, который я не особо жаловал, есть именно то что надо: общая форма чтения/записи Н байт по адресу М.

 

HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)

 

HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)

 

 

Даже можно задать размер адреса: иногда 1 байт, иногда 2.

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


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

Я приведу кусок - обработчик прерывания. Сразу скажу, что он работает в моей, назовем это так, - экосистеме. Это макросы, различные процедуры, не приведенные здесь. Обработчик - один для нескольких I2C периферий. Поэтому используются управляющие структуры данных. Смотрите не столько на инструкции, сколько на комментарии.

 

СПАСИБО. БУДУ РАЗБИРАТЬСЯ

 

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


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

Я бы еще вспомнил процедуру "I2C bus recovery", описанную, например, здесь:

http://www.cs.unc.edu/~stc/FAQs/Interfaces/I2C-AN10216_1.pdf

на странице 17.

Мне приходилось прибегать.

О, это вообще отдельная тема! EEPROMы cтрадают такой херней, что затыкаются и держат SDA в нуле. Причем так строго, что иногда и безудержное дергание SCL не помогает. В одной ответственной схеме (охранное устройство) пришлось предусмотреть типа watchdog, который снимал питание с EEPROM, если SDA слишком долго висит в нуле (схему рисовать не буду; описательно - цепочка быстро заряжается высоким SDA, медленно разряжается низким, и, долетев до низа, выключает транзистор, который и обесточивает EEPROM).

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


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

Просто "безудержное дёргание" и не должно помогать. Ведь есть такая вещь как "clock stretching". SCL надо дёргать с его учётом.

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


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

Просто "безудержное дёргание" и не должно помогать. Ведь есть такая вещь как "clock stretching". SCL надо дёргать с его учётом.

Это был оборот речи. Если периферийный I2C "застретчил" SCL и на том и застрял, то тут уже горю ничем не поможешь.

 

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


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

Это был оборот речи. Если периферийный I2C "застретчил" SCL и на том и застрял, то тут уже горю ничем не поможешь.

Я не о том. Я говорю, что если тупо подёргать ножкой: set "1", пауза, set "0", пауза, ...

То может оказаться, что для чипа памяти прошёл всего один клок, так как он всё это время держал SCL=0 выбирая данные из одной из своих ячеек.

После каждого SCL="1" ждать надо пока напряжение на линии в реале не станет "1".

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


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

О, это вообще отдельная тема! EEPROMы cтрадают такой херней, что затыкаются и держат SDA в нуле.

Это только для тех, кто не понимает, как работает автомат I2C в этом самом EEPROM и соответственно не понимают, как его вывести в исходное состояние.

А автомат там простой и тупой. Завесить его намертво нереально. Для выведения в исходное, правда, обычно приходится ручками диаграмму отрабатывать. От "безудержного-же дергания",

как и от любого мусора, можно только затыкание получить.

 

 

 

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


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

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

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

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

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

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

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

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

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

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