Чт мар 22, 2018 19:13:03
Чт мар 22, 2018 21:13:01
Пт мар 23, 2018 05:17:01
Чт июл 19, 2018 07:52:23
Чт авг 30, 2018 22:58:10
Сб сен 07, 2019 16:51:13
А ее можно где-нибудь увидеть?goldenandy писал(а):Тоже сделал либу для данного дисплея с использованием экранного буфера в ОЗУ - 1 кб.
Ну, так не зря в ДШ заявлено 72 мкс - при такой паузе изготовитель гарантирует работоспособность. А все, что меньше - типичный оверклок, может и заработать, но не обязано.goldenandy писал(а):И столкнулся буквально сегодня с тормозным дисплеем.
Так дисплей отказался рисовать картинку нормально при паузе в 50 мкс между отправками.... Нормально завелся аж при 68 мкс....
Хотя предыдущий работал без артефактов на 37 мкс паузах.... Вот такие пироги
Сб ноя 30, 2019 02:30:48
Сб янв 04, 2020 19:28:05
Чт апр 09, 2020 17:31:27
Чт авг 06, 2020 00:30:10
Вс фев 28, 2021 20:46:44
А ее можно где-нибудь увидеть?goldenandy писал(а):Тоже сделал либу для данного дисплея с использованием экранного буфера в ОЗУ - 1 кб.
#ifndef _DISPLAYDRIVER_7920_BLOCK_
#define _DISPLAYDRIVER_7920_BLOCK_
/*****************************************************************************************
Драйвер для взаимодействия с дисплеем на контроллере ST7920
Особенность данного контроллера - пауза между посылками команд и слов данных.
Согласно документации - пауза 72 мкс.
По факту на некоторых дисплеях можно добиться работоспособности вплоть до 35мкс.
Для минимизации потребления процессорного времени алгоритм отправки данных
в дисплей реализован через периодический вызов процедуры внутренней синхронизации
с периодичностью 72мкс. От основной программы требуется только периодический вызов
процедуры синхронизации, например, из прерывания таймера. Остальные действия по отправке
данных на экран будут прозрачны для основной программы.
*****************************************************************************************/
/*****************************************************************************************
ВНИМАНИЕ! В данном драйвере отсутствуют процедуры физического общения с дисплеем!
Эти процедуры должны быть описаны отдельно.
Это 2 процедуры - одна отвечает за управлением пином RST дисплея,
вторая - за отправку одного или двух байт данных/команды в дисплей.
Как будет реализована такая отправка - ногодрыгом, через аппаратный SPI на ожиданиях/прерываниях/DMA
и т.д. - драйвер не знает.
Для работы с дисплеем предоставлены 2 процедуры - очистки дисплея и включения/выключения пикселя.
Вся остальная работа выполняется в процедуре обслуживания, которую необходимо вызывать из
основноай программы с периодичностью 72мкс.
*****************************************************************************************/
#include <stdint.h>
//_____
/******** Процедуры физического взаимодействия МК с дисплеем ********/
/******** Процедуры должны быть реализованы в основной программе ********/
extern void ST7920_setResetLevel(uint8_t resetLevel);
// установка уровня Reset. При инициализации железа начальный уровень - высокий.
extern void ST7920_sendData(uint8_t dataBuf[], uint8_t count, uint8_t dataFlag);
// Установка флага данные/команда (dataFlag), отправка count байт данных из буфера dataBuf в дисплей
// _____
// Параллельное подключение дисплея
// (Вывод PSB через 4к7 подключен к питанию)
// ----------
// 1. На линии RS выставляется признак данных/команды - флаг dataFlag (1-данные, 0-команда)
// 2. Линия R/W прижата к общему прводу, мы данные из дисплея не читаем
// 3. Через время не менее 10 нс* строб E переводится в 1.
// 4. На выводах D0-D7 выставляются данные.
// 5. Через не менее чем через 140 нс после поднятия строб E переводится в 0.
// При этом данные на D0-D7 должны присутствовать на шине не менее чем за 40нс до опускания строба.
// 6. После опусания строба данные на шине и сигнал R/S должны присутствовать без изменений еще 20 нс
// _____
// Последовательное подключение дисплея
// (Вывод PSB подключен к общему проводу)
// ----------
// Линия RS выступает как сигнал CS - Chip select, idle-уровень - низкий, активный - высокий.
// Линия E остается сигналом строба (CLK), активный уровень низкий, idle - высокий
// Линия R/W - данные (DATA).
// Данные считвываются с линии DATA в момент перехода CLK из низкого уровня в высокий.
// AVR.SPI:
// SPCR.CPOL=1 (SCK is high when idle, Leading edge is falling),
// SPCR.CPHA=1 (Sample on trailing edge)
// Первый такт CLK должен начинаться не ранее чем через 60 нс* после активации CS.
// Сигнал CS снимается не ранее, чем через 60 нс после заднего фронта CLK
// Байт данных передается в дисплей за 24 такта CLK (передача 3х байт)
// Период сигнала CLK - не менее 600 нс ( 1.66(6) МГц), длительность низкого и высокого уровней - не менее 300 нс
// Данные на линии DATA должны быть выставлены минимум за 40 нс до перехода CLK из низкого в высокий уровень
// и должны там удерживаться еще не менее 40нс
// 1. Выставить высокий уровень CS.
// 2. Последовательно передать 3 байта:
// 2.1. Первый байт: 1 1 1 1 1 RW DC 0
// DC - признак данных или команды, 1-данные, 0-команда
// RW - 0-запись данных (мы данные из дисплея не читаем)
// Итого: 0xF8 - для команды, 0xFA - для данных
// 2.2. Второй байт: D7 D6 D5 D4 0 0 0 0
// 2.3. Третий байт: D3 D2 D1 D0 0 0 0 0
// 3. Выставить низкий уровень CS.
// _____
// * 1 такт AVR при 8 МГц = 125 нс, при 16 МГц = 62,5 нс
// ** SPI clock - проверялось на 1, 1.125, 1.25, 1.5, 1.75 МГц. На 2МГц завелось с мусором, на 2.25 МГц не завелось.
//_____
/******** Процедуры управления дисплеем ********/
void ST7920_init(void);
// Установка флага необходимости сброса и инициализации дисплея.
// Сампроццесс сброса и инициализации пройдет в процессе периодических вызовов процедуры внутренней синхронизации
void ST7920_reInit(uint8_t clearRAM);
// Установка флага необходимости переинициализации контроллера дисплея без подачи сигнала сброса.
// Очиста видеопамяти - опционально
void ST7920_sync(void);
// Процедура внутренней синхронизации, должна вызываться с периодичностью 72 мкс.
uint8_t ST7920_getIdleFlag(void);
// процедура проверки статуса обновления, возвращает 1, если все данные на дисплее обновлены.
//_____
/******** Процедуры вывода графики ********/
void ST7920_clearDisplay(void);
// очистка видеопамяти
void ST7920_putPixel(uint8_t x, uint8_t y, uint8_t color);
// вывод пикселя в видеопамять. color ==1 - пиксель будет зажжен, ==0 - погашен
#endif //_DISPLAYDRIVER_7920_BLOCK_
#include "st7920.h"
#include <string.h>
typedef enum {
dpOff = 0,
dpReset1,
dpReset2,
dpInit1,
dpInit2,
dpInit3,
dpScanChanges,
dpSetRow,
dpSendCol,
dpSendData
} displayPhaseType;
// режимы дисплея
static volatile displayPhaseType displayPhase = dpOff;
// организация видеопамяти - 32 строки по 16 слов.
// Итого 256*32 - как принято в архитектуре ST7920
// При этом левый блок 128*32 - верняя часть реального дисплея, правый - нижняя часть.
// так сделано для оптимизации вывода, ибо он должен происходить максимально быстро.
static volatile uint16_t videoram[32][256/16];
// массив флагов с признаками необходимости обновления обновления
static volatile uint8_t updateFlags[32];
// просто константы
static const uint8_t masks[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
// флаг наличия обновлений
static volatile uint8_t needUpdate = 0;
#define AS_DATA 1
#define AS_COMMAND 0
void ST7920_init(void){
displayPhase = dpReset1;
}
void ST7920_reInit(uint8_t clearRAM){
if (clearRAM) memset((void*)videoram, 0x00, sizeof(videoram));
memset((void*)updateFlags, 0xFF, sizeof(updateFlags));
displayPhase = dpInit1;
}
void ST7920_sync(void){
static uint16_t delay = 0;
static uint8_t data[2];
static uint8_t row = 0;
static uint8_t col = 0;
if (delay) {
delay--;
return;
}
switch (displayPhase) {
case dpOff:
return;
//break;
case dpScanChanges:
if ( !needUpdate ) return;
if ( row == 0 ) needUpdate = 1;
while (row<32) {
if (updateFlags[row]) {
// нужно обновлять данные в дисплей....
col = 0;
displayPhase = dpSetRow;
updateFlags[row] = 0;
break;
}
row++;
} // while row < 32
if (displayPhase == dpScanChanges) {
if (row == 32) {
row = 0;
if (needUpdate==1) needUpdate = 0;
}
break;
} // if (displayPhase == dpScanChanges)
// break; - тут брейк не нужен, мы нашли неотправленные данные и едем их отправлять
case dpSetRow:
case dpSendCol:
if (displayPhase == dpSetRow) data[0] = 0x80 | row;
else data[0] = 0x80; // col == 0
ST7920_sendData(data, 1, AS_COMMAND);
displayPhase++;
break;
case dpSendData:
data[0] = ((uint16_t)videoram[row][col])>>8;
data[1] = videoram[row][col] & 0xFF;
ST7920_sendData(data, 2, AS_DATA);
col++;
if (col==16) displayPhase = dpScanChanges; // если дошагали до конца строки - возвращаемся к сканированию.
break;
case dpReset1:
memset((void*)videoram, 0x00, sizeof(videoram));
memset((void*)updateFlags, 0xFF, sizeof(updateFlags));
needUpdate = 2;
ST7920_setResetLevel(0);
delay = 1000; // 72 ms - 1000 * 72 us = 72 ms
displayPhase++;
break;
case dpReset2:
ST7920_setResetLevel(1);
delay = 50; // 3.6 ms - 50 * 72 us = 3.6 ms
displayPhase++;
break;
case dpInit1:
case dpInit2:
case dpInit3:
switch ((uint8_t)displayPhase) {
case dpInit1:
data[0] = 0x30; // Standard mode
break;
case dpInit2:
data[0] = 0x01; // Clear text display
break;
case dpInit3:
data[0] = 0x36; // Enter extended mode
break;
}
ST7920_sendData(data, 1, AS_COMMAND);
delay = 100; //100 * 72 us = ~7.2 ms
displayPhase++;
row = 0;
col = 0;
break;
} // switch (displayPhase)
}
void ST7920_clearDisplay(void){
// очистка видеопамяти
memset((void*)videoram, 0x00, sizeof(videoram));
memset((void*)updateFlags, 0xFF, sizeof(updateFlags));
needUpdate = 2;
}
void ST7920_putPixel(uint8_t x, uint8_t y, uint8_t color){
uint16_t xMask;
uint8_t xIndex;
if (x > 127 || y > 63) return;
y &= 0x3F;
xIndex = x/16;
x = 15-(x & 0x0F);
if (y>31) xIndex += 8;
y &= 0x1F;
xMask = masks[x & 0x07];
if (x>7) xMask <<= 8;
uint16_t vRam = videoram[y][xIndex];
if (color) videoram[y][xIndex] |= xMask;
else videoram[y][xIndex] &= ~xMask;
if (vRam == videoram[y][xIndex]) return;
updateFlags[y] = 1;
needUpdate = 2;
}
uint8_t ST7920_getIdleFlag(void){
return needUpdate==0 || displayPhase==dpOff;
}
Чт апр 15, 2021 12:35:51
Чт апр 15, 2021 16:12:19
Чт апр 15, 2021 21:08:51
Чт апр 15, 2021 22:06:28
Пт апр 16, 2021 09:11:53
Пт апр 16, 2021 09:36:39
Пт апр 16, 2021 10:10:44
void GLCD_display(unsigned char code *addr)
{
int i,j;
//*******display top half screen
for(i=0;i<32;i++) //
{
TransferData((0x80 + i),0); //SET VERTICAL ADDRESS
TransferData(0x80,0); //SET HORIZONTAL ADDRESS
for(j=0;j<16;j++)
{
TransferData(*addr,1);
addr++;
}
}
//*******display bottom half screen
for(i=0;i<32;i++) //
{
TransferData((0x80 + i),0); //SET VERTICAL ADDRESS
TransferData(0x88,0); //SET HORIZONTAL ADDRESS
for(j=0;j<16;j++)
{
TransferData(*addr,1);
addr++;
}
}
}
Или, если есть возможность, взять МК чуть потолще. Ибо 368 байт ОЗУ - это грустно на данное время.
Пт апр 16, 2021 10:46:27
Пт апр 16, 2021 11:04:52
Ибо, зажечь пиксель, не имея информации о соседях - тяжело.