13. Схема Charlieplexing (4 линии - 12 светодиодов)

13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 16 сен 2014, 22:23

Краткий порядок темы :

Первая страница :
1. Вступительная часть.
2. Дополнительный материал.
3. Монтаж схемы на макетке.
4. Простые задания для начинающих (программы).
5. Печатные платы модулей.
6. Особенность монтажа разъёма PLH на печатной плате модуля.
7. Программа - статический режим вывода.
8. Программа - простой индикатор уровня (точка).
9. Программа - скорость движения “бегущего огня” зависит от громкости.
10. Программа - динамический режим вывода.

Вторая страница :
11. Программа – индикатор уровня (динамический вывод).
12. Вариант электронного конструктора !

===============================

1. Вступительная часть.

Схема включения светодиодов Чарлиплексинг ( Charlieplexing на Wiki ) предложена в 1995 году Чарли Алленом. На следующем изображении вариант схемы для четырёх линий управления и 12-ти светодиодов :

Изображение

D1Attiny13A-PU; С10,1 мкФ; R110 кОм; R2-R5 150 Ом; разъём X1 - BH-10.

В схеме используется микроконтроллер ATtiny13A, выводы PB0-PB3 которого являются четырьмя линиями управления. Вывод PB4 микроконтроллера используется для подключения внешнего управляющего сигнала (от кнопки, фототранзистора, тактирующего генератора, напряжения для АЦП микроконтроллера, кодированного последовательного сигнала для программного UART-а микроконтроллера и так далее).

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

Изображение

Прозрачные столбики на фотографиях - это различные варианты применения данной схемы ("бегущие огни", "индикатор уровня", "падающая капля"). Максимальная высота столбика более полуметра.

2. Дополнительный материал :

- “Падение напряжения на светодиоде.”
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 16 сен 2014, 22:33

3. Монтаж схемы на макетке.

На следующем изображении несколько изменённая схема из первого сообщения :

Изображение

Она перерисована для удобства монтажа на макетке :

Изображение

Дополнительно к входу PB4 подключена кнопка.

Порядок монтажа на макетке :

3-1. Установите светодиоды VD1-VD12 и перемычки между ними по схеме. Обратите внимание на чередование полярности светодиодов. Чтобы светодиоды на макетке стояли более ровно, их выводы (анод и катод) можно сделать одинаковыми (можно подрезать анодный вывод) :

Изображение

3-2. Выполните дополнительный проводной монтаж и установите сопротивления R2-R5 по схеме, при этом, сам микроконтроллер в макетку устанавливать пока не надо :

Изображение

3-3. Проверьте правильность монтажа светодиодов. Для этого необходимо последовательно попарно подавать сигналы на соответствующие выводы сопротивлений R2-R5 согласно таблице :

Изображение

На изображении подключение для проверки светодиода VD1.

Изображение

Конечный вариант сборки :

Изображение
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 16 сен 2014, 22:40

4. Простые задания для начинающих.

1. По коду программ определите последовательность включаемых светодиодов.
2. Допишите код программ, чтобы работал "бегущий огонь" для всех светодиодов (начиная c VD1 до VD12 и по кругу).

4-1 :

Код: выделить все
//мк ATtiny13A
//F_CPU = 1200000

#include <avr/io.h>
#include <util/delay.h>  // Подключение системного файла для работы с задержками

#define DEL     _delay_ms(1000)  // Задержка 1 секунда (1000 миллисекунд)
                 
int main(void)               

   PORTB = 0b11110000; 
   DDRB  = 0b00000000;
 
  while (1)
  {       
   PORTB = 0b11110010;
   DDRB  = 0b00000110;
   DEL;     
   PORTB = 0b11110100;
   DDRB  = 0b00000110;
   DEL;   
   PORTB = 0b11110100;
   DDRB  = 0b00001100;
   DEL;     
   PORTB = 0b11111000;
   DDRB  = 0b00001100;
   DEL; 
   PORTB = 0b11110001;
   DDRB  = 0b00000101;
   DEL;     
   PORTB = 0b11110100;
   DDRB  = 0b00000101;
   DEL;   
   }
}

4-2 :

Код: выделить все
//MCU = ATtiny13a
//F_CPU = 1200000

#include <avr/io.h>
#include <util/delay.h>  // Подключение системного файла для работы с задержками

#define DEL     _delay_ms(1000)  // Задержка 1 секунда (1000 миллисекунд)

typedef unsigned char uchar;

// led_ddr - массив значений четырех битов регистра DDRB для включения нужного светодиода
// led_port - массив значений четырех битов регистра PORTB для включения нужного светодиода

const uchar led_ddr[]  = {
                    0b0110, 0b0110,
                    0b1100, 0b1100, 0b0101, 0b0101};

const uchar led_port[]= {
                    0b0010, 0b0100,
                    0b0100, 0b1000, 0b0001, 0b0100};

int main(void) {
    PORTB = 0b11110000;
    DDRB =  0b00000000;

    while(1) {
        PORTB = led_port[0];          // записать в PORTB значение из массива led_port с индексом 0
        DDRB = led_ddr[0];            // записать в PORTB значение из массива led_ddr с индексом 0
        DEL;                          // задержка

        PORTB = led_port[1];          // записать в PORTB значение из массива led_port с индексом 1
        DDRB = led_ddr[1];            // записать в PORTB значение из массива led_ddr с индексом 1
        DEL;                          // задержка

        PORTB = led_port[2];          // записать в PORTB значение из массива led_port с индексом 2
        DDRB = led_ddr[2];            // записать в PORTB значение из массива led_ddr с индексом 2
        DEL;                          // задержка

        PORTB = led_port[3];          // записать в PORTB значение из массива led_port с индексом 3
        DDRB = led_ddr[3];            // записать в PORTB значение из массива led_ddr с индексом 3
        DEL;                          // задержка

        PORTB = led_port[4];          // записать в PORTB значение из массива led_port с индексом 4
        DDRB = led_ddr[4];            // записать в PORTB значение из массива led_ddr с индексом 4
        DEL;                          // задержка

        PORTB = led_port[5];          // записать в PORTB значение из массива led_port с индексом 5
        DDRB = led_ddr[5];            // записать в PORTB значение из массива led_ddr с индексом 5
        DEL;                          // задержка
    }
}

4-3 :

Код: выделить все
//MCU = ATtiny13a
//F_CPU = 1200000

#include <avr/io.h>
#include <util/delay.h>  // Подключение системного файла для работы с задержками

#define DEL     _delay_ms(1000)  // Задержка 1 секунда (1000 миллисекунд)

typedef unsigned char uchar;

// led_ddr - массив значений четырех битов регистра DDRB для включения нужного светодиода
// led_port - массив значений четырех битов регистра PORTB для включения нужного светодиода

const uchar led_ddr[]  = {
                    0b0110, 0b0110,
                    0b1100, 0b1100, 0b0101, 0b0101};

const uchar led_port[]= {
                    0b0010, 0b0100,
                    0b0100, 0b1000, 0b0001, 0b0100};

int main(void) {
    PORTB = 0b11110000;
    DDRB =  0b00000000;

    uchar cnt = 0;      // Счетчик
    while(1) {
        PORTB = led_port[cnt];          // записать в PORTB значение из массива led_port с индексом cnt
        DDRB = led_ddr[cnt];            // записать в PORTB значение из массива led_ddr с индексом cnt
        DEL;                            // задержка

        cnt++;                          // увеличить счетчик на один
        if(cnt == sizeof(led_ddr))      // sizeof() - определяет количество элементов в массиве
            cnt = 0;
    }
}
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 18 сен 2014, 01:19

5. Печатные платы модулей.

На фотографиях можно увидеть прозрачные столбики. В них светодиоды включены по данной схеме :

Изображение

На следующем изображении столбики без прозрачной оболочки :

Изображение

В конструкции модуля используется два вида печатных плат - плата переходник для микроконтроллера ATtiny13A (два варианта) и набор из двенадцати плат для установки на них светодиодов. Основное отличие между вариантами печатных плат (позиций 1 и 2,3) является расстояние между проводниками линий управления (соответственно 7,62 мм и 5,08 мм). В обоих вариантах можно использовать светодиоды 3 мм (поз.1 и 2) и 5 мм (поз. 3). В качестве защитного экрана для варианта с расстоянием 5,08 мм используется полупрозрачная термоусадка (диаметр 10 мм, на изображении !). В изготовлении конструкций использовался провод НВ1-0.5 мм2.

Изображение

Печатные платы односторонние. Рисунок топологии печатных плат выполнен во втором слое (на изображении смотрим сквозь стеклотекстолит). Зелёным цветом обозначена перемычка. Слева два вида плат для микроконтроллера (с разъёмом для программатора и без него), справа набор из двенадцати плат для установки светодиодов.

При изготовлении модуля используются разъёмы PLH-40 (межплатный, два отрезка по 4 контакта) и BH-10R (для подключения шлейфа от программатора), SMD сопротивления типа 0805.
Вложения
t13cp45.zip
файлы печатных плат (lay6)
(7.99 KiB) Скачиваний: 724
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 18 сен 2014, 01:21

Набор печатных плат с расстоянием между линиями управления - 7,62 мм

Изображение
Вложения
t13cp47.zip
файлы печатных плат (lay6)
(14.67 KiB) Скачиваний: 706
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 19 сен 2014, 01:25

6. Особенность монтажа разъёма PLH на плате.

Печатные платы модулей для микроконтроллеров являются односторонними, при этом, контактные площадки для пайки штырей разъёма PLH находятся со стороны его установки (со стороны нижнего слоя платы, а не верхнего). И поэтому на подборке изображений приводится разъясняющая последовательность монтажа разъёма. Рассматривается вариант монтажа разъёма PLH одной из версий плат без разъёма для программатора :

Изображение

1. Припаиваются SMD сопротивления.
2. В отверстия платы устанавливаются две 4-ёх контактные части от разъёма PLH. В данном случае штыри разъёма должны быть на уровне верхней поверхности печатной платы или чуть выше.
3. Плата переворачивается для пайки штырей.
4. Штыри разъёма качественно !!! припаиваются к площадкам печатной платы.
5. Спаянный модуль короткими выводами устанавливается в макетку и аккуратно надавливается в его центре (по стрелке).
6. При надавливании штыри разъёма опускаются до дна макетки, а пластмассовая его часть сдвигается вверх. В зависимости от производителя разъёма PLH сила давления может быть разной. Для качественных разъёмов она больше.
7. Вид на модуль после монтажа разъёма PLH.
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 22 сен 2014, 11:57

7. Программа - статический режим вывода.

В программе реализованы простые 12-ти канальные "бегущие огни" с управлением от кнопки (к входу PB4 подключена кнопка). При нажатии на кнопку меняется направление сдвига "бегущего огня". Состояние фьюзов - по-умолчанию (при покупке микроконтроллера, ничего не меняется).

Код: выделить все
//MCU = ATtiny13a
//F_CPU = 1200000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

typedef unsigned char uchar;
typedef unsigned int uint;

// Запуск таймера с предделителем на 1024
#define ENABLE_TIMER \
    TCCR0B &= 0b11111000;\
    TCCR0B |= 0b00000101;

// PROGMEM - хранить массивы в памяти программ, а не в оперативной.
// led_ddr - массив значений четырех битов регистра DDRB для включения нужного светодиода
// led_port - массив значений четырех битов регистра PORTB для включения нужного светодиода
// индекс массива соответствует номеру светодиода.

const uchar led_ddr[12] PROGMEM  = {
                    0b0011, 0b0011, 0b0110, 0b0110,
                    0b1100, 0b1100, 0b0101, 0b0101,
                    0b1001, 0b1001, 0b1010, 0b1010};

const uchar led_port[12] PROGMEM = {
                    0b0001, 0b0010, 0b0010, 0b0100,
                    0b0100, 0b1000, 0b0001, 0b0100,
                    0b0001, 0b1000, 0b0010, 0b1000};

// Функция включения одного из 12 светодиодов
// i - номер светодиода
static void led_on(uchar i);

uchar direction = 0;        // направление
uchar clock = 0;            // счетчик

// Обработчик прерывания таймера по совпадению
ISR(TIM0_COMPA_vect, ISR_BLOCK) {

    if(direction == 0)      // выбор направления движения
        led_on(clock);
    else
        led_on(11 - clock);

    clock++;
    if(clock == 12)
        clock = 0;
}

// Oбработчик прерывания PCINT0 на ножке PB4
ISR(PCINT0_vect) {
    _delay_ms(10);      // Антидребезг

    if(bit_is_clear(PINB, PB4)) {   // Если прерывание было вызвано нажатием кнопки на PB4, то сменить направление.
        direction ^= 1;
        clock = 11 - clock;
    }
}

int main(void) {
    DDRB = 0b00000011;
    PORTB = 0b00010001;

    TCCR0A = 0b00000010;      // Режим - сброс по совпадению (CTC)
    TIMSK0 = (1 << OCIE0A);    // Разрешение прерываний по совпадению с OCR0A

    OCR0A = 60;
    ENABLE_TIMER;

    GIMSK = 0b00100000;         // Разрешение прерываний PCINT0
    PCMSK = 0b00010000;         // Разрешение прерываний PCINT0 на PB4

    sei();                      // Глобальное разрешение прерываний
    for(;;) {}                  // Бесконечный цикл
}

// Функция включения одного из 12 светодиодов
// i - номер светодиода
void led_on(uchar i){
    DDRB &= 0b11110000;     // Обнуление ножек к которым подключены светодиоды
    PORTB &= 0b11110000;

    DDRB |= pgm_read_byte(led_ddr + i);        // установка значений из массива в регистры для включения нужного светодиода
    PORTB |= pgm_read_byte(led_port + i);
}

/* Макрос pgm_read_byte(array + i)
* Возвращает байт из массива array под индексом i.
* Используется вместо array[i],
* так как массивы led_ddr и led_port сохранены не в оперативной памяти,
* а в памяти программ.
*/

Дмитрий Лиман (студент НИЯУ МИФИ)
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 28 сен 2014, 01:04

8. Программа - простой индикатор уровня (точка).

В примере используется статический режим вывода индикации и по ходу последующих вопросов предлагается реализовать вариант простого индикатора уровня. Разряд PB4 (вывод 3) микроконтроллера используется как вход АЦП (ADC2). Состояние фьюзов - по-умолчанию (при покупке микроконтроллера).

Код: выделить все
//MCU = ATtiny13a
//F_CPU = 1200000

#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

typedef unsigned char uchar;

// PROGMEM - хранить массивы в памяти программ, а не в оперативной.
// led_ddr - массив значений четырех битов регистра DDRB для включения нужного светодиода
// led_port - массив значений четырех битов регистра PORTB для включения нужного светодиода
// иднекс массива соответствует номеру светодиода.

const uchar led_ddr[12] PROGMEM  = {
                    0b0011, 0b0011, 0b0110, 0b0110,
                    0b1100, 0b1100, 0b0101, 0b0101,
                    0b1001, 0b1001, 0b1010, 0b1010};

const uchar led_port[12] PROGMEM = {
                    0b0001, 0b0010, 0b0010, 0b0100,
                    0b0100, 0b1000, 0b0001, 0b0100,
                    0b0001, 0b1000, 0b0010, 0b1000};

// Функция включения одного из 12 светодиодов
// i - номер светодиода
static void led_on(uchar i);

int main(void) {
    DDRB = 0b00000011;
    PORTB = 0b00000001;

    ADMUX  = 0b00100010;
    ADCSRA = 0b11100111;
    DIDR0 = 0b00010000;

    while(1) {
        _delay_ms(1);
      led_on(ADCH / 22); // включение нужного светодиода в зависимости от значения на АЦП
    }
}

// Функция включения одного из 12 светодиодов
// i - номер светодиода
void led_on(uchar i){
    DDRB  &= 0b11110000;     // Обнуление ножек к которым подключены светодиоды
    PORTB &= 0b11110000;

    DDRB |= pgm_read_byte(led_ddr + i);        // установка значений из массива в регистры для включения нужного светодиода
    PORTB |= pgm_read_byte(led_port + i);
}

/* Макрос pgm_read_byte(array + i)
* Возвращает байт из массива array под индексом i.
* Используется вместо array[i],
* так как массивы led_ddr и led_port сохранены не в оперативной памяти,
* а в памяти программ.
*/

Дмитрий Лиман (студент НИЯУ МИФИ)

Задание :

1. По справочнику разберите назначения всех разрядов регистров ADMUX; ADCSRA; DIDR0.

2. При измерении напряжения от 0 до 5 вольт на входе АЦП (вывод 3) должны последовательно по одному включаться все двенадцать светодиодов. Объясните, почему в строке “led_on (ADCH / 22)” значение делителя установлено 22.

3. Очень вероятно, что максимальное значение напряжения от источника сигнала будет не 5 вольт, а например, 1.5 вольта. Определите и примените вид выражения “led_on (ADCH / 22)” таким, чтобы при изменении напряжения от 0 до 1.5 вольт на входе АЦП также последовательно по одному включались все двенадцать светодиодов.

Дополнительный материал : - другой пример работы АЦП.
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 28 сен 2014, 01:14

9. Программа - скорость движения “бегущего огня” зависит от громкости.

В светодинамических устройствах почти всегда скорость движения “бегущего огня” зависит от уровня громкости, например, в помещении. Данным примером предлагается вариант реализации такой зависимости. В примере используется статический режим вывода индикации и скорость движения “бегущего огня” зависит от напряжения на входе АЦП (ADC2, вывод 3). Максимальное напряжение входного сигнала – около 2 вольт. Состояние фьюзов - по-умолчанию (при покупке микроконтроллера).

Код: выделить все
//MCU = ATtiny13a
//F_CPU = 1200000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

typedef unsigned char uchar;

// Запуск таймера с предделителем на 1024
#define ENABLE_TIMER \
    TCCR0B &= 0b11111000;\
    TCCR0B |= 0b00000101;

// PROGMEM - хранить массивы в памяти программ, а не в оперативной.
// led_ddr - массив значений четырех битов регистра DDRB для включения нужного светодиода
// led_port - массив значений четырех битов регистра PORTB для включения нужного светодиода
// иднекс массива соответствует номеру светодиода.

const uchar led_ddr[12] PROGMEM  = {
                    0b0011, 0b0011, 0b0110, 0b0110,
                    0b1100, 0b1100, 0b0101, 0b0101,
                    0b1001, 0b1001, 0b1010, 0b1010};

const uchar led_port[12] PROGMEM = {
                    0b0001, 0b0010, 0b0010, 0b0100,
                    0b0100, 0b1000, 0b0001, 0b0100,
                    0b0001, 0b1000, 0b0010, 0b1000};

// Функция включения одного из 12 светодиодов
// i - номер светодиода
static void led_on(uchar i);

// Обработчик прерывания таймера по совпадению
ISR(TIM0_COMPA_vect, ISR_BLOCK) {
    static uchar clock = 0;                 // счетчик
    uchar adcValue = ADCH;                  // считывание значения на АЦП

    if(adcValue > 6) {                      // Порог срабатывания
        OCR0A = 200 - (adcValue << 1);      // Установка скорости бегущего огня в зависимости от значения на АЦП
        led_on(clock);

        clock++;
        if(clock == 12)
            clock = 0;
    }
}

int main(void) {
    DDRB = 0b00000011;
    PORTB = 0b00000001;

    TCCR0A = 0b00000010;      // Режим - сброс по совпадению (CTC)
    TIMSK0 = (1 << OCIE0A);    // Разрешение прерываний по совпадению с OCR0A

    OCR0A = 60;
    ENABLE_TIMER;

    ADMUX  = 0b00100010;
    ADCSRA = 0b11100111;
    DIDR0 = 0b00010000;

    sei();                      // Глобальное разрешение прерываний
    for(;;) {}                  // Бесконечный цикл
}

// Функция включения одного из 12 светодиодов
// i - номер светодиода
void led_on(uchar i){
    DDRB  &= 0b11110000;     // Обнуление ножек к которым подключены светодиоды
    PORTB &= 0b11110000;

    DDRB |= pgm_read_byte(led_ddr + i);        // установка значений из массива в регистры для включения нужного светодиода
    PORTB |= pgm_read_byte(led_port + i);
}
/* Макрос pgm_read_byte(array + i)
* Возвращает байт из массива array под индексом i.
* Используется вместо array[i],
* так как массивы led_ddr и led_port сохранены не в оперативной памяти,
* а в памяти программ.
*/

Дмитрий Лиман (студент НИЯУ МИФИ)
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

Re: 13. Схема Charlieplexing (4 линии - 12 светодиодов)

Сообщение VVZ » 28 сен 2014, 14:54

10. Программа - динамический режим вывода.

В программе реализованы 12-ти канальные "бегущие огни" (режим Счёт) с управлением от кнопки (к входу PB4 подключена кнопка). При нажатии на кнопку меняется направление "бегущего огня". Состояние фьюзов - меняется, убирается делитель частоты на 8 (частота 1200000x8=9600000 Гц).

Код: выделить все
//MCU = ATtiny13a
//F_CPU = 9600000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

typedef unsigned char uchar;
typedef unsigned int uint;

// Запуск таймера с предделителем на 1024
#define ENABLE_TIMER \
    TCCR0B &= 0b11111000;\
    TCCR0B |= 0b00000011;

uint ledState = 1;      // глобальная переменная, биты которой соответствуют состоянию светодиодов
uchar direction = 0;    // направление бегущего огня

// PROGMEM - хранить массивы в памяти программ, а не в оперативной.
// led_ddr - массив значений четырех битов регистра DDRB для включения нужного светодиода
// led_port - массив значений четырех битов регистра PORTB для включения нужного светодиода
// иднекс массива соответствует номеру светодиода.

const uchar led_ddr[12] PROGMEM  = {
                    0b0011, 0b0011, 0b0110, 0b0110,
                    0b1100, 0b1100, 0b0101, 0b0101,
                    0b1001, 0b1001, 0b1010, 0b1010};

const uchar led_port[12] PROGMEM = {
                    0b0001, 0b0010, 0b0010, 0b0100,
                    0b0100, 0b1000, 0b0001, 0b0100,
                    0b0001, 0b1000, 0b0010, 0b1000};

// Функция включения одного из 12 светодиодов
// i - номер светодиода
static void led_on(uchar i);

static void led_scan(void);                 // Динамическая развертка
static uint countLeft(uint port);       // Счет влево
static uint countRight(uint port);      // Счет вправо

//Обработчик прерывания таймера по совпадению
ISR(TIM0_COMPA_vect, ISR_BLOCK) {
    static uint clock = 0;

    led_scan();                   // динамическая развертка

    if(clock % 256 == 0) {        // Каждые 256 вызовов прерывания - выполнять один шаг счета
        if(direction == 0)        // Выбор направления
            ledState = countRight(ledState);
        else
            ledState = countLeft(ledState);
    }

    clock++;
}

// Oбработчик прерывания PCINT0 на ножке PB4
ISR(PCINT0_vect, ISR_NOBLOCK) {
    _delay_ms(10);                  // Антидребезг

    if(bit_is_clear(PINB, PB4)) {   // Если прерывание было вызвано нажатием кнопки на PB4, то сменить направление.
        direction ^= 1;
    }
}

int main(void) {
    DDRB =  0b00000011;
    PORTB = 0b00010001;

   TCCR0A = 0b00000010;      //Режим - сброс по совпадению (CTC)
   TIMSK0 = (1 << OCIE0A);    //Разрешение прерывания по совпадению с OCR0A

    OCR0A = 64;
    ENABLE_TIMER;

    GIMSK = 0b00100000;         // Разрешение прерываний PCINT0
    PCMSK = 0b00010000;         // Разрешение прерываний PCINT0 на PB4

    sei();                      // Глобальное разрешение прерываний
    for(;;) {}                  // Бесконечный цикл
}

// Функция включения одного из 12 светодиодов
// i - номер светодиода
void led_on(uchar i){
    DDRB &= 0b11110000;     // Обнуление ножек к которым подключены светодиоды
    PORTB &= 0b11110000;

    DDRB |= pgm_read_byte(led_ddr + i);        // установка значений из массива в регистры для включения нужного светодиода
    PORTB |= pgm_read_byte(led_port + i);
}

/* Макрос pgm_read_byte(array + i)
* Возвращает байт из массива array под индексом i.
* Используется вместо array[i],
* так как массивы led_ddr и led_port сохранены не в оперативной памяти,
* а в памяти программ.
*/

// Функция динамической развертки
void led_scan(void) {
    static uchar i = 0;
    static uint shiftedLedState = 0;

    if((shiftedLedState & 0x01) == 1)   // Если i-ый бит равен 1, то зажечь i-ый светодиод, иначе отключить все светодиоды
        led_on(11-i);
    else
        PORTB &= 0xF0;

    i++;
    shiftedLedState >>= 1;
    if(i == sizeof(led_ddr)) {          // Если все биты пройдены, то начать заново
        i = 0;
        shiftedLedState = ledState;
    }
}

// Счет вправо
uint countRight(uint port) {
    if((port & 0x01) == 1)
        port >>= 1;
    else {
        port >>= 1;
        port |= 0b100000000000;
    }
    return port;
}

// Счет влево
uint countLeft(uint port) {
    if((port & 0b100000000000) == 0) {
        port <<= 1;
        port |= 0b00000001;
    }
    else
        port <<= 1;
    return port;
}

Дмитрий Лиман (студент НИЯУ МИФИ)
VVZ
 
Сообщений: 589
Зарегистрирован: 28 апр 2011, 09:44

След.

Вернуться в Микроконтроллеры - Оборудование (программаторы, схемы с МК)

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 0