mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	* add general switchOff * refactoring of leddevices regarding ledcount and switchoff * remove obsolete includes
		
			
				
	
	
		
			226 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef LEDDEVICEWS2812B_H_
 | 
						|
#define LEDDEVICEWS2812B_H_
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
 | 
						|
// Set tabs to 4 spaces.
 | 
						|
 | 
						|
// =================================================================================================
 | 
						|
//
 | 
						|
//		 __      __  _________________   ______  ____________   ____________________.__
 | 
						|
//		/  \    /  \/   _____/\_____  \ /  __  \/_   \_____  \  \______   \______   \__|
 | 
						|
//		\   \/\/   /\_____  \  /  ____/ >      < |   |/  ____/   |       _/|     ___/  |
 | 
						|
//		 \        / /        \/       \/   --   \|   /       \   |    |   \|    |   |  |
 | 
						|
//		  \__/\  / /_______  /\_______ \______  /|___\_______ \  |____|_  /|____|   |__|
 | 
						|
//		       \/          \/         \/      \/             \/         \/
 | 
						|
//
 | 
						|
// WS2812 NeoPixel driver
 | 
						|
// Based on code by Richard G. Hirst and others
 | 
						|
// Adapted for the WS2812 by 626Pilot, April/May 2014
 | 
						|
// See: https://github.com/626Pilot/RaspberryPi-NeoPixel-WS2812
 | 
						|
// Version: https://github.com/626Pilot/RaspberryPi-NeoPixel-WS2812/blob/1d43407d9e6eba19bff24330bc09a27963b55751/ws2812-RPi.c
 | 
						|
// Huge ASCII art section labels are from http://patorjk.com/software/taag/
 | 
						|
//
 | 
						|
// LED driver adaptation by Kammerjaeger ()
 | 
						|
// mostly code removed that was not needed
 | 
						|
//
 | 
						|
// License: GPL
 | 
						|
//
 | 
						|
// You are using this at your OWN RISK. I believe this software is reasonably safe to use (aside
 | 
						|
// from the intrinsic risk to those who are photosensitive - see below), although I can't be certain
 | 
						|
// that it won't trash your hardware or cause property damage.
 | 
						|
//
 | 
						|
// Speaking of risk, WS2812 pixels are bright enough to cause eye pain and (for all I know) possibly
 | 
						|
// retina damage when run at full strength. It's a good idea to set the brightness at 0.2 or so for
 | 
						|
// direct viewing (whether you're looking directly at the pixels or not), or to put some diffuse
 | 
						|
// material between you and the LEDs.
 | 
						|
//
 | 
						|
// PHOTOSENSITIVITY WARNING:
 | 
						|
// Patterns of light and darkness (stationary or moving), flashing lights, patterns and backgrounds
 | 
						|
// on screens, and the like, may cause epilleptic seizures in some people. This is a danger EVEN IF
 | 
						|
// THE PERSON (WHICH MAY BE *YOU*) HAS NEVER KNOWINGLY HAD A PHOTOSENSITIVE EPISODE BEFORE. It's up
 | 
						|
// to you to learn the warning signs, but symptoms may include dizziness, nausea, vision changes,
 | 
						|
// convlusions, disorientation, involuntary movements, and eye twitching. (This list is not
 | 
						|
// necessarily exhaustive.)
 | 
						|
//
 | 
						|
// NEOPIXEL BEST PRACTICES: https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices
 | 
						|
//
 | 
						|
// Connections:
 | 
						|
//		Positive to Raspberry Pi's 3.3v, for better separation connect only ground and data directly
 | 
						|
//					(5v can be used then without a problem, at least it worked for me, Kammerjaeger)
 | 
						|
//		Negative to Raspberry Pi's ground
 | 
						|
// 		Data to GPIO18 (Pin 12) (through a resistor, which you should know from the Best
 | 
						|
// 		Practices guide!)
 | 
						|
//
 | 
						|
//    Buy WS2812-based stuff from: http://adafruit.com
 | 
						|
//
 | 
						|
//  To activate: use led device "ws2812s" in the hyperion configuration
 | 
						|
//                                 (it needs to be root so it can map the peripherals' registers)
 | 
						|
//
 | 
						|
// =================================================================================================
 | 
						|
 | 
						|
// This is for the WS2812 LEDs. It won't work with the older WS2811s, although it could be modified
 | 
						|
// for that without too much trouble. Preliminary driver used Frank Buss' servo driver, but I moved
 | 
						|
// to Richard Hirst's memory mapping/access model because his code already works with DMA, and has
 | 
						|
// what I think is a slightly cleaner way of accessing the registers: register[name] rather than
 | 
						|
// *(register + name).
 | 
						|
 | 
						|
// At the time of writing, there's a lot of confusing "PWM DMA" code revolving around simulating
 | 
						|
// an FM signal. Usually this is done without properly initializing certain registers, which is
 | 
						|
// OK for their purpose, but I needed to be able to transfer actual coherent data and have it wind
 | 
						|
// up in a proper state once it was transferred. This has proven to be a somewhat painful task.
 | 
						|
// The PWM controller likes to ignore the RPTL1 bit when the data is in a regular, repeating
 | 
						|
// pattern. I'M NOT MAKING IT UP! It really does that. It's bizarre. There are lots of other
 | 
						|
// strange irregularities as well, which had to be figured out through trial and error. It doesn't
 | 
						|
// help that the BCM2835 ARM Peripherals manual contains outright errors and omissions!
 | 
						|
 | 
						|
// Many examples of this kind of code have magic numbers in them. If you don't know, a magic number
 | 
						|
// is one that either lacks an obvious structure (e.g. 0x2020C000) or purpose. Please don't use
 | 
						|
// that stuff in any code you release! All magic numbers found in reference code have been changed
 | 
						|
// to DEFINEs. That way, instead of seeing some inscrutable number, you see (e.g.) PWM_CTL.
 | 
						|
 | 
						|
// References - BCM2835 ARM Peripherals:
 | 
						|
//              http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
 | 
						|
//
 | 
						|
//              Raspberry Pi low-level peripherals:
 | 
						|
//              http://elinux.org/RPi_Low-level_peripherals
 | 
						|
//
 | 
						|
//              Richard Hirst's nice, clean code:
 | 
						|
//              https://github.com/richardghirst/PiBits/blob/master/PiFmDma/PiFmDma.c
 | 
						|
//
 | 
						|
//              PWM clock register:
 | 
						|
//              http://www.raspberrypi.org/forums/viewtopic.php?t=8467&p=124620
 | 
						|
//
 | 
						|
//              Simple (because it's in assembly) PWM+DMA setup:
 | 
						|
//              https://github.com/mikedurso/rpi-projects/blob/master/asm-nyancat/rpi-nyancat.s
 | 
						|
//
 | 
						|
//              Adafruit's NeoPixel driver:
 | 
						|
//              https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp
 | 
						|
 | 
						|
// Hyperion includes
 | 
						|
#include <leddevice/LedDevice.h>
 | 
						|
 | 
						|
//#define BENCHMARK
 | 
						|
#define WS2812_ASM_OPTI
 | 
						|
 | 
						|
// The page map contains pointers to memory that we will allocate below. It uses two pointers
 | 
						|
// 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
 | 
						|
// enough, it writes to peripherals by their bus addresses.)
 | 
						|
struct page_map_t
 | 
						|
{
 | 
						|
	uint8_t *virtaddr;
 | 
						|
	uint32_t physaddr;
 | 
						|
};
 | 
						|
 | 
						|
// Control Block (CB) - this tells the DMA controller what to do.
 | 
						|
struct dma_cb_t
 | 
						|
{
 | 
						|
	unsigned	info;		// Transfer Information (TI)
 | 
						|
	unsigned	src;		// Source address (physical)
 | 
						|
	unsigned	dst;		// Destination address (bus)
 | 
						|
	unsigned	length;		// Length in bytes (not words!)
 | 
						|
	unsigned	stride;		// We don't care about this
 | 
						|
	unsigned	next;		// Pointer to next control block
 | 
						|
	unsigned	pad[2];		// These are "reserved" (unused)
 | 
						|
};
 | 
						|
 | 
						|
///
 | 
						|
/// Implementation of the LedDevice interface for writing to Ws2801 led device.
 | 
						|
///
 | 
						|
class LedDeviceWS2812b : public LedDevice
 | 
						|
{
 | 
						|
public:
 | 
						|
	///
 | 
						|
	/// Constructs specific LedDevice
 | 
						|
	///
 | 
						|
	/// @param deviceConfig json device config
 | 
						|
	///
 | 
						|
	LedDeviceWS2812b();
 | 
						|
 | 
						|
	~LedDeviceWS2812b();
 | 
						|
 | 
						|
	///
 | 
						|
	/// Sets configuration
 | 
						|
	///
 | 
						|
	/// @param deviceConfig the json device config
 | 
						|
	/// @return true if success
 | 
						|
	bool setConfig(const Json::Value&) {return true;};
 | 
						|
 | 
						|
	/// constructs leddevice
 | 
						|
	static LedDevice* construct(const Json::Value &);
 | 
						|
 | 
						|
private:
 | 
						|
	///
 | 
						|
	/// Writes the led color values to the led-device
 | 
						|
	///
 | 
						|
	/// @param ledValues The color-value per led
 | 
						|
	/// @return Zero on succes else negative
 | 
						|
	///
 | 
						|
	virtual int write(const std::vector<ColorRgb> &ledValues);
 | 
						|
 | 
						|
	page_map_t *page_map;						// This will hold the page map, which we'll allocate
 | 
						|
	uint8_t *virtbase;					// Pointer to some virtual memory that will be allocated
 | 
						|
 | 
						|
	volatile unsigned int *pwm_reg;		// PWM controller register set
 | 
						|
	volatile unsigned int *clk_reg;		// PWM clock manager register set
 | 
						|
	volatile unsigned int *dma_reg;		// DMA controller register set
 | 
						|
	volatile unsigned int *gpio_reg;		// GPIO pin controller register set
 | 
						|
 | 
						|
	// Contains arrays of control blocks and their related samples.
 | 
						|
	// One pixel needs 72 bits (24 bits for the color * 3 to represent them on the wire).
 | 
						|
	// 		 768 words = 341.3 pixels
 | 
						|
	// 		1024 words = 455.1 pixels
 | 
						|
	// The highest I can make this number is 1016. Any higher, and it will start copying garbage to the
 | 
						|
	// PWM controller. I think it might be because of the virtual->physical memory mapping not being
 | 
						|
	// contiguous, so *pointer+1016 isn't "next door" to *pointer+1017 for some weird reason.
 | 
						|
	// However, that's still enough for 451.5 color instructions! If someone has more pixels than that
 | 
						|
	// to control, they can figure it out. I tried Hirst's message of having one CB per word, which
 | 
						|
	// seems like it might fix that, but I couldn't figure it out.
 | 
						|
	#define NUM_DATA_WORDS 1016
 | 
						|
	struct control_data_s {
 | 
						|
		dma_cb_t cb[1];
 | 
						|
		uint32_t sample[NUM_DATA_WORDS];
 | 
						|
	};
 | 
						|
 | 
						|
	//struct control_data_s *ctl;
 | 
						|
 | 
						|
	// PWM waveform buffer (in words), 16 32-bit words are enough to hold 170 wire bits.
 | 
						|
	// That's OK if we only transmit from the FIFO, but for DMA, we will use a much larger size.
 | 
						|
	// 1024 (4096 bytes) should be enough for over 400 elements. It can be bumped up if you need more!
 | 
						|
	unsigned int PWMWaveform[NUM_DATA_WORDS];
 | 
						|
 | 
						|
	void initHardware();
 | 
						|
	void startTransfer();
 | 
						|
 | 
						|
	void clearPWMBuffer();
 | 
						|
	void setPWMBit(unsigned int bitPos, unsigned char bit);
 | 
						|
 | 
						|
	unsigned int mem_phys_to_virt(uint32_t phys);
 | 
						|
	unsigned int mem_virt_to_phys(void *virt);
 | 
						|
	void terminate(int dummy);
 | 
						|
	void fatal(const char *fmt, ...);
 | 
						|
	void * map_peripheral(uint32_t base, uint32_t len);
 | 
						|
	void printBinary(unsigned int i, unsigned int bits);
 | 
						|
 | 
						|
#ifdef BENCHMARK
 | 
						|
	unsigned int runCount;
 | 
						|
	long combinedNseconds;
 | 
						|
	long shortestNseconds;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#endif /* LEDDEVICEWS2812B_H_ */
 |