Anyone have a working WS2812 Library for the DAs?

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Updating a project to use the new chips and I don't seem to be able to find a working library.  Seems support from the fastled library doesn't exist yet.

 

I really, really, don't want to take the time to learn machine code to write a new one......

 

Also,

These new variants are really growing on me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The Arduino core for these parts has a Neopixel library. I haven't used it though. https://github.com/SpenceKonde/D...

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

With proper use of peripherals, you only need to write data to the SPI.
This configuration is possible with mega0 / tiny1 / AVR-Dx.
However, the available pins are limited depending on the peripheral.
If that's okay, I can give you some advice.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

obdevel wrote:

The Arduino core for these parts has a Neopixel library. I haven't used it though. https://github.com/SpenceKonde/D...

 

I just played with that one for a while and I don't think it's going to work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kabasan wrote:

With proper use of peripherals, you only need to write data to the SPI.
This configuration is possible with mega0 / tiny1 / AVR-Dx.
However, the available pins are limited depending on the peripheral.
If that's okay, I can give you some advice.

 

 

I prefer the bit bang, but it looks like it's not an option at the moment.  What is required on the component side for making this work?  I'm interested in listening to what you have.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chriscalandro wrote:
I prefer the bit bang

Surely, that's pretty much independent of what microcontroller you use?

 

It's just a matter of:

set pin high;
wait a bit;

set pin low;
wait a bit;

repeated as required...

 

The only really target-specific bit is how to set a pin high or low - and that's not specific to this application

 

Also, it may well be possible to synthesise the bits just using the SPI itself - without the aid of CCL - there are various references on the interwebs, and I've done it on an nRF chip ...

 

 

Note that there are similar LEDs which do use standard SPI; eg,  SK9822 or APA102C

 

https://www.pololu.com/category/130/led-strips

 

But beware that there are quite a lot of web sites selling  WS2812B-based products which incorrectly call them "SPI".

 

 

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I tried to make the sample code as simple as possible.

Since SPI has no bitrate degrees of freedom, it actually uses USART MSPI mode.

////////////////////////////////////////////////////////////////
// 2812B for DA-CCL
////////////////////////////////////////////////////////////////
#define F_CPU	24000000UL
#include <avr/io.h>
#include <avr/cpufunc.h>

////////////////////////////////////////////////////////////////
// fuses
////////////////////////////////////////////////////////////////
FUSES = {
	.WDTCFG		= FUSE_WDTCFG_DEFAULT,
	.BODCFG		= FUSE_BODCFG_DEFAULT,
	.OSCCFG		= FUSE_OSCCFG_DEFAULT,
	.SYSCFG0	= FUSE_SYSCFG0_DEFAULT,
	.SYSCFG1	= FUSE_SYSCFG1_DEFAULT,
	.CODESIZE	= FUSE_CODESIZE_DEFAULT,
	.BOOTSIZE	= FUSE_BOOTSIZE_DEFAULT,
};

////////////////////////////////////////////////////////////////
// Define
////////////////////////////////////////////////////////////////
#define F_CPU_MHZ	((float)F_CPU / 1000000L)

// Byte number of RGB data array
#define GRN	0
#define RED	1
#define BLU	2

// WS2812B parameters
#define T0H_NS		400
#define T1H_NS		800
#define T_WIDTH_NS	1250
#define T_RESET_US	50

// Number of LEDs
#define LED_NUM	10

////////////////////////////////////////////////////////////////
// variable
////////////////////////////////////////////////////////////////
uint8_t buf_2812[LED_NUM][3] = {
	{0x00, 0xFF, 0x00},		// LED1:R
	{0xFF, 0x00, 0x00},		// LED2:G
	{0x00, 0x00, 0xFF},		// LED3:B
	{0xFF, 0x00, 0xFF},		// LED4:C (B+G)
	{0x00, 0xFF, 0xFF},		// LED5:M (R+B)
	{0xFF, 0xFF, 0x00},		// LED6:Y (R+G)
	{0xFF, 0xFF, 0xFF},		// LED7:W (R+G+B)
	{0x00, 0x00, 0x00},		// LED8:BLK
	{0x00, 0xFF, 0x00},		// LED9:R
	{0xFF, 0x00, 0x00},		// LED10:G
};

////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////
int16_t main(void){

	// All digital-input disable
	PORTA.PINCONFIG = PORT_ISC_INPUT_DISABLE_gc;
	PORTA.PINCTRLUPD = 0xFF;
	PORTC.PINCTRLUPD = 0xFF;
	PORTD.PINCTRLUPD = 0xFF;
	PORTF.PINCTRLUPD = 0xFF;

	// clock 24MHz
	_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
	_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_24M_gc);

	////////////////////////////////////////////////////////////////
	// WS2812B Waveform generator configuration
	////////////////////////////////////////////////////////////////
	// EVENTS
	EVSYS.CHANNEL0 = EVSYS_CHANNEL0_USART0_XCK_gc;
	EVSYS.USERTCB1CAPT = EVSYS_USER_CHANNEL0_gc;
	EVSYS.USERTCB2CAPT = EVSYS_USER_CHANNEL0_gc;

	// USART0(MSPI mode)
	USART0.BAUD = (uint16_t)(F_CPU_MHZ * T_WIDTH_NS * 4 / 125 + 32) & 0xFFC0;
	USART0.CTRLC = USART_CMODE_MSPI_gc | USART_UCPHA_bm;	// SPI mode1
	USART0.CTRLB = USART_TXEN_bm;

	// TCB1('1' bit generation)
	TCB1.CNT = TCB1.CCMP = (uint16_t)(F_CPU_MHZ * T1H_NS / 1000 + 0.5f);
	TCB1.EVCTRL = TCB_CAPTEI_bm;
	TCB1.CTRLB = TCB_CNTMODE_SINGLE_gc;
	TCB1.CTRLA = TCB_ENABLE_bm;

	// TCB2('0' bit generation)
	TCB2.CNT = TCB2.CCMP = (uint16_t)(F_CPU_MHZ * T0H_NS / 1000 + 0.5f);
	TCB2.EVCTRL = TCB_CAPTEI_bm;
	TCB2.CTRLB = TCB_CNTMODE_SINGLE_gc;
	TCB2.CTRLA = TCB_ENABLE_bm;

	// CCL-LUT0
	CCL.TRUTH0 = 0b11011000;
	CCL.LUT0CTRLC = CCL_INSEL2_TCB2_gc;
	CCL.LUT0CTRLB = CCL_INSEL1_TCB1_gc | CCL_INSEL0_USART0_gc;
	CCL.LUT0CTRLA = CCL_OUTEN_bm | CCL_ENABLE_bm;
	CCL.CTRLA |= CCL_ENABLE_bm;
	PORTA.DIRSET = PIN3_bm;	// CCL-LUT0 OUTPUT

	////////////////////////////////////////////////////////////////
	// WS2812B Waveform generator configuration end
	////////////////////////////////////////////////////////////////

	uint8_t *ptr = (uint8_t*)buf_2812;
	while (1){

		// WS2812B send (10LED = 30byte)
		for (uint8_t i = 0; i < 30; i++){
			while (!(USART0.STATUS & USART_DREIF_bm));
			USART0.TXDATAL = ptr[i];
		}

		// Wait for transmission completion
		USART0.STATUS = USART_TXCIF_bm;
		while (!(USART0.STATUS & USART_TXCIF_bm));

		// Reset code
		TCB0.CCMP = (uint16_t)(F_CPU_MHZ * T_RESET_US) - 1;
		TCB0.CNT = 0;
		TCB0.INTFLAGS = TCB_CAPT_bm;
		TCB0.CTRLB = TCB_CNTMODE_INT_gc;
		TCB0.CTRLA = TCB_CLKSEL_DIV1_gc | TCB_ENABLE_bm;
		while (!(TCB0.INTFLAGS & TCB_CAPT_bm));
		TCB0.CTRLA = 0;
	}
}

 

 

This is the execution result and schematic.

 

 

 

By letting interrupts send data, the foreground can use CPU resources for other processing.

I hate bit bang that use 100% of CPU resources.

 

Edit:Fixed typo

Last Edited: Wed. Feb 3, 2021 - 12:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, nice project!

 

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I made a sample to send to LED in the background.
Please try if you are interested.
I also do experimental things such as using register variables to speed up interrupt processing.
I don't know what happens to you

 

main.c

////////////////////////////////////////////////////////////////
// 2812B for DA-CCL
////////////////////////////////////////////////////////////////
#include <avr/io.h>
#include "pixel.h"

////////////////////////////////////////////////////////////////
// variable
////////////////////////////////////////////////////////////////
uint8_t buf_2812[10][3] = {
    {0x00, 0xFF, 0x00},     // LED1:R
    {0xFF, 0x00, 0x00},     // LED2:G
    {0x00, 0x00, 0xFF},     // LED3:B
    {0xFF, 0x00, 0xFF},     // LED4:C (B+G)
    {0x00, 0xFF, 0xFF},     // LED5:M (R+B)
    {0xFF, 0xFF, 0x00},     // LED6:Y (R+G)
    {0xFF, 0xFF, 0xFF},     // LED7:W (R+G+B)
    {0x00, 0x00, 0x00},     // LED8:BLK
    {0x00, 0xFF, 0x00},     // LED9:R
    {0xFF, 0x00, 0x00},     // LED10:G
};

////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////
int16_t main(void){
    // clock 24MHz
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSCHF_gc);
    _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_24M_gc);

    pixel_start(buf_2812, 10);

    while (1){
        // Edit buf_2812 as you wish.
    };
}

 

pixel.h

/* pixel.h */ 
#ifndef PIXEL_H_
#define PIXEL_H_

// Byte number of RGB data array
#define GRN 0
#define RED 1
#define BLU 2

void pixel_start(void* buf_adr, uint16_t num_pixels);

#endif /* PIXEL_H_ */

 

pixel.c

/*
 * pixel.c
 * Created: 2021/02/04 11:36:38
 *  Author: KABASAN
    F_CPU:  24MHz only
    Used:   USART0, TCB1, TCB2,CCL-LUT0, Event-channel0
 */ 
#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU_MHZ   24.0F
// WS2812B parameters
#define T0H_NS      400
#define T1H_NS      800
#define T_WIDTH_NS  1250
#define T_RESET_US  50

register uint16_t buf_ptr asm("r2");
register uint16_t buf_end asm("r4");
uint16_t buf_org;

void pixel_start(void* buf_adr, uint16_t num_pixels){
    // WS2812B Waveform generator configuration

    // EVENTS
    EVSYS.CHANNEL0 = EVSYS_CHANNEL0_USART0_XCK_gc;
    EVSYS.USERTCB1CAPT = EVSYS_USER_CHANNEL0_gc;
    EVSYS.USERTCB2CAPT = EVSYS_USER_CHANNEL0_gc;

    // USART0(MSPI mode)
    USART0.BAUD = (uint16_t)(F_CPU_MHZ * T_WIDTH_NS * 4 / 125 + 32) & 0xFFC0;
    USART0.CTRLC = USART_CMODE_MSPI_gc | USART_UCPHA_bm;    // SPI mode1
    USART0.CTRLB = USART_TXEN_bm;

    // TCB1('1' bit generation)
    TCB1.CNT = TCB1.CCMP = (uint16_t)(F_CPU_MHZ * T1H_NS / 1000 + 0.5F);
    TCB1.EVCTRL = TCB_CAPTEI_bm;
    TCB1.CTRLB = TCB_CNTMODE_SINGLE_gc;
    TCB1.CTRLA = TCB_ENABLE_bm;

    // TCB2('0' bit generation)
    TCB2.CNT = TCB2.CCMP = (uint16_t)(F_CPU_MHZ * T0H_NS / 1000 + 0.5F);
    TCB2.EVCTRL = TCB_CAPTEI_bm;
    TCB2.CTRLB = TCB_CNTMODE_SINGLE_gc;
    TCB2.CTRLA = TCB_ENABLE_bm;

    // CCL-LUT0
    CCL.TRUTH0 = 0b11011000;
    CCL.LUT0CTRLC = CCL_INSEL2_TCB2_gc;
    CCL.LUT0CTRLB = CCL_INSEL1_TCB1_gc | CCL_INSEL0_USART0_gc;
    CCL.LUT0CTRLA = CCL_OUTEN_bm | CCL_ENABLE_bm;
    CCL.CTRLA = CCL_ENABLE_bm;
    PORTA.DIRSET = PIN3_bm; // CCL-LUT0 OUTPUT

    buf_ptr = (uint16_t)buf_adr;
    buf_end = buf_ptr + (num_pixels * 3);
    buf_org = buf_ptr;
    USART0.CTRLA = USART_DREIE_bm;
    sei();
}

ISR(USART0_DRE_vect){
    USART0.TXDATAL = *(uint8_t*)buf_ptr;        buf_ptr++;
    if (buf_ptr == buf_end){
        USART0.STATUS = USART_TXCIF_bm;
        USART0.CTRLA = USART_TXCIE_bm;
    }
}
ISR(USART0_TXC_vect){
    USART0.CTRLA = 0;

    CCL.LUT0CTRLA = 0;
    TCB1.CTRLA = 0;
    TCB1.CCMP = (uint16_t)(F_CPU_MHZ * T_RESET_US) - 1;
    TCB1.CNT = 0;
    TCB1.INTFLAGS = TCB_CAPT_bm;
    TCB1.INTCTRL = TCB_CAPT_bm;
    TCB1.CTRLB = TCB_CNTMODE_INT_gc;
    TCB1.CTRLA = TCB_CLKSEL_DIV1_gc | TCB_ENABLE_bm;
}
ISR(TCB1_INT_vect){
    TCB1.CTRLA = 0;
    TCB1.CNT = TCB1.CCMP = (uint16_t)(F_CPU_MHZ * T1H_NS / 1000 + 0.5F);
    TCB1.EVCTRL = TCB_CAPTEI_bm;
    TCB1.INTCTRL = 0;
    TCB1.CTRLB = TCB_CNTMODE_SINGLE_gc;
    TCB1.CTRLA = TCB_ENABLE_bm;
    CCL.LUT0CTRLA = CCL_OUTEN_bm | CCL_ENABLE_bm;

    buf_ptr = buf_org;
    USART0.CTRLA = USART_DREIE_bm;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Any updates on a bit bang method?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My ignorance here is vast, but from a quick search I gather the DA parts are built around the standard 8-bit AVR core.  If that is correct, what prevents you from using existing bit-banging code written for the AVRs?  Clock frequency is the biggest issue - what frequency are you trying to run the DA at?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

from what I gather, the way the registers are layed out don't allow that to work.  I've always used Light_Ws2812 in the past.  I was hoping someone had or has updated a library that works similarly so I don't have to write my own...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chriscalandro wrote:
from what I gather, the way the registers are layed out don't allow that to work.

I'd love to know what the issue supposedly is.  The code should just need one output bit of one GPIO port.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:

I'd love to know what the issue supposedly is.  The code should just need one output bit of one GPIO port.

 

chriscalandro wrote:

I've always used Light_Ws2812 in the past.

 

I wonder if it's to do with this...

 

Quote:

This library uses a bit-banging approach with cycle optimized assembler innerloops.

 

...as some instruction timings have reputedly changed.

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

https://github.com/FastLED/FastLED claims to have code for mega0/etc.  As does the neopixel library where you said:

I just played with that one for a while and I don't think it's going to work.

Care to be more explicit?  What didn't work?  What exactly are you looking for?  (language, environment, performance, etc.)

Do you have a library for the traditional AVRs that you liked?

 

(If light_ws2812 was ok, we could focus much more on making that work on the new chips, instead of going on a wild goose hunt.  It looks to be actively maintained, though perhaps by someone who isn't using the new AVR chips...)

 

I'd love to know what the issue supposedly is.  The code should just need one output bit of one GPIO port.

 There are several issues.

  1. "just GPIO Ports" are not accessed the same way as they are in traditional AVRs.   Sure, it might be just replacing PORTB with VPORTB.OUT, but that can get buried, and can be beyond the beginning programmer just "searching for a library"
  2. GPIO Timing on the new parts is different.  SBI/CBI are only once cycle (they were two cycles in traditional AVR.)  I don't know offhand whether that makes a practical difference or nor.
  3. Many widely publicized libraries try to get fancier to avoid the "simple" bit-banged approach.  SPI, DMA, CLC...
  4. The libraries tend to end up being written in "C" with in-line assembly language, which obscures both the C and the asm code :-(
  5. The "respected" libraries like FastLED linked above are complex and featurefull C++ libraries aimed at various Arduino-like boards and environments.  They are perhaps miracles of portability and abstraction, but the result is an obfuscated mess that is difficult for all but the most experienced programmers to understand...  (Example follows...)

 

FastLED:

/// AVR definitions for pins.  Getting around  the fact that I can't pass GPIO register addresses in as template arguments by instead creating
/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register.  A similar
/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!)
typedef volatile uint8_t & reg8_t;

#define _R(T) struct __gen_struct_ ## T
#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }};

// Register name equivalent (using flat names)
#if defined(AVR_ATtinyxy7) || defined(AVR_ATtinyxy6) || defined(AVR_ATtinyxy4) || defined(AVR_ATtinyxy2)

// ATtiny series 0/1 and ATmega series 0
#define _FL_IO(L,C) _RD8(PORT ## L ## _DIR); _RD8(PORT ## L ## _OUT); _RD8(PORT ## L ## _IN); _FL_DEFINE_PORT3(L, C, _R(PORT ## L ## _OUT));
#define _FL_DEFPIN(_PIN, BIT, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, 1<<BIT, _R(PORT ## L ## _OUT), _R(PORT ## L ## _DIR), _R(PORT ## L ## _IN)> {};

#elif defined(__AVR_ATmega4809__)

// Leverage VPORTs instead of PORTs for faster access
#define _FL_IO(L,C) _RD8(VPORT ## L ## _DIR); _RD8(VPORT ## L ## _OUT); _RD8(VPORT ## L ## _IN); _FL_DEFINE_PORT3(L, C, _R(VPORT ## L ## _OUT));
#define _FL_DEFPIN(_PIN, BIT, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, 1<<BIT, _R(VPORT ## L ## _OUT), _R(VPORT ## L ## _DIR), _R(VPORT ## L ## _IN)> {};

#else
   :

 

Light_ws2812:

#if (w1_nops&4)
w_nop4
#endif
#if (w1_nops&8)
w_nop8
#endif
#if (w1_nops&16)
w_nop16
#endif
    "       sbrs  %1,7  \n\t"    //  '1' [03] '0' [02]
    "       out   %2,%4 \n\t"    //  '1' [--] '0' [03] - fe-low
    "       lsl   %1    \n\t"    //  '1' [04] '0' [04]
#if (w2_nops&1)
  w_nop1
#endif
#if (w2_nops&2)
  w_nop2
#endif
#if (w2_nops&4)
  w_nop4
#endif
#if (w2_nops&8)
  w_nop8
#endif

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd love to know what the issue supposedly is. 

I assume the "trick" for Xmega chips like DA is simply to use the "virtual ports" which should have optimal access (just like "normal" ports in Tiny/Mega).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I assume the "trick" for Xmega chips like DA is simply to use the "virtual ports" which should have optimal access (just like "normal" ports in Tiny/Mega).

 

Taking a quick look at the code, I'd say these lines at the end of  Light_ws281.h need to be changed:

#define ws2812_PORTREG  CONCAT_EXP(PORT,ws2812_port)
#define ws2812_DDRREG   CONCAT_EXP(DDR,ws2812_port)

#endif /* LIGHT_WS2812_H_ */

 

This could be done in a well thought way if the objective was to port the library to the AVR-Dx and release it, but for private use I'd just brute force whatever port is to be used, for example for port A:

 

#define ws2812_PORTREG  VPORTA.OUT
#define ws2812_DDRREG   VPORTA.DIR

#endif /* LIGHT_WS2812_H_ */

 

This leaves the possible timing issues inside the asm code for later... but with luck it will just work.

 

 

edit: yeah, although the AVR-Dx have these VPORT registers which are mostly compatible with classic AVR, the register naming convention is not compatible. For this reason, porting code can be a bit harder than it could be.

in general:

PORTx = VPORTx.OUT
DDRx  = VPORTx.DIR
PINx  = VPORTx.IN

 

edit #2:

A more polished option is:

 

#define ws2812_PORTREG  CONCAT_EXP(VPORT,ws2812_port).OUT
#define ws2812_DDRREG   CONCAT_EXP(VPORT,ws2812_port).DIR

This is basically what I used in a similar situation in a project.

Last Edited: Mon. Jun 14, 2021 - 12:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Advanced Examples | Getting Started with Configurable Custom Logic (CCL)

[bottom]

RGB Lighting with WS2812

This use case consists of a circuit composed of CIPs for interfacing the CCL and SPI peripherals with the WS2812 LED.

[GitHub]

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Each WS2812 module consists of three internal LEDs: Green, Red, and Blue.  Each LED has 255 levels of brightness (8 bits).  To select a color and light the WS2812, send 24 data bit units: 8 per color, in Green, Red, and Blue order.  Each unit is 0.4 microseconds of logic high, 0.45 uS of the data bit logic, and 0.4 uS of logic low  (72 units of @0.4 microSeconds each,  total time per LED module per color: @30 microSeconds) . 

  This is usually done by bit-banging in assembler in order to achieve the precise timing.   The AdaFruit NeoPixel library for Arduino has working code for both 16MHz and 8MHz system clock AVRs.

Last Edited: Mon. Jun 14, 2021 - 08:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

edit #2:

A more polished option is:

 

#define ws2812_PORTREG  CONCAT_EXP(VPORT,ws2812_port).OUT
#define ws2812_DDRREG   CONCAT_EXP(VPORT,ws2812_port).DIR

This is basically what I used in a similar situation in a project.

 

Checking back in and that is exactly what I've done.  I didn't know what I was looking for with the VPORT, but when I discovered it it made sense.  I haven't tested it yet.

 

Simonetta wrote:

Each WS2812 module consists of three internal LEDs: Green, Red, and Blue.  Each LED has 255 levels of brightness (8 bits).  To select a color and light the WS2812, send 24 data bit units: 8 per color, in Green, Red, and Blue order.  Each unit is 0.4 microseconds of logic high, 0.45 uS of the data bit logic, and 0.4 uS of logic low  (72 units of @0.4 microSeconds each,  total time per LED module per color: @30 microSeconds) . 

  This is usually done by bit-banging in assembler in order to achieve the precise timing.   The AdaFruit NeoPixel library for Arduino has working code for both 16MHz and 8MHz system clock AVRs.

 

I'm not using Arduino.

Last Edited: Mon. Jul 5, 2021 - 09:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

>>  The AdaFruit NeoPixel library for Arduino has working code for both 16MHz and 8MHz system clock AVRs.

I'm not using Arduino.

So?  The low-level bit-banging code is going to be inline ASM without much in the way of "Arduino dependencies"  - it's still useful to look at!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to give a feel for the level of complexity involved, my own WS2812B code, for 8MHz AVR, is 40 assembly instructions - 10 in the preamble and 30 in the loop.  It is callable from C.

Last Edited: Tue. Jul 6, 2021 - 03:52 AM