Я думаю из периферии в первую очередь - таймер, USART, SPI. Из внешних устройств - дисплеи.
Сегодня кое как допилил симбиоз I2C и EEPROM AT24Cxx.
Есть такие функции
// Чтение/запись массива. Формат: Адрес в EEPROM, Буфер, Количество байт
int16_t Read(uint16_t MemAddress, uint8_t* DataBuff, uint16_t Size); // Чтение массива
int16_t Write(uint16_t MemAddress, uint8_t* DataBuff, uint16_t Size); // Запись массива
// Чтение/запись одного байта. Формат: Адрес в EEPROM
int16_t Read(uint16_t MemAddress); // Чтение одного байта
int16_t Write(uint16_t MemAddress, uint8_t Data); // Запись одного байта
Что ещё туда добавлять?
Пока меня прёт.
Тест
Проверяем уведомления на почту при новом ответе в теме.
@aveal Тест прошёл.
Доделал сегодня датчики BME280, BMP280. По I2C.
Но выложу позже. Сейчас готовлю статью по UART, так как очень удобно отлаживать программу.
Я провел сравнение по объему занимаемой памяти, что-то результат оказался не так внушителен, как я ожидал... Итак, первый проект - из этой статьи - вообще без изменений:
И я создал тестовый проект на HAL с инициализацией USART1 и отправкой 32 тестовых байт:
8.4 КБ флеша против 9.49 КБ.
Вся идея не уменьшении кода, а удобстве пользования.
На МК с малым объёмом памяти - 16 и меньше, HAL забивает вообще 60%.
Здесь можно выборочно взять нужные библиотеки.
Кроме этого. Этот размер занимает вся откомпилированная библиотека. И уже работа с ней отъедает мизер памяти.
Вся идея не уменьшении кода, а удобстве пользования.
На МК с малым объёмом памяти - 16 и меньше, HAL забивает вообще 60%.
Здесь можно выборочно взять нужные библиотеки.Кроме этого. Этот размер занимает вся откомпилированная библиотека. И уже работа с ней отъедает мизер памяти.
Да, понимаю. Также быстродействие будет намного выше я думаю, потому что HAL медленная довольно, даже в некоторых проектах были из-за этого трудности.
потому что HAL медленная довольно, даже в некоторых проектах были из-за этого трудности.
У меня знакомец пишет на HAL.
Когда писал трёхфазный двигатель на таймере, инициализацию делал на HAL. А всё остальное на CMSIS. Почему то на HAL не хотело работать хоть тресни. А там всего то таблица синусов да перегон её в таймер через DMA.
Я то же начинал писать на HAL. Один раз столкнулся с тем, что HAL ни в какую не хотел запускать таймер в одноимпульсном режиме. Один раз запустил и больше не захотел. В чём дело, так и не нашёл.
А насчёт того, что Вы говорили, что переводить старые проекты на новый тип программирования. Зачем. У меня запущен проект на чистом HAL. Потом появились проекты где я HAL обернул в классы. Потом начал куски HAL переводить на прямую работу с регистрами. А потом просто начал писать библиотеки и постепенно в каждом следующем проекте доля написанных библиотек увеличивалась.
Кроме этого, я конечно столкнулся с несовместимостью версий библиотек в разных проектах. В этом случае, при архивации проекта я вместе с проектом пакую и текущие на тот момент библиотеки. Если приходится вернуться к старому проекту, я просто сваливаю в один каталог и проект и библиотеки идущие с ним и редактирую пути доступа.
Случайно наткнулся на этот цикл статей, автору респект громадный, делает свои библиотеки, делится ими, так еще и описывает в статьях.
Всем, кто пользуется библиотеками, так как я начал выкладывать библиотеки без их описаний для разных ядер.
У вас могут возникнуть проблемы с компиляцией. Например есть ядро, для которого не написан SPI, а в библиотеке лежит драйвер дисплея работающего по SPI, компилятор будет ругаться.
Вот здесь ругается на драйвер дисплея:
Заметьте, что крестик стоит напротив *.h и *.cpp, чаще всего он стоит напротив *.h файла.
Что бы этого не происходило, необходимо исключить библиотеку из компиляции.
Делается это таким образом:
Правой кнопкой мыши щёлкаем на соответствующем *.cpp файле и выбираем
Ставим галочки и нажимаем ОК. Больше он нам мешать не будет. Так делается на всех файлах, на которые будет ругаться.
Есть нюансик с SPI. Драйвер зависимый от ядра лежит в каталоге драйверов. "Софтварный" SPI лежит в каталоге библиотек. Вместе они компилиться не будут. Так как они одинаковы по структуре и переменным. Только часть отвечающая за передачу разная. Поэтому нужно исключить из компиляции именно тот, который не собираетесь использовать. Позже могут ещё появиться такие дубликаты. С ними придётся поступать так же.
От любопытства посмотрел либлиотеки. Мдяяяя... От C++ тут кроме точек :: ничего и нету 🙂 Афтару - глубжее и четчее изучать изык С++! Конкретно - изучить КЛАССЫ и ШАБЛОНЫ. Потому как единственный вменяемый способ описания работы с периферией микроконтроллера, такой как GPIO, SPI, I2C и проч - шаблонные классы со статическими методами. На примере GPIO конечный результат должен выглядеть так:
using pa0 = GpioA::pin0; using pb5 = GpioB::pin5; pa0::Set(); if(pa0 == pa0::State::SET) pb5::Reset(); GpioA::Configure<pin0_msk | pin2_msk>::AsOutputPins<OutType::PP, Speed::MED>();
на примере SPI - так:
Spi1::Configure<
Role::MASTER,
FrameSize::_8bit,
Endian::MSB,
Duplex::FULL,
ClockDiv::_8,
ClockMode::M0,
SS_Input::SW_NOSEL,
SS_Out::DISABLE>();
Spi1::Enable();
Spi1::Send(0x45);
Spi1::Dma::Tx::Enable();
while(Spi1::Flags::IsTXE());
while(Spi1::Flags::IsBusy());
Spi1::Disable();
То есть, благодаря шаблонам (template), не требуется заводить переменные для хранения конфигурации.
При этом, класс тоже должен быть шаблонным:
template<Letter Name> Gpio; template<int N> Spi;
то есть, конкретный модуль GPIOA, GPIOB или SPI1, SPI2 и тд вычисляется на этапе компиляции посредством constexpr:
template<int N> Spi {
public:
private:
static constexpr SPI_Typedef* GetBase();
static constexpr SPI_Typedef* spi = GetBase();
};
А чтобы сократить запись, используем using:
#ifdef GPIOA using GpioA = Gpio<Letter::A>; #endif #ifdef GPIOB using GpioB = Gpio<Letter::B>; #endif #ifdef SPI1 using Spi1 = Spi<1>; #endif #ifdef SPI2 using Spi2 = Spi<2>; #endif
#ifdef здесь особенно актуален для Gpio, поскольку в разных МК разное количество доступных портов, а всего по максимуму их - от GPIOA до GPIOK. И если в заголовочнике МК определен например GPIOK, будет доступен этот вариант, а если нет - то недоступен. Чтобы не переписывать каждый раз список портов. Аналогично можно поступать и с остальной периферией.
Ну и для Gpio ради типобезопасности введен
enum class Letter {A, B, C, D, ... и так далее
Аналогично поступаем и с остальной периферией.
Например, для осцилляторов:
class Hse {
public:
enum class Source {XTAL = 0, GEN = RCC_CR_HSEBYP};
template<Source Source> static void Enable();
static void Disable();
static bool IsReady();
struct Irq {
static void Enable();
.... и так далее ....
применение:
Hse::Enable<Hse::Source::XTAL>(); while(Hse::IsReady != true);
В общем, вектор движения ясен? Вот это уже будет C++. А то, что здесь в библиотеке написано - это обычный "Си с классами".
Продолжая тему...
Посмотрел несколько файлов топикстартера. Ну, всё по-классике - типовая ошибка в смешивании функционала. Так называемый "побочный эффект функции". То есть, функция (в С++ - метод) называется I2C::Init(). Её основное назначение - настройка модуля I2C. А у автора в ней прописана и настройка пинов, и подключение тактирования модуля. Это - и есть "побочный эффект функции". То есть, по названию I2C мы понимаем, что работаем только с I2C. Но по факту там работа и с GPIO, и с RCC. Особенно лишнее здесь GPIO. Просто потому, что I2C, как и многие другие модули, не фиксированы жестко на конкретных пинах, они могут быть перенесены. Следовательно, привязываться к пинам в функции настройки модуля - совершенно излишне.
Включение тактирования модуля в I2C::Init() хоть и не сильно мешается, но тоже побочный эффект. Оставить можно, но это будет не совсем чисто, так сказать. Потому как например SPI, и особенно DMA можно перенастраивать в течение работы программы. И повторное включение тактирования будет лишним. Особенно это заметно в DMA.
@yumayuma Спасибо за подсказки.
Я прекрасно понимаю, что как программист я не очень. Всю жизнь писал на ассемблере.
Нужда заставила перейти на С++, когда перешёл на STM.
И я оказался одним из первых, кто занялся библиотеками для него. Все используют HAL и говорят, что С++ не нужен.
Я начал писать всё это, когда не совсем понимал, как лучше сделать. За пример брал Ардуиновские библиотеки, которые не могут быть эталоном.
Я уже начал переписывать их, правда сюда не выкладывал. Как Вы и говорите, я начал отделять "Мух от мёда".
Все, понимающие больше меня почему то не хотят с этим связываться.







