HRP-N3 - серия источников питания с максимальной пиковой мощностью в 350% от MEAN WELL

Подключение часов реального времени DS1307 к микроконтроллеру STM8 через модуль I2C. Часть 2 - Обмен данными с помощью модуля I2C

STMicroelectronics STM8S003F3

- Одесса

Первый этап обмена данными – завершение работы с микросхемой DS1307

Все руководства по работе с интерфейсом I2C, в том числе и существующие в сети Интернет примеры работы с микросхемой DS1307, начинаются с инициализации модуля I2C с последующей передачей стартового бита. Однако при работе с часами реального времени так делать нельзя, иначе вы получите, как говорят в Одессе, «большой гембель». Это происходит из-за скрытого коварства интерфейса I2C, усугубленного энергетической независимостью микросхемы DS1307.

Выбираем схему BMS для заряда литий-железофосфатных (LiFePO4) аккумуляторов

Согласно спецификации интерфейса I2C [3], для передачи стартового бита необходимо перевести линию SDA в состояние низкого уровня при высоком уровне на линии SCL. Первая проблема заключается в том, что линия SDA может удерживаться в состоянии низкого уровня как микроконтроллером, так и микросхемой DS1307, ведь обмен данными по этой линии происходит в двух направлениях. Вторая проблема заключается в том, что интерфейс I2C может работать на какой угодно частоте – обмен данными можно даже приостановить на неопределенное время, а затем, например, через год, продолжить с того же самого места. И, наконец, третья проблема заключается в том, что микросхема DS1307 имеет собственный источник энергии (батарейку), от которого питаются все ее узлы, в том числе, и отвечающие за работу интерфейса I2C.

Таким образом, если в момент, когда микросхема DS1307 удерживала низкий уровень на линии SDA, произойдет отключение модуля I2C, например, из-за проблем с питанием, перезагрузки микроконтроллера или обнаружения ошибок в обмене данными, то шина I2C может оказать намертво зависшей. Линия SDA будет удерживаться в состоянии низкого уровня микросхемой DS1307 до тех пор, пока в батарейке есть энергия, и микроконтроллер не сможет сформировать ни стартовый, ни стоповый бит.

Вывести микросхему DS1307 из этого состояния можно двумя способами. Первый способ заключается в полном отключении питания – для этого придется извлечь батарейку, что, во-первых, долго и неудобно, а во-вторых, приводит к сбросу времени. Второй способ заключается в том, что микроконтроллер должен «завершить начатое», то есть закончить начатый когда-то обмен данными. А для этого нужно продолжить формирование импульсов на линии SCL до тех пор, пока микросхема DS1307 не освободит линию SDA.

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

Подготовка к запуску модуля I2C состоит из нескольких шагов. На первом шаге (Листинг 3) нужно отключить модуль I2C (если он до этого был включен). Отключение модуля производится с помощью подпрограммы I2C_Cmd, которая, в данном случае, сбрасывает бит PE в регистре CR1. Однако прежде чем это сделать, необходимо завершить предыдущие коммуникации, иначе модуль I2C может оставить порты ввода-вывода, связанные с линиями SCL и SDA, в режиме альтернативных функций и даже удерживать на них низкий уровень сигнала. Проще всего это сделать путем генерации стопового сигнала путем вызова подпрограммы I2C_GenerateSTOP.

Листинг 3. Исходный код первого шага запуска модуля I2C

switch (P->State)
{
  case RESET_I2C:
    // отключаем модуль I2C
    if (I2C->CR1 & I2C_CR1_PE)
    {
      I2C_GenerateSTOP(ENABLE);
      I2C_Cmd(DISABLE);
    }

    // настройка портов
    GPIO_Init(CLK_LINE, GPIO_MODE_OUT_OD_HIZ_SLOW);
    GPIO_Init(SDA_LINE, GPIO_MODE_OUT_OD_HIZ_SLOW);

    If (GPIO_ReadInputPin(CLK_LINE) && GPIO_ReadInputPin(SDA_LINE))
    {
      P->State = START_I2C;
    }
    else
    {
      P->PulseCount = 0;
      P->ErrorCount = 0;
      P->State = REGLUE_I2C;
    }

    break;

    . . .
}

После этого порты, связанные с линиями SCL и SDA, устанавливаются в режим выхода с открытым стоком и производится проверка реальных уровней сигнала, присутствующих на них. Если на обоих выводах обнаруживается сигнал с высоким уровнем, значит можно включать модуль I2C. В этом случае переменной State присваивается значение START_I2C. Если же хоть на одном из выводов обнаружен сигнал с низким уровнем, то можно попробовать вручную вывести микросхему DS1307 из этого состояния. В этом случае переменной State присваивается значение REGLUE_I2C и производится обнуление служебных переменных PulseCount и ErrorCount.

На первом шаге «реанимации» интерфейса I2C (Листинг 4) происходит проверка линии SCL. Если она находится в состоянии высокого уровня, значит можно переходить к следующему шагу. Если же нет, то, возможно, нужно немного подождать.

Листинг 4. Исходный код проверки состояния линии SCL

case REGLUE_I2C:
    // проверка линии SCL
    if (GPIO_ReadInputPin(SCL_LINE))
    {
      P->State++;
      P->ErrorCount = 0;
    }
    else
    {
      P->PulseCount++;

      if (P->PulseCount == 0)
      {
        P->ErrorCount++;

        if (P->ErrorCount >= MAX_ERROR_COUNT)
          P->State = HARD_ERROR;
      }
    }
    break;

Механизм ожидания реализован следующим образом. При каждом вызове подпрограммы DS1307_Execute переменная PulseCount увеличивается на единицу. Если ее значение в этот момент равно 255, то добавление еще одной единицы приведет к ее переполнению, и она станет равной нулю. В этот момент увеличивается счетчик ошибок ErrorCount. И если значение ErrorCount станет больше константы MAX_ERROR_COUNT, то переменной State будет присвоено значение HARD_ERROR и система перейдет в состояние ошибки. Это свидетельствует о серьезной аварии – при низком уровне на линии SCL дальнейшая работа интерфейса I2C невозможна.

Принцип вывода микросхемы DS1307 из зависшего состояния.
Рисунок 5. Принцип вывода микросхемы DS1307 из зависшего состояния.

На следующем шаге (Листинг 5) происходят самые интересные процессы – формирование импульсов на линии SCL с контролем линии SDA при высоком уровне сигнала на линии SCL. Если обнаруживается, что микросхема DS1307 наконец-то освободила линию SDA, то микроконтроллер сразу же установит на ней низкий уровень, что эквивалентно передаче стартового бита (Рисунок 5). Уже этого будет достаточно для сброса цифрового автомата микросхемы DS1307. Однако для полного и корректного завершения предыдущего сеанса обмена требуется сформировать стоповый бит, а для этого необходимо установить на линии SDA высокий уровень при высоком уровне сигнала на выводе SCL.

Листинг 5. Исходный формирования импульсов на линии SCL

case REGLUE_I2C + 1:
    // проверка линии SCL
    if (GPIO_ReadInputPin(SCL_LINE))
    {
      // проверка линии SDA
      if (GPIO_ReadInputPin(SDA_LINE))
      {
        // формируем стоп
        GPIO_WriteLow(SDA_LINE);

        P->PulseCount = 0;
        P->State++;
      }
      else
      {
        P->PulseCount++;

        if (P->PulseCount == 0)
        {
          P->ErrorCount++;

          if (P->ErrorCount > MAX_ERROR_COUNT)
            P->State = HARD_ERROR;
          else
            GPIO_WriteLow(SCL_LINE);
        }
      }
    }
    else
    {
      P->PulseCount++;
      if (P->PulseCount == 0)
        GPIO_WriteHigh(SCL_LINE);
    }

    break;

Длительность импульсов на линии SCL формируется так же, как и на предыдущем шаге – при нулевом значении переменной PulseCount. Количество импульсов на линии SCL определяется константой MAX_ERROR_COUNT и равно 10. Если после десяти импульсов на линии SCL линия SDA не освободилась, это означает наличие аппаратных проблем. В таком случае переменной State будет присвоено значение HARD_ERROR и система перейдет в состояние ошибки.

На последнем шаге происходит формирование стопового бита (Листинг 6). Здесь просто формируется требуемая задержка (путем инкремента переменной PulseCount), после чего на линии SDA устанавливается высокий уровень логического сигнала.

Листинг 6. Исходный код формирования стопового бита

case REGLUE_I2C + 2:
    // формируем стоп
    P->PulseCount++;

    if (P->PulseCount == 0)
    {
      GPIO_WriteHigh(SDA_LINE);
      P->State = START_I2C;
    }
    break;

После выполнения всех проверок, наконец-то, можно включить модуль I2C, не забыв предварительно перевести порты ввода-вывода, подключенные к линиям SDA и SCL, в исходное состояние (Листинг 7). Подробно останавливаться на настройках модуля I2C нет необходимости – они все хорошо описаны в технической документации на микроконтроллеры STM8 и библиотеку StdPeriph_Lib. В данном случае частота обмена по интерфейсу I2C была выбранной равной 5 кГц, а частота тактирования модуля I2C – 12 МГц.

Листинг 7. Исходный запуска модуля I2C

case START_I2C: // включаем модуль I2C
  // отключаем порты
  GPIO_Init(SCL_LINE, GPIO_MODE_IN_FL_NO_IT);
  GPIO_Init(SDA_LINE, GPIO_MODE_IN_FL_NO_IT);

  // включаем модуль I2C
  I2C_Init(5000, 0, I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, 12);

  P->State = READ_DATA;

  break;

Второй этап – получение информации с микросхемы DS1307

С этого момента работа с модулем I2C и микросхемой DS1307 уже не вызывает особых проблем. Практически весь последующий код является интуитивно понятным и созданным на основе примеров из StdPeriph_Lib.

Вначале необходимо передать стартовый бит (Листинг 8), однако прежде чем это сделать, необходимо настроить модуль I2C, чтобы он передавал бит подтверждения ACK в текущем байте (потом эта настройка будет меняться). Для реализации этих и всех последующих операций с модулем I2C проще всего использовать существующие подпрограммы из библиотеки StdPeriph_Lib – для этого достаточно подключить модуль stm8s_i2c.h с помощью директивы #include.

Листинг 8. Исходный код передачи стартового бита

case READ_DATA:
  I2C_AcknowledgeConfig(I2C_ACK_CURR);

  I2C_GenerateSTART(ENABLE);
  P->State++;

  break;

После передачи стартового бита модуль I2C переходит в режим ведущего, поэтому признаком завершения этой операции является обнаружение события, соответствующего константе I2C_EVENT_MASTER_MODE_SELECT. На следующем шаге нужно передать адрес микросхемы DS1307 (0b1101000х), чтобы она «понимала», что обращаются именно к ней.

Согласно технической документации на микросхему DS1307, младший бит адреса, передаваемого по интерфейсу I2C, определяет характер обращения: если он равен нулю, следовательно, к микросхеме обращаются для чтения, если единице – для записи. Чтобы не запутаться с адресацией, в подпрограмму I2C_Send7bitAddress передается дополнительный параметр – константа I2C_DIRECTION_TX или I2C_DIRECTION_RX, в зависимости от значения которого младший бит адреса устанавливается автоматически. В данном случае, мы обращаемся к микросхеме DS1307 в режиме записи (Листинг 9).

Листинг 9. Исходный код передачи адреса для чтения информации из DS1307

case READ_DATA + 1:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT))
  {
    I2C_Send7bitAddress(0xD0, I2C_DIRECTION_TX);
    P->State++;
  }
  break;

После передачи адреса микросхемы необходимо передать адрес первой ячейки памяти, откуда будет считываться информация. В данном случае нас интересуют байты, начинающиеся с нулевого адреса (Листинг 10).

Листинг 10. Исходный код передачи адреса первой ячейки памяти

case READ_DATA + 2:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    I2C_SendData(0x00);
    P->State++;
  }
  break;

На этом первый сеанс записи в микросхему закончен – теперь она «знает», какие именно данные будут «интересовать» микроконтроллер. Однако чтобы получить эту информацию, нужно вначале перезагрузить интерфейс I2C. А для этого нужно снова передать стартовый бит и адрес микросхемы, но, указав, что теперь передавать данные будет она. Исходный код этих двух шагов такой же, как и на предыдущих этапах, за исключением того, что теперь младший бит адреса устанавливается равным нулю, а перед началом приема данных нужно обнулить счетчик принятых байт (Листинг 11).

Листинг 11. Исходный код перезагрузки интерфейса I2C для приема данных

case READ_DATA + 3:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING))
  {
    I2C_GenerateSTART(ENABLE);
    P->State++;
  }
  break;

case READ_DATA + 4:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT))
  {
    I2C_Send7bitAddress(0xD0, I2C_DIRECTION_RX);
    P->State++;
    P->ByteCount = 0;
  }
  break;

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

В данном случае модуль I2C первоначально был настроен на передачу бита ACK сразу после приема восьми информационных бит текущего байта (Листинг 8). При приеме предпоследнего байта эту настройку нужно изменить, что и производится с помощью подпрограммы I2C_AcknowledgeConfig (Листинг 12).

Листинг 12. Исходный код приема информации от микросхемы DS1307

case READ_DATA + 5:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED))
  {
    P->Rx[P->ByteCount] = I2C_ReceiveData();
    P->ByteCount++;

    // после приема последнего байта АСК передавать не нужно
    if (P->ByteCount == BUFFER_SIZE - 1)
      I2C_AcknowledgeConfig(I2C_ACK_NONE);

    if (P->ByteCount == BUFFER_SIZE)
    {
      // приняли все данные
      I2C_GenerateSTOP(ENABLE);

      P->State = CHECK_DATA;
    }
  }
  break;

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

Третий этап – проверка принятых данных

Проверку принятых значений необходимо проводить обязательно, поскольку микросхема DS1307 имеет одну очень интересную особенность, которая может отнять немало времени у людей, невнимательно читающих техническую документацию. Дело в том, что старший (седьмой бит) регистра секунд является флагом остановки часов CH (Таблица 2). И этот флаг поднимается после каждого сбоя питания микросхемы, в том числе и после замены батареи. Таким образом, при первом включении микросхемы DS1307 будет получено время «00:00:80», при этом часы будут стоять, потому что этот флаг еще и запрещает работу тактового генератора. А это, в свою очередь, может создать иллюзию неисправности микросхемы или кварцевого резонатора, потому что осциллограф покажет полное отсутствие каких-либо колебаний на выводах 1 и 2 микросхемы DS1307.

Сбросить флаг CH можно только через интерфейс I2C путем записи информации в соответствующий регистр. Таким образом, если принятые данные являются некорректными, то нужно, как минимум, «запустить» часы, обнулив флаг CH, а заодно и установить время.

Есть еще один важный момент. Связь платы часов с контроллером системы осуществляется через переменную Time, поэтому ее значение может изменяться двумя способами: через интерфейс I2C, в результате обмена данными с микросхемой DS1307, и через интерфейс UART, в результате обмена данными с контроллером системы.

Признаком модификации переменной Time через интерфейс UART является флаг HasDataToWrite. Поэтому если этот флаг установлен, то переменной State будет присвоено значение WRITE_DATA и модуль I2C начнет запись информации в первые три регистра микросхемы DS1307 (Листинг 13). Если же он сброшен, произойдет проверка данных, принятых через интерфейс I2C. Если они окажутся корректными, то переменной State будет присвоено значение READ_DATA, что приведет к началу нового сеанса чтения данных их микросхемы DS1307. Если же нет, тогда переменной State будет присвоено значение WRITE_DATA для того, чтобы записать в микросхему DS1307 корректное время, а заодно и запустить часы.

Листинг 13. Исходный код обработки информации от микросхемы DS1307

case CHECK_DATA: // проверка данных
  if (P->HasDataToWrite)
  {
    CalculateTxBuffer(P);
    P->State = WRITE_DATA;
    P->HasDataToWrite = FALSE;
  }
  else
  {
    if (CalculateTime(P))
    {
      P->State = READ_DATA;
    }
    else
    {
      // неправильное время - сброс микросхемы
      P->Tx[0] = 0;
      P->Tx[1] = 0;
      P->Tx[2] = 0;

      P->State = WRITE_DATA;
    }
  }
  break;

Проверка принятых данных и вычисление значения переменной Time производятся в подпрограмме CalculateTime. Если результатом ее выполнения будет «Ложь», тогда произойдет обнуление передающего буфера Tx и запись его в микросхему. Обратное преобразование формата – формирование значений регистров микросхемы из значения переменной Time производится в подпрограмме CalculateTxBuffer, после выполнения которой производится сброс флага HasDataToWrite.

Четвертый этап – запись данных в микросхему DS1307

Этот этап (Листинг 14) во многом напоминает этап чтения. Вначале передается стартовый бит, затем адрес микросхемы в режиме записи и адрес первой ячейки, куда будет записываться информация. Но если в режиме чтения после этих операций происходил перезапуск интерфейса путем повторной передачи стартового бита, то в режиме записи дальше будет передаваться содержимое буфера Tx. И как только будут переданы все три байта, будет сформирован стоповый бит. После этого переменной State будет присвоено значение READ_DATA, что приведет к началу нового сеанса чтения данных из микросхемы.

Листинг 14. Исходный код записи информации в микросхему DS1307

case WRITE_DATA:
  I2C_GenerateSTART(ENABLE);
  P->State++;
  break;

case WRITE_DATA + 1:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT))
  {
    I2C_Send7bitAddress(0xD0, I2C_DIRECTION_TX);
    P->State++;
  }
  break;

case WRITE_DATA + 2:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    I2C_SendData(0x00);
    P->ByteCount = 0;
    P->State++;
  }
  break;

case WRITE_DATA + 3:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING))
  {
    I2C_SendData(P->Tx[P->ByteCount]);
    P->ByteCount++;

    if (P->ByteCount == BUFFER_SIZE)
      P->State++;
  }
  break;

case WRITE_DATA + 4:
  if (I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    I2C_GenerateSTOP(ENABLE);
    P->State = READ_DATA;
  }
  break;

Подпрограммы преобразования форматов времени

Поскольку в системе время хранится в двух разных форматах, потребовалось создать две подпрограммы, выполняющие их прямое и обратное преобразование. Исходный код подпрограммы CalculateTime, формирующей значение переменной Time из приемного буфера Rx, приведен в Листинге 15. Эта подпрограмма также проверяет корректность данных, полученных по интерфейсу I2C.

Поскольку результат расчета является 32-разрядным, а в 8-разрядных микроконтроллерах STM8 нет модуля аппаратного умножения, для сокращения количества операций была применена небольшая хитрость – в исходный код программы были заложены результаты промежуточных умножений, хранящиеся в виде пяти массивов констант:

  • S10_C – количество секунд в каждом из десятков секунд;
  • M_C – количество секунд в каждой из единиц минут;
  • M_C10 – количество секунд в каждом из десятков минут;
  • H_C – количество секунд в каждой из единиц часов;
  • H_C10 – количество секунд в каждом из десятков часов.

Это позволило упростить и ускорить расчет значения переменной Time, который теперь сводится лишь к сложению шести 32-разрядных чисел, выбираемых из массивов по значениям соответствующих переменных (S10, M, M10, H и H10), сформированных из приемного буфера Rx.

Листинг 15. Исходный код подпрограммы CalculateTime

bool CalculateTime(TDS1307* P)
{
  bool Result;
  uint8_t S, S10, M, M10, H, H10;

  // секунды
  S = P->Rx[0] & 0x0F;
  S10 = (P->Rx[0] & 0xF0) >> 4;

  // минуты
  M = P->Rx[1] & 0x0F;
  M10 = (P->Rx[1] & 0xF0) >> 4;

  // часы
  H = P->Rx[2] & 0x0F;
  H10 = (P->Rx[2] & 0xF0) >> 4;

  // проверка корректности времени
  Result = (bool)((S < 10) & (S10 < 6) & (M < 10) & (M10 < 6)
                  & (((H10) == 2) & (H < 4)) | ((H10 < 2) & (H < 10)));

  // массивы предварительно рассчитанных слагаемых
  const int8_t S10_C[6] = {0, 10, 20, 30, 40, 50};
  const int16_t M_C[10] = {0, 60, 120, 180, 240, 300, 360, 420, 480, 540};
  const int16_t M10_C[6] = {0, 600, 1200, 1800, 2400, 3000};
  const int16_t H_C[10] = {0, 3600, 7200, 10800, 14400, 18000, 21600, 25200,
                           28800, 32400};
  const int32_t H10_C[3] = {0, 36000, 72000};

  // расчет переменной Time
  if (Result)
    P->Time = H10_C[H10] + H_C[H] + M10_C[M10] + M_C[M] + S10_C[S10] + S;

  return Result;
}

Обратное преобразование формата производится в подпрограмме CalculateTxBuffer (Листинг 16). В ней значение переменной Time «раскладывается по полочкам» – по регистрам микросхемы DS1307. Поскольку подобное преобразование подразумевает использование достаточно большого количества операций деления, которые в микроконтроллерах STM8 аппаратно выполнить не представляется возможным, здесь они также были заменены циклическими операциями вычитания и сложения.

Листинг 16. Исходный код подпрограммы CalculateTxBuffer

void CalculateTxBuffer(TDS1307* P)
{
  int32_t Buf = P->Time;
  uint8_t S, M, H;

  // нормализация принятого значения
  while (Buf >= 86400)
    Buf -= 86400;

  // определение количества часов
  H = 0;
  while (Buf >= 3600)
  {
    H++;
    Buf -= 3600;
  }
  // определение количества минут
  M = 0;
  while (Buf >= 60)
  {
    M++;
    Buf -= 60;
  }
  // определение количества секунд
  S = (uint8_t)Buf;
  // формирование значения регистра секунд
  P->Tx[0] = 0;
  while (S >= 10)
  {
    P->Tx[0] += 0x10;
    S -= 10;
  }
  P->Tx[0] += S;
  // формирование значения регистра минут
  P->Tx[1] = 0;
  while (M >= 10)
  {
    P->Tx[1] += 0x10;
    M -= 10;
  }
  P->Tx[1] += M;
  // формирование значения регистра часов
  P->Tx[2] = 0;
  while (H >= 10)
  {
    P->Tx[2] += 0x10;
    H -= 10;
  }
  P->Tx[2] += H;
}

Заключение

Модуль I2C микроконтроллеров STM8 оказался не очень удобным для практического использования. Однако по сравнению с «ручным» методом формирования сигналов интерфейса I2C, который мне когда-то приходилось делать на микроконтроллерах, у которых не было столь развитой периферии, его использование позволяет сэкономить достаточно большое количество времени как машинного, так и личного. Поэтому с модулем I2C нужно учиться работать, и это не подлежит сомнению.

Не буду спорить, что приведенные алгоритмы и исходный код могут быть далеко не самыми красивыми и оптимальными. Однако у них одно неоспоримое преимущество – они рабочие. По крайне мере, на момент написания статьи тестовая установка проработала уже больше трех недель в режиме 24/7, и при этом не было замечено каких-либо сбоев. А если они и были, то их устранение произошло автоматически, без моего участия. Поэтому я надеюсь, что этот материал окажется полезным как специалистам, которые уже имеют определенный опыт программирования микроконтроллеров, так и тем, кто делает только первые шаги в освоении этого нелегкого, но очень увлекательного дела.

Дополнительная информация

  1. А. Русу. Жонглируем прерываниями: особенности работы с модулем UART микроконтроллеров STM8
  2. STM8S Value line
  3. I2C Bus
  4. STM8S Series and STM8AF Series 8-bit microcontrollers. Reference Manual
  5. STM8S/A Standard Peripherals Library

Материалы по теме

  1. Datasheet Maxim DS1307
  2. Datasheet STMicroelectronics STM8S003F3
  3. Datasheet Texas Instruments LM1117
  4. Datasheet MaxLinear SP485

Содержание цикла «Подключение часов реального времени DS1307 к микроконтроллеру STM8 через модуль I2C»

  1. Часть 1 - Особенности аппаратной части
  2. Часть 2 - Обмен данными с помощью модуля I2C
47 предложений от 23 поставщиков
Рабочее напряжение 3.3-5VHC-12 - радио модуль связи на базе чипа SI4438, работающий со стандартным Serial UART интерфейсом. Модуль поддерживает AT команды,...
ЗУМ-СМД
Россия
STM8S003F3P6
STMicroelectronics
4.35 ₽
STM8S003F3P6
STMicroelectronics
14 ₽
ChipWorker
Весь мир
STM8S003F3P6
STMicroelectronics
121 ₽
Flash-Turtle
Весь мир
STM8S003F3P6
STMicroelectronics
по запросу
Электронные компоненты. Бесплатная доставка по России
Для комментирования материалов с сайта и получения полного доступа к нашему форуму Вам необходимо зарегистрироваться.
Имя
Фрагменты обсуждения:Полный вариант обсуждения »
  • Ничего себе так, програма. Особенно в функции CalculateTxBuffer - ну надо додуматся находить остаток и целое не целочисленным делением, а последовательным вычитанием ... :)
  • А что, у них есть команда деления?