Push-pull VS open-drain.
На картинке видно, что open-drain может быть с внутренней подтяжкой и без неё.
Если использовать подтяжку становится вообще непонятно чем он отличается от push-pull.
А теперь давайте подумаем, что будет если мы соединим два PP выхода и установим на них разные логические уровни?



Это и есть тот случай когда PP не может заменить OD.
А теперь представим другую ситуацию, есть шина данных, например, I2C у которой логическая единица не соответствует напряжению нашего МК и равна либо 1.8, либо 5 вольт. В обоих случаях, используя PP выход, подключить МК к такой шине не получится потому, что PP не может выдать ни 1.8, ни 5 вольт. Но это очень просто сделать, используя OD и внешний подтягивающий резистор.
What is Open Drain : Configuration & Its Applications
In order to have interference with the outside world using microcontrollers, it can be done through pins. These pins act as physical points for an integrated circuit where a PCB connection can be established. Each pin has its own circuitry, and it is configured to pins to allow interference with various kinds of circuits. And the most important point is to select the appropriate pin configuration in the design of an embedded system. Today, this article explains open-drain output configuration, how it is configured, its applications, and its advantages.
What is an Open Drain?
It is also called Open Collector which is a transistor having a connection with the ground pin. In a simple way, it behaves like a switch that gets connected or disconnected depending on the input signal. This open-drain configuration permits different devices to establish communication bi-directionally on one wire. This is generally considered as a mode where it provides only pull-down functionality. This is the basic open-drain definition.
Circuit and Working
In most of the integrated circuits, the observed output configuration type is open-drain. As a replacement for outputting a signal with a corresponding current or voltage, the output is fed to the base terminal of the NPN transistor, and the collector terminal is externally connected to the integrated circuit pin. The emitter terminal has an internal connection with the grounded pin. When the output circuit is in MOSFET configuration, then it is termed as open drain circuit and it operates in the same way.

Open Drain Circuit
In the working of open collector devices, they allow current flow either in the logic ‘0’ low voltage active state or logic ‘1’ high voltage non-active (high impedance) state. The open-collector devices generally function using an external pull-up resistor which allows the signal line to be in high phase up, so that to the device connected on the wire flows enough amount of current in order to pull the line to low and to the signal the start of a communication.
When all the devices which are connected to the signal wire are in a non-active state, then the pull-up makes the wire at a high voltage phase. When one or more devices function in the active state, then the voltage of the signal wire moves to low.
In the open drain working, the signal wire also functions in bi-directional which corresponds that the device can have both input/output signals on the wire at the same period of time. Along with the regulation of the pins state, which is connected to the signal wire, it can also recognize the level of voltage of the wire.
Even though the output of the open collector device is in the high state, the signal wire which is attached to it might be in low state because of the other device that is connected to the signal wire. So, with the bi-directional behavior, the open collector device can be useful for the interconnection of multiple devices on a single line.
Open Drain Configuration
The open-drain configuration is employed at the controller’s output port in order to lessen the output circuitry. When the output port is fixed at open-collector/drain state, then there is no additional equipment necessary for pulling down the pin’s output value.
In this configuration, it turns off entire pull-up transistors and only drives pull-down transistors of the port pin when the latch pin of the port is in logic ‘0’ state. In order to function in logic output, then the port is configured in a manner such that it has a resistor-connected VDD terminal and an external pull-up transistor. In this state, the pull-down approach is similar to a quasi-bidirectional method.
In order to employ analog input pin as high-impedance input and a comparator is enabled state, then that pin has to be in the configuration of open collector and the related register bit has to be in ‘1’ state.
In both of the scenarios, the point to be noted is that the open drain/collector is a part of the output transistor which is directly connected out to a pin that is on the external side of the integrated circuit.
The below picture represents the open drain configuration.
Types of Output Configurations in Comparison with Push-Pull and Pull Up Mode
In digital sensing applications, the crucial part is to select the suitable output configuration where the main options are open collector, push-pull and push-up. To know these configurations, let us know a few terms which are explained below:
Floating Pin – A pin is considered to be in a floating state when it is not connected to any other pins and also it is not led by any circuit in the IC. The voltage level of the floating pin is unclear and cannot be predicted. Also, when a high impedance pin is not led either by pull-up or pull-down circuits, then it is also called a floating pin.
High Impedance – A pin that has a high impedance essentially takes out its impact on the electrical circuit to which it is connected. It is usually not driven actively and is in a floating state except it is connected to any other external device.
Open Drain v/s Push Pull
Push-pull output configuration is the most implemented type where the name itself indicates that this configuration holds the ability to drive two different output levels. One is to sink current from the load section and the other is to source current to the load section. This configuration is implemented through switches. The two scenarios push and pull representations are shown below:

Push Pull Output
The open drain advantages and disadvantages in comparison with push-pull configuration are:
- It can be connected with multiple outputs which allow for multiple OR operation
- It can be switched between high/fewer voltages than the supply voltage
- It can directly drive loads those are ≤ 10 mA
- It requires very minimal power to operate as there are no external/additional pull-up resistors
- It has high functional speeds in the range of 10KHz
- It has stable high and low outputs
- It requires high power to operate when compared with push-pull because of drain via load resistor when the device is in ON condition
- The switching speed is minimal. It achieves faster switching speeds when minimal load resistor values are used
- It does not hold the ability to drive external loads
- It cannot combine output signals to multiple sensors to a single bus
On the whole, push-pull configuration is selected when there is the requirement of minimal circuitry, and an open collector configuration is chosen for the requirements of interfacing with various system voltages, load switching, and when multiple device outputs are to be connected to a single bus.
Open Drain v/s Pull Up
In order to know the pull-up concept, let us understand output possibilities in an open-collector configuration.
- In the open-drain configuration, the logic at the pin defines that it drives the device only to the logic ‘0’ state, and the other possible condition is a high impedance state where it requires the implementation of a transistor.
- When the drain terminal moves to open state (corresponds that the device is in ‘OFF’ condition), then the pin floats at a high impedance state.
- Making the pin to high logic (logic ‘1’) from logic ‘0’ needs additional components and for this external pull-up resistors are used. Upon the activation of the pull-up resistor, the input pin changes its state to Vdd.
Output mode in open-drain configuration simply does not have a PMOS transistor. When the switch is in the OFF state, the drain gets opened and output moves into a floating state. In this configuration, the pin is only pulled up but not pulled down. The open-drain configuration is of no use when it does not support pull-up functionality.
In order to make this configuration applicable in real-world scenarios, either an external or internal pull-up resistor has to be used. These days, all the microcontrollers are provided with a pull-up resistor for the output pin.
Applications
The applications of open drain include the following.
- The crucial application is in summing, weighting, and limiting both analog and digital logic.
- Used in vacuum fluorescent displays and in Nixie tubes
- Used in SCS-1 devices for the purpose of electrical signaling.
What is open-drain used for?
Open collector/drain presents high impedance either high or low voltage is applied to the gate terminal.
What is open drain and wired or?
The open-drain technique is used to establish communication between multiple devices bi-directionally on a single wire.
Is open-drain high impedance?
When the device does not use a bus, then the output of the open collector is in high impedance.
What is an open-drain transistor?
Open drain transistor represents that the output pin is led by only one transistor where it pulls the pin to only one voltage level usually to the ground.
Does open-drain need pull up?
Open-drain configuration requires a pull-up resistor so that the output will be properly at high.
What is an open-drain buffer?
The open collector/drain buffer is 74LVC1G07 IC with open-drain output. This buffer when connected with other open-collector outputs is suitable for the application of active low (wired-OR) or wired-AND functions.
Please refer to this link to know more about Analog Electronics MCQs.
So, this is all about the concept of the open drive. This article has clearly focused on explaining what is open-drain configuration, its comparison with push-pull and pull-up configurations, circuits, and applications. Also, know how an open-drain output configuration is used in driving LEDs?
Основы на пальцах. Часть 4
Но диоды, резисторы, транзисторы и конденсаторы это так, лишь обвязка. Особо на них не развернешься (нет, маньяки, конечно могут, но габариты устройств там будут феерические). Самое вкусное нас поджидает в микросхемах 🙂
Делятся они на цифровые и аналоговые. Для начала кратко пробегусь по цифровым микросхемам.
Миром правит цифра!
Краеугольным камнем цифровой схемотехники служит понятие нуля и единицы , понятие это совершенно условное , т.к. фактически нет никакого нуля и нет никакой единицы, есть лишь уровни напряжения – высокий и низкий, а также некий порог после которого данный уровень напряжения принято считать высоким или низким. Скажем все, что ниже 0.7 вольт считаем за низкий уровень, т.е. 0, все что выше 2.4 вольт высоким, т.е. единица. Между 0.7 и 2.4 вольта, когда не ясно какой уровень, это состояние совершенно неопределенное его нельзя оценивать как входную величину, иначе на выходе системы в таком случае будет непредсказуемый результат.
Сопротивление входов очень высокое, практически можно считать его бесконечным.
Выход в микросхеме бывает разных типов. Различают push-pull и open drain (в нашей литературе его называют Открытым Коллектором или ОК ). Отличие заключается в способе выдачи сигнала на выход. В Push-Pull выходе когда нужен низкий уровень, то выход тупо и беспрекословно замыкается на землю, имеющую нулевой потенциал, а когда высокий, то на напряжение питания.
В открытом коллекторе все несколько иначе. Когда нам надо получить низкий уровень, то мы сажаем ногу на землю, а вот высокий уровень получается подтягивающим резистором ( pullup ), который, в отсутствии посадки на землю и большого сопротивления висящей на выходе нагрузке, заводит на ногу высокий потенциал. Тут можешь вспомнить закон Ома и посчитать какое будет напряжение выхода на открытом коллекторе если подтягивающий резистор обычно порядка 1КилоОм, а сопротивление входа больше 1МегаОм. Тип выхода определяется из документации на микросхему, некоторые микрухи имеют программируемый выход, например, все контроллеры AVR. Исходя из этого становится понятен смысл регистров Port и DDR в контроллере AVR – они определяют тип выхода Open Drain + PullUp , Push-Pull или просто Open Drain .
О микросхемах дискретной логики И, ИЛИ, НЕ я рассказывать не буду, каждую описать, так это справочник не на одну сотню страниц будет. Да и постепенно они уходят в прошлое, вытесняемые контроллерами и программируемыми матрицами. Скажу лишь главное – работают они по жесткой таблице истинности, которую можно найти в соответствующем datasheet.
Аналог рулит!
Цифра может и правит миром, но я вот последнее время люблю аналоговую технику. Ряд задач автоматики и регулирования на аналоговых цепях сделать в разы проще, чем на микроконтроллере или цифровой логике. Основное отличие от цифровых микрух в том, что тут нет четких состояний , а вход и выход могут изменяться плавно от минус питания до плюс питания. Основой аналоговой схемотехники является операционный усилитель .
Адская вещь, скажу тебе. Содержит выход и два входа. Один вход прямой, другой инверсный. Внутри напряжения по этим двум входам математически складываются (с учетом знака входа), а результат умножается на коэффициент усиления и выдается на выход. Коэффициент усиления этого девайса в идеальном случае достигает бесконечности, а в реальном близок к сотням тысяч. В чем это выражается? А в том, что подаешь ты на вход скажем 1 милливольт, а выход сразу же зашкаливает под максимум – выдавая сразу напряжение питания. Как же тогда работать, если его зашкаливает от малейшего сигнала? А просто. Ну во первых зависит от задачи. Например если нам нужно сравнивать два сигнала, то один мы подаем на отрицательный вход, а другой на положительный. В данном случае выход нам покажет либо минимум напряжения, либо максимум, в зависимости от того больше сигнал на отрицательном входе или на положительном. Такой режим работы операционного усилителя называется компаратором. Я его применил недавно, чтобы отследить просадку напряжения питания на устройстве. Смотри на схему, видишь на минус у меня идет опорное напряжение со стабилитрона. Оно всегда равно 3.3 вольта – за этим следит стабилитрон. А вот на второй вход идет напряжение с делителя – оно зависит от общего напряжения питания. В нормальном режиме, когда на входе 12 вольт, то с делителя идет порядка 4 вольт, это выше чем 3.3 опорного и с компаратора выходит +5 вольт (максимум питающего). При просадке напруги ниже определенного порога с делителя начинает выходить уже менее 3.3 вольт и компаратор резко перекидывается в противоположное положение – 0 вольт (минимум питающего). Этот переход отслеживает микроконтроллер и дает сигнал тревоги.
| Испльзование операционных усилителей |
Если от операционного усилителя надо получить усиление, то нужно как то обуздать его бешеный коэффициент. Для этого ему добавляют отрицательную обратную связь. Т.е. берут и с выхода подают сигнал на отрицательный вход, подмешивая его к основному входному сигналу. В итоге, выходной сигнал вычитается из входного. А коэффициент усиления становится равным отношению резисторов на входе и выходе (смотри схему).
Но это далеко не все фишки которые умеет делать операционный усилитель. Если в обратную связь сунуть конденсатор, то получим интегратор, выдающий на выходе интеграл от функции входного сигнала. А если скомбинировать конденсатор с резистором, да индуктивность на вход… В общем, тут можно книгу писать, а занимается этими занятными процессами отдельная наука – автоматическое управление. Кстати, именно на операционных усилителях сделаны аналоговые компьютеры, считающие дифференциальные уравнения с такой скоростью, что все цифровые компы нервно курят в уголке.
Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!
А я встрял на три года, как минимум, ежемесячной пахоты над статьями :)))))))))))) Спасибо вам за такой мощный пинок.
Читаем даташиты 2: SPI на STM32; ШИМ, таймеры и прерывания на STM8

В первой части я попробовал рассказать хобби-электронщикам, выросшим из штанишек Ардуино, как и зачем им стоит читать даташиты и прочую документацию к микроконтроллерам. Текст получился большой, поэтому я пообещал практические примеры показать в отдельной статье. Ну что же, назвался груздем.
Сегодня я покажу, как с помощью даташитов решить довольно простые, но необходимые для множества проектов задачи на контроллерах STM32 (Blue Pill) и STM8. Все демо-проекты посвящены моим любимым светодиодам, зажигать мы их будем в больших количествах, для чего придется задействовать всякую интересную периферию.
Текст опять получился огромный, поэтому для удобства делаю содержание:
Дисклеймер: я не инженер, не претендую на глубокие познания в электронике, статья предназначена для таких же как я любителей. На самом деле, в качестве целевой аудитории я рассматривал самого себя двухлетней давности. Если бы мне кто-то тогда рассказал, что даташиты на незнакомый чип читать не страшно, я бы не потратил кучу времени на выискивание каких-то кусков кода в интернете и изобретение костылей с ножницами и лейкопластырем.
В центре этой статьи — даташиты, а не проекты, поэтому код может быть не слишком причесан и часто костылен. Сами проекты очень простые, хотя и годные для первого знакомства с новым чипом.
Надеюсь, что моя статья поможет кому-то на похожем этапе погружения в хобби.
STM32
16 светодиодов c DM634 и SPI
Небольшой проект с использованием Blue Pill (STM32F103C8T6) и светодиодного драйвера DM634. С помощью даташитов разберемся с драйвером, IO-портами STM и настроим SPI.
DM634
Тайваньский чип с 16-ю 16-битными ШИМ-выходами, можно соединять в цепочки. Младшая 12-битная модель известна по отечественному проекту Lightpack. В свое время, выбирая между DM63x и хорошо известным TLC5940, остановился на DM по нескольким причинам: 1) TLC на Алиэкспрессе точно поддельный, а этот – нет; 2) у DM автономный ШИМ со своим генератором частоты; 3) его можно было недорого купить в Москве, а не ждать посылки с Али. Ну и, конечно, было интересно самому научиться управлять чипом, а не использовать готовую библиотеку. Чипы сейчас в основном представлены в корпусе SSOP24, их несложно припаять на переходник.
Поскольку производитель тайваньский, даташит к чипу написан на китайском английском, а значит, будет весело. Сперва смотрим на распиновку (Pin Connection), чтобы понять, к какой ноге что подключать, и описание пинов (Pin Description). 16 выводов:
Источники втекающего постоянного тока (открытый сток)
Sink / Open-drain output – сток; источник втекающего тока; выход, в активном состоянии подключенный к земле, – светодиоды к драйверу подключаются катодами. Электрически это, конечно, никакой не «открытый сток» (open drain), но в даташитах такое обозначение для выводов в режиме стока встречается часто.
Внешние резисторы между REXT и GND для установки значения выходного тока
Между пином REXT и землей устанавливается референсный резистор, контролирующий внутреннее сопротивление выходов, см. график на стр. 9 даташита. В DM634 этим сопротивлением можно также управлять программно, устанавливая общую яркость (global brightness); в этой статье вдаваться в подробности не буду, просто поставлю сюда резистор на 2.2 – 3 кОм.
Чтобы понять, как управлять чипом, посмотрим на описание интерфейса устройства:

Ага, вот он, китайский английский во всей красе. Перевести это проблематично, понять при желании можно, но есть другой путь – взглянуть, как описывается подключение в даташите к функционально близкому TLC5940:
… Для ввода данных в устройство требуются только три пина. Передний фронт сигнала SCLK сдвигает данные с пина SIN во внутренний регистр. После того, как все данные загружены, короткий высокий сигнал XLAT фиксирует последовательно переданные данные во внутренних регистрах. Внутренние регистры – срабатывающие по уровню сигнала XLAT задвижки. Все данные передаются старшим битом вперед.
Latch – задвижка/защелка/фиксатор.
Rising edge – передний фронт импульса
MSB first – старшим (крайним левым) битом вперед.
to clock data – передавать данные последовательно (побитно).
Слово latch часто встречается в документации к чипам и переводится разнообразно, поэтому для понимания позволю себе
Итак, передача данных в DM634 осуществляется так: выставляем вход DAI в значение старшего бита дальнего светодиода, дергаем DCK вверх-вниз; выставляем вход DAI в значение следующего бита, дергаем DCK; и так далее, пока все биты не будут переданы (clocked in), после чего дергаем LAT. Это можно сделать вручную (bit-bang), но лучше воспользоваться специально под это заточенным интерфейсом SPI, благо он представлен на нашем STM32 в двух экземплярах.
Синяя Таблетка STM32F103
Вводные: контроллеры STM32 – значительно сложнее Atmega328, чем могут пугать. При этом из соображений энергосбережения на старте у них отключена почти вся периферия, а тактовая частота составляет 8 МГц от внутреннего источника. К счастью, программисты STM написали код, доводящий чип до «расчетных» 72 МГц, а авторы всех известных мне IDE включили его в процедуру инициализации, поэтому тактировать нам не нужно (но можно, если очень хочется). А вот включить периферию придется.
Документация: на Blue Pill установлен популярный чип STM32F103C8T6, к нему есть два полезных документа:
-
для микроконтроллеров STM32F103x8 и STM32F103xB; для всей линейки STM32F103 и не только.
В даташите нам могут быть интересны:
- Pinouts – распиновки чипов – на тот случай, если мы решим делать платы сами;
- Memory Map – карта памяти для конкретного чипа. В Reference Manual есть карта для всей линейки, в ней упомянуты регистры, которых нет на нашем.
- Таблица Pin Definitions – перечисление основных и альтернативных функций пинов; для «синей таблетки» в интернете можно найти более удобные картинки со списком пинов и их функциями. Поэтому немедленно гуглим Blue Pill pinout и держим вот такую картинку под рукой:
NB: на картинке из интернета была ошибка, подмеченная в комментариях, за что спасибо. Картинка заменена, но это урок — информацию не из даташитов лучше проверять.
Даташит убираем, открываем Reference Manual, отныне пользуемся только им.
Порядок действий: разбираемся со стандартным вводом/выводом, настраиваем SPI, включаем нужную периферию.
Ввод-вывод
На Atmega328 ввод-вывод реализован предельно просто, из-за чего обилие опций STM32 может сбить с толку. Сейчас нам нужны только выводы, но даже их имеется четыре варианта:
вывод с открытым стоком, вывод «тяни-толкай», альтернативный «тяни-толкай», альтернативный открытый сток
«Тяни-толкай» (push-pull) – привычный вывод с Ардуины, пин может принимать значение либо HIGH, либо LOW. А вот с «открытым стоком» возникают сложности, хотя на самом деле тут все просто:
Конфигурация вывода / когда порт назначен на вывод: / включен буфер вывода: / – режим открытого стока: «0» в выводном регистре активирует N-MOS, «1» в выводном регистре оставляет порт в режиме Hi-Z (P-MOS не активируется) / – режим «тяни-толкай»: «0» в выводном регистре активирует N-MOS, «1» в выводном регистре активирует P-MOS.
Все отличие открытого стока (open drain) от «тяни-толкай» (push-pull) состоит в том, что в первом пин не может принять состояние HIGH: при записи единицы в выводной регистр он переходит в режим высокого сопротивления (high impedance, Hi-Z). При записи нуля пин в обоих режимах ведет себя одинаково, как логически, так и электрически.
В обычном режиме вывода пин просто транслирует содержимое выводного регистра. В «альтернативном» им управляет соответствующая периферия (см. 9.1.4):
Если бит порта сконфигурирован как вывод альтернативной функции, выводной регистр отключается, а пин подключается к выводному сигналу периферии
Альтернативный функционал каждого пина описан в Pin Definitions даташита и есть на скачанной картинке. На вопрос, что делать, если у пина несколько альтернативных функций, ответ дает сноска в даташите:
Если несколько периферийных блоков используют один и тот же пин, во избежание конфликта между альтернативными функциями одновременно следует использовать только один периферийный блок, переключаясь с помощью бита активации тактирования периферии (в соответствующем регистре RCC).
Наконец, у пинов в режиме вывода есть еще скорость тактирования. Это еще одна фишка энергосбережения, в нашем случае просто ставим на максимум и забываем.
Итак: мы используем SPI, значит, два пина (с данными и с тактовым сигналом) должны быть «альтернативная функция тяни-толкай», а еще один (LAT) – «обычный тяни-толкай». Но прежде, чем их назначать, разберемся со SPI.
SPI или Serial Peripherial Interface (последовательный периферийный интерфейс) – простой и весьма эффективный интерфейс для связи МК с другими МК и вообще внешним миром. Принцип его работы уже описан выше, там, где про китайский LED-драйвер (в reference manual см раздел 25). SPI может работать в режиме мастера («хозяина») и слейва («раба»). У SPI есть четыре базовых канала, из которых задействованы могут быть не все:
- MOSI, Master Output / Slave Input: этот пин в режиме мастера отдает, а в режиме слейва принимает данные;
- MISO, Master Input / Slave Output: наоборот, в мастере принимает, в слейве – отдает;
- SCK, Serial Clock: задает частоту передачи данных в мастере или принимает тактовый сигнал в слейве. По сути, отбивает биты;
- SS, Slave Select: с помощью этого канала слейв узнает, что от него что-то хотят. На STM32 называется NSS, где N = negative, т.е. контроллер становится слейвом, если в этом канале земля. Хорошо комбится с режимом Open Drain Output, но это другая история.
Как и все остальное, SPI на STM32 богат функционалом, что несколько осложняет его понимание. Например, он умеет работать не только SPI, но и I2S-интерфейсом, причем в документации их описания идут вперемешку, надо своевременно отсекать лишнее. У нас же задача крайне простая: надо всего лишь отдавать данные, задействуя только MOSI и SCK. Идем в раздел 25.3.4 (half-duplex communication, полудуплексная связь), где находим 1 clock and 1 unidirectional data wire (1 тактовый сигнал и 1 однонаправленный поток данных):
В этом режиме приложение использует SPI либо в режиме только передачи, либо только приема. / Режим только передачи похож на дуплексный режим: данные передаются по передающему пину (MOSI в режиме мастера или MISO в режиме слейва), а принимающий пин (MISO или MOSI соответственно) может использоваться как обычный пин ввода-вывода. В этом случае приложению достаточно игнорировать буфер Rx (если его прочитать, там не будет переданных данных).
Отлично, пин MISO у нас освободился, подключим к нему сигнал LAT. Разберемся со Slave Select, которым на STM32 можно управлять программно, что необычайно удобно. Читаем одноименный абзац раздела 25.3.1 SPI General Description:
Программное управление NSS (SSM = 1) / Информация о выборе слейва содержится в бите SSI регистра SPI_CR1. Внешний пин NSS остается свободным для других нужд приложения.
Пора писать в регистры. Я решил использовать SPI2, ищем в даташите его базовый адрес – в разделе 3.3 Memory Map (Карта памяти):

Открываем раздел 25.3.3 с говорящим названием «Настройка SPI в режиме мастер»:

1. Установите тактовую частоту последовательного интерфейса битами BR[2:0] в регистре SPI_CR1.
Регистры собраны в одноименном разделе reference manual. Сдвиг адреса (Address offset) у CR1 – 0x00, по умолчанию все биты сброшены (Reset value 0x0000):

Биты BR устанавливают делитель тактовой частоты контроллера, определяя таким образом частоту, на которой будет работать SPI. Частота STM32 у нас будет 72 МГц, LED-драйвер, согласно его даташиту, работает с частотой до 25 МГц, таким образом, делить надо на четыре (BR[2:0] = 001).
2. Установите биты CPOL и CPHA, чтобы определить отношения между передачей данных и тактированием последовательного интерфейса (см. схему на стр 240)
Поскольку мы тут читаем даташит, а не рассматриваем схемы, давайте лучше изучим текстовое описание битов CPOL и CPHA на стр. 704 (SPI General Description):
Фаза и полярность тактового сигнала
С помощью битов CPOL и CPHA регистра SPI_CR1 можно программно выбрать четыре варианта отношений таймингов. Бит CPOL (полярность тактового сигнала) управляет состоянием тактового сигнала, когда данные не передаются. Этот бит управляет режимами мастер и слейв. Если CPOL сброшен, пин SCK в режиме покоя находится в низком уровне. Если бит CPOL установлен, пин SCK в режиме покоя находится в высоком уровне.
Если установлен бит CPHA (фаза тактового сигнала), стробом-ловушкой старшего бита выступает второй фронт сигнала SCK (нисходящий, если CPOL сброшен, или восходящий, если CPOL установлен). Данные фиксируются по второму изменению тактового сигнала. Если бит CPHA сброшен, стробом-ловушкой старшего бита выступает передний фронт сигнала SCK (нисходящий, если CPOL установлен, или восходящий, если CPOL сброшен). Данные фиксируются по первому изменению тактового сигнала.
Вкурив в эти знания, приходим к выводу, что оба бита должны остаться нулями, т.к. нам надо, чтобы сигнал SCK оставался низким, когда не используется, а данные передавались по переднему фронту импульса (см. Rising Edge в даташите DM634).
Кстати, здесь мы впервые столкнулись с особенностью лексики в даташитах ST: в них фраза «сбросить бит в ноль» – пишется to reset a bit, а не to clear a bit, как, например, у Атмеги.
3. Установите бит DFF для определения 8-битного или 16-битного формата блока данных
Я специально взял 16-битный DM634, чтобы не заморачиваться с передачей 12-битных данных ШИМ, как у DM633. DFF имеет смысл поставить в единицу:
4. Сконфигурируйте бит LSBFIRST в регистре SPI_CR1 для определения формата блока
LSBFIRST, как видно из его названия, настраивает передачу младшим битом вперед. Но DM634 хочет получать данные, начиная со старшего бита. Поэтому оставляем сброшенным.
5. В аппаратном режиме, если требуется ввод с пина NSS, подавайте на пин NSS высокий сигнал во время всей последовательности передачи байтов. В программном режиме NSS установите биты SSM и SSI в регистре SPI_CR1. Если пин NSS должен работать на вывод, надо установить только бит SSOE.
Устанавливаем SSM и SSI, чтобы забыть про аппаратный режим NSS:
6. Должны быть установлены биты MSTR и SPE (они остаются установленными только если на NSS подается высокий сигнал)
Собственно, этими битами мы назначаем наш SPI мастером и включаем его:
SPI настроен, давайте сразу напишем функции, отправляющие байты драйверу. Продолжаем читать 25.3.3 «Настройка SPI в режиме мастер»:
Порядок передачи данных
Передача начинается когда в буфер Tx записывается байт.
Байт данных загружается в сдвиговый регистр в параллельном режиме (из внутренней шины) во время передачи первого бита, после чего передается в последовательном режиме пину MOSI, первым или последним битом вперед в зависимости от установки бита LSBFIRST в регистре CPI_CR1. Флаг TXE устанавливается после передачи данных из буфера Tx в сдвиговый регистр, а также создается прерывание, если установлен бит TXEIE в регистре CPI_CR1.
Я выделил несколько слов в переводе, чтобы обратить внимание на одну особенность реализации SPI в контроллерах STM. На Атмеге флаг TXE (Tx Empty, Tx пуст и готов принимать данные) устанавливается только после того, как весь байт отправился наружу. А здесь этот флаг устанавливается после того, как байт оказался засунут во внутренний сдвиговый регистр. Поскольку пихается он туда всеми битами одновременно (параллельно), а дальше данные передаются последовательно, TXE устанавливается до того, как байт полностью отправится. Это важно, т.к. в случае нашего LED-драйвера нам надо дернуть пин LAT после отправки всех данных, т.е. только флага TXE нам будет недостаточно.
А это значит, что нам нужен еще какой-то флаг. Посмотрим в 25.3.7 – «Флаги статусов»:
<. >
Флаг BUSY
Флаг BSY устанавливается и сбрасывается аппаратно (запись в него ни на что не влияет). Флаг BSY показывает состояние коммуникативного слоя SPI.
Он сбрасывается:
когда передача завершена (кроме режима мастера, если передача непрерывна)
когда SPI отключен
когда происходит ошибка режима мастера (MODF=1)
Если передача не непрерывна, флаг BSY сброшен между каждой передачей данных
Окей, пригодится. Выясняем, где находится буфер Tx. Для этого читаем «Регистр данных SPI»:
Биты 15:0 DR[15:0] Регистр данных
Полученные данные или данные для передачи.
Регистр данных разделен на два буфера – один для записи (буфер передачи) и второй для чтения (буфер приема). Запись в регистр данных пишет в буфер Tx, а чтение из регистра данных вернет значение, содержащееся в буфере Rx.
Ну и регистр статусов, где найдутся флаги TXE и BSY:

Ну а поскольку нам надо передать 16 раз по два байта, по числу выходов LED-драйвера, то как-то так:
Но мы пока не умеем дергать пин LAT, поэтому вернемся в I/O.
Назначаем пины
У STM32F1 регистры, отвечающие за состояние пинов, довольно необычны. Понятно, что их больше, чем у Атмеги, но они еще и отличаются от других чипов STM. Раздел 9.1 Общее описание GPIO:
Каждый из портов ввода/вывода общего назначения (GPIO) обладает двумя 32-битными регистрами конфигурации (GPIOx_CRL и GPIOx_CRH), двумя 32-битными регистрами данных (GPIOx_IDR и GPIOx_ODR), 32-битным регистром установки/сброса (GPIOx_BSRR), 16-битным регистром сброса (GPIOx_BRR) и 32-битным блокирующим регистром (GPIOx_LCKR).
Необычны, а также довольно неудобны, здесь первые два регистра, потому что 16 пинов порта разбросаны по ним в формате «по четыре бита на брата». Т.е. пины с нулевого по седьмой сидят в CRL, а остальные – в CRH. При этом остальные регистры успешно умещают в себя биты всех пинов порта – часто оставаясь наполовину «зарезервированными».
Для простоты начнем с конца списка.
Блокирующий регистр нам не потребуется.
Регистры установки и сброса довольно забавны тем, что частично дублируют друг друга: можно все писать только в BSRR, где старшие 16 битов будут сбрасывать пин в ноль, а младшие – устанавливать в 1, либо использовать также BRR, младшие 16 битов которого только сбрасывают пин. Мне по душе второй вариант. Эти регистры важны тем, что обеспечивают атомарный доступ к пинам:
Атомарная установка или сброс
Не нужно отключать прерывания при программировании GPIOx_ODR на битовом уровне: можно изменять один или несколько битов одной атомарной операцией записи APB2. Это достигается записью «1» в регистр установки/сброса (GPIOx_BSRR или, только для сброса, в GPIOx_BRR) бита, который требуется изменить. Прочие биты останутся неизменными.
Регистры данных имеют вполне говорящие названия – IDR = Input Direction Register, регистр ввода; ODR = Output Direction Register, регистр вывода. В нынешнем проекте они нам не потребуются.
Ну и, наконец, управляющие регистры. Поскольку нам интересны пины второго SPI, а именно PB13, PB14 и PB15, сразу смотрим на CRH:

И видим, что надо будет что-то написать в биты с 20-го по 31-й.
Мы уже выше разобрались с тем, что мы хотим от пинов, поэтому тут я обойдусь без скриншота, просто скажу, что MODE задает направление (ввод, если оба бита выставлены в 0) и скорость пина (нам нужно 50MHz, т.е. оба пина в «1»), а CNF задает режим: обычный «тяни-толкай» – 00, «альтернативный» – 10. По умолчанию, как мы видим выше, у всех пинов прописан третий снизу бит (CNF0), он устанавливает их в режим floating input.
Поскольку я планирую что-то еще делать с этим чипом, я для простоты задефайнил вообще все возможные значения MODE и CNF как для нижнего, так и для верхнего контрольных регистров.
Наши пины находятся на порту B (базовый адрес – 0x40010C00), код:
И, соответственно, можно написать дефайны для LAT, который будет дергаться регистрами BRR и BSRR:
(LAT_low просто по инерции, как-то всегда было, пусть себе останется)
Теперь все уже здорово, только не работает. Потому что это STM32, тут экономят электричество, а значит, надо включить тактирование нужной периферии.
Включаем тактирование
За тактирование отвечают часики, они же Clock. И мы уже могли заметить аббревиатуру RCC. Ищем ее в документации: это Reset and Clock Control (Управление сбросом и тактированием).
Как выше было сказано, к счастью, самое сложное из темы тактирования за нас сделали люди из STM, за что им большое спасибо (еще раз дам ссылку на сайт Di Halt’а, чтобы было понятно, насколько это заморочено). Нам нужны всего лишь регистры, отвечающие за включение тактирования периферии (Peripheral Clock Enable Registers). Для начала найдем базовый адрес RCC, он в самом начале «Карты памяти»:

А дальше либо кликнуть по ссылке, где пытаться в табличке что-то найти, либо, гораздо лучше, пробежаться по описаниям включающих регистров из разделов про enable registers. Где мы найдем RCC_APB1ENR и RCC_APB2ENR:

И в них, соответственно, биты, включающие тактирование SPI2, IOPB (I/O Port B) и альтернативных функций (AFIO).
Финальный код можно найти тут.
Если есть возможность и желание потестить, то подключаем DM634 так: DAI к PB15, DCK к PB13, LAT к PB14. Питаем драйвер от 5 вольт, не забываем объединить земли.

STM8 PWM
ШИМ на STM8
Когда я только планировал эту статью, я решил для примера попробовать освоить какой-нибудь функционал незнакомого мне чипа с помощью только даташита, чтобы не получался сапожник без сапог. STM8 на эту роль подходил идеально: во-первых, у меня была пара китайских плат с STM8S103, а во-вторых, он не слишком популярен, а потому соблазн почитить и найти решение в интернете упирается в отсутствие этих самых решений.
К чипу также есть даташит и reference manual RM0016, в первом распиновка и адреса регистров, во втором – все остальное. Программируется STM8 на C в страшненькой IDE ST Visual Develop.
Тактирование и ввод-вывод
По умолчанию STM8 работает на частоте 2 МГц, это надо сразу исправить.
Тактовый сигнал HSI (скоростной внутренний)
Тактовый сигнал HSI получается от внутреннего 16-МГц RC-генератора с программируемым делителем (от 1 до 8). Он задается в регистре делителя тактового сигнала (CLK_CKDIVR).
Примечание: на старте ведущим источником тактового сигнала выбирается HSI RC-генератор с делителем 8.
Находим адрес регистра в даташите, описание в refman и видим, что регистр надо очистить:
Поскольку мы собираемся запускать ШИМ и подключать светодиоды, смотрим распиновку:

Чип маленький, многие функции подвешены на одни и те же пины. То, что в квадратных скобках – «альтернативный функционал», он переключается «байтами опций» (option bytes) – что-то вроде фьюзов Атмеги. Менять их значения можно программно, но не нужно, т.к. активируется новый функционал только после перезагрузки. Проще воспользоваться ST Visual Programmer (качается вместе с Visual Develop), умеющим менять эти байты. В распиновке видно, что выводы CH1 и CH2 первого таймера спрятаны в квадратные скобки; надо в STVP проставить биты AFR1 и AFR0, причем второй также перенесет вывод CH1 второго таймера с PD4 на PC5.
Таким образом, управлять светодиодами будут 6 пинов: PC6, PC7 и PC3 для первого таймера, PC5, PD3 и PA3 для второго.
Настройка самих пинов ввода-вывода на STM8 проще и логичнее, чем на STM32:
- знакомый по Atmega регистр направления данных DDR (Data Direction Register): 1 = вывод;
- первый контрольный регистр CR1 при выводе задает режим «тяни-толкай» (1) или открытый сток (0); поскольку я подключаю светодиоды к чипу катодами, оставляю тут нули;
- второй контрольный регистр CR2 при выводе задает скорость тактирования: 1 = 10 МГц
Настройка ШИМ
Для начала определимся с терминами:
- PWM Frequency – частота, с которой тикает таймер;
- Auto-reload, AR – автозагружаемое значение, до которого будет считать таймер (период импульса);
- Update Event, UEV – событие, случающееся, когда таймер досчитал до AR;
- PWM Duty Cycle – коэффициент заполнения ШИМ, часто называют «скважностью»;
- Capture/Compare Value – значение для захвата/сравнения, досчитав до которого таймер что-то сделает (в случае ШИМ – инвертирует выходной сигнал);
- Preload Value – предзагруженное значение. Compare value не может меняться, пока таймер тикает, иначе цикл ШИМ поломается. Поэтому новые передаваемые значения помещаются в буфер и вытаскиваются оттуда, когда таймер достигает конца отсчета и сбрасывается;
- Edge-aligned и Center-aligned modes – выравнивание по границе и по центру, то же, что атмеловские Fast PWM и Phase-correct PWM.
- OCiREF, Output Compare Reference Signal – референсный выводной сигнал, собственно, то, что в режиме ШИМ оказывается на соответствующем пине.
Как уже ясно из распиновки, возможности ШИМ есть у двух таймеров – первого и второго. Оба 16-битные, первый обладает массой дополнительных фич (в частности, умеет считать и вверх, и вниз). Нам надо, чтобы оба работали одинаково, поэтому я решил начать с заведомо более бедного второго, чтобы случайно не использовать что-то, чего в нем нет. Некоторая проблема состоит в том, что описание функционала ШИМ всех таймеров в reference manual находится в главе про первый таймер (17.5.7 PWM Mode), поэтому приходится все время прыгать туда-сюда по документу.
ШИМ на STM8 обладает важным преимуществом над ШИМ Атмеги:
ШИМ с выравниванием по границе
Конфигурация счета снизу вверх
Счет снизу вверх активен, если бит DIR в регистре TIM_CR1 сброшен
Пример
Пример использует первый режим ШИМ. Референсный сигнал ШИМ OCiREF удерживается в высоком уровне, пока TIM1_CNT < TIM1_CCRi. Иначе он принимает низкий уровень. Если значение для сравнение в регистре TIM1_CCRi больше, чем автозагружаемое значение (регистр TIM1_ARR), сигнал OCiREF удерживается в 1. Если значение для сравнения равно 0, OCiREF удерживается на нуле.…
Таймер STM8 во время update event сперва проверяет compare value, и лишь потом выдает референсный сигнал. У Атмеги таймер сперва шарашит, а потом сравнивает, в результате чего при compare value == 0 на выходе получается игла, с которой надо как-то бороться (например, программно инвертируя логику).
Итак, что мы хотим сделать: 8-битный ШИМ ( AR == 255 ), считаем снизу вверх, выравнивание по границе. Поскольку лампочки подключены к чипу катодами, ШИМ должен выдавать 0 (LED горит) до compare value и 1 после.
Мы уже прочитали про некие PWM mode, поэтому находим нужный регистр второго таймера поиском в reference manual по этой фразе (18.6.8 – TIMx_CCMR1):
110: Первый режим ШИМ – при счете снизу вверх, первый канал активен, пока TIMx_CNT < TIMx_CCR1. В противном случае первый канал неактивен. [дальше в документе ошибочный копипаст из таймера 1]
111: Второй режим ШИМ – при счете снизу вверх, первый канал неактивен, пока TIMx_CNT < TIMx_CCR1. В противном случае первый канал активен.
Поскольку светодиоды подключены к МК катодами, нам подходит второй режим (первый тоже, но мы пока этого не знаем).
Бит 3 OC1PE: Включить предзагрузку вывода 1
0: Регистр предзагрузки на TIMx_CCR1 выключен. Писать в TIMx_CCR1 можно в любое время. Новое значение работает сразу.
1: Регистр предзагрузки на TIMx_CCR1 включен. Операции чтения/записи обращаются к регистру предзагрузки. Предзагруженное значение TIMx_CCR1 загружается в теневой регистр во время каждого события обновления.
*Примечание: для правильной работы режима ШИМ регистры предзагрузки должны быть включены. Это необязательно в режиме одиночного сигнала (в регистре TIMx_CR1 установлен бит OPM).
Окей, включаем все, что нужно, для трех каналов второго таймера:
AR состоит из двух восьмибитных регистров, тут все просто:
Второй таймер умеет считать только снизу-вверх, выравнивание по границе, менять ничего не надо. Установим делитель частоты, например, в 256. У второго таймера делитель выставляется в регистре TIM2_PSCR и представляет собой степень двойки:
Осталось включить выводы и сам второй таймер. Первая задача решается регистрами Capture/Compare Enable: их два, три канала по ним разбросаны несимметрично. Здесь мы также можем узнать, что можно менять полярность сигнала, т.е. в принципе можно было использовать и PWM Mode 1. Пишем:
Ну и, наконец, запускаем таймер в регистре TIMx_CR1:

Напишем простенький аналог AnalogWrite(), который будет передавать таймеру собственно значения для сравнения. Регистры предсказуемо называются Capture/Compare registers, их по два на каждый канал: младшие 8 бит в TIM2_CCRxL и старшие в TIM2_CCRxH. Поскольку мы завели 8-битный ШИМ, достаточно писать только младшие биты:
Внимательный читатель заметит, что у нас получился слегка бракованный ШИМ, неспособный выдать 100% заполнение (при максимальном значении 255 сигнал инвертируется на один цикл таймера). Для светодиодов это не играет роли, а внимательный читатель уже сам догадывается, как это исправить.
ШИМ на втором таймере работает, переходим к первому.
Первый таймер обладает ровно теми же битами в таких же регистрах (просто те биты, что оставались «зарезервированы» во втором таймере, в первом активно используются для всяких продвинутых штук). Поэтому достаточно найти адреса этих же регистров в даташите и скопировать код. Ну и поменять значение делителя частоты, т.к. первый таймер хочет получить не степень двойки, а точное 16-битное значение в два регистра Prescaler High и Low. Все делаем и… первый таймер не работает. В чем дело?
Решить проблему можно только путем просмотра всего раздела про управляющие регистры таймера 1, где ищем тот, которого нет у второго таймера. Найдется 17.7.30 Break register (TIM1_BKR), где есть такой бит:
Включить главный вывод
Вот теперь точно все, код там же.

STM8 Multiplex
Мультиплексинг на STM8
Третий мини-проект состоит в том, чтобы подключить к второму таймеру в режиме ШИМ восемь RGB-светодиодов и заставить их показывать разные цвета. В основе – концепция LED-мультиплексинга, состоящая в том, что если очень-очень быстро зажигать и гасить светодиоды, нам будет казаться, что они горят постоянно (persistence of vision, инерция зрительного восприятия). Когда-то я делал что-то такое на Ардуине.
Алгоритм работы выглядит так:
- подключили анод первого RGB LED;
- зажгли его, подав нужные сигналы на катоды;
- дождались конца цикла ШИМ;
- подключили анод второго RGB LED;
- зажгли его.
Ну и т.д. Разумеется, для красивой работы требуется, чтобы подключение анода и «зажигание» светодиода происходили одновременно. Ну или почти. В любом случае, нам надо написать код, который будет в три канала второго таймера выдавать значения, при достижении UEV менять их и одновременно менять активный в данный момент RGB-светодиод.
Поскольку переключение LED выполняется автоматически, нужно создать «видеопамять», откуда обработчик прерывания будет получать данные. Это простой массив:
Для того, чтобы поменять цвет конкретного светодиода, достаточно будет записать в этот массив нужные значения. А за номер активного светодиода будет отвечать переменная
Демукс
Для правильного мультиплексинга нам потребуется, как ни странно, демультиплексор CD74HC238. Демультиплексор – чип, аппаратно реализующий оператор << . Через три входных пина (биты 0, 1 и 2) мы скармливаем ему трехбитное число X, а он в ответ активирует выход номер ( 1<<X ). Остальные входы чипа используются для масштабирования всей конструкции. Этот чип нам нужен не только для сокращения числа занятых пинов микроконтроллера, но и для безопасности – чтобы случайно не врубить больше светодиодов, чем можно, и не сжечь МК. Чип стоит копейки, его стоит держать в домашней аптечке всегда.
CD74HC238 у нас будет отвечать за то, чтобы подавать напряжение к аноду нужного светодиода. В полноценном мультиплексе он бы подавал напряжение на столбец через P-MOSFET, но в этом демо можно и напрямую, т.к. он тянет 20 мА, согласно absolute maximum ratings в даташите. Из даташита CD74HC238 нам потребуется распиновка и вот эта шпаргалка:
H = высокий уровень напряжения, L = низкий уровень напряжения, X – все равно
Подключаем E2 и E1 к земле, E3, A0, A1 и A3 к пинам PD5, PC3, PC4 и PC5 STM8. Поскольку таблица выше содержит и низкий, и высокий уровни, настраиваем эти пины как push-pull выводы.
ШИМ на втором таймере настраивается так же, как в предыдущей истории, с двумя отличиями:
Во-первых, нам надо включить прерывание на Update Event (UEV), которое будет вызывать функцию, переключающую активный LED. Делается это изменением бита Update Interrupt Enable в регистре с говорящим названием
Регистр включения прерываний
Второе отличие связано с таким явлением мультиплексинга, как ghosting – паразитное свечение диодов. В нашем случае оно может появитсья из-за того, что таймер, вызвав прерывание на UEV, идет тикать дальше, и обработчик прерывания не успевает переключить LED прежде чем таймер уже начнет что-то писать в выводы. Для борьбы с этим придется инвертировать логику (0 = максимальная яркость, 255 = ничего не горит) и не допускать крайних значений скважности. Т.е. добиться того, чтобы после UEV светодиоды полностью гасли на один такт ШИМ.
Избегаем установки r, g и b в 255 и не забываем их инвертировать при использовании.
Прерывания
Суть прерывания в том, что при определенных обстоятельствах чип прекращает выполнять основную программу и вызывает какую-то внешнюю функцию. Прерывания возникают из-за внешних или внутренних воздействий, в том числе от таймера.
Когда мы в первый раз создали проект в ST Visual Develop, то кроме main.c мы получили окно с загадочным файлом stm8_interrupt_vector.c , автоматически включенным в проект. В этом файле на каждое прерывание привязана функция NonHandledInterrupt . Нам надо привязать свою функцию к нужному прерыванию.
В даташите есть таблица векторов прерываний, где мы находим нужные:
13 TIM2 обновление/переполнение
14 TIM2 захват/сравнение
Нам надо менять LED при UEV, так что нужно прерывание №13.
Соответственно, во-первых, в файле stm8_interrupt_vector.c меняем имя функции, отвечающей за прерывание №13 (IRQ13) по умолчанию на свое:
Во-вторых, нам придется создать файл main.h такого содержания:
Ну и, наконец, прописать эту функцию в своем main.c :
Осталось включить прерывания. Делается это ассемблерной командой rim – искать ее придется в Programming Manual:
Другая ассемблерная команда – sim – выключает прерывания. Их надо отключать на время записи новых значений в «видеопамять», чтобы вызванное в неудачный момент прерывание не испортило массив.

Если хоть кому-то эта статья пригодится, значит, я не зря ее писал. Буду рад комментариям и замечаниям, постараюсь ответить на все.