102 lines
12 KiB
C++
102 lines
12 KiB
C++
#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<lenLED; i++){ // Проходим по всем светодиодам
|
||
arrRGB[i*3+0] = g; // Устанавливаем цвет Green (зелёный)
|
||
arrRGB[i*3+1] = r; // Устанавливаем цвет Red (красный)
|
||
arrRGB[i*3+2] = b; // Устанавливаем цвет Blue (синий)
|
||
}
|
||
}else if(lenLED > 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);}
|