Top.Mail.Ru

Микроконтроллер STM32 и драйвер светодиодов DM164.

Часто в своих устройствах хочется организовать красивую индикацию, а может устройство, в принципе, по своей основной функции должно управлять большим количеством разноцветных светодиодов. В этом случае отличным, да и по сути единственным решением является использование RGB-светодиодов, которые управляются при помощи трех ШИМ сигналов (по одному на каждую цветовую составляющую - Red/Green/Blue). И вроде бы все отлично, но что если светодиодов требуется подключить, к примеру, 12 штук, и все они должны светиться по-разному. И 12 - это еще далеко не предел.

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

Начнем с обсуждения электронных компонентов, которые я буду использовать в работе. Итак, в качестве мозгового центра устройства будет выступать микроконтроллер STM32F103RBT6. Какой именно контроллер задействовать абсолютно непринципиально, так что это не имеет большого значения. Важным является выбор драйвера, я буду использовать (и активно использую) DM164. Светодиоды также можно использовать абсолютно разные, главное, чтобы они были с общим анодом.

Собственно, пару слов о самой микросхеме. У DM164 есть 24 выходных канала, то есть к одному драйверу мы можем подключить 8 светодиодов (по три канала на один светодиод). Нам для решения задачи понадобятся два драйвера (8 + 4 диодов). Для каждой цветовой составляющей мы можем задать яркость от 0 до 65536, таким образом, многообразие цветов вполне неплохое ) В зависимости от используемых диодов мы можем настроить значение выходного тока, для этого используются внешние резисторы. Максимально драйвер может выдавать 90 мА. Я не буду перечислять факты из документации на микросхему, поэтому давайте остановимся на уже упомянутых основных характеристиках драйвера и перейдем к практике.

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

Схема подключения

Давайте начнем по порядку. На выводы VDD1 и VDD2 мы подаем напряжение питания - 3.3 В.

Соответственно, выводы VSSx мы заземляем. Кроме того, подключаем землю на выводы DCKPH и DOUTPH, которые отвечают за настройку протокола обмена. Вот как это выглядит:

Драйвер светодиодов DM164

В нашем случае DCKPH = DOUTPH = L. К каждому из драйверов подключены три резистора, которые как раз задают выходной ток каналов DM164. Зависимость вот такая:

Зависимость выходного тока от номинала внешних резисторов

Мои светодиоды рассчитаны на ток 30 мА, поэтому, исходя из графика, я поставил резисторы номиналом 2.7 кОм. С этим все довольно просто, переходим к управляющим выводам.

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

Раз уж упомянул SPI, то давайте рассмотрим выводы DOUT, DCK и DIN. Собственно, это и есть выводы SPI. Управление драйвером осуществляется именно по этому интерфейсу. Отправляя данные драйверу мы устанавливаем яркость для каждого из выходных каналов, а принимая данные мы можем диагностировать возникновение ошибок. Но в данном примере мы не будем использовать прием, поскольку, по большому счету, для стабильной работы это и не требуется.

Поскольку нам надо подключить два драйвера, а не один, то мы используем так называемое каскадное подключение. Как видно из схемы вывод DOUT первой микросхемы идет дальше, а именно на вход DIN второго драйвера. Таким образом, со стороны контроллера мы используем всего один модуль SPI. Яркости каждого канала соответствует 16-битное число, которое и необходимо отправить. Один светодиод - это три канала, поэтому у первого драйвера мы задействуем все 24, а у второго только половину (12) каналов. В результате нехитрых вычислений получается, что отправлять нам нужно будет 36 значений типа uint16_t. Причем первые 12 из них "проскочат" первый драйвер и благодаря каскадному подключению дойдут до второго.

Двигаемся дальше по выводам микросхемы, и на очереди сигнал GCK. Сюда мы должны подать ШИМ-сигнал с нашего микроконтроллера, он будет использоваться для формирования выходного сигнала каналов драйвера. Максимальная частота при напряжении питания 3.3 В составляет 36 МГц.

Вывод LATCH - "защелка". Именно подав этот сигнал мы дадим драйверу команду активировать выходы в соответствии с данными, которые мы к тому моменту уже должны будем передать.

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

IWAVE отвечает за режим работы драйвера и на этот вход мы подадим высокий уровень (высокий - traditional Iout waveform, низкий - average separate Iout waveform).

MSEL позволяет конфигурировать драйвер - для этого его нужно перевести в состояние логической единицы.

И, наконец, ONEST - включает/отключает функцию one-shot. Мы этот режим не будем использовать, поэтому на эту ножку просто подаем низкий уровень.

Диоды я подключил к последовательным каналам - то есть первый диод, к примеру, подключен катодами на IOUT0, IOUT1, IOUT2, а на общий анод подаем напряжение питания (3.3 В). Аналогичным образом подключаются все остальные диоды. У меня получилось, что за синюю составляющую цвета первого светодиода отвечает IOUT0, за красную - IOUT1, ну и за зеленую - IOUT2 (BGR).

Итак, вроде бы с электрическим подключением разобрались, давайте переходить к программной реализации. Для этого проекта я буду использовать Keil и библиотеку SPL.

Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32.

Пример программы для драйвера светодиодов.

Начинаем с подключения требующихся файлов:

#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_spi.h"

Далее объявим переменные:

SPI_InitTypeDef spi;
GPIO_InitTypeDef gpio;
TIM_TimeBaseInitTypeDef timer;
TIM_OCInitTypeDef timerPWM;

uint16_t currentColorData[36];
uint32_t i = 0;
uint16_t timeout;

Определим тайм-аут для передачи данных по SPI. На настройках периферии - таймеров и SPI - я не буду отдельно останавливаться, поскольку все это мы уже обсуждали в статьях, посвященных этим модулям.

#define TIMEOUT_TIME                                             0x1000

Подключено у меня следующим образом:

  • ALARM - PB0
  • MSEL - PB1
  • LTH - PB10
  • IWAVE - PC4
  • ONEST - PC5
  • SPI_MOSI - PA7
  • SPI_CLK - PA5
  • GCK - PA3

Теперь нам нужно настроить все эти выводы и подать нужный сигнал на каждый из них, а также настроить GCK на генерацию ШИМ и модуль SPI1. GCK у нас на PA3, а это четвертый канал таймера TIM2:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_13;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &gpio);

GPIO_ResetBits(GPIOC, GPIO_Pin_4);
GPIO_ResetBits(GPIOC, GPIO_Pin_5);

gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_10;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio);

GPIO_ResetBits(GPIOB, GPIO_Pin_1);
GPIO_ResetBits(GPIOB, GPIO_Pin_10);

gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio);

TIM_TimeBaseStructInit(&timer);
timer.TIM_Prescaler = 1;
timer.TIM_Period = 2;
TIM_TimeBaseInit(TIM2, &timer); 

TIM_OCStructInit(&timerPWM);
timerPWM.TIM_Pulse = 1;
timerPWM.TIM_OCMode = TIM_OCMode_PWM1;
timerPWM.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC4Init(TIM2, &timerPWM);

TIM_Cmd(TIM2, ENABLE);

spi.SPI_Direction = SPI_Direction_1Line_Tx;
spi.SPI_DataSize = SPI_DataSize_16b;
spi.SPI_CPOL = SPI_CPOL_Low;
spi.SPI_CPHA = SPI_CPHA_1Edge;
spi.SPI_NSS = SPI_NSS_Soft;
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
spi.SPI_FirstBit = SPI_FirstBit_MSB;
spi.SPI_CRCPolynomial = 7;
spi.SPI_Mode = SPI_Mode_Master;
SPI_Init(SPI1, &spi);

SPI_Cmd(SPI1, ENABLE);

Подготавливаем передаваемые данные:

for (i = 0; i < 36; i += 3)
{
	currentColorData[i] = 65535;
	currentColorData[i + 1] = 0;
	currentColorData[i + 2] = 0;
}

Как вы помните, каналы у нас сгруппированы по три (каждый светодиод - 3 канала) и первый канал - это у нас синий, второй - красный, а третий - зеленый цвет. Таким образом, при передаче такого массива все светодиоды должны выдать максимально яркий и чистый синий цвет.

Выполняем отправку:

for (i = 0; i < 36; i++)
{
	timeout = TIMEOUT_TIME;
	while ((SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) & (timeout != 0))
	{
		timeout--;
	}
	
	SPI_I2S_SendData(SPI1, currentColorData[i]);

	timeout = TIMEOUT_TIME;
	while ((SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) & (timeout != 0))
	{
		timeout--;
	}
}

Данные переданы - необходимо их "защелкнуть" - кратковременно подаем высокий уровень на LATCH.

GPIO_SetBits(GPIOB, GPIO_Pin_10);
GPIO_ResetBits(GPIOB, GPIO_Pin_10);

Вот и все! Диоды начинают светить красивым сними цветом. В общем-то можно задать любой цвет свечения, а можно разукрасить все светодиоды в разные цвета, а можно и вообще организовать красивую динамическую индикацию ) Все зависит от фантазии и, конечно, от задачи, которую необходимо реализовать. А мы на сегодня на этом заканчиваем, до скорой встречи 🤝

Подписаться
Уведомить о
guest

2 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Паша
Паша
8 лет назад

Спасибо! Будем использовать по необходимости.
P.S. Эта та что в корпусе LQFP48 ?

2
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x