Библиотека TWI (I2C), DS1307, AT24C256, MPU9150 Raw, PCF8574, PCF8575, MS5803-30BA

Тема в разделе "Микроконтроллеры AVR", создана пользователем Alex19, 16 сен 2015.

  1. Alex19

    Alex19 Гуру

    Недавно мне понадобилась быстрая библиотека для работы с TWI (I2C) для Ардуины. Поэтому, не стал долго думать и реши спуститься к AVR и разобраться как все это работает.

    После непродолжительных поисков нашел восхитительную документацию и примеры по TWI на русском - http://chipenable.ru/index.php/prog...ispolzovaniya-twi-modulya-osnovy-i2c-ch1.html (написанная Павлом Бобковым, там 5 частей с примерами для DS1307, спасибо ему огромное).

    В последней статье в качестве основы был взят пример от компании Atmel - AVR315: Using TWI module as I2C master. Вот сам документ - http://www.atmel.com/Images/doc2564.pdf, переведенный на русский + сама библиотека - http://www.gaw.ru/html.cgi/txt/app/micros/avr/AVR315.htm.

    Автор немного изменил код данной библиотеки под себя, я тоже не взял, как есть, а сделал не большие изменения:) (каждый добавляет приправу по вкусу).

    Сам код библиотеки
    TWI.cpp
    Код (C++):

    #include "Arduino.h"
    #include "TWI.h"
    //*********************** Переменные, константы TWI *************************//
    // Буфер для TWI.
    volatile static uint8_t twiBuf[TWI_BUFFER_SIZE];

    // Размер получаемого/отправляемого пакета данных.
    volatile static uint8_t twiMsgSize;

    // Текущий статус TWI.
    volatile static uint8_t twiState = TWI_NO_STATE;
    //*********************** /Переменные, константы TWI *************************//

    //*****************************  TWI скорость  *******************************//
    // TWI скорость - 400kHz
    //#define TWI_SPEED                       400000UL
    // TWI скорость - 100kHz
    #define TWI_SPEED                       100000UL
    //*****************************  /TWI скорость  ******************************//

    //***************************  Приватные функции  ****************************//
    /// <summary>
    /// Ждем, пока модуль занят.
    /// </summary>
    static void TwiTransceiverBusy(void)
    {
      // Ждем пока прерывание разрешено.

      // TWCR (TWI Control Register) - Регистр управления TWCR.
      // TWIE (TWI Interrupt Enable) - разрешение прерывания TWI модуля. Когда бит TWIE и бит I регистра SREG установлены в 1 - прерывания модуля TWI разрешены. Прерывания будут вызываться при установке бита TWINT.
      while (TWCR & (1<<TWIE)){}            
    }
    //***************************  /Приватные функции  ***************************//

    //***************************  Публичные функции  ****************************//
    /// <summary>
    /// Инициализация и установка частоты SCL сигнала.
    /// </summary>
    void TwiMasterInit(void)
    {
      // Проверка объявлен ли TWI_SPEED.
      #ifndef TWI_SPEED
        #error TWI_SPEED not declared!
      #endif

      // Обнуляем статусный регистр.
      // TWSR (TWI Status Register) - Статусный регистр TWSR отражает состояние TWI модуля и двухпроводной шины, а также содержит разряды, задающие коэффициент деления частоты SCL сигнала.
      TWSR = 0;  

      // Устанавливаем скорость передачи данных.
      TwiChangeSpeed(TWI_SPEED);

      // Разрешаем работу TWI модуля.
      // TWCR (TWI Control Register) - Регистр управления TWCR.
      // TWEN (TWI Enable Bit) - бит разрешения работы TWI модуля. Когда бит TWEN устанавливается в 1, TWI модуль включается и берет на себя управление выводами SCL и SDA. Когда бит TWEN сбрасывается, TWI модуль выключается.
      TWCR = 1<<TWEN;
    }

    /// <summary>
    /// Изменение скорости TWI.
    /// </summary>
    /// <param name="twiSpeed">Скорость TWI.</param>
    void TwiChangeSpeed(uint32_t twiSpeed)
    {
      // Устанавливаем скорость передачи данных.
      // TWBR (TWI Bit Rate Register) - Регистр скорости передачи.
      TWBR = ((F_CPU / twiSpeed) - 16) / 2;  
    }

    /// <summary>
    /// Получить статус TWI модуля.
    /// </summary>
    /// <returns>Статус TWI модуля.</returns>
    uint8_t TwiGetState(void)
    {
      // Ждем, пока модуль занят.
      TwiTransceiverBusy();

      // Возвращаем статус TWI модуля.
      return twiState;                  
    }

    /// <summary>
    /// Получить статус, работа с TWI завершена.
    /// </summary>
    /// <returns>Работа с TWI завершена.</returns>
    uint8_t TwiGetFinishStep(void)
    {
      // TWCR (TWI Control Register) - Регистр управления TWCR.
      // TWIE (TWI Interrupt Enable) - разрешение прерывания TWI модуля. Когда бит TWIE и бит I регистра SREG установлены в 1 - прерывания модуля TWI разрешены. Прерывания будут вызываться при установке бита TWINT.
      return (!(TWCR & (1<<TWIE)));
    }

    /// <summary>
    /// Передать данные.
    /// </summary>
    /// <param name="msg">Массив данных для отправки.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiSendData(uint8_t *msg, uint8_t msgSize)
    {
      // Ждем, когда TWI модуль освободится.
      TwiTransceiverBusy();

      // Сохраняем кол. байт для передачи.
      twiMsgSize = msgSize;

      // Сохраняем первый байт сообщения.
      twiBuf[0]  = msg[0];        

      // Если первый байт типа SLA+W.
      if (!(msg[0] & (1<<TWI_READ_BIT)))
      {
        // Сохраняем остальную часть сообщения.
        for (uint8_t i = --msgSize; i > 0; i--)
        {    
          twiBuf[i] = msg[i];
        }
      }

      // Обнуляем переменную, которая хранит статус операции.                
      twiState = TWI_NO_STATE;

      // Разрешаем прерывание и формируем состояние старт.

      // TWCR (TWI Control Register) - Регистр управления TWCR.
      // TWEN (TWI Enable Bit) - бит разрешения работы TWI модуля. Когда бит TWEN устанавливается в 1, TWI модуль включается и берет на себя управление выводами SCL и SDA. Когда бит TWEN сбрасывается, TWI модуль выключается.
      // TWIE (TWI Interrupt Enable) - разрешение прерывания TWI модуля. Когда бит TWIE и бит I регистра SREG установлены в 1 - прерывания модуля TWI разрешены. Прерывания будут вызываться при установке бита TWINT.
      // TWINT(TWI Interrupt Flag) - флаг прерывания TWI модуля. Этот бит устанавливается аппаратно, когда TWI модуль завершает текущую операцию (формирование состояния СТАРТ, передачи адресного пакета и так далее).
      // Бит TWINT очищается программно, записью единицы. При выполнении обработчика прерывания этот бит не сбрасывается аппаратно, как в других модулях.
      // TWSTA (TWI START Condition Bit) - флаг состояния СТАРТ. Когда этот бит устанавливается в 1, TWI модуль проверяет не занята ли шина и формирует состояние СТАРТ. Если шина занята, он будет ожидать появления на ней состояния СТОП и после этого выдаст состояние СТАРТ. Бит TWSTA должен быть очищен программно, когда состояние СТАРТ передано.
      TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);                        
    }

    /// <summary>
    /// Передать данные. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="msg">Массив данных для отправки.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiSendDataOnInit(uint8_t* msg, uint8_t msgSize)
    {
      // Передаем данные.
      TwiSendData(msg, msgSize);

      // Ждем, когда TWI модуль освободится.
      TwiTransceiverBusy();
    }

    /// <summary>
    /// Устанавливаем начальный регистр.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="beginReg">Номер начального регистра.</param>
    void TwiSetBeginReg(uint8_t address, uint8_t beginReg)
    {
      // Объявляем массив для отправки.
      uint8_t msgBuf[2];

      // Адресный пакет.
      msgBuf[0] = TWI_GET_WRITE_BYTE(address);

      // Адрес регистра.
      msgBuf[1] = beginReg;

      // Передаем данные.
      TwiSendData(msgBuf, 2);
    }

    /// <summary>
    /// Устанавливаем начальный регистр. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="beginReg">Номер начального регистра.</param>
    void TwiSetBeginRegOnInit(uint8_t address, uint8_t beginReg)
    {
      // Устанавливаем начальный регистр.
      TwiSetBeginReg(address, beginReg);

      // Ждем, когда TWI модуль освободится.
      TwiTransceiverBusy();
    }

    /// <summary>
    /// Отправляем запрос на чтение.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiReadBytes(uint8_t address, uint8_t msgSize)
    {
      // Объявляем массив для отправки.
      uint8_t msgBuf[2];

      // Адресный пакет.
      msgBuf[0] = TWI_GET_READ_BYTE(address);

      // Передаем данные.
      TwiSendData(msgBuf, msgSize);
    }

    /// <summary>
    /// Отправляем запрос на чтение. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiReadBytesOnInit(uint8_t address, uint8_t msgSize)
    {
      // Отправляем запрос на чтение.
      TwiReadBytes(address, msgSize);

      // Ждем, когда TWI модуль освободится.
      TwiTransceiverBusy();
    }

    /// <summary>
    /// Читаем значение регистра по адресу и номеру регистра. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="numberReg">Номер регистра.</param>
    uint8_t TwiGetByteOnInit(uint8_t address, uint8_t numberReg)
    {
      // Устанавливаем начальный регистр.
      TwiSetBeginRegOnInit(address, numberReg);

      // Отправляем запрос на чтение.
      TwiReadBytesOnInit(address, 2);

      // Объявляем массив для получения данных.
      uint8_t msgBuf[1];

      // Переписываем данные буфера драйвера в свой буфер.
      TwiGetData(msgBuf, 1);

      return msgBuf[0];
    }

    /// <summary>
    /// Получить принятые данные. Переписываем данные буфера драйвера в свой буфер.
    /// </summary>
    /// <param name="msg">Массив в который необходимо переписать данные.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    /// <returns>Статус TWI модуля.</returns>
    uint8_t TwiGetData(uint8_t *msg, uint8_t msgSize)
    {
      // Ждем, когда TWI модуль освободится.
      TwiTransceiverBusy();

      // Сохраняем кол. байт для передачи.
      msgSize = msgSize+1;

      // Если сообщение успешно принято.
      if(twiState == TWI_SUCCESS)
      {
        // Записываем данные в массив msg.
        for(uint8_t i = (msgSize-1); i > 0; i--)
        {  
           msg[i-1] = twiBuf[i];
         }
      }

      return twiState;                              
    }
    //***************************  /Публичные функции  ***************************//
     
     
    Последнее редактирование: 19 сен 2015
    Igor68, rico, AlexVS и ещё 1-му нравится это.
  2. Alex19

    Alex19 Гуру

    Продолжение
    Код (C++):

    //*******************************  Прерывания  *******************************//
    /// <summary>
    /// Обработчик прерывания TWI модуля.
    /// </summary>
    ISR(TWI_vect)
    {
      // TWCR (TWI Control Register) - Регистр управления TWCR.
      // TWEN (TWI Enable Bit) - бит разрешения работы TWI модуля. Когда бит TWEN устанавливается в 1, TWI модуль включается и берет на себя управление выводами SCL и SDA. Когда бит TWEN сбрасывается, TWI модуль выключается.
      // TWIE (TWI Interrupt Enable) - разрешение прерывания TWI модуля. Когда бит TWIE и бит I регистра SREG установлены в 1 - прерывания модуля TWI разрешены. Прерывания будут вызываться при установке бита TWINT.
      // TWINT(TWI Interrupt Flag) - флаг прерывания TWI модуля. Этот бит устанавливается аппаратно, когда TWI модуль завершает текущую операцию (формирование состояния СТАРТ, передачи адресного пакета и так далее).
      // Бит TWINT очищается программно, записью единицы. При выполнении обработчика прерывания этот бит не сбрасывается аппаратно, как в других модулях.
      // TWDR (TWI Data Register) - Регистр данных.  
      // TWSTO (TWI STOP Condition Bit) - флаг состояния СТОП. Когда этот бит устанавливается в 1 в режиме ведущего, TWI модуль выдает на шину состояние СТОП и сбрасывает этот бит. В режиме ведомого установка этого бита может использоваться для восстановления после ошибки. При этом состояние СТОП не формируется, но TWI модуль возвращается к начальному не адресованному состоянию.
      // TWEA (TWI Enable Acknowledge Bit) - разрешение бита подтверждения. Если бит TWEA установлен в 1, TWI модуль формирует сигнал подтверждения (ACK), когда это требуется. А требуется это в трех случаях: ведущее или ведомое устройство получило байт данных, ведомое устройство получило общий вызов, ведомое устройство получило свой адрес.
      // TWSTA (TWI START Condition Bit) - флаг состояния СТАРТ. Когда этот бит устанавливается в 1, TWI модуль проверяет не занята ли шина и формирует состояние СТАРТ. Если шина занята, он будет ожидать появления на ней состояния СТОП и после этого выдаст состояние СТАРТ. Бит TWSTA должен быть очищен программно, когда состояние СТАРТ передано.
      // TWWC (TWI Write Collision Flag) - флаг конфликта записи. Этот флаг устанавливается аппаратно, когда выполняется запись в регистр данных (TWDR) при низком значении бита TWINT. То есть когда TWI модуль уже выполняет какие-то операции.
      // Флаг TWWC сбрасывается аппаратно, когда запись в регистр данных выполняется при установленном флаге прерывания TWINT.
                 
      // Текущий индекс буфера twiBuf.
      static uint8_t twiBufIndex;

      switch (TWSR)
      {
        // Состояние START сформировано.
      case TWI_START:  
        // Состояние повторный START сформировано.  
      case TWI_REP_START:                    
        twiBufIndex = 0;
        // Был передан пакет SLA+W и получено подтверждение.
      case TWI_MTX_ADR_ACK:
        // Был передан байт данных и получено подтверждение.  
      case TWI_MTX_DATA_ACK:          
        if (twiBufIndex < twiMsgSize)
        {
          // Загружаем в регистр данных следующий байт.
          TWDR = twiBuf[twiBufIndex];                

          // Сбрасываем флаг TWINT.
          TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
          twiBufIndex++;
        }
        else
        {
          twiState = TWI_SUCCESS;

          // Формируем состояние СТОП, сбрасываем флаг, запрещаем прерывания.
          TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO)|(0<<TWIE);
        }
        break;
        // Байт данных принят и передано подтверждение.
      case TWI_MRX_DATA_ACK:        
        twiBuf[twiBufIndex] = TWDR;
        twiBufIndex++;
        // Был передан пакет SLA+R и получено подтвеждение.
      case TWI_MRX_ADR_ACK:          
        if (twiBufIndex < (twiMsgSize-1))
        {
          // Если это не предпоследний принятый байт, формируем подтверждение.
          TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA);                            
        }
        else
        {
          // Если приняли предпоследний байт, подтверждение не формируем.
          TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);          
        }  
        break;
        // Был принят байт данных без подтверждения.
      case TWI_MRX_DATA_NACK:          
        twiBuf[twiBufIndex] = TWDR;
        twiState = TWI_SUCCESS;
        // Формируем состояние стоп.
        TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
        break;
        // Был потерян приоритет.
      case TWI_ARB_LOST:      
        // Сбрасываем флаг TWINT, формируем повторный СТАРТ.
        TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
        break;
        // Был передан пает SLA+W и не получено подтверждение.
      case TWI_MTX_ADR_NACK:
        // Был передан пакет SLA+R и не получено подтверждение.  
      case TWI_MRX_ADR_NACK:
        // Был передан байт данных и не получено подтверждение.  
      case TWI_MTX_DATA_NACK:
        // Ошибка на шине из-за некоректных состояний СТАРТ или СТОП.
      case TWI_BUS_ERROR:      
      default:  
        //twiErrorsCount++;
        //twiLastStateErrors = twiState = stat;

        twiState = TWSR;

        // Запретить прерывание.    
        TWCR = (1<<TWEN)|(0<<TWIE)|(0<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
        break;
      }
    }
    //*******************************  /Прерывания  ******************************//
     
     
    Последнее редактирование: 17 сен 2015
    Igor68 и ИгорьК нравится это.
  3. Alex19

    Alex19 Гуру

    TWI.h
    Код (C++):
    #ifndef TWI_H
    #define TWI_H

    //************************ Переменные, константы TWI *************************//
    // Если не определена тактовая частота, определяем ее здесь.
    #ifndef F_CPU
       #define F_CPU  16000000UL
    #endif

    // Размер буфера TWI модуля.
    #define TWI_BUFFER_SIZE  32

    // Позиция R/W бита в адресном пакете.
    #define TWI_READ_BIT     0
    //************************ /Переменные, константы TWI ************************//

    //********************** Статусные коды TWI модуля ***************************//
    // Общие статусные коды.

    // Состояние START сформировано.
    #define TWI_START                  0x08
    // Состояние повторный START сформировано.
    #define TWI_REP_START              0x10
    // Был потерян приоритет.
    #define TWI_ARB_LOST               0x38

    // Статусные коды ведущего передатчика.

    // Был передан пакет SLA+W и получено подтверждение.
    #define TWI_MTX_ADR_ACK            0x18
    // Был передан пакет SLA+W и не получено подтверждение.
    #define TWI_MTX_ADR_NACK           0x20
    // Был передан байт данных и получено подтверждение.
    #define TWI_MTX_DATA_ACK           0x28
    // Был передан байт данных и не получено подтверждение.
    #define TWI_MTX_DATA_NACK          0x30

    // Статусные коды ведущего приемника.

    // Был передан пакет SLA+R и получено подтвеждение.
    #define TWI_MRX_ADR_ACK            0x40
    // Был передан пакет SLA+R и не получено подтверждение.
    #define TWI_MRX_ADR_NACK           0x48
    // Байт данных принят и передано подтверждение.
    #define TWI_MRX_DATA_ACK           0x50
    // Был принят байт данных без подтверждения.
    #define TWI_MRX_DATA_NACK          0x58

    // Другие статусные коды.

    // Неопределенное состояние (TWINT = "0").
    #define TWI_NO_STATE               0xF8
    // Ошибка на шине из-за некоректных состояний СТАРТ или СТОП.
    #define TWI_BUS_ERROR              0x00

    // Пользовательские коды.

    // Успешное завершение.
    #define TWI_SUCCESS                0xff

    //********************** /Статусные коды TWI модуля **************************//

    //*********************** Функции получения байта адреса *********************//
    #define TWI_GET_READ_BYTE(ADDRESS)  ((ADDRESS)<<1)|1;
    #define TWI_GET_WRITE_BYTE(ADDRESS) ((ADDRESS)<<1)|0;
    //*********************** /Функции получения байта адреса ********************//

    //***************************  Публичные функции  ****************************//
    /// <summary>
    /// Инициализация и установка частоты SCL сигнала.
    /// </summary>
    void TwiMasterInit(void);

    /// <summary>
    /// Изменение скорости TWI.
    /// </summary>
    /// <param name="twiSpeed">Скорость TWI.</param>
    void TwiChangeSpeed(uint32_t twiSpeed);

    /// <summary>
    /// Получить статус TWI модуля.
    /// </summary>
    /// <returns>Статус TWI модуля.</returns>
    uint8_t TwiGetState(void);

    /// <summary>
    /// Получить статус, работа с TWI завершена.
    /// </summary>
    /// <returns>Работа с TWI завершена..</returns>
    uint8_t TwiGetFinishStep(void);

    /// <summary>
    /// Передать данные.
    /// </summary>
    /// <param name="msg">Массив данных для отправки.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiSendData(uint8_t *msg, uint8_t msgSize);

    /// <summary>
    /// Передать данные. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="msg">Массив данных для отправки.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiSendDataOnInit(uint8_t* msg, uint8_t msgSize);

    /// <summary>
    /// Устанавливаем начальный регистр.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="beginReg">Номер начального регистра.</param>
    void TwiSetBeginReg(uint8_t address, uint8_t beginReg);

    /// <summary>
    /// Устанавливаем начальный регистр. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="beginReg">Номер начального регистра.</param>
    void TwiSetBeginRegOnInit(uint8_t address, uint8_t beginReg);

    /// <summary>
    /// Отправляем запрос на чтение.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    void TwiReadBytes(uint8_t address, uint8_t msgSize);

    /// <summary>
    /// Отправляем запрос на чтение. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="msg">Массив данных для отправки.</param>
    void TwiReadBytesOnInit(uint8_t address, uint8_t msgSize);

    /// <summary>
    /// Читаем значение регистра по адресу и номеру регистра. С дополнительным ожиданием завершения операции.
    /// </summary>
    /// <param name="address">Адрес устройства.</param>
    /// <param name="numberReg">Номер регистра.</param>
    uint8_t TwiGetByteOnInit(uint8_t address, uint8_t numberReg);

    /// <summary>
    /// Получить принятые данные. Переписываем данные буфера драйвера в свой буфер.
    /// </summary>
    /// <param name="msg">Массив в который необходимо переписать данные.</param>
    /// <param name="msgSize">Размер массива msg.</param>
    /// <returns>Статус TWI модуля.</returns>
    uint8_t TwiGetData(uint8_t *msg, uint8_t msgSize);
    //***************************  /Публичные функции  ***************************//

    #endif //TWIM_H
    Разумеется, я сделаю полное описание работы с библиотекой.
    А так же о ее плюсах и минусах, а так же чем ее можно дополнить, включая примеры анонсированные в заголовке. Но из-за ограничений на размер постов, придется написать несколько.

    UPD. Написать код, намного, быстрее чем его описать. И объяснить, почему выбор пал на, то или иное решение. Поэтому постараюсь написать сегодня по максимуму, если, что-то не успею продолжу завтра.
     
    Последнее редактирование: 17 сен 2015
    Igor68, rico, Tomasina и ещё 1-му нравится это.
  4. ИгорьК

    ИгорьК Гуру

    Слава Труду!
     
  5. Alex19

    Alex19 Гуру

    Начнем с описания.

    Данная библиотека проверена на ATmega328P, ATmega32U4, ATmega2560. То есть практически на всех AVR Ардуинах. Стремился сохранить скорость и сильно не раздувать код.

    Все библиотеки работаю, по одному принципу, разнятся подходы в работе и мелочи. TWI работает на прерываниях, используется внутренний буфер для отправки и получения данных. А после переписывается в буфер, переменную программы.

    Подготовили данные, включили прерывание, отправили, выключили прерывания, забрали данные и так по кругу. Определяем занят ли TWI модуль, по включенным прерываниям.

    TWI_BUFFER_SIZE - Размер внутреннего буфера.
    twiBuf[TWI_BUFFER_SIZE] - Внутренний буфер.

    // Размер получаемого/отправляемого пакета данных.
    twiMsgSize - используется в прерывании, для определения, сколько надо отправить/получить байт.

    Сами методы.
    TwiTransceiverBusy - Ждет завершения работы TWI (а это определяет по выключенному прерыванию TWI) в цикле.

    TwiMasterInit - Инициализация и установка частоты SCL сигнала.
    Значение берется из константы TWI_SPEED, возможные значения 100000U или 400000U (100 и 400Кгц).

    Обратите внимание, что в отличии от AVR315, где скорость и делители задаются жестко, в примере от Павла. Есть возможность задавать весь диапазон скоростей. Плюс у него идет проверка задания скорости, но в моем приложениях это не требуется.
    Код (C++):

    /*предделители для установки скорости обмена twi модуля*/
    uint8_t pre[4] = {2, 8, 32, 128};

    /****************************************************************************
    Инициализация и установка частоты SCL сигнала
    ****************************************************************************/

    uint8_t TWI_MasterInit(uint16_t fr)
    {
      uint8_t i;
      uint16_t twbrValue;

      for(i = 0; i<4; i++){
        twbrValue = ((((F_CPU)/1000UL)/fr)-16)/pre[i];
        if ((twbrValue > 0)&& (twbrValue < 256)){
           TWBR = (uint8_t)twbrValue;
           TWSR = i;
           TWDR = 0xFF;
           TWCR = (1<<TWEN);
           return TWI_SUCCESS;
        }
      }
      return 0;
    }
    Эту библиотеку так же можно доработать, чтобы получить весь диапазон, если требуется. Если кто-то будет это делать не забудьте, добавить маску в прерывании.
    Код (C++):
    // Маска для статусного регистра TWSR.
    #define TWSR_MASK                       0xfc  
    Код (C++):
    /// <summary>
    /// Обработчик прерывания TWI модуля.
    /// </summary>
    ISR(TWI_vect)
    {
      // Текущий индекс буфера twiBuf.
      static uint8_t twiBufIndex;
      uint8_t stat = TWSR & TWSR_MASK;

      switch (stat)
      {
    Она нужна, для того, чтобы убрать младшие регистры TWSR. Которые влияют на предделитель.

    TwiChangeSpeed - Изменение скорости (или 100, или 400Кгц). Порой в проекте участвуют разные модули и приходится переключать частоту во время работы программы. Это моя функция, видимо потребовалось, только мне.

    TwiGetState - Получить статус модуля. Ждет завершения работы TWI (а это определяет по выключенному прерыванию TWI) и возвращает статус. Она общая, у всех решений.
    Список возвращаемых значений, можно посмотреть в файле TWI.h в блоке - Статусные коды TWI модуля.

    TwiGetFinishStep - Возвращает 1 или 0, в зависимости от того, включено прерывание или нет. Потребовалось мне.

    TwiSendData - Передаем данные.
    Данная функция, лишь в мелочах отличается от Оригинала и решения от Павла. Изменен цикл for, вместо i++ у меня i--.

    Получает массив uint8_t, следующей формы.
    1. Адресный пакет (Адрес + W\R бит).
    2. Данные для записи.

    Если происходит, чтение, то отправляется только 1 элемент массива.

    Второй параметр это размер массива, если записываем данные, то размер данных которое надо записать + адресный пакет. Если читаем, то сколько надо прочесть + адресный пакет.

    TwiSendDataOnInit - Передаем данные. Данная функция это функция TwiSendData + TwiTransceiverBusy.
    Она потребовалась для работы модуля в setup, где приходится ждать завершения работы модуля. Конечно, можно было использовать delay, но какой, для каждого модуля пришлось бы подбирать свой или ставить с запасом.

    TwiSetBeginReg - Установка начального регистра для чтения. Данная функция, просто облегчает работу с библиотекой. Потребовалось мне.

    TwiSendDataOnInit - Установка начального регистра для чтения. Данная функция, просто облегчает работу с библиотекой. Она потребовалась для работы модуля в setup, где приходится ждать завершения работы модуля.

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

    Первый параметр адрес, кол-во байт которые необходимо прочесть + адресный пакет.

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

    TwiGetByteOnInit - Читаем значение 1 регистра по адресу и номеру регистра. Она потребовалась для работы модуля в setup, где приходится ждать завершения работы модуля.

    TwiGetData - Переписываем данные буфера драйвера в свой буфер/переменную. Передаем массив uint8_t и кол-во байт которое надо переписать. Данная функция, лишь в мелочах отличается от Оригинала и решения от Павла. Изменен цикл for, вместо i++ у меня i--. И в моей, передается именно кол-во данных которое надо переписать.

    ISR(TWI_vect) - Прерывание.

    Так, вроде описал, кстати там, не плохое описание в коде, но если будут вопросы, пишите. Остались примеры. И описание проблем, которые присущи всем библиотекам, которые я перечислил в 1 посте. И методы борьбы с ними, какие-то защиты, я реализую, какие-то, мне не нужны, но я постараюсь их описать.
     
    Последнее редактирование: 17 сен 2015
    Igor68 нравится это.
  6. Alex19

    Alex19 Гуру

    Пример DS1307
    Код (C++):

    #include "Arduino.h"
    #include "TWI.h"

    // Переменные для опроса часов 1 раз в сек.
    uint32_t interval = 1000;
    uint32_t previousMillis = 0;

    // Переменные для определения максимального времени цикла.
    uint32_t timeCycleBegin = 0;
    uint32_t timeCycle = 0;
    uint32_t timeCycleMax = 0;

    // Адрес DS1307.
    #define DS1307_ADDRESS    104

    // Структура для хранения данных DS1307.
    // twimStep используется для хранения последней операции.
    typedef struct
    {
      uint8_t sec, min, hour, day, date, month, year, twimStep;
    } RTC;

    // Переменная структуры, которая хранит данные и последнюю операцию с DS1307.
    RTC Rtc;

    void setup()
    {
      Serial.begin(115200);
      // Инициализация I2C.
      TwiMasterInit();
      // Устанавливаем начальное значение часов.
      uint8_t rtcBuf[9];
      // Адресный пакет.
      rtcBuf[0] = TWI_GET_WRITE_BYTE(DS1307_ADDRESS);  
      // Адрес регистра.
      rtcBuf[1] = 0;
      // Значение секунд.  
      rtcBuf[2] = Uint8ToBcd(55);  
      // Значение минут.
      rtcBuf[3] = Uint8ToBcd(58);  
      // Значение часов.  
      rtcBuf[4] = Uint8ToBcd(2);      
      // Значение дней недели.  
      rtcBuf[5] = Uint8ToBcd(3);  
      // Значение дней.  
      rtcBuf[6] = Uint8ToBcd(16);  
      // Значения месяцев.  
      rtcBuf[7] = Uint8ToBcd(9);      
      // Значения годов.  
      rtcBuf[8] = Uint8ToBcd(15);          
      // Записываем новые значения.
      TwiSendDataOnInit(rtcBuf, 9);
    }

    void loop()
    {
      // Начальное время цикла.
      timeCycleBegin = micros();
      // Проверяем часы, каждую секунду.
      uint32_t currentMillis = millis();
      if(currentMillis - previousMillis > interval)
      {
        previousMillis = currentMillis;
       
        // Если обращения к DS1307 не было.
        if (!Rtc.twimStep)
        {
          // Устанавливаем начальный адрес для дальнейшего чтения.
          TwiSetBeginReg(DS1307_ADDRESS, (uint8_t)0);
         
          // Увеличиваем номер операции.
          Rtc.twimStep++;
        }
      }
      // Если работа модуля завершена и номер операции с DS1307 равен 1.
      if (TwiGetFinishStep() && Rtc.twimStep == 1)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Считываем время с DS1307.
          TwiReadBytes(DS1307_ADDRESS, 8);
         
          // Увеличиваем номер операции.
          Rtc.twimStep++;
        }
        else
        {
          // Здесь можно обработать исключение.
         
          // Просто сбрасываю номер операции, чтобы работа с модулем DS1307 прекратилась.
          Rtc.twimStep = 0;
        }
      }
      // Если работа модуля завершена и номер операции с DS1307 равен 2.
      if (TwiGetFinishStep() && Rtc.twimStep == 2)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Переписываем данные буфера драйвера в свой буфер.
          TwiGetData((uint8_t*)&Rtc, 7);
         
          // Но в основном так.
          /*Rtc.sec  = rtcBuf[0];
          Rtc.min  = rtcBuf[1];
          Rtc.hour = rtcBuf[2];
          Rtc.day = rtcBuf[3];
          Rtc.date = rtcBuf[4];
          Rtc.month = rtcBuf[5];
          Rtc.year = rtcBuf[6];*/

         
          // Приводим значения к подобающему виду.
          Rtc.sec  = BcdToUint8(Rtc.sec);
          Rtc.min  = BcdToUint8(Rtc.min);
          Rtc.hour = BcdToUint8(Rtc.hour);
          Rtc.day = BcdToUint8(Rtc.day);
          Rtc.date = BcdToUint8(Rtc.date);
          Rtc.month = BcdToUint8(Rtc.month);
          Rtc.year = BcdToUint8(Rtc.year);
           
          Serial.print("Year 20");
          Serial.print(Rtc.year);
          Serial.print(" Month ");
          Serial.print(Rtc.month);
          Serial.print(" Date ");
          Serial.print(Rtc.date);
          Serial.print(" Day ");
          Serial.print(Rtc.day);
         
          Serial.print(" Time ");
          Serial.print(Rtc.hour);
          Serial.print(":");
          Serial.print(Rtc.min);
          Serial.print(":");
          Serial.println(Rtc.sec);
        }
       
        // Обнуляем номер последней операции с модулем.
        Rtc.twimStep = 0;
      }
      /*
      // Определяем скорость работы.
      timeCycle = GetDifferenceULong(timeCycleBegin, micros());
      // Если текущее значение больше, последнего максимального, отображаем его.
      if (timeCycle > timeCycleMax)
      {
        timeCycleMax = timeCycle;
       
        Serial.print("Max - ");
        Serial.println(timeCycleMax);
      }*/

    }

    // Переменные для определения разницы между 2 uint32
    #define unsignedLongMax 4294967295UL
    #define digitalOne 1UL

    // Функция которая возвращает разницу между 2-мя uint32.
    uint32_t GetDifferenceULong(uint32_t BeginTime, uint32_t EndTime)
    {
      // Защита от переполнения
      if (EndTime < BeginTime)
      {  
        return unsignedLongMax - BeginTime + EndTime + digitalOne;
      }
      else
      {
        return EndTime - BeginTime;
      }
    }

    // Функция конвертации Uint8 в BCD формат.
    uint8_t Uint8ToBcd(uint8_t input)
    {
        uint8_t high = 0;
       
        while (input >= 10)                
        {
            high++;
            input -= 10;
        }

        return  (high << 4) | input;    
    }

    // Функция конвертации BCD формат в Uint8.
    uint8_t BcdToUint8(uint8_t input)
    {
      uint8_t value = 0;
      value = ((input >> 4) * 10);
      value += (input&0xf);
      return value;
    }
     
    Пример AT24C256
    Код (C++):

    #include "Arduino.h"

    #include "TWI.h"

    #include <util/delay.h>

    // Адрес AT24C256.
    #define EEPROM_ADDRESS 0x50              

    // Массив для работы с TWI.
    uint8_t twiBuf[19];

    // Массив для проверки по страничной записи.
    uint8_t dmpMemory[16] =
    {
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15
    };

    void setup()
    {
      Serial.begin(115200);
      // Инициализация I2C.
      TwiMasterInit();
      #ifdef TWI_DEBUG
        //Устанавляем размер регистра адреса в байтах.
        TwiSetToLengthAddressReg(2);
      #endif
      // Записываем байт по адресу.
      TwiEepromWriteByte(EEPROM_ADDRESS, 9, (uint8_t)199);
      // Читаем байт по адресу.
      uint8_t byteRead = TwiEepromReadByte(EEPROM_ADDRESS, 9);
      Serial.println(byteRead);
      // Записываем страницу, по адресу.
      TwiEepromWritePage(EEPROM_ADDRESS, 0, dmpMemory, 16);
      // Читаем страницу, по адресу.
      TwiEepromReadPage(EEPROM_ADDRESS, 0, 16);
      for (uint8_t i=0; i < 16 ;i++)
      {
        Serial.println(twiBuf[i]);
      }
    }

    void loop()
    {
    }

    /// <summary>
    /// Записываем байт по адресу.
    /// </summary>
    /// <param name="deviceAddress">Адрес устройства.</param>
    /// <param name="addressReg">Адрес регистра.</param>
    /// <param name="data">Байт для записи.</param>
    void TwiEepromWriteByte(uint8_t deviceAddress, uint16_t addressReg, uint8_t data)
    {
      // Адресный пакет.
      twiBuf[0] = TWI_GET_WRITE_BYTE(deviceAddress);  
      // Адрес регистра.
      // AT24C256 использует 15 битную адресацию к регистрам.
      twiBuf[1]  = addressReg >> 8;  
      twiBuf[2]  = addressReg & 0xFF;
      // Байт, который необходимо записать.
      twiBuf[3] = data;
      // Записываем новые значения.
      TwiSendDataOnInit(twiBuf, 4);
      // Ждем, пока будет записана ячейка данных.
      _delay_ms(10);
    }

    /// <summary>
    /// Читаем байт по адресу.
    /// </summary>
    /// <param name="deviceAddress">Адрес устройства.</param>
    /// <param name="addressReg">Адрес регистра.</param>
    /// <returns>Возвращаем прочитанный байт.</returns>
    uint8_t TwiEepromReadByte(uint8_t deviceAddress, uint16_t addressReg)
    {
      // Адресный пакет.
      twiBuf[0] = TWI_GET_WRITE_BYTE(deviceAddress);
      // Адрес регистра.
      // AT24C256 использует 15 битную адресацию к регистрам.
      twiBuf[1]  = addressReg >> 8;  
      twiBuf[2]  = addressReg & 0xFF;                    
      // Отправляем начальный адрес для дальнейшего чтения.
      TwiSendDataOnInit(twiBuf, 3);
      // Отправляем запрос на чтение.
      TwiReadBytesOnInit(deviceAddress, 2);
      // Переписываем данные буфера драйвера в свой буфер.
      TwiGetData(twiBuf, 1);
      return twiBuf[0];
    }

    /// <summary>
    /// Записываем страницу, по адресу.
    /// </summary>
    /// <param name="deviceAddress">Адрес устройства.</param>
    /// <param name="addressReg">Адрес начального регистра.</param>
    /// <param name="data">Массив байтов для записи.</param>
    /// <param name="length">Размер массива байтов для записи.</param>
    void TwiEepromWritePage(uint8_t deviceAddress, uint16_t addressReg, uint8_t* data, uint8_t length)
    {
      // Адресный пакет.
      twiBuf[0] = TWI_GET_WRITE_BYTE(deviceAddress);  
      // Адрес регистра.
      // AT24C256 использует 15 битную адресацию к регистрам.
      twiBuf[1]  = addressReg >> 8;  
      twiBuf[2]  = addressReg & 0xFF;
      // Сохраняем данные из массива.
      for (uint8_t i = 0; i < length; i++)
      {
        twiBuf[i+3] = data[i];
      }
      // Записываем новые значения.
      TwiSendDataOnInit(twiBuf, length+3);
      // Ждем, пока будут записаны ячейки данных.
      _delay_ms(10);
    }

    /// <summary>
    /// Читаем страницу, по адресу.
    /// </summary>
    /// <param name="deviceAddress">Адрес устройства.</param>
    /// <param name="addressReg">Адрес начального регистра.</param>
    /// <param name="length">Размер массива байтов для чтения.</param>
    void TwiEepromReadPage(uint8_t deviceAddress, uint16_t addressReg, uint8_t length)
    {
      // Адресный пакет.
      twiBuf[0] = TWI_GET_WRITE_BYTE(deviceAddress);
      // Адрес регистра.
      // AT24C256 использует 15 битную адресацию к регистрам.
      twiBuf[1]  = addressReg >> 8;  
      twiBuf[2]  = addressReg & 0xFF;                    
      // Отправляем начальный адрес для дальнейшего чтения.
      TwiSendDataOnInit(twiBuf, 3);
      // Отправляем запрос на чтение.
      TwiReadBytesOnInit(deviceAddress, length+1);
      // Переписываем данные буфера драйвера в свой буфер.
      TwiGetData(twiBuf, length);
    }
     
    Силы покидают, даже не смог написать нормальные коментарии к примерам. Завтра их подправлю, чтобы было понятно и продолжим.
     
    Последнее редактирование: 19 сен 2015
    Igor68 и ИгорьК нравится это.
  7. Alex19

    Alex19 Гуру

    Пример MPU9150, весь пример очень большой, будет в прищепке. Тут только 2 функции.
    Файл .ino
    Код (C++):

    #include "Arduino.h"

    #include "TWI.h"

    #include "MPU9150.h"

    #include <util/delay.h>

    // Переменные для хранения полученных значений.
    // Акселерометр.
    int16_t ax, ay, az;
    // Гироскоп.
    int16_t gx, gy, gz;
    // Компас.
    int16_t mx, my, mz;

    void setup()
    {
      Serial.begin(9600);
      while (!Serial)
      {
        ; // Wait for serial port to connect. Needed for Leonardo only
      }
      // Иногда для Leonardo, нужно доп. время, чтобы работать в setup.
      _delay_ms(1000);
      // Инициализация I2C.
      TwiMasterInit();
      // Сообщение - инициализация I2C.
      Serial.println("Initializing I2C devices...");
      // Инициализация модуля Mpu9150.
      Mpu9150Init();
    }

    void loop()
    {
      // Читаем сырые данные с датчиков.
      GetMotion(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);

      // Отображаем значения с датчиков.
      Serial.print("a/g/m:\t");
      Serial.print(ax);
      Serial.print("\t");
      Serial.print(ay);
      Serial.print("\t");
      Serial.print(az);
      Serial.print("\t");
      Serial.print(gx);
      Serial.print("\t");
      Serial.print(gy);
      Serial.print("\t");
      Serial.print(gz);
      Serial.print("\t");
      Serial.print(mx);
      Serial.print("\t");
      Serial.print(my);
      Serial.print("\t");
      Serial.println(mz);
    }
     
    2 Функции из MPU9150.cpp
    Код (C++):

    /// <summary>
    /// Иницилизация Mpu9150.
    /// </summary>
    void Mpu9150Init()
    {
      // Сброс всего устройства.
      TwiWriteByteOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_PWR_MGMT_1, 0x80);
      // Ждем  сброса MPU9150.
      _delay_ms(50);
      // Настраиваем тактирование MPU6050. На PLL with X axis gyroscope reference.
      TwiWriteBitsAfterReadOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_PWR_MGMT_1, MPU9150_PWR1_CLKSEL_BIT, MPU9150_PWR1_CLKSEL_LENGTH, MPU9150_CLOCK_PLL_XGYRO);
      // Настраиваем гироскоп MPU6050. Максимальная шкала на ± 250 °/s.
      TwiWriteBitsAfterReadOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_GYRO_CONFIG, MPU9150_GCONFIG_FS_SEL_BIT, MPU9150_GCONFIG_FS_SEL_LENGTH, MPU9150_GYRO_FS_250);
      // Настраиваем акселерометр MPU6050. Максимальная шкала на 2g.
      TwiWriteBitsAfterReadOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_ACCEL_CONFIG, MPU9150_ACONFIG_AFS_SEL_BIT, MPU9150_ACONFIG_AFS_SEL_LENGTH, MPU9150_ACCEL_FS_2);
      // Отключаем спящий режим.
      TwiWriteBitAfterReadOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_PWR_MGMT_1, MPU9150_PWR1_SLEEP_BIT, 0);
      // Включаем прямой доступ к TWI Slaves.
      TwiWriteByteOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_INT_PIN_CFG, 0x02);
      // Проверка соединения.
      Serial.println("Testing device connections...");
      uint8_t addTwiDevice = TwiGetByteOnInit(MPU9150_DEFAULT_ADDRESS, MPU9150_RA_WHO_AM_I);
      Serial.println(addTwiDevice == 0x68 ? "MPU6050 connection successful." : "MPU6050 connection failed.");
      addTwiDevice = TwiGetByteOnInit(MPU9150_RA_MAG_ADDRESS, MPU9150_RA_MAG_WIA);
      Serial.println(addTwiDevice == 0x48 ? "AK8975C connection successful." : "AK8975C connection failed.");
    }

    /// <summary>
    /// Получаем сырые данные с модуля MPU9150.
    /// </summary>
    /// <param name="ax">Акселерометр X.</param>
    /// <param name="ay">Акселерометр Y.</param>
    /// <param name="az">Акселерометр Z.</param>
    /// <param name="gx">Гироскоп X.</param>
    /// <param name="gy">Гироскоп Y.</param>
    /// <param name="gz">Гироскоп Z.</param>
    /// <param name="mx">Компас X.</param>
    /// <param name="my">Компас Y.</param>
    /// <param name="mz">Компас Z.</param>
    void GetMotion(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz, int16_t* mx, int16_t* my, int16_t* mz)
    {
      // Номер операции с модулем.
      static uint8_t imuStep = 0;
      // Если обращения к модулю не было.
      if (!imuStep)
      {  
        // Устанавливаем начальный адрес для дальнейшего чтения.
        TwiSetBeginReg(MPU9150_DEFAULT_ADDRESS, (uint8_t)MPU9150_RA_ACCEL_XOUT_H);
     
        // Увеличиваем номер операции.
        imuStep++;
      }
      // Если работа модуля завершена и номер операции с Mpu9150 равен 1.
      if (TwiGetFinishStep() && imuStep == 1)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Считываем данные с датчиков.    
          TwiReadBytes(MPU9150_DEFAULT_ADDRESS, 15);
       
          // Увеличиваем номер операции.
          imuStep++;
        }
        else
        {
          // Здесь можно обработать исключение.
       
          // Просто сбрасываю номер операции, чтобы работа с модулем прекратилась.
          imuStep = 0;
        }
      }
      // Если работа модуля завершена и номер операции с Mpu9150 равен 2.
      if (TwiGetFinishStep() && imuStep == 2)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Переписываем данные буфера драйвера в свой буфер.
          TwiGetData(twiBuf, 14);
     
          // Приводим значения к подобающему виду.
          *ax = (((int16_t)twiBuf[0]) << 8) | twiBuf[1];
          *ay = (((int16_t)twiBuf[2]) << 8) | twiBuf[3];
          *az = (((int16_t)twiBuf[4]) << 8) | twiBuf[5];
          *gx = (((int16_t)twiBuf[8]) << 8) | twiBuf[9];
          *gy = (((int16_t)twiBuf[10]) << 8) | twiBuf[11];
          *gz = (((int16_t)twiBuf[12]) << 8) | twiBuf[13];
       
          // Увеличиваем номер операции.
          imuStep++;
        }
        else
        {
          // Здесь можно обработать исключение.
       
          // Просто сбрасываю номер операции, чтобы работа с модулем прекратилась.
          imuStep = 0;
        }
      }
      // Если работа модуля завершена и номер операции с Mpu9150 равен 3.
      if (TwiGetFinishStep() && imuStep == 3)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Адресный пакет.
          twiBuf[0] = TWI_GET_WRITE_BYTE(MPU9150_RA_MAG_ADDRESS);
       
          // Адрес регистра.
          twiBuf[1] = 0x0A;

          // Отправляем байт.
          // Отправляем команду для получение данных с компаса.    
          twiBuf[2] = 0x01;
       
          // Отправляем начальный адрес для дальнейшего чтения.
          TwiSendData(twiBuf, 3);
       
          // Увеличиваем номер операции.
          imuStep++;
        }
        else
        {
          // Здесь можно обработать исключение.
       
          // Просто сбрасываю номер операции, чтобы работа с модулем прекратилась.
          imuStep = 0;
        }
      }
      // Если работа модуля завершена и номер операции с Mpu9150 равен 4.
      if (TwiGetFinishStep() && imuStep == 4)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Устанавливаем начальный адрес для дальнейшего чтения.
          TwiSetBeginReg(MPU9150_RA_MAG_ADDRESS, (uint8_t)MPU9150_RA_MAG_XOUT_L);
       
          // Увеличиваем номер операции.
          imuStep++;
        }
        else
        {
          // Здесь можно обработать исключение.
       
          // Просто сбрасываю номер операции, чтобы работа с модулем прекратилась.
          imuStep = 0;
        }
      }
      // Если работа модуля завершена и номер операции с Mpu9150 равен 5.
      if (TwiGetFinishStep() && imuStep == 5)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Считываем данные с компаса.    
          TwiReadBytes(MPU9150_RA_MAG_ADDRESS, 7);
       
          // Увеличиваем номер операции.
          imuStep++;
        }
        else
        {
          // Здесь можно обработать исключение.
       
          // Просто сбрасываю номер операции, чтобы работа с модулем прекратилась.
          imuStep = 0;
        }
      }
      // Если работа модуля завершена и номер операции с Mpu9150 равен 6.
      if (TwiGetFinishStep() && imuStep == 6)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Переписываем данные буфера драйвера в свой буфер.
          TwiGetData(twiBuf, 6);
     
          // Приводим значения к подобающему виду.
          *mx = (((int16_t)twiBuf[1]) << 8) | twiBuf[0];
          *my = (((int16_t)twiBuf[3]) << 8) | twiBuf[2];
          *mz = (((int16_t)twiBuf[5]) << 8) | twiBuf[4];
       
          // Обнуляем номер последней операции с модулем.
          imuStep = 0;
        }
        else
        {
          // Здесь можно обработать исключение.
       
          // Просто сбрасываю номер операции, чтобы работа с модулем прекратилась.
          imuStep = 0;
        }
      }
    }
     
    Файлы надо переименовать в MPU9150.cpp и MPU9150.h

    Теперь коротко о примерах.
    Пример DS1307, как и AT24C256 можно использовать в приложениях. А вот MPU9150, не стоит, у него есть прерывание и ограничение по кол-ву запросов в минуту.

    Часы DS1307 у меня от Амперки.
    Но сам чип, не является очень точным, поэтому навряд ли я буду его использовать.

    AT24C256 у меня в виде модуля YL-90, на самом чипе не слова о AT24C256, там другая маркировка (загаданный Китай), о которой в сети почти ни чего нет. Но работает он как AT24C256:).

    MPU9150 у меня тоже китаец, модуль так и называется MPU-9150, больше ни каких надписей нет.

    Мой труд, лишь блеклая тень, по сравнению с работой Павлом Бобковым, хотелось бы сказать ему огромное спасибо. Я лишь изучил, доделал под себя и добавил примеров. Он же, восхитительно описал все от А до Я.

    Рекомендую ознакомится с его сайтом - http://chipenable.ru/, там все просто и доступно.

    Теперь об общих нюансах использования данных библиотек и этой в частности.
    1. Установка скорости работы, о ней расписал, при описании метода TwiMasterInit.
    Относится к оригиналу и моей библиотеке, но не к библиотеке Павла.
     

    Вложения:

    • MPU9150_cpp.txt
      Размер файла:
      28 КБ
      Просмотров:
      927
    • MPU9150_h.txt
      Размер файла:
      2,6 КБ
      Просмотров:
      974
    Последнее редактирование: 19 сен 2015
    Igor68, ИгорьК и Максим B нравится это.
  8. Alex19

    Alex19 Гуру

    2. Размер буфера TWI модуля.
    #define TWI_BUFFER_SIZE 32

    Размер буфера задается по принципу, 1 байт (адресный пакет), 2 байт (номер регистра, возможно 2 и 3 как в примере с AT24C256), максимальный объем записи/чтения.

    Если Вы ошибетесь в размере, то ошибка себя не проявит на этапе компиляции. Кроме не правильно работы, будут проблемы и другие проблемы. Так как запись в массив или чтение с выходом за свои границы, добавит не мало экшина в отладке.

    Решается, просто или мы внимательно указываем размерность массива (переменная TWI_BUFFER_SIZE) или вводим дополнительную проверку в функциях TwiSendData, TwiGetData.

    Примерно так.
    Код (C++):

    uint8_t TwiGetData(uint8_t *msg, uint8_t msgSize)
    {
      if (msgSize > msgSize)
      {
        twiState = Состояние, ошибка размерности массива.
      }
     
    И точно так же в TwiSendData, тогда по уму ее нужно изменить, чтобы она возвращала какой-то статус. Данные изменения вносить не планирую.

    Что планируется во второй версии (надеюсь в течении недели доберусь).
    1. Перенести блок "Переменные, константы" TWI из файла cpp, в h.
    2. Дополнить возможностью отладки.
    Добавить переменную кол-во ошибок и функцию, которая будет возвращать данную переменную.

    Примерно так
    Код (C++):
    // Кол-во ошибок TWI.
    volatile static uint16_t twiErrorsCount = 0;
    А в прерывании так
    Код (C++):
    case TWI_BUS_ERROR:
      default:
          twiErrorsCount++;
    Добавить переменную хранящую последнюю ошибку.
    Код (C++):
    // Последний статус ошибки TWI.
    volatile static uint8_t twiLastStateErrors = 0;
    В прерывании

    Код (C++):
    case TWI_BUS_ERROR:
        default:
           twiLastStateErrors = twiState = stat;
    Вынести текущий индекс буфера twiBuf, за пределы прерывания. И сделать функцию возвращения данного буфера. Чтобы видеть после какого индекса массива произошёл сбой.

    Так же добавить возможность отображение отправленных данных, как сделано в библиотеке I2Cdev. Пример отображаемой информации в Serial.
    Вся отладка будет задаваться параметром TWI_DEBUG.

    В третей версии (не буду загадывать когда она появится, много работы), добавится выбор отладки либо с помощью Serial, либо с помощью библиотеки UART которую уже давно использую в проекте.

    Так же буду добавлять примеры модулей с которыми работаю.

    Искрение надеюсь, что данный материал будет кому-то полезен. Если буду вопросы, замечания пишите.

    Всем удачи, и силы и терпения,чтобы не опускать рук, если попадается сложный проект, как мне в последнее время:( (кода не много, но проектирования, тестов на месяцы).

    UPD1.
    Разобрался наконец со вставкой кода, теперь буду везде вставлять CODE=cpp, для Си, Си++ и кода Ардуины.

    UPD2.
    В прищепке версия 1.8, в ней все что должно быть в версии 2.0, но его нужно сделать более оптимальным.

    Дебаг включается, просто раскоментарьте TWI_DEBUG, в файле TWI.h.
    Вывод в сериал мониторе для DS1307
    Для AT24C256
    Для модуля AT24C256 потребовалось учитывать 2 байтовую адресацию, поэтому для корректной работы отладки, перед работой с данным модулем необходимо вызвать данную функцию.
    Код (C++):
    #ifdef TWI_DEBUG
        //Устанавляем размер регистра адреса в байтах.
        TwiSetToLengthAddressReg(2);
      #endif
    А после завершения работы с модулем.
    Код (C++):
    #ifdef TWI_DEBUG
        //Устанавляем размер регистра адреса в байтах.
        TwiSetToLengthAddressReg(1);
      #endif
    Поддержки 3, 4 байтной адресации, не будет, пока не попадались такие датчики.

    Почему это не версия 2, потому что она не оптимальна, во первых текст прямо в коде, так же лучше вынести Serial в отдельные функции, чтобы с легкостью менять UART библиотека или Serial, порой надо SoftSerial и т.д.

    UPD.
    Мне пришлось удалить версию 1.8, из-за ошибок, в отладке. Как только поправлю, залью.
     

    Вложения:

    • TWI_cpp.txt
      Размер файла:
      33,9 КБ
      Просмотров:
      810
    • TWI_h.txt
      Размер файла:
      7,2 КБ
      Просмотров:
      672
    Последнее редактирование: 18 сен 2015
    ИгорьК нравится это.
  9. Unixon

    Unixon Оракул Модератор

    Пожелания проекту:
    1) заменить комментарии с кириллицей на двуязычную документацию в Doxygen
    2) появиться на github
     
    Igor68 нравится это.
  10. Alex19

    Alex19 Гуру

    Спасибо за интерес к моему хобби.

    Проект очень громко сказано, хотя приятно:). Это лишь 1/10 часть проекта над которым работаю, уже не один месяц. Сказывается не хватка времени, порой месяцами к нему не подхожу. И вполне возможно, он не влезет на AVR, но мне достанется опыт и я смогу поделится наработками.

    По предложениям.
    1. Коментарии пишу упрощенно в стиле C#, но задумывался по поводу Doxygen.
    Что касается анг. языка, то пока он даже не хромает, а просто без ног, но учить рано или поздно придется, пока могу предложить лишь машинный перевод.
    2. На сколько я понимаю, это какая-то вариация на тему svn в сети, обычным svn давно пользуюсь.

    Но раз появился интерес я учту эти пожелания. Когда это появиться, не рискну сказать.

    Пока планы, доделать версию до версии 2.0. Далее придется прерваться, на новый станок на производстве и новый тех. процесс. Надеюсь будет возможность урывками сделать 3 версию и учесть пожелания.

    Я ведь любитель, мне еще во многом надо разобраться самому. Те же DS18B20 (можно ли его использовать в проекте), облегченная версия библиотеки SPI, сеть на базе RS-485, но это мелочи, самое сложное MPU9150 (разобраться в ориентации) и OSD. Так, что порой буду отвлекаться на другие вещи.
     
    Последнее редактирование: 18 сен 2015
  11. Unixon

    Unixon Оракул Модератор

    По моему личному мнению, английский в технике - это как латынь в медицине и греческий алфавит в математике, своеобразный общепринятый DSL (domain specific language). Экономистов и прочих гуманитариев, пишущих формулы кириллицей, иногда хочется немного приложить лопатой :)

    Документация на русском всячески приветствуется, только если она не разбавляет собой исходник, т.к. зависимость от кодировки текста это нехорошая штука для текста программы. Далеко не все ещё пишут в UTF-8, под виндами их родная кодировка CP-1251 по прежнему весьма популярна.

    Git - это тоже система управления версиями, только она принципиально другая и намного лучше SVN. Ну а github это хостинг для проектов, использующих Git.

    p.s. А чем вас не устроила реализация I2C в Wiring?
     
    Alex19 нравится это.
  12. Alex19

    Alex19 Гуру

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

    Дельное замечание. Уже так давно привык UTF-8, что позабыл, что есть другие.

    Не доводилось сталкиваться, надо будет почитать.

    Достойная реализация, но все зависит от проекта, а так же чипа.

    Сейчас я использую Nano на Atmega328, а это всего 2KB оперативки и 32KB под программу из которых 2KB съел загрузчик. И тут невольно начнешь экономить, к примеру код для получения сырых данных для MPU9150.
    Arduino IDE 1.6.1
    Реализация I2C в Wiring - 5 874 байт, оперативной 546 байт (для глобальных переменных).
    Эта реализация - 4 440 байт, оперативной 435 байт (для глобальных переменных).

    А я пока даже не думал об оптимизации ну и конечно скорость работы, в проекте много всего, что требует оперативного ответа, тот же UART с кольцевым буфером и т.д.

    P.S. Понимаю, что для проекта был выбран не самый удачный контролер. Там куда более гармонично смотрелся Leaflabs Maple Mini.

    Или такой - http://world.taobao.com/item/45320108886.htm?spm=a312a.7700714.0.0.YmhYSS#detail, тогда можно было бы вообще не задумываться о объеме кода и т.д. Возможно, когда упрусь, буду переписывать на 1 из них:).

    UPD.
    Мне пришлось удалить версию 1.8, из-за ошибок, в отладке. Как только поправлю, залью.
     
    Последнее редактирование: 18 сен 2015
  13. Alex19

    Alex19 Гуру

    Исправил библиотеку, ошибка была смешная.

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

    Сейчас можно заняться DMP модуля MPU9150.
    Пока буду делать, за одно еще раз протестирую.

    UPD. Пока прервался, до выходных много работы. Плюс надо разобратся с еще одним модулем.
     
    Последнее редактирование: 23 сен 2015
  14. Alex19

    Alex19 Гуру

    После долгой паузы возвращаюсь к хобби.

    В прищепке, версия 2.0.
    Что в ней доработано.
    1. Работа с одно регистровыми модулями, без адресации (яркий пример PCF8574).
    2. 2 Варианта отображения отладочной информации.
    Настраивается в файле TWI.h

    Вариант 1.
    Код (C++):
    // Отладка в стиле I2CDEV
    #define TWI_DEBUG_LIKE_I2CDEV
    Результат в Serial
    Вариант 2.
    Код (C++):
    // Пошаговая отладка.
    #define TWI_DEBUG_STEP_BY_STEP
    Результат в Serial
    Иногда мне надо подробная информация иногда нет. Кроме того, вариант TWI_DEBUG_LIKE_I2CDEV был сделан для более простого сравнения с примерами I2CDEV (проще не придумывать велосипед, а просто воспользоватся открытым кодом, доработав его под себя).

    3. Возможность посылать отладочную информацию в Serial, SoftwareSerial и UART.
    По умолчанию используется Serial.

    В TwiMasterInit, есть такие строчки.
    Код (C++):
    #ifdef TWI_DEBUG_USE_SERIAL
        if (!debugTwiSerial)
        {
          // Устанавливаем Serial для отладки.
          SetSerial(Serial);
        }
    #endif
    Если нужно использовать Serial1, Serial2 и т.д., перед вызовом TwiMasterInit добавьте следующие строки

    Код (C++):
    #ifdef TWI_DEBUG_USE_SERIAL
        // Устанавливаем Serial для отладки.
        SetSerial(Serial2);
    #endif
    Только не забудьте инициализировать нужный Serial в setup (Serial2.begin(115200)).

    При необходимости использовать SoftwareSerial, объявите и передайте его все той же функции SetSerial.
    Как это выглядит
    Код (C++):
    #ifdef TWI_DEBUG_USE_SERIAL
        // Устанавливаем Serial для отладки.
        SetSerial(mySerial);
    #endif
    При необходимости работать с UART через свои библиотеки, необходимо подправить файл TWI.h. Комментируем строку
    Код (C++):
    #define TWI_DEBUG_USE_SERIAL
     
    И раскомментируем строку
    Код (C++):
    //#define TWI_DEBUG_USE_UART
     
    Затем находим данный блок

    Код (C++):
    #ifndef DEBUG_PRINT
        #ifdef TWI_DEBUG_USE_UART
          #define DEBUG_PRINT(x) PrintDebug(x)
        #else
          #define DEBUG_PRINT(x) debugTwiSerial->print(x)
        #endif
      #endif

      #ifndef DEBUG_PRINTF
        #ifdef TWI_DEBUG_USE_UART
          #define DEBUG_PRINTF(x) PrintDebug(x, y)
        #else
          #define DEBUG_PRINTF(x, y) debugTwiSerial->print(x, y)
        #endif
      #endif

      #ifndef DEBUG_PRINTLN
        #ifdef TWI_DEBUG_USE_UART
          #define DEBUG_PRINTLN(x) PrintLnDebug(x)
        #else
          #define DEBUG_PRINTLN(x) debugTwiSerial->println(x)
        #endif
      #endif

      #ifndef DEBUG_PRINTLNF
        #ifdef TWI_DEBUG_USE_UART
          #define DEBUG_PRINTLNF(x, y) PrintLnDebug(x, y)
        #else
          #define DEBUG_PRINTLNF(x, y) debugTwiSerial->println(x, y)
        #endif
      #endif
    Нам нужно подставить свои функции вместо PrintLnDebug.

    4. Добавились некоторые функции для отладки, которые работают, если TWI_DEBUG не закомментирована.
    TwiGetCountError() - Возвращает кол-во ошибок со старта приложения.
    TwiGetLastError() - Возвращает последнюю ошибку.
    TwiGetTwiBufIndex() - Возвращает последний обработанный индекс массива в прерывании.
     

    Вложения:

    • TWI_cpp.txt
      Размер файла:
      35,3 КБ
      Просмотров:
      669
    • TWI_h.txt
      Размер файла:
      8,8 КБ
      Просмотров:
      841
    Igor68, amironov73 и ИгорьК нравится это.
  15. Alex19

    Alex19 Гуру

    День добрый.
    Времени пока разобраться с документированием и залить на github. Много работы и текущих проектов, пока буду публиковать тут примеры работы с модулями на данной библиотеке.

    Потребовалось расширить кол-во цифровых портов, остановился на PCF8575, но так как ее пока нет, решил временно воспользоватся PCF8574. К тому же, не было опыта работы с модулем без адресов регистров, хотелось убедится, что библиотека работает правильно.

    Схема подключения.
    [​IMG]

    Код.
    Код (C++):
    #include "Arduino.h"
    #include "TWI.h"

    // Начальный адрес PCF8574.
    #define PCF8574_BEGIN_ADDRESS    0x20

    // Пин A0 на PCF8574 для изменения адреса.
    #define PCF8574_PIN_A0  0
    // Пин A1 на PCF8574 для изменения адреса.
    #define PCF8574_PIN_A1  0
    // Пин A2 на PCF8574 для изменения адреса.
    #define PCF8574_PIN_A2  0

    // Получаем адрес.
    #define PCF8574_ADDRESS (PCF8574_BEGIN_ADDRESS|PCF8574_PIN_A0|(PCF8574_PIN_A1<<1)|(PCF8574_PIN_A2<<2))

    // Светодиоды.
    #define LED_1 B01111111
    #define LED_2 B10111111
    #define LED_3 B11011111
    #define LED_4 B11101111

    // Кнопки.
    #define BTN_1 B00000001
    #define BTN_2 B00000010
    #define BTN_3 B00000100
    #define BTN_4 B00001000

    // Переменная хранящее максимальное значение uint32_t(unsigned long).
    #define unsignedLongMax 4294967295UL

    // Переменная хранящее 1.
    #define digitalOne 1UL

    // Структура для хранения данных PCF8574.
    // portIn - полученные данные.
    // portInOld - предыдущие полученные данные.
    // portOut - данные для отправки.
    // portInOld - предыдущие данные для отправки.
    // twimStep используется для хранения последней операции.
    typedef struct
    {
      uint8_t portIn, portInOld, portOut, portOutOld, twimStep;
    }
    PCF8574;

    // Переменная структуры для хранения данных PCF8574.
    PCF8574 Pcf8574;

    // Переменные для определения максимального времени цикла.
    uint32_t timeCycleBegin = 0;
    uint32_t timeCycle = 0;
    uint32_t timeCycleMax = 0;

    /// <summary>
    /// Предварительная настройка.
    /// </summary>
    void setup()
    {
      Serial.begin(115200);

      // Инициализация I2C.
      TwiMasterInit();

      // Устанавливаем размер регистра адреса в байтах.
      TwiSetToLengthAddressReg(0);

      // Сохраняем 1-ое значение, в Pcf8574.portInOld.
      Pcf8574.portInOld = B11111111 & B00001111;
    }

    /// <summary>
    /// Основной цикл.
    /// </summary>
    void loop()
    {
      // Начальное время цикла.
      timeCycleBegin = micros();

      // Отправляем запрос на чтение.
      if (!Pcf8574.twimStep)
      {
        // Отправляем запрос на чтение.
        TwiReadBytes(PCF8574_ADDRESS, 2);

        // Увеличиваем номер операции.
        Pcf8574.twimStep++;
      }

      // Если работа модуля завершена и номер операции равен 1.
      if (TwiGetFinishStep() && Pcf8574.twimStep == 1)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Переписываем данные буфера драйвера в свой буфер.
          TwiGetData((uint8_t*)&Pcf8574.portIn, 1);

          // Для теста.
          //Serial.print("Pcf8574.portIn - ");
          //Serial.println(Pcf8574.portIn, BIN);

          // Увеличиваем номер операции.
          Pcf8574.twimStep++;
        }
        else
        {
          // Здесь можно обработать исключение.

          // Просто сбрасываю номер операции, чтобы работа с модулем PCF8574 прекратилась.
          Pcf8574.twimStep = 0;
        }
      }

      // Данные получены, переходим к анализу.
      if (Pcf8574.twimStep == 2)
      {
        // Сравниваем с предыдущем полученным значением.
        if (Pcf8574.portInOld != (Pcf8574.portIn & B00001111))
        {
          // Сохранием новые значения.
          Pcf8574.portInOld = (Pcf8574.portIn & B00001111);

          // Устанавливаем начальное значение портов для маски.
          Pcf8574.portOut = 0xff;

          // Проверяем кнопку 1.
          if (!(Pcf8574.portIn & BTN_1))
          {
            // Кнопка нажата.
            Pcf8574.portOut &= LED_1;
          }
          else
          {
            // Кнопка не нажата.
            Pcf8574.portOut |= ~LED_1;
          }

          // Проверяем кнопку 2.
          if (!(Pcf8574.portIn & BTN_2))
          {
            // Кнопка нажата.
            Pcf8574.portOut &= LED_2;
          }
          else
          {
            // Кнопка не нажата.
            Pcf8574.portOut |= ~LED_2;
          }

          // Проверяем кнопку 3.
          if (!(Pcf8574.portIn & BTN_3))
          {
            // Кнопка нажата.
            Pcf8574.portOut &= LED_3;
          }
          else
          {
            // Кнопка не нажата.
            Pcf8574.portOut |= ~LED_3;
          }

          // Проверяем кнопку 4.
          if (!(Pcf8574.portIn & BTN_4))
          {
            // Кнопка нажата.
            Pcf8574.portOut &= LED_4;
          }
          else
          {
            // Кнопка не нажата.
            Pcf8574.portOut |= ~LED_4;
          }

          // Массив для отправки данных.
          uint8_t portBuf[2];

          // Адресный пакет.
          portBuf[0] = TWI_GET_WRITE_BYTE(PCF8574_ADDRESS);
          // Порты.
          portBuf[1] = Pcf8574.portOut;

          // Записываем новые значения.
          TwiSendData(portBuf, 2);
     
          // Сохраняем записываемые значения.
          Pcf8574.portOutOld = Pcf8574.portOut >> 4;
        }

        // Обнуляем номер последней операции с модулем.
        Pcf8574.twimStep = 0;
      }

      // Определяем скорость работы.
      timeCycle = GetDifferenceULong(timeCycleBegin, micros());
      // Если текущее значение больше, последнего максимального, отображаем его.
      if (timeCycle > timeCycleMax)
      {
        timeCycleMax = timeCycle;

        Serial.print("Max - ");
        Serial.println(timeCycleMax);
      }
    }

    /// <summary>
    /// Получение разницы между 2 uint32_t.
    /// </summary>
    uint32_t GetDifferenceULong(uint32_t BeginTime, uint32_t EndTime)
    {
      if (EndTime < BeginTime)
      {
        // Защита от переполнения
        return unsignedLongMax - BeginTime + EndTime + digitalOne;
      }
      else
      {
        return EndTime - BeginTime;
      }
    }
     
    У данного модуля, есть прерывание, но я его не использую, мне нужны только выходы. Но было интересно сделать проверку на изменение, без прерывания. Надеюсь будет кому-то полезно, удачи!

    UPD. Рано или поздно все приходят к необходимости документирования кода, Unixon подсказал отличный инструмент, с большим количеством описаний на русском.

    Вот некоторые из них - http://habrahabr.ru/post/252101/, http://habrahabr.ru/post/252443/, http://habrahabr.ru/post/253223/, http://www.devexp.ru/2010/02/ispolzovanie-doxygen-dlya-dokumentirovaniya-koda/.

    И учить английский, найти бы время.
     
    Последнее редактирование: 13 ноя 2015
  16. Alex19

    Alex19 Гуру

    Оказалось у моего знакомого был модуль PCF8575 и он любезно предложил свой, пока мой в пути.

    Подключение, как и работа идентичны. Разница лишь в том, что у PCF8574 8 цифровых, а у PCF8575 16 цифровых и соответственно 1 и 2 регистра.

    Пример кода.
    Код (C++):
    #include "Arduino.h"
    #include "TWI.h"

    // Начальный адрес PCF8575.
    #define PCF8575_BEGIN_ADDRESS    0x20

    // Пин A0 на PCF8575 для изменения адреса.
    #define PCF8575_PIN_A0  0
    // Пин A1 на PCF8575 для изменения адреса.
    #define PCF8575_PIN_A1  0
    // Пин A2 на PCF8575 для изменения адреса.
    #define PCF8575_PIN_A2  0

    // Получаем адрес.
    #define PCF8575_ADDRESS (PCF8575_BEGIN_ADDRESS|PCF8575_PIN_A0|(PCF8575_PIN_A1<<1)|(PCF8575_PIN_A2<<2))

    // Светодиоды.
    #define LED_1 65279 // B1111111011111111
    #define LED_2 65023 // B1111110111111111
    #define LED_3 64511 // B1111101111111111
    #define LED_4 63487 // B1111011111111111

    // Кнопки.
    #define BTN_1 1
    #define BTN_2 2
    #define BTN_3 4
    #define BTN_4 8

    // Переменная хранящее максимальное значение uint32_t(unsigned long).
    #define unsignedLongMax 4294967295UL

    // Переменная хранящее 1.
    #define digitalOne 1UL

    // Структура для хранения данных PCF8575.
    // portIn - полученные данные.
    // portInOld - предыдущие полученные данные.
    // portOut - данные для отправки.
    // portInOld - предыдущие данные для отправки.
    // twimStep используется для хранения последней операции.
    typedef struct
    {
      uint16_t portIn, portInOld, portOut, portOutOld;
      uint8_t twimStep;
    }
    PCF8575;

    // Переменная структуры для хранения данных PCF8575.
    PCF8575 pcf8575;

    // Переменные для определения максимального времени цикла.
    uint32_t timeCycleBegin = 0;
    uint32_t timeCycle = 0;
    uint32_t timeCycleMax = 0;

    /// <summary>
    /// Предварительная настройка.
    /// </summary>
    void setup()
    {
      Serial.begin(115200);

      // Инициализация I2C.
      TwiMasterInit();

      // Устанавливаем размер регистра адреса в байтах.
      TwiSetToLengthAddressReg(0);

      // Сохраняем 1-ое значение, в PCF8575.portInOld.
      pcf8575.portInOld = 65535 & 255;
    }

    int temp = 0;

    /// <summary>
    /// Основной цикл.
    /// </summary>
    void loop()
    {
      // Начальное время цикла.
      timeCycleBegin = micros();
     
      // Отправляем запрос на чтение.
      if (!pcf8575.twimStep)
      {
        // Отправляем запрос на чтение.
        TwiReadBytes(PCF8575_ADDRESS, 3);

        // Увеличиваем номер операции.
        pcf8575.twimStep++;
      }

      // Если работа модуля завершена и номер операции равен 1.
      if (TwiGetFinishStep() && pcf8575.twimStep == 1)
      {
        // Проверяем как была завершена операция, если успешно идем дальше.
        if (TwiGetState() == TWI_SUCCESS)
        {
          // Переписываем данные буфера драйвера в свой буфер.
          TwiGetData((uint8_t*)&pcf8575.portIn, 2);

          // Для теста.
          //Serial.print("pcf8575.portIn - ");
          //Serial.println(pcf8575.portIn, BIN);

          // Увеличиваем номер операции.
          pcf8575.twimStep++;
        }
        else
        {
          // Здесь можно обработать исключение.

          // Просто сбрасываю номер операции, чтобы работа с модулем PCF8575 прекратилась.
          pcf8575.twimStep = 0;
        }
      }

      // Данные получены, переходим к анализу.
      if (pcf8575.twimStep == 2)
      {
        // Сравниваем с предыдущем полученным значением.
        if (pcf8575.portInOld != (pcf8575.portIn & 255))
        {
          // Сохранием новые значения.
          pcf8575.portInOld = (pcf8575.portIn & 255);
         
          // Устанавливаем начальное значение портов для маски.
          pcf8575.portOut = 65535;

          // Проверяем кнопку 1.
          if (!(pcf8575.portIn & BTN_1))
          {
            // Кнопка нажата.
            pcf8575.portOut &= LED_1;
          }
          else
          {
            // Кнопка не нажата.
            pcf8575.portOut |= ~LED_1;
          }

          // Проверяем кнопку 2.
          if (!(pcf8575.portIn & BTN_2))
          {
            // Кнопка нажата.
            pcf8575.portOut &= LED_2;
          }
          else
          {
            // Кнопка не нажата.
            pcf8575.portOut |= ~LED_2;
          }

          // Проверяем кнопку 3.
          if (!(pcf8575.portIn & BTN_3))
          {
            // Кнопка нажата.
            pcf8575.portOut &= LED_3;
          }
          else
          {
            // Кнопка не нажата.
            pcf8575.portOut |= ~LED_3;
          }

          // Проверяем кнопку 4.
          if (!(pcf8575.portIn & BTN_4))
          {
            // Кнопка нажата.
            pcf8575.portOut &= LED_4;
          }
          else
          {
            // Кнопка не нажата.
            pcf8575.portOut |= ~LED_4;
          }

          // Массив для отправки данных.
          uint8_t portBuf[3];

          // Адресный пакет.
          portBuf[0] = TWI_GET_WRITE_BYTE(PCF8575_ADDRESS);
          // Порты.
          portBuf[1] = (uint8_t)pcf8575.portOut;
          portBuf[2] = (uint8_t)(pcf8575.portOut >> 8);

          // Записываем новые значения.
          TwiSendData(portBuf, 3);
         
          // Сохраняем записываемые значения.
          pcf8575.portOutOld = pcf8575.portOut >> 8;
        }

        // Обнуляем номер последней операции с модулем.
        pcf8575.twimStep = 0;
      }
     
      // Определяем скорость работы.
      timeCycle = GetDifferenceULong(timeCycleBegin, micros());
      // Если текущее значение больше, последнего максимального, отображаем его.
      if (timeCycle > timeCycleMax)
      {
        timeCycleMax = timeCycle;
     
        Serial.print("Max - ");
        Serial.println(timeCycleMax);
      }
    }

    /// <summary>
    /// Получение разницы между 2 uint32_t.
    /// </summary>
    uint32_t GetDifferenceULong(uint32_t BeginTime, uint32_t EndTime)
    {
      if (EndTime < BeginTime)
      {
        // Защита от переполнения
        return unsignedLongMax - BeginTime + EndTime + digitalOne;
      }
      else
      {
        return EndTime - BeginTime;
      }
    }
     
  17. Alex19

    Alex19 Гуру

    Сегодня поговорим о датчике давления MS5803-30BA.
    Причина тому данная библиотека - https://github.com/millerlp/MS5803_30 для ардуино. Мне потребовалось подключить данный датчик. А так как в проекте практически ни чего нет, решил воспользоватся данной библиотекой.

    Вот результат ее работы.
    20 бар.

    Сам датчик - http://www.amsys-sensor.eu/sheets/amsys.en.ms5803_30ba.pdf.
    Application note AN520 - http://www.amsys-sensor.eu/sheets/amsys.fr.an520_e.pdf (Данный документ для датчиков серии MS56xx, MS57xx (исключая аналоговые сенсоры), и с примерами работы как с I2C, так и SPI для ATMEGA 644P).

    Подключение стр. 13 описания. Код для датчика из примеров (Application note AN520) в прищепке - AN520 (проверял на ATmega2560, ATmega328p). Его есть смысл использовать, если Вы не можете использовать прерывания TWI.

    Код для работы с библиотекой, в прищепке MS5803_30BA_AVR.

    Результат работы AN520 и MS5803_30BA_AVR
     

    Вложения:

    • AN520.zip
      Размер файла:
      3 КБ
      Просмотров:
      536
    • MS5803_30BA_AVR.zip
      Размер файла:
      11,2 КБ
      Просмотров:
      543
    Последнее редактирование: 13 дек 2015
    ИгорьК нравится это.
  18. siriniti

    siriniti Нуб

    Здравствуйте! Нашла Вашу тему, очень много информации,но новичку трудно разобраться. Если Вас не затруднит, помогите советом, пожалуйста! Ситуация такая: есть модуль OA PCF8574, arduino Uno, и акселерометры troyka (на базе LIS331dlh). Будет необходимо подцепить 8 датчиков к PCF8574. Помогите, пожалуйста, разобраться с адресацией. В какой библиотеке производить замену адреса на адрес, сформированный при помощи расширителя? Или это делается из основной программы? Запуталась, потому что для LIS есть своя библиотека считывания данных с датчика, а процесс считывания тоже не бесхитростный,так как у него несколько регистров. прилагаю библиотечные файлы от акселерометра и расширителя.
     

    Вложения:

    • LPS331.cpp
      Размер файла:
      4 КБ
      Просмотров:
      998
    • LPS331.h
      Размер файла:
      1,8 КБ
      Просмотров:
      682
    • troyka-imu.h
      Размер файла:
      264 байт
      Просмотров:
      896
    • PCF8574.cpp
      Размер файла:
      5,5 КБ
      Просмотров:
      943
    • PCF8574.h
      Размер файла:
      6,8 КБ
      Просмотров:
      505
  19. Unixon

    Unixon Оракул Модератор

    Вам нужен другой расширитель - PCA9547 (8-канальный мультиплексор I2C). Тот, что у вас есть (PCF8574), не подходит, это просто 8-битный порт ввода-вывода.
     
    Alex19 нравится это.
  20. siriniti

    siriniti Нуб

    спасибо за совет!!