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

Это позволяет одним светодиодом отображать, например, два состояния какой-то системы: зелёный для нормальной работы и красный для индикации ошибки. Довольно часто такие светодиоды ставятся внутрь кнопки, и нажатие кнопки дублируется перемигиванием светодиода, или переключением его в другой цвет (конечно, это делается внешним МК).

Но не все знают, что таким светодиодом можно показывать ещё и третий цвет! Никакой rocket science, нужно просто быстро переключать светодиод из одного цвета в другой. Я покажу, как это сделать на STM32 — сначала неправильно, а потом правильно.

Нам ведь важна только разность потенциалов на выводах светодиода? Поэтому удобнее всего поступить так: подключим светодиод к двум ногам ввода-вывода, для включения одного цвета подадим «1» на первую ногу и «0» на вторую. На светодиоде, таким образом, будет напряжение +3,3В, и светится зелёный цвет. Если же поменять полярность, подать «0» на первую ногу и «1» на вторую — на светодиоде получится напряжение -3,3В, и загорится красный цвет. Конечно, нельзя забывать о токоограничительном резисторе, который не даст току подняться выше 10-20мА.

Неправильный, но простой метод

Всё очень просто: подключаем светодиод через 330Ом резистор к каким-нибудь ножкам ввода-вывода, и в цикле их включаем и выключаем. Я использую PB0 и PB2 на плате STM32VLDiscovery.

Код будет очень простым:

#include "stm32f10x_conf.h"

void main()
{
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
 
 GPIO_InitTypeDef GPIO_InitStructure;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 GPIOB->ODR=0;
#define del1 3000
#define del2 10000
 int i;
 while(1)
 {
 GPIOB->ODR=GPIO_Pin_0;
 for(i=0; i<del1; i++);
 
 GPIOB->ODR=GPIO_Pin_2;
 for(i=0; i<del2; i++);
 }
}

Скачать проект

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

Более сложный, но правильный метод

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

#include "stm32f10x_conf.h"

int a=0;

void TIM6_DAC_IRQHandler(void)
{
 if (TIM6->SR & TIM_SR_UIF) {
 // Сбрасываем флаг прерывания
 TIM6->SR &= ~TIM_SR_UIF;
 
 if(a) GPIOB->ODR=GPIO_Pin_0;
 else GPIOB->ODR=GPIO_Pin_2;
 a=!a;
 }
}

void main()
{
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
 
 GPIO_InitTypeDef GPIO_InitStructure;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;
 GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 GPIOB->ODR=0;
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); 

 // Настраиваем прескалер. При тактовой частоте 24МГц частота
 // таймера будет равна 24МГц/240 = 100кГц
 TIM6->PSC = 240;

 // Настраиваем период таймера = 1000 циклов - полный цикл таймера
 // будет равен 1/100кГц*1000 = 10 миллисекунд.
 TIM6->ARR = 1000;
 TIM6->DIER |= TIM_DIER_UIE;
 NVIC_EnableIRQ(TIM6_DAC_IRQn);
 TIM6->CR1 |= TIM_CR1_CEN;
 
 while(1);
}

Скачать проект

Регулировка яркости двухцветного светодиода

С двухцветными светодиодами всегда возникает проблема — яркость кристаллов разная, и если включать их с одинаковой скважностью (к примеру, 50% времени красный, 50% времени зелёный) — цвета смешиваются неточно, и получается не то что мы хотели. Красные светодиоды всегда выглядят ярче, чем зелёные — и получается не жёлтый и даже не оранжевый цвет, а практически полностью красный.

Очевидный программный способ регулировки яркости — поставить разные длительности задержек: в первом примере для этого достаточно изменить дефайны del1 и del2, во втором примере потребуется загружать в регистр ARR разные значения на разных шагах. Но это всё понятно и вопросов не возникает.

Аппаратный способ регулировки для кого-то может быть не столь очевидным. Понятно что можно изменить сопротивление токоограничительного резистора, но так изменится яркость обоих кристаллов. Поэтому сделаем немного хитрее — пустим ток в одном направлении по одному резистору, а в другом направлении — по другому. Это, конечно, очень легко сделать двумя диодами.