STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32

Предисловие


Меня частенько упрекают, что я даю материал в слишком трудной манере и не по порядку. Спешу вас расстроить, я и дальше буду следовать этой логике.


Стерильно-приторных уроков “для чайников” по программированию МК, в том числе и по STM32, на просторах инета полно. Учат они чему-то? Не уверен. Они лишь дают возможность разобраться в какой-то функции, когда возникает потребность.


У меня цели другие: научить думать и пользоваться документацией. Это можно сделать только на живых и настоящих примерах. Только в бою, так сказать. Я стараюсь показывать не столько возможности, сколько путь от задумки до реализации. Стараюсь выстраивать логику процесса так, чтоб не дочитав до конца вы уже могли бы предположить, что получится или какие могут возникать проблемы.


Я практикующий радионженер. Я не занимаюсь DIY. Я стараюсь показывать вещи так, как их делать ПРАВИЛЬНО. Делать на совесть, а не тяп-ляп и так сойдёт. Одно дело, когда вы берёте чужую реализацию и не разбираясь суёте в свой проект. Другое дело, когда сами пишете библиотеку с настроением, мол, разбираться некогда. Как то работает и х** с ним. Это ужас ужасный!


Предыдущие статьи:


Настройка Sublime Text 3, SW4 и STM32CubeMX для разработки STM32 под Windows 10

Настройка Sublime Text 3, SW4 и STM32CubeMX для разработки STM32 под Linux

STM32 от Булкина. Урок 1: Вводный, где мы немножко похулиганим

STM32 от Булкина. Atmega и Arduino vs STM32 и HAL


Ладно, сегодня у нас интересная тема!


Пишем библиотеку сами для STM32


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


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


Я долго не мог придумать, что же такое взять в качестве примера из того, что у меня самого не реализовано. И с удивлением обнаружил, что у меня нет библиотеки для классического текстового LCD на Hitachi HD44780. Это 1-но, 2-х или 4-х строчные дисплеи до 20 символов на строку. Те самые, которые все так любят втыкать во все свои DIY.


Полазил по просторам и с ещё большим удивлением обнаружил, что все реализации для шины I2C основаны на дурацкой классической библиотеке Arduino LiquidCrystal_I2C. Ну, думаю, сам Бог велел!


Начнём с главного: чтения документации.


Как работает дисплей


Дисплей основан на старинном чипе от HITACHI HD44780. У него нет последовательного интерфейса, как, например, у ST7920. Тем не менее, он прост до безобразия.


Открываем даташит раздел “Interfacing to the MPU” и видим примерную диаграмму, как устроен обмен данными. Смотрим, смотрим и видим фигу. Но всё-таки что-то почерпнуть можно.

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Базовый его режим 8-ми битный. Т.е. мы можем передавать ему 1 байт за раз. Для чего у него есть восемь ног DB0-DB7. Ещё у него используются 3 ноги:


E: выдаём строб (импульс), который сообщает дисплею, что на ногах DB0-DB7 выставлены нужные данные, мол, давай, считывай

RS: Сообщаем дисплею, что мы хотим передать или считать, команду или конфигурацию

R/W: Сообщаем дисплею, пишем мы данные или считываем


На схеме показывается 4-битный режим. Это когда мы используем 4 ноги DB4-DB7 вместо восьми и передаём два раза по 4 бита. Режим полезен там, где жалко отдавать лишние ноги у МК. Или для нашего расширителя портов на PCF8574.


Пытливый ум заметит, что сначала мы передаём старшие 4 бита, потом младшие. Также обратит внимание, что для передачи данных на ноге R/W должен быть 0, а для чтения 1.


Итак, как же выглядит передача данных в 8-битном режиме:


Для передачи команды дисплею, на ноге RS мы выставляем 0. Если надо передать символ, выставляем 1;

Если мы передаем команду или данные, то выставляем 0 на ноге R/W;

На ногах DB0-DB7, мы выставляем значения побитово того, что хотим передать;

Выдаём строб (импульс) на ноге E;

Документация рекомендует после строба считывать готовность дисплея к приёму следующей команды.


Как же выглядит передача данных в 4-битном режиме:


Для передачи команды дисплею, на ноге RS мы выставляем 0. Если надо передать символ, выставляем 1;

Если мы передаем команду или данные, то выставляем 0 на ноге R/W;

На ногах D4-D7 дисплея, мы выставляем значения старших 4-х бит, что хотим передать;

Выдаём строб (импульс) на ноге E;

На ногах D4-D7 дисплея, мы выставляем значения младших 4-х бит, что хотим передать;

Выдаём строб (импульс) на ноге E;

Документация рекомендует после двух стробов считывать готовность дисплея к приёму следующей команды.


Я тут накидал диаграмку, как передаются данные в 4-х битном режиме. Передаём два байта 0xA6 и 0xE9.

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Обратите внимание, нельзя вот просто так взять и щёлкнуть стробом. Нужно помнить, что ширина строба и пауза между ними должны соответствовать паспортным данным. Идём в даташит и ищем что-то похожее на delay, timeout, execution time и т.д. Обязательно даются такие данные. Находим табличку “Table 6: Instructions” и видим, что на исполнение команды требуется от 37мкс до 41мкс. На возврат курсора в начало экрана требуется 1.52мс. Также при хаотичном листании документа в поисках информации, какая же должна быть пауза, находим в диаграмме “Figure 24: 4-Bit Interface” это:


When BF is not checked, the waiting time between instructions is longer than the execution instuction time. (See Table 6.)

Т.е. если мы не проверяем флаг занятости дисплея (почему объясню позже), то пауза должна быть больше, чем время исполнения инструкции. Т.о. я указал на диаграмме ширину строба 50мкс, интервал между парными стробами тоже в 50мкс, а интервал между данными 60мкс, для гарантии (китайские микрухи такие китайские).


Сами символы хранятся в таблицах, которые бывают Японскими или Кириллическими. На Али кириллицу хрен купишь, поэтому мы можем только загрузить в дисплей 8 собственных символов. Полностью это алфавит не покроет, но хоть что-то.


Как с ними работать, будем смотреть позже. Сейчас нас волнует подключение и протокол.

Подключение дисплея к шине I2C


Но нам вот жалко отдавать 7 ног МК (в 4-битном режиме) на дисплей. И кто-то взял и придумал копеешный модуль, который цепляет дисплей к I2C и сохраняет старый протокол.


Основан он на расширителе портов PCF8574. Вещь простая до безобразия. У него есть 8 ног, на которых мы можем выставлять 0 или 1. По I2C мы тупо шлём один байт на него, где каждый бит соответствует ноге. Либо тупо считываем такой же байт с текущим состоянием этих самых ножек.


Так вот модуль подключен по аналогичной схеме (я реализовывал это у себя на плате года два назад):

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Пытливый ум, глядя на эту схему, задастся вопросом: А как же строб выдавать? Да ещё тайминги соблюдать. Да и вообще, как дрыгать ножками RS да R/W, чтоб не мешать данным и не сводить с ума дисплей? А вот тут и начинается самое интересное.


Ход мыслей такой. Давайте сначала заглянем в документацию PCF8574 и поищем там диаграмму по обмену данными. Находим прекрасную картинку:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Внимательно смотрим и видим, что состояние на ногах меняется сразу по окончании приёма байта от МК. Т.е. нам нужно передать данные и выставить ногу P2 в высокий уровень чтобы включить строб. Потом передать данные и выставить P2 уже в ноль, т.е. строб мы выключаем. А для этого нам надо разобраться, что такое шина I2C и с чем её едят.


Шина I2C


Откровенно говоря, не люблю я её. Использую только там, где нет альтернативы. Скорость небольшая, ёмкость линии ограничена 400пФ, в результате длина линии очень маленькая. К тому же сама суть протокола имеет существенный недостаток, об этом позже. Для каждого готового устройства приходится вручную подбирать номиналы подтягивающих резисторов. В этом плане SPI гораздо удобнее и круче, хоть и требует минимум 3-х ног. Ладно, к сути.


I2C - это асимметричная последовательная шина, использует две двунаправленные линии. Т.е. данные передаются последовательно в одном направлении. Обе линии подтянуты к питанию. Шина типа “Ведущий-Ведомый”. Теоретически возможно ситуация, когда хоть все устройства могут быть как ведущим, так и ведомым. На практике реализовать это почти невозможно.


Для понимания работы, надо сначала запомнить правила:


- Данные на линии SDA могут меняться только при низком уровне на линии SCL

- Пока на линии SCL высокий уровень, на линии SDA данные не меняются

- Утрируя, есть три состояния: СТАРТ, СТОП и передача данных.

- Формировать сигналы СТАРТ и СТОП может только ведущий, даже в случае приёма им данных от ведомого

- Адрес ведомого устройства состоит из 7-ми бит.


Сигнал СТАРТ - это перевод линии SDA в низкий уровень при высоком уровне линии SCL.


Сигнал СТОП - перевод линии SDA в высокий уровень также при высоком уровне SCL.


Т.о. для начала передачи данных ведомогу, ведущий формирует сигнал СТАРТ. Все ведомые устройства на линии начинают слушать. Затем ведущий выстреливает адрес ведомого, с которым он хочет поговорить и сажает SDA на ноль. Адрес этот, как видно по картинке выше, занимает старшие 7 бит, а последний бит задаёт читаем мы данные или пересылаем. Если устройство на линии есть, оно удержит линию SDA в низком уровне, это значит, что оно готово общаться. Тоже самое и по окончании приёма данных. По окончании передачи ведущий формирует сигнал СТОП.


Вот тут и кроется главная проблема шины I2C. После передачи данных, если ведомый занят, он может продолжать удерживать линию SDA. Ведомый также может удерживать и SCL, если он не успевает обрабатывать данные, т.е. ведомый может снижать скорость передачи данных. По стандарту, устройства должны управлять линиями по схеме Open Drain. И даже если какое-то устройство монопольно займёт линию, другое сможет её сбросить. Теоретически. На практике же, если, например, ведомый подвис и держит линию, а мы поднимаем её на ведущем, оживить ведомого порой можно только reset’ом. Там вообще такие бывают дичайшие комбинации, что однажды даже пришлось прокидывать отдельную линию RESET для ведомых устройств и периодически их дергать.


Итак. Более менее и в общих чертах мы разобрались с I2C. На wiki есть неплохая статья, да и вообще погуглите, шина непростая, я дал лишь общую информацию для понимания вопроса.

Приступаем к написанию библиотеки


Вот и настал момент, когда мы почти готовы написать первые строки кода. Давайте сначала посмотрим, как устроены другие библиотеки. Мы же ленивые и надеемся обойтись малой кровью.


Откроем классическую Arduino LiquidCrystal_I2C. Просто бегло пройдём по ней глазками. Не знаю, как у вас, у меня сразу глаз цепляется за несколько вещей:


- Используются аппаратные задержки

- Куча однотипных функций

- Нет никаких оптимизаций по экономии потребления памяти

- Нет контроля ошибок

- Нет вменяемых комментариев


Если мы просто пороемся на GitHub в поисках библиотек для STM32, почти все они будут на основе этой же LiquidCrystal_I2C. С теми же недостатками. Я не буду глубоко туда влезать, я просто сделаю всё по-своему.


Итак, составим требования к нашей библиотеке:


- Никаких аппаратных задержек

- Использовать DMA для передачи данных

- Минимум функций, максимально выносить всё в #define

- Максимально экономим память

- Каждое обращение к дисплею должно контролироваться


Создаём проект


Для начала надо создать проект. Я уже написал инструкцию, как правильно настроить STM32CubeMX у себя в блоге, не буду повторяться тут. Полностью проект с уроком доступен в моем репо на GitHUB.


Отмечу только, что урок написан для отладочной платы на STM32F303VC. У меня сейчас нет под рукой STM32F103C8, так что всё проверял на STM32F3DISCOVERY. Но адаптировать под любую другую плату можно без особых проблем.


Дальше, конечно, мы можете взять готовую библиотеку, я её выложил на GitHub. Я вкратце напишу, что я делал.


Создадим два файла:

Inc/lcd_hd44780_i2c.h
Src/lcd_hd44780_i2c.c

Для начала написания кода, нам бы вообще понять, что делать в реальности. Для этого нам надо сделать две вещи: включить дисплей и послать на него пару символов. Открываем даташит дисплея, ищем описание процедуры инициализации.

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Отлично! Всё написано по шагам, с таймингами и даже биты указаны! Но мы любопытные и хотим сразу знать, что же битики значат, чтобы сразу заполнить заголовочный файл #define'ами. Вспоминаем про "Table 6: Instructions". Там прям идеально, с комментариями, расписаны все биты команд.


Открываем наш заголовочный файл и предварительно накидываем:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Это та самая нудная часть работы, о которой я говорил. Внимательно смотрим в табличку, двоичный код переводим в HEX. Поясню на примере:


Инструкция Display on/off control требует всегда выставленного бита DB3. Открываем калькулятор, вводим двоичное 1000 и получаем 0x08 HEX.


В самой инструкции есть три команды:


- Display on/off

- Cursor on/off

- Blinking of cursor position character


Калькулятором высчитываем их HEX и будем их потом суммировать с LCD_BIT_DISPLAY_CONTROL.


Биты RS, RW, E и Backlight относятся к PCF8574, так что не забываем прописать и их.


Позже аналогичным способом напишем и остальные #define.


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


Но теперь мы задумались и пришли к выводу, что нам нужно где-то хранить те параметры, что мы уже отправляли на дисплей. Также надо хранить данные шины, параметры дисплея и прочее. Для этого мы создадим структуру:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Обратите внимание. В этом struct мы храним не саму структуру для I2C, а лишь указатель. Т.о. мы не дублируем данные и всегда под рукой их состояние.


Судя по алгоритму инициализации, первые этапы уникальны и можно реализовать их тупо отправляя данные через базовые функции HAL. Их мы реализуем в функции lcdInit().


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


Посмотрите реализацию в уже готовой библиотеке. В чём фишка. Для того, чтобы выдавать строб, мы дважды шлём первую партию 4-х бит. Третьим байтом шлём младшие 4-бит и закрываем строб.


И вот, что получается на деле:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

При таком раскладе и скорости шины I2C в 100кбит, ширина строба ~180мкс, пауза между стробами ~90мкс, и пауза между парными стробами ~530мкс. По идее, и я так думал предварительно, можно не удлинять строб на два байта, обойтись одним. Но на деле оказалось, что 90мкс мало для ширины строба, но достаточно для паузы между стробами. Похоже, что кварц в дисплее работает на более низкой частоте, чем положено по даташиту. Как говорил - Китай такой Китай =( А может мой дисплей дурит.


Также можно сократить длинную паузу раза в два, для этого есть два способа:


- Использовать прерывания и циклом фигачить побайтово прямо в регистр. Но это постоянные прерывания с обработчиками, на больших данных будут блокировки. А я этого ой как не люблю. Я предпочитаю отправить данные через DMA и забыть о них. И начать заниматься другими делами, пусть МК сам разруливает.


- Либо создать большущий буфер на отправку, для 20 символьного дисплея это будет порядка 120 байт. Надо будет просто подготовить данные в буфере и отправить одним выстрелом в DMA. Но я решил экономить память.


Но нас интересует вопрос, я так ругал Ардуиновскую библиотеку, а есть ли выигрыш? А вот смотрите, что показывает LiquidCrystal_I2C:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Комментарии излишни. Но ведь я учу вас критическому мышлению, не так ли? Что если оптимизировать код библиотеки Ардуино? Да! И я уверен, получится значительно улучшить параметры передачи. Делайте, думаю стотыщпятьсот людей вам скажут спасибо. Ведь я к чему это всё говорю. К тому, что в этом и есть беда Ардуино - такой вот код, где никто не думает об оптимизациях. Причём ведь делает вид, что всё согласно даташиту. Например, зачем нужны задержки между передачами в 50мкс, если в реальности между стробами 500мкс??


У пытливого ума опять же возникает вопрос, а есть выигрыш на большой передаче? А вот смотрите, сверху STM32, а снизу LiquidCrystal_I2C, данные одинаковые, процедура инициализации тоже:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост
STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Итог: STM32 83мс, LiquidCrystal_I2C 122мс. Повторю, если использовать прерывания или готовый буфер вместо чистого DMA, можно получить ещё больший выигрыш, думаю вполне реально сократить это время до 60мс. Но надо ли? С таким дисплеем и его откликом это уже за гранью добра и зла =)


Что ещё интересного в библиотеке


Я написал одну единственную функцию, которая занимается командами. Это функция lcdCommand().


Она занимается как установкой параметров, так и снятием. В качестве входных параметров, у неё команда и флаг - снять или выставить команду. Оба параметра - это нумерованные списки LCDCommands и LCDParamsActions.


Обратите внимание, никаких if/else. Всё сделано на Switch/Case. Несмотря на то, что их аж три штуки и приходится как минимум дважды проверять команду, работает это невероятно быстро. И причина тому - Бинарное дерево, в которое компилятор транслирует наш код. По сути, там всего два узла, так что поиск происходит за несколько тактов.


Конечно, вы можете использовать и запись типа if (command == LCD_DISPLAY), это также будет откомпилировано в бинарное дерево, но такой код читается хуже.


В результате мы получили возможность через #define определить прототипы функций, с коротким написанием и удобным чтением:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

А вообще, совет. Там, где у вас чётко обозначенные варианты, использовать switch/case, там, где необходимо сравнивать величины, использовать if/else. И не стесняйтесь нумерованных списков enum - они занимают очень мало памяти. Компилятор сам подбирает тип, но всё также, как с обычными целочисленными переменными, чем больше список, тем больше разрядность.


Почему не проверяем готовность дисплея, как в даташите


А потому, мои дорогие, что в случае с I2C это лишено смысла. Посмотрите на реальную передачу. На один только запрос уходит минимум 1 байт плюс ещё байт на адрес. Итого 180мкс. Для проверки готовности мы сначала должны выставить R/W в 1, потом еще щелкать стробами и внутри 1-го строба проверять бит BF на ноге DB7. Посчитали? Это при том, что по документации занят дисплей от 37мкс до 1,52мс. Проще просто использовать трюк с I2C.


Что можно придумать с русскими символами


У нас есть только возможность загрузить своих 8 символов. Я с этим сталкивался и, скажу, это нелегкий выбор =) Для этого в дисплее есть доступный EPROM на 8 ячеек. Каждая в каждую ячейку можно записать символ из 8 строк по 5 точек в каждой. Соответственно, это массив из 8 байт, где младшие 5 бит и есть наши точки. На самом деле, последняя строка - это курсор, так что, если уж соответсвовать стандартам, на символ можно использовать 5х7 точек. Вот схема из даташита (Example of Correspondence between EPROM Address Data and Character Pattern (5 × 8 Dots)):

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Например, символ Д в HEX будет такой:

uint8_t symD[8] = { 0x07, 0x09, 0x09, 0x09, 0x09, 0x1F, 0x11 }; // Д

Соответственно загружаем его в CGRAM функцией:

lcdLoadCustomChar(0, &symD);

и выводим функцией:

lcdPrintChar(0);



Ну а как просто вывести текст?


Это элементарно. Нужно просто выставить курсор и отправить код символа в дисплей. Он сдвинет курсор на следующую позицию автоматически, а мы следом шлём следующий символ. И т.д.


Код символа в C/С++ определяется, если взять его в одиночные кавычки, например, 'B'. Либо просто перебором берём из строки &data[i]. Реализацию можете посмотреть в исходнике.


В готовом виде это:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Обратите внимание. Мы отправляем в функцию не строку, а указатель на массив uint8_t. Т.е. мы, во-первых, создаём строку в памяти, во-вторых, преобразуем строку в unsigned int, в-третьих, отправляем указатель на неё. Это, конечно, вариант для примера. В боевых устройствах использовать такую запись плохой тон. Т.к. во-первых, мы используем динамическое выделение памяти, что само по себе в условиях крайне её ограниченности не айс. Лучше стараться заранее выделить память под некоторые переменные. А во-вторых, приходится вручную пересчитывать размер строки. Так что хорошим тоном будет примерно так:

STM32 от Булкина. Урок 2: Пишем библиотеку сами для STM32 Stm32, Урок, Длиннопост

Немного о комментариях в коде


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


Конечно, глупо комментировать каждую строчку. Но хорошим тоном, по моему опыту, являются:


Перед каждой функцией писать стандартный заголовок с тегами brief и note. В них стоит описать что это за функция и как она работает. Там же дать описания переменных и что она возвращает. Во многих современных редакторах есть плагин типа Docblockr. Просто перед функцией пишете /** и плагин сам создаёт отформатированный заголовок, вам нужно только дописать ручками несколько строк.

Давать отсылки на переменные из других файлов и документацию

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

Добавляйте комменты для выделения этапов и всяких неочевидных вещей

Я сейчас дописываю документацию к библиотеке, читать её можно будет тут.


Напоследок


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


Я постарался заострить внимание на ключевых моментах. Параллельно показать какие-то банальные фишки, которые многие боятся использовать. Например, указатели.


Вам же стоит открыть готовую библиотеку и посмотреть на её устройство собственными глазками. Она элементарная, проблем с чтением кода быть не должно. Но есть моменты, которые стоит соблюдать. Все их зацепить я не могу. Спрашивайте в комментах, постараюсь отвечать подробно.

Сообщество Ремонтёров

7.3K постов42.1K подписчиков

Добавить пост

Правила сообщества

ЕСЛИ НЕ ХОТИТЕ, ЧТОБЫ ВАС ЗАМИНУСИЛИ НЕ ПУБЛИКУЙТЕ В ЭТОМ СООБЩЕСТВЕ ПРОСЬБЫ О ПОМОЩИ В РЕМОНТЕ, ДЛЯ ЭТОГО ЕСТЬ ВТОРОЕ СООБЩЕСТВО:


Посты с просьбами о помощи в ремонте создаются в дочернем сообществе: https://pikabu.ru/community/HelpRemont

К публикации допускаются только тематические статьи с тегом "Ремонт техники".

В сообществе строго запрещено и карается баном всего две вещи:

1. Оскорбления.

2. Реклама.

В остальном действуют базовые правила Пикабу.

Вы смотрите срез комментариев. Показать все
1
DELETED
Автор поста оценил этот комментарий

Автор, ну без прерываний не так интересно, в них вся мякотка. Но гайд хорош, спору нет.

раскрыть ветку (8)
1
Автор поста оценил этот комментарий

В таком серьёзном режиме с прерываниями работаю с RS485+Modbus ну и с фазовым контролем мощности. В остальных вещах стараюсь использовать DMA и выпуливать/получать всё одним пакетом.

раскрыть ветку (7)
DELETED
Автор поста оценил этот комментарий

Ну а разве нельзя чтобы прерывание от и2ц само дергало дма без участия процессора?

раскрыть ветку (6)
Автор поста оценил этот комментарий

Как это? DMA собсно и придумано, чтоб в обход это делать. Просто когда я говорю про чистые прерывания, имею ввиду, как, например, с Modbus. Типа такого: https://gist.github.com/firebull/f961a4f5376899c5830cb1b95d2...


Включаем прерывание и с каждым байтом что-то делаем. Я так не люблю просто. Ну коробит меня. Считайте это моей фобией =) По большому счёту нет каких-то проблем, если не делать в прерываниях серьёзную работу. Если двигать указатели, то вообще проблем никаких, всё за один такт. Но там, где можно использовать DMA, я его использую. С Modbus использовать DMA на приём можно, но гиморно, т.к. надо соблюдать тайминги между фреймами. А на отправку, если фрейм умещается в 254 байта, использовать DMA милое дело =)


Опять же - какая задача. У меня есть один контроллер, который занимается довольно серьёзными математическими расчётами.  Куча переменных с плавающей точкой. Графики, прогнозирование и прочее. Куча датчиков ADC. И вот меня коробит, что это всё хозяйство будут постоянно прерывания дёргать. Но приходится мириться и подходить к этому очень аккуратно, чтоб в прерываниях вот ничего лишнего не было. Экономлю буквально до такта.


Есть еще фазовый регулятор на два устройства. Он постоянно под прерываниями из-за датчика нуля и таймеров, которые щёлкают симисторами. Раз в секунду получает запрос по Modbus и выпуливает ответ. Работает отлично.

раскрыть ветку (5)
DELETED
Автор поста оценил этот комментарий

Из даташита на I2C 40х серии: "Depending on specific device implementation DMA capability can be available for reduced CPU overload. 1-byte buffer with DMA сapability". То есть, я так понимаю, дма шлет байт через и2ц, и2ц генерирует прерывание по завершению передачи байта и тем самым пинает дма, чтобы тот еще один байт подавал. Но в подробности не вдавался.


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

раскрыть ветку (4)
Автор поста оценил этот комментарий

Всё так. Но я не об этом. И все события по прерыванию ловлю и как раз и говорю, что нефиг там ничем заниматься кроме сдвига адреса буфера, например, или выставить флаг или уведомить задачку FreeRTOS. Например, если касаться прерываний того же I2C, то там вообще СИЛЬНО рекомендуется выставлять максимальный приоритет. И на практике, лучше вообще ставить непрываемый приоритет, чтоб другое прерывание не вмешалось. Иначе задница. Зависон словить как нефиг делать. Поэтому касаясь I2C я всегда стараюсь делать с ним как можно меньше телодвижений. Минимум три прерывания там необходимы - два на DMA и один общий на саму шину. Вот и пусть они сами междусобойчик делают =) Я туда не вмешиваюсь, просто смотрю на флаг занятости шины. Освободилась? Смотрим, что получили. Надо отправить? Закинул в DMA и иди гуляй.


С таймерами другая песня. Там все прогнозируемо. И надо помнить, что настроим таймер, например, на 1мкс и делая какую-то задачу в самом прерывании, нарываешься на проблемы. Ведь при такой задержке и частоте МК в 72Мгц, у тебя всего-то несколько десятков тактов в запасе =) И лучше искать какие-то другие решения.


Так что конечно, я не занимаюсь тупо опросом всего и вся по кругу =) Прерывания на полную катушку. Просто если есть возможность использовать DMA - использую.

раскрыть ветку (3)
DELETED
Автор поста оценил этот комментарий

Понятно) ну системный таймер на 1мкс - это мощно, я обхожусь 1мс) Ну да у каждого свои временные рамки на реагирование.

раскрыть ветку (2)
Автор поста оценил этот комментарий

Нет, какой системный, боже упаси!!! Таймер для OneWire, например.

раскрыть ветку (1)
DELETED
Автор поста оценил этот комментарий

А, ну тогда понятно))

Вы смотрите срез комментариев. Чтобы написать комментарий, перейдите к общему списку