Проблемы с SPI в STM32F0 (HAL)

Posted by Nosaer | Posted in , , ,

    Отличительной особенностью в работе с SPI в серии F0, является использование 16 битного режима при передаче данных:
Data buffer address alignment restriction:
      (#) In case more than 1 byte is requested to be transferred, the HAL SPI uses 16-bit access for data buffer.
          But there is no support for unaligned accesses on the Cortex-M0 processor.
          So, if the user wants to transfer more than 1 byte, it shall ensure that 16-bit aligned address is used for:
          (##) pData parameter in HAL_SPI_Transmit(), HAL_SPI_Transmit_IT(), HAL_SPI_Receive() and HAL_SPI_Receive_IT()
          (##) pTxData and pRxData parameters in HAL_SPI_TransmitReceive() and HAL_SPI_TransmitReceive_IT()
      (#) There is no such restriction when going through DMA by using HAL_SPI_Transmit_DMA(), HAL_SPI_Receive_DMA()
          and HAL_SPI_TransmitReceive_DMA().
    Функции  в HAL написаны так, чтобы при передаче массива данных по SPI данные передавались пачками по 2 байта.
    Не смотря на то, какой режим передачи вы выберете в STM32CubeMX, передаваться будет всегда по 2 байта. Разница только в том, что если в 8 битном режиме остался 1 байт для передачи данных, то он будет отправлен отдельно.
    Для режима приемо-передачи есть один нюанс, это флаг SPI_FLAG_RXNE. Вся загвоздка в том, что пока аппаратно не будет получено 2 байта, этот флаг не будет выставлен. А следовательно передача в этом месте зависнет и будет ждать прихода еще одного байта.
Для этого при передаче последнего байта, флаг SPI_FLAG_RXNE выставляется самостоятельно (программно).
    Но проблема в том, что это не работает.  Хотя и реализовано вроде бы все правильно.
Но при работе и использовании функций на передачу и приемо-передачу отладчик вываливается с ошибкой в вечный цикл.
    Проблема распространенная. Часто встречался с ней на форумах, но в большинстве своем решение сводится к тому, чтобы отказаться в данном случае от HAL и переписать функцию на SPL или CMSIS.
    Для функции HAL_SPI_Transmit, мне в свое время помогло полное отключение оптимизации. Но это явно не тот выход.
Решение у меня в данном случае такое:

Имеем:
  /* Transmit and Receive data in 8 Bit mode */
  else
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U))
    {
      if (hspi->TxXferCount > 1U)
      {
        hspi->Instance->DR = *((uint16_t *)pTxData);
        pTxData += sizeof(uint16_t);
        hspi->TxXferCount -= 2U;
      }
      else
      {
        *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++);
        hspi->TxXferCount--;
      }
    }
    while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    {
      /* check TXE flag */
      if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)))
      {
        if (hspi->TxXferCount > 1U)
        {
          hspi->Instance->DR = *((uint16_t *)pTxData);
          pTxData += sizeof(uint16_t);
          hspi->TxXferCount -= 2U;
        }
        else
        {
          *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++);
          hspi->TxXferCount--;
        }
        /* Next Data is a reception (Rx). Tx not allowed */
        txallowed = 0U;
      }
      /* Wait until RXNE flag is reset */
      if ((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)))
      {
        if (hspi->RxXferCount > 1U)
        {
          *((uint16_t *)pRxData) = hspi->Instance->DR;
          pRxData += sizeof(uint16_t);
          hspi->RxXferCount -= 2U;
          if (hspi->RxXferCount <= 1U)
          {
            /* set fiforxthresold before to switch on 8 bit data size */
            SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);
          }
        }
        else
        {
          (*(uint8_t *)pRxData++) = *(__IO uint8_t *)&hspi->Instance->DR;
          hspi->RxXferCount--;
        }
        /* Next Data is a Transmission (Tx). Tx is allowed */
        txallowed = 1U;
      }
      if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout))
      {
        errorcode = HAL_TIMEOUT;
        goto error;
      }
    }
  }

  После преобразования, имеем:
  /* Transmit and Receive data in 8 Bit mode */
else
{
while((hspi->TxXferCount > 0) || (hspi->RxXferCount > 0))
{
  /* check TXE flag */
  if((hspi->TxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_TXE) == SPI_FLAG_TXE))
  {
    *(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr++);
    hspi->TxXferCount--;
    /* Enable CRC Transmission */
    if((hspi->TxXferCount == 0) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
    {
    hspi->Instance->CR1 |= SPI_CR1_CRCNEXT;
    }
  }
      /* Wait until RXNE flag is reset */
      if((hspi->RxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_RXNE) == SPI_FLAG_RXNE))
      {
        (*hspi->pRxBuffPtr++) =  *(__IO uint8_t *)&hspi->Instance->DR;
        hspi->RxXferCount--;
      }
      if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout))
      {
        errorcode = HAL_TIMEOUT;
        goto error;
      }
    }
  }

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

Ремонт шуруповерта Интерскол(Li-ion)

Posted by Nosaer | Posted in , , ,

    В результате попадания влаги в аккумулятор шуруповерта, коррозия постепенно разъела плату балансировки.
    Пытался восстановить видимые поврежденные участки перемычками и заменой неисправных элементов, но попытки не увенчались успехом. Грешу на какое нибудь переходное отверстие, коих на плате немерено. Да и сама плата визуально выглядит не очень. Огромное количество всевозможных элементов. На основных микросхемах спилили маркировки, огромное количество переходных отверстий. Схему найти не смог, а без схемы на этой плате разобраться так и не смог.  На самом сайте Интерскола, сообщают о не ремонтопригодности и предлагают купить новый аккумулятор.
    Аккумулятор в моем шуруповерте работает от 5 литиевых элементов типоразмера 18650 емкостью 1300 мАч каждый, от производителя HighStar. После 4 лет активной эксплуатации к элементам особых нареканий не было, но все же решил заменить на новые: LitoKala HG2 на 3000мАч. Элементы необходимо выбирать высокотоковые, я взял на 30 Ампер.
    На всеми известном китайском сайте нашел плату балласта на 5 элементов. Плата не большая, тем самым хорошо уместилась в родной корпус.
При желании можно найти и на 4 элемента и на 3 элемента и.т.д.
    В моем случае аккумулятор имеет 3 вывода. Красный и черный используются когда аккумулятор подключен к шуруповерту. Желтый и черный используются для зарядки аккумулятора.
    В зависимости от производителя, если верить форумам, может быть всего 2 провода. Либо так же три, но третий сигнализирует об окончании заряда, при превышении порогового значения напряжения либо температуры.
    Производитель платы балласта рекомендует 2 схемы подключения.
Для трехпроводного подключения:
И для двухпроводного подключения.
    В моем случае, подключить по первой схеме не получилось, так как у меня на  выводе для зарядки  21 Вольт, а на плате балласта вывод для подключения зарядки управляется по земле и должен иметь отрицательный потенциал. Поэтому решил P- и  C- замкнуть перемычкой, и соединить желтый и красный провода на разъеме аккумулятора.
    Необходимо уделить внимание на соединение между собой литиевых элементов. Перемычки с маленьким сечением легко прожгутся при первом же включении шуруповерта. Так же желательно сильно не перегревать элементы, а по возможность пользоваться контактной сваркой. Если контактной сварки нет, то понадобиться хороший флюс, который позволит подпаяться к элементам. Я использовал флюс: Kingbo RMA 218
    Во время зарядки аккумуляторов, на плате балласта по мере заряда будут загораться светодиоды. Когда загорятся все, аккумулятор будет заряжен полностью. В моем случае, Через минуту после того как загорелись все светодиоды, они все погасли и ток заряда перестал на них поступать. Об этом собственно сигнализировал и сам зарядник, на котором потух светодиод процесса зарядки.
    После первой зарядки желательно проверить прекращается ли подача тока на элементе по завершению заряда. У товарища был случай, когда один MOSFET пробило, и он продолжал заряжать аккумуляторы.
    Аналогичным способом можно переделать шуруповерт на NiMh аккумуляторах, под литье. Но тогда придется еще и менять само зарядное устройство.