Hatred's Log Place

DON'T PANIC!

Dec 4, 2022 - 9 minute read - electronic

LGT8Fx / LGT8F328P

На правах заметки.

Intro

В народе этот чип называют “клоном” Atmega328P или “антикризисным Arduino” (ну не сам чип, а те платки, которые доступны на AliExpress).

Даташит смотреть в разделе “Полезные ссылки и документация”.

Клоном оно не является. Это самобытная разработка, которая в некотором отношение по пинам (в некоторых режимах) совместима ATmega328P. При этом, если, к примеру, плату с Али зашить обычным примером я миганием светодиода, собранным для BSP Arduino Nano, то диод будет мигать неадекватно. Т.е. для этого контроллера нужно свой пакет BSP, как, к примеру, для совсем “левых” контроллеров, типа ESP32.

Т.е. из схожего:

  • система комманд и ассемблер, тулчейн
  • корпус и совместимость пинов, при этом, LGT8F328 может заменить 328 мегу, то обратная замена не всегда возможна (далее).

Основные особенности LGT8F328P:

  1. Питание от 1.8-5.5В для всех частот
  2. Максимальная частота на внутренней RC цепочке и внешнем кварце до 32Mhz
  3. Нет фьюзов, как следствие, все настройки периферии нужно делать в стартовом коде, аналогично “взрослым” контроллерам. Отсюда и необходимость в отдельном “ядре” (BSP - Board Support Package из “взрослой” терминологии) для Arduino IDE.
  4. Особенности BSP для Arduino IDE, что прошивается только на 57кбод.
  5. Дополнительный третий 16 битный таймер. И все таймеры существенно проапргейжены в плане максимальных частот PWM и числа ног с PWM.
  6. Появился ЦАП
  7. АЦП тут уже 12 битный, а не 10 битный. Хотя, по отзывам, реальная точность осталась в районе 10 бит
  8. Нет EEPROM. Но он может программно эмулироваться на внутреннем флеше. Т.е. - больше EEPROM - меньше на код.

Хорошее ревью с примерами кода: Обзор клона меги328 -LGT8F328P

В общем, контроллер новый, самобытный. Для домашнего использования очень интересен, но для промышленного использования я бы поостерёгся использовать.

Для домашнего использования хорош и тем, что можно купить на Али версю платы Нано за ~115 рублей с бесплатной доставкой против 250 рублей за аналогичный Nano v3. Плюс платы с односторонним расположением элементов, что позволяет монтировать на плату в виде модуля.

Официальный тулинг

Статья: LGT8F328P Arduino совместимая плата. Программирование на чистом Си

Ну а инструмент называется: LGTSDK Builder

Поддержка в Arduino IDE

Теперь про интеграцию в Arduino IDE. Как я писал выше - нужен свой BSP. Как минимум настройки для PLL и EEPROM при старте.

Я нашёл более-менее работающих два:

  1. https://github.com/dbuezas/LGT8fx
  2. https://github.com/nulllaborg/arduino_nulllab (альтернативный репозиторий: https://gitee.com/nulllab/nulllab_arduino)

Рекомендую ознакомиться так же со ссылками на страницах обоих BSP. Можно найти полезного.

LGT8fx

Рекомендую именно его на момент 2024.11.26 (актуальная версия: 2.0.7), до этого рекомендовал Nulllab

Ставим через Board Manager в Arduino IDE. В File → Preference, Settings tab добавляем:

https://raw.githubusercontent.com/dbuezas/lgt8fx/master/package_lgt8fx_index.json

На момент первоначального составления заметки была актуальна версия 1.0.5 (оригинальный BSP LGT8fx выглядел заброшенным.) и была доступна неофициальная 1.0.6 (ака v1.0.7 pre release), которая:

  1. Работает с платами с внешним 16Mhz кварцем
  2. Реализуют классический Arduino интерфейс для работы с EEPROM.

Естественно ставить её нужно было вручную. Сейчас же (2023.06.23) актуальная версия - 2.0.6, уже содержит вышеупомянутые доработки.

Для установки в Board Manager вводим:

gt8fx

И ставим “LGT8fx Boards”

В Tools → Board → LGT8fx Board выбираем единственную (2023.06.25) борду: LGT8F328. Собственно на ней и собраны популярные “клоны” Ardunio Nano и Arduino Pro Mini.

После этого выбираем в Tools → Variants выбираем:

  • 328P-LQFP32 - для большинства клонов в форм-факторе Arduino Pro Mini и Arduino Nano Ну или посчитайте ножки :)

Из настроек полезные, по сути, только частота работы и что используется: внутренний клок или внешний кварц.

Внешний кварц понятен, есть три варианта (Tools → Clock Source):

  • External 12Mhz
  • External 16Mhz
  • External 32Mhz

Внутренний клок всегда один (там же, Tools → Clock Source):

  • Internal 32Mhz

А вот нужную частоту работы уже догоняем делителями (Tools → Clock Divider): 1, 2, 4, 8, 16, 32

Я не разбирался, работают ли делители, при выборе внешнего кварца, но для внутреннего клока они точно работают.

Следующие настройки, скорее всего, никогда не придётся менять:

  • Upload Speed: 57600, при другой у меня ничего не прошивалось, но зависит от прошитого FSBL (aka просто загрузчик, позволяющий обновляться по UART).
  • SERIAL_RX_BUFFER_SIZE: 64, скорее всего сделано как какой-то WA. Не вникал.

Настроек для размера EEPROM нет, он есть всегда и его размер - 1 кБ. Если нужна гибкость в настройке - используйте Nulllab BSP,

Ну и стоит отдельно упомянуть, что этот BSP - это развитие “официального” оного: https://github.com/LGTMCU/Larduino_HSP. Можете и его попробовать. В некоторых источниках есть и на него ссылка.

Nulllab

В целом можно и его рекомендовать: широкий набор источников клока, делителей (косвенно, через указание конкретной частоты) и возможность настройки EEPROM. Но LGT8fx Boards очень оживился, а этот как был версией 1.0.3 на момент составления заметки (2022.12.04), так и остался на момент правки (2023.06.25).

Ставим через Board Manager в Arduino IDE. В File → Preference, Settings tab добавляем:

https://raw.githubusercontent.com/nulllaborg/arduino_nulllab/master/package_nulllab_boards_index.json

Nullab в Board Manager ищется по словам… nulllab :-) При выборе борды нужно выбрать:

  • Tools → Board → Nulllab AVR Compatible Board → DIY Board

При этом станет доступен полный “тюниг” для всех борд:

  • Tools → CPU Frequency: 16 Mhz
  • Tools → Clock Source: External (16 Mhz)
  • Tools → EEPROM size: от 0 до 8кБ с шагом x2
  • Tools → Upload Speed: 57600 - это важно, иначе не будет шиться (см выше)

Как и LGT8fx Boards - это развитие “официального” BSP: https://github.com/LGTMCU/Larduino_HSP.

Покупка

Естественно - Aliexpress. Я брал:

Полезные ссылки, обсуждения и документация

Табличка, где сравнивается скорость выполнения  в тактах некоторых ассемблерных команд в обычном AVR и в LGT8 (по данным по ссылке 1):

Instruction Function                    Cycle of AVR    Cycle of LGT8XM

ADIW        Add immediate to word       2               1
SBIW        Subtract immediate to word  2               1
MUL/S/SU    8bit multiply               2               1
FMUL/S/SU   Fractional multiply         2               1
RJMP/RCALL  Relative jump/call          2/3             1
IJMP/ICALL  Indirect jump/call          2/3             2
RET/IRET    Return                      4               2
CPSE        Compare, skip if equal      1/2/3           1/2
SBIS/SBRS   Skip if set                 1/2/3           1/2
SBIC/SBRC   Skip if cleared             1/2/3           1/2
LD/LDD      Load indirect               2               1
ST/STD      Store indirect              2               1
LPM         Load program memory         3               2
PUSH/POP    Stack access                2               1

Другие MCU от LGT в платах Arduino и их сравнения:

Pinout

Schematic

LGT8F328P MiniEVB

Качество шакальное, но нашёл только тут скачивал через тут. Оба два ресурса - через VPN или Tor.

Code snippets

Определение в коде, что строимся для LGT8fx

  • LGT8fx Boards BSP и Nulllab BSP, определены макросы:
    • Все “P” (например - 328P) платы:
      • __LGT8FX8P__
      • __LGT8F__
    • LQFP48:
      • __LGT8FX8P48__
    • SSOP20:
      • __LGT8F_SSOP20__
    • Прочее: смотреть в ARDUINO_DIR/packages/BSP/hardware/avr/VERSION/variants/XXX/pins_arduino.h

GUID

В каждом чипе прошит уникальный GUID (aka Serial Number). Может использоваться для расшифровки/кодирования чего-то

uint32_t guid = (uint32_t)&GUID0;

Вывод тактовой на ногу PB0

CLKPR = 1<<PMCE; //разрешить изменение
CLKPR = 1<<5 | 1<<0; //делитель =2 и вывод clk

Переключение тактирования на кварц

sysClock(EXT_OSC);
CLKPR = 1<<PMCE;//разрешить изменение 
CLKPR = 1<<5; // вывод clk

Тактирование от внешнего генератора 32MHz

#include "lgtx8p.h"
int main(){
PMCR=1<<PMCE; //разрешить выбор источника тактирования
PMCR= 1<<2 | 1<<5; //External high frequency crystal 
PMX2= 1<<WCE;//разрешить изменения
PMX2= 1<<XIEN;//разрешить вход тактовой частоты от кварц. генератора
CLKPR = 1<<PMCE;//разрешить изменение
CLKPR = 1<<5; //делитель =1 и вывод clk
}

ЦАП. Вывод пилы

#include "lgtx8p.h"
 void setup() {
DACON= 1<<DACEN | 1<<DAOE ; // dac-on, включить пин
static uint8_t n=0;
cli();
while(1){  DAL0=n++;  } 
}
 
void loop() {}

Пример задействования ног  SWC,SWD,ADC6,ADC7,AREF - их можно сделать выходами порта E

#include "lgtx8p.h"
void setup() {
PMX2= 1<<WCE;
PMX2= 1<<E6EN; //сделеть AREF ногой PE6
MCUSR&= ~(1<<7);
MCUSR|= 1<<7;// Освободить PE0,PE2 от SWD
//ноги SWC=PE0,ADC6=PE1,SWD=PE2, ADC7=PE3,AREF=PE6
DDRE= 1<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<6 ;

Пример ногодрыга с переключением клоков на  тактовую 32МГц

int main(){
CLKPR = 1<<PMCE;//разрешить изменение
CLKPR = 0; //делитель =1
DDRB = 1<<4; // 12 пин OUTPUT
 while (1){ PINB=0x10; }
}

Тест на наличие таймера3 -при 16МГц тактовой светодиод Тх замигает с частотой примерно 5Герц

int main() {
*(uint8_t*)0x33 =0x2 ;//  DDRF=1<<DDF1
*(uint8_t*)0x90 =0x40; // TCCR3A=1<<COM3A0;
*(uint8_t*)0x91 =0xD;  // TCCR3B=1<<WGM32 | 1<<CS30 |1<<CS32;
*(uint8_t*)0x99 =0x5; // OCR3AH=0x5; 
*(uint8_t*)0x98 =0xDC; // OCR3AL=0xDC;

Можно перенести Rx и Tx (оба одновременно или только какой то один) на другие пины

PMX0= 1<<7;//разрешить ремаппинг
PMX0= (1<<RXD5) |( 1<<TXD6) ;

Нужно, так как на этой плате неправильно разведён USART. Обмен данными возможен только между мк и usb-мостом. Какой-либо внешний сериальный сигнал подать нет возможности, он просто не прожмёт линию, которую каждый чип и светодиоды тянут вверх. Но выход есть, на помощь приходят регистры port multiplexing (PMX). Так что подключить bluetooth/GPS не проблема. 

Так же отремаппить можно и выход таймера OC3A, который сидит на пине Txd, и так же страдает от резистора ( #286):

PMX0= 1<<7;//разрешить ремаппинг
PMX1= (1<<C3AC);

Цитата от сниппета:

Ловить на PD6, причём в данном режиме переводить PD6 в OUTPUT категорически запрещено, иначе будет к.з   т.к. сигнал OC3A будет выводится не на сам PD6, а на запараллеленый ему физически вход компаратора. В общем там всё запутанно :)

ШИМ / PWM

У меня на таймере Timer3 не получилось получить ШИМ на ноге D1 (TX). После экспериментов там появился сигнал, но уровня 1.5В. Смотреть, также, предыдущий пункт про ремаппинг.

Предыдущий пункт про ремаппинг всё так же смотреть: вдруг пригодиться. На платах MiniEVB (см схему выше) цепь TX (D1) не притягивается никуда ничем, а с конвертером разделена резистором, так что ей ничего не мешает работать полноценно как таймер. В моём коде оказалась ошибка: не записывалось значение в регистр OC3A. При этом, нужно деактивировать порт PD и активировать выход на PF:

// Для OC3A (D1)
DDRD &= ~(1u << DDD1);
DDRF |= (1u << DDF1);

// Для OC3B (D2)
DDRD &= ~(1u << DDD2);
DDRF |= (1u << DDF2);

Правда при этом мы лишаемся TX USART. Его можно перенести на другую ногу (см пинаут и предыдущий пункт), но там тоже нужный ШИМ.

А вот тут есть идея как запустить на A0/PC0, но не проверена:

Для настройки PWM онлайн:

Или отличная статья с деталями: LGT8F328P таймеры (Arduino). Тут всё по полочкам, по режимам.

Стоит отметить про Timer0: он используется для Millis по переполнению. Делитель там 64 (но завязываться на это не стоит). Соответственно мне нужен был делитель 1, а значит время от millis стало в 64 раза быстрее. Костыльно для задержек решил проблему так:

constexpr long ms(long val)
{
  return val * 64;
}
constexpr unsigned long long int operator""_ms(unsigned long long int val) {
  return val * 64; // we use divider 1 vs default divider 64 for timer0
}

void loop() {
  static long tm = millis(); 
  if (millis() - tm > 15_ms) {
    ...
  }
  if (millis() - tm > ms(some_val)) {
    ...
  }
}

В идеале же нужно прочитать делитель при старте и заменить 64 на прочитанное значение.