board707 / DMD_STM32

STM32Duino library for RGB, Monochrome and Two-color led matrix panels
GNU General Public License v3.0
54 stars 18 forks source link

Pico + RTOS #17

Closed wsandor closed 1 month ago

wsandor commented 1 year ago

First of all, thank you for your great work! At the beginning I tried your dmd_rgb example and it worked fine. Then tried to experiment with it. I would like to use an RTOS task to generate images at the background (as I did on ESP32). Here a big problem arose: if I include the FreeRTOS.h the matrix stops working. (even if there is no tasks created, just the include) I presume the two libraries both try to use some same resources (timer?) which causes the conflict. I am afraid that my knowledgeat the moment is still not enough to find the exact reason. Maybe you can find and correct it easily. If it would be a too big work, then please take it as a feature request for the future.

board707 commented 1 year ago

Thank you for this info. I am very sorry, but I never run my library in a RTOS environment. The library needs for direct low-level access to the timers and GPIO registers, so it may be incompatible with a time-sharing software like RTOS. I can say more about the issue after making some test of ir.

board707 commented 1 year ago

Hi Decided to return to your question. Could you please to provide a short example sketch for this issue?

wsandor commented 1 year ago

Hi, Thanks for dealing with this issue! For example, if I put these lines to the beginning of your dmd_rgb.ino demo (from the examples\STM32F4_RP2040\dmd_rgb folder)

#include <FreeRTOS.h> // Must always be `#include` first. It references other configuration files, and sets defaults where necessary.
#include <FreeRTOSConfig.h> // Contains a multitude of API and environment configurations.
#include <variantHooks.cpp> // Contains the RP2040 specific configurations for this port of FreeRTOS.
#include <heap_3.c> // Contains the heap allocation scheme based on `malloc()`. Other schemes are available, but depend on user configuration for specific MCU choice.

Then the matrix don't works properly. I do not need to call any RTOS functions. What I see with an oscilloscope is that there are no clock and data signals on the pins, but the ABCD, LATCH and the OE are okay. The brightness on the display is changing as your scetch runs, the display shows the same data on every row, possibly the last one that was shifted out before starting this program. In the meantime I tried to run my program on two cores (without RTOS), and the matrix works normally if I call the dmd functions only from the core on wich dmd.init() was called, so some multitasking could be done without the RTOS (I generated Adafruit GFX bitmaps on the second core while the dmd which was run on the first, showed them).

IMG_20221213_163936

The cause of the blue squares on the displays are different, the shift drivers would need some initialization. By the way, could you help me about this also? On my modules there are MBI5124 type driver ICs, which need some initialization. I can do this part, but don't know hot to put it into your code. I want it to be universal, as there are other types of shift registers which also need some initialization. So, what I would like is a new parameter to the DMD_RGB declaration or to the dmd.init() (I don't know which place would be better) which tells the type of initialization needed. And a function which can do the initialization before the matrix stuff (PIO, DMA,... ) starts. This function has to know about the RGB, CLK LAT and OE pins to be able to bit-bang the init sequence to them. I hope you can understand what I mean.

board707 commented 1 year ago

Thanks for your info

What I see with an oscilloscope is that there are no clock and data signals on the pins, but the ABCD, LATCH and the OE are okay. The brightness on the display is changing as your scetch runs...

It means that the timers and PIO-machines are work properly. Lacking data and clock signals, therefore, can be caused by problems with either DMA or dynamic memory management, the library using them both actively. Unfortunately, my experience with RTOS is very limited and I don't know where is looking further

On my modules there are MBI5124 type driver ICs, which need some initialization. I can do this part, but don't know hot to put it into your code.

I have worked with similar matrices and even added one of them to the library. But since I do not have such a matrix for tests, then with the next update I had to remove this code from the release, since I cannot support it. In the library, each type of panel is designed as a separate template. I can create a new template for panels with registers and insert the initialisation into the chip_init() method, which was once created for this.

wsandor commented 1 year ago

I can create a new template for panels with registers and insert the initialisation into the chip_init() method, which was once created for this.

I would appreceiate if you could do this.

board707 commented 1 year ago

Suppose you have a 64x32 s16 panel You can add this at the end of RGB.h file:

template<int COL_DEPTH>
class DMD_RGB<RGB64x32_S16_wsandor, COL_DEPTH> : public DMD_RGB_BASE2<COL_DEPTH>
{
public:
    DMD_RGB(uint8_t* mux_list, byte _pin_nOE, byte _pin_SCLK, uint8_t* pinlist,
        byte panelsWide, byte panelsHigh, bool d_buf = false) :
        DMD_RGB_BASE2<COL_DEPTH>(4, mux_list, _pin_nOE, _pin_SCLK, pinlist,
            panelsWide, panelsHigh, d_buf, COL_DEPTH, 16, 64, 32)
    {}

protected:
       void chip_init() override {
        // insert your shift register initialization here
          }

};

and add define for new panel type

#define RGB64x32_S16_wsandor 33

to the other similar defines at the beginning of the RGB.h.

Using of the new defined panel type:

DMD_RGB <RGB64x32_S16_wsandor, COLOR_4BITS> dmd(mux_list, DMD_PIN_nOE, DMD_PIN_SCLK, custom_rgbpins, DISPLAYS_ACROSS, DISPLAYS_DOWN, ENABLE_DUAL_BUFFER);
wsandor commented 1 year ago

Thank you! Some minor changes was also necessary in DMD_RGB.h and DMD_RGB.cpp as the chip_init() was in the STM32 conditional part, but now it is working as expected. Here is my MBI5124 init sequence, maybe someone will find it useful:

void chip_init() {
    uint16_t PIXELS_PER_ROW = this->DisplaysTotal * this->DMD_PIXELS_ACROSS * this->DMD_PIXELS_DOWN / (2 * this->nRows);
    #define CLK_PULSE          digitalWrite(this->pin_DMD_CLK, HIGH); digitalWrite(this->pin_DMD_CLK, LOW);

    bool REGR[16] = {0,1, 1,1, 1,1, 0,1,0,1, 1,0,1,0,1,1};    // this sets the R control register
    bool REGG[16] = {1,1, 1,1, 0,0, 0,1,0,1, 1,0,1,0,1,1};    // this sets the G control register
    bool REGB[16] = {1,1, 1,0, 1,1, 0,1,0,1, 1,0,1,0,1,1};    // this sets the B control register

    for (uint8_t _pin:{this->rgbpins[0], this->rgbpins[1], this->rgbpins[2], this->rgbpins[3], this->rgbpins[4], this->rgbpins[5], this->pin_DMD_CLK, this->pin_DMD_SCLK /*This is Latch*/, this->pin_DMD_nOE}){
        pinMode(_pin, OUTPUT);
        digitalWrite(_pin, LOW);
    }

    digitalWrite(this->pin_DMD_nOE, HIGH); // Disable Display

    // Send Data to control register REG R, G, B data value according to the MBI5124 data sheet
    for (int l = 0; l < PIXELS_PER_ROW; l++){
        for (uint8_t _pin:{this->rgbpins[0], this->rgbpins[3]})  //R
          digitalWrite(_pin, REGR[l%16]);   // we have 16 bits shifters and write the same value all over the matrix array
        for (uint8_t _pin:{this->rgbpins[1], this->rgbpins[4]})  //G
          digitalWrite(_pin, REGG[l%16]);
        for (uint8_t _pin:{this->rgbpins[2], this->rgbpins[5]})  //B 
          digitalWrite(_pin, REGB[l%16]);
        if (l == PIXELS_PER_ROW - 4){         // pull the latch 4 clocks before the end of matrix so that REG1 starts counting to save the value
            digitalWrite(this->pin_DMD_SCLK, HIGH);//Latch
        }
        CLK_PULSE
    }

    // drop the latch and save data to the REG all over the MBI5124 chips
    digitalWrite(this->pin_DMD_SCLK, LOW);

    // blank data regs to keep matrix clear after manipulations
    for (uint8_t _pin:{this->rgbpins[0], this->rgbpins[1], this->rgbpins[2], this->rgbpins[3], this->rgbpins[4], this->rgbpins[5]}) {
        digitalWrite(_pin, LOW);
    }

    for (int l = 0; l < PIXELS_PER_ROW; ++l){
        CLK_PULSE
    }

    digitalWrite(this->pin_DMD_SCLK, HIGH);
    digitalWrite(this->pin_DMD_SCLK, LOW);
    digitalWrite(this->pin_DMD_nOE, LOW); // Enable Display
}       
board707 commented 1 year ago

Thank you! Is it eliminate "blue squares" artifacts?

wsandor commented 1 year ago

I hope so. To tell the truth these artifacts are only rarely visible - if some garbage data shifted out to the modules (during program downloading or because of a bad program) and accidentelly written to the driver's config register. Since I've done the initialization, I haven't experienced it, but I will test it further.

wsandor commented 1 year ago

Just some feedback: I haven't experienced the artifacts prior to the chip init, so it seems that it works properly.

Other topic: I extended the library with the handling of HUB12 RG panels with RP2040. It is based on your DMD_monochrome_Parallel code. It uses 2 consecutive outputs for one RG panel, so you have 4 outputs maximum. The bit depth is only 1. (where a pixel's red or green component greater than 0 on the GFX canvas it draws an RG pixel according to it) I've tried it with 32x16S4 cards and 16x8 Static cards. I have to limit the brigtness in both cases, because if the brightness was 255, the output become instable. (the first module in the row was almost good, the next very bad). I think it is some timing issue on OE, but limiting the brightness to 254 seems to solve it, so I haven't investigate it further. I've also include the module size as static const in the DMD classes in order to able to calculate the screen size from DISPLAYS_ACROSS and DISPLAYS_DOWN at compile time (and declare some GFXCanvas objects which I use in my program). I've attached the code: (RG.zip), use it if you wish. The work is still in progress and only the RP2040 part is tested, the ST parts (I left them from the monochrome unit) are broken.

board707 commented 1 year ago

Hi ! Thank you for your feedback, it got me very interested. I will definitely take a look at your code.

The current version of the library supports working with two-color RG panels, but with an HUB08 interface. Except for the location of the signals in the connector, the rest of the work is similar to three-color, that is, RGB HUB75 panels. It using DMD_RGB class and works with STM32 and RP2040 boards. The suggested color depth is 1bit, but there is no limitation in the library.

wsandor commented 1 year ago

Hi! I haven't tried the HUB08 RG /RGB part yet, but from the description it seems to me that it can only outputs to 2 card rows. (maybe I am wrong with it) My version (which is mostly your parallel monochrome version) could output to 4 rows at once, and I can use the same pcb to drive HUB75 RGB panels or HUB12 RGs (in HUB12 mode I use the two B channels and the A, B row signals as RG outputs too, while the C and D row signals become the A and B row outputs here)

board707 commented 1 year ago

I haven't tried the HUB08 RG /RGB part yet, but from the description it seems to me that it can only outputs to 2 card rows

Sorry, I don't understand on which card rows you talk about. The HUB08 connector has two red and two green inputs, so it is similar to RGB HUB75, but without lines for blue color. How do you connect signals of DMD_monochrome_Parallel code to RG matrix? Could you show your connections?

wsandor commented 1 year ago

Here is my connector pinout:

HUB75HUB12

Depending on program compile, I can use 1 RGB or 4 RG outputs

board707 commented 1 year ago

Sorry, but it still unclear for me... Could you show the connector on your RG matrix?

wsandor commented 1 year ago

If you mean the connector on the LED cards, then they are "normal" 2x8 pin headers with HUB12 pinout. (But as your monochrome library can drive 8 from it parallel, the RG can 4, and in my implementation they use the same data pins on the processor as the HUB75, so if there are 5 output connectors on the card (1 HUB75 and 4 HUB12) then I can use 1 RGB output or 4 RG depending on the program)

board707 commented 1 year ago

I think it might be of interest not only to me. Do you want to open a new topic in the discussions, where to describe in more detail your design with a diagram of connections between a card and a controller and explanations for the code?

Anyway, thanks for sharing your ideas.

board707 commented 1 year ago

@wsandor Your idea about the work of RG matrices using DMD_monochrome_Parallel code still is not quite clear to me, but your additions to the RGB.h file seemed very interesting.

I've also include the module size as static const in the DMD_ classes

In the next release, instead of using a separate template for each matrix, I plan to switch to parametric templates that will describe several similar matrices at once. Among the parameters will be the dimensions of the panel, so that your static constants can be calculated automatically. In addition, I was interested in your template of the matrix RGB64x32plainS16_MBI5124. This chip has required a setup of the some configuration registers before use. I remembered that I have a code for similar matrices on FM6216a driver, which I did not know how to combine with the main library code. Now I have an example how to do it. But your coordinate transformations for matrices with complex patterns seem too complicated to me. In my opinion, they can be written shorter.

wsandor commented 1 year ago

But your coordinate transformations for matrices with complex patterns seem too complicated to me. In my opinion, they can be written shorter.

You probably right, but for me, it seems to be a simpler (for my brain ;) ) and more universal solution. I haven't tested which one is faster at runtime.

board707 commented 1 year ago

This topic is continued in discussion #28

board707 commented 1 year ago

@wsandor , you wrote:

declare some GFXCanvas objects which I use in my program

Could you please show an example using GFXCanvas objects with the library? if you do not want to post it in the public domain - you can write to me by mail dd@jwee.ru Thanks

wsandor commented 1 year ago

Unfortunately, I don't have a whole simple sample to show, but try to show the relevant parts. The main purpose behind it was to be able to make a moving (scrolloing) message which runs before the main screen content. Originally I wrote this for an other similar library for ESP32. I use DMD in single buffer mode, and make double buffering in my program: (maybe it is not neccessary but when I first tried with double buffering, I had some problems (unfortunately I haven't remember what was it exactly, maybe there are simpler solutions to it.

GFXcanvas16 background1(SCREENWIDTH, SCREENHEIGHT);
GFXcanvas16 background2(SCREENWIDTH, SCREENHEIGHT);
GFXcanvas16 *readCanvas;
GFXcanvas16 *writeCanvas;

readCanvas = &background1;
writeCanvas = &background2;

When I draw in the main program I draw for the writeCanvas, e.g.:

void drawString(int16_t x, int16_t y, uint16_t charColor, const char text[]) {
  writeCanvas->setCursor(x + actXOffset, y + actYOffset);    // 
  writeCanvas->setTextColor(charColor);
  writeCanvas->setTextWrap(false);
  writeCanvas->print(text);
}

The moving message part is where I would like to use RTOS, but not succeded with it. However it could be done without it also. For the moving message I use a 1bit GFX canvas

GFXcanvas16 workCanvas(SCREENWIDTH, SCREENHEIGHT);
GFXcanvas1 mmsCanvas(SCREENWIDTH + MMS_EXTRAWIDTH, MMS_MAX_HEIGHT);

And this is the part that do the screen refresh (I left the part that draws mmsCanvas in it - draws the text and shift left it if it is necesssary ):

if (time_us_64() - frameTime > 10000) {
  frameTime = time_us_64();
  baseDrawed = false;
  if (refreshScreen) {    // refreshScreen is true, when the main screen changed and need to refresh
    MyCanvas16 *p = readCanvas;
    readCanvas = writeCanvas;
    writeCanvas = p;
    refreshScreen = false;
   }
  workCanvas.drawRGBBitmap(0, 0, readCanvas->getBuffer(), readCanvas->width(), readCanvas->height());
  baseDrawed = true;
}
if (!mmsFinished) {            // mmsFinished is false when the moving message showing
  if (!baseDrawed) {      // if the main screen hasn't changed we have to draw it here
    workCanvas.drawRGBBitmap(0,0, readCanvas->getBuffer(), readCanvas->width(), readCanvas->height());
  }
  workCanvas.drawBitmap(actMMSLeft, actMMSTop, mmsCanvas.getBuffer(), mmsCanvas.width(), mmsCanvas.height(), actMMSColor); // Copy MMS to screen
  baseDrawed = true;
}  
if (baseDrawed)  {    //if anything chaged, we rehresh the Output in DMD
  dmd.drawRGBBitmap(0,0, workCanvas.getBuffer(), workCanvas.width(), workCanvas.height());
} 

I hope you can understand the working principle from it. I you've any questions don't hesitate to ask.

board707 commented 1 year ago

Thanks a lot! I understand the general principle, because I myself have already thought about adding additional scrolling layers to the code. This was the reason why I decided to ask you about using canvases. I will try to write a full-fledged example of scrolling using layers, maybe then I will have clarifying questions.

board707 commented 1 month ago

Hi @wsandor . Returning to RTOS issue.

if I put these lines to the beginning of your dmd_rgb.ino demo

#include <FreeRTOS.h> // Must always be `#include` first. It references other configuration files, and sets defaults where necessary.
#include <FreeRTOSConfig.h> // Contains a multitude of API and environment configurations.
#include <variantHooks.cpp> // Contains the RP2040 specific configurations for this port of FreeRTOS.
#include <heap_3.c> // Contains the heap allocation scheme based on `malloc()`. Other schemes are available, but depend on user configuration for specific MCU choice.

Then the matrix don't works properly. I do not need to call any RTOS functions.

I accidentally checked this code again and found that on the latest editions of the RP2040 package and DMD_STM32 library, the dmd_rgb.ino example with these additives works fine. Could you provide a short example code, which I could use to test compatibility of my library with RTOS?

board707 commented 1 month ago

Hi again I tested some codes, combined from dmd_rgb.ino and RP2040 Blink RTOS example https://github.com/earlephilhower/arduino-pico/libraries/FreeRTOS/examples/StaticMulticore-FreeRTOS/StaticMulticore-FreeRTOS.ino This combined code compiles, and the DMD output works fine and, as far as I see - with the unchanged speed. In contrast, although Blink works, it is very slow. In the code, the LED should blink at intervals of 250-on 500-off - but in reality the delays are 5-20 seconds long, and the periods are not uniform. It seems that the DMD interrupts has a priority and does not allow the RTOS to work.

wsandor commented 1 month ago

Hi board707, Thanks for dealing with this issue again! As it hasn't worked, I abandoned RTOS and haven't tried it since then. At the present unfotunately I don't have time to try it, but I hope I could deal with it within some weeks. I will write if I will have results, bad or good.

board707 commented 1 month ago

Thanks for your feedback. Don’t waste your time, I’ll try to find examples myself and understand what the problem is. I'll write if I have any results.

board707 commented 1 month ago

Hi! I did some tests and I have the results.

As I wrote above I took example from the FreeRTOS library of the arduino-pico package and added them to the dmd_rgb example of the library. The resulting code didn't work. Then I tried to run the Pico FreeRTOS example as is - and it turned out that the example itself did not work either. I wrote to Earle Philhower and he replied that this was a bug in the example itself - and suggested how to fix it. After that, all the examples worked, including the one with DMD_STM32 library.

Thus, I am inclined to believe that the DMD_STM32 library is compatible with RTOS. :)

wsandor commented 1 month ago

Thank you for the info! I will try it.