mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Minor reformat of code to match hyperion-style. Added ws2812b as option to disable for devices without dma-pwm
Former-commit-id: 579f4426285fb537706a22af446f5280748f2ab7
This commit is contained in:
parent
c0f0837b92
commit
8ad0e88e2f
@ -13,6 +13,9 @@ message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
|
|||||||
option(ENABLE_SPIDEV "Enable the SPIDEV device" ON)
|
option(ENABLE_SPIDEV "Enable the SPIDEV device" ON)
|
||||||
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
|
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
|
||||||
|
|
||||||
|
option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" ON)
|
||||||
|
message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM})
|
||||||
|
|
||||||
option(ENABLE_V4L2 "Enable the V4L2 grabber" ON)
|
option(ENABLE_V4L2 "Enable the V4L2 grabber" ON)
|
||||||
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
// Define to enable the spi-device
|
// Define to enable the spi-device
|
||||||
#cmakedefine ENABLE_SPIDEV
|
#cmakedefine ENABLE_SPIDEV
|
||||||
|
|
||||||
|
// Define to enable the ws2812b-pwm-device
|
||||||
|
#cmakedefine ENABLE_WS2812BPWM
|
||||||
|
|
||||||
// Define to enable the spi-device
|
// Define to enable the spi-device
|
||||||
#cmakedefine ENABLE_TINKERFORGE
|
#cmakedefine ENABLE_TINKERFORGE
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ if(ENABLE_SPIDEV)
|
|||||||
)
|
)
|
||||||
endif(ENABLE_SPIDEV)
|
endif(ENABLE_SPIDEV)
|
||||||
|
|
||||||
|
if(ENABLE_WS2812BPWM)
|
||||||
SET(Leddevice_HEADERS
|
SET(Leddevice_HEADERS
|
||||||
${Leddevice_HEADERS}
|
${Leddevice_HEADERS}
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.h
|
${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.h
|
||||||
@ -76,6 +77,7 @@ SET(Leddevice_SOURCES
|
|||||||
${Leddevice_SOURCES}
|
${Leddevice_SOURCES}
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.cpp
|
${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.cpp
|
||||||
)
|
)
|
||||||
|
endif(ENABLE_WS2812BPWM)
|
||||||
|
|
||||||
if(ENABLE_TINKERFORGE)
|
if(ENABLE_TINKERFORGE)
|
||||||
SET(Leddevice_HEADERS
|
SET(Leddevice_HEADERS
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
#include "LedDevicePhilipsHue.h"
|
#include "LedDevicePhilipsHue.h"
|
||||||
#include "LedDeviceTpm2.h"
|
#include "LedDeviceTpm2.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_WS2812BPWM
|
||||||
#include "LedDeviceWS2812b.h"
|
#include "LedDeviceWS2812b.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
||||||
{
|
{
|
||||||
@ -183,11 +185,14 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
|||||||
LedDeviceTpm2 * deviceTpm2 = new LedDeviceTpm2(output, rate);
|
LedDeviceTpm2 * deviceTpm2 = new LedDeviceTpm2(output, rate);
|
||||||
deviceTpm2->open();
|
deviceTpm2->open();
|
||||||
device = deviceTpm2;
|
device = deviceTpm2;
|
||||||
}else if (type == "ws2812b")
|
}
|
||||||
|
#ifdef ENABLE_WS2812BPWM
|
||||||
|
else if (type == "ws2812b")
|
||||||
{
|
{
|
||||||
LedDeviceWS2812b * ledDeviceWS2812b = new LedDeviceWS2812b();
|
LedDeviceWS2812b * ledDeviceWS2812b = new LedDeviceWS2812b();
|
||||||
device = ledDeviceWS2812b;
|
device = ledDeviceWS2812b;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << "Unable to create device " << type << std::endl;
|
std::cout << "Unable to create device " << type << std::endl;
|
||||||
|
@ -252,7 +252,8 @@ LedDeviceWS2812b::LedDeviceWS2812b() :
|
|||||||
// init bit pattern, it is always 1X0
|
// init bit pattern, it is always 1X0
|
||||||
unsigned int wireBit = 0;
|
unsigned int wireBit = 0;
|
||||||
|
|
||||||
while ((wireBit + 3) < ((NUM_DATA_WORDS) * 4 * 8)){
|
while ((wireBit + 3) < ((NUM_DATA_WORDS) * 4 * 8))
|
||||||
|
{
|
||||||
setPWMBit(wireBit++, 1);
|
setPWMBit(wireBit++, 1);
|
||||||
setPWMBit(wireBit++, 0); // just init it with 0
|
setPWMBit(wireBit++, 0); // just init it with 0
|
||||||
setPWMBit(wireBit++, 0);
|
setPWMBit(wireBit++, 0);
|
||||||
@ -264,16 +265,16 @@ LedDeviceWS2812b::LedDeviceWS2812b() :
|
|||||||
#ifdef WS2812_ASM_OPTI
|
#ifdef WS2812_ASM_OPTI
|
||||||
|
|
||||||
// rotate register, used to move the 1 around :-)
|
// rotate register, used to move the 1 around :-)
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline)) uint32_t arm_ror_imm(uint32_t v, uint32_t sh)
|
||||||
uint32_t arm_ror_imm(uint32_t v, uint32_t sh) {
|
{
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
asm ("ROR %[Rd], %[Rm], %[Is]" : [Rd] "=r" (d) : [Rm] "r" (v), [Is] "i" (sh));
|
asm ("ROR %[Rd], %[Rm], %[Is]" : [Rd] "=r" (d) : [Rm] "r" (v), [Is] "i" (sh));
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate register, used to move the 1 around, add 1 to int counter on carry
|
// rotate register, used to move the 1 around, add 1 to int counter on carry
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline)) uint32_t arm_ror_imm_add_on_carry(uint32_t v, uint32_t sh, uint32_t inc)
|
||||||
uint32_t arm_ror_imm_add_on_carry(uint32_t v, uint32_t sh, uint32_t inc) {
|
{
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
asm ("RORS %[Rd], %[Rm], %[Is]\n\t"
|
asm ("RORS %[Rd], %[Rm], %[Is]\n\t"
|
||||||
"ADDCS %[Rd1], %[Rd1], #1"
|
"ADDCS %[Rd1], %[Rd1], #1"
|
||||||
@ -281,16 +282,16 @@ uint32_t arm_ror_imm_add_on_carry(uint32_t v, uint32_t sh, uint32_t inc) {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline)) uint32_t arm_ror(uint32_t v, uint32_t sh)
|
||||||
uint32_t arm_ror(uint32_t v, uint32_t sh) {
|
{
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
asm ("ROR %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (sh));
|
asm ("ROR %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (sh));
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline __attribute__((always_inline))
|
static inline __attribute__((always_inline)) uint32_t arm_Bit_Clear_imm(uint32_t v, uint32_t v2)
|
||||||
uint32_t arm_Bit_Clear_imm(uint32_t v, uint32_t v2) {
|
{
|
||||||
uint32_t d;
|
uint32_t d;
|
||||||
asm ("BIC %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (v2));
|
asm ("BIC %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (v2));
|
||||||
return d;
|
return d;
|
||||||
@ -306,10 +307,6 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
mLedCount = ledValues.size();
|
mLedCount = ledValues.size();
|
||||||
//printf("Set leds, number: %d\n", mLedCount);
|
|
||||||
|
|
||||||
// Clear out the PWM buffer
|
|
||||||
// Disabled, because we will overwrite the buffer anyway.
|
|
||||||
|
|
||||||
// Read data from LEDBuffer[], translate it into wire format, and write to PWMWaveform
|
// Read data from LEDBuffer[], translate it into wire format, and write to PWMWaveform
|
||||||
unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern
|
unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern
|
||||||
@ -323,11 +320,10 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
// 72 bits per pixel / 32 bits per word = 2.25 words per pixel
|
// 72 bits per pixel / 32 bits per word = 2.25 words per pixel
|
||||||
// Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes"
|
// Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes"
|
||||||
// Times 4 because DMA works in bytes, not words
|
// Times 4 because DMA works in bytes, not words
|
||||||
// cbp->length = (mLedCount * 2.25) * 4;
|
|
||||||
cbp->length = ((mLedCount * 2.25) + 1) * 4;
|
cbp->length = ((mLedCount * 2.25) + 1) * 4;
|
||||||
if(cbp->length > NUM_DATA_WORDS * 4) {
|
if(cbp->length > NUM_DATA_WORDS * 4)
|
||||||
|
{
|
||||||
cbp->length = NUM_DATA_WORDS * 4;
|
cbp->length = NUM_DATA_WORDS * 4;
|
||||||
// mLedCount = NUM_DATA_WORDS / 2.25;
|
|
||||||
mLedCount = (NUM_DATA_WORDS - 1) / 2.25;
|
mLedCount = (NUM_DATA_WORDS - 1) / 2.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +332,8 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
for(size_t i=0; i<mLedCount; i++) {
|
for(size_t i=0; i<mLedCount; i++)
|
||||||
|
{
|
||||||
// Create bits necessary to represent one color triplet (in GRB, not RGB, order)
|
// Create bits necessary to represent one color triplet (in GRB, not RGB, order)
|
||||||
//printf("RGB: %d, %d, %d\n", ledValues[i].red, ledValues[i].green, ledValues[i].blue);
|
//printf("RGB: %d, %d, %d\n", ledValues[i].red, ledValues[i].green, ledValues[i].blue);
|
||||||
colorBits = ((unsigned int)ledValues[i].red << 8) | ((unsigned int)ledValues[i].green << 16) | ledValues[i].blue;
|
colorBits = ((unsigned int)ledValues[i].red << 8) | ((unsigned int)ledValues[i].green << 16) | ledValues[i].blue;
|
||||||
@ -350,8 +347,6 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
unsigned int wordOffset = (int)(wireBit / 32);
|
unsigned int wordOffset = (int)(wireBit / 32);
|
||||||
wireBit +=3;
|
wireBit +=3;
|
||||||
|
|
||||||
// printBinary(startbitPattern, 32);
|
|
||||||
// printf(" %d\n", j);
|
|
||||||
if (colorBits & (1 << j)) {
|
if (colorBits & (1 << j)) {
|
||||||
PWMWaveform[wordOffset] |= startbitPattern;
|
PWMWaveform[wordOffset] |= startbitPattern;
|
||||||
} else {
|
} else {
|
||||||
@ -359,7 +354,6 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
}
|
}
|
||||||
|
|
||||||
startbitPattern = arm_ror_imm(startbitPattern, 3);
|
startbitPattern = arm_ror_imm(startbitPattern, 3);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed
|
unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed
|
||||||
setPWMBit(wireBit, colorBit);
|
setPWMBit(wireBit, colorBit);
|
||||||
@ -391,9 +385,6 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
//remove one to undo optimization
|
//remove one to undo optimization
|
||||||
wireBit --;
|
wireBit --;
|
||||||
|
|
||||||
// printBinary(PWMWaveform[(int)(wireBit / 32)], 32);
|
|
||||||
// printf(" pre\n");
|
|
||||||
|
|
||||||
#ifdef WS2812_ASM_OPTI
|
#ifdef WS2812_ASM_OPTI
|
||||||
int rest = 32 - wireBit % 32; // 64: 32 - used Bits
|
int rest = 32 - wireBit % 32; // 64: 32 - used Bits
|
||||||
startbitPattern = (1 << (rest-1)); // set new bitpattern to start at the benigining of one bit (3 bit in wave form)
|
startbitPattern = (1 << (rest-1)); // set new bitpattern to start at the benigining of one bit (3 bit in wave form)
|
||||||
@ -406,7 +397,8 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
unsigned int oldbitPattern = startbitPattern;
|
unsigned int oldbitPattern = startbitPattern;
|
||||||
|
|
||||||
// zero rest of the 4 bytes / int so that output is 0 (no data is send)
|
// zero rest of the 4 bytes / int so that output is 0 (no data is send)
|
||||||
for (int i = 0; i < rest; i += 3){
|
for (int i = 0; i < rest; i += 3)
|
||||||
|
{
|
||||||
unsigned int wordOffset = (int)(wireBit / 32);
|
unsigned int wordOffset = (int)(wireBit / 32);
|
||||||
wireBit += 3;
|
wireBit += 3;
|
||||||
PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern);
|
PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern);
|
||||||
@ -419,22 +411,14 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
unsigned int oldwireBitValue = wireBit;
|
unsigned int oldwireBitValue = wireBit;
|
||||||
|
|
||||||
// zero rest of the 4 bytes / int so that output is 0 (no data is send)
|
// zero rest of the 4 bytes / int so that output is 0 (no data is send)
|
||||||
for (int i = 0; i < rest; i += 3){
|
for (int i = 0; i < rest; i += 3)
|
||||||
|
{
|
||||||
setPWMBit(wireBit, 0);
|
setPWMBit(wireBit, 0);
|
||||||
wireBit += 3;
|
wireBit += 3;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// printBinary(PWMWaveform[(int)(oldwireBitValue / 32) -1 ], 32);
|
|
||||||
// printf(" post\n");
|
|
||||||
// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32);
|
|
||||||
// printf(" post\n");
|
|
||||||
|
|
||||||
// This block is a major CPU hog when there are lots of pixels to be transmitted.
|
memcpy ( ctl->sample, PWMWaveform, cbp->length );
|
||||||
// It would go quicker with DMA.
|
|
||||||
// for(unsigned int i = 0; i < (cbp->length / 4); i++) {
|
|
||||||
// ctl->sample[i] = PWMWaveform[i];
|
|
||||||
// }
|
|
||||||
memcpy ( ctl->sample, PWMWaveform, cbp->length ); // memcpy does the same and is potentially faster
|
|
||||||
|
|
||||||
// Enable DMA and PWM engines, which should now send the data
|
// Enable DMA and PWM engines, which should now send the data
|
||||||
startTransfer();
|
startTransfer();
|
||||||
@ -444,35 +428,29 @@ int LedDeviceWS2812b::write(const std::vector<ColorRgb> &ledValues)
|
|||||||
|
|
||||||
#ifdef WS2812_ASM_OPTI
|
#ifdef WS2812_ASM_OPTI
|
||||||
startbitPattern = oldbitPattern;
|
startbitPattern = oldbitPattern;
|
||||||
for (int i = 0; i < rest; i += 3){
|
for (int i = 0; i < rest; i += 3)
|
||||||
|
{
|
||||||
unsigned int wordOffset = (int)(wireBit / 32);
|
unsigned int wordOffset = (int)(wireBit / 32);
|
||||||
wireBit += 3;
|
wireBit += 3;
|
||||||
PWMWaveform[wordOffset] |= startbitPattern;
|
PWMWaveform[wordOffset] |= startbitPattern;
|
||||||
startbitPattern = arm_ror_imm(startbitPattern, 3);
|
startbitPattern = arm_ror_imm(startbitPattern, 3);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
for (int i = 0; i < rest; i += 3){
|
for (int i = 0; i < rest; i += 3)
|
||||||
|
{
|
||||||
setPWMBit(wireBit, 1);
|
setPWMBit(wireBit, 1);
|
||||||
wireBit += 3;
|
wireBit += 3;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32);
|
|
||||||
// printf(" restored\n");
|
|
||||||
|
|
||||||
// Wait long enough for the DMA transfer to finish
|
|
||||||
// 3 RAM bits per wire bit, so 72 bits to send one color command.
|
|
||||||
//float bitTimeUSec = (float)(NUM_DATA_WORDS * 32) * 0.4; // Bits sent * time to transmit one bit, which is 0.4μSec
|
|
||||||
//printf("Delay for %d μSec\n", (int)bitTimeUSec);
|
|
||||||
//usleep((int)bitTimeUSec);
|
|
||||||
|
|
||||||
#ifdef BENCHMARK
|
#ifdef BENCHMARK
|
||||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timeEnd);
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timeEnd);
|
||||||
timespec result;
|
timespec result;
|
||||||
|
|
||||||
result.tv_sec = timeEnd.tv_sec - timeStart.tv_sec;
|
result.tv_sec = timeEnd.tv_sec - timeStart.tv_sec;
|
||||||
result.tv_nsec = timeEnd.tv_nsec - timeStart.tv_nsec;
|
result.tv_nsec = timeEnd.tv_nsec - timeStart.tv_nsec;
|
||||||
if (result.tv_nsec < 0) {
|
if (result.tv_nsec < 0)
|
||||||
|
{
|
||||||
result.tv_nsec = 1e9 - result.tv_nsec;
|
result.tv_nsec = 1e9 - result.tv_nsec;
|
||||||
result.tv_sec -= 1;
|
result.tv_sec -= 1;
|
||||||
}
|
}
|
||||||
@ -491,7 +469,6 @@ int LedDeviceWS2812b::switchOff()
|
|||||||
LedDeviceWS2812b::~LedDeviceWS2812b()
|
LedDeviceWS2812b::~LedDeviceWS2812b()
|
||||||
{
|
{
|
||||||
// Exit cleanly, freeing memory and stopping the DMA & PWM engines
|
// Exit cleanly, freeing memory and stopping the DMA & PWM engines
|
||||||
// We trap all signals (including Ctrl+C), so even if you don't get here, it terminates correctly
|
|
||||||
terminate(0);
|
terminate(0);
|
||||||
#ifdef BENCHMARK
|
#ifdef BENCHMARK
|
||||||
printf("WS2812b Benchmark results: Runs %d - Avarage %lu (n) - Minimum %ld (n)\n",
|
printf("WS2812b Benchmark results: Runs %d - Avarage %lu (n) - Minimum %ld (n)\n",
|
||||||
@ -512,47 +489,46 @@ LedDeviceWS2812b::~LedDeviceWS2812b()
|
|||||||
// Convenience functions
|
// Convenience functions
|
||||||
// --------------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------------
|
||||||
// Print some bits of a binary number (2nd arg is how many bits)
|
// Print some bits of a binary number (2nd arg is how many bits)
|
||||||
void LedDeviceWS2812b::printBinary(unsigned int i, unsigned int bits) {
|
void LedDeviceWS2812b::printBinary(unsigned int i, unsigned int bits)
|
||||||
|
{
|
||||||
int x;
|
int x;
|
||||||
for(x=bits-1; x>=0; x--) {
|
for(x=bits-1; x>=0; x--)
|
||||||
|
{
|
||||||
printf("%d", (i & (1 << x)) ? 1 : 0);
|
printf("%d", (i & (1 << x)) ? 1 : 0);
|
||||||
if(x % 16 == 0 && x > 0) {
|
if(x % 16 == 0 && x > 0)
|
||||||
|
{
|
||||||
printf(" ");
|
printf(" ");
|
||||||
} else if(x % 4 == 0 && x > 0) {
|
}
|
||||||
|
else if(x % 4 == 0 && x > 0)
|
||||||
|
{
|
||||||
printf(":");
|
printf(":");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse the bits in a word
|
// Reverse the bits in a word
|
||||||
unsigned int reverseWord(unsigned int word) {
|
unsigned int reverseWord(unsigned int word)
|
||||||
|
{
|
||||||
unsigned int output = 0;
|
unsigned int output = 0;
|
||||||
//unsigned char bit;
|
//unsigned char bit;
|
||||||
int i;
|
int i;
|
||||||
for(i=0; i<32; i++) {
|
for(i=0; i<32; i++)
|
||||||
//bit = word & (1 << i) ? 1 : 0;
|
{
|
||||||
output |= word & (1 << i) ? 1 : 0;
|
output |= word & (1 << i) ? 1 : 0;
|
||||||
if(i<31) {
|
if(i<31)
|
||||||
|
{
|
||||||
output <<= 1;
|
output <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not sure how this is better than usleep...?
|
|
||||||
/*
|
|
||||||
static void udelay(int us) {
|
|
||||||
struct timespec ts = { 0, us * 1000 };
|
|
||||||
nanosleep(&ts, NULL);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Shutdown functions
|
// Shutdown functions
|
||||||
// --------------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------------
|
||||||
void LedDeviceWS2812b::terminate(int dummy) {
|
void LedDeviceWS2812b::terminate(int dummy) {
|
||||||
// Shut down the DMA controller
|
// Shut down the DMA controller
|
||||||
if(dma_reg) {
|
if(dma_reg)
|
||||||
|
{
|
||||||
CLRBIT(dma_reg[DMA_CS], DMA_CS_ACTIVE);
|
CLRBIT(dma_reg[DMA_CS], DMA_CS_ACTIVE);
|
||||||
usleep(100);
|
usleep(100);
|
||||||
SETBIT(dma_reg[DMA_CS], DMA_CS_RESET);
|
SETBIT(dma_reg[DMA_CS], DMA_CS_RESET);
|
||||||
@ -560,21 +536,22 @@ void LedDeviceWS2812b::terminate(int dummy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shut down PWM
|
// Shut down PWM
|
||||||
if(pwm_reg) {
|
if(pwm_reg)
|
||||||
|
{
|
||||||
CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1);
|
CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1);
|
||||||
usleep(100);
|
usleep(100);
|
||||||
pwm_reg[PWM_CTL] = (1 << PWM_CTL_CLRF1);
|
pwm_reg[PWM_CTL] = (1 << PWM_CTL_CLRF1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the allocated memory
|
// Free the allocated memory
|
||||||
if(page_map != 0) {
|
if(page_map != 0)
|
||||||
|
{
|
||||||
free(page_map);
|
free(page_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
//exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LedDeviceWS2812b::fatal(const char *fmt, ...) {
|
void LedDeviceWS2812b::fatal(const char *fmt, ...)
|
||||||
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
@ -586,18 +563,22 @@ void LedDeviceWS2812b::fatal(const char *fmt, ...) {
|
|||||||
// Memory management
|
// Memory management
|
||||||
// --------------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------------
|
||||||
// Translate from virtual address to physical
|
// Translate from virtual address to physical
|
||||||
unsigned int LedDeviceWS2812b::mem_virt_to_phys(void *virt) {
|
unsigned int LedDeviceWS2812b::mem_virt_to_phys(void *virt)
|
||||||
|
{
|
||||||
unsigned int offset = (uint8_t *)virt - virtbase;
|
unsigned int offset = (uint8_t *)virt - virtbase;
|
||||||
return page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE);
|
return page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate from physical address to virtual
|
// Translate from physical address to virtual
|
||||||
unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys) {
|
unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys)
|
||||||
|
{
|
||||||
unsigned int pg_offset = phys & (PAGE_SIZE - 1);
|
unsigned int pg_offset = phys & (PAGE_SIZE - 1);
|
||||||
unsigned int pg_addr = phys - pg_offset;
|
unsigned int pg_addr = phys - pg_offset;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < NUM_PAGES; i++) {
|
for (unsigned int i = 0; i < NUM_PAGES; i++)
|
||||||
if (page_map[i].physaddr == pg_addr) {
|
{
|
||||||
|
if (page_map[i].physaddr == pg_addr)
|
||||||
|
{
|
||||||
return (uint32_t)virtbase + i * PAGE_SIZE + pg_offset;
|
return (uint32_t)virtbase + i * PAGE_SIZE + pg_offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,22 +588,28 @@ unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map a peripheral's IO memory into our virtual memory, so we can read/write it directly
|
// Map a peripheral's IO memory into our virtual memory, so we can read/write it directly
|
||||||
void * LedDeviceWS2812b::map_peripheral(uint32_t base, uint32_t len) {
|
void * LedDeviceWS2812b::map_peripheral(uint32_t base, uint32_t len)
|
||||||
|
{
|
||||||
int fd = open("/dev/mem", O_RDWR);
|
int fd = open("/dev/mem", O_RDWR);
|
||||||
void * vaddr;
|
void * vaddr;
|
||||||
|
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
|
{
|
||||||
fatal("Failed to open /dev/mem: %m\n");
|
fatal("Failed to open /dev/mem: %m\n");
|
||||||
|
}
|
||||||
vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base);
|
vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base);
|
||||||
if (vaddr == MAP_FAILED)
|
if (vaddr == MAP_FAILED)
|
||||||
|
{
|
||||||
fatal("Failed to map peripheral at 0x%08x: %m\n", base);
|
fatal("Failed to map peripheral at 0x%08x: %m\n", base);
|
||||||
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
return vaddr;
|
return vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero out the PWM waveform buffer
|
// Zero out the PWM waveform buffer
|
||||||
void LedDeviceWS2812b::clearPWMBuffer() {
|
void LedDeviceWS2812b::clearPWMBuffer()
|
||||||
|
{
|
||||||
memset(PWMWaveform, 0, NUM_DATA_WORDS * 4); // Times four because memset deals in bytes.
|
memset(PWMWaveform, 0, NUM_DATA_WORDS * 4); // Times four because memset deals in bytes.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,29 +617,26 @@ void LedDeviceWS2812b::clearPWMBuffer() {
|
|||||||
// The (31 - bitIdx) is so that we write the data backwards, correcting its endianness
|
// The (31 - bitIdx) is so that we write the data backwards, correcting its endianness
|
||||||
// This means getPWMBit will return something other than what was written, so it would be nice
|
// This means getPWMBit will return something other than what was written, so it would be nice
|
||||||
// if the logic that calls this function would figure it out instead. (However, that's trickier)
|
// if the logic that calls this function would figure it out instead. (However, that's trickier)
|
||||||
void LedDeviceWS2812b::setPWMBit(unsigned int bitPos, unsigned char bit) {
|
void LedDeviceWS2812b::setPWMBit(unsigned int bitPos, unsigned char bit)
|
||||||
|
{
|
||||||
// Fetch word the bit is in
|
// Fetch word the bit is in
|
||||||
unsigned int wordOffset = (int)(bitPos / 32);
|
unsigned int wordOffset = (int)(bitPos / 32);
|
||||||
unsigned int bitIdx = bitPos - (wordOffset * 32);
|
unsigned int bitIdx = bitPos - (wordOffset * 32);
|
||||||
|
|
||||||
//printf("bitPos=%d wordOffset=%d bitIdx=%d value=%d\n", bitPos, wordOffset, bitIdx, bit);
|
switch(bit)
|
||||||
|
{
|
||||||
switch(bit) {
|
|
||||||
case 1:
|
case 1:
|
||||||
PWMWaveform[wordOffset] |= (1 << (31 - bitIdx));
|
PWMWaveform[wordOffset] |= (1 << (31 - bitIdx));
|
||||||
// PWMWaveform[wordOffset] |= (1 << bitIdx);
|
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
PWMWaveform[wordOffset] &= ~(1 << (31 - bitIdx));
|
PWMWaveform[wordOffset] &= ~(1 << (31 - bitIdx));
|
||||||
// PWMWaveform[wordOffset] &= ~(1 << bitIdx);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== Init Hardware ====
|
// ==== Init Hardware ====
|
||||||
|
void LedDeviceWS2812b::initHardware()
|
||||||
void LedDeviceWS2812b::initHardware() {
|
{
|
||||||
int pid;
|
int pid;
|
||||||
int fd;
|
int fd;
|
||||||
char pagemap_fn[64];
|
char pagemap_fn[64];
|
||||||
@ -692,22 +676,24 @@ void LedDeviceWS2812b::initHardware() {
|
|||||||
-1, // File descriptor
|
-1, // File descriptor
|
||||||
0); // Offset
|
0); // Offset
|
||||||
|
|
||||||
if (virtbase == MAP_FAILED) {
|
if (virtbase == MAP_FAILED)
|
||||||
|
{
|
||||||
fatal("Failed to mmap physical pages: %m\n");
|
fatal("Failed to mmap physical pages: %m\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((unsigned long)virtbase & (PAGE_SIZE-1)) {
|
if ((unsigned long)virtbase & (PAGE_SIZE-1))
|
||||||
|
{
|
||||||
fatal("Virtual address is not page aligned\n");
|
fatal("Virtual address is not page aligned\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf("virtbase mapped 0x%x bytes at 0x%x\n", NUM_PAGES * PAGE_SIZE, virtbase);
|
|
||||||
|
|
||||||
// Allocate page map (pointers to the control block(s) and data for each CB
|
// Allocate page map (pointers to the control block(s) and data for each CB
|
||||||
page_map = (page_map_t *) malloc(NUM_PAGES * sizeof(*page_map));
|
page_map = (page_map_t *) malloc(NUM_PAGES * sizeof(*page_map));
|
||||||
if (page_map == 0) {
|
if (page_map == 0)
|
||||||
|
{
|
||||||
fatal("Failed to malloc page_map: %m\n");
|
fatal("Failed to malloc page_map: %m\n");
|
||||||
} else {
|
return;
|
||||||
//printf("Allocated 0x%x bytes for page_map at 0x%x\n", NUM_PAGES * sizeof(*page_map), page_map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use /proc/self/pagemap to figure out the mapping between virtual and physical addresses
|
// Use /proc/self/pagemap to figure out the mapping between virtual and physical addresses
|
||||||
@ -715,17 +701,20 @@ void LedDeviceWS2812b::initHardware() {
|
|||||||
sprintf(pagemap_fn, "/proc/%d/pagemap", pid);
|
sprintf(pagemap_fn, "/proc/%d/pagemap", pid);
|
||||||
fd = open(pagemap_fn, O_RDONLY);
|
fd = open(pagemap_fn, O_RDONLY);
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0)
|
||||||
|
{
|
||||||
fatal("Failed to open %s: %m\n", pagemap_fn);
|
fatal("Failed to open %s: %m\n", pagemap_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t newOffset = (unsigned long)virtbase >> 9;
|
off_t newOffset = (unsigned long)virtbase >> 9;
|
||||||
if (lseek(fd, newOffset, SEEK_SET) != newOffset) {
|
if (lseek(fd, newOffset, SEEK_SET) != newOffset)
|
||||||
|
{
|
||||||
fatal("Failed to seek on %s: %m\n", pagemap_fn);
|
fatal("Failed to seek on %s: %m\n", pagemap_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Page map: %d pages\n", NUM_PAGES);
|
printf("Page map: %d pages\n", NUM_PAGES);
|
||||||
for (unsigned int i = 0; i < NUM_PAGES; i++) {
|
for (unsigned int i = 0; i < NUM_PAGES; i++)
|
||||||
|
{
|
||||||
uint64_t pfn;
|
uint64_t pfn;
|
||||||
page_map[i].virtaddr = virtbase + i * PAGE_SIZE;
|
page_map[i].virtaddr = virtbase + i * PAGE_SIZE;
|
||||||
|
|
||||||
@ -766,7 +755,8 @@ void LedDeviceWS2812b::initHardware() {
|
|||||||
// Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes"
|
// Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes"
|
||||||
// Times 4 because DMA works in bytes, not words
|
// Times 4 because DMA works in bytes, not words
|
||||||
cbp->length = ((mLedCount * 2.25) + 1) * 4;
|
cbp->length = ((mLedCount * 2.25) + 1) * 4;
|
||||||
if(cbp->length > NUM_DATA_WORDS * 4) {
|
if(cbp->length > NUM_DATA_WORDS * 4)
|
||||||
|
{
|
||||||
cbp->length = NUM_DATA_WORDS * 4;
|
cbp->length = NUM_DATA_WORDS * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,19 +770,6 @@ void LedDeviceWS2812b::initHardware() {
|
|||||||
// Pointer to next block - 0 shuts down the DMA channel when transfer is complete
|
// Pointer to next block - 0 shuts down the DMA channel when transfer is complete
|
||||||
cbp->next = 0;
|
cbp->next = 0;
|
||||||
|
|
||||||
// Testing
|
|
||||||
/*
|
|
||||||
ctl = (struct control_data_s *)virtbase;
|
|
||||||
ctl->sample[0] = 0x00000000;
|
|
||||||
ctl->sample[1] = 0x000000FA;
|
|
||||||
ctl->sample[2] = 0x0000FFFF;
|
|
||||||
ctl->sample[3] = 0xAAAAAAAA;
|
|
||||||
ctl->sample[4] = 0xF0F0F0F0;
|
|
||||||
ctl->sample[5] = 0x0A0A0A0A;
|
|
||||||
ctl->sample[6] = 0xF00F0000;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Stop any existing DMA transfers
|
// Stop any existing DMA transfers
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
dma_reg[DMA_CS] |= (1 << DMA_CS_ABORT);
|
dma_reg[DMA_CS] |= (1 << DMA_CS_ABORT);
|
||||||
@ -894,7 +871,8 @@ void LedDeviceWS2812b::initHardware() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Begin the transfer
|
// Begin the transfer
|
||||||
void LedDeviceWS2812b::startTransfer() {
|
void LedDeviceWS2812b::startTransfer()
|
||||||
|
{
|
||||||
// Enable DMA
|
// Enable DMA
|
||||||
dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(((struct control_data_s *) virtbase)->cb);
|
dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(((struct control_data_s *) virtbase)->cb);
|
||||||
dma_reg[DMA_CS] = DMA_CS_CONFIGWORD | (1 << DMA_CS_ACTIVE);
|
dma_reg[DMA_CS] = DMA_CS_CONFIGWORD | (1 << DMA_CS_ACTIVE);
|
||||||
@ -902,7 +880,4 @@ void LedDeviceWS2812b::startTransfer() {
|
|||||||
|
|
||||||
// Enable PWM
|
// Enable PWM
|
||||||
SETBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1);
|
SETBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1);
|
||||||
|
|
||||||
// dumpPWM();
|
|
||||||
// dumpDMA();
|
|
||||||
}
|
}
|
||||||
|
@ -108,22 +108,23 @@
|
|||||||
// per address. This is because the software (this program) deals only in virtual addresses,
|
// per address. This is because the software (this program) deals only in virtual addresses,
|
||||||
// whereas the DMA controller can only access RAM via physical address. (If that's not confusing
|
// whereas the DMA controller can only access RAM via physical address. (If that's not confusing
|
||||||
// enough, it writes to peripherals by their bus addresses.)
|
// enough, it writes to peripherals by their bus addresses.)
|
||||||
typedef struct {
|
struct page_map_t
|
||||||
|
{
|
||||||
uint8_t *virtaddr;
|
uint8_t *virtaddr;
|
||||||
uint32_t physaddr;
|
uint32_t physaddr;
|
||||||
} page_map_t;
|
};
|
||||||
|
|
||||||
// Control Block (CB) - this tells the DMA controller what to do.
|
// Control Block (CB) - this tells the DMA controller what to do.
|
||||||
typedef struct {
|
struct dma_cb_t
|
||||||
unsigned int
|
{
|
||||||
info, // Transfer Information (TI)
|
unsigned info; // Transfer Information (TI)
|
||||||
src, // Source address (physical)
|
unsigned src; // Source address (physical)
|
||||||
dst, // Destination address (bus)
|
unsigned dst; // Destination address (bus)
|
||||||
length, // Length in bytes (not words!)
|
unsigned length; // Length in bytes (not words!)
|
||||||
stride, // We don't care about this
|
unsigned stride; // We don't care about this
|
||||||
next, // Pointer to next control block
|
unsigned next; // Pointer to next control block
|
||||||
pad[2]; // These are "reserved" (unused)
|
unsigned pad[2]; // These are "reserved" (unused)
|
||||||
} dma_cb_t;
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Implementation of the LedDevice interface for writing to Ws2801 led device.
|
/// Implementation of the LedDevice interface for writing to Ws2801 led device.
|
||||||
|
Loading…
Reference in New Issue
Block a user