Измеритель уровня - SPI STM32

May 01, 2020 02:49



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

Дополнено...



Про измеритель уровня здесь уже был пост. Теперь все это можно начинать воплощать в железе. Для отвлечения от надоевшей работы над двигателем я сел паять платы. А разгуляться здесь есть где, одних резисторов в цепях сегментов целых 100 шт.!





Шаг сегментов - 2.54 мм. В ряд установлено 5 матриц по 10 сегментов каждая. Отверстия для выводов идут через 2.54 мм, никаких зазоров между матрицами не предусмотрено. Иначе «сбой» шага сегментов был бы заметен. Но так индикаторы с трудом помещаются. Если измерить длину корпуса, то в верхней части она в точности равна 25.4 мм, а вот ниже корпус расширяется. Похоже, что компаунд внутри немного распирает корпус. Буквально на 0.2 мм. Пришлось торцы слегка обработать на большом плоском напильнике.

Каждую линейку решил собрать из индикаторов трех цветов: сначала 30 зеленых сегментов, затем 10 желтых, затем 10 красных. Пришлось подбирать для каждой группы свои резисторы, чтобы выровнять яркость. Самыми плохими предсказуемо оказались зеленые, для них резисторы выбрал 270 Ом. Самые яркие - желтые, подошел резистор 560 Ом. Красные чуть менее яркие, но за счет меньшего падения с тем же резистором 560 Ом ток получается чуть больше, чем у желтых, в итоге яркость очень близкая. Получилось вполне нормально, хотя зеленые светят чуть хуже. Но поднимать ток выше я не стал, жалко электричества. Все вместе индикаторы будут регулироваться по яркости общим стабилизатором.

Между линейками расположены светодиоды подсветки шкалы, а снизу - подсветки трафаретов. Общая яркость светодиодов будет регулироваться отдельным стабилизатором. Выбрал SMD диоды в корпусе 3528. Тут самыми яркими оказались как раз зеленые. Они просто пылают. Поставил резисторы 1 кОм, чтобы хоть как-то выровнять яркость. Желтые оказались хуже всех, пришлось поставить 390 Ом. Красные - средние, резистор взял 560 Ом. Короче, все наоборот по сравнению с сегментами матриц. Забавно, что два зеленых диода, которые докупал чуть позже, по эффективности несколько отличаются. Тоже суперяркие, но полностью гаснут при чуть большем напряжении, т.е. имеют более высокое падение. При рабочем токе чуть тусклее, но мириться можно. Мораль - светодиоды лучше брать из одной партии, особенно суперяркие.

Измеритель уровня состоит из двух плат - платы индикации и платы процессора. Пока плату процессора собирать не стал, потому что не знаю, какой впаять процессор. Слабо представляю необходимую вычислительную мощность. Если не хватит STM32F100 на 24 МГц, тогда поставлю STM32F103 на 72 МГц. А пока плату индикации подключил к Discovery.

На днях получил любопытное сообщение на форуме. Кто-то наконец попробовал перекомпилировать исходники лабораторного блока питания, которые уже года 4 лежат на сайте. И они не заработали! Вернее, заработали, но с дефектом. На LED-индикаторы выводится то, что надо, но одновременно хаотически подмаргивают другие сегменты. Все остальное в программе нормально работает. Если ту же самую плату прошить готовым hex, который выложен, то все работает нормально. Получается, чуть более свежая версия компилятора делает неработоспособный hex.

Это меня удивило, никаких трюков в программе нет, она не должна быть чувствительна к компилятору. В свое время компилил с помощью версии IAR EWARM 6.5. Как раз у меня оказалась установлена версия 7.3, попробовал ей. И получил тот же глюк - хаотичное мигание сегментов. Подозрение сразу пало на модуль, который работает с SPI. А там был такой код:

//----------------------- Запись байта через SPI1: ---------------------------

void TSreg::SpiWr(uint8_t d)
{
//ожидание освобождения буфера:
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = d;
}

//-------------------------- Загрузка регистров: -----------------------------

void TSreg::operator = (uint32_t Value)
{
Pin_LOAD = 0;
SpiWr(BYTE1(Value));
SpiWr(BYTE2(Value));
SpiWr(BYTE3(Value));
//ожидание окончания передачи:
while(SPI1->SR & SPI_SR_BSY);
Pin_LOAD = 1;
}

Если перед Pin_LOAD = 1 добавить небольшую задержку, все начинает работать. Посмотрел листинг, новый компилятор выдал местами другой результат. Например, функцию SpiWr() заинлайнил. Поэтому задержки могли поменяться.

Но это следствие какой-то другой ошибки. В теории, все должно работать. Флаг BSY снимается по последнему фронту тактового сигнала, сразу после этого можно формировать сигнал LOAD. Достаточно лишь выдержать минимальное время удержания для регистров (оно порядка 20 нс), а для этого достаточно задержки выполнения одной команды. Разборки с причиной проблемы заставили читать Reference Manual. Объем документации STM32 такой неприличный, что прочитать ее невозможно. Не останется времени что-то делать. Читать приходится лишь описание регистров. Возле этого описания обычно помечены важные моменты, но насчет SPI_SR_BSY ничего интересного там нет. Зато в глубинах документации нашлась цитата: «During discontinuous communications, there is a 2 APB clock period delay between the write operation to SPI_DR and the BSY bit setting. As a consequence, in transmit-only mode, it is mandatory to wait first until TXE is set and then until BSY is cleared after writing the last data». Получается, с анализом только флага BSY можно получить сигнал LOAD в начале передачи последнего байта, а не после ее окончания. Добавил проверку TXE, все заработало.

while(!(SPI1->SR & SPI_SR_TXE));
while(SPI1->SR & SPI_SR_BSY);

В проекте измерителя уровня тоже используется цепочка регистров 74HC595, висящая на порту SPI процессора STM32. Короче говоря, та же ситуация. Воспользовался полученным опытом, сразу поставил правильную проверку. Сегменты подключены к регистрам хаотично, как было удобней разводить. Пришлось создать большой массив констант и наполнить его структурами в виде пар чисел «Номер регистора - номер выхода».

const Seg_t SegsL[SEGS][CHANS] =
{
{ {U1, Q2}, /*сегмент L01*/ {U1, Q7}, /*сегмент R01*/ },
{ {U1, Q1}, /*сегмент L02*/ {U1, Q6}, /*сегмент R02*/ },
{ {U2, Q0}, /*сегмент L03*/ {U2, Q4}, /*сегмент R03*/ },
{ {U2, Q3}, /*сегмент L04*/ {U2, Q5}, /*сегмент R04*/ },
{ {U2, Q2}, /*сегмент L05*/ {U2, Q7}, /*сегмент R05*/ },
{ {U2, Q1}, /*сегмент L06*/ {U2, Q6}, /*сегмент R06*/ },
{ {U3, Q0}, /*сегмент L07*/ {U3, Q4}, /*сегмент R07*/ },
{ {U3, Q3}, /*сегмент L08*/ {U3, Q5}, /*сегмент R08*/ },
................................и т.д.

Протестировал - все нормально. Написал несколько методов для доступа к сегментам разными способами - можно рисовать сразу столбик, можно зажигать отдельный сегмент, можно и то, и другое вместе. Отдельно сделал управление светодиодами трафаретов и подсветкой шкалы.

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

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

Появились подозрения на какие-то аппаратные проблемы. Конечно, я еще раз перепробовал всякие варианты управления сигналом LOAD, раз недавно с ним были проблемы. Никакого влияния не заметил. Можно делать задержку хоть секунду, ничего не меняется. Если снижать скорость SPI, то сначала тоже ничего не меняется, но если снизить в 16 раз, то глюк исчезает. Весьма странное поведение.

Выяснил, что закономерность следующая: если одновременно зажигается больше 90 светодиодов, тогда глюк проявляется. Он всегда в точности один и тот же - не горят два нужных светодиода и горит один ненужный. Все они подключены к одному регистру, который последний в цепочке. Повторяемость глюка 100%. Если зажигать меньшее количество светодиодов, причем в любой комбинации, глюка нет.

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

Попытка посмотреть, что происходит с сигналами, ничего не дала. Стоит прикоснуться щупом осциллографа к входу регистра CLK или LOAD, как глюк исчезает. И это даже со щупом 1:10. Но исчезает только при касании выводов последних регисторов в цепочке. Если стать на регистры в начале, осциллограф не влияет. Ну а сигналы - выбросы есть, но ничего сильно криминального.

Анализ содержимого регистров показал, что наблюдаемая картина может быть следствием лишнего сдвига, но только в последнем регистре в цепочке. Получается, в момент перезаписи из сдвигового регистра в выходной регистр 74HC595 на линии CLK появляется какой-то мусор, который дает лишний сдвиг. Переключение большого количества выходов может создать условия для снижения помехозащищенности, а сама помеха на линию CLK может наводиться со стороны линии LOAD, они на плате идут рядом. Если так, то можно пойти двумя путями: или снизить уровень помех со стороны LOAD, или снизить чувствительность к помехам у CLK. Обе эти задачи решаются небольшими конденсаторами на землю. Действительно, установка 220 пФ на любой из сигналов вблизи последней микросхемы в цепочке проблему решает. Менее рискованно будет поставить конденсатор на LOAD.

Можно пойти и другим путем - добавить резистор последовательно в сигнал LOAD. Последовательное включение 470 Ом ситуацию улучшает, но не решает проблему полностью. Пробовал включать низкую скорость для пина STM32 - не увидел никакой разницы.



Оказалось забавно, что в цепочке из 14 регистров лишь последний нормально работать не хочет. Разводку платы в файле подправил, добавил конденсатор на сигнал LOAD в конце цепочки регистров. Но это решение не очень нравится. Даже не знаю, что в этой ситуации было бы самым правильным. Последовательные резисторы? Согласование и терминирование сигнальных линий? Или все же небольшие конденсаторчики на землю? Это ведь проверенное годами решение.

Дополнение.

Любая проблема эффективней решается не именно тогда, когда ее решают, а в любое другое время, когда решение идет в фоновом режиме. Вероятно, благодаря Dithering мыслей шумовой составляющей (о чем я как-то писал здесь под тегом "душа"). Главное было понять механику глюка. Анализ содержимого регистров указывал на то, что в момент перехода сигнала LOAD из 0 в 1 происходит лишний сдвиг информации. Это означает, что на вход SCLK действует помеха. У меня исторически сложилась работа SPI в режиме CPHA = 0, CPOL = 0. Дело в том, что в некоторых устройствах у меня питание регистров может отключаться. Чтобы выходной ток портов не питал их через защитные диоды, я выбрал режим SPI, когда в режиме покоя SCLK = 0. Другие выводы я тоже устанавливал в 0. Но в данном случае такой режим походит не лучшим образом.



В то время, когда LOAD собирается прыгнуть вверх, SCLK сидит в нуле и готов принять помеху. На нем появляется "иголка", которая вызывает паразитный сдвиг. Если поменять режим работы SPI, чтобы после передачи байта SCLK оставался в 1, то он не будет восприимчив к такой помехе. Проверка показала, что так и есть. Глюк ушел и не возвращается. Хочется верить, что навсегда.

reel-to-reel, Электроника-004, electronics, магнитофон, катушечник

Previous post Next post
Up