TerraStat/lib/iarduino_NeoPixel/iarduino_NeoPixel.cpp

102 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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);}