#include "iarduino_NeoPixel.h" iarduino_NeoPixel::iarduino_NeoPixel(uint8_t i, uint16_t j){ pinOutput = i; // Сохраняем номер выхода, к которому подключены светодиоды lenLED = j; // Сохраняем количество подключённых светодиодов portOutput = portOutputRegister(digitalPinToPort(pinOutput)); // Получаем указатель на адрес регистра выходных значений порта на котором присутствует выход pinOutput. Функция digitalPinToPort возвращает номер, соответствующий порту на котором находится вывод pinOutput, а функция portOutputRegister возвращает указатель на адрес регистра выходных значений этого порта pinMask = digitalPinToBitMask(pinOutput); // Получаем маску вывода, это байт в котором установлен только один бит. Порядковый номер этого бита, соответствует номеру вывода pinOutput (от 0 до 7) на порту portOutput } bool iarduino_NeoPixel::begin(void){ pinMode (pinOutput, OUTPUT); // Конфигурируем вывод pinOutput как выход digitalWrite (pinOutput, LOW); // Устанавливаем на выходе pinOutput уровень логического «0» lenRGB = lenLED*3; // Получаем размер массива цветов светодиодов if((arrRGB = (uint8_t *) malloc(lenRGB))){ // Выделяем область памяти, размером lenRGB байт, под массив для хранения цветов светодиодов, адрес первой ячейки памяти массива передаём указателю arrRGB memset (arrRGB, 0, lenRGB); // Сбрасываем в 0 все байты массива по указателю arrRGB }else {lenRGB=0; return false;} // Если память выделить не удалось, то указываем размер массива равным 0 байт и возвращаем false return true; // Возвращаем флаг успешной инициализации } void iarduino_NeoPixel::setColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t j){ if(lenRGB){ // Если была успешно вызвана функция begin, то ... if(j<127){ r=map(j, 0,126,0, r); g=map(j, 0,126,0, g); b=map(j, 0,126,0, b);}else // Если требуется затемнить цвет, то ... if(j>127){ r=map(j,128,255,r,255); g=map(j,128,255,g,255); b=map(j,128,255,b,255);} // Если требуется осветлить цвет, то ... if(n == NeoPixelAll){ // Если требуется установить цвета для всех светодиодов, то ... for(uint16_t i=0; i n){ // Если требуется установть цвет только одному светодиоду и количество светодиодов больше чем номер нужного светодиода, то ... arrRGB[n*3+0] = g; // Устанавливаем цвет Green (зелёный) arrRGB[n*3+1] = r; // Устанавливаем цвет Red (красный) arrRGB[n*3+2] = b; // Устанавливаем цвет Blue (синий) } } } void iarduino_NeoPixel::write(){ uint8_t bitLow = *portOutput & ~pinMask; // Получаем значение, запись которого в регистр по адресу portOutput, приведёт к установке логического «0» на вывхде pinOutput, не затрагивая уровни других выводов uint8_t bitHigh = *portOutput | pinMask; // Получаем значение, запись которого в регистр по адресу portOutput, приведёт к установке логической «1» на вывхде pinOutput, не затрагивая уровни других выводов uint8_t byte = 0; uint8_t bit = 8; if(lenRGB){ // Передаём данные, если количество светодиодов больше 0 и была успешно вызвана функция begin // Время передачи одного бита = 1250 нс (20 м.т.) // ______ // | 375нс| 875нс (14мт) // | (6мт)|________________ передача логического «0» // _______________ // | 875 нс | 375нс // | (14 м.т.) |_______ передача логической «1» // // HHHHHH XXXXXXXX LLLLLL уровни машинный тактов (м.т.) 1 м.т. = 62,5 нс // 123456 78901234 567890 noInterrupts(); // Запрещаем исполнение прерываний во время вывода данных asm volatile( // Команды Assembler "getbyte: \n\t" // Метака getbyte. "ldi %[bit] , 8 \n\t" // 1 м.т. => T=01 bit = 8 Указываем что в передаваемом байте есть 8 не переданных битов "ld %[byte] , %a[arr]+ \n\t" // 2 м.т. => T=03 byte = *arr++ Получаем 1 байт для передачи, копируя его из СОЗУ в регистр byte. Адрес ячейки СОЗУ берётся из указателя arr, значение которого мы увеличиваем после получения байта "sbiw %[len] , 1 \n\t" // 2 м.т. => T=05 len-- Уменьшаем значение регистра len (счётчик количества переданных байт) на 1 "brne sendbyte \n\t" // 2 м.т. => T=07 if(len>0){goto sendbyte} Если полученный байт не за пределами счетчика len, то переходим к блоку sendbyte для передачи этого байта "rjmp exitsend \n\t" // 2 м.т. goto exitsend Выходим из Ассемблерной вставки "sendbyte: \n\t" // (для 0/1) (для 0/1) Метка sendbyte. "st %a[port], %[high] \n\t" // 2 м.т. => T=02 port = high Устанавливаем на выводе pinOutput уровень логической «1» (записывая в порт port значение регистра high) "nop \n\t" // 1 м.т. => T=03 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=04 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=05 Пропускаем один машинный такт (м.т.) "sbrs %[byte] , 7 \n\t" // 1/2 м.т. => T=06/07 if(byte & b10000000){jump} Если установлен 7 бит в регистре byte, то следующая строка пропускается. Команда выполняется за 2 м.т. если условие соблюдается, иначе за 1 м.т. "st %a[port], %[low] \n\t" // 2 м.т. => T=08 port = high Устанавливаем на выводе pinOutput уровень логического «0» (записывая в порт port значение регистра low) "rol %[byte] \n\t" // 1 м.т. => T=09/08 byte <<= 1 Сдвигаем влево все биты находящиеся в byte, (т.к. при передаче бита мы сверяемся со значением старшего бита) "nop \n\t" // 1 м.т. => T=10/09 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=11/10 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=12/11 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=13/12 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=14/13 Пропускаем один машинный такт (м.т.) "nop \n\t" // 1 м.т. => T=15/14 Пропускаем один машинный такт (м.т.) "st %a[port], %[low] \n\t" // 2 м.т. => T=17/16 port = low Устанавливаем на выводе pinOutput уровень логического «0» (записывая в порт port значение регистра low) "dec %[bit] \n\t" // 1 м.т. => T=01 bit-- Уменьшаем значение регистра bit (счетчик не переданных битов байта) на 1 "breq getbyte \n\t" // 1/2 м.т. => T=03 if(bit==0){goto} Если значение bit равно 0 (все биты переданы), то переходим к метке getbyte (для получения следующего байта) "rjmp sendbyte \n\t" // 2 м.т. => T=05 goto sendbyte Переходим к передачи следующего бита текущего байта "exitsend: \n" :// Список выходных параметров [port] "+e" (portOutput), // +e один из двухбайтных регистров указателей x,y,z Адрес порта для передачи данных [byte] "+r" (byte), // +r любой однобайтный регистр от r0 до r31 Передаваемый байт данных [bit] "+r" (bit) // +r любой однобайтный регистр от r0 до r31 Количество еще не переданных бит из byte :// Список входных параметров [low] "r" (bitLow), // r любой однобайтный регистр от r0 до r31 Значение которое требуется записать в порт, чтоб на выводе pinOutput установился логический «0» [high] "r" (bitHigh), // r любой однобайтный регистр от r0 до r31 Значение которое требуется записать в порт, чтоб на выводе pinOutput установилась логическая «1» [arr] "e" (arrRGB), // e один из двухбайтных регистров указателей x,y,z Адрес первой ячейки блока памяти в котором находится массив данных [len] "w" (lenRGB+1) // w один из верхних двухбайтных регистров r24,r26,r28,r30 Счетчик количества переданных байтов ); interrupts(); // Возобновляем исполнение прерываний (если таковые были) } } void iarduino_NeoPixel::setColor(uint16_t n, uint32_t rgb, uint8_t j){setColor(n, (rgb&0xFF0000)>>16, (rgb&0x00FF00)>>8, (rgb&0x0000FF), j);}