Электроника и радиотехника => Цифровая техника => Тема начата: Shaman от 23 Сен., 2020, 19:16
Название: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 23 Сен., 2020, 19:16
Захотелось мне сделать свой космический модуль... Задний фонарь для велосипеда с указанием поворотов и стоп-сигналом. Поскольку лепить такое на ардуино будет слишком громоздким и есть потребность поглубже разобраться в написании программ на Си решено было использовать голый микроконтроллер. Потому как отдельных светодиодов зажечь нужно много (27) решено было использовать сдвиговые регистры 74HC595 (4шт.). Микроконтроллер был взят Atmega328P как самый доступный. Поскольку в AVR я новичок то решил разбить задачу на этапы и первый из них это написать процедуру, которая будет из некоего буфера экрана брать данные и вносить их последовательно в регистры. Немного побившись головой об стол получил вот такой код: Немного побившись головой об стол получил вот такой код:
[свернуть]
Он даже скомпилировался значит явных ошибок нет. Осталось выяснить как его можно проверить на железе или в эмуляторе. А пока уважаемые гуру не могли бы вы проверить меня на ошибки?
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 23 Сен., 2020, 23:00
Насколько я понимаю, схема подключения регистров выглядит примерно так Подсоединение каскада регистров
Схема-регистров-595.png
[свернуть]
Для начала надо точно понимать, что собой представляет 74HC595 (она же ИР52 в отечественной системе обозначений). Думаю, с этим проблем нет, но я всё-таки нарисую, чтобы было куда пальцем тыкать (это важно т.к. устраняет неопределённости, когда один думает об одном, а другой о другом и в итоге друг друга не понимают).
ИР52 по внутреннему строению состоит как бы из двух микросхем. Вот как-то так Внутри регистра
Схема-регистров-595-внутри.png
[свернуть]
Т.е. внутри у неё фактически два регистра, первый регистр сдвига, куда данные можно задвигать при помощи сигналов D (Data) и C (Clock). При этом процесс записи и сдвига (вообще, сдвиг - это тоже запись) происходит строго по фронту (переходу из 0 в 1) сигнала Clock. Чисто логически это можно изобразить так Работа регистра
Схема-регистров-595-сдвиг.png
[свернуть]
Такая связка двух микросхем в одном корпусе весьма удобна - поскольку выходной регистр "непрозрачный", то на его выходах всегда хранится информация, которая там запомнилась при переходе сигнала OC (по схеме Ready) из 0 в 1. Получается, что пока мы "двигаем" Data в регистре сдвига, на выходах Q0~Q7 информация "стоит как вкопанная" с прошлого раза. И только когда мы "дёрнем" OC, на выходах появится то, что мы только что записывали в регистр сдвига. Очень, очень удобно :)
Диаграмма записи всех 8 бит в регистр типа 595 рекомендую делать такого вида Диаграмма записи
Схема-регистров-595-процедура.png
[свернуть]
Что тут получается. Самое главное: опускаем сигнал Clock -> выставляем сигнал Data (очередной бит, который мы ходим задвинуть) -> поднимаем сигнал Clock. Делаем так столько раз, сколько бит нам нужно записать в регистр. В случае 4-х последовательно включенных регистров 595, сделать так надо 32 раза (есть исключения, но они связаны уже со схемотехникой. Вдруг, например, выводы Q4~Q7 самого правого (по схеме выше) регистра никуда не подключены - тогда их и заполнять не надо, а значит, сдвигов можно делать меньше).
Сигнал Ready рекомендую (именно рекомендую, т.к. фактически, опустить, т.е. сделать 0, его можно в любой момент) опускать ровно перед тем, как соберётесь записывать Data в регистр сдвига. А как всё запишете - поднимайте. Дело в том, что такое поведение сигнала Ready даёт возможность контролировать физически (осциллографом), когда у вас началась процедура записи, а когда закончилась, что облегчает ловлю багов (не хвалитесь - баги всегда есть, даже если вы их не видите).
Посмотрим код. Вот честно... каждый раз говорю, что Си, а тем более ++ я знаю весьма фигово. То ли дело - ассемблер :)
Цитата: Shaman#define _data 3 // устанавливаем пин
Наверное, не устанавливаем пин, а назначаем разряд порта. Да, тут надо аккуратно, потому что здесь есть одно место, порождающее глобальную путаницу при малейшей неосторожности (я тоже постоянно путаюсь). Биты порта нумеруются с 1 по 8. А вот разряды с 0 по 7. При этом 1-й бит порта - это 0-й разряд, второй бит - первый разряд и т.д. Я тоже буду путать(ся) в показаниях - имейте это в виду и следите за руками :)
про дефайны
При таком способе назначений (define) для работы с портами используется записи типа
Цитата: ShamanPORTC |= (1<<_clock)
Честно говоря, мне совсем не нравятся эти записи "1<<Clock". Считаю, что в этом месте надо чётко писать "Clock" без всяких 1<<
Для этого надо... всего лишь привести в порядок дефайн. Например, для Data это должно выглядеть так Это потому что на порт посмотреть как на биты, слева старший, справа младший, то он выглядит как бинарное число '00001000', т.е. '8' в нашем обиходе.
Да, '8' надо записать просто потому, что говорят, в Си, нет возможности записывать бинарные числа. Или врут? Я в коде вижу бинарную запись. Удивительно... Впрочем, можно (и может даже нагляднее) использовать такое определение Здесь записано, что 1 сдвинута логически влево три раза, т.е. на три разряда. Бинарно это выглядит так 00000001(bin) << 3 получаем 00001000(bin)Точно также определим "_clock" Если сделать так, то процедура изменения значения порта станет выглядеть так
[свернуть]
Алгоритм вроде верен, но на 100% ручаться не могу. Единственное, проход битов производится от 0 до 7, что означает, что первым будет задвигаться в регистр младший бит, затем старший, а на микросхемах выходы маркированы наоборот. Это совершенно ничего не значит, просто надо иметь ввиду, что Q7 микросхемы в этом случае соответствует младшему биту буфера (в проектах даже можно переименовывать имена выводов микросхем, чтобы они соответствовали их реальной логической роли). Да, разве что установку _clock в 0 я бы я бы делал не после установки _data, а перед. Но можно и так оставить, если между установкой _clock в "0" и установкой в "1" вставить какой-нибудь nop.
Для проверки работоспособности можно в CharBuffer записать какие-нибудь значения и поморгать (в цикле, естественно, не быстром)
зы: долго вспоминал, как выглядят логические опреаторы. В итоге Вики (https://ru.wikipedia.org/wiki/%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B_%D0%B2_C_%D0%B8_C%2B%2B) сдала все :)
зы:зы: Просвещаюсь дальше: оказывается, в Си нет типа "byte" (https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%82%D0%B8%D0%BF%D0%BE%D0%B2_%D0%A1%D0%B8). Похоже, что тип "char" - вполне нормально для данного места. Хотя если определить его как "unsigned char", то он видимо и будет полностью равноценен типу "byte", который я точно где-то использовал...
зы:зы:зы: С вашего позволения продолжу. Могу показать свой ассемблерный код, которым я делаю то же самое на этом же (точнее, на 168, но это те же штаны) процессоре. ↓ спойлер ↓
AsmCode.png
[свернуть]
Это один файл, просто я показал нужные места. Если чуть помедитировать, то станет видна та же картина: имеется буфер под выводимые на индикатор символы и процедура вывода этих символов, чуть украшенная ещё парой входов на "гасим всё" и "зажигаем всё".
И не стесняйтесь на комментарии того, что вы делаете в своём коде. Обильное комментирование - залог понимания кода, когда к нему вдруг придётся вернуться.
И вот как чисто физически, на осциллографе (весьма хреновеньком, надо сказать) выглядит бурст обновления "экрана". AsmCode_Burst_8_bytes.png AsmCode_Burst.png Синий луч - сигнал "Clock", жёлтый - сигнал "Rdy" (Ready, Latch, OC...). Data не показана - там не так явно. Частота процессора здесь 14,3 МГц, в пределах одного байта частота следования битов 1 МГц, а между байтами видна небольшая задержка в передаче - всё согласно программы: между байтами ведь выполняется несколько больше действий, нежели между битами.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 25 Сен., 2020, 16:01
Благодарю за столь подробный ответ! :o :)
С вашей помощью немного оптимизировал код. Сейчас он выглядит так:
[свернуть]
И работает
[свернуть]
Команды дерганья пинов решил полностью вывести в дефайны и сделать их 2-х видов выставление 1 и 0. В противном случае, даже при использовании ваших примеров, при использовании других выводов и портов либо я не смогу назначить им уникальные имена поскольку все эти варианты [/quote] означают что я всего лишь заменяю операцию сдвига бита или вообще номер бита уникальным именем, а эта операция используется по отношению ко всем портам. А вот мой вариант это уже использование конкретного бита на конкретном порту и буде возникнет необходимость поменять порт и бит нужно всего лишь исправить несколько строчек кода, а не шарахаться по всему листингу.
И да простят меня прожженные AVR-щики :), но я буду комментировать что выбираю я именно пины просто так привык со среды Arduino. Там оперирование идет именно пинами, а не абстрактными регистрами и битами. Просто подкорректирую формулировку дабы было понятно, что оперирование идет именно через регистры памяти привязанные к пинам и биты в них.
Сейчас буду думать в каком виде хранить данные выводимые на экран и как их передавать в буфер. Если подскажут идею не обижусь. ;) ;D
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 28 Сен., 2020, 21:01
Ну вот, а то всё "мама, мама"! ;) Вполне нормально, разве что потакание привычке называть разряд порта пином в будущем приведёт к непоняткам (видимо, ещё не сталкивались с другим клаcсом чипов, где вывод порта можно назначать на определённый пин).
Для раскладки экрана могу порекомендовать изобрести "шрифт". Подобно тому, как изобретают (назначают) "шрифт" всяким семисегментным индикаторам. Пусть, например, там будут "буквы": 1) габарит 2) стоп 3) влево 4) вправо, и ещё какие-нибудь (я слабо представляю, что там в реальности творится). По событиям контроллер будет выводить "буквы" в экранную область памяти и вызывать вот эту процедурку, которая заработала чуть выше. Я так думаю...
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 02 Дек., 2020, 12:01
Интересно, что есть у КМОП-серии аналогично работающая микросхема типа CD4094. Традиционная КМОП (не 74HC) серий 561, 1561 хотя и более медленная, но умеет работать в гораздо более широком диапазоне питающих напряжений, о трёх до 15 вольт. Если полистать старые журналы, то быстро найдётся устройства, где питание таких микросхем (всяких часов, автоматики и т.п.) производится батарейкой "Крона" или напрямую от бортсети автомобиля (12 вольт).
Кстати, фишка. Некоторые, вроде аналогично работающие микросхемы, отличаются типом защёлки на OC (Output Clock). У одних там именно защёлка (Latch), работающая по уровню сигнала (низкий - хранение, высокий - прозрачно), а у других по фронту (flip-flop).
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 01 Фев., 2021, 18:36
Доброго времени суток продолжаю клепать свой космический модуль. И сейчас уткнулся в проблему. Гуру помогите разобраться :) Для того чтобы сделать оранжевый свет на RGB светодиоде, ток на зелёном был просто ограничен (этот пиксель нужен только для оранжевого), а красный регулируется через шим. Поскольку его нужно светить ещё и м полную яркость. Шим подаётся на 13 ногу U2 и U4 каждая из которых отвечает за группу красных и синих светодиодов.
Fonar.jpg
Поясню, на рисунке 1 светододы сверху в низ синие, зелёные, красные. Зелёные подключены к U3 Синие слева и красные справа по 4 шт. к U2 Красные слева и синие справа по 4 шт. к U4 И 3 светодиода в центре к U5
Мне нужно получить вот такой эффект
[свернуть]
И вод здесь у меня возникли трудности Сейчас мигание после бегущих огней организовано просто перезаписью буфера экрана и задержкой через delay
кусок кода
[свернуть]
Но мне хотелось бы реализовать это через тот же 13 пин
И собственно вот. Не могу реализовать это с помощью простейших логических элементов.
Имеем. 1 вывод ШИМ 2 максимум 3 вывода микроконтроллера для управления коммутацией из.
Задача. Коммутировать Шим только на правую или левую часть. Возможность вместо шим или при её отсутствии подавать на вывод 13 0 или 1.
Дополнение. Если выключить шим командой TCCR1B &= ~(1<<CS10), то на выходе останется тот уровень, который был последним и управлять им через выставления бита порта нельзя.
Постарался написать как можно внятнее если расписывать дальше получается поток мыслей. Если что-то не понятно спрашивайте, дополню.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 02 Фев., 2021, 15:53
Что-то мне подсказывает (могу быть не прав т.к. до железа сейчас не дотянуться), что состояние пина, на который выводится ШИМ сигнал, можно контролировать, записывая в регистр счётчика таймера так называемое начальное значение (т.е. просто значение), с которого счётчик начнёт счёт, если вновь запустить его. Потому что то, что выводится на пин, зависит от содержимого этого счётчика, которое внутри сравнивается с содержимым регистра сравнения (цифровым компаратором), по результату чего и формируется состояние пина. Если в регистр счётчика записать нужное значение, то можно предопределить состояние пина.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 11 Фев., 2021, 00:29
Не помогает либо я не правильно пишу код пробую записать во весь регистр целиком
[свернуть]
и в каждый по отдельности
[свернуть]
Регистр сравнения устанавливаю командой OCR1A = 256 На 10-ти битном счетчике это середина На выходе после отключения рандомное состояние (читай последнее установленное до выключения)
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 12 Фев., 2021, 21:31
Честно говоря, в последние дни был пригружен по работе, поэтому в код не вникал. Но вот тебе картинка, может поможет
Включить-ШИМ.png
Для управления "жёстким" включить-выключить нужно две управляющие ноги. Нога "Б" включает 100% яркость. Нога "А" выключает. Но преимущество за ногой "Б", нога "А" не может выключить, если включена нога "Б".
Микросхема КР1564ЛЕ1, она же 74HC02. Одной микросхемы достаточно для управления двумя каналами (потому что в ней четыре логических элемента).
Да, ШИМ получается инвертированный, 100% заполнение на выходе контроллера соответствует минимальной яркости.
Нет ЛЕ1 - возьми универсальную микросхему всех времён и народов ЛА3
Включить-ШИМ-2.png
Немного изменится логика работы. Но может так даже удобнее, тут уж по вкусу...
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 08 Нояб., 2021, 17:53
Прошло и полтора года, но проект пока не умер и на данный момент находиться вот в такой стадии: Fonar.jpg
Код U1 (отвечает за управление индикацией)
[свернуть]
Код U7 (отвечает за кнопки и индикацию на руле)
[свернуть]
Видео с примером
[свернуть]
Видео чуть постарее текущей версии кода. На текущий момент добавлен переход от одного сигнала поворота на другой без выключения предыдущего и проверка передаваемого байта, а также отправка подтверждения о приёме.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 08 Нояб., 2021, 18:03
Вот, собственно, передачи байта и его проверки я хочу коснуться.
На текущий момент это происходит так: После того как байт сформирован взводится бит готовности и вызывается функция отправки оного.
↓ спойлер ↓
[свернуть]
Байт отправляется два раза и на принимающей стороне складывается в отдельные элементы массива буфера приёма, после чего проверяется на идентичность. И при совпадении записывается в массив статуса, а при не совпадении отправляется код ошибки, по приходу которого передающая сторона повторяет отправку. На передающей стороне повтор отправки пока не реализован.
↓ спойлер ↓
[свернуть]
Собственно почему проверка сделана так просто. Здесь объём передачи весьма небольшой и даже если следом за байтом передавать контрольную сумму, то объём передачи будет так же 2 байта, а на её вычисление тратятся ресурсы. Пробовал играться с битом четности, но всё, что смог сделать это его включить. Он передаётся, но принимающая сторона на него не реагирует или я поздно считываю реакцию когда бит паритета уже сброшен. Вызвать ошибку я пытался включив на приемнике проверку по нечётности битов.
А расписал я всё это ради вопроса. Такой вариант проверки имеет право на жизнь или есть полее элегантные решения? Просто хочу рассмотреть варианты, дабы не пришлось потом пол программы переписывать.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 08 Нояб., 2021, 18:19
Почему нет? Вполне имеет смысл как раз в силу уже указанной причины. Второй байт можно несколько видоизменить (с целью небольшого повышения степени обнаружения ошибки), например, поксорить с 0FFh (простая инверсия бит), а на принимающей стороне в качестве проверки поксорить между собой оба принятых байта. Если результат будет 0, то значит передача с высокой степенью вероятности верна, а если хоть где-то один битик выпал - результат будет не 0 (всё-равно будет вероятность, что два или более битиков выпадут так, что результат проверки даст "верно", но это справедливо для любых алгоритмов проверки в принципе. Вопрос только в вероятности таких ситуаций). Возможны и другие варианты, типа перепутывания битов, сложения с константой и т.п.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 08 Нояб., 2021, 22:54
Т.е. получается код будет выглядеть так?
Передающая сторона
[свернуть]
Принимающая сторона
[свернуть]
По крайней мере у меня так заработало. Но тогда на принимающей стороне нужно не XOR-ить (в СИ знак ^), а делать AND (в СИ знак &), или я путаюсь в терминах?
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 09 Нояб., 2021, 19:06
Таблица истинности XOR
A
B
A XOR B
0
0
0
1
0
1
0
1
1
1
1
0
Если поксоришь байт сам на себя получишь 0 Если поксоришь байт с 0FFh - получишь инвертированный побитно байт Если поксоришь байт на инвертированную копию - получишь 0FFh
Первый и третий варианты отличаются только тем, как смотреть на результат.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 10 Нояб., 2021, 09:23
Цитата: Slabovik от 08 Нояб., 2021, 18:19Второй байт можно несколько видоизменить (с целью небольшого повышения степени обнаружения ошибки), например, поксорить с 0FFh (простая инверсия бит), а на принимающей стороне в качестве проверки поксорить между собой оба принятых байта. Если результат будет 0,
Понятно, опять пролазит математика :) . Для меня 0 это 0 пусть и тоже информация. А 0xFF это 0b11111111, 255 что угодно только не ноль :) . Сравнивать конечно можно и 0xFF, но если пишут 0, я и пытаюсь его добиться. :)
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 23 Нояб., 2021, 00:25
У меня затык в коде, не могу разобраться с двоичной математикой. Есть вот такой кусок кода
[свернуть]
По задумке когда взведён бит STCE и последние 2 бита элементов 0 и 3 массива status не равны, происходит установка 2 элемента массива status в 1, а последние биты элемента 0 присваиваются элементу 3.
Это работает только для комбинации status 0 10000001 лог дебага
[свернуть]
из лога видно, что на втором круге программа в if ((status[0] & 3)^(status[3] & 3) != 0) уже не проваливается т.к. последние 2 бита 1: /t status = {10000001, 10, 1, 1} равны.
Но для комбинации status 0 10000010 это не так лог дебага
[свернуть]
программа всё равно продолжает проваливаться в if ((status[0] & 3)^(status[3] & 3) != 0) даже при условии, что 1: /t status = {10000010, 10, 1, 10} равны.
Я не понимаю, где ошибка. Когда проверяешь выражение if ((status[0] & 3)^(status[3] & 3) != 0) на калькуляторе оно работает ↓ спойлер ↓
Калькулятор_001.jpg
[свернуть]
Если кто-то видит, где ошибка ткните, пожалуйста, пальцем. P.S. Полный листинг во вложении
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 23 Нояб., 2021, 20:18
Смотрел-смотрел и тоже не понимаю, его он хочет. Но что-то надо делать. Возможно, вопрос в плоскости интерпретации косплейятором приоритетов, а может ещё что...
Я правильно понимаю, что если два младших (не последних !) бита различаются, то нужно выполнить операции?
Для начала, я бы пробовал преобразовывать выражение в эквивалентное. Например
if ((status[0] & 3)^(status[3] & 3) != 0)
можно изменить так
if (((status[0] & 3)^(status[3] & 3)) != 0)
лучше так
if (((status[0] ^ (status[3]) & 3) != 0)
а можно и так
if (((status[0] ^ (status[3]) & 3) > 0)
Чтобы проверить одинаковость бит ксорить не обязательно
if (((status[0] & 3) - (status[3] & 3)) != 0)
и т.д.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 26 Нояб., 2021, 15:16
Да правильно. Говорила мама учи математику... Вынес 3-ку за скобки, вот так: if (((status[0] ^ status[3]) & 3) != 0), заработало. Благодарю.
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Slabovik от 26 Нояб., 2021, 15:21
Ага, ну значит дело действительно было в интерпретации компилятором приоритетов операций. Мы думаем так, а компилятор работает не так :) Расстановка скобок дала прямое указание компилятору делать в нужном нам порядке :)
Название: Re: Как засветить светодиоды от AVR через сдвиговый регистр?
Отправлено: Shaman от 26 Нояб., 2021, 19:34
Цитата: Slabovik от 26 Нояб., 2021, 15:21Ага, ну значит дело действительно было в интерпретации компилятором приоритетов операций. Мы думаем так, а компилятор работает не так :) Расстановка скобок дала прямое указание компилятору делать в нужном нам порядке :)
Ну да, так тоже работает if (((status[0]& 3)^(status[3] & 3)) != 0) Неочевидная вещь для новичка, я привык, что в математике сначала выполняется то, что находится с лева от знаков равенства и попался не поставив скобку. Хотя так должно быть быстрее: if (((status[0] ^ status[3]) & 3) != 0)