Go Down

Topic: RGBW TM1814 on Arduino Uno (Read 606 times) previous topic - next topic

ConnorN

Hello,

I was hoping to get some help getting some RGBW TM1814 pixel LED's running on an Arduino Uno.
I've tried multiple libraries from the Adafruit neopixel library and varying FastLED RGBW capable forks and the main FastLED Library.
from what I understand from reading the datasheets on for the TM1814 (here) and the adafruit RGBW neopxiel (or SK6812 here) the timings are pretty close except for the TReset which I believe might be the cause of my problems.

I have also tried other dev boards, the teensy 3.5 and sparkfun ESP32 thingplus with 3.3v - 5v logic level circuits with no luck.
If anyone could point out something I might've missed or has had some previous success with this chip and can shed some light it would be fantastic.

thanks in advance,
Connor

Rintin

Have you tried to invert the signal? When I read the datasheets correct, the SK6812 uses idle low for DIN, and the TM1814 idle high.

ConnorN

#2
Aug 18, 2019, 08:19 am Last Edit: Aug 19, 2019, 09:34 am by ConnorN
Hey Rintin,

No I haven't I'm still a bit new to all of this how would I go about that?

*EDIT*
I've tried a couple of different ideas such as using logic gates, particularly a nand gate with both inputs tied to the sing output pin of my arduino with no success.

thanks for the reply.

ConnorN

So I'm still yet to get anything to really work as of yet.
I've tried using NAND gates to invert the signal and running the Adafruit Neopixel library with RGBW.
I've tried adjusting the PWM clock and resolution to hit 1000Hz
I now definitely feel like the problem lies in the data transmission timings.
If anyone with the know how to adjust the timings of the signals sent could guide me in the right direction that would be great.
I've linked the library here

ConnorN

Another quick little update.
I finally got an email back from the supplier with some sample code for what appears to be a STC15W104 MCU.
here is the code:
Code: [Select]
#include <reg52.h> //MCU头文件
#include "intrins.h" //包含nop指令头文件
#define nop  _nop_(); //宏定义

/********************************定义控制端口************************************/
sbit DO=P3^3; //定义信号输出DO

/**********************************定义变量**************************************/
unsigned int IC=5; //控制TM1814个数
unsigned int LED_PX; //像素点数
unsigned char PWM=255;         //TM1814灰度
unsigned char Rda,Gda,Bda,Wda;          //R、G、B、W灰度数据
unsigned char bdata LED_data;           //可位操作的数据发送暂存变量声明
sbit bit0=LED_data^0; //被发送的数据各位定义
sbit bit1=LED_data^1;
sbit bit2=LED_data^2;
sbit bit3=LED_data^3;
sbit bit4=LED_data^4;
sbit bit5=LED_data^5;
sbit bit6=LED_data^6;
sbit bit7=LED_data^7;
bit flag=0;                            //定义一个标志位

/***********************************延时函数*************************************/
void delay(unsigned int n)             //n=1,延时500us
{
unsigned int i;
while(n--)
for(i=0;i<860;i++);
}

/**********************发送0码函数,低电平400ns,周期1.25us**********************/
void send_data_0()   
{
DO=0;
nop;nop;nop;nop;nop;nop;
nop;nop;nop;nop;nop;    
DO=1;
nop;nop;nop;nop;nop;nop;nop;
nop;nop;nop;nop;nop;
}

/**********************发送1码函数,低电平800ns,周期1.25us**********************/
void send_data_1()         
{
DO=0;
nop;nop;nop;nop;nop;nop;
nop;nop;nop;nop;nop;
nop;nop;nop;nop;nop;nop;
nop;nop;nop;nop;nop;
DO=1;   
}

/************************发送1个字节数据,高位先发*******************************/
void send_data(unsigned char DATA)
{
LED_data=DATA;                         
if(bit7)  send_data_1();  else send_data_0();
if(bit6)  send_data_1();  else send_data_0();
if(bit5)  send_data_1();  else send_data_0();
if(bit4)  send_data_1();  else send_data_0();
if(bit3)  send_data_1();  else send_data_0();
if(bit2)  send_data_1();  else send_data_0();
if(bit1)  send_data_1();  else send_data_0();
if(bit0)  send_data_1();  else send_data_0();

/**************发送恒流值数据,包含R、G、B、W电流值,恒流范围6-38mA***************/
void current_set(unsigned char Ir,unsigned char Ig,unsigned char Ib,unsigned char Iw)
{
Ir=(Ir-6)/0.5;                         
Ig=(Ig-6)/0.5;
Ib=(Ib-6)/0.5;
Iw=(Iw-6)/0.5;                    //恒流数据计算         
send_data(Iw);                     //发送恒流值
send_data(Ir);             
send_data(Ig);
send_data(Ib);
send_data(~Iw);                    //发送恒流值反码(C1C2校验)
send_data(~Ir);             
send_data(~Ig);
send_data(~Ib);
}

/*******************************发送1帧数据****************************************/
void send_px()
{
unsigned int i;
current_set(20,20,20,20);           //R、G、B、W通道均设定20mA恒流
for(i=0;i<LED_PX;i++)
{
send_data(Wda);             //发送W灰度数据
send_data(Rda);             //发送R灰度数据
send_data(Gda);             //发送G灰度数据
send_data(Bda);             //发送B灰度数据
}
}

/*================================主函数==========================================*/
void main()
{
unsigned char i;
LED_PX=IC;                          //像素点数等于TM1814个数
Rda=Gda=Bda=Wda=0;                  //R、G、B、W灰度数据复位清零
while(1)
{
for(i=0;i<PWM;i++)           //红色渐亮 
{
Rda=i;               //灰度计算
send_px();           //发送灰度数据
delay(50);           //复位延时
}
flag=1;                      //标志位置位
while(flag)                  //标志位置位后循环函数
{
for(i=0;i<PWM;i++)   //红色渐灭,绿色渐亮
{
Gda=i;
Rda=PWM-i;   //灰度计算
send_px();   //发送灰度数据
delay(50);   //复位延时
}
for(i=0;i<PWM;i++)   //绿色渐灭,蓝色渐亮
{
Bda=i;           
Gda=PWM-i;   //灰度计算
send_px();   //发送灰度数据
delay(50);   //复位延时
}
for(i=0;i<PWM;i++)   //蓝色渐灭,白色渐亮
{
Wda=i;
Bda=PWM-i;   //灰度计算
send_px();   //发送灰度数据
delay(50);   //复位延时
}
for(i=0;i<PWM;i++)   //白色渐灭,红色渐亮
{
Rda=i;
Wda=PWM-i;   //灰度计算
send_px();   //发送灰度数据
delay(50);   //复位延时
}
}
}
}


Any thoughts on getting it adapted to arduino?

Grumpy_Mike

#5
Aug 24, 2019, 06:20 am Last Edit: Aug 24, 2019, 06:21 am by Grumpy_Mike
Quote
Any thoughts on getting it adapted to arduino?
Unless you have an oscilloscope then forget it. That uses a bit of machine code to make the timing and you will have to convert that to Arduino's machine code. Basically that is what the two WS2812 libraries do and if it is the timing that is off your best bet is hacking one of the two libraries to tweak the timing.

But without an oscilloscope to measure what you get you are just working in the dark.

ConnorN

Hey Mike,

I'm definitely feeling the Oscilloscope would be the best method, I'm trying to get my hands on one.
In the mean time I guess I'll continue my to bang my head on the wall with some trial and error ;).
I'm continuing to try and hack the neopixel library.

Surely though I should be able to at least narrow it down. if the Data Rate IN for the chip is 800Khz should I match the PWM frequency to this? The teensy has a default PWM frequency of 488.28Hz. Then according to the PJRC website when The chip is run at 120MHz clock the PWM signal is 915.527Hz. the PWM signal can be adjusted using code;
Code: [Select]
void setup() {
analogWriteFrequency(PIN, FREQUENCY);
analogWriteResolution();//number of bits
}


the code from the adafruit library that i'm using seems to be this section:
Code: [Select]
#if defined(TEENSYDUINO) && defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6
#define CYCLES_800_T0H  (F_CPU / 4000000) //400 nS
#define CYCLES_800_T1H  (F_CPU / 1250000) //800nS
#define CYCLES_800      (F_CPU /  800000) //total time ~1.25uS
#define CYCLES_400_T0H  (F_CPU / 2000000)
#define CYCLES_400_T1H  (F_CPU /  833333)
#define CYCLES_400      (F_CPU /  400000)
   
  uint8_t          *p   = pixels,
                   *end = p + numBytes, pix, mask;
  volatile uint8_t *set = portSetRegister(pin),
                   *clr = portClearRegister(pin);
  uint32_t          cyc;

  ARM_DEMCR    |= ARM_DEMCR_TRCENA;
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;

#ifdef NEO_KHZ400 // 800 KHz check needed only if 400 KHz support enabled
  if(is800KHz) {
#endif
    cyc = ARM_DWT_CYCCNT + CYCLES_800;
    while(p < end) {
      pix = *p++;
      for(mask = 0x80; mask; mask >>= 1) {
        while(ARM_DWT_CYCCNT - cyc < CYCLES_800);
        cyc  = ARM_DWT_CYCCNT;
        *set = 1;
        if(pix & mask) {
          while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H);
        } else {
          while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H);
        }
        *clr = 1;
      }
    }
    while(ARM_DWT_CYCCNT - cyc < CYCLES_800);
#ifdef NEO_KHZ400
  } else { // 400 kHz bitstream
    cyc = ARM_DWT_CYCCNT + CYCLES_400;
    while(p < end) {
      pix = *p++;
      for(mask = 0x80; mask; mask >>= 1) {
        while(ARM_DWT_CYCCNT - cyc < CYCLES_400);
        cyc  = ARM_DWT_CYCCNT;
        *set = 1;
        if(pix & mask) {
          while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H);
        } else {
          while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H);
        }
        *clr = 1;
      }
    }
    while(ARM_DWT_CYCCNT - cyc < CYCLES_400);
  }


it appears to me that by adjusting adjusting the value after F_CPU I should be able to adjust the timings but I have no clue what F_CPU is. the other part of the equation is the Reset timing which I'm assuming is the 
while(ARM_DWT_CYCCNT - cyc < CYCLES_800);

I'm also confused as to where ARM_DWT_CYCCNT variable comes from, this is the first time it appears in the entire code and what the use of the variable mask in the code is.

thanks again,

Grumpy_Mike

Quote
should I match the PWM frequency to this?
No, the PWM should have nothing to do with it.

Quote
I'm also confused as to where ARM_DWT_CYCCNT variable comes from
It must be defined in a .h file somewhere. Maybe in the ARM support files.

Quote
what the use of the variable mask in the code is.
That sort of thing is called a "walking one" it goes through all the bits in the variable one at a time. It is then used to decide what sort of pulse is produced for the information in that bit. It is a standard technique when you want to do something depending on the state of bits in a variable.

ConnorN

#8
Aug 25, 2019, 06:04 am Last Edit: Aug 25, 2019, 06:43 am by ConnorN
Thanks for clearing this up Mark!
this should help me narrow down what I'm doing and refocus my efforts.
I'll post any updates as they come!

ConnorN

Success! :D

I finally got my hands on an oscilloscope today and got the LED's working!
So it was a timing issue with the total run time being too short originally for the tm1814 chip which lead to a misread of the 0 and 1 timings.
These timings work:
Code: [Select]
#define CYCLES_800_T0H  (F_CPU / 3333333.33)// working: 3333333.33
#define CYCLES_800_T1H  (F_CPU / 1666666.66)// working: 1666666.66
#define CYCLES_800      (F_CPU /  900000) //working: 96000 - 1100000
#define CYCLES_400_T0H  (F_CPU / 2000000)
#define CYCLES_400_T1H  (F_CPU /  833333)
#define CYCLES_400      (F_CPU /  400000)

Keeping in mind the closer to 1100000 the more chance of flicker.

I also had to modify the way the constant current frames (C1 and C2) were stored and output by the code. I modified the setPixelColor function to accept the C1 and C2 and store them in the pixel buffer. I've attached the modified Neopixel.cpp and Neopixel.h files for anyone who might stumble across this.

I'm going to continue playing around with this and slowly optimising it but for now I'm going to move on with the rest of the project.

Go Up