DMD_STM32 - версия библиотеки DMD для СТМ32Дуино

FoxJone
Offline
Зарегистрирован: 19.04.2019

Antsanv пишет:

Добрый день.

Скажите, я правильно понимаю: на один SPI мы цепляем последовательно 20 матриц, но в геометрии допустим 5(д)х4(в). И для того чтобы отображать текст высотой 64 пикселя надо просто сконвертировать нужный шрифт и указать в настройках скетча параметры 5х4?

Совершенно верно

Вот мой шрифт на 96 пикселей:

Antsanv
Offline
Зарегистрирован: 18.12.2018

Попробовал матрицы с инверсией Р10 R1/G1.
В изначальной DMD2 работало так:
dmdG.fillScreen(true); - очистка
dmdR.drawString(2, 0, recievedMsg, GRAPHICS_INVERSE); - вывод строки

В Ваше библиотеке пробовал:
dmd.clearScreen( 0 );
dmd.drawString(1, 1, k, strlen(k), 1, 0);

не получается.
 

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

Попробовал матрицы с инверсией Р10 R1/G1.

это случайно не двуцветные матрицы? - если да, то я с ними не работал.

Для одноцветных матриц с инверсией в моей библиотеке есть отдельный метод

dmd.inverseAll(true);

даете эту команду после dmd.init();

и далее работаете с матрицей как обычно

 

кстати, синтаксис

dmd.drawString(2, 0, recievedMsg, GRAPHICS_INVERSE); - вывод строки

тоже должен работать
 

 

Antsanv
Offline
Зарегистрирован: 18.12.2018

Спасибо. Это именно двухцветные матрицы красные/зеленые. У них еще один пин задействован G. В изначальной библиотеке я создавал два soft объекта и управлял ими. 

SoftDMD dmdR(DISPLAYS_WIDE, DISPLAYS_HIGH, PIN_OE, PIN_A, PIN_B, PIN_SCK, PIN_CLK, PIN_MOSI_R);
SoftDMD dmdG(DISPLAYS_WIDE, DISPLAYS_HIGH, PIN_OE, PIN_A, PIN_B, PIN_SCK, PIN_CLK, PIN_MOSI_G);

завтра попробую Ваш совет. Еще раз спасибо.

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

Спасибо. Это именно двухцветные матрицы красные/зеленые.

не знаю, удастся ли их запустить в двухцветном варианте. Как одноцветные может и заработают.

Что касается инверсии - для начала выясните, какое значение параметра в методе clearScreen() - true или false - полностью гасит панели. После этого добиться правильного вывода уже будет нетрудно.

Antsanv
Offline
Зарегистрирован: 18.12.2018

Не гасит не один из имеющихся. 
В любом варианте засвечиваются все пиксели. Строка выводится в инверсии. Но выводится.

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

Не гасит не один из имеющихся. 
В любом варианте засвечиваются все пиксели.

что-то тогда сомневаюсь, что что-то выйдет.

clearScreen() выводит на панели либо все нули, либо единицы. В зависимости от того, инвертированная панель или нет - либо true гасит все пиксели, либо false... Но какой-то из двух точно должен гасить.

Antsanv
Offline
Зарегистрирован: 18.12.2018

b707 пишет:

Antsanv пишет:

Не гасит не один из имеющихся. 
В любом варианте засвечиваются все пиксели.

что-то тогда сомневаюсь, что что-то выйдет.

clearScreen() выводит на панели либо все нули, либо единицы. В зависимости от того, инвертированная панель или нет - либо true гасит все пиксели, либо false... Но какой-то из двух точно должен гасить.

Логично. Проверить не удалось, панели забрали на неделю. Но возможно все дело было в длинном шлейфе (80 см) и 3,3В непредсказуемо падало до панели.

Проверял сегодня шрифты и получается что fontconverter с высотой ошибается
"где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях"
ставлю size 16, а на выводе получается высота в 26 светодиодов. 

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

Проверял сегодня шрифты и получается что fontconverter с высотой ошибается
"где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях"
ставлю size 16, а на выводе получается высота в 26 светодиодов. 

да. я тоже это замечал, правда у меня фонты получались немного меньше указанного - ставлю 16. получаю 13.  А 26 вместо 16-ти - это как-то сильно :)

Возможно это как-то зависит от исходного Truetype файла.

Antsanv
Offline
Зарегистрирован: 18.12.2018

Под ESP не планируете доработки библиотеки?

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

Под ESP не планируете доработки библиотеки?

под 8266 точно нет, а под ESP32 возможно. Но пока есть более актуальные задачи.

aidar_i
Offline
Зарегистрирован: 07.10.2018

Спасибо за библиотеку!

Пробую работать Вашей библиотекой с Ардуино, у Ардуино на 328 камне мало памяти, решил использовать МЕГА2560.

Чтобы работала ATmega2560 и ATmega328P заменил в файлах DMD_STM32.h и DMD_STM32.cpp  (__AVR_ATmega328P__) на (__AVR_ATmega2560__) || (__AVR_ATmega328P__). Погонял в Протеус - работает. Хочу в железе попробовать. Библиотеку DMD запускал в железе - работает.

Есть вопрос по регулировке яркости, в железе на Ардуино работает?

Увидел , нет. Можно сделать?

Нашел подсказку в Вашем файле DMD_STM32.cpp (как в DMD2).

Поставил в строки от 745 по 748 строки 

  if(brightness == 255)

    digitalWrite(pin_DMD_nOE, HIGH);
  else
    analogWrite(pin_DMD_nOE, brightness);
Правильно?
В Протеус заработал, буду завтра в железе испытывать.
/*--------------------------------------------------------------------------------------

 DMD_STM32.cpp  - STM32 port of DMD.h library (see below)
 
 adapted by Dmitry Dmitriev (c) 2019
 
 DMD.cpp - Function and support library for the Freetronics DMD, a 512 LED matrix display
           panel arranged in a 32 x 16 layout.

 Copyright (C) 2011 Marc Alexander (info <at> freetronics <dot> com)

 ---

 This program is free software: you can redistribute it and/or modify it under the terms
 of the version 3 GNU General Public License as published by the Free Software Foundation.

 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with this program.
 If not, see <http://www.gnu.org/licenses/>.

--------------------------------------------------------------------------------------*/
#include "DMD_STM32.h"


#if defined(__STM32F1__)
static volatile DMD* running_dmds[2];
static volatile uint8_t running_dmd_len =0;
static uint16_t scan_int = 2000;

static void register_running_dmd(DMD *dmd)
{
  uint8_t spi_num =dmd->spi_num;
  if (!spi_num) return;
  if (running_dmd_len == 0) {
	  Timer4.pause(); // останавливаем таймер перед настройкой
      Timer4.setPeriod(scan_int); // время в микросекундах (500мс)
      Timer4.attachInterrupt(TIMER_UPDATE_INTERRUPT, scan_running_dmds); // активируем прерывание
      Timer4.refresh(); // обнулить таймер 
      Timer4.resume(); // запускаем таймер  

      Timer3.pause();
      Timer3.setPeriod(30); 
      Timer3.refresh();
	  Timer3.resume();
	  }
  if (!running_dmds[spi_num -1]) running_dmd_len++;

  running_dmds[spi_num -1] = dmd;
}

static void inline __attribute__((always_inline)) scan_running_dmds()
{
  static volatile uint8_t i =0;
 
    DMD *next = (DMD*)running_dmds[i%2];
	i++;
    if(next) {
      next->scanDisplayByDMA();
	  
    }
  
}
static void SPI1_DMA_callback() {
	DMD *next = (DMD*)running_dmds[0];
	next->latchDMA();
}

static void SPI2_DMA_callback() {
	DMD *next = (DMD*)running_dmds[1];
	next->latchDMA();
}

  
#endif


/*--------------------------------------------------------------------------------------
 Setup and instantiation of DMD library
 Note this currently uses the SPI port for the fastest performance to the DMD, be
 careful of possible conflicts with other SPI port devices
--------------------------------------------------------------------------------------*/


DMD::DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi )
:pin_DMD_A(_pin_A), pin_DMD_B(_pin_B), pin_DMD_nOE(_pin_nOE), pin_DMD_SCLK(_pin_SCLK), 
 DisplaysWide(panelsWide), DisplaysHigh(panelsHigh), SPI_DMD(_spi)
{
	
	uint16_t ui;
    
    DisplaysTotal=DisplaysWide*DisplaysHigh;
    row1 = DisplaysTotal<<4;
    row2 = DisplaysTotal<<5;
    row3 = ((DisplaysTotal<<2)*3)<<2;
    bDMDScreenRAM = (byte *) malloc(DisplaysTotal*DMD_RAM_SIZE_BYTES);
    
 #if defined(__STM32F1__)
    pin_DMD_CLK = SPI_DMD.sckPin();   
    pin_DMD_R_DATA = SPI_DMD.mosiPin() ; 
	pinMode(pin_DMD_nOE, PWM);  // setup the pin as PWM
	SPI_DMD.begin(); //Initialize the SPI_2 port.
	/*SPI_DMD.setBitOrder(MSBFIRST); // Set the SPI_2 bit order
	SPI_DMD.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
	SPI_DMD.setClockDivider(SPI_CLOCK_DIV16);  // Use a different speed to SPI 1 */
	//SPI_DMD.beginTransaction(SPISettings(DMD_SPI_CLOCK, MSBFIRST, SPI_MODE0));
#if defined( DMD_USE_DMA )	
	dmd_dma_buf = (byte *) malloc(DisplaysTotal*DMD_DMA_BUF_SIZE);
    rx_dma_buf =  (byte *) malloc(DisplaysTotal*DMD_DMA_BUF_SIZE);
    spiDmaDev = DMA1;
    if (SPI_DMD.dev() == SPI1) {
      	  spiTxDmaChannel = DMA_CH3;
		  spi_num =1;
      }
      else {
		  spiTxDmaChannel = DMA_CH5;
		  spi_num =2;
      }
#endif
	
#elif defined(__AVR_ATmega2560__) || (__AVR_ATmega328P__)
    pin_DMD_CLK = 13;
    pin_DMD_R_DATA = 11;
	pinMode(pin_DMD_nOE, OUTPUT); 
	 // initialize the SPI port
    SPI.begin();		// probably don't need this since it inits the port pins only, which we do just below with the appropriate DMD interface setup
    SPI.setBitOrder(MSBFIRST);	//
    SPI.setDataMode(SPI_MODE0);	// CPOL=0, CPHA=0
    SPI.setClockDivider(DMD_SPI_CLOCK);
 #endif
    

    digitalWrite(pin_DMD_A, LOW);	// 
    digitalWrite(pin_DMD_B, LOW);	// 
    digitalWrite(pin_DMD_CLK, LOW);	// 
    digitalWrite(pin_DMD_SCLK, LOW);	// 
    digitalWrite(pin_DMD_R_DATA, HIGH);	// 
  
    pinMode(pin_DMD_A, OUTPUT);	//
    pinMode(pin_DMD_B, OUTPUT);	//
    pinMode(pin_DMD_CLK, OUTPUT);	//
    pinMode(pin_DMD_SCLK, OUTPUT);	//
    pinMode(pin_DMD_R_DATA, OUTPUT);	//
    

    clearScreen(true);
    //brightness =20000;
    bDMDByte = 0;

}


//DMD::~DMD()
//{
//   // nothing needed here
//}

void DMD::init(uint16_t scan_interval) {
#if defined(__STM32F1__)
    scan_int = scan_interval;
	SPI_DMD.begin(); //Initialize the SPI_2 port.
	SPI_DMD.setBitOrder(MSBFIRST); // Set the SPI_2 bit order
	SPI_DMD.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
	//SPI_DMD.setClockDivider(SPI_CLOCK_DIV16);  // Use a different speed to SPI 1 */
	SPI_DMD.beginTransaction(SPISettings(DMD_SPI_CLOCK, MSBFIRST, SPI_MODE0));
	register_running_dmd(this);
	brightrange = Timer3.getOverflow();
	setBrightness(brightrange/3);
	/*
	pinMode(PA1, OUTPUT);
	digitalWrite(PA1, LOW);*/
	
#endif
}
//DMD I/O pin macros
void
 DMD::LIGHT_DMD_ROW_01_05_09_13()       { digitalWrite( pin_DMD_B,  LOW ); digitalWrite( pin_DMD_A,  LOW ); }
void
 DMD::LIGHT_DMD_ROW_02_06_10_14()       { digitalWrite( pin_DMD_B,  LOW ); digitalWrite( pin_DMD_A, HIGH ); }
void
 DMD::LIGHT_DMD_ROW_03_07_11_15()       { digitalWrite( pin_DMD_B, HIGH ); digitalWrite( pin_DMD_A,  LOW ); }
void
 DMD::LIGHT_DMD_ROW_04_08_12_16()       { digitalWrite( pin_DMD_B, HIGH ); digitalWrite( pin_DMD_A, HIGH ); }
void
 DMD::LATCH_DMD_SHIFT_REG_TO_OUTPUT()   { digitalWrite( pin_DMD_SCLK, HIGH ); 
                                          //delayMicroseconds(1);
                                          digitalWrite( pin_DMD_SCLK,  LOW ); }
 #if defined(__STM32F1__)
    void DMD::OE_DMD_ROWS_OFF()                 { pinMode( pin_DMD_nOE, INPUT  ); }
	void DMD::OE_DMD_ROWS_ON()                 { pinMode( pin_DMD_nOE, OUTPUT  ); }
  #elif defined(__AVR_ATmega2560__) || (__AVR_ATmega328P__)
    void DMD::OE_DMD_ROWS_OFF()                 { digitalWrite( pin_DMD_nOE, LOW  ); }
    void DMD::OE_DMD_ROWS_ON()                  { digitalWrite( pin_DMD_nOE, HIGH ); }
 #endif
 



/*--------------------------------------------------------------------------------------
 Set or clear a pixel at the x and y location (0,0 is the top left corner)
--------------------------------------------------------------------------------------*/
void
 DMD::writePixel(unsigned int bX, unsigned int bY, byte bGraphicsMode, byte bPixel)
{
    unsigned int uiDMDRAMPointer;

    if (bX >= (DMD_PIXELS_ACROSS*DisplaysWide) || bY >= (DMD_PIXELS_DOWN * DisplaysHigh)) {
	    return;
    }
	// inverse data bits for some panels
	bPixel = bPixel^inverse_ALL_flag;
	
    byte panel=(bX/DMD_PIXELS_ACROSS) + (DisplaysWide*(bY/DMD_PIXELS_DOWN));
    bX=(bX % DMD_PIXELS_ACROSS) + (panel<<5);
    bY=bY % DMD_PIXELS_DOWN;
    //set pointer to DMD RAM byte to be modified
    uiDMDRAMPointer = bX/8 + bY*(DisplaysTotal<<2);

    byte lookup = bPixelLookupTable[bX & 0x07];

    switch (bGraphicsMode) {
    case GRAPHICS_NORMAL:
	    if (bPixel == true)
		bDMDScreenRAM[uiDMDRAMPointer] &= ~lookup;	// zero bit is pixel on
	    else
		bDMDScreenRAM[uiDMDRAMPointer] |= lookup;	// one bit is pixel off
	    break;
    case GRAPHICS_INVERSE:
	    if (bPixel == false)
		    bDMDScreenRAM[uiDMDRAMPointer] &= ~lookup;	// zero bit is pixel on
	    else
		    bDMDScreenRAM[uiDMDRAMPointer] |= lookup;	// one bit is pixel off
	    break;
    case GRAPHICS_TOGGLE:
	    if (bPixel == true) {
		if ((bDMDScreenRAM[uiDMDRAMPointer] & lookup) == 0)
		    bDMDScreenRAM[uiDMDRAMPointer] |= lookup;	// one bit is pixel off
		else
		    bDMDScreenRAM[uiDMDRAMPointer] &= ~lookup;	// one bit is pixel off
	    }
	    break;
    case GRAPHICS_OR:
	    //only set pixels on
	    if (bPixel == true)
		    bDMDScreenRAM[uiDMDRAMPointer] &= ~lookup;	// zero bit is pixel on
	    break;
    case GRAPHICS_NOR:
	    //only clear on pixels
	    if ((bPixel == true) &&
		    ((bDMDScreenRAM[uiDMDRAMPointer] & lookup) == 0))
		    bDMDScreenRAM[uiDMDRAMPointer] |= lookup;	// one bit is pixel off
	    break;
    }

}
/*--------------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------------*/
void DMD::drawString(int bX, int bY, const char *bChars, byte length,
		     byte bGraphicsMode, byte orientation)
{
    if (bX >= (DMD_PIXELS_ACROSS*DisplaysWide) || bY >= DMD_PIXELS_DOWN * DisplaysHigh)
	return;
    uint8_t height = Font->get_height(); 
    if (bY+height<0) return;

    int strWidth = 0;
	this->drawLine(bX -1 , bY, bX -1 , bY + height, GRAPHICS_INVERSE);

    for (int i = 0; i < length; i++) {
        
		int charWide = this->drawChar(bX+strWidth, bY, bChars[i], bGraphicsMode, orientation); 
		
		//DEBUG
	    //Serial.println(bChars[i]);
		//Serial.println(bChars[i], HEX);
		//Serial.println(charWide);
		 
		//DEBUG
	    if (charWide > 0) {
	        strWidth += charWide ;
	        this->drawLine(bX + strWidth , bY, bX + strWidth , bY + height, GRAPHICS_INVERSE);
            strWidth++;
        } else if (charWide < 0) {
            return;
        }
        if ((bX + strWidth) >= DMD_PIXELS_ACROSS * DisplaysWide || bY >= DMD_PIXELS_DOWN * DisplaysHigh) return;
    }
}
/*--------------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------------*/
void DMD::drawMarquee(const char *bChars, byte length, int left, int top, byte orientation) 
{    
// temp parameter for beta version
	uint8_t matrix_h =16;
	
    marqueeWidth = 0;
    for (int i = 0; i < length; i++) {
	    marqueeText[i] = bChars[i];
		marqueeWidth += charWidth(bChars[i], orientation) + 1;
	 }
	
	
	if (orientation == 1) {
		  marqueeHeight = matrix_h;
		}
	else {
		  marqueeHeight= Font->get_height(); 
		}
    
    marqueeText[length] = '\0';
    marqueeOffsetY = top;
    marqueeOffsetX = left;
    marqueeLength = length;
    drawString(marqueeOffsetX, marqueeOffsetY, marqueeText, marqueeLength,
	   GRAPHICS_NORMAL, orientation);
}
 /*--------------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------------*/
boolean DMD::stepMarquee(int amountX, int amountY, byte orientation)
{
    boolean ret=false;
    marqueeOffsetX += amountX;
    marqueeOffsetY += amountY;
    if (marqueeOffsetX < -marqueeWidth) {
	    marqueeOffsetX = DMD_PIXELS_ACROSS * DisplaysWide;
	    clearScreen(true);
        ret=true;
    } else if (marqueeOffsetX > DMD_PIXELS_ACROSS * DisplaysWide) {
	    marqueeOffsetX = -marqueeWidth;
	    clearScreen(true);
        ret=true;
    }
    
        
    if (marqueeOffsetY < -marqueeHeight) {
	    marqueeOffsetY = DMD_PIXELS_DOWN * DisplaysHigh;
	    clearScreen(true);
        ret=true;
    } else if (marqueeOffsetY > DMD_PIXELS_DOWN * DisplaysHigh) {
	    marqueeOffsetY = -marqueeHeight;
	    clearScreen(true);
        ret=true;
    }

    // Special case horizontal scrolling to improve speed
    if (amountY==0 && amountX==-1) {
        // Shift entire screen one bit
        for (int i=0; i<DMD_RAM_SIZE_BYTES*DisplaysTotal;i++) {
            if ((i%(DisplaysWide*4)) == (DisplaysWide*4) -1) {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]<<1)+1;
            } else {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]<<1) + ((bDMDScreenRAM[i+1] & 0x80) >>7);
            }
        }

        // Redraw last char on screen
        int strWidth=marqueeOffsetX;
        for (byte i=0; i < marqueeLength; i++) {
            int wide = charWidth(marqueeText[i], orientation);
            if (strWidth+wide >= DisplaysWide*DMD_PIXELS_ACROSS) {
                drawChar(strWidth, marqueeOffsetY,marqueeText[i],GRAPHICS_NORMAL, orientation);
                return ret;
            }
            strWidth += wide+1;
        }
    } else if (amountY==0 && amountX==1) {
        // Shift entire screen one bit
        for (int i=(DMD_RAM_SIZE_BYTES*DisplaysTotal)-1; i>=0;i--) {
            if ((i%(DisplaysWide*4)) == 0) {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]>>1)+128;
            } else {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]>>1) + ((bDMDScreenRAM[i-1] & 1) <<7);
            }
        }

        // Redraw last char on screen
        int strWidth=marqueeOffsetX;
        for (byte i=0; i < marqueeLength; i++) {
            int wide = charWidth(marqueeText[i], orientation);
            if (strWidth+wide >= 0) {
                drawChar(strWidth, marqueeOffsetY,marqueeText[i],GRAPHICS_NORMAL, orientation);
                return ret;
            }
            strWidth += wide+1;
        }
    } else {
        drawString(marqueeOffsetX, marqueeOffsetY, marqueeText, marqueeLength,
	       GRAPHICS_NORMAL, orientation);
    }

    return ret;
}


/*--------------------------------------------------------------------------------------
 Clear the screen in DMD RAM
--------------------------------------------------------------------------------------*/
void DMD::clearScreen(byte bNormal)
{
    if (bNormal^inverse_ALL_flag) // clear all pixels
        memset(bDMDScreenRAM,0xFF,DMD_RAM_SIZE_BYTES*DisplaysTotal);
    else // set all pixels
        memset(bDMDScreenRAM,0x00,DMD_RAM_SIZE_BYTES*DisplaysTotal);
}

/*--------------------------------------------------------------------------------------
 Draw or clear a line from x1,y1 to x2,y2
--------------------------------------------------------------------------------------*/
void DMD::drawLine(int x1, int y1, int x2, int y2, byte bGraphicsMode)
{
    int dy = y2 - y1;
    int dx = x2 - x1;
    int stepx, stepy;

    if (dy < 0) {
	    dy = -dy;
	    stepy = -1;
    } else {
	    stepy = 1;
    }
    if (dx < 0) {
	    dx = -dx;
	    stepx = -1;
    } else {
	    stepx = 1;
    }
    dy <<= 1;			// dy is now 2*dy
    dx <<= 1;			// dx is now 2*dx

    writePixel(x1, y1, bGraphicsMode, true);
    if (dx > dy) {
	    int fraction = dy - (dx >> 1);	// same as 2*dy - dx
	    while (x1 != x2) {
	        if (fraction >= 0) {
		        y1 += stepy;
		        fraction -= dx;	// same as fraction -= 2*dx
	        }
	        x1 += stepx;
	        fraction += dy;	// same as fraction -= 2*dy
	        writePixel(x1, y1, bGraphicsMode, true);
	    }
    } else {
	    int fraction = dx - (dy >> 1);
	    while (y1 != y2) {
	        if (fraction >= 0) {
		        x1 += stepx;
		        fraction -= dy;
	        }
	        y1 += stepy;
	        fraction += dx;
	        writePixel(x1, y1, bGraphicsMode, true);
	    }
    }
}

/*--------------------------------------------------------------------------------------
 Draw or clear a circle of radius r at x,y centre
--------------------------------------------------------------------------------------*/
void DMD::drawCircle(int xCenter, int yCenter, int radius,
		     byte bGraphicsMode)
{
    int x = 0;
    int y = radius;
    int p = (5 - radius * 4) / 4;

    drawCircleSub(xCenter, yCenter, x, y, bGraphicsMode);
    while (x < y) {
	    x++;
	    if (p < 0) {
	        p += 2 * x + 1;
	    } else {
	        y--;
	        p += 2 * (x - y) + 1;
	    }
	    drawCircleSub(xCenter, yCenter, x, y, bGraphicsMode);
    }
}

void DMD::drawCircleSub(int cx, int cy, int x, int y, byte bGraphicsMode)
{

    if (x == 0) {
	    writePixel(cx, cy + y, bGraphicsMode, true);
	    writePixel(cx, cy - y, bGraphicsMode, true);
	    writePixel(cx + y, cy, bGraphicsMode, true);
	    writePixel(cx - y, cy, bGraphicsMode, true);
    } else if (x == y) {
	    writePixel(cx + x, cy + y, bGraphicsMode, true);
	    writePixel(cx - x, cy + y, bGraphicsMode, true);
	    writePixel(cx + x, cy - y, bGraphicsMode, true);
	    writePixel(cx - x, cy - y, bGraphicsMode, true);
    } else if (x < y) {
	    writePixel(cx + x, cy + y, bGraphicsMode, true);
	    writePixel(cx - x, cy + y, bGraphicsMode, true);
	    writePixel(cx + x, cy - y, bGraphicsMode, true);
	    writePixel(cx - x, cy - y, bGraphicsMode, true);
	    writePixel(cx + y, cy + x, bGraphicsMode, true);
	    writePixel(cx - y, cy + x, bGraphicsMode, true);
	    writePixel(cx + y, cy - x, bGraphicsMode, true);
	    writePixel(cx - y, cy - x, bGraphicsMode, true);
    }
}

/*--------------------------------------------------------------------------------------
 Draw or clear a box(rectangle) with a single pixel border
--------------------------------------------------------------------------------------*/
void DMD::drawBox(int x1, int y1, int x2, int y2, byte bGraphicsMode)
{
    drawLine(x1, y1, x2, y1, bGraphicsMode);
    drawLine(x2, y1, x2, y2, bGraphicsMode);
    drawLine(x2, y2, x1, y2, bGraphicsMode);
    drawLine(x1, y2, x1, y1, bGraphicsMode);
}

/*--------------------------------------------------------------------------------------
 Draw or clear a filled box(rectangle) with a single pixel border
--------------------------------------------------------------------------------------*/
void DMD::drawFilledBox(int x1, int y1, int x2, int y2,
			byte bGraphicsMode)
{
    for (int b = x1; b <= x2; b++) {
	    drawLine(b, y1, b, y2, bGraphicsMode);
    }
}

/*--------------------------------------------------------------------------------------
 Draw the selected test pattern
--------------------------------------------------------------------------------------*/
void DMD::drawTestPattern(byte bPattern)
{
    unsigned int ui;

    int numPixels=DisplaysTotal * DMD_PIXELS_ACROSS * DMD_PIXELS_DOWN;
    int pixelsWide=DMD_PIXELS_ACROSS*DisplaysWide;
    for (ui = 0; ui < numPixels; ui++) {
	    switch (bPattern) {
	    case PATTERN_ALT_0:	// every alternate pixel, first pixel on
		    if ((ui & pixelsWide) == 0)
		        //even row
		        writePixel((ui & (pixelsWide-1)), ((ui & ~(pixelsWide-1)) / pixelsWide), GRAPHICS_NORMAL, ui & 1);
		    else
		        //odd row
		        writePixel((ui & (pixelsWide-1)), ((ui & ~(pixelsWide-1)) / pixelsWide), GRAPHICS_NORMAL, !(ui & 1));
		    break;
	    case PATTERN_ALT_1:	// every alternate pixel, first pixel off
		    if ((ui & pixelsWide) == 0)
		        //even row
		        writePixel((ui & (pixelsWide-1)), ((ui & ~(pixelsWide-1)) / pixelsWide), GRAPHICS_NORMAL, !(ui & 1));
		    else
		        //odd row
		        writePixel((ui & (pixelsWide-1)), ((ui & ~(pixelsWide-1)) / pixelsWide), GRAPHICS_NORMAL, ui & 1);
		    break;
	    case PATTERN_STRIPE_0:	// vertical stripes, first stripe on
		    writePixel((ui & (pixelsWide-1)), ((ui & ~(pixelsWide-1)) / pixelsWide), GRAPHICS_NORMAL, ui & 1);
		    break;
	    case PATTERN_STRIPE_1:	// vertical stripes, first stripe off
		    writePixel((ui & (pixelsWide-1)), ((ui & ~(pixelsWide-1)) / pixelsWide), GRAPHICS_NORMAL, !(ui & 1));
		    break;
        }
    }
}

#if defined(__STM32F1__)

void DMD::latchDMA() {
	   //SPI_DMD.dmaTransferFinish();
	  while (spi_is_tx_empty(SPI_DMD.dev()) == 0); // "5. Wait until TXE=1 ..."
      while (spi_is_busy(SPI_DMD.dev()) != 0); // "... and then wait until BSY=0 before disabling the SPI." 
      spi_tx_dma_disable(SPI_DMD.dev());
     
      dma_disable(spiDmaDev, spiTxDmaChannel);
      dma_clear_isr_bits(spiDmaDev, spiTxDmaChannel);
      
	  pwmWrite(pin_DMD_nOE, 0);	//for stm32

		
        LATCH_DMD_SHIFT_REG_TO_OUTPUT();
        switch (bDMDByte) {
        case 0:			// row 1, 5, 9, 13 were clocked out
            LIGHT_DMD_ROW_01_05_09_13();
            bDMDByte=1;
            break;
        case 1:			// row 2, 6, 10, 14 were clocked out
            LIGHT_DMD_ROW_02_06_10_14();
            bDMDByte=2;
            break;
        case 2:			// row 3, 7, 11, 15 were clocked out
            LIGHT_DMD_ROW_03_07_11_15();
            bDMDByte=3;
            break;
        case 3:			// row 4, 8, 12, 16 were clocked out
            LIGHT_DMD_ROW_04_08_12_16();
            bDMDByte=0;
            break;
        }
       

   pwmWrite(pin_DMD_nOE, brightness);	//for stm32
   
   //digitalWrite(PA1, LOW);

}
	



void DMD::scanDisplayByDMA()
{
    //if PIN_OTHER_SPI_nCS is in use during a DMD scan request then scanDisplayBySPI() will exit without conflict! (and skip that scan)
    //if( digitalRead( PIN_OTHER_SPI_nCS ) == HIGH )
    //{
	    //digitalWrite(PA1, HIGH);
        //SPI transfer pixels to the display hardware shift registers
        int rowsize=DisplaysTotal<<2;
        int offset=rowsize * bDMDByte;
		//digitalWrite(SPI2_NSS_PIN, LOW); // manually take CSN low for SPI_1 transmission
        pwmWrite(pin_DMD_nOE, 0);

	    //uint8_t tt_buf[256];
        uint8_t* buf_ptr = dmd_dma_buf;
        
		/*memcpy(buf_ptr, bDMDScreenRAM +offset+row3, rowsize); buf_ptr += rowsize;
		memcpy(buf_ptr, bDMDScreenRAM +offset+row2, rowsize); buf_ptr += rowsize;
		memcpy(buf_ptr, bDMDScreenRAM +offset+row1, rowsize); buf_ptr += rowsize;
		memcpy(buf_ptr, bDMDScreenRAM +offset, rowsize); */
		 for (int i=0;i<rowsize;i++) {
        	//SPI_DMD.dmaSendAsync(bDMDScreenRAM, 16);
            *buf_ptr = (bDMDScreenRAM[offset+i+row3]);buf_ptr++;
            *buf_ptr = (bDMDScreenRAM[offset+i+row2]);buf_ptr++;
            *buf_ptr = (bDMDScreenRAM[offset+i+row1]);buf_ptr++;
            *buf_ptr = (bDMDScreenRAM[offset+i]);buf_ptr++;
        }
		
		//SPI_DMD.onReceive(tx_callback);
		//SPI_DMD.onTransmit(tx_callback);
		//SPI_DMD.dmaTransfer(dmd_dma_buf, tt_buf, rowsize*4);
		if (SPI_DMD.dev() == SPI1) {
			SPI_DMD.onTransmit(SPI1_DMA_callback);
	        dma_attach_interrupt(spiDmaDev, spiTxDmaChannel, SPI1_DMA_callback);
			}
        else if (SPI_DMD.dev() == SPI2) {
			SPI_DMD.onTransmit(SPI2_DMA_callback);
	        dma_attach_interrupt(spiDmaDev, spiTxDmaChannel, SPI2_DMA_callback);
			}
		//dma_attach_interrupt(DMA1, ccdma, tx_callback);
        //SPI_DMD.dmaTransferAsync(dmd_dma_buf,  rowsize*4, tx_callback);
        SPI_DMD.dmaSend(dmd_dma_buf,  rowsize*4, 1);
		//digitalWrite(PA1, LOW);
      
}


#endif	
/*--------------------------------------------------------------------------------------
 Scan the dot matrix LED panel display, from the RAM mirror out to the display hardware.
 Call 4 times to scan the whole display which is made up of 4 interleaved rows within the 16 total rows.
 Insert the calls to this function into the main loop for the highest call rate, or from a timer interrupt
--------------------------------------------------------------------------------------*/
//int i = 0;
void DMD::scanDisplayBySPI()
{
    //if PIN_OTHER_SPI_nCS is in use during a DMD scan request then scanDisplayBySPI() will exit without conflict! (and skip that scan)
    //if( digitalRead( PIN_OTHER_SPI_nCS ) == HIGH )
    //{
				
        //SPI transfer pixels to the display hardware shift registers
        int rowsize=DisplaysTotal<<2;
        int offset=rowsize * bDMDByte;
		//digitalWrite(SPI2_NSS_PIN, LOW); // manually take CSN low for SPI_1 transmission
        
#if defined(__STM32F1__)
	    pwmWrite(pin_DMD_nOE, 0);
        //SPI_DMD.setDataSize(DATA_SIZE_8BIT);
       // SPI_DMD.beginTransaction(SPISettings(DMD_SPI_CLOCK, MSBFIRST, SPI_MODE0));
        
#if defined(DMD_USE_DMA)
	    uint8_t* buf_ptr = dmd_dma_buf;
        for (int i=0;i<rowsize;i++) {
        	
            *buf_ptr = (bDMDScreenRAM[offset+i+row3]);buf_ptr++;
            *buf_ptr = (bDMDScreenRAM[offset+i+row2]);buf_ptr++;
            *buf_ptr = (bDMDScreenRAM[offset+i+row1]);buf_ptr++;
            *buf_ptr = (bDMDScreenRAM[offset+i]);buf_ptr++;
        }
	
	    SPI_DMD.dmaTransfer(dmd_dma_buf, rx_dma_buf, rowsize*4);
#else   //  not DMA

        for (int i=0;i<rowsize;i++) {
            SPI_DMD.write(bDMDScreenRAM[offset+i+row3]);
            SPI_DMD.write(bDMDScreenRAM[offset+i+row2]);
            SPI_DMD.write(bDMDScreenRAM[offset+i+row1]);
            SPI_DMD.write(bDMDScreenRAM[offset+i]);
        }
#endif  //   DMA
	
        //SPI_DMD.endTransaction(); 
       //pwmWrite(pin_DMD_nOE, 0);	//for stm32
#elif defined(__AVR_ATmega2560__) || (__AVR_ATmega328P__)
       for (int i=0;i<rowsize;i++) {
            SPI.transfer(bDMDScreenRAM[offset+i+row3]);
            SPI.transfer(bDMDScreenRAM[offset+i+row2]);
            SPI.transfer(bDMDScreenRAM[offset+i+row1]);
            SPI.transfer(bDMDScreenRAM[offset+i]);
        }
		OE_DMD_ROWS_OFF();
#endif
       //digitalWrite(SPI2_NSS_PIN, HIGH); // manually take CSN high between spi transmissions
        //
		
        LATCH_DMD_SHIFT_REG_TO_OUTPUT();
        switch (bDMDByte) {
        case 0:			// row 1, 5, 9, 13 were clocked out
            LIGHT_DMD_ROW_01_05_09_13();
            bDMDByte=1;
            break;
        case 1:			// row 2, 6, 10, 14 were clocked out
            LIGHT_DMD_ROW_02_06_10_14();
            bDMDByte=2;
            break;
        case 2:			// row 3, 7, 11, 15 were clocked out
            LIGHT_DMD_ROW_03_07_11_15();
            bDMDByte=3;
            break;
        case 3:			// row 4, 8, 12, 16 were clocked out
            LIGHT_DMD_ROW_04_08_12_16();
            bDMDByte=0;
            break;
        }
        //OE_DMD_ROWS_ON();
		
		// Output enable pin is either fixed on, or PWMed for a variable brightness display
		 //if(brightness == 255)
			// {OE_DMD_ROWS_ON();}
		// else
			//analogWrite(PIN_DMD_nOE, brightness);	//for atmega
#if defined(__STM32F1__)
   pwmWrite(pin_DMD_nOE, brightness);	//for stm32
#elif defined(__AVR_ATmega2560__) || (__AVR_ATmega328P__)
   if(brightness == 255)
    digitalWrite(pin_DMD_nOE, HIGH);
  else
    analogWrite(pin_DMD_nOE, brightness);
#endif	

		
    //}
}
/*--------------------------------------------------------------------------------------
       Select current font
--------------------------------------------------------------------------------------*/
void DMD::selectFont(DMD_Font * font)
{
    this->Font = font;
	
}
/*--------------------------------------------------------------------------------------
  draw char with selected font at coordinates bX bY
--------------------------------------------------------------------------------------*/
int DMD::drawChar(const int bX, const int bY, const unsigned char letter, byte bGraphicsMode, byte orientation)
{
	
	if (orientation) {
		return drawCharV(bX, bY, letter, bGraphicsMode);
	}
	if (bX > (DMD_PIXELS_ACROSS*DisplaysWide) || bY > (DMD_PIXELS_DOWN*DisplaysHigh)) return -1;
	
	
	unsigned char c = letter;
	if (! Font->is_char_in(c)) return 0;

	uint8_t height = Font->get_height();

	if (c == ' ') { //CHANGED FROM ' '
		int charWide = Font->get_char_width(' ');
		this->drawFilledBox(bX, bY, bX + charWide , bY + height, GRAPHICS_INVERSE);
        return charWide;
	}
    

	
	if (Font->is_gfx_font()) {

		

		
		DMD_GFX_Font* ff = (DMD_GFX_Font *)Font;
		GFXfont * gfxFont_p = ff->get_font_by_char(c);
		c -= ff->get_first_by_char(c);
		//Serial.print("Char index ");Serial.println(c, HEX);
		
		GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont_p->glyph))[c]);
		uint8_t  *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont_p->bitmap);

		uint16_t bo = pgm_read_word(&glyph->bitmapOffset);
		//Serial.print("Bitmap offset ");Serial.println(bo);
		uint8_t  w = pgm_read_byte(&glyph->width),
			h = pgm_read_byte(&glyph->height);
		int8_t   xo = pgm_read_byte(&glyph->xOffset),
			yo = height + pgm_read_byte(&glyph->yOffset);
		uint8_t  ww = pgm_read_byte(&glyph->xAdvance);
		uint8_t  xx, yy, bits = 0, bit = 0;
		int16_t  xo16 = 0, yo16 = 0;

		this->drawFilledBox(bX, bY, bX + ww, bY + height, GRAPHICS_INVERSE);


		for (yy = 0; yy<h; yy++) {
			for (xx = 0; xx<w; xx++) {
				if (!(bit++ & 7)) {
					bits = pgm_read_byte(&bitmap[bo++]);
				}
				if (bits & 0x80) {
					writePixel(bX + xo + xx, bY + yo + yy, bGraphicsMode, true);
					//writePixel(x+xo+xx, y+yo+yy, color);
				}
				else {
					writePixel(bX + xo + xx, bY + yo + yy, bGraphicsMode, false);
					//writeFillRect(x+(xo16+xx)*size, y+(yo16+yy)*size,size, size, color);
				}
				bits <<= 1;
			}
		}
		return ww;
	}
	else {
		
		
		DMD_Standard_Font* ff = (DMD_Standard_Font *) Font;
		uint8_t width = ff->get_char_width(c);
		uint8_t bytes = (height + 7) / 8;
		uint16_t index = ff->get_bitmap_index(c);
		c -= ff->get_first();
		
		if (bX < -width || bY < -height) return width;

		

		// last but not least, draw the character
		for (uint8_t j = 0; j < width; j++) { // Width
			for (uint8_t i = bytes - 1; i < 254; i--) { // Vertical Bytes
				uint8_t data = pgm_read_byte(ff->font_ptr + index + j + (i * width));
				int offset = (i * 8);
				if ((i == bytes - 1) && bytes > 1) {
					offset = height - 8;
				}
				for (uint8_t k = 0; k < 8; k++) { // Vertical bits
					if ((offset + k >= i * 8) && (offset + k <= height)) {
						if (data & (1 << k)) {
							writePixel(bX + j, bY + offset + k, bGraphicsMode, true);
						}
						else {
							writePixel(bX + j, bY + offset + k, bGraphicsMode, false);
						}
					}
				}
			}
		}
		return width;
	}
}
 
/*--------------------------------------------------------------------------------------
  draw char vertically with selected font at coordinates bX bY
--------------------------------------------------------------------------------------*/
int DMD::drawCharV(const int bX, const int bY, const unsigned char letter, byte bGraphicsMode)
{
	// temp parameter for beta version
	uint8_t matrix_h =16;
	
	
	if (bX > (DMD_PIXELS_ACROSS*DisplaysWide) || bY > (DMD_PIXELS_DOWN*DisplaysHigh)) return -1;
	
	
	unsigned char c = letter;
	if (! Font->is_char_in(c)) return 0;

	uint8_t height = Font->get_height();

	if (c == ' ') { //CHANGED FROM ' '
		int charWide = Font->get_char_width(' ');
		this->drawFilledBox(bX, bY, bX + height , bY + matrix_h, GRAPHICS_INVERSE);
        return height;
	}
    

	
	if (Font->is_gfx_font()) {

		

		
		DMD_GFX_Font* ff = (DMD_GFX_Font *)Font;
		GFXfont * gfxFont_p = ff->get_font_by_char(c);
		c -= ff->get_first_by_char(c);
		//Serial.print("Char index ");Serial.println(c, HEX);
		
		GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont_p->glyph))[c]);
		uint8_t  *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont_p->bitmap);

		uint16_t bo = pgm_read_word(&glyph->bitmapOffset);
		//Serial.print("Bitmap offset ");Serial.println(bo);
		uint8_t  w = pgm_read_byte(&glyph->width),
			h = pgm_read_byte(&glyph->height);
		int8_t   yo = w + (matrix_h -w)/2,
			xo = height + pgm_read_byte(&glyph->yOffset);
		uint8_t  hh = xo + h;
		uint8_t  xx, yy, bits = 0, bit = 0;
		int16_t  xo16 = 0, yo16 = 0;

		this->drawFilledBox(bX, bY, bX + hh, bY + matrix_h, GRAPHICS_INVERSE);


		for (xx = 0; xx<h; xx++) {
			for (yy = 0; yy<w; yy++) {
				if (!(bit++ & 7)) {
					bits = pgm_read_byte(&bitmap[bo++]);
				}
				if (bits & 0x80) {
					writePixel(bX + xo + xx, bY + yo - yy, bGraphicsMode, true);
					//writePixel(x+xo+xx, y+yo+yy, color);
				}
				else {
					writePixel(bX + xo + xx, bY + yo - yy, bGraphicsMode, false);
					//writeFillRect(x+(xo16+xx)*size, y+(yo16+yy)*size,size, size, color);
				}
				bits <<= 1;
			}
		}
		return hh;
	}
	else {
	/*	
		
		DMD_Standard_Font* ff = (DMD_Standard_Font *) Font;
		uint8_t width = ff->get_char_width(c);
		uint8_t bytes = (height + 7) / 8;
		uint16_t index = ff->get_bitmap_index(c);
		c -= ff->get_first();
		
		if (bX < -width || bY < -height) return width;

		

		// last but not least, draw the character
		for (uint8_t j = 0; j < width; j++) { // Width
			for (uint8_t i = bytes - 1; i < 254; i--) { // Vertical Bytes
				uint8_t data = pgm_read_byte(ff->font_ptr + index + j + (i * width));
				int offset = (i * 8);
				if ((i == bytes - 1) && bytes > 1) {
					offset = height - 8;
				}
				for (uint8_t k = 0; k < 8; k++) { // Vertical bits
					if ((offset + k >= i * 8) && (offset + k <= height)) {
						if (data & (1 << k)) {
							writePixel(bX + j, bY + offset + k, bGraphicsMode, true);
						}
						else {
							writePixel(bX + j, bY + offset + k, bGraphicsMode, false);
						}
					}
				}
			}
		}
		return width; */
	} 
} 
 
/*--------------------------------------------------------------------------------------
  char width in pixels with selected font
     routine moved to DMD_Font classes
--------------------------------------------------------------------------------------*/

int DMD::charWidth(const unsigned char letter, byte orientation)
{
  return (uint8_t)Font->get_char_width(letter, orientation);
}

/// Next part is customly added by mozokevgen
///

/*--------------------------------------------------------------------------------------
 Draw the image
--------------------------------------------------------------------------------------*/
void
	DMD::drawImg(const int bX, const int bY, const uint8_t *img, byte length)
{
	if (bX > DMD_PIXELS_ACROSS*DisplaysWide)
	{
		return;
	}
	// if(flagFirst)
	// {
		// marqueeOffsetY = bY;
		// marqueeOffsetX = bX;
		// marqueeWidth = length;
		// marqueeHeight = 16;
		
		// // for (int i = 0; i < length*2; i++)
		// // {
			// // marqueeImg[i] = img[i];
		// // }
		
		// marqueeLength = length;
	// }

    uint8_t bytes = (DMD_PIXELS_DOWN + 7) / 8;
	
    // draw Image (copy from dmd.drawChar)
    for (uint8_t j = 0; j < length; j++) { // Width DMD_PIXELS_ACROSS
	    for (uint8_t i = bytes - 1; i < 254; i--) { // Vertical Bytes
	        uint8_t data = img[j + (i * length)];
		    int offset = (i * 8);
		    if ((i == bytes - 1) && bytes > 1) {
		        offset = DMD_PIXELS_DOWN - 8;
            }
	        for (uint8_t k = 0; k < 8; k++) { // Vertical bits
		        if ((offset+k >= i*8) && (offset+k <= DMD_PIXELS_DOWN)) {
					if(bX + j < DMD_PIXELS_ACROSS*DisplaysWide){
		            if (data & (1 << k)) {
			            writePixel(bX + j, bY + offset + k, GRAPHICS_NORMAL, true);
		            } else {
			            writePixel(bX + j, bY + offset + k, GRAPHICS_NORMAL, false);
		            }}
		        }
	        }
	    }
    }
}
/*--------------------------------------------------------------------------------------
 Scroll the image
--------------------------------------------------------------------------------------*/
boolean DMD::stepImg(int amountX, int amountY)
{
    boolean ret=false;
    // marqueeOffsetX += amountX;
    // marqueeOffsetY += amountY;
    // if (marqueeOffsetX < -marqueeWidth) {
	    // marqueeOffsetX = DMD_PIXELS_ACROSS * DisplaysWide;
	    // clearScreen(true);
        // ret=true;
    // } else if (marqueeOffsetX > DMD_PIXELS_ACROSS * DisplaysWide) {
	    // marqueeOffsetX = -marqueeWidth;
	    // clearScreen(true);
        // ret=true;
    // }
    
        
    // if (marqueeOffsetY < -marqueeHeight) {
	    // marqueeOffsetY = DMD_PIXELS_DOWN * DisplaysHigh;
	    // clearScreen(true);
        // ret=true;
    // } else if (marqueeOffsetY > DMD_PIXELS_DOWN * DisplaysHigh) {
	    // marqueeOffsetY = -marqueeHeight;
	    // clearScreen(true);
        // ret=true;
    // }

    // Special case horizontal scrolling to improve speed
    if (amountY==0 && amountX==-1) {
        // Shift entire screen one bit
		//byte buf[] = {bDMDScreenRAM[0]};
        for (int i=0; i<DMD_RAM_SIZE_BYTES*DisplaysTotal;i++) {
            if ((i%(DisplaysWide*4)) == (DisplaysWide*4) -1) {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]<<1)+1;
            } else {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]<<1) + ((bDMDScreenRAM[i+1] & 0x80) >>7);
            }
        }

        // Redraw last char on screen
        // int imgWidth=marqueeOffsetX;
        // for (byte i=0; i < marqueeLength; i++) {
            // int wide = 1;
            // if (imgWidth+wide >= DisplaysWide*DMD_PIXELS_ACROSS) {
				// uint8_t buf[] = {marqueeImg[i], marqueeImg[i+32]};
                // drawImg(imgWidth, marqueeOffsetY,buf, false);
                // return ret;
            // }
            // imgWidth += wide+1;
        // }
    } else if (amountY==0 && amountX==1) {
        // Shift entire screen one bit
        for (int i=(DMD_RAM_SIZE_BYTES*DisplaysTotal)-1; i>=0;i--) {
            if ((i%(DisplaysWide*4)) == 0) {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]>>1)+128;
            } else {
                bDMDScreenRAM[i]=(bDMDScreenRAM[i]>>1) + ((bDMDScreenRAM[i-1] & 1) <<7);
            }
        }

        // Redraw last line on screen
		// if (marqueeOffsetX + marqueeLength > DisplaysWide*DMD_PIXELS_ACROSS)
		// {
			// int overflow = DisplaysWide*DMD_PIXELS_ACROSS - marqueeOffsetX + marqueeLength;
			
		// }
        // int imgWidth=marqueeOffsetX;
        // for (byte i=0; i < marqueeLength; i++) {
            // int wide = 1;
            // if (imgWidth+wide >= 0) {
				// uint8_t buf[] = {marqueeImg[i], marqueeImg[i+32]};
                // drawImg(imgWidth, marqueeOffsetY, buf, false);
                // return ret;
            // }
            // imgWidth += wide+1;
        // }
    } else {
        // drawImg(marqueeOffsetX, marqueeOffsetY, marqueeImg,
	        // false);
    }

    return ret;
}

/*--------------------------------------------------------------------------------------
 Animation
--------------------------------------------------------------------------------------*/
void
	DMD::animation(const int bX, const int bY, const uint8_t *img1, byte length1, const uint8_t *img2, byte length2, bool beginFlag)
{
	static uint8_t blank[] = {0x00, 0x00};
	if(beginFlag)
	{
		marqueeOffsetY = bY;
		marqueeOffsetX = bX;
	}
	
	if (spriteFlag)
	{
		drawImg(marqueeOffsetX-1, marqueeOffsetY, blank, 1);
		drawImg(marqueeOffsetX, marqueeOffsetY, img1, length1);
		
		marqueeOffsetX += 1;
		spriteFlag = !spriteFlag;
	}
	else 
	{
		drawImg(marqueeOffsetX-1, marqueeOffsetY, blank, 1);
		drawImg(marqueeOffsetX, marqueeOffsetY, img2, length2);
		marqueeOffsetX += 1;
		spriteFlag = !spriteFlag;
	}
	
}
/*--------------------------------------------------------------------------------------
   string width in pixels
--------------------------------------------------------------------------------------*/

uint16_t DMD::stringWidth(const char *bChars, uint8_t length)
{
	// this->Font
	uint16_t width = 0;
	// char c;
	int idx;
	
	for(idx = 0; idx < length; idx++) {
    int cwidth = charWidth(bChars[idx]);
    if(cwidth > 0)
      width += cwidth + 1;
	}
	if(width) {
		width--;
	}
	return width;
}

 

 

aidar_i
Offline
Зарегистрирован: 07.10.2018

Испытываю в железе. Ардуино МЕГА и STM32.

Обнаружил , что шрифт UkrRusSystemFont5x7 и UkrRusArial14 неправильно декодируется. Пример с шрифтом GlametrixBold правильно. Правильно начинает работать только если убрать выделенную часть кода или обнулить их (лучше нужно в библиотеке переставить шрифты).

int utf8_rus(char* dest, const unsigned char* src) {
  
  uint8_t i, j;
  for ( i =0, j =0; src[i]; i++) {
   if ((src[i] == 0xD0 )&& src[i+1])  { dest[j++] = src[++i] - 0x00;}
    else if ((src[i] == 0xD1 )&& src[i+1]) {dest[j++] = src[++i] + 0x00;  }
    else dest[j++] = src[i];
  }
  dest[j] ='\0';
  return j;
}
Один шрифт UkrRusSystemFont5x7 подправил, второй не смог (переставил, но ширину букв правильно не смог поставить).
/*
 *
 * System5x7
 *
 *
 * File Name           : System5x7.h
 * Date                : 28 Oct 2008
 * Font size in bytes  : 470
 * Font width          : 5
 * Font height         : 7
 * Font first char     : 32
 * Font last char      : 127
 * Font used chars     : 94
 *
 * The font data are defined as
 *
 * struct _FONT_ {
 *     uint16_t   font_Size_in_Bytes_over_all_included_Size_it_self;
 *     uint8_t    font_Width_in_Pixel_for_fixed_drawing;
 *     uint8_t    font_Height_in_Pixel_for_all_characters;
 *     unit8_t    font_First_Char;
 *     uint8_t    font_Char_Count;
 *
 *     uint8_t    font_Char_Widths[font_Last_Char - font_First_Char +1];
 *                  // for each character the separate width in pixels,
 *                  // characters < 128 have an implicit virtual right empty row
 *
 *     uint8_t    font_data[];
 *                  // bit field of all characters
 */

#include <inttypes.h>
#include <avr/pgmspace.h>

#ifndef SYSTEM5x7_H
#define SYSTEM5x7_H

#define SYSTEM5x7_WIDTH 5
#define SYSTEM5x7_HEIGHT 7

/*
 * added to allow fontname to match header file name. 
 * as well as keep the old name for backward compability
 */

#define UkrRusSystemFont5x7 System5x7

const static uint8_t System5x7[] PROGMEM = {
    0x0, 0x0, // size of zero indicates fixed width font, actual length is width * height
    0x05, // width
    0x07, // height
    0x20, // first char
    0xA6, // char count
    
    // Fixed width; char width table not used !!!!
    
    // font data
    0x00, 0x00, 0x00, 0x00, 0x00,// (space)
	0x00, 0x00, 0x5F, 0x00, 0x00,// !
	0x00, 0x07, 0x00, 0x07, 0x00,// "
	0x14, 0x7F, 0x14, 0x7F, 0x14,// #
	0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
	0x23, 0x13, 0x08, 0x64, 0x62,// %
	0x36, 0x49, 0x55, 0x22, 0x50,// &
	0x00, 0x05, 0x03, 0x00, 0x00,// '
	0x00, 0x1C, 0x22, 0x41, 0x00,// (
	0x00, 0x41, 0x22, 0x1C, 0x00,// )
	0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
	0x08, 0x08, 0x3E, 0x08, 0x08,// +
	0x00, 0x50, 0x30, 0x00, 0x00,// ,
	0x08, 0x08, 0x08, 0x08, 0x08,// -
	0x00, 0x60, 0x60, 0x00, 0x00,// .
	0x20, 0x10, 0x08, 0x04, 0x02,// /
	0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
	0x00, 0x42, 0x7F, 0x40, 0x00,// 1
	0x42, 0x61, 0x51, 0x49, 0x46,// 2
	0x21, 0x41, 0x45, 0x4B, 0x31,// 3
	0x18, 0x14, 0x12, 0x7F, 0x10,// 4
	0x27, 0x45, 0x45, 0x45, 0x39,// 5
	0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
	0x01, 0x71, 0x09, 0x05, 0x03,// 7
	0x36, 0x49, 0x49, 0x49, 0x36,// 8
	0x06, 0x49, 0x49, 0x29, 0x1E,// 9
	0x00, 0x36, 0x36, 0x00, 0x00,// :
	0x00, 0x56, 0x36, 0x00, 0x00,// ;
	0x00, 0x08, 0x14, 0x22, 0x41,// <
	0x14, 0x14, 0x14, 0x14, 0x14,// =
	0x41, 0x22, 0x14, 0x08, 0x00,// >
	0x02, 0x01, 0x51, 0x09, 0x06,// ?
	0x32, 0x49, 0x79, 0x41, 0x3E,// @
	0x7E, 0x11, 0x11, 0x11, 0x7E,// A
	0x7F, 0x49, 0x49, 0x49, 0x36,// B
	0x3E, 0x41, 0x41, 0x41, 0x22,// C
	0x7F, 0x41, 0x41, 0x22, 0x1C,// D
	0x7F, 0x49, 0x49, 0x49, 0x41,// E
	0x7F, 0x09, 0x09, 0x01, 0x01,// F
	0x3E, 0x41, 0x41, 0x51, 0x32,// G
	0x7F, 0x08, 0x08, 0x08, 0x7F,// H
	0x00, 0x41, 0x7F, 0x41, 0x00,// I
	0x20, 0x40, 0x41, 0x3F, 0x01,// J
	0x7F, 0x08, 0x14, 0x22, 0x41,// K
	0x7F, 0x40, 0x40, 0x40, 0x40,// L
	0x7F, 0x02, 0x04, 0x02, 0x7F,// M
	0x7F, 0x04, 0x08, 0x10, 0x7F,// N
	0x3E, 0x41, 0x41, 0x41, 0x3E,// O
	0x7F, 0x09, 0x09, 0x09, 0x06,// P
	0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
	0x7F, 0x09, 0x19, 0x29, 0x46,// R
	0x46, 0x49, 0x49, 0x49, 0x31,// S
	0x01, 0x01, 0x7F, 0x01, 0x01,// T
	0x3F, 0x40, 0x40, 0x40, 0x3F,// U
	0x1F, 0x20, 0x40, 0x20, 0x1F,// V
	0x7F, 0x20, 0x18, 0x20, 0x7F,// W
	0x63, 0x14, 0x08, 0x14, 0x63,// X
	0x03, 0x04, 0x78, 0x04, 0x03,// Y
	0x61, 0x51, 0x49, 0x45, 0x43,// Z
	0x00, 0x00, 0x7F, 0x41, 0x41,// [
	0x02, 0x04, 0x08, 0x10, 0x20,// "\"
	0x41, 0x41, 0x7F, 0x00, 0x00,// ]
	0x04, 0x02, 0x01, 0x02, 0x04,// ^
	0x40, 0x40, 0x40, 0x40, 0x40,// _
	0x00, 0x01, 0x02, 0x04, 0x00,// `
	0x20, 0x54, 0x54, 0x54, 0x78,// a
	0x7F, 0x48, 0x44, 0x44, 0x38,// b
	0x38, 0x44, 0x44, 0x44, 0x20,// c
	0x38, 0x44, 0x44, 0x48, 0x7F,// d
	0x38, 0x54, 0x54, 0x54, 0x18,// e
	0x08, 0x7E, 0x09, 0x01, 0x02,// f
	0x08, 0x14, 0x54, 0x54, 0x3C,// g
	0x7F, 0x08, 0x04, 0x04, 0x78,// h
	0x00, 0x44, 0x7D, 0x40, 0x00,// i
	0x20, 0x40, 0x44, 0x3D, 0x00,// j
	0x00, 0x7F, 0x10, 0x28, 0x44,// k
	0x00, 0x41, 0x7F, 0x40, 0x00,// l
	0x7C, 0x04, 0x18, 0x04, 0x78,// m
	0x7C, 0x08, 0x04, 0x04, 0x78,// n
	0x38, 0x44, 0x44, 0x44, 0x38,// o
	0x7C, 0x14, 0x14, 0x14, 0x08,// p
	0x08, 0x14, 0x14, 0x18, 0x7C,// q
	0x7C, 0x08, 0x04, 0x04, 0x08,// r
	0x48, 0x54, 0x54, 0x54, 0x20,// s
	0x04, 0x3F, 0x44, 0x40, 0x20,// t
	0x3C, 0x40, 0x40, 0x20, 0x7C,// u
	0x1C, 0x20, 0x40, 0x20, 0x1C,// v
	0x3C, 0x40, 0x30, 0x40, 0x3C,// w
	0x44, 0x28, 0x10, 0x28, 0x44,// x
	0x0C, 0x50, 0x50, 0x50, 0x3C,// y
	0x44, 0x64, 0x54, 0x4C, 0x44,// z
	0x00, 0x08, 0x36, 0x41, 0x00,// {
	0x00, 0x00, 0x7F, 0x00, 0x00,// |
	0x00, 0x41, 0x36, 0x08, 0x00,// }
	0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
	0x08, 0x1C, 0x2A, 0x08, 0x08, // <-
	// Вставлено

      	0x7e, 0x11, 0x11, 0x11, 0x7e,//A	0x90
      	0x7f, 0x49, 0x49, 0x49, 0x33,//Б	0x91
      	0x7f, 0x49, 0x49, 0x49, 0x36,//В	0x92
      	0x7f, 0x01, 0x01, 0x01, 0x03,//Г	0x93
      	0xe0, 0x51, 0x4f, 0x41, 0xff,//Д	0x94
      	0x7f, 0x49, 0x49, 0x49, 0x41,//E	0x95
      	0x77, 0x08, 0x7f, 0x08, 0x77,//Ж	0x96
      	0x41, 0x49, 0x49, 0x49, 0x36,//З	0x97
      	0x7f, 0x10, 0x08, 0x04, 0x7f,//И	0x98
      	0x7c, 0x21, 0x12, 0x09, 0x7c,//Й	0x99
      	0x7f, 0x08, 0x14, 0x22, 0x41,//K	0x9A
      	0x20, 0x41, 0x3f, 0x01, 0x7f,//Л	0x9B
      	0x7f, 0x02, 0x0c, 0x02, 0x7f,//M	0x9C
      	0x7f, 0x08, 0x08, 0x08, 0x7f,//H	0x9D
      	0x3e, 0x41, 0x41, 0x41, 0x3e,//O	0x9E
      	0x7f, 0x01, 0x01, 0x01, 0x7f,//П	0x9F
      // -------------------------------------------------			
      	0x7f, 0x09, 0x09, 0x09, 0x06,//P	0xA0
      	0x3e, 0x41, 0x41, 0x41, 0x22,//C	0xA1
      	0x01, 0x01, 0x7f, 0x01, 0x01,//T	0xA2
      	0x47, 0x28, 0x10, 0x08, 0x07,//У	0xA3
      	0x1c, 0x22, 0x7f, 0x22, 0x1c,//Ф	0xA4
      	0x63, 0x14, 0x08, 0x14, 0x63,//X	0xA5
      	0x7f, 0x40, 0x40, 0x40, 0xff,//Ц	0xA6
      	0x07, 0x08, 0x08, 0x08, 0x7f,//Ч	0xA7
      	0x7f, 0x40, 0x7f, 0x40, 0x7f,//Ш	0xA8
      	0x7f, 0x40, 0x7f, 0x40, 0xff,//Щ	0xA9
      	0x01, 0x7f, 0x48, 0x48, 0x30,//Ъ	0xAA
      	0x7f, 0x48, 0x30, 0x00, 0x7f,//Ы	0xAB
      	0x00, 0x7f, 0x48, 0x48, 0x30,//Ь	0xAC
      	0x22, 0x41, 0x49, 0x49, 0x3e,//Э	0xAD
      	0x7f, 0x08, 0x3e, 0x41, 0x3e,//Ю	0xAE
      	0x46, 0x29, 0x19, 0x09, 0x7f,//Я	0xAF		
      // -------------------------------------------------
	         	0x20, 0x54, 0x54, 0x54, 0x78,//a	0xB0
      	0x3c, 0x4a, 0x4a, 0x49, 0x31,//б	0xB1
      	0x7c, 0x54, 0x54, 0x28, 0x00,//в	0xB2
      	0x7c, 0x04, 0x04, 0x04, 0x0c,//г	0xB3
      	0xe0, 0x54, 0x4c, 0x44, 0xfc,//д	0xB4
      	0x38, 0x54, 0x54, 0x54, 0x18,//e	0xB5
      	0x6c, 0x10, 0x7c, 0x10, 0x6c,//ж	0xB6
      	0x44, 0x44, 0x54, 0x54, 0x28,//з	0xB7
      	0x7c, 0x20, 0x10, 0x08, 0x7c,//и	0xB8
      	0x7c, 0x41, 0x22, 0x11, 0x7c,//й	0xB9
      	0x7c, 0x10, 0x28, 0x44, 0x00,//к	0xBA
      	0x20, 0x44, 0x3c, 0x04, 0x7c,//л	0xBB
      	0x7c, 0x08, 0x10, 0x08, 0x7c,//м	0xBC
      	0x7c, 0x10, 0x10, 0x10, 0x7c,//н	0xBD
      	0x38, 0x44, 0x44, 0x44, 0x38,//o	0xBE
      	0x7c, 0x04, 0x04, 0x04, 0x7c, //п	0xBF
		
      	0x7C, 0x14, 0x14, 0x14, 0x08,//p 	0x80
      	0x38, 0x44, 0x44, 0x44, 0x20,//c	0x81
      	0x04, 0x04, 0x7c, 0x04, 0x04,//т	0x82
      	0x0C, 0x50, 0x50, 0x50, 0x3C,//у	0x83
      	0x30, 0x48, 0xfc, 0x48, 0x30,//ф	0x84
      	0x44, 0x28, 0x10, 0x28, 0x44,//x	0x85
      	0x7c, 0x40, 0x40, 0x40, 0xfc,//ц	0x86
      	0x0c, 0x10, 0x10, 0x10, 0x7c,//ч	0x87
      	0x7c, 0x40, 0x7c, 0x40, 0x7c,//ш	0x88
      	0x7c, 0x40, 0x7c, 0x40, 0xfc,//щ	0x89
      	0x04, 0x7c, 0x50, 0x50, 0x20,//ъ	0x8A
      	0x7c, 0x50, 0x50, 0x20, 0x7c,//ы	0x8B
      	0x7c, 0x50, 0x50, 0x20, 0x00,//ь	0x8C
      	0x28, 0x44, 0x54, 0x54, 0x38,//э	0x8D
      	0x7c, 0x10, 0x38, 0x44, 0x38,//ю	0x8E
      	0x08, 0x54, 0x34, 0x14, 0x7c,//я	0x8F
      // -------------------------------------------------			
    	0x3E, 0x49, 0x49, 0x41, 0x22, //Є    0xC0
    	0x00, 0x41, 0x7F, 0x41, 0x00, //I    0xC1
    	0x00, 0x41, 0x7E, 0x41, 0x00, //Ї    0xC2
    	0x38, 0x54, 0x54, 0x44, 0x28, //є    0xC3
    	0x00, 0x48, 0x7A, 0x40, 0b0,  //і    0xC4
    	0x00, 0x4A, 0x78, 0x42, 0x00 //ї    0xC5	
    
};

#endif

Бегущая строка очень неприятно двигается, потому что вертикальная часть букв идет волной. Убрать бы эту волну.

Уменьшил частоту до 1000 волны почти нет, но внутри букв есть мерцания при перемещении, закомментировал прокрутку , мерцают. Поэтому и при движении мерцают. Увеличение частоты мерцания убирают, но увеличивается эффект волны.

 

b707
Offline
Зарегистрирован: 26.05.2017

aidar_i пишет:

Ардуино МЕГА и STM32.

это как?

Цитата:
Обнаружил , что шрифт UkrRusSystemFont5x7 и UkrRusArial14 неправильно декодируется. Пример с шрифтом GlametrixBold правильно.

фонты UkrRusSystemFont5x7 и UkrRusArial14 остались от старой библиотеки, у них другая кодировка, чем у фонтов TTF. Соответнно процедура utf8_rus для них не подходит. "Старые" фонты вообще должны показываться вообще без перекодирования, но для этого в скетче ардуино должна быть задана правильная кодировка. Посмотрите на предыдущей странице ветки - этот вопрос уже обсуждался.

Цитата:
Уменьшил частоту до 1000 волны почти нет, но внутри букв есть мерцания при перемещении, закомментировал прокрутку , мерцают. Поэтому и при движении мерцают. Увеличение частоты мерцания убирают, но увеличивается эффект волны.

это на какой плате? - если можно, выложите пример кода, где видно мерцание.  А "волна" - это слишком редкое обновление картинки.

aidar_i
Offline
Зарегистрирован: 07.10.2018

b707 пишет:

aidar_i пишет:

Ардуино МЕГА и STM32.

это как?

Отдельно на МЕГА2560 и отдельно на АТМЕГА328.

 

FoxJone
Offline
Зарегистрирован: 19.04.2019

aidar_i пишет:

b707 пишет:

aidar_i пишет:

Ардуино МЕГА и STM32.

это как?

Отдельно на МЕГА2560 и отдельно на АТМЕГА328.

Осталось понять, где в этой схеме STM32...

ЗЫ. Коллега Дмитрий, я тут себе сегодня остатки мозга поломал с вашим ДМД, отписал проблему на мыло, не глянете?

Искренне надеюсь, что проблема у меня в программировании... Потому что, если вы скажете, что все правильно, значит завтра я возьму тестер и начну проверять дорожки на плате - и не удивлюсь, если они вдруг ведут не к тем пинам, к каким заявлено - это уже становится традицией...

aidar_i
Offline
Зарегистрирован: 07.10.2018

И STM32.

aidar_i
Offline
Зарегистрирован: 07.10.2018

aidar_i пишет:

b707 пишет:

aidar_i пишет:

Ардуино МЕГА и STM32.

это как?

Отдельно на МЕГА2560 и отдельно на АТМЕГА328.

Опечатка Отдельно на МЕГА2560 и отдельно на СТМ32.

aidar_i
Offline
Зарегистрирован: 07.10.2018

b707 пишет:
фонты UkrRusSystemFont5x7 и UkrRusArial14 остались от старой библиотеки, у них другая кодировка, чем у фонтов TTF. Соответнно процедура utf8_rus для них не подходит. "Старые" фонты вообще должны показываться вообще без перекодирования, но для этого в скетче ардуино должна быть задана правильная кодировка. Посмотрите на предыдущей странице ветки - этот вопрос уже обсуждался.

Посмотрел, понял. не совсем удобно, но буду так пользоваться.

Цитата:
это на какой плате? - если можно, выложите пример кода, где видно мерцание.  А "волна" - это слишком редкое обновление картинки.

/*--------------------------------------------------------------------------------------
 dmd_cyrillic_chars
  DMD_STM32 example code for STM32F103xxx board
 ------------------------------------------------------------------------------------- */

/*--------------------------------------------------------------------------------------
  Includes
--------------------------------------------------------------------------------------*/
#include <DMD_STM32.h> 
//#include "st_fonts/SystemFont5x7.h"
//#include "st_fonts/Arial_Black_16_ISO_8859_1.h"
#include "st_fonts/UkrRusArial14.h";
#include "gfx_fonts/GlametrixLight12pt7b.h"
#include "gfx_fonts/GlametrixBold12pt7b.h"

// We'll use SPI 2   
SPIClass dmd_spi(2);


//Fire up the DMD library as dmd
#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

// ----- Select pins for P10 matrix connection ------------
// pins A, B, SCLK may be any digital I/O, pin nOE should be PWM pin as PB1,PA8

// SPI specific pins as CLK and R_DATA has predefined values:
//   for SPI(1) CLK = PA5  R_DATA = PA7
//   for SPI(2) CLK = PB13  R_DATA = PB15
// --------------------------------------------------------


#define DMD_PIN_A PB11
#define DMD_PIN_B PB12
#define DMD_PIN_nOE PB1
#define DMD_PIN_SCLK PB10

DMD dmd(DMD_PIN_A, DMD_PIN_B, DMD_PIN_nOE, DMD_PIN_SCLK, DISPLAYS_ACROSS, DISPLAYS_DOWN, dmd_spi );


// --- Define fonts ----
// DMD.h old style font
DMD_Standard_Font UkrRusArial_F(UkrRusArial_14);

// GFX font with sepatate parts for Latin and Cyrillic chars
//DMD_GFX_Font GlametrixL((uint8_t*)&GlametrixLight12pt7b,(uint8_t*)&GlametrixLight12pt8b_rus,0x80,13);
DMD_GFX_Font GlametrixBold((uint8_t*)&GlametrixBold12pt7b,(uint8_t*)&GlametrixBold12pt8b_rus, 0x80, 13); 


/*--------------------------------------------------------------------------------------
  UTF8 char recoding
 
--------------------------------------------------------------------------------------*/
int utf8_rus(char* dest, const unsigned char* src) {
  
  uint8_t i, j;
  for ( i =0, j =0; src[i]; i++) {
   if ((src[i] == 0xD0 )&& src[i+1])  { dest[j++] = src[++i] - 0x10;}
    else if ((src[i] == 0xD1 )&& src[i+1]) {dest[j++] = src[++i] + 0x30;  }
    else dest[j++] = src[i];
  }
  dest[j] ='\0';
  return j;
}

/*--------------------------------------------------------------------------------------
  setup
  Called by the Arduino architecture before the main loop begins
--------------------------------------------------------------------------------------*/
void setup(void)
{
   dmd.init();
   dmd.clearScreen( true );   //true is normal (all pixels off), false is negative (all pixels on)
   // set matrix brightness (0-255)
   dmd.setBrightness(80);
}


/*--------------------------------------------------------------------------------------
  loop
  Arduino architecture main loop
--------------------------------------------------------------------------------------*/
void loop(void)
{
   
   const unsigned char m[] = "Привет Ардуино!";
   char k[30];
   dmd.selectFont(&GlametrixBold);
   utf8_rus(k,m);
   dmd.drawMarquee(k,strlen(k),(32*DISPLAYS_ACROSS)-1,0);
   
   long prev_step =millis();
   
   while(1){
     if ((millis() - prev_step) > 50 ) {
       dmd.stepMarquee(-1,0);
       prev_step=millis();
      
     }
   }
}

На плате "синезуб".

Частоту менял в библиотеке DMD_STM32.h

class DMD
{
  public:
    //Instantiate the DMD 
DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi );
void init(uint16_t scan_interval = 2000);

Когда ставлю 1000 волны почти исчезают, но появляются мерцания внутри букв сильнее, когда ставлю выше 3000 до 5100 мерцания исчезают полностью и появляется волна сильная, что буквы кажутся искривленными.

АТМЕГА аналогично, на DMD такого не замечал. Можно, конечно поставить около 1500 и терпеть.

b707
Offline
Зарегистрирован: 26.05.2017

aidar_i пишет:

Частоту менял в библиотеке DMD_STM32.h

class DMD
{
  public:
    //Instantiate the DMD 
DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi );
void init(uint16_t scan_interval = 2000);

зачем же менять в библиотеке, вы понимаете, что значит эта строчка? :)

void init(uint16_t scan_interval = 2000);

это значит что процедуру dmd.init() в скетче можно вызывать с параметром, определяющем частоту опроса матриц:

void setup() {
dmd.init(2000);
......

что касается волны - попробуйте поставить меньше 1000, например на СТМ я ставлю 700

 

aidar_i
Offline
Зарегистрирован: 07.10.2018

Это я понимаю, что это частота опроса, волна меня устраивает и при 1000. Но тогда все светодиоды, из которых состоит буква мерцают, и это неприятно на вид. Ставил даже 500. Чем меньше тем больше мерцания. Это проявляется именно при бегущей строке. Кстати, когда я прикрутил яркость к библиотеке DMD произошло то же самое. Значит, мне кажется, дело в нем?

b707
Offline
Зарегистрирован: 26.05.2017

aidar_i пишет:
Кстати, когда я прикрутил яркость к библиотеке DMD произошло то же самое. Значит, мне кажется, дело в нем?

Да, дело в нем, это интерференция между частотой ШИМ управления яркостью и частотой сканирования матриц. На Атмеле нужно поднимать частоту ШИм на пине nOE - и мерцание должно пропасть. На СТМ это уже сделано.

Если под платой "синезуб" вы имеете в виду СТМ32 "блюпилл" - то там никакого мерцания категорически быть не должно, для СТМ эта проблема уже решена. 

Вечром попробую вашу конфигурацию, но вообще все должно работать. я на СТМ ставлю обновление 700, 500 и даже 300 - картинка четкая.

aidar_i
Offline
Зарегистрирован: 07.10.2018

Да, плата эта, перевел неправильно. На Атмеле сперва мерцал статичный текст, по совету форумчанина с феетроникс поднял до 5100, всё стало нормально. Волны появляются потому что через строку точки двигаются с опозданием, сперва , например, верхняя точка двигается и так через строки, потом вторая и через строку с опозданием. Пробовал 5100 ставить, тогда мерцание пропадало, но буквы становились кривыми.

В гитхабе в CHANGES.txt написано версия ## [0.4.0] - 2019-10-16. Вы писали в 83 сообщении, что вышла 0.4.1. Где ошибка?

b707
Offline
Зарегистрирован: 26.05.2017

aidar_i, сейчас собрал ваш конфиг с теми пинами, что указаны у вас в коде сообщения #169, SPI2, ставлю скан матрицы 700 - буквы абсолютно ровные и никакого мерцания нет.

на Гитхабе сейчас актуальный код для СТМ -  версия 0.4.1 от 23.10.19

 

aidar_i
Offline
Зарегистрирован: 07.10.2018

Спасибо! Завтра проверю.

aidar_i
Offline
Зарегистрирован: 07.10.2018

Неровность на широких шрифтах не особенно видно, сильно видно на тонких шрифтах на системных , например, 5х7

aidar_i
Offline
Зарегистрирован: 07.10.2018

Проверил, да на 700 нет волн. На Мега2560 на 300 лучше.

aidar_i
Offline
Зарегистрирован: 07.10.2018

Проверил работу всех шрифтов библиотеки, не заработал только Arial_Black_16_ISO_8859_1

b707
Offline
Зарегистрирован: 26.05.2017

aidar_i пишет:

Проверил работу всех шрифтов библиотеки, не заработал только Arial_Black_16_ISO_8859_1

отлично. Если нашли какие-то ошибки или есть замечания - буду рад откликам

Antsanv
Offline
Зарегистрирован: 18.12.2018

https://drive.google.com/open?id=1pzjoRVmd8NX7lIgYx_GHgMiJ4Ux4rtSS

Пробую библиотеку. Спасибо автору, проделана огромная работа!
Не знаю хорошо ли видно на видео, но при скролинге с периодом около 30 мс на буквах проявляются артефакты. Контур букв как бы плавает на 1 пиксел +/-

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

Не знаю хорошо ли видно на видео, но при скролинге с периодом около 30 мс на буквах проявляются артефакты. Контур букв как бы плавает на 1 пиксел +/-

отлично видно. У меня такого нет. Можете выложить коротенький код, демонстрирующий проблему?

Если нет - укажите набор пинов и с каким периодом иницируете таймер библиотеки (строчка dmd.init(). Еще желательно знать фонт, используемый для вывода

Antsanv
Offline
Зарегистрирован: 18.12.2018
// SPI 1
#define DMD_PIN_A PB11 
#define DMD_PIN_B PB12 
#define DMD_PIN_nOE PB1
#define DMD_PIN_SCLK PB10
SPIClass dmd_spi(1); // We'll use SPI 1   
DMD dmd(DMD_PIN_A, DMD_PIN_B, DMD_PIN_nOE, DMD_PIN_SCLK, DISPLAYS_ACROSS, DISPLAYS_DOWN, dmd_spi);
DMD_GFX_Font GlametrixBold((uint8_t*)&GlametrixBold12pt7b,(uint8_t*)&GlametrixBold12pt8b_rus0x8013); 

 

uint16_t displayScroll(const char* text, uint8_t x = 0, uint8_t y = 0) {
    static uint32_t prev_step = 0;
    char k[255];
    static uint8_t lenText = 0;
    static uint16_t strWidth, numStep = 0;
    if (numStep == 0) {
        dmd.clearScreen(true);
        selectFont(fontUsed);
        setBrightness();
        //dmd.selectFont(&arialbd16);
        //dmd.selectFont(&arialbd32);
        utf8_rus(k,text);
        lenText = strlen(k);
        strWidth = dmd.stringWidth(k, lenText);
        Serial2.print("lenText: "); Serial2.println(lenText);
        dmd.drawMarquee(k, lenText, (32*DISPLAYS_ACROSS)-1, 0);
        numStep++;
    }
    else {
        if (numStep < strWidth+(32*DISPLAYS_ACROSS)) {
            if ((millis() - prev_step) > speedTicker) {
                dmd.stepMarquee(-1,0);
                prev_step = millis();
                numStep++;
                //Serial1.print("numStep "); Serial1.println(numStep);
            }
        }
        else numStep = 0;
    }
    return numStep;
}

Сорри за фон, так вставляет из Platformio

dmd.init() без параметров

 

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

dmd.init() без параметров

а если dmd.init(750); например?

Antsanv
Offline
Зарегистрирован: 18.12.2018

b707 пишет:

Antsanv пишет:

dmd.init() без параметров

а если dmd.init(750); например?

Значительно лучше!

Antsanv
Offline
Зарегистрирован: 18.12.2018

Небольшой вклад в проект.
Тестовая плата с преобразователями на 5В (нужны если шлейф длинный, но можно и без них обойтись).
https://drive.google.com/open?id=1ggiflr1d-Fe1sobbmCVaLkPhH5E6mCnj

b707
Offline
Зарегистрирован: 26.05.2017

К сожалению, разрешение схемы такое, что многие надписи не читаются, а гербер с телефона не открыть. Если не ошибаюсь, на схеме 4 выхода SPI ? Не очень понял как это работает и что за микросхемы U3 U4

Antsanv
Offline
Зарегистрирован: 18.12.2018

https://drive.google.com/open?id=1q1UUFCeDblsk9uMYXpjA5QI-4aYAGT9s

SPI 2 выхода. Просто разъемов 4. Два 3,3 вольта (напрямую от STM), а два 5 вольт через преобразователь уровней. 

b707
Offline
Зарегистрирован: 26.05.2017

Antsanv пишет:

SPI 2 выхода. Просто разъемов 4. Два 3,3 вольта (напрямую от STM), а два 5 вольт через преобразователь уровней. 


а смысл что-то преобразовывать? От 3.3 к 5в преобразователь не нужен, а обратно матрица ничего не передает....да если б и передавала, большинство пинов блюпилл толерантно к 5в

Antsanv
Offline
Зарегистрирован: 18.12.2018

Если первый шлейф до панели больше 50 см, передается некорректно (пропуск строк и т.п.).  И там преобразование однонаправленное.

FoxJone
Offline
Зарегистрирован: 19.04.2019

Не совсем понятная городулька... А нельзя просто сделать короткий шлейф? Честно говоря, за всю свою практику (а я очень много табло сделал) только пару раз приходилось делать длинные шлейфы.

Antsanv
Offline
Зарегистрирован: 18.12.2018

FoxJone пишет:

Не совсем понятная городулька... А нельзя просто сделать короткий шлейф? Честно говоря, за всю свою практику (а я очень много табло сделал) только пару раз приходилось делать длинные шлейфы.

А мне приходилось делать длинные шлейфы. 
Ну и как говорится, не нравится, не пользуйтесь.

FoxJone
Offline
Зарегистрирован: 19.04.2019

Я же не в обиду, а реально интересно - зачем длинный шлейф от контроллера к модулю? Учитывая, что я свои контроллеры на этот же модуль и сажаю обычно...

Antsanv
Offline
Зарегистрирован: 18.12.2018

Никаких обид, бывает такая компановка, что удобней контроллер отнести от модулей.
А вообще изначально преобразователи делались под Р4 RGB и ESP32, ну и просто перенесено на тестовую плату под STM.