Уважаемые,нужен совет, все собрал в кучу,работает менюшка на дисплее от 1202,но есть косяки с русскими названиями. есть буфер размером в 16 байт,строка из прогмем копируется в буфер и дальше на экран.
пока в строке не более 7 символов все нормально,на всех последующих страницах меню все названия отображаются,как только превысил - так на первой страничке меню все правильно отображается а на последующих страницах идут пустые строки,с буржуйским языком все отлично работает при размере строки в 16 символов косяков не наблюдал.
я так подозреваю трабла сидит в кодировке символов UTF-8 из-за двубайтной кодировки русских букв? пробовал в настройках IDE менять кодировку - либо пусто вместо русских букв либо кракозяблы. шрифт использую тот что в исправленной версии от Gres.
Использовал бибу от Gres,класс титановый велосипед для тактовой кнопки от Клапауций 999 ,и MenuOS_9R3 от Kashkanov Artem radiolok@yandex.ru.
Если народу интересно,могу выложить скетч на всеобщее обозрение и оптимизацию,видео некуда закинуть. пока там еще жуткая смесь из русских и англицких слов,но это поправимо.
разбито на несколько файлов,коментарии писались для сынишки
#include <WProgram.h>
#include <lcd1202.h>
#include <EEPROM.h>
#include <stdio.h>
#include <avr/pgmspace.h> //Храним статические массивы во флеш
//File types definitions
#define T_FOLDER 0 //папка, содержит список файлов
#define T_DFOLDER 2 //Динамическая папка. Содержит несколько копий одного файла
#define T_SFOLDER 3 //Папка выбора
#define T_APP 4 //Приложение
#define T_CONF 5 //Конфиг
//#define T_SCONF 6 //Конфиг выбора. Располагается в папке выбора
//#define T_DCONF 7 //Динамический конфиг
#define FILENUMB 19 //общее количество файлов в массив
#define FILEREW 3 // количество байт на каждый файл
#define MAXDEPTH 4 // максимальная глубина внутренного раздела #define MAXDEPTH 4
#define DISPSTR 4 // количество строк, используемых для записи меню. // Дисплей может иметь несколько строк!
#define LCDCOL 16 // Максимальное количество символов в строке
#define LCDBITS (DISPSTR-1) // маска курсора
#define LCDINVBITS ~LCDBITS // маска раздела
#define ROWSHIFT 2 // сдвиг позиции курсора на 2 знакоместо по y
#define PROGV "v.9" // Version of menuOS
#define CONF_MAXLIMIT_BOOL_TO_LIM 50 // максимальное значение при смене типа конфига из булева в лимитированный
// у меня устанавливает максимум для тока двигателя в амперах и для датчика вибрации
#define CONF_LIM_MAX 255 // максимальное значение лимитированного конфига - 1 байт
#define lowByte(w) ((byte) ((w) & 0xff))
#define highByte(w) ((byte) ((w) >> 8))
//#define DynConf 1// Динамический конфиг
#define Conf 0 // Конфиг
// адреса сохранения в ЕЕПРОМ
#define addr_Save_SensVibro 0
#define addr_Save_SensTok 2
#define addr_Save_RotateZikl_1 4
#define addr_Save_TimeZikl_1 6
#define addr_Save_RotateZikl_2 8
#define addr_Save_TimeZikl_2 10
#define addr_Save_RotateZikl_3 12
#define addr_Save_TimeZikl_3 14
#define Podsvetka 3 //D8 - Дисплей LCD_Led подсветка дисплея
LCD1202 lcd(4, 5, 6, 7); // RST, CS, MOSI, SCK
//~~~~~~~~~~~~~~~~~~~~
// код класса class BUTTON {};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// класс титановый велосипед для тактовой кнопки.
// фильтр дребезга, отслеживание событий: нажатие, отпускание, двойное нажатие(doubleclick), нажато и удерживается в течении определённого времени, отпущено и неактивно в течении определённого времени.
class BUTTON {
public:
//================================================================
static const byte bounce_ = 50; // длительность отслеживания дребезга.
static const byte doubleclick_ = 200; // длительность отслеживания двойного клика.
static const unsigned long timer_ = 3000; // длительность отслеживания неактивности.
static const unsigned int retention_ = 1000; // длительность отслеживания нажатия и удержания.
//================================================================
boolean click_down;
boolean click_up;
boolean doubleclick;
boolean timer;
boolean retention;
//=================================
unsigned long m;
boolean p;
boolean b;
boolean dc;
byte c;
boolean t;
boolean r;
//=================================
byte _pb;
//=================================
BUTTON(byte pb) {
_pb = pb;
pinMode(_pb, INPUT);
digitalWrite(_pb, 1);
//====
click_down = 0;
click_up = 0;
doubleclick = 0;
timer = 0;
retention = 0;
//====
m = millis();
p = digitalRead(_pb);
b = 0;
dc = 0;
c = 0;
t = 0;
r = 0;
//====
}
void read() {
//=======================================================
unsigned long nm = millis();
boolean np = digitalRead(_pb);
//=================
boolean nb = 0;
boolean ndc = 0;
boolean nt = 0;
boolean nr = 0;
//================
click_down = 0;
click_up = 0;
doubleclick = 0;
timer = 0;
retention = 0;
//=================
if (np != p) {
p = np;
m = nm;
}
//=======================================================
if (nm - m > bounce_) {
nb = 1;
}
if (nm - m > doubleclick_) {
ndc = 1;
}
if (ndc != dc) {
dc = ndc;
if (dc == 1) {
c = 0;
}
}
if (nb != b) {
b = nb;
if (p == 0 && b == 0) {
click_down = 1;
++c;
if (c == 2) {
c = 0;
doubleclick = 1;
}
}
if (p == 1 && b == 1) {
click_up = 1;
}
}
//=======================================================
if (nm - m > timer_) {
nt = 1;
}
if (nt != t) {
t = nt;
if (p == 1 && t == 1) {
timer = 1;
}
}
//=======================================================
if (nm - m > retention_) {
nr = 1;
}
if (nr != r) {
r = nr;
if (p == 0 && r == 1) {
retention = 1;
}
}
//=======================================================
}
};
//~~~~~~~~~~~~~~~~~~~~
BUTTON KeyStop(12); // объявляем объект класса BUTTON кнопку с именем KeyStop, подключенную к пину 3.
BUTTON KeySetup(9); // объявляем объект класса BUTTON кнопку с именем KeySetup, подключенную к пину 6.
BUTTON KeyPusk(8); // объявляем объект класса BUTTON кнопку с именем KeyPusk, подключенную к пину 7.
BUTTON KeyPlus(11); // объявляем объект класса BUTTON кнопку с именем KeyPlus, подключенную к пину 5.
BUTTON KeyMinus(10); // объявляем объект класса BUTTON кнопку с именем KeyMinus, подключенную к пину 4.
//======================================================================================
//byte DConf_Shift=0;
byte butt; // переменная последней нажатой кнопки
byte level; // сохранить текущий уровень узла
volatile byte fileNumb=0; // запоминаем номер текущего файла
int workType=0; //используется для блокировки в приложениях. Флаг активности того или иного модуля
volatile byte configLim[4];//используемые фр строковых параметров и текущего значения файла конфигурации
byte brCrumbs[MAXDEPTH][3]; //массив хлебных крошек. Используется для
//навигации, особенно с динамическими и выбор файлов.
//двухмерный 1 байт - номер программы, второй - положение курсора
//==== некоторые буферы =================================================
char buffer[LCDCOL-1]="MenuOS";
char buff[LCDCOL-3];
byte fileData[3]; //используется для хранения информации о текущем файле
//массив конфигов
/* brCrumbs[level][0] - хранить номер последнего выбранного файла. обычно онемевшей из папки файл
brCrumbs[level][1] - хранить текущую позицию списка файлов в папке
brCrumbs[level][2] - Флаг. Используется для считывания количества выбранных файлов*/
/*===================================================================
Массив: fileStruct
Содержит: Файлы различных типов
Каждый файл описывается тремя элементами:
<type>[parameter 1][parameter 2]
type - тип файла.
параметры файла зависят от его типа:
---------------------------------------------------------------------
T_FOLDER - папка, содержит список файлов
parameter 1 - Номер первого файла в массиве
parameter 2 - Количество файлов в папке минус 1
---------------------------------------------------------------------
T_DFOLDER - Динамическая папка. Содержит несколько копий одного файла
parameter 1 - Номер первого файла в массиве
parameter 2 - Количество копий файла
---------------------------------------------------------------------
T_SFOLDER - Папка выбора
parameter 1 - Номер первого файла в массиве
parameter 2 - Количество файлов в папке минус 1
---------------------------------------------------------------------
T_APP - Приложение
parameter 1 - Не используется
parameter 2 -номер вызываемого приложения
---------------------------------------------------------------------
T_CONF - Конфиг
parameter 1 - Не используется
parameter 2 -номер конфига
=====================================================================*/
static uint8_t fileStruct[FILENUMB*FILEREW] PROGMEM =
{
T_FOLDER,1,2, //0 "МЕНЮ" заголовок,стартовая папка. Содержит в себе файлы 1-3. Вложенные файлы должны идти подряд по структуре!
T_DFOLDER,17,3, //1 "Work" Динамическая папка с приложениями. запуск программы работы - выводит 3 копий приложения описанного в строке 11 данного меню
T_FOLDER,4,1, //2 "Settings" - Папка выбора папок конфигов. Содержит файлы с 4 по 5
T_APP,0,1, //3 "Info" - приложение. Выводит системную информацию
T_FOLDER,9,1, //4 "Sensors" - Папка выбора конфигов для датчиков. Содержит в себе варианты с 9 по 10
T_FOLDER,6,2, //5 "Set Zikl" - Папка выбора папок для циклов. Содержит файлы с 6 по 8
T_FOLDER,11,1, //6 "Zikl_1" - Папка выбора конфигов для 1 цикла. Содержит файлы с 11 по 12
T_FOLDER,13,1, //7 "Zikl_2" - Папка выбора конфигов для 2 цикла. Содержит файлы с 11по 12
T_FOLDER,15,1, //8 "Zikl_3" - Папка выбора конфигов для 3 цикла. Содержит файлы с 11 по12
T_CONF,addr_Save_SensVibro,0, //9 "Sensor Vibro" - булев конфиг T_CONF - Конфиг,parameter 1 - сдвиг стартовой ячейки EEPROM относительно номера родительской папки выбора.,parameter 2 -номер конфига=0
T_CONF,addr_Save_SensTok,0, //10 "Sensor Tok" - булев конфиг T_CONF - Конфиг,parameter 1 - сдвиг стартовой ячейки EEPROM относительно номера родительской папки выбора. 2 -номер конфига=0
T_CONF,addr_Save_RotateZikl_1,1, //11 "Rotate 1" - лимитированный конфиг оборотов. Номер конфига - 1.
T_CONF,addr_Save_TimeZikl_1,1, //12 "Time 1" - лимитированный конфиг оборотов. Номер конфига - 1.
T_CONF,addr_Save_RotateZikl_2,1, //13 "Rotate 2" - лимитированный конфиг оборотов. Номер конфига - 1.
T_CONF,addr_Save_TimeZikl_2,1, //14 "Time 2" - лимитированный конфиг оборотов. Номер конфига - 1.
T_CONF,addr_Save_RotateZikl_3,1, //15 "Rotate 3" - лимитированный конфиг оборотов. Номер конфига - 1.
T_CONF,addr_Save_TimeZikl_3,1, //16 "Time 3" - лимитированный конфиг оборотов. Номер конфига - 1.
T_APP,0,2 //17 App # - Приложение №2.
};
/*===================================================================
Массив: fileNames
Содержит: имена файлов. Файлы следуют в том же порядке, что и в предыдущем массиве
=====================================================================*/
prog_char file_0[] PROGMEM = "Меню"; //1 заголовок,стартовая папка
prog_char file_1[] PROGMEM = "Work"; //2 Динамическая папка,запуск программы работы хранит несколько копий одного файла
prog_char file_2[] PROGMEM = "Настрой"; //3 "Settings" - папка конфигов.
prog_char file_3[] PROGMEM = "Info"; //4 "Info" - приложение. Выводит системную информацию
prog_char file_4[] PROGMEM = "Датчики"; //5 "Sensors" - Папка выбора конфигов датчиков
prog_char file_5[] PROGMEM = "Set Zikl"; //6 ""Set Zikl" - Папка выбора папок циклов
prog_char file_6[] PROGMEM = "Цикл 1"; //7"Zikl_1" - Папка выбора конфигов для 1 цикла.
prog_char file_7[] PROGMEM = "Цикл 2"; //8 "Zikl 2" - Папка выбора конфигов для 2 цикла.
prog_char file_8[] PROGMEM = "Цикл 3"; //9 "Zikl 3" - Папка выбора конфигов для 3 цикла.
prog_char file_9[] PROGMEM = "Sensor Vibro"; //8 "Sensor Vibro" - булев конфиг. Номер конфига - 0
prog_char file_10[] PROGMEM = "Sensor Tok"; //9 "Sensor Tok" - булев конфиг. Номер конфига - 1
prog_char file_11[] PROGMEM = "Обороты"; //10 ""Rotate" - лимитированный конфиг оборотов
prog_char file_12[] PROGMEM = "Время"; //11 "Time" - лимитированный конфиг оборотов
prog_char file_13[] PROGMEM = "Обороты"; //12 ""Rotate" - лимитированный конфиг оборотов
prog_char file_14[] PROGMEM = "Время"; //13 "Time" - лимитированный конфиг оборотов
prog_char file_15[] PROGMEM = "Обороты"; //14 ""Rotate" - лимитированный конфиг оборотов
prog_char file_16[] PROGMEM = "Время"; //15 "Time" - лимитированный конфиг оборотов
prog_char file_17[] PROGMEM = "App #"; //16 "App #" - App # - Приложение №2.
PROGMEM const char *fileNames[] = {
file_0,//указатель на "МЕНЮ" заголовок,стартовая папка
file_1,//указатель на "Work" Динамическая папка,запуск программы работы
file_2,//указатель на "Settings" - папка конфигов.
file_3,//указатель на "Info" - приложение которое Выводит системную информацию
file_4,//указатель на "Sensors" - папка конфигов.
file_5,//указатель на "Set Zikl" - Папка выбора папок циклов
file_6,//указатель на "Zikl_1" - Папка выбора конфигов для 1 цикла.
file_7,//указатель на "Zikl_2" - Папка выбора конфигов для 2 цикла.
file_8,//указатель на "Zikl_3" - Папка выбора конфигов для 3 цикла.
file_9,//указатель на "Sensor Vibro" - булев конфиг.
file_10,//указатель на "Sensor Tok" - булев конфиг.
file_11,//указатель на "Rotate1" -лимитированный конфиг оборотов
file_12,//указатель на "Time1" -лимитированный конфиг времени
file_13,//указатель на "Rotate2" -лимитированный конфиг оборотов
file_14,//указатель на "Time2" -лимитированный конфиг времени
file_15,//указатель на "Rotate3" -лимитированный конфиг оборотов
file_16,//указатель на "Time3" -лимитированный конфиг времени
file_17 //указатель на "App #" - App # - Приложение №2.
};
/*===================================================================
Массив: configsLimit
Содержит:Параметры конфигов:
булев, только Да/Нет
Числовой, С ограничением или бел.
Каждый конфиг описывается тремя значениями:
1. Минимальное значение
2. Максимальное значение
3. Номер ячейки EEPROM
Если максимальное значение ==0 - Это безлимитный конфиг
Если максимальное значение == 1 -Это булев конфиг
Иначе это лимитированный конфиг, как сверху так и снизу.
Учитывайте, что безлимитный конфиг занимает две ячейки EEPROM, а динамический конфиг занимает, для
безлимитного конфига = 2 ячейки EEPROM на количество копий динамического конфига
Для лимитированного конфига = 1 йчейка EEPROM на количество копий динамического конфига
=====================================================================*/
PROGMEM static uint8_t configsLimit[] = {
0,1,0, // 0 Булев конфиг. Да/Нет
10,CONF_LIM_MAX,1, // 1 Лимитированный конфиг. Числовое значение может быть только между 10 и 255
0,0,2, // 2 нелимитированный конфиг.
0,0,20 // 3 конфиг для динамической папки.
};
void setup()
{
Serial.begin (9600);
lcd.Inicialize(); //Инициализация дисплея
lcd.Clear_LCD(); //Очистка дисплея
Podsvetka_ON(); //включим подсветку дисплея
Priwetstwie(); // выведем приветствие
delay(1000);
lcd.Clear_LCD(); //Очистка диспле
fileNumb=0;
fileGet(fileNumb); // Получение данных о файле
pageList();
lcd.Update(); //Обновление экрана, загрузка информации на дисплей из буфера
}
void loop()
{
butt=buttonsStat();
if (butt)
{
lcd.Clear_LCD(); //Очистка дисплея
dispStaticDraw(); // Эта функция обрисовывает экран
dispHead(buffer); // функция вывода сообщения в шапку дисплея
menuButtons(butt); //опрос кнопок
lcd.Update(); //Обновление экрана, загрузка информации на дисплей из буфера
}
}
/*=============================================================================
Общий для всех типов файлов обработчик кнопок. Возвращает номер нажатой кнопки
=============================================================================
Функция опрашивает порт с кнопками и возвращает номер нажатой кнопки.*/
//----- обработчики кнопок меню -------------------------
void menuButtons(uint8_t but)//Обработчик кнопок в меню
{
switch (but)
{
case 5://<
fileReturn();
break;
case 1://<
fileReturn();
break;
case 2://- и все что нам надо - изменить положение курсора :)
if (brCrumbs[level][1]>0) brCrumbs[level][1]--;
else brCrumbs[level][1]=fileData[2];//дошли до начала, начали с конца.
pageList();//обрисовываем экран только когда это требуется
break;
case 3://+ тут то же самое
if (brCrumbs[level][1]<fileData[2]) brCrumbs[level][1]++;
else brCrumbs[level][1]=0;//не дошли еще до конца? тогда стремимся к нему. Дошли? класс, начинаем сначала
pageList();
break;
case 4://>
fileSelect();
break;
}
delay(200);
}
//========================================================
/*=============================================================================
Общий для всех типов файлов обработчик кнопок. Возвращает номер нажатой кнопки
=============================================================================*/
byte buttonsStat()
{
butt=0;
KeyStop.read(); // обновляем состояние переменных кнопки KeyStop.
KeySetup.read(); // обновляем состояние переменных кнопки KeySetup
KeyPlus.read(); // обновляем состояние переменных кнопки KeyPlus
KeyMinus.read(); // обновляем состояние переменных кнопки KeyMinus
KeyPusk.read(); // обновляем состояние переменных кнопки KeySetup
if (KeyPlus.click_down) { //если нажимали кнопку KeyPlus
click_down_KeyPlus(); // внутри функции увеличиваем значение ( M_1_Par_1) пунктов меню вверх
}
if (KeyMinus.click_down) { //если нажимали кнопку KeyMinus
click_down_KeyMinus(); // внутри функции уменьшаем значение ( M_1_Par_1 )пунктов меню вниз
}
if (KeyStop.click_down) { //если нажимали кнопку KeyStop
click_down_KeyStop(); // внутри функции обнуляем кучу флагов и выход в предыдущее меню
}
if (KeySetup.click_down) { //если нажимали кнопку сет
click_down_KeySetup(); // внутри функции ставим флаг setup,а флаг rabota обнуляем
}
if (KeyPusk.click_down) { //если нажимали кнопку сет
click_down_KeyPusk(); // внутри функции ставим флаг rabota ,а флаг setup обнуляем
}
/* if (KeyPusk.retention) { //если нажимали кнопку сет
retention_KeyPusk();
}*/
return butt;
}
//========================================================================
// ----- KeySetup -----------
void click_down_KeySetup() {
// tone (SoundPin, 3000, 50); // "Бип" при нажатии кнопки.
butt=1; //кнопка <
}
//================================================================================
// ----- KeyPlus -----------
void click_down_KeyPlus() {
// tone (SoundPin, 3000, 50); // "Бип" при нажатии кнопки.
butt=3;//кнопка +
}
//===================================================================================
// ----- KeyMinus -----------
void click_down_KeyMinus() {
// tone (SoundPin, 3000, 50); // "Бип" при нажатии кнопки.
butt=2;//кнопка -
}
//=====================================================================================
// ----- KeyStop -----------
void click_down_KeyStop() {
//tone (SoundPin, 3000, 50); // "Бип" при нажатии кнопки.
butt=4;//кнопка >
}
//========================================================================================
// ----- KeyPusk -----------
void click_down_KeyPusk() {
// tone (SoundPin, 3000, 50); // "Бип" при нажатии кнопки.
butt = 5;
}
/*void retention_KeyPusk() {
// fSave=1;
}*/
//=============================================================================
/*uint8_t buttonsStat()
{
uint8_t button=(~BUTTONSPORT)&BUTTONSMASK;
return button;
}*/
void ConfigUpButton()
{
if (configLim[1]==0)
{//значит что без ограничений
configLim[3]++;
}
else if (configLim[1]==1)
{//значит что вырианты только да или нет
configLim[3]=!configLim[3];
}
else{//изменяющееся:
if (configLim[3]<configLim[1]) configLim[3]++;
else configLim[3]=configLim[0];
}
}
void ConfigDownButton()
{
if (configLim[1]==0)
{//значит что без ограничений
if (configLim[3]>0) configLim[3]--;
else configLim[3]=0;
}
else if (configLim[1]==1)
{//значит что вырианты только да или нет
configLim[3]=!configLim[3];
}
else{//изменяющееся:
if (configLim[3]>configLim[0]) configLim[3]--;
else configLim[3]=configLim[1];
}
}
//================================================================================
/* Эта функция обрисовывает экран
В качестве входного параметра - определенная комбинация кнопок. Как хотите. так и выводите как говорится.*/
void dispStaticDraw() //Рисует сепараторы, и кнопки. общая функция для всех модулей.
{
lcd.drawFastHLine(0, 9, 96, 1); // Выводим по координатам x, y, горизонтальную линию, длиной 96 пикселей, черным цветом
lcd.drawFastHLine(0, 55, 96, 1); // Выводим по координатам x, y, горизонтальную линию, длиной 96 пикселей, черным цветом
lcd.fillRect(0,57,96,11,1); // подсветка кнопок
lcd.drawString(1, 59, 0, " < - + > "); // кнопки
for (int i=1;i<4;i++){ // рисуем перемычки в прямоугольнике (разделяем кнопки)
lcd.drawFastVLine(24*i, 57, 11, 0); } // Выводим по координатам x, y, вертикальную линию, высотой 16 пикселей, черным цветом
}
//***************************************************************
/*-- функция вывода сообщения в шапку дисплея --*/
void dispHead(char head[LCDCOL])
{
lcd.fillRect(0, 0, 96, 8,0); // затираем область вывода сообщения в шапку дисплея
lcd.setCursor_drawString_1607(1, 0, 1, head); // выводим сообщение
}
//**************************************************************************
/* функция вывода курсора х,y-координаты в пикселах,color - цвет курсора 0-просто квадрат, 1- закрашеный квадрат */
void DispCursor( byte x, byte y,boolean color)
{
if(color==0) lcd.drawRect(x,y,5,3,1);
else lcd.fillRect(x,y,5,5,color);
}
//***** Работа с графикой **********************************************************
//функция подсвечивает строку
void stringSelect()
{
uint8_t curst=brCrumbs[level][1]&LCDBITS; // выкорчевываем лишнюю инфу. чем хреново? кол-во строк должно быть кратно 2 :)
lcd.fillRect(2,12+(10*curst),5,3,1); // рисуем прямоугольничек динамический курсор
}
//===================================================
/* Эта функция выводит строку в одну из 4 строк дисплея. */
/*byte str - номер стоки, char textstr[16]- надпись */
void dispString(byte str,char textstr[LCDCOL])
{
if ((str>=0) && (str<DISPSTR)){ // ограничим область работы
lcd.setCursor_drawString_1607(2, str+1, 1,textstr); // переходим в область вывода текста и пишем
}
}
//=========== ЭКРАН ПРИВЕТСТВИЯ =================================
void Priwetstwie()
{//***** рисуем рамку по периметру ***************************
lcd.drawRect(0, 0, 96, 68, 1); // Рисуем прямоугольник по координатам x, y, высота, ширина, цвет
//***** выводим текст ****************************************
lcd.drawString(20, 1, 1, "регулятор"); // выводим толстую надпись
lcd.drawString(23, 10, 1, "оборотов"); // выводим толстую надпись
lcd.drawString(20, 20, 1, "медогонки"); // выводим толстую надпись
lcd.drawString(30, 35, 1, "ПЧЕЛКА"); // выводим толстую надпись
lcd.drawString(25, 50, 1, "v - 1.07"); // выводим толстую надпись
lcd.drawString(20, 58, 1, "lcd 1202"); // выводим толстую надпись
lcd.Update(); // Обновление экрана, загрузка информации на дисплей из буфера
}
//=========== ПОДСВЕТКА ДИСПЛЕЯ ===============================
void Podsvetka_ON()
{
pinMode(Podsvetka,OUTPUT);
digitalWrite(Podsvetka,HIGH); // включили
}
//********************************************************
void Podsvetka_OFF()
{
digitalWrite(Podsvetka,LOW); // выключили
}
//========== Чтение из ЕЕПРОМ ====================================
inline byte ConfigRead(unsigned int _param1){
return EEPROM.read(_param1);
}
//========== Сохранение в ЕЕПРОМ =================================
inline void ConfigWrite(unsigned int _param1, byte _param2){
EEPROM.write (_param1, _param2);
}
//=======================================================
//обработчики типов файлов
void programSelect()//тип файла 0x01
{
byte Sensor;
char buffString[4]; //объявляем локальный массив
lcd.Clear_LCD(); // Очистка дисплея
workType=1;//включили зацикливание программы
dispStaticDraw();//Обрисовываем экран
switch (fileData[2])
{
case 1://Звывод инфо о программе
dispHead("System info");
Sensor=ConfigRead(addr_Save_SensVibro);
dispString(0,"Вибрация");
if(Sensor==0) lcd.setCursor_drawString_1607(12, 1, 1,"Выкл"); // переходим в область вывода текста и пишем
else lcd.setCursor_drawString_1607(12, 1, 1,"Вкл "); // переходим в область вывода текста и пишем
Sensor=ConfigRead(addr_Save_SensTok);
dispString(1,"Ток");
if(Sensor==0) lcd.setCursor_drawString_1607(12, 2, 1,"Выкл"); // переходим в область вывода текста и пишем
else lcd.setCursor_drawString_1607(12, 2, 1,"Вкл "); // переходим в область вывода текста и пишем
dispString(2,"Обороты");
lcd.setCursor_drawString_1607(12, 3, 1,utoa(CONF_LIM_MAX, buffString, 10)); // переходим в область вывода текста и пишем
dispString(3,"Время");
lcd.setCursor_drawString_1607(12, 4, 1,utoa(CONF_LIM_MAX, buffString, 10)); // переходим в область вывода текста и пишем
lcd.Update(); //Обновление экрана, загрузка информации на дисплей из буфера
delay(3000);
workType=0;
break;
case 2://Звывод инфо о программе
Programm1();
break;
default:
break;
}
lcd.Clear_LCD(); // Очистка дисплея
fileReturn();
}
void Programm1(){
//Serial.println("Enter Programm1");
lcd.Clear_LCD();
DispCursor(0,0,1);//ставим курсор
lcd.setCursor_drawString_1607(2,0,1,"App #"); // переходим в область вывода текста и пишем
lcd.setCursor_drawString_1607(8,0, 1,utoa((brCrumbs[level-1][1]), buff, 10)); // пишем в строку значение
while (workType){
//Serial.println("while workType");
butt=buttonsStat();
if (butt){//если обновили информацию
switch (butt)
{
case 2:
//Serial.println("case 2 butt");
//Serial.println(butt);
break;
case 3:
//Serial.println("case 3 butt");
//Serial.println(butt);
break;
case 5:
//Serial.println("case 5 butt");
//Serial.println(butt);
workType=0;
break;
}
}
}
//Serial.println("Exit Programm1");
// delay(1000);
}
//=========== чтение данных о конфиге
void ConfigReadLimits(byte cnumb)//данные о конфиге. Входной парамтер - номер конфига СИСТЕМНЫЙ
{
for (int i=0;i<3;i++)
configLim[i]=pgm_read_byte(&configsLimit[cnumb*3+i]);//Считываем числовые значения
}
//=========== выбор конфига ============================
/*
#define MAXDEPTH 4 // максимальная глубина внутренного раздела
byte brCrumbs[MAXDEPTH][3]; //массив хлебных крошек. Используется для
//навигации, особенно с динамическими и выбор файлов.
//двухмерный 1 байт - номер программы, второй - положение курсора
brCrumbs[level][0] - хранить номер последнего выбранного файла. обычно онемевшей из папки файл
brCrumbs[level][1] - хранить текущую позицию списка файлов в папке
brCrumbs[level][2] - Флаг. Используется для считывания количества выбранных файлов
*/
void configSelect(byte cnumb, byte conftype)//тип файла 0x02
{
byte Conf_Shift=0;
workType=2; //Флаг активности модуля
ConfigReadLimits(cnumb); //читаем данные о конфиге. Входной парамтер - номер конфига
if (conftype==Conf)
{
Conf_Shift=brCrumbs[level-1][1]; //
}
if (configLim[1]==0)// если безлимитный конфиг
configLim[3]=(ConfigRead(fileData[1]))*256+ConfigRead(configLim[2]+1+Conf_Shift*2);//прочитали 2 байта.-809856yfg
else // иначе для лимит конфига = 1 йчейка EEPROM на количество копий динамического конфига
Serial.println("ConfigRead адрес");
Serial.println(fileData[1]);
configLim[3]=ConfigRead(fileData[1]);
Serial.println("ConfigRead data");
Serial.println(configLim[3]);
ConfigDispSetup(conftype);
while (workType==2)
{//запускаем программу редактирования конфигов
ConfigDispStatic();
butt=buttonsStat(); //читаем кнопки
if (butt){ //если обновили информацию
switch (butt) // если нажали
{
case 3: //кнопка +
ConfigUpButton(); // увеличиваем
break;
case 2: //кнопка -
ConfigDownButton();// уменьшаем
break;
case 5: //кнопка пуск выход и сохранение конфига в еепроме
if (conftype==Conf)//если конфиг
{
if (configLim[1]==0)// если Это безлимитный конфиг то занимает две ячейки EEPROM
{
ConfigWrite(configLim[2]+Conf_Shift*2,highByte(configLim[3]));//записываем старший байт
ConfigWrite(configLim[2]+Conf_Shift*2+1,lowByte(configLim[3]));//записываем младший байт
}
else //1 ячейка EEPROM на количество копий конфига
{
ConfigWrite(fileData[1],configLim[3]);//записываем 1 байт
Serial.println("ConfigWrite addr");
Serial.println(fileData[1]);
Serial.println("ConfigWrite data");
Serial.println(configLim[3]);
}
}
// выводим надпись "сохранено" в строке заголовка
DispCursor(0,3,1);//ставим курсор
lcd.setCursor_drawString_1607(1, 0, 1,"Сохранено"); // переходим в область вывода текста и пишем
lcd.Update(); //Обновление экрана, загрузка информации на дисплей из буфера
delay(1000); // чтобы успеть прочитать
workType=0;//сбросим флаг зацикливания
break;
}
}
lcd.Update(); //Обновление экрана, загрузка информации на дисплей из буфера
}
fileReturn();
}
//========================================
void ConfigDispStatic()
{
char buffString[4]; //объявляем локальный массив который будет использоваться для преобразования значения конфига в строку для вывода
switch (configLim[1]){// определяем по 2 байту какой конфиг- с огранич,без огранич,булев?
case 0://значит что без ограничений
lcd.setCursor_drawString_1607(0,ROWSHIFT, 1," "); // пишем в строку пробел- затираем всю строку
dispString(1,"Значение:"); // пишем в 2 строку
DispCursor(3,21,1); //ставим курсор
lcd.setCursor_drawString_1607(12, ROWSHIFT,1,utoa(configLim[3], buffString, 10)); //DispNumber(configLim[3]);
// if (fileData[1]==10)//if (fileData[1]==10) -10 "Sensor Tok" - булев конфиг
// {
// lcd.drawChar(25, 85, 1, 'A');
// //lcd.setCursor_drawString_1607(15,ROWSHIFT, 1,"A"); // переходим в область вывода текста и пишем // выводим "Да "
// }
break;
case 1://значит что вырианты только да или нет
dispString(1,"Включить:"); // пишем в 2 строку
DispCursor(3,21,1);
//возможность подключить или отключить датчик определенный булевым конфигом
//если подключаем датчик то переводим конфиг из булева в лимитированный
// что бы конфиг стал опять булевым нужно просто значение установить в 0
if (configLim[3])
{
// если датчик подключен сделать конфиг лимитированым
configLim[1]=CONF_MAXLIMIT_BOOL_TO_LIM;//делаем конфиг лимитным что бы ввести зачение тока двигателя или вибрации
lcd.setCursor_drawString_1607(12,ROWSHIFT, 1,"Да "); // переходим в область вывода текста и пишем // выводим "Да "
dispString(2,"Датчик включен"); // переходим в область вывода текста и пишем // выводим "Да
lcd.Update(); //Обновление экрана, загрузка информации на дисплей из буфера
delay(2000);
}
else
{
lcd.setCursor_drawString_1607(12,ROWSHIFT, 1,"Нет");// выводим "Нет"
}
break;
default: // если лимитированный конфиг
dispString(1," "); // пишем в строку пробел- затираем всю строку
DispCursor(3,21,1); //ставим курсор
dispString(1,"Значение:"); // пишем в 2 строку
lcd.setCursor_drawString_1607(12, ROWSHIFT,1,utoa(configLim[3], buffString, 10));
dispString(2," "); // пишем в строку пробел- затираем всю строку
dispString(2,"Минимум:"); // пишем в 3 строку
lcd.setCursor_drawString_1607(12, ROWSHIFT+1,1,utoa(configLim[0], buffString, 10));
dispString(3," "); // пишем в строку пробел- затираем всю строку
dispString(3,"Максимум:"); // пишем в 4 строку
lcd.setCursor_drawString_1607(12, ROWSHIFT+2,1,utoa(configLim[1], buffString, 10));
break;
}
}
//*********************************
void ConfigDispSetup(uint8_t conftype)
{
dispStaticDraw(); // выводим изображение кнопок
dispHead("Конфиг"); //пишем заголовок
dispString(0,buffer); //пишем 1 строку название конфига
ConfigDispStatic();
}
//=========== Получение данных о файле ======================
void fileGet(byte fnumb)//данные о файле. Входной параметр - номер файла СИСТЕМНЫЙ
{
for (uint8_t i=0;i<FILEREW;i++)
{
fileData[i]=pgm_read_byte(&fileStruct[FILEREW*fnumb+i]);//Считываем числовые значения
/* // отладка смотрим что у нас в массиве
switch(i)
{
case(0):
Serial.println("fileData[0]");
if (fileData[0]==0)
{
Serial.println("Folder");
}
if (fileData[0]==2)
{
Serial.println("Dyn Folder" ) ;
}
if (fileData[0]==3)
{
Serial.println("select Folder");
}
if (fileData[0]==4)
{
Serial.println("T_APP");
}
if (fileData[0]==5)
{
Serial.println("CONFIG ");
}
if (fileData[0]==6)
{
Serial.println("Select CONFIG");
}
if (fileData[0]==7)
{
Serial.println("Dyn CONFIG");
}
break;
case(1):
Serial.println("fileData[1]");
Serial.println(fileData[1]);
break;
case(2):
Serial.println("fileData[2]");
Serial.println(fileData[2]);
break;
case(3):
Serial.println("fileData[3]");
Serial.println(fileData[3]);
break;
}*/
}
strcpy_P(buffer, (char*)pgm_read_word(&(fileNames[fnumb])));//Считываем название
}
//=================================================
void fileReturn()
{
lcd.Clear_LCD(); //Очистка дисплея
//во первых, удалим текущую информацию о местоположении:
brCrumbs[level][0]=0;
brCrumbs[level][1]=0;
brCrumbs[level][2]=0;
//вернемся на уровень назад, только если он еще больше нуля. А то уйдем еще в минуса.
if (level>0) level--; //находясь на уровне выше, уже можно прочесть инфу о родителе:
fileNumb=brCrumbs[level][0];//при этом положение курсора не сбрасывается! мы вернемся туда, откуда пришли.
fileGet(fileNumb); //Читаем инфу о файле.
pageList();
}
/*
brCrumbs[level][0] - хранить номер последнего выбранного файла. обычно онемевшей из папки файл
brCrumbs[level][1] - хранить текущую позицию списка файлов в папке
brCrumbs[level][2] - Флаг. Используется для считывания количества выбранных файлов
*/
void fileSelect()//вызывается в обработчике кнопок меню при нажатии >
{
//вычисляем новый номер файла. Для этого смотрим, мы в обычной папке. или динамической?
if (!fileData[0])//обычная папка. В ней все больно просто
{
fileNumb=fileData[1]+brCrumbs[level][1];//берем номер стартового файла и прибавляем текущую позицию списка файлов в папке (сколько мы вниз нащелкали.)
}
else //динамическая папка тут все еще проще
{
fileNumb=fileData[1];//ЖЖОШ!
}
level++;//на уровень глубже
brCrumbs[level][0]=fileNumb;//вошли внутрь? супер, запишем инфу о нашем местоположении.номер последнего выбранного файла
brCrumbs[level][1]=0;//текущую позицию списка файлов в папке =0
brCrumbs[level][2]=0;//количества выбранных файлов
fileGet(fileNumb);//считываем информацию
byte fileType=fileData[0];//Читаем 1 байт файла
switch (fileType)//сюда добавляем в зависимости от типа файла.
{
case T_FOLDER:
pageList();
break;
case T_APP:
programSelect();
break;
case T_CONF:
configSelect(fileData[2],Conf);
break;
case T_DFOLDER:
pageList();
break;
case T_SFOLDER:
pageList();
break;
}
}
void pageList()
{
dispStaticDraw(); //рисуем статику
char buff[LCDCOL]; // объявляем массив размером ((LCDCOL=16 Максимальное количество символов в строке)-5)=11
char buff2[LCDCOL]; // объявляем массив размером ((LCDCOL=16 Максимальное количество символов в строке)-3)=13
uint8_t currs; // локальная переменная
uint8_t fstart; // локальная переменная
switch (fileData[0]){ //перебираем
case T_FOLDER://обычная папка..
if ((brCrumbs[level][1]&LCDINVBITS)==((fileData[2])&LCDINVBITS)) currs=(fileData[2])&LCDBITS;//Это максимальное положение курсора на последней странице
/*brCrumbs[level][х] - массив хлебных крошек. двухмерный 1 байт "level" - номер программы,
2 байт "х" - положение курсора */
else currs=LCDBITS;//currs=3;
fstart=fileData[1]+(brCrumbs[level][1]&LCDINVBITS);//номер файла для старта вывода.
for (uint8_t i=0;i<=currs;i++)//пишем из массива для статических папок
{
strcpy_P(buff, (char*)pgm_read_word(&(fileNames[i+fstart])));//прочитали имя
dispString(i,buff);//пишем из массива
}
break;
case T_DFOLDER://динамическая папка
if ((brCrumbs[level][1]&LCDINVBITS)==((fileData[2])&LCDINVBITS)) currs=(fileData[2])&LCDBITS;//Это максимальное положение курсора на последней странице
else currs=LCDBITS;
strcpy_P(buff, (char*)pgm_read_word(&(fileNames[fileData[1]])));//читаем общее название
for (uint8_t i=0;i<=currs;i++)
{
sprintf(buff2, "%s %d", buff, (brCrumbs[level][1]&LCDINVBITS)+i);
dispString(i,buff2);
}
break;
case T_SFOLDER://папка выбора параметра
uint8_t config_folder_view = pgm_read_byte(&fileStruct[FILEREW*fileData[1]+1]);//читаем байт сдвига.
uint8_t config_folder_data = ConfigRead(brCrumbs[level][0]+config_folder_view);//чтение значения выбора
if (config_folder_data>fileData[2]) config_folder_data=fileData[2];
if (config_folder_view<0) config_folder_view=0;
if (brCrumbs[level][2]==0){
brCrumbs[level][1]=config_folder_data;//ставим курсор в выбранную позицию.
brCrumbs[level][2]=1;
}
if ((brCrumbs[level][1]&LCDINVBITS)==((fileData[2])&LCDINVBITS)) currs=(fileData[2])&LCDBITS;//Это максимальное положение курсора на последней странице
else currs=LCDBITS;
fstart=fileData[1]+(brCrumbs[level][1]&LCDINVBITS);//номер файла для старта вывода.
for (byte i=0;i<=currs;i++)//пишем из массива для статических папок
{
strcpy_P(buff, (char*)pgm_read_word(&(fileNames[i+fstart])));//прочитали имя
dispString(i,buff);//пишем из массива
}
break;
}
dispHead(buffer);
stringSelect();
}
код уменьшился на 4 байта,значит точно кодировки мать их, но символы налазят друг на друга,хотя в функции как я понял должен быть пробел в 1 пиксел ,поправте если ошибаюсь.
//========= Нарисовать букву ======================================================================
void LCD1202::drawChar(byte x, byte y, boolean color, unsigned char c) {
..........
for (byte i=0; i<6; i++ ) {
byte line;
(i == 5)? line = 0x0 : line = pgm(font+(c*5)+i);
for (byte j = 0; j<8; j++) {
(line & 0x1)? drawPixel(x+i, y+j, color) : drawPixel(x+i, y+j, !color);
line >>= 1;
}
}
}
Для начала тип с плавающей запятой нужно преобразовать в целочисленный какой угодно - int16_t , uint16_t , int32_t , uint32_t ну так далее.при этом предварительно (на примере 1,43%) умножить на 100 чтоб получилось 143. теперь можно печатать учитывая количество знаков и нули.
float f = 1.43;
int i = 100 * f; // i равна 143
lcd.Print (i/100); // i на 100 не делим, а просто печатаем без сотых долей "1"
lcd.Print(",");
if(i%100 < 10){ // если сотые равно меньше десяти
lcd.Print( 0 ); // печатаем ноль
}
lcd.Print(i%100); //печатаем сотые доли "43"
Для начала тип с плавающей запятой нужно преобразовать в целочисленный какой угодно - int16_t , uint16_t , int32_t , uint32_t ну так далее.при этом предварительно (на примере 1,43%) умножить на 100 чтоб получилось 143. теперь можно печатать учитывая количество знаков и нули.
float f = 1.43;
int i = 100 * f; // i равна 143
lcd.Print (i/100); // i на 100 не делим, а просто печатаем без сотых долей "1"
lcd.Print(",");
if(i%100 < 10){ // если сотые равно меньше десяти
lcd.Print( 0 ); // печатаем ноль
}
lcd.Print(i%100); //печатаем сотые доли "43"
Это я видел. Знаю. Но в библе gres. Для каждого вывода нужно указывать место курсору
Ну тогда притензии к автору бибы. Я от таких проблем в своей избавился.
Дело в том что не только у каждого пользователя свои требования к библиотеке но и у каждого проэкта свои требования к ней, и вем не угодишь и всего не учтёшь.
ну хорошо. допустим не нужно указывать место курсору. а где тогда он должен выводить? продолжать в строке?
и обратный случай: нужно вывести на 2 пикселя левее и на 5 ниже относительно существующего, например дробь захотелось сделать, как это сделать без указания?
а насчет вывода без преобразования надо смотреть как сделано у hugoboss317, но это в том случае если реально нужно и не лень этим заниматься
ну хорошо. допустим не нужно указывать место курсору. а где тогда он должен выводить? продолжать в строке?
не хочу казаться назойлевым но я решил это так:
Обьявляем место откуда начинаем печатать символы, а далее курсор передвигается сам после каждого символа на его ширину плюс 1 пиксел. Когда заканчивается строка, курсор переходин на строку ниже на высоту символа плюс пиксел. если нужно поднять, опустить или передвинуть на пару тройку четвёрку пикселов или вобще в другое место, просто указываем место вызовом этой функции.
НЕ утверждаю что именно так правильно и только так и надо, но решил что так удобнее.
Меня по сути то устраивает библотека которая в Я.Д. у Jeka_tm лежит "Nokia1100_Library". Но у меня почему то она плохо ведет себя с ресетом. Более менее если ресет кинуть на 3.3в. Но если в питании какой нибудь скачек то диисплей почему то больше не кажет. А с остальными библиотеками все нормально... Может знаете почему такие артефакты происходят?
Добрый всем вечер. Кто - нибудь пробовал масштабировать шрифты? Хранить в памяти шрифты с большими буквами накладно. А если взять, скажем шрифт 5x8 и увеличить его в 4 раза? На просторах интернета я нашел что - то подобное:
//***************************************************************
void LCD_draw_string_4X(const char* text, uint8_t xpos, uint8_t ypos){
for (unsigned char i = 0; text[i]; i++){ //
LCD_draw_char_4X(text[i], xpos + i * 18, ypos);}} //18 - расстояние между символами.
//***************************************************************
void LCD_draw_char_4X(char code, char xpos, char ypos){ //
uint8_t d; //
for (int b = 0; b < 4; b++){ //
GotoXY(xpos, ypos + b); //
for (int i = 0; i < 6; i++){ //
if (i == 5) {d = 0;} //Шестой элемент всегда был 0 (в родной таблице)
else {d = (pgm_read_byte(&Font[code-0x20][i]) >> (b * 2));}
uint8_t m = 0; //
if (d & 0x01) m |= 0x0F; //
if (d & 0x02) m |= 0xF0; //
for (int p = 0; p < 3; p++) {Lcd_Write(DATA, m);}
} //for (int i = 0; i < 6; i++)
} //for (int b = 0; b < 4; b++)
} //void LCD_draw_char_4X(char code, uint8_t xpos, uint8_t ypos)
//***************************************************************
Как это работает после строки pgm_read_byte(&Font[code-0x20][i]) понять мне пока сложно. Может кто из местных гуру разберет. Мне бы хотелось применить масштабирование 6х и 8х. Но уже с масштабом 4х символы получаются болше чем 16х32.
Шрифт полностью не привожу, он стандартный с набором русских букв:
Читаем байт из программной памяти, читаем бит, если "1" стивим пиксел, если "0", едим дальше, это без саштаба, с машбабом ставим не пиксел а квадрат размером с маштаб. только переежаем дальше не на пиксел а на тот же размер мастаба
Спасибо. Для меня в этом коде много избыточного. Где - то даже похожую конструкцию видал:
if((pgm_read_byte(&FontPointer[(ch - FONT_START)* (FONT_WIDTH * (type/8)) + qbit + index / (1+widht) + FONT_WIDTH*(j/8)])) & _BV(j%8)){ //сам не знаю как это написал :D
Кажетс для STM32 что - то. Смутил только макрос _BV(j%8). Как будет без него? Если нетрудно, приведите пожалуйста код LcdFillRect, а я если получится обязательно выложу.
Да нет как таковой библиотеки - то. Вывожу шрифт 5*8 адаптированным под себя кодом из просторов Сети:
void Putc(unsigned char c){ //
if (c>127) c=c-64; //переносим символы кирилицы в кодировке CP1251 в начало второй
for ( unsigned char i = 0; i < 5; i++ ){// половины таблицы ASCII (начиная с кода 0x80)
unsigned char glyph = pgm_read_byte(&(Font[c-0x20][i]));
Lcd_Write(DATA,glyph); //
if (Width) {Lcd_Write(DATA,glyph);}}//
Lcd_Write(DATA,0x00); //зазор между символами по горизонтали в 1 пиксель
//Lcd_Write(DATA,0x00); //можно сделать две линии
}
Шрифт 5х8 обычный, от туда же. Кусок этого шрифта я привел в посту 323. Этот же шрифт использую как входной в программе масштабирования х4 (изначально там был входной шрифт 6*8, но шестой байт был всегда нулевым). Саму программу масштабирования я привел в посту 323. Все остальное - стандартно. Вывод строки, подмена printf.
Но LCD_draw_char_4X для меня слишком запутанна, я хотел изменить масштаб 2,4,6,8. Собственно, с этой идеей и обратился сюда.
А вообще, я конечно понимаю, что тема большая, но тут всё уже давно раскрыто. Вы, вероятно не понимаете как именно происходит вывод данных на экран. Почитайте с начала, станет понятней.
Ну и десерт. Вы используетет контроллер с флэшом 128 кБ и не хватает места для хорошей библиотеки, которую, к примеру, обсуждают в начале темы?
Ковырял код - уперся в функцию LcdPixel. Там работа со страницами, цветом пикселя - черт ногу сломит. Как все это потом попадает в контроллер я так и не понял. В функции LcdPageTWO кажется все это скидывается в контроллер, но из каких - то массивов в RAM. Зачем так сложно?
В заголовке указан список контроллеров
LCD Nokia 1280, 1202, 1203, 1100 96x68 (96x64 for 1100) by H`UGO
А Вы хотите на монохромном дисплее выводить цвет? :) Это то же что по микноволновке телевизор смотреть.
А если серьёзно..... Зачем Вы там пытаетесь разобряться в библиотеке? Нужно свего лишь понять как правильно выводить данные на экран. Я в очередной раз повторю что все тонкости этого экрана мы уже тут обсудили и с ними разобрались, Вы просто не хотите читать, а я не хочу десятый раз повторяться, хотя кажется придётся.
Все выводимые данные должны быть внутри "страниц"
LcdPageONE(); // начало вывода
do{
...... // тут выводим
...... // ещё выводим
...... // и тут можно
}
while (LcdPageTWO()); // окончили вывод
Внутри этих же страниц задержку не делаем (delay();)
Выводимые данные там не меняем
int a = 1;
LcdPageONE();
do{
LcdsetFont(font_5x8);
LcdGotoXY(0, 5);
LcdPrint(a, ON, 1)
a++; // вот тут нельзя
}
while (LcdPageTWO());
a++; // а тут можно
Получается что Вы пытались на микроволновку вывести изображение...
А если серьезно - подо что все это писалось? На перечисленных в хидере дисплеях цвета нет.
В том то и дело что не поняв как отправляются данные на контроллер невозможно разобраться как правильно их выводить на экран. Ок. Грузить не буду. Буду курить тему с нуля.
Может о разном говорим? Я по ссылке в 204 посту скачал в частности PCF8814_new.cpp. Функция
void LcdChar(int8_t x, int8_t y, unsigned char ch, uint8_t color, uint8_t size)
имеет параметр uint8_t color и обращается к LcdPixel при малом размере букв и к LcdFillRect в случае болших букв. В любом случае дороги ведут к LcdPixel. Вот ее текст:
void LcdPixel(int8_t x, int8_t y, uint8_t color)
{
if(x < 0 || x >= X_RES || y < 0 || y >= Y_RES) return; // не тратим время за пределами видимой области
uint8_t data;
if(y < 24 && page == 3) data = Lcd_page[x][y/8]; // читаем
else if((y >= 24 && y < 48) && page == 2){y-=24; data = Lcd_page[x][y/8];} // байт из
else if(y >= 48 && page == 1) {y-=48; data = Lcd_page[x][y/8];} // массива
else return;
switch(color){
case ON: SetPix(data, y%8); break; //data |= _BV(y%8);
case OFF: ClearPix(data, y%8); break; //data &= ~_BV(y%8);
case INV: InvPix(data, y%8); break; //data ^= 1<<y%8;
}
Lcd_page[x][y/8] = data; //заносим байт в массив
}
Вот тут я с этими page и застрял. В файле PCF8814_new.cpp к uint8_t LcdPageTWO(void) никто не обращается. Но color все - таки есть. Возможно это не цвет, а атрибуты монохрома ON OFF INV. Но данные заносятся в Lcd_page[x][y/8] = data;. Как они потом попадают в контроллер пока неясно.
Возможно это не цвет, а атрибуты монохрома ON OFF INV.
Точно. Такая собственно и была задумка, что можно не только в любое время и в любом месте "включить", "выключить", но и инвертировать состояние отдельного пикселя. Но точно не сделать его красным или зелёным )))
Попытаюсь всё-таки вкратце...:
Контроллер экрана устроен так, что состояние пикселов внутри него (видеобуфера) читать нельзя. Плюс к этому нельзя и ещё управлять отдельными пикселами. Только отправить по установленному адресу (координатам) байт. Пример: нужно установить пиксел по оси Х ноль, по У один. Посылаем команду с адресом первый банк, первая линия, и посылаем данные b00000010 или 0х02 в HEX-e. Если мы захотим зажечь следующий пиксел по оси У, то пошлём данные b00000100 и получится что предидущий пиксел мы вынужденно потушили. Если выводить по банкам, на который разделён экран, только символы, то это нам подходит, но строки будут только в координатах по У 0, 8, 16, 24, и т.д. О выводе геометрических фигур речь особо не шла. Я на первых парах написал библиотеку с возможностью вывода символов больше чем 8 пикселов в высоту и вывода линий, но пересекаться они не могли, и если в банке учто что выведено, то там печатать нельзя, т.к. картинка поменяется и будет не такая как желаем. Для полноценной геометрии, символов и картинок, ещё до того как мы с jeka_tm начали борьбу с экраном, применили следующее: Создали массив размером с разрешение экрана, и всё что нужно видеть на экране заносили в него, включая, выключая и инвертируя лубой пиксел, и в нужное время просто переносили содержание массива в видеобуфер. Вроде проблема была решена, но..... Появилась новая. Этот массив распологается в оперативной памяти и занимает почти всё место и ..... ну там всё сложно, наползание на стэки и всё такое.... короче контроллерам Atmega 8 и 16 работа с этой бибой была заказана а мной, до того как я это понял, было заказанно несколько таких экранов и контроллеров под них. Вот я начал думать думать и придумал чтоб создавать массив размером втрое меньше и выводить его сначала вверхнюю часть экрана, потом в среднюю, потом в нижнюю предварительно заполнив его нужными данными. А содержание библиотеки это воплошение идеи. Всё заработало....
Понял. Графика мне не нужна, но масштабирование тоже работать без RAM не будет - конечное изделие у меня также Мега8. Функция вывода которую я привел работает без буфера в RAM. Наверно придется ее оставить.
В моей 1202 было с точностью наоборот. Одна из 15 не заводилась ни в какую. Подключать ресет не стал, подключил резюк 1К на плюс 3.3В, а кондер 1мкФ на землю. Так она и пашет без проблем второй месяц.
Ну нельзя так просто взять какую то функцию и просто кудато вставить и применять. Необходимо выполнить определённый перечень команд которые в разных библиотеках по разному воплощены.
По моему оптимальным вариантом всёж будет не искать ошмётки по сети и сгортать их в кучу, и не пытаться изучать библиотеки если просто надо вывести значение.
Включайте мою библиотеку
Включайте шрифт 5х8
Если не нужны русские символы, их можно закоментировать.
Если не нужны буквы, их тоже можно закоментировать
Может кому - нибудь и мои труды пригодятся. Выкладываю как здесь это называется свой скетч. Программа умеет выводить стандартный шрифт 5х8 с русскими буквами в масштабах х1 и х4, а также 5х8 широкий если глобальная переменная не 0.
Процессор Мега 1284р легко меняется на любой другой. Промежуточный буфер в RAM не используется. Объем занимаемой памяти 35байт RAM и 5228 флэш. Если выкинуть плавучку, получается около 3кБ. Графики нет.
Main:
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define CMD 0
#define DATA 1
//функции LCD
void Init(void);
void Clear(void);
void GotoXY(char x,char y);
void Putc(unsigned char c);
void Print(char * message);
void Lcd_Write(char mode,unsigned char c);
void LCD_draw_char_4X(char code, char xpos, char ypos);
void LCD_draw_string_4X(const char* text, uint8_t xpos, uint8_t ypos);
volatile char StrobCNT = 9; //темп выполнения программы
volatile unsigned char Beep = 0; //счетчик пищалки
char Width=1; //<>0 - удвоенная ширина символов
static int _putchar(char c, FILE *stream){Putc(c); return 0;}//вывод на LCD
static FILE mystdout = FDEV_SETUP_STREAM(_putchar, NULL, _FDEV_SETUP_WRITE);
//***************************************************************
int main(void){
#define F 3.3/1023 //опорное / число отсчетов
unsigned int adcTemp; //буфер ADC int
float Fl; //значение напряжения
char buf[5]; //буфер для печати большими буквами
Init( ); //инициализация
while (1){ //основной цикл
if (StrobCNT>9) { //темп выполнения
PORTD |= (1<<(PD7)); //маркер выполнения
StrobCNT = 0; //очистить счетчик
GotoXY (0, 6); //позиция для служебной строки
//Измерение напряжения. Премя выполнения 12мс
adcTemp = ADCL; //читать
adcTemp |=(ADCH<<8); //значение АЦП
Fl = F * (adcTemp<<1); //пересчет во float с масштабом х2
printf("%u ",adcTemp); //вывод служебной строки
sprintf(buf, "%2.2fV",(double)Fl); //вывод напряжения
LCD_draw_string_4X(buf, 4, 2); //крупными цифрами
ADCSRA |= (1 << ADSC); //однократный запуск АЦП. Готовность по времени
PORTD^= (1 << PORTD7); //маркер выполнения
} //if (StrobCNT>9)
} //while (1)
return 0;
}
//***************************************************************
SIGNAL (TIMER1_OVF_vect ) { //Прерывание по таймеру 1.
StrobCNT++;
if (Beep) {TCCR0A=(1<<WGM01)|(1<<COM0B0); Beep--;}
else {TCCR0A=0;}}
//***************************************************************
void Init(void) { //Инициализация
WDTCSR |= (0<<WDCE) | (0<<WDE);//отключение WatchDog
WDTCSR = (0<<WDE) | (1<<WDP3);//с периодом = 512K циклов (~4.0 s)
//Порты
PORTD |= (1<<2); //signal up LCD_CSX PORTB |= 1<<5;
DDRD = 0xff; //data direction for signals LCD_CLC, LCD_SDA, LCD_CSX
DDRB = 0b11111011; //PB2 - кнопка, на ввод
PORTB |= (1 << PORTB2); //включение подпорки. Необходимо для кнопки
//LCD
_delay_ms(10); //выжидем не менее 5мс для установки генератора(менее 5 мс может неработать)
Lcd_Write (CMD, 0xA4); //all on/normal display
Lcd_Write (CMD, 0x2F); //power control set(charge pump on/off)
Lcd_Write (CMD, 0xC8); //vertical reverse. Для 1110 удалить!!!
Lcd_Write (CMD, 0xa1); //invert screen in horizontal axis
Lcd_Write (CMD, 0xaf); //display ON/OFF
Clear (); //clear LCD
//_delay_ms(100); //выжидем не менее 5мс для установки генератора(менее 5 мс может неработать)
//Lcd_Write (CMD, 0xa6); //positive mode (A6, A7)
//Настройка АЦП. ADCSRA: Минимальная частота F/128 (ADPS0-2 = 1), прерывания запрещены (ADIE=0)
//однократный режим (ADSC=0).
ADCSRA=(1<<ADEN)|(0 << ADSC)|(0<<ADATE)|(0<<ADIF)|(0<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
//ADMUX: ион - внутренний 2.56 В, вход ADC0(37выв, MUX0-4 = 0), выравнивание правое (ADLAR=0)
ADMUX = (1<<REFS1)|(1<<REFS0)|(0<<ADLAR)|(0<<MUX4)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
DIDR0 =(1<<ADC1D)|(1<<ADC2D);//Запретить цифровой вход на входах АЦП
ADCSRA |= (1<<ADSC); //начать преобразование
//Настройка таймера 0. Пищалка
OCR0A=0x50; //частота пищалки
TCCR0B=(1<<CS01)|(1<<CS00); //делитель 1024 (стр 109)
TCCR0A=(1<<WGM01)|(1<<COM0B0);//режим Clear Timer on Compare mode
//Настройка таймера 1
TCCR1B=(1<<CS11); //clkI/O/64 (From prescaler)
TIMSK1 |= (1<<TOIE1); //Разрешение прерывания от таймера 1
stdout = &mystdout; //переназначение вывода printf
asm("sei"); //
Beep = 3;} //
nokia1100_lcd_lib.c
#include "nokia1100_lcd_font.h" // Подключаем шрифт (будет размещен в программной памяти)
#define CMD 0
#define DATA 1
extern char Width; //<>0 - удвоенная ширина символов
//функции LCD
void Init(void);
void Clear(void);
void GotoXY(char x,char y);
void Putc(unsigned char c);
void Print(char * message);
void Lcd_Write(char mode,unsigned char c);
void LCD_draw_char_4X(char code, char xpos, char ypos);
void LCD_draw_string_4X(const char* text, uint8_t xpos, uint8_t ypos);
//******************************************************************************
void Clear(void){ //
Lcd_Write(CMD,0x40); //Y=0
Lcd_Write(CMD,0xB0); //
Lcd_Write(CMD,0x10); //X=0
Lcd_Write(CMD,0x00); //
for(unsigned int i=0;i<864;i++) Lcd_Write(DATA,0x00);}
//******************************************************************************
void Lcd_Write(char cmd,unsigned char c) //
{ unsigned char i; //cmd = 1 - передача данных
PORTD &= ~(1<<(PD4)); //сбросить в 0 линию CS
PORTD &= ~(1<<(PD6)); //сбросить в 0 линию CLK
if (cmd) PORTD |= (1<<(PD5)); //если девятый бит cmd = 1 - выставить 1 в SDA
else PORTD &= ~(1<<(PD5)); //иначе - выставить 0
PORTD |= (1<<(PD6)); //установить в 1 линию CLK
for (i = 0 ; i < 8 ; i++ ){ //передача 8-бит:
PORTD &= ~(1<<(PD6)); //сбросить в 0 линию CLK
if (c & 0x80 ) PORTD |= (1<<(PD5)); //если передаваемый бит 1 - выставить 1 в SDA
else PORTD &= ~(1<<(PD5)); //иначе - выставить 0
PORTD |= (1<<(PD6)); //установить в 1 линию CLK
c <<= 1;} //сдвиг передаваемого бита
//PORTD |= (1<<(PD4));
} //установить в 1 линию CS
//******************************************************************************
void Putc(unsigned char c){ //
if (c>127) c=c-64; //переносим символы кирилицы в кодировке CP1251 в начало второй
for ( unsigned char i = 0; i < 5; i++ ){// половины таблицы ASCII (начиная с кода 0x80)
unsigned char glyph = pgm_read_byte(&(Font[c-0x20][i]));
Lcd_Write(DATA,glyph); //
if (Width) {Lcd_Write(DATA,glyph);}}//
Lcd_Write(DATA,0x00); //зазор между символами по горизонтали в 1 пиксель
//Lcd_Write(DATA,0x00); //можно сделать две линии
}
//******************************************************************************
//Вывод строки символов на LCD-экран NOKIA 1100 в текущее место. Если строка выходит
//за экран в текущей строке, то остаток переносится на следующую строку.
void Print(char * message){while (*message) Putc(*message++);}
//******************************************************************************
//Устанавливает курсор в необходимое положение. Отсчет начинается в верхнем
//левом углу. По горизонтали 16 знакомест, по вертикали - 8
//x: 0..15
//y: 0..7
void GotoXY(char x,char y){ //
Lcd_Write(CMD,(0xB0|(y&0x0F))); //установка адреса по Y: 0100 yyyy
Lcd_Write(CMD,(0x00|(x&0x0F))); //установка адреса по X: 0000 xxxx - биты (x3 x2 x1 x0)
Lcd_Write(CMD,(0x10|((x>>4)&0x07)));} //установка адреса по X: 0010 0xxx - биты (x6 x5 x4)
//***************************************************************
void LCD_draw_string_4X(const char* text, uint8_t xpos, uint8_t ypos){
for (unsigned char i = 0; text[i]; i++){ //
LCD_draw_char_4X(text[i], xpos + i * 18, ypos);}} //18 - расстояние между символами.
//***************************************************************
void LCD_draw_char_4X(char code, char xpos, char ypos){ //
uint8_t d; //
for (int b = 0; b < 4; b++){ //
GotoXY(xpos, ypos + b); //
for (int i = 0; i < 6; i++){ //
if (i == 5) {d = 0;} //Шестой элемент всегда был 0 (в родной таблице)
else {d = (pgm_read_byte(&Font[code-0x20][i]) >> (b * 2));}
uint8_t m = 0; //
if (d & 0x01) m |= 0x0F; //
if (d & 0x02) m |= 0xF0; //
for (int p = 0; p < 3; p++) {Lcd_Write(DATA, m);}
} //for (int i = 0; i < 6; i++)
} //for (int b = 0; b < 4; b++)
} //void LCD_draw_char_4X(char code, uint8_t xpos, uint8_t ypos)
Маркеры PORTD |= (1<<(PD7)); дают возможность посмотреть время выполнения основного цикла. Время выполнения основного цикла 10.4мс. Кварц 7.2МГц. Среда AVRStudio4 и GCC.
Решил проблему масштабирования. Нашел таки в просторах Сети, адаптировал. Программа умеет масштабировать отдельно по осям Х, Y. Масштабы 1, 2, 4, 8. При масштабе 8 получаются буквы высотой почти на весь экран. Думаю что можно сделать и промежуточные варианты масштабов поигравшись с этим:
case 1: {shift2=1; shift3=1; }break; //
case 2: {shift2=16;shift3=2; }break; //
case 4: {shift2=4; shift3=8; }break; //
case 8: {shift2=2; shift3=32;}break;}
Уважаемые,нужен совет, все собрал в кучу,работает менюшка на дисплее от 1202,но есть косяки с русскими названиями. есть буфер размером в 16 байт,строка из прогмем копируется в буфер и дальше на экран.
пока в строке не более 7 символов все нормально,на всех последующих страницах меню все названия отображаются,как только превысил - так на первой страничке меню все правильно отображается а на последующих страницах идут пустые строки,с буржуйским языком все отлично работает при размере строки в 16 символов косяков не наблюдал.
я так подозреваю трабла сидит в кодировке символов UTF-8 из-за двубайтной кодировки русских букв? пробовал в настройках IDE менять кодировку - либо пусто вместо русских букв либо кракозяблы. шрифт использую тот что в исправленной версии от Gres.
может вместо
использовать что то вроде ?
Использовал бибу от Gres,класс титановый велосипед для тактовой кнопки от Клапауций 999 ,и MenuOS_9R3 от Kashkanov Artem radiolok@yandex.ru.
Если народу интересно,могу выложить скетч на всеобщее обозрение и оптимизацию,видео некуда закинуть. пока там еще жуткая смесь из русских и англицких слов,но это поправимо.
давай посмотрим
разбито на несколько файлов,коментарии писались для сынишки
оригинал менюшки https://rlntech.org/menuos/
я выкинул динамич конфиги,мне они не нужны.
пробовал вот так,
код уменьшился на 4 байта,значит точно кодировки мать их, но символы налазят друг на друга,хотя в функции как я понял должен быть пробел в 1 пиксел ,поправте если ошибаюсь.
надо смотреть код вывода строки. там если русские символы изменяется позиция. если убрать смещение то должно быть нормально
но вообще как это слишком замороченно. переводить символы в коды вручную и т.д.
Gres, Можете подсказать, как в вашей библиотеке вывести именно переменную?
Пытался так, но ошибку выдает
Вы преобразуйте переменную в строку (utoa;itoa вам в помощь) и все получится. я вот так сделал
jeka_tm, а ты в библиотеке разве не писал вывод переменных разных типов?
неа. для этого надо прицеплять print.h или printf.h. но как это делать я не знаю
Я без этого управился, перегрузил функции.
Вы преобразуйте переменную в строку (utoa;itoa вам в помощь) и все получится. я вот так сделал
Спасибо! А можете еще подсказать, как быть с такой записью;
1,43%
Можно как то вывести отдельно "1" "," "43" "%"
Без перевода курсора каждый раз ?
Для начала тип с плавающей запятой нужно преобразовать в целочисленный какой угодно - int16_t , uint16_t , int32_t , uint32_t ну так далее.при этом предварительно (на примере 1,43%) умножить на 100 чтоб получилось 143. теперь можно печатать учитывая количество знаков и нули.
Для начала тип с плавающей запятой нужно преобразовать в целочисленный какой угодно - int16_t , uint16_t , int32_t , uint32_t ну так далее.при этом предварительно (на примере 1,43%) умножить на 100 чтоб получилось 143. теперь можно печатать учитывая количество знаков и нули.
Это я видел. Знаю. Но в библе gres. Для каждого вывода нужно указывать место курсору
lcd.drawString(1, 1,1, "%");
Ну тогда притензии к автору бибы. Я от таких проблем в своей избавился.
Дело в том что не только у каждого пользователя свои требования к библиотеке но и у каждого проэкта свои требования к ней, и вем не угодишь и всего не учтёшь.
ну хорошо. допустим не нужно указывать место курсору. а где тогда он должен выводить? продолжать в строке?
и обратный случай: нужно вывести на 2 пикселя левее и на 5 ниже относительно существующего, например дробь захотелось сделать, как это сделать без указания?
а насчет вывода без преобразования надо смотреть как сделано у hugoboss317, но это в том случае если реально нужно и не лень этим заниматься
ну хорошо. допустим не нужно указывать место курсору. а где тогда он должен выводить? продолжать в строке?
не хочу казаться назойлевым но я решил это так:
Обьявляем место откуда начинаем печатать символы, а далее курсор передвигается сам после каждого символа на его ширину плюс 1 пиксел. Когда заканчивается строка, курсор переходин на строку ниже на высоту символа плюс пиксел. если нужно поднять, опустить или передвинуть на пару тройку четвёрку пикселов или вобще в другое место, просто указываем место вызовом этой функции.
НЕ утверждаю что именно так правильно и только так и надо, но решил что так удобнее.
ну может быть. один из вариантов. смотря для чего. я делал код под себя
Меня по сути то устраивает библотека которая в Я.Д. у Jeka_tm лежит "Nokia1100_Library". Но у меня почему то она плохо ведет себя с ресетом. Более менее если ресет кинуть на 3.3в. Но если в питании какой нибудь скачек то диисплей почему то больше не кажет. А с остальными библиотеками все нормально... Может знаете почему такие артефакты происходят?
Более менее если ресет кинуть на 3.3в.
Питание чипа тоже 3,3, не 5? Ещё можно емкость после кренки поставить, никогда не мешало и хорошо стабилизирует работу.
hugoboss317. А у вас есть какой нибудь проект с вашей библиотекой? Хотелось бы посмотреть скетч.
Добрый всем вечер. Кто - нибудь пробовал масштабировать шрифты? Хранить в памяти шрифты с большими буквами накладно. А если взять, скажем шрифт 5x8 и увеличить его в 4 раза? На просторах интернета я нашел что - то подобное:
Как это работает после строки pgm_read_byte(&Font[code-0x20][i]) понять мне пока сложно. Может кто из местных гуру разберет. Мне бы хотелось применить масштабирование 6х и 8х. Но уже с масштабом 4х символы получаются болше чем 16х32.
Шрифт полностью не привожу, он стандартный с набором русских букв:
Да, и это работает в железе ATMega1284p + 1202LCD
Конечно! Как раз для экономии места я в своей библиотеке написал возможность выводить двойную ширину и маштабировать.
Кодом масштабирования не поделитесь?
Да не вопрос. Если конечно поможет
Могу пояснить сам алгоритм:
Читаем байт из программной памяти, читаем бит, если "1" стивим пиксел, если "0", едим дальше, это без саштаба, с машбабом ставим не пиксел а квадрат размером с маштаб. только переежаем дальше не на пиксел а на тот же размер мастаба
Спасибо. Для меня в этом коде много избыточного. Где - то даже похожую конструкцию видал:
Кажетс для STM32 что - то. Смутил только макрос _BV(j%8). Как будет без него? Если нетрудно, приведите пожалуйста код LcdFillRect, а я если получится обязательно выложу.
А я и не говорил что это надо копировать и куда то вставит. Даже больше скажу, пост 326 лишний и поста 327 более чем достаточно.
>> Смутил только макрос _BV(j%8). Как будет без него?
_BV(bit) то же что (1<<(bit))
>> приведите пожалуйста код LcdFillRect
Это построение прямоугольника, может в вашей библиотеке подобная функция есть.
Печально, но ничего не получилось. Возможно потому, что я не смог реализовать функцию LcdFillRect. Мозгов не хватило.
Так Вы бы мне сначала показали какую именно библиотеку используете.
Да нет как таковой библиотеки - то. Вывожу шрифт 5*8 адаптированным под себя кодом из просторов Сети:
Шрифт 5х8 обычный, от туда же. Кусок этого шрифта я привел в посту 323. Этот же шрифт использую как входной в программе масштабирования х4 (изначально там был входной шрифт 6*8, но шестой байт был всегда нулевым). Саму программу масштабирования я привел в посту 323. Все остальное - стандартно. Вывод строки, подмена printf.
Но LCD_draw_char_4X для меня слишком запутанна, я хотел изменить масштаб 2,4,6,8. Собственно, с этой идеей и обратился сюда.
Нет библиотеки????
Скетч сюда
А вообще, я конечно понимаю, что тема большая, но тут всё уже давно раскрыто. Вы, вероятно не понимаете как именно происходит вывод данных на экран. Почитайте с начала, станет понятней.
Ну и десерт. Вы используетет контроллер с флэшом 128 кБ и не хватает места для хорошей библиотеки, которую, к примеру, обсуждают в начале темы?
Я контроллером PCF8814 занимаюсь от силы неделю.
У меня самопальный макет. Конечное изделие - Mega8 с 8-ю килобайтами памяти. Что такое скетч? Подозреваю что набросок кода.
Не хотите читать, заставлять не стану.
А вот для контроллера Atmega8 настоятельно рекомендую мою библиотеку, т.к. писал её как раз под контроллеры с RAM 1 kB.
пост 204.
По моему, это оптимальный вариант. Там примеры - это и есть скетчи.
Удачи
Ковырял код - уперся в функцию LcdPixel. Там работа со страницами, цветом пикселя - черт ногу сломит. Как все это потом попадает в контроллер я так и не понял. В функции LcdPageTWO кажется все это скидывается в контроллер, но из каких - то массивов в RAM. Зачем так сложно?
В заголовке указан список контроллеров
LCD Nokia 1280, 1202, 1203, 1100 96x68 (96x64 for 1100) by H`UGO
В которых (могу ошибаться) цветом и и не пахнет.
а черный или белый?
В которых (могу ошибаться) цветом и и не пахнет.
А Вы хотите на монохромном дисплее выводить цвет? :) Это то же что по микноволновке телевизор смотреть.
А если серьёзно..... Зачем Вы там пытаетесь разобряться в библиотеке? Нужно свего лишь понять как правильно выводить данные на экран. Я в очередной раз повторю что все тонкости этого экрана мы уже тут обсудили и с ними разобрались, Вы просто не хотите читать, а я не хочу десятый раз повторяться, хотя кажется придётся.
Все выводимые данные должны быть внутри "страниц"
Внутри этих же страниц задержку не делаем (delay();)
Выводимые данные там не меняем
Получается что Вы пытались на микроволновку вывести изображение...
А если серьезно - подо что все это писалось? На перечисленных в хидере дисплеях цвета нет.
В том то и дело что не поняв как отправляются данные на контроллер невозможно разобраться как правильно их выводить на экран. Ок. Грузить не буду. Буду курить тему с нуля.
Реально не понимаю о каком цвете Вы говорите и вобще что именно имеете ввиду
Может о разном говорим? Я по ссылке в 204 посту скачал в частности PCF8814_new.cpp. Функция
void LcdChar(int8_t x, int8_t y, unsigned char ch, uint8_t color, uint8_t size)
имеет параметр uint8_t color и обращается к LcdPixel при малом размере букв и к LcdFillRect в случае болших букв. В любом случае дороги ведут к LcdPixel. Вот ее текст:
Вот тут я с этими page и застрял. В файле PCF8814_new.cpp к uint8_t LcdPageTWO(void) никто не обращается. Но color все - таки есть. Возможно это не цвет, а атрибуты монохрома ON OFF INV. Но данные заносятся в Lcd_page[x][y/8] = data;. Как они потом попадают в контроллер пока неясно.
вот он и добрался до самой интересной части в библиотеке)))
Возможно это не цвет, а атрибуты монохрома ON OFF INV.
Точно. Такая собственно и была задумка, что можно не только в любое время и в любом месте "включить", "выключить", но и инвертировать состояние отдельного пикселя. Но точно не сделать его красным или зелёным )))
Попытаюсь всё-таки вкратце...:
Контроллер экрана устроен так, что состояние пикселов внутри него (видеобуфера) читать нельзя. Плюс к этому нельзя и ещё управлять отдельными пикселами. Только отправить по установленному адресу (координатам) байт. Пример: нужно установить пиксел по оси Х ноль, по У один. Посылаем команду с адресом первый банк, первая линия, и посылаем данные b00000010 или 0х02 в HEX-e. Если мы захотим зажечь следующий пиксел по оси У, то пошлём данные b00000100 и получится что предидущий пиксел мы вынужденно потушили. Если выводить по банкам, на который разделён экран, только символы, то это нам подходит, но строки будут только в координатах по У 0, 8, 16, 24, и т.д. О выводе геометрических фигур речь особо не шла. Я на первых парах написал библиотеку с возможностью вывода символов больше чем 8 пикселов в высоту и вывода линий, но пересекаться они не могли, и если в банке учто что выведено, то там печатать нельзя, т.к. картинка поменяется и будет не такая как желаем. Для полноценной геометрии, символов и картинок, ещё до того как мы с jeka_tm начали борьбу с экраном, применили следующее: Создали массив размером с разрешение экрана, и всё что нужно видеть на экране заносили в него, включая, выключая и инвертируя лубой пиксел, и в нужное время просто переносили содержание массива в видеобуфер. Вроде проблема была решена, но..... Появилась новая. Этот массив распологается в оперативной памяти и занимает почти всё место и ..... ну там всё сложно, наползание на стэки и всё такое.... короче контроллерам Atmega 8 и 16 работа с этой бибой была заказана а мной, до того как я это понял, было заказанно несколько таких экранов и контроллеров под них. Вот я начал думать думать и придумал чтоб создавать массив размером втрое меньше и выводить его сначала вверхнюю часть экрана, потом в среднюю, потом в нижнюю предварительно заполнив его нужными данными. А содержание библиотеки это воплошение идеи. Всё заработало....
Короче эта проблема
Понял. Графика мне не нужна, но масштабирование тоже работать без RAM не будет - конечное изделие у меня также Мега8. Функция вывода которую я привел работает без буфера в RAM. Наверно придется ее оставить.
Маштабирование без RAM работать будет. Но нужно написать соответствующую функцию.
Собственно, это я и искал.
В моей 1202 было с точностью наоборот. Одна из 15 не заводилась ни в какую. Подключать ресет не стал, подключил резюк 1К на плюс 3.3В, а кондер 1мкФ на землю. Так она и пашет без проблем второй месяц.
Ну нельзя так просто взять какую то функцию и просто кудато вставить и применять. Необходимо выполнить определённый перечень команд которые в разных библиотеках по разному воплощены.
По моему оптимальным вариантом всёж будет не искать ошмётки по сети и сгортать их в кучу, и не пытаться изучать библиотеки если просто надо вывести значение.
Включайте мою библиотеку
Включайте шрифт 5х8
Если не нужны русские символы, их можно закоментировать.
Если не нужны буквы, их тоже можно закоментировать
строка 43
строка 177
И печатайте себе в масштабе 2х 3х 4х
Может кому - нибудь и мои труды пригодятся. Выкладываю как здесь это называется свой скетч. Программа умеет выводить стандартный шрифт 5х8 с русскими буквами в масштабах х1 и х4, а также 5х8 широкий если глобальная переменная не 0.
Процессор Мега 1284р легко меняется на любой другой. Промежуточный буфер в RAM не используется. Объем занимаемой памяти 35байт RAM и 5228 флэш. Если выкинуть плавучку, получается около 3кБ. Графики нет.
Main:
nokia1100_lcd_font.h:
Маркеры PORTD |= (1<<(PD7)); дают возможность посмотреть время выполнения основного цикла. Время выполнения основного цикла 10.4мс. Кварц 7.2МГц. Среда AVRStudio4 и GCC.
Решил проблему масштабирования. Нашел таки в просторах Сети, адаптировал. Программа умеет масштабировать отдельно по осям Х, Y. Масштабы 1, 2, 4, 8. При масштабе 8 получаются буквы высотой почти на весь экран. Думаю что можно сделать и промежуточные варианты масштабов поигравшись с этим:
Вот скетч:
Время выполнения основного цикла 16.7мс. RAM 31байт, код 5278байт. Входной шрифт во всех случаях 5х8.
Исходник функции масштабирования брал здесь:
http://alex-exe.ru/radio/stm32/lcd-nokia1100-stm32/