hzeller / rpi-rgb-led-matrix

Controlling up to three chains of 64x64, 32x32, 16x32 or similar RGB LED displays using Raspberry Pi GPIO
GNU General Public License v2.0
3.71k stars 1.17k forks source link

32x16 panel strange pixel mapping #283

Closed gpulido closed 6 years ago

gpulido commented 7 years ago

Hello, I have adquired a panel of 32x16 P10 and after the wiring using an active3 board, I ran the examples and it seems that the representation matrix is not the right one for this panel. Althought is a 32x16, it was splitting the panel into two 16x16 showing the same information. After using -c2 parameter the whole panel behaves like one. Then I use a little python program to just lit each coordinate in secuence to know the mapping. This programs use a SampleBase and I have found the following: Even with the -c2 and led_rows=32 parameter the matrix obtained from

offset_canvas = self.matrix.CreateFrameCanvas()

Has a very strange dimension: self.matrix.width = 64 and self.matrix.height= 16. This is the double number of pixels than the panel. After running the program the mapping matrix is the following (I only paste the first 16 pixeles as the second half behaves exactly the same with numrow = numrow + 32;

image Each cell represents the coordinates (row, column) from the matrix that lits it in the panel Each cell has two values as it is being painted twice (for each of the panels) The light brown are being painted first using the [0-7] first row index, then the light blue using the [8-15] rowindex. Then is painting the second half of the 16 columns, using the same schema.

Given this information how can I configure this panel? UPDATE: After reviewing the hardware documentation it seems to be a 1/4 scan multiplexing panel.

Thank you in advance and thanks for the amazing library and boards schemas. Regards Gabriel

hzeller commented 7 years ago

Yes, this looks like a 1:4 multiplexing panel. You need a transformer for this until I have implemented something to make that simpler. See issue #242 where someone provided a transformer.

Since you are using Python, you have to manually add this in the C++ part of code by calling

   ApplyStaticTransformer(P10outdoorTransformer());

at the two relevant places in the code, then re-compile and re-install your Python libraries.

gpulido commented 7 years ago

Thank @hzeller Unfortunatelly the p10outdoortransfomer seems that is not valid for my matrix. To have the 16x32 size I had to use a -c2 -r16 and it is behaving very strange. I'm trying to generate a transfomer by my own, although I'm not sure about the previous flags. Without using the p10transformer, if i used a -c2 -r8 it generates a 8x64 matrix that when iterated it filled the panel without any repeated pixel (although not in order of course) Should I keep to use that flags? Thank you in advance

gpulido commented 7 years ago

I have been playing a bit around the transformation matrix and I have some doubts about the scan and rows parameters regarding the canvas size for the transformation. Some data:

Using a program python that iterates through each pixel in order:

 while True:
            for x in range(0, self.matrix.width):
                for y in range(0, self.matrix.height):
                     offset_canvas.SetPixel(newx, newy, 255, 0, 0)

(where self.matrix.width = 64 and self.matrix.height = 8 using the -c2 -r8 parameters) I have managed to iterate the pixels in order from top to bottom left to right:

 while True:
            for x in range(0, self.matrix.width):
                for y in range(0, self.matrix.height):
                    print x,y
                    if x % 2 == 0:
                        if y < 4:
                            newx = x + 8 -(x % 16 // 2)
                            newy = y
                        else:
                            newx = x - (x % 16 // 2)
                            newy = (y + 4) % 4
                    else:
                        if y < 4:
                            newx = x + 7 - (x % 16 // 2)
                            newy = y + 4
                        else:
                            newx = x -1 - (x % 16 // 2)
                            newy = y

This is how the panel refresh using that: https://goo.gl/photos/Rgsd43MbGQZTdaMt9

Then I created a transformation matrix in c++ which this information:

/********************************/
/* P10outdoor Transformer Canvas */
/********************************/
class P10outdoorTransformer::TransformCanvas : public Canvas {
public:
  TransformCanvas() : delegatee_(NULL) {}

  void SetDelegatee(Canvas* delegatee);

  virtual void Clear();
  virtual void Fill(uint8_t red, uint8_t green, uint8_t blue);
  virtual int width() const;
  virtual int height() const;
  virtual void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue);

private:
  Canvas *delegatee_;
};

void P10outdoorTransformer::TransformCanvas::SetDelegatee(Canvas* delegatee) {
  delegatee_ = delegatee;
}

void P10outdoorTransformer::TransformCanvas::Clear() {
  delegatee_->Clear();
}

void P10outdoorTransformer::TransformCanvas::Fill(uint8_t red, uint8_t green, uint8_t blue) {
  delegatee_->Fill(red, green, blue);
}

int P10outdoorTransformer::TransformCanvas::width() const {
  return delegatee_->width();
}

int P10outdoorTransformer::TransformCanvas::height() const {
  return delegatee_->height();
}

void P10outdoorTransformer::TransformCanvas::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) {
  int new_x = x;
  int new_y = y;  
  if(x % 2 == 0)
  {
    if (y < 4)
    {
      new_x = x + 8 - ((x % 16 ) / 2);
      new_y = y;
    }
    else
    {
      new_x = x - ((x % 16 ) / 2);
      new_y = (y + 4) % 4;
    }    
  }
  else
  {
    if (y < 4)
    {
      new_x = x + 7 - ((x % 16 ) / 2);
      new_y = y + 4;
    }
    else
    {
      new_x = x - 1 -((x % 16 ) / 2);
      new_y = y;
    }    
  } 
  delegatee_->SetPixel(new_x, new_y, red, green, blue);
}
/*************************/
/* P10outdoor Transformer */
/*************************/
P10outdoorTransformer::P10outdoorTransformer()
  : canvas_(new TransformCanvas()) {
}

P10outdoorTransformer::~P10outdoorTransformer() {
  delete canvas_;
}

Canvas *P10outdoorTransformer::Transform(Canvas *output) {
  assert(output != NULL);

  canvas_->SetDelegatee(output);
  return canvas_;
}

But after I test with the python demos I obtain the following: https://goo.gl/photos/TQqWipvsx52q57iJ7

So I'm very close but I don't know what is happening here. Some of my doubts: In the transformer I don't know if I have to change the height and the width of the canvas so instead of provide a 8x64 it provides a 16x32 by doing

int P10outdoorTransformer::TransformCanvas::width() const {
  return delegatee_->width() / 2;
}

int P10outdoorTransformer::TransformCanvas::height() const {
  return delegatee_->height() * 2;
}

If I use that configuration, I don't know in which coordinate system are the x and y values from the setPixel method, that they come on the original size which is 8 x 64 and I have to transform them into 16x32 or it is the inverse transformation:

That is all for the moment, Any hint or idea on how to solve this puzzle please?

Thank you in advance Regards

hzeller commented 7 years ago

So I'm very close but I don't know what is happening here. Some of my doubts: In the transformer I don't know if I have to change the height and the width of the canvas so instead of provide a 8x64 it provides a 16x32 by doing

int P10outdoorTransformer::TransformCanvas::width() const { return delegatee->width() / 2; } int P10outdoorTransformer::TransformCanvas::height() const { return delegatee->height() * 2; }

This is correct, you want to return how the new, transformed, matrix is like. While internally, it is 8x64 in some patchy pattern, the resulting matrix in the physical world is 16x32.

Once the transformer is applied, if you now ask in your application the size of the matrix, it will return 16 heigh and 32 wide.

If I use that configuration, I don't know in which coordinate system are the x and y values from the setPixel method,

The SetPixel() method in the transformer receives pixels in the 16x32 configuration so you now have to calculate where that maps to the underlying 8x64 configuration and call delegatee_->SetPixel() with x in the range of 0..63 and y in 0..7.

So if you are inside the SetPixel() in the transformer you get calls with all the pixels y=0..15, x=0..31. You could printf() that for debugging if you want. The transformer matrix is called once in the beginning, so you would see these nicely in order.

that they come on the original size which is 8 x 64 and I have to transform them into 16x32 or it is the inverse transformation:

  • The set pixel provides me the pixels of the new Canvas that he wants to paint and ask the method where have to find it on the original (8x64) coordinate system.

Yes, this one.

  • How all this logic is mixed with the c and r parameter?

    The -c and -r parameter describe the underlying matrix, so here you have to give -c 2 -r 8, which you already do.

Sorry it is a bit confusing, but the manufacturers make these panels in different mappings to have the refresh flickering less apparent, so in the end it is for a good reason :)

I don't have such a panel myself, otherwise I would've already added something that makes it simpler. But with people like you who figure out the mappings of certain panels, I can then code later that simplifies things. Then, it will be as simple as saying -c 1 -r 16 --led-multiplexing=8 or something.

gpulido commented 7 years ago

Hello again, Thank you very much for your usefull tips. I have managed to create a transformer that works with one panel. This is the relevant part of the transformer:

int P10outdoorTransformer::TransformCanvas::width() const {
  return delegatee_->width()/2;
}

int P10outdoorTransformer::TransformCanvas::height() const {
  return delegatee_->height()*2;
}

void P10outdoorTransformer::TransformCanvas::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) {
  int new_x = x;
  int new_y = y;

  if (y>= 0 && y<4)
  {
    new_x = x + 8 + 8*(x/8);
    new_y = y;
  }
  else if (y>= 4 && y<8)
  {
    new_x = x+ 8*(x/8);
    new_y = y%4;
  }
  else if (y>= 8 && y<12)
  {
    new_x = x + 8+ 8*(x/8);
    new_y = (y +4)%8;
  }
  else if (y>= 12 && y<16)
  {
    new_x = x+ 8*(x/8);
    new_y = y%8;
  }
 delegatee_->SetPixel(new_x, new_y, red, green, blue);
}

Now I have to generalize it to be able to manage several P lines and several concatenated panels. It just a matter of figuring out correctly the module for the y coordinate to apply :)

One question, in order to be able to use this matrix also with the c samples what should I do? As they are already added to the transformer and the led-matrix.cc I don't know if it is necesary to add the transformation to the demo-main.cc

gpulido commented 7 years ago

Hello again, after done some module maths this is final transform function that takes into account complete vertical and horizontal extension. I have tested it using two panels in daisy chain (-r8 -c4) to obtain a 16x64 display and in parallel config using two lines (-r8 -c2 -P2) and it worked perfectly:


int P10outdoorTransformer::TransformCanvas::width() const {
  return delegatee_->width()/2;
}

int P10outdoorTransformer::TransformCanvas::height() const {
  return delegatee_->height()*2;
}

void P10outdoorTransformer::TransformCanvas::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) {
  int new_x = x;
  int new_y = y;  
  int xoffset =8*(x/8);
  int yoffset = ((y + 4)%8) /4;
  new_x = x + 8 * yoffset + xoffset;
  new_y = (y%4) + 4*(y/8);        
  delegatee_->SetPixel(new_x, new_y, red, green, blue);
}

I hope this could help others.

gausie commented 7 years ago

Hey @gpulido - I have exactly the same matrix as you and, having just run your Python script above we clearly need the same solution. Thank you so much for putting the time in! Could you go into a bit more detail with the transformer? Is this all you needed?

EDIT: Here is a fork with a working branch

https://github.com/gausie/rpi-rgb-led-matrix/tree/p10transformer

However, the transforms don't seem to be applied in the python bindings

gpulido commented 7 years ago

Hello @gausie In order to use the transformer into python directly you have to add it on the places that @hzeller pointed out: here and here After doing so you need to recompile the led the python bindings. I made it by removing the bin folder from the python directory and rebuilding them by :

make build-python
sudo make install-python

I hope this solve your problem.

gausie commented 7 years ago

Yeah I did that and still no luck. The native examples run fine but the Python is still stuck in the old way. I'm pretty sure I cleaned the project of all build artefacts, but maybe not.

I'm using Python 3 if that has any differences.

gpulido commented 7 years ago

You can try removing completely the python folder and pull it again from the github (just the directory) and build it again. Also maybe you have to review your python installation lib folder and review that the led matrix library is renewed...

gausie commented 7 years ago

Did all of that and it now works! Thank you for this and for your work above!

On Sun, 19 Feb 2017 at 19:52 Gabriel Pulido notifications@github.com wrote:

You can try removing completely the python folder and pull it again from the github (just the directory) and build it again. Also maybe you have to review your python installation lib folder and review that the led matrix library is renewed...

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hzeller/rpi-rgb-led-matrix/issues/283#issuecomment-280943679, or mute the thread https://github.com/notifications/unsubscribe-auth/AAK-LptDnAE4l6ZP9umlbVeDhV47hDuKks5reJ2CgaJpZM4L_IHb .

Dhanielk commented 6 years ago

Hi @hzeller, @gpulido, @gausie. With your comments, I was able to make P8 32x16 1/4 scan panel work. But to make it work properly, I had to use chain 2x real value. I am running it on 4 panels (128x16 WxH).

Any clue where to look? I think it also slows it down to use chain=8 instead of real 4.

Also I need to rotate it 180 degree, because it's upside down, but that's not real issue.

Thanks, Daniel

hzeller commented 6 years ago

chain x2 is exactly what is expected, as the data to be clocked in is double the length, so your solution is working!

For the rotation, you can stack a rotation transformer.

Dhanielk commented 6 years ago

Thanks for the reply @hzeller... however I am still bit sceptical. When I run rotating square, I have only half of the image on the left side of P1.

This seems like with chain = 8, it's creating phantom panels.

Setup: rpi > P1 > P2 > P3 > P4

How it seems with demos (rotating square is on left half of P1): P? > P? > P? > P? > P1 > P2 > P3 > P4

Hope it make sense...

Thanks, Daniel

hzeller commented 6 years ago

BTW, there is now a new option --led-multiplexing which might help solve these things with the stock code.