Скажите, я правильно понимаю: на один SPI мы цепляем последовательно 20 матриц, но в геометрии допустим 5(д)х4(в). И для того чтобы отображать текст высотой 64 пикселя надо просто сконвертировать нужный шрифт и указать в настройках скетча параметры 5х4?
Спасибо. Это именно двухцветные матрицы красные/зеленые. У них еще один пин задействован G. В изначальной библиотеке я создавал два soft объекта и управлял ими.
Спасибо. Это именно двухцветные матрицы красные/зеленые.
не знаю, удастся ли их запустить в двухцветном варианте. Как одноцветные может и заработают.
Что касается инверсии - для начала выясните, какое значение параметра в методе clearScreen() - true или false - полностью гасит панели. После этого добиться правильного вывода уже будет нетрудно.
Не гасит не один из имеющихся.
В любом варианте засвечиваются все пиксели.
что-то тогда сомневаюсь, что что-то выйдет.
clearScreen() выводит на панели либо все нули, либо единицы. В зависимости от того, инвертированная панель или нет - либо true гасит все пиксели, либо false... Но какой-то из двух точно должен гасить.
Не гасит не один из имеющихся.
В любом варианте засвечиваются все пиксели.
что-то тогда сомневаюсь, что что-то выйдет.
clearScreen() выводит на панели либо все нули, либо единицы. В зависимости от того, инвертированная панель или нет - либо true гасит все пиксели, либо false... Но какой-то из двух точно должен гасить.
Логично. Проверить не удалось, панели забрали на неделю. Но возможно все дело было в длинном шлейфе (80 см) и 3,3В непредсказуемо падало до панели.
Проверял сегодня шрифты и получается что fontconverter с высотой ошибается
"где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях"
ставлю size 16, а на выводе получается высота в 26 светодиодов.
Проверял сегодня шрифты и получается что fontconverter с высотой ошибается
"где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях"
ставлю size 16, а на выводе получается высота в 26 светодиодов.
да. я тоже это замечал, правда у меня фонты получались немного меньше указанного - ставлю 16. получаю 13. А 26 вместо 16-ти - это как-то сильно :)
Возможно это как-то зависит от исходного Truetype файла.
Пробую работать Вашей библиотекой с Ардуино, у Ардуино на 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;
}
Обнаружил , что шрифт UkrRusSystemFont5x7 и UkrRusArial14 неправильно декодируется. Пример с шрифтом GlametrixBold правильно. Правильно начинает работать только если убрать выделенную часть кода или обнулить их (лучше нужно в библиотеке переставить шрифты).
int utf8_rus(char* dest, const unsigned char* src) {
Бегущая строка очень неприятно двигается, потому что вертикальная часть букв идет волной. Убрать бы эту волну.
Уменьшил частоту до 1000 волны почти нет, но внутри букв есть мерцания при перемещении, закомментировал прокрутку , мерцают. Поэтому и при движении мерцают. Увеличение частоты мерцания убирают, но увеличивается эффект волны.
Обнаружил , что шрифт UkrRusSystemFont5x7 и UkrRusArial14 неправильно декодируется. Пример с шрифтом GlametrixBold правильно.
фонты UkrRusSystemFont5x7 и UkrRusArial14 остались от старой библиотеки, у них другая кодировка, чем у фонтов TTF. Соответнно процедура utf8_rus для них не подходит. "Старые" фонты вообще должны показываться вообще без перекодирования, но для этого в скетче ардуино должна быть задана правильная кодировка. Посмотрите на предыдущей странице ветки - этот вопрос уже обсуждался.
Цитата:
Уменьшил частоту до 1000 волны почти нет, но внутри букв есть мерцания при перемещении, закомментировал прокрутку , мерцают. Поэтому и при движении мерцают. Увеличение частоты мерцания убирают, но увеличивается эффект волны.
это на какой плате? - если можно, выложите пример кода, где видно мерцание. А "волна" - это слишком редкое обновление картинки.
ЗЫ. Коллега Дмитрий, я тут себе сегодня остатки мозга поломал с вашим ДМД, отписал проблему на мыло, не глянете?
Искренне надеюсь, что проблема у меня в программировании... Потому что, если вы скажете, что все правильно, значит завтра я возьму тестер и начну проверять дорожки на плате - и не удивлюсь, если они вдруг ведут не к тем пинам, к каким заявлено - это уже становится традицией...
фонты 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();
}
}
}
Когда ставлю 1000 волны почти исчезают, но появляются мерцания внутри букв сильнее, когда ставлю выше 3000 до 5100 мерцания исчезают полностью и появляется волна сильная, что буквы кажутся искривленными.
АТМЕГА аналогично, на DMD такого не замечал. Можно, конечно поставить около 1500 и терпеть.
Это я понимаю, что это частота опроса, волна меня устраивает и при 1000. Но тогда все светодиоды, из которых состоит буква мерцают, и это неприятно на вид. Ставил даже 500. Чем меньше тем больше мерцания. Это проявляется именно при бегущей строке. Кстати, когда я прикрутил яркость к библиотеке DMD произошло то же самое. Значит, мне кажется, дело в нем?
Кстати, когда я прикрутил яркость к библиотеке DMD произошло то же самое. Значит, мне кажется, дело в нем?
Да, дело в нем, это интерференция между частотой ШИМ управления яркостью и частотой сканирования матриц. На Атмеле нужно поднимать частоту ШИм на пине nOE - и мерцание должно пропасть. На СТМ это уже сделано.
Если под платой "синезуб" вы имеете в виду СТМ32 "блюпилл" - то там никакого мерцания категорически быть не должно, для СТМ эта проблема уже решена.
Вечром попробую вашу конфигурацию, но вообще все должно работать. я на СТМ ставлю обновление 700, 500 и даже 300 - картинка четкая.
Да, плата эта, перевел неправильно. На Атмеле сперва мерцал статичный текст, по совету форумчанина с феетроникс поднял до 5100, всё стало нормально. Волны появляются потому что через строку точки двигаются с опозданием, сперва , например, верхняя точка двигается и так через строки, потом вторая и через строку с опозданием. Пробовал 5100 ставить, тогда мерцание пропадало, но буквы становились кривыми.
В гитхабе в CHANGES.txt написано версия ## [0.4.0] - 2019-10-16. Вы писали в 83 сообщении, что вышла 0.4.1. Где ошибка?
aidar_i, сейчас собрал ваш конфиг с теми пинами, что указаны у вас в коде сообщения #169, SPI2, ставлю скан матрицы 700 - буквы абсолютно ровные и никакого мерцания нет.
на Гитхабе сейчас актуальный код для СТМ - версия 0.4.1 от 23.10.19
Пробую библиотеку. Спасибо автору, проделана огромная работа!
Не знаю хорошо ли видно на видео, но при скролинге с периодом около 30 мс на буквах проявляются артефакты. Контур букв как бы плавает на 1 пиксел +/-
К сожалению, разрешение схемы такое, что многие надписи не читаются, а гербер с телефона не открыть. Если не ошибаюсь, на схеме 4 выхода SPI ? Не очень понял как это работает и что за микросхемы U3 U4
SPI 2 выхода. Просто разъемов 4. Два 3,3 вольта (напрямую от STM), а два 5 вольт через преобразователь уровней.
а смысл что-то преобразовывать? От 3.3 к 5в преобразователь не нужен, а обратно матрица ничего не передает....да если б и передавала, большинство пинов блюпилл толерантно к 5в
Не совсем понятная городулька... А нельзя просто сделать короткий шлейф? Честно говоря, за всю свою практику (а я очень много табло сделал) только пару раз приходилось делать длинные шлейфы.
Не совсем понятная городулька... А нельзя просто сделать короткий шлейф? Честно говоря, за всю свою практику (а я очень много табло сделал) только пару раз приходилось делать длинные шлейфы.
А мне приходилось делать длинные шлейфы.
Ну и как говорится, не нравится, не пользуйтесь.
Я же не в обиду, а реально интересно - зачем длинный шлейф от контроллера к модулю? Учитывая, что я свои контроллеры на этот же модуль и сажаю обычно...
Никаких обид, бывает такая компановка, что удобней контроллер отнести от модулей.
А вообще изначально преобразователи делались под Р4 RGB и ESP32, ну и просто перенесено на тестовую плату под STM.
Коллега Дмитрий, настал день, когда я наконец то разгреб все свои проекты и добрался до вашей библиотеки. Ну как добрался.... Дотянул до того, что все сроки прошли и если я в скором времени не сдам заказчику контроллер на СТМке, то меня начнут называть желтой лягушкой. И еще земляным червяком)
Для разминки и для понимания, что библиотека все таки может, весь день сегодня гонял ее в разных режимах и в принципе все нормально работает, кроме бегущей строки. Которая drawMarquee.
Она прекрасно работает на обычных диповских модулях, которые GRAPHICS_NORMAL. Но проблема в том, что у меня модули инверсные и вот с ними получается затык. Затык в том, что сама строка идет нормально, в инверсном режиме, а вот фон идет в режиме GRAPHICS_NORMAL. То есть по краям строки и промежуток между буквами засвечен полностью.
Не подскажете куда копать?
Ну и сразу заметочку: в DrawString и drawMarquee есть параметр "длина строки". Вот на мой взгляд, совершенно не нужный и бесполезный (В DMD2 его уже нет). Хотя, может я не знаю о каких то его применениях... Так вот может есть смысл убрать его и считать длину строки прямо в библиотеке?
Добрый день.
Скажите, я правильно понимаю: на один SPI мы цепляем последовательно 20 матриц, но в геометрии допустим 5(д)х4(в). И для того чтобы отображать текст высотой 64 пикселя надо просто сконвертировать нужный шрифт и указать в настройках скетча параметры 5х4?
Совершенно верно
Вот мой шрифт на 96 пикселей:
Попробовал матрицы с инверсией Р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);
не получается.
Попробовал матрицы с инверсией Р10 R1/G1.
это случайно не двуцветные матрицы? - если да, то я с ними не работал.
Для одноцветных матриц с инверсией в моей библиотеке есть отдельный метод
dmd.inverseAll(true);
даете эту команду после dmd.init();
и далее работаете с матрицей как обычно
кстати, синтаксис
dmd.drawString(2, 0, recievedMsg, GRAPHICS_INVERSE); - вывод строки
тоже должен работать
Спасибо. Это именно двухцветные матрицы красные/зеленые. У них еще один пин задействован G. В изначальной библиотеке я создавал два soft объекта и управлял ими.
завтра попробую Ваш совет. Еще раз спасибо.
Спасибо. Это именно двухцветные матрицы красные/зеленые.
не знаю, удастся ли их запустить в двухцветном варианте. Как одноцветные может и заработают.
Что касается инверсии - для начала выясните, какое значение параметра в методе clearScreen() - true или false - полностью гасит панели. После этого добиться правильного вывода уже будет нетрудно.
Не гасит не один из имеющихся.
В любом варианте засвечиваются все пиксели. Строка выводится в инверсии. Но выводится.
Не гасит не один из имеющихся.
В любом варианте засвечиваются все пиксели.
что-то тогда сомневаюсь, что что-то выйдет.
clearScreen() выводит на панели либо все нули, либо единицы. В зависимости от того, инвертированная панель или нет - либо true гасит все пиксели, либо false... Но какой-то из двух точно должен гасить.
Не гасит не один из имеющихся.
В любом варианте засвечиваются все пиксели.
что-то тогда сомневаюсь, что что-то выйдет.
clearScreen() выводит на панели либо все нули, либо единицы. В зависимости от того, инвертированная панель или нет - либо true гасит все пиксели, либо false... Но какой-то из двух точно должен гасить.
Логично. Проверить не удалось, панели забрали на неделю. Но возможно все дело было в длинном шлейфе (80 см) и 3,3В непредсказуемо падало до панели.
Проверял сегодня шрифты и получается что fontconverter с высотой ошибается
"где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях"
ставлю size 16, а на выводе получается высота в 26 светодиодов.
Проверял сегодня шрифты и получается что fontconverter с высотой ошибается
"где filename - файл фонта формата Truetype, size - размер получающегося фонта в писелях"
ставлю size 16, а на выводе получается высота в 26 светодиодов.
да. я тоже это замечал, правда у меня фонты получались немного меньше указанного - ставлю 16. получаю 13. А 26 вместо 16-ти - это как-то сильно :)
Возможно это как-то зависит от исходного Truetype файла.
Под ESP не планируете доработки библиотеки?
Под ESP не планируете доработки библиотеки?
под 8266 точно нет, а под ESP32 возможно. Но пока есть более актуальные задачи.
Спасибо за библиотеку!
Пробую работать Вашей библиотекой с Ардуино, у Ардуино на 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)
Испытываю в железе. Ардуино МЕГА и STM32.
Обнаружил , что шрифт UkrRusSystemFont5x7 и UkrRusArial14 неправильно декодируется. Пример с шрифтом GlametrixBold правильно. Правильно начинает работать только если убрать выделенную часть кода или обнулить их (лучше нужно в библиотеке переставить шрифты).
Бегущая строка очень неприятно двигается, потому что вертикальная часть букв идет волной. Убрать бы эту волну.
Уменьшил частоту до 1000 волны почти нет, но внутри букв есть мерцания при перемещении, закомментировал прокрутку , мерцают. Поэтому и при движении мерцают. Увеличение частоты мерцания убирают, но увеличивается эффект волны.
Ардуино МЕГА и STM32.
это как?
фонты UkrRusSystemFont5x7 и UkrRusArial14 остались от старой библиотеки, у них другая кодировка, чем у фонтов TTF. Соответнно процедура utf8_rus для них не подходит. "Старые" фонты вообще должны показываться вообще без перекодирования, но для этого в скетче ардуино должна быть задана правильная кодировка. Посмотрите на предыдущей странице ветки - этот вопрос уже обсуждался.
это на какой плате? - если можно, выложите пример кода, где видно мерцание. А "волна" - это слишком редкое обновление картинки.
Ардуино МЕГА и STM32.
Отдельно на МЕГА2560 и отдельно на АТМЕГА328.
Ардуино МЕГА и STM32.
Отдельно на МЕГА2560 и отдельно на АТМЕГА328.
Осталось понять, где в этой схеме STM32...
ЗЫ. Коллега Дмитрий, я тут себе сегодня остатки мозга поломал с вашим ДМД, отписал проблему на мыло, не глянете?
Искренне надеюсь, что проблема у меня в программировании... Потому что, если вы скажете, что все правильно, значит завтра я возьму тестер и начну проверять дорожки на плате - и не удивлюсь, если они вдруг ведут не к тем пинам, к каким заявлено - это уже становится традицией...
И STM32.
Ардуино МЕГА и STM32.
Отдельно на МЕГА2560 и отдельно на АТМЕГА328.
Опечатка Отдельно на МЕГА2560 и отдельно на СТМ32.
Посмотрел, понял. не совсем удобно, но буду так пользоваться.
На плате "синезуб".
Частоту менял в библиотеке DMD_STM32.h
Когда ставлю 1000 волны почти исчезают, но появляются мерцания внутри букв сильнее, когда ставлю выше 3000 до 5100 мерцания исчезают полностью и появляется волна сильная, что буквы кажутся искривленными.
АТМЕГА аналогично, на DMD такого не замечал. Можно, конечно поставить около 1500 и терпеть.
Частоту менял в библиотеке DMD_STM32.h
зачем же менять в библиотеке, вы понимаете, что значит эта строчка? :)
это значит что процедуру dmd.init() в скетче можно вызывать с параметром, определяющем частоту опроса матриц:
что касается волны - попробуйте поставить меньше 1000, например на СТМ я ставлю 700
Это я понимаю, что это частота опроса, волна меня устраивает и при 1000. Но тогда все светодиоды, из которых состоит буква мерцают, и это неприятно на вид. Ставил даже 500. Чем меньше тем больше мерцания. Это проявляется именно при бегущей строке. Кстати, когда я прикрутил яркость к библиотеке DMD произошло то же самое. Значит, мне кажется, дело в нем?
Да, дело в нем, это интерференция между частотой ШИМ управления яркостью и частотой сканирования матриц. На Атмеле нужно поднимать частоту ШИм на пине nOE - и мерцание должно пропасть. На СТМ это уже сделано.
Если под платой "синезуб" вы имеете в виду СТМ32 "блюпилл" - то там никакого мерцания категорически быть не должно, для СТМ эта проблема уже решена.
Вечром попробую вашу конфигурацию, но вообще все должно работать. я на СТМ ставлю обновление 700, 500 и даже 300 - картинка четкая.
Да, плата эта, перевел неправильно. На Атмеле сперва мерцал статичный текст, по совету форумчанина с феетроникс поднял до 5100, всё стало нормально. Волны появляются потому что через строку точки двигаются с опозданием, сперва , например, верхняя точка двигается и так через строки, потом вторая и через строку с опозданием. Пробовал 5100 ставить, тогда мерцание пропадало, но буквы становились кривыми.
В гитхабе в CHANGES.txt написано версия ## [0.4.0] - 2019-10-16. Вы писали в 83 сообщении, что вышла 0.4.1. Где ошибка?
aidar_i, сейчас собрал ваш конфиг с теми пинами, что указаны у вас в коде сообщения #169, SPI2, ставлю скан матрицы 700 - буквы абсолютно ровные и никакого мерцания нет.
на Гитхабе сейчас актуальный код для СТМ - версия 0.4.1 от 23.10.19
Спасибо! Завтра проверю.
Неровность на широких шрифтах не особенно видно, сильно видно на тонких шрифтах на системных , например, 5х7
Проверил, да на 700 нет волн. На Мега2560 на 300 лучше.
Проверил работу всех шрифтов библиотеки, не заработал только Arial_Black_16_ISO_8859_1
Проверил работу всех шрифтов библиотеки, не заработал только Arial_Black_16_ISO_8859_1
отлично. Если нашли какие-то ошибки или есть замечания - буду рад откликам
https://drive.google.com/open?id=1pzjoRVmd8NX7lIgYx_GHgMiJ4Ux4rtSS
Пробую библиотеку. Спасибо автору, проделана огромная работа!
Не знаю хорошо ли видно на видео, но при скролинге с периодом около 30 мс на буквах проявляются артефакты. Контур букв как бы плавает на 1 пиксел +/-
Не знаю хорошо ли видно на видео, но при скролинге с периодом около 30 мс на буквах проявляются артефакты. Контур букв как бы плавает на 1 пиксел +/-
отлично видно. У меня такого нет. Можете выложить коротенький код, демонстрирующий проблему?
Если нет - укажите набор пинов и с каким периодом иницируете таймер библиотеки (строчка dmd.init(). Еще желательно знать фонт, используемый для вывода
Сорри за фон, так вставляет из Platformio
dmd.init() без параметров
dmd.init() без параметров
а если dmd.init(750); например?
dmd.init() без параметров
а если dmd.init(750); например?
Значительно лучше!
Небольшой вклад в проект.

Тестовая плата с преобразователями на 5В (нужны если шлейф длинный, но можно и без них обойтись).
https://drive.google.com/open?id=1ggiflr1d-Fe1sobbmCVaLkPhH5E6mCnj
К сожалению, разрешение схемы такое, что многие надписи не читаются, а гербер с телефона не открыть. Если не ошибаюсь, на схеме 4 выхода SPI ? Не очень понял как это работает и что за микросхемы U3 U4
https://drive.google.com/open?id=1q1UUFCeDblsk9uMYXpjA5QI-4aYAGT9s
SPI 2 выхода. Просто разъемов 4. Два 3,3 вольта (напрямую от STM), а два 5 вольт через преобразователь уровней.
SPI 2 выхода. Просто разъемов 4. Два 3,3 вольта (напрямую от STM), а два 5 вольт через преобразователь уровней.
а смысл что-то преобразовывать? От 3.3 к 5в преобразователь не нужен, а обратно матрица ничего не передает....да если б и передавала, большинство пинов блюпилл толерантно к 5в
Если первый шлейф до панели больше 50 см, передается некорректно (пропуск строк и т.п.). И там преобразование однонаправленное.
Не совсем понятная городулька... А нельзя просто сделать короткий шлейф? Честно говоря, за всю свою практику (а я очень много табло сделал) только пару раз приходилось делать длинные шлейфы.
Не совсем понятная городулька... А нельзя просто сделать короткий шлейф? Честно говоря, за всю свою практику (а я очень много табло сделал) только пару раз приходилось делать длинные шлейфы.
А мне приходилось делать длинные шлейфы.
Ну и как говорится, не нравится, не пользуйтесь.
Я же не в обиду, а реально интересно - зачем длинный шлейф от контроллера к модулю? Учитывая, что я свои контроллеры на этот же модуль и сажаю обычно...
Никаких обид, бывает такая компановка, что удобней контроллер отнести от модулей.
А вообще изначально преобразователи делались под Р4 RGB и ESP32, ну и просто перенесено на тестовую плату под STM.
Коллега Дмитрий, настал день, когда я наконец то разгреб все свои проекты и добрался до вашей библиотеки. Ну как добрался.... Дотянул до того, что все сроки прошли и если я в скором времени не сдам заказчику контроллер на СТМке, то меня начнут называть желтой лягушкой. И еще земляным червяком)
Для разминки и для понимания, что библиотека все таки может, весь день сегодня гонял ее в разных режимах и в принципе все нормально работает, кроме бегущей строки. Которая drawMarquee.
Она прекрасно работает на обычных диповских модулях, которые GRAPHICS_NORMAL. Но проблема в том, что у меня модули инверсные и вот с ними получается затык. Затык в том, что сама строка идет нормально, в инверсном режиме, а вот фон идет в режиме GRAPHICS_NORMAL. То есть по краям строки и промежуток между буквами засвечен полностью.
Не подскажете куда копать?
Ну и сразу заметочку: в DrawString и drawMarquee есть параметр "длина строки". Вот на мой взгляд, совершенно не нужный и бесполезный (В DMD2 его уже нет). Хотя, может я не знаю о каких то его применениях... Так вот может есть смысл убрать его и считать длину строки прямо в библиотеке?
FoxJone, рад слышать.
как работаете с инверсией? В библиотеке есть отдельный метод
dmd.inverseAll(true);
поставьте эту строчку сразу после dmd.init() - и напишите, помогло ли
Если нет - мне понадобится больше информации, у кастомных GFX-фонтов, к сожалению, вопрос фона - больной, не только у меня в библиотеке
Да самый простой пример.
А выглядит вот так:
а если в 5-й строчке dmd.clearScreen(false );
а если в 5-й строчке dmd.clearScreen(false );
Все тоже самое, только экран с самого начала весь засвечен. То, что на фото перед строкой темно - это и есть клеарскрин.
я. кажется. начинаю понимать, что искать. Но мне нужно посмотреть код
FoxJone. а можете проверить - что будет, если шаг скроллинга поставить -2 (строка 18):
ret=dmd.stepMarquee(-2,0);