Отправка буквы за буквой на ЖК-дисплей, однако дисплей знает полную строку?
Я думаю, что это мой последний пост об этом конкретном проекте, но сейчас у меня действительно странный поворот
Мне удалось создать нужную мне веб-страницу и передать строку дальше. Теперь, когда загорается светодиод, он отправляет соответствующую букву на ЖК-дисплей.
Проблема сейчас в том, что каким-то образом, когда я отправляю длинную строку, ЖК-дисплей каким-то образом "знает" и отображает переполнение во второй строке, что для меня не имеет никакого смысла. Все, о чем я могу думать, это то, что Ethernet Shield использует те же соединения, что и мой I2C, который подключается к ЖК-дисплею.
Как бы то ни было, вот мой код:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <Ethernet.h>
#define latchPin 5
#define clockPin 7
#define dataPin 6
LiquidCrystal_I2C lcd(0x27,16,2);
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
EthernetServer server(80);
String text;
char readHttp[100];
int i;
boolean temtexto;
char lcdtext[16];
void setup() {
Serial.begin(9600);
for (int i = 2; i < 7; i++) {
if (i != 4) {
pinMode(i, OUTPUT);
}
}
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);
lcd.init();
lcd.setBacklight(HIGH);
lcd.setCursor(0,0);
lcd.print("INICIANDO");
Serial.println("Iniciando");
if (Ethernet.begin(mac) == 0) {
lcd.clear();
lcd.setCursor(1,0);
lcd.print("FALHA AO PEGAR");
Serial.println("FALHA");
lcd.setCursor(7,1);
lcd.print("IP");
delay(5000);
return;
}
lcd.clear();
lcd.setCursor(0,0);
lcd.print("PRONTO");
lcd.setCursor(0,1);
lcd.print(Ethernet.localIP());
lcd.setCursor(0,0);
Serial.println(Ethernet.localIP());
server.begin();
brilho(255);
inicializationEffect();
}
void loop(){
EthernetClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
lcd.setBacklight(LOW);
lcd.clear();
char c = client.read();
if (c == ';') { temtexto = false; readHttp[i] = '\n';}
if (temtexto) {
if(i < 100) {
readHttp[i] = toupper(c);
i++;
}
}
if (c == '?') { temtexto = true; }
if (c == '\n') {
client.println("HTTP/1.1 200 OK"); // Envia nova pagina
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
client.println("<HTML>");
client.println("<HEAD>");
client.println("<TITLE>Parede Stranger Things</TITLE>");
client.println("</HEAD>");
client.println("<BODY>");
client.println("<H1>Qual texto deseja enviar?</H1>");
client.println("<input type=text id=texto style=width:295px maxlength=50><br>");
client.println("<input type=submit value='Enviar' style=width:150px;height:45px onClick=location.href='/?'+document.getElementById('texto').value+';'>");
client.println("</BODY>");
client.println("</HTML>");
client.println("<string>");
client.println("</string>");
delay(1);
client.stop();
char *leader = readHttp;
char *follower = leader;
// While we're not at the end of the string (current character not NULL)
while (*leader) {
// Check to see if the current character is a %
if (*leader == '%') {
// Grab the next two characters and move leader forwards
leader++;
char high = *leader;
leader++;
char low = *leader;
// Convert ASCII 0-9A-F to a value 0-15
if (high > 0x39) high -= 7;
high &= 0x0f;
// Same again for the low byte:
if (low > 0x39) low -= 7;
low &= 0x0f;
// Combine the two into a single byte and store in follower:
*follower = (high << 4) | low;
} else {
// All other characters copy verbatim
*follower = *leader;
}
// Move both pointers to the next character:
leader++;
follower++;
}
// Terminate the new string with a NULL character to trim it off
*follower = 0;
lcd.clear();
executeEffect(readHttp);
memset(readHttp, 0, sizeof readHttp);
i = 0;
}
}
}
}
}
void lineActivation(int line, int keepalive) {
if (keepalive == 0) {
for (int i = 2; i <= 4; i++) {
if (i == 4) { i = 8; } //porta 4 usada pelo SD do Ethernet
digitalWrite(i, LOW);
}
}
if (line >= 1 && line <=3){
if (line == 3) { line = 7; } //porta 4 usada pelo SD do Ethernet
digitalWrite(line+1, HIGH); //coloca a linha 1 em estado lógico alto
}
}
void activateLetter(char letter, int on, int off) {
int lt;
lt = (((int)letter - 64) % 9);
if (((int)letter - 64) > 8) {
lt = lt + 1;
}
if (on) {
callCol(1<<lt-1);
delay(on);
}
if (off) {
callCol(0);
delay(off);
}
}
void callCol(int col){
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, highByte(col));
shiftOut(dataPin, clockPin, MSBFIRST, lowByte(col));
digitalWrite(latchPin, HIGH);
}
void executeEffect(char text[]) {
if (strlen(text) > 0) {
brilho(255);
lcd.setBacklight(HIGH);
memset(lcdtext, ' ', sizeof lcdtext);
}
for (int i = 0; i < strlen(text); i++) {
if (text[i] == ' ') {
lcdScreen(' ');
delay(1000);
} else if (((int)text[i] < -58 && (int)text[i] > -65) || ((int)text[i] < -28 && (int)text[i] > -33)) {
text[i] = 'A';
} else if (((int)text[i] < -47 && (int)text[i] > -41) || ((int)text[i] < -9 && (int)text[i] > -14)) {
text[i] = 'O';
} else if (((int)text[i] < -52 && (int)text[i] > -57) || ((int)text[i] < -20 && (int)text[i] > -25)) {
text[i] = 'E';
} else if (((int)text[i] < -49 && (int)text[i] > -53) || ((int)text[i] < -16 && (int)text[i] > -21)) {
text[i] = 'I';
} else if (((int)text[i] < -35 && (int)text[i] > -40) || ((int)text[i] < -3 && (int)text[i] > -8)) {
text[i] = 'U';
} else if (((int)text[i] == -25 || (int)text[i] == -57)) {
text[i] = 'C';
}
if ((int)text[i] < 65 or (int)text[i] > 90) {
} else {
lineActivation(int(((int)text[i] - 64) / 9) + 1, 0);
//Serial.println(text[i]);
lcdScreen(text[i]);
activateLetter(text[i], 1000, 200);
}
}
for (int i = 0; i < 16; i++){
lcdScreen(' ');
delay(500);
}
lcd.setBacklight(LOW);
lcd.clear();
}
void lcdScreen(char ltc) {
Serial.println(ltc);
memcpy(lcdtext, &lcdtext[1], sizeof(lcdtext) - sizeof(char));
lcdtext[15] = ltc;
lcd.setCursor(0,0);
lcd.print(lcdtext);
}
void inicializationEffect() {
Serial.println("LineByLine");
for (int i = 1; i < 27; i++) {
lineActivation(int(i / 9) + 1, 0);//ativa a linha
activateLetter(char(64 + i), 100, 10); //ativa a letra
}
}
void waveEffect() {
Serial.println("Wave");
for (int i = 1; i < 4; i++) {
lineActivation(i, 1);//ativa a linha
}
for (int i = 1; i < 10; i++) {
activateLetter(char(72 + i), 100, 0);
}
for (int i = 1; i < 10; i++) {
activateLetter(char(72 + i), 0, 100);
}
lineActivation(0, 0);
}
void blink() {
Serial.println("Blink");
callCol(511);
lineActivation(1,1);
lineActivation(2,1);
lineActivation(3,1);
for (int i = 0; i < 256; i++){
brilho(i);
delay(1);
}
delay(100);
for (int i = 255; i >= 0; i--){
brilho(i);
delay(1);
}
}
void brilho(byte brt) {
analogWrite(9, 255-brt);
}
Я использую сетевой экран W5100, адаптер I2C для ЖК-дисплея и ЖК-дисплей 16x2. I2C подключается к выводам A4 (SDA) и A5 (SCL).
Вот изображение того, как отображается ЖК-дисплей, когда я отправляю "Это действительно длинная строка":
В первой строке текст прокручивается по одному символу за раз, как и было задумано. Однако во второй строке происходит переполнение текста, который, насколько я могу судить, я вообще никогда не отправлял на ЖК-дисплей. Даже если я попытаюсь выполнить функцию lcd.clear() после каждого отправленного символа, она все равно сохранится с концом строки во второй строке.
@Feeds, 👍1
2 ответа
Я думаю, то, что вы видите, является результатом:
lcd.print(lcdtext);
и тот факт, что lcdtext не завершается нулем. print()
ожидает
строку, то есть некоторую последовательность из нуля или более символов, отличных от '\0', за которыми следует символ '\0'
. Не завершив его с помощью null самостоятельно, на практике он продолжит чтение памяти после вашего расположения lcdtext в памяти, пока не найдет '\0'
. Где-то там он, вероятно, находит дополнительные символы, которые вы видите.
Вы могли бы сделать так, чтобы в конце всегда было '\0'
, увеличив lcdtext на один символ больше, если это необходимо для этого. Или вы могли бы:
lcd.write(lcdtext, sizeof lcdtext);
В этой форме с длиной / размером в качестве второго аргумента write
не ожидает строку, просто произвольный фрагмент символов, независимо от того, содержат ли они '\0'
или нет, и знает, что нужно остановиться на любом количестве символов, которое было указано.
В качестве исключения следует использовать memmove, а не
memcpy
, когда входные и выходные назначения перекрываются. Что происходит с memcpy
, когда они перекрываются, не определено:
memmove(lcdtext, &lcdtext[1], sizeof(lcdtext) - 1);
memmove
- это внешне то же самое, только разработанное для этого сценария. Разница между ними заключается в том, какие оптимизации включаются благодаря знанию того, что источник и место назначения могут перекрываться, а могут и не перекрываться. Возможно, это не является источником проблемы на вашей текущей платформе, в зависимости от того, как был написан memcpy, но это может произойти позже или при смене платформы.
Хм, я вроде как решил эту проблему, но я все еще не понимаю, как lcdtext
получал полную строку.
Я заменил функцию ЖК-экрана следующим образом:
void lcdScreen(char ltc) {
Serial.println(ltc);
Serial.print(lcdtext);
memmove(lcdtext, &lcdtext[1], sizeof(lcdtext) - 1);
lcdtext[15] = ltc;
lcd.setCursor(0,0);
for (int i = 0; i < 16; i++){
lcd.print(lcdtext[i]);
}
}
Это работает, но я все еще не понимаю, почему у меня был полный текст, когда я просто вызываю эту функцию с одним символом каждый раз
- ЖК-дисплей I2C отображает странные символы
- Экран LCD 16*02 I2C показывает только первый напечатанный символ
- 16/2 arduino I2C ЖК-дисплей не загорается?
- Альтернатива LCD-дисплеям UART/I2C?
- Датчики I2C не работают при подключении к LCD дисплею 20X04
- Нумерация выводов ЖК-дисплея против нумерации выводов контроллера I2C против эскиза
- Проблема стабильности кода прерываний, связанного с датчиком расхода
- Проблемы с подключением ЖК-дисплея I2C
Я заменил memcpy на memmove, и он работал так же, как и раньше. Функция lcd.write не включена<LiquidCrystal_I2C.h>, это библиотека, которую я использую для I2C для связи со светодиодом. Добавление '\0' в конец просто удалило 4 строки в конце. КОЛЬЦО все еще появлялось, @Feeds
@Feeds, я не удивлен, что
memmove ()
не имеет никакого значения для AVR. Я немного удивлен, что вы не можете использовать эту формуwrite()
, поскольку она является частью классаPrint
, который предоставляетprint ()
иprintln()
среди прочих. У вас есть ссылка на точную версию библиотеки LiquidCrystal_I2C, которую вы используете?, @timemageТеперь, когда я думаю об этом, проблема может заключаться в том, что lcdtext является не строкой, а массивом символов. Библиотека, которую я использую, - это библиотека sketch https://github.com/johnrickman/LiquidCrystal_I2C , версия 1.1.2, @Feeds
В https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.h#L55 вы можете увидеть, где печать унаследована. В https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.cpp#L10 реализована функция записи одного символа(). https://github.com/arduino/ArduinoCore-avr/blob/1.8.3/cores/arduino/Print.cpp#L34 где реализация по умолчанию предназначена для упомянутой двухпараметрической версии write(), используется односимвольная функция write()., @timemage
Другими словами, это должно работать нормально. Если это камень преткновения, уделите этому некоторое время, и если вы все еще застряли, возможно, начните новый вопрос с сокращенного примера кода, спрашивающего об этом одном аспекте., @timemage
Сейчас я далеко от своего arduino, но я проведу тестирование, как только вернусь домой, чтобы сообщить о своих выводах. Я все еще в недоумении от того, как работает ЖК-дисплей, @Feeds